@brief Encapsulates pixel iterator construction with optional XYMap reordering
| 46 | |
| 47 | /// @brief Encapsulates pixel iterator construction with optional XYMap reordering |
| 48 | class ReorderingPixelIteratorAny { |
| 49 | private: |
| 50 | /// @brief Get thread-local buffer for addressing transformation |
| 51 | /// @return Reference to thread-local CRGB vector (reused across calls) |
| 52 | static fl::vector<CRGB>& getReorderBufferTLS() { |
| 53 | return SingletonThreadLocal<fl::vector<CRGB>>::instance(); |
| 54 | } |
| 55 | fl::optional<PixelController<RGB, 1, 0xFFFFFFFF>> mAddressedController; |
| 56 | PixelIteratorAny mPixelIterator; |
| 57 | |
| 58 | public: |
| 59 | /// @brief Construct pixel iterator with optional addressing transformation |
| 60 | /// @param pixels Source pixel controller |
| 61 | /// @param addressing Optional XYMap for transformation |
| 62 | /// @param rgbOrder RGB color order |
| 63 | /// @param rgbw RGBW settings |
| 64 | /// @param channelName Channel name for error messages |
| 65 | ReorderingPixelIteratorAny( |
| 66 | PixelController<RGB, 1, 0xFFFFFFFF>& pixels, |
| 67 | const XYMap* addressing, |
| 68 | EOrder rgbOrder, |
| 69 | Rgbw rgbw, |
| 70 | Rgbww rgbww, |
| 71 | const fl::string& channelName) |
| 72 | : mPixelIterator(pixels, rgbOrder, rgbw, rgbww) { |
| 73 | |
| 74 | // Apply addressing transformation if configured |
| 75 | if (addressing) { |
| 76 | u16 numLeds = pixels.size(); |
| 77 | u16 width = addressing->getWidth(); |
| 78 | u16 height = addressing->getHeight(); |
| 79 | u16 expectedLeds = width * height; |
| 80 | |
| 81 | // Validate that XYMap dimensions match channel LED count |
| 82 | if (expectedLeds != numLeds) { |
| 83 | FL_ERROR("Channel '" << channelName << "': XYMap dimensions (" << width << "x" << height |
| 84 | << "=" << expectedLeds << ") don't match LED count (" << numLeds |
| 85 | << "). Addressing transformation may produce unexpected results."); |
| 86 | } |
| 87 | |
| 88 | // Cast mData to CRGB array |
| 89 | const CRGB* pixelArray = (const CRGB*)pixels.mData; |
| 90 | |
| 91 | // Get thread-local buffer (reuses allocation if size unchanged) |
| 92 | fl::vector<CRGB>& buffer = getReorderBufferTLS(); |
| 93 | buffer.clear(); |
| 94 | buffer.resize(numLeds); |
| 95 | |
| 96 | // Fill buffer by mapping each physical index to its source |
| 97 | for (u16 physicalIdx = 0; physicalIdx < numLeds; physicalIdx++) { |
| 98 | u16 x = physicalIdx % width; |
| 99 | u16 y = physicalIdx / width; |
| 100 | u16 sourceIdx = addressing->mapToIndex(x, y); |
| 101 | buffer[physicalIdx] = (sourceIdx < numLeds) ? pixelArray[sourceIdx] : CRGB::Black; |
| 102 | } |
| 103 | |
| 104 | // Construct PixelController with reordered buffer |
| 105 | mAddressedController.emplace( |