| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | /* | ||
| 3 | * This file is part of the Falcon Player (FPP) and is Copyright (C) | ||
| 4 | * 2013-2022 by the Falcon Player Developers. | ||
| 5 | * | ||
| 6 | * The Falcon Player (FPP) is free software, and is covered under | ||
| 7 | * multiple Open Source licenses. Please see the included 'LICENSES' | ||
| 8 | * file for descriptions of what files are covered by each license. | ||
| 9 | * | ||
| 10 | * This source file is covered under the LGPL v2.1 as described in the | ||
| 11 | * included LICENSE.LGPL file. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <string> | ||
| 15 | #include "fpp-json-fwd.h" | ||
| 16 | #include <vector> | ||
| 17 | |||
| 18 | #include "ChannelOutput.h" | ||
| 19 | |||
| 20 | typedef enum virtualPixelColor { | ||
| 21 | kVPC_RGB, | ||
| 22 | kVPC_RBG, | ||
| 23 | kVPC_GRB, | ||
| 24 | kVPC_GBR, | ||
| 25 | kVPC_BRG, | ||
| 26 | kVPC_BGR, | ||
| 27 | kVPC_RGBW, | ||
| 28 | kVPC_Red, | ||
| 29 | kVPC_White, | ||
| 30 | kVPC_Blue, | ||
| 31 | kVPC_Green, | ||
| 32 | kVPC_Custom, | ||
| 33 | } VirtualPixelColor; | ||
| 34 | |||
| 35 | typedef struct virtualDisplayPixel { | ||
| 36 | int x; | ||
| 37 | int y; | ||
| 38 | int z; | ||
| 39 | int xs; | ||
| 40 | int ys; | ||
| 41 | int zs; | ||
| 42 | int ch; | ||
| 43 | int r; | ||
| 44 | int g; | ||
| 45 | int b; | ||
| 46 | int cpp; | ||
| 47 | unsigned char customR; | ||
| 48 | unsigned char customG; | ||
| 49 | unsigned char customB; | ||
| 50 | |||
| 51 | VirtualPixelColor vpc; | ||
| 52 | } VirtualDisplayPixel; | ||
| 53 | |||
| 54 | class VirtualDisplayBaseOutput : public ChannelOutput { | ||
| 55 | public: | ||
| 56 | VirtualDisplayBaseOutput(unsigned int startChannel, unsigned int channelCount); | ||
| 57 | virtual ~VirtualDisplayBaseOutput(); | ||
| 58 | |||
| 59 | virtual int Init(Json::Value config) override; | ||
| 60 | |||
| 61 | int InitializePixelMap(void); | ||
| 62 | int ScaleBackgroundImage(std::string& bgFile, std::string& rgbFile); | ||
| 63 | void LoadBackgroundImage(void); | ||
| 64 | |||
| 65 | void GetPixelRGB(VirtualDisplayPixel& pixel, unsigned char* channelData, | ||
| 66 | unsigned char& r, unsigned char& g, unsigned char& b); | ||
| 67 | void DrawPixel(int rOffset, int gOffset, int bOffset, | ||
| 68 | unsigned char r, unsigned char g, unsigned char b); | ||
| 69 | virtual void DrawPixels(unsigned char* channelData); | ||
| 70 | |||
| 71 | virtual void DumpConfig(void) override; | ||
| 72 | |||
| 73 | virtual void GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) override; | ||
| 74 | |||
| 75 | std::string m_backgroundFilename; | ||
| 76 | float m_backgroundBrightness; | ||
| 77 | int m_width; | ||
| 78 | int m_height; | ||
| 79 | int m_bytesPerPixel; | ||
| 80 | int m_bpp; | ||
| 81 | float m_scale; | ||
| 82 | int m_previewWidth; | ||
| 83 | int m_previewHeight; | ||
| 84 | std::string m_fitTo; | ||
| 85 | std::string m_colorOrder; | ||
| 86 | unsigned char* m_virtualDisplay; | ||
| 87 | int m_pixelSize; | ||
| 88 | |||
| 89 | uint16_t*** m_rgb565map; | ||
| 90 | |||
| 91 | std::vector<VirtualDisplayPixel> m_pixels; | ||
| 92 | |||
| 93 | protected: | ||
| 94 | // Byte size of a w*h buffer at bpp bytes per pixel, or 0 when the | ||
| 95 | // dimensions cannot form one (non-positive, or the product would overflow | ||
| 96 | // a signed int -- every downstream offset multiplies the same factors, so | ||
| 97 | // capping the total at INT_MAX keeps all of that arithmetic safe too). | ||
| 98 | // Callers fail init instead of heap-smashing. Static: no vtable/object | ||
| 99 | // impact on plugin-facing classes. | ||
| 100 | static size_t virtualDisplayBufferBytes(int w, int h, int bpp); | ||
| 101 | |||
| 102 | public: | ||
| 103 | // Historically public: external channel-output plugins may reference this | ||
| 104 | // directly, so it stays public while the helper above stays protected. | ||
| 105 | bool m_allowDuplicatePixels; // Set to true for 3D mode where multiple pixels can be at same coords | ||
| 106 | }; | ||
| 107 | |||
| 108 | 1536543 | inline void VirtualDisplayBaseOutput::GetPixelRGB(VirtualDisplayPixel& pixel, | |
| 109 | unsigned char* channelData, unsigned char& r, unsigned char& g, unsigned char& b) { | ||
| 110 | // every path below sets all three; a combination none of them handles | ||
| 111 | // shows black rather than whatever the caller's variables held (the last | ||
| 112 | // pixel's colour, or uninitialised stack) | ||
| 113 | 1536543 | r = g = b = 0; | |
| 114 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1536543 times.
|
1536543 | if (pixel.vpc == kVPC_Custom) { |
| 115 | ✗ | r = pixel.customR; | |
| 116 | ✗ | g = pixel.customG; | |
| 117 | ✗ | b = pixel.customB; | |
| 118 |
2/2✓ Branch 0 taken 66 times.
✓ Branch 1 taken 1536477 times.
|
1536543 | } else if ((pixel.cpp == 3) || |
| 119 |
2/4✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
|
66 | ((pixel.cpp == 4) && (channelData[pixel.ch + 3] == 0))) { |
| 120 | // RGBW with W off shows its RGB part; xLights writes "RGBW" for | ||
| 121 | // every RGBW model | ||
| 122 |
3/4✓ Branch 0 taken 66 times.
✓ Branch 1 taken 1536477 times.
✓ Branch 2 taken 66 times.
✗ Branch 3 not taken.
|
1536543 | if (pixel.vpc == kVPC_RGB || pixel.vpc == kVPC_RGBW) { |
| 123 | 1536543 | r = channelData[pixel.ch]; | |
| 124 | 1536543 | g = channelData[pixel.ch + 1]; | |
| 125 | 1536543 | b = channelData[pixel.ch + 2]; | |
| 126 | ✗ | } else if (pixel.vpc == kVPC_RBG) { | |
| 127 | ✗ | r = channelData[pixel.ch]; | |
| 128 | ✗ | g = channelData[pixel.ch + 2]; | |
| 129 | ✗ | b = channelData[pixel.ch + 1]; | |
| 130 | ✗ | } else if (pixel.vpc == kVPC_GRB) { | |
| 131 | ✗ | r = channelData[pixel.ch + 1]; | |
| 132 | ✗ | g = channelData[pixel.ch]; | |
| 133 | ✗ | b = channelData[pixel.ch + 2]; | |
| 134 | ✗ | } else if (pixel.vpc == kVPC_GBR) { | |
| 135 | ✗ | r = channelData[pixel.ch + 2]; | |
| 136 | ✗ | g = channelData[pixel.ch]; | |
| 137 | ✗ | b = channelData[pixel.ch + 1]; | |
| 138 | ✗ | } else if (pixel.vpc == kVPC_BRG) { | |
| 139 | ✗ | r = channelData[pixel.ch + 1]; | |
| 140 | ✗ | g = channelData[pixel.ch + 2]; | |
| 141 | ✗ | b = channelData[pixel.ch]; | |
| 142 | ✗ | } else if (pixel.vpc == kVPC_BGR) { | |
| 143 | ✗ | r = channelData[pixel.ch + 2]; | |
| 144 | ✗ | g = channelData[pixel.ch + 1]; | |
| 145 | ✗ | b = channelData[pixel.ch]; | |
| 146 | } | ||
| 147 | ✗ | } else if (pixel.cpp == 4) { | |
| 148 | ✗ | r = channelData[pixel.ch + 3]; | |
| 149 | ✗ | g = channelData[pixel.ch + 3]; | |
| 150 | ✗ | b = channelData[pixel.ch + 3]; | |
| 151 | ✗ | } else if (pixel.cpp == 1) { | |
| 152 | ✗ | if (pixel.vpc == kVPC_Red) { | |
| 153 | ✗ | r = channelData[pixel.ch]; | |
| 154 | ✗ | g = 0; | |
| 155 | ✗ | b = 0; | |
| 156 | ✗ | } else if (pixel.vpc == kVPC_Green) { | |
| 157 | ✗ | r = 0; | |
| 158 | ✗ | g = channelData[pixel.ch]; | |
| 159 | ✗ | b = 0; | |
| 160 | ✗ | } else if (pixel.vpc == kVPC_Blue) { | |
| 161 | ✗ | r = 0; | |
| 162 | ✗ | g = 0; | |
| 163 | ✗ | b = channelData[pixel.ch]; | |
| 164 | ✗ | } else if (pixel.vpc == kVPC_White) { | |
| 165 | ✗ | r = channelData[pixel.ch]; | |
| 166 | ✗ | g = channelData[pixel.ch]; | |
| 167 | ✗ | b = channelData[pixel.ch]; | |
| 168 | } | ||
| 169 | } | ||
| 170 | 1536543 | } | |
| 171 |