1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
struct LUTCoord {
union {
struct {
uint8_t redIndex;
uint8_t greenIndex;
uint8_t blueIndex;
};
uint8_t raw[3];
};
inline LUTCoord() __attribute__((always_inline))
{
}
inline LUTCoord( uint8_t ir, uint8_t ig, uint8_t ib) __attribute__((always_inline))
: redIndex(ir), greenIndex(ig), blueIndex(ib)
{
}
};
/// 8 vertices of a cube
struct LUTSubcube {
CRGB n000;
CRGB n001;
CRGB n010;
CRGB n011;
CRGB n100;
CRGB n101;
CRGB n110;
CRGB n111;
};
/// This 3D LUT can be used provided that the space between entries is the same for all dimensions.
class Array3DLUT {
public:
CRGB *entries;
uint8_t size;
float step;
uint8_t maxSubcubeIndex;
Array3DLUT(CRGB *entries, uint8_t size);
virtual ~Array3DLUT();
/// Retrieve an entry in the table.
/// @param indexes - 3D index of entry
/// @returns Entry value
CRGB lookup(LUTCoord indexes);
/// Returns the values of the 8 vertices of the target sub-cube.
/// The given coordinates must be those of the lowest point.
/// @param subcubeOriginCoord - lowest point coordinates of the desired subcube
/// @returns Each (8) subcube values
struct LUTSubcube lookup_subcube(LUTCoord subcubeOriginCoord);
/// Similar to lookup but instead of a coordinate in the LUT,
/// we pass a point and the result will be interpolated.
/// @param input - RGB coordinates of a point in the LUT
/// @returns Interpolated value in the LUT
struct CRGB lookup3DTetrahedral(CRGB &input);
};
Array3DLUT::Array3DLUT(CRGB *entries, uint8_t size) {
this->entries = entries;
this->size = size;
this->step = 255. / (size - 1);
this->maxSubcubeIndex = size - 2;
}
Array3DLUT::~Array3DLUT() {
// TODO Auto-generated destructor stub
}
CRGB Array3DLUT::lookup(LUTCoord indexes) {
uint8_t offset =
(indexes.redIndex * this->size * this->size)
+ (indexes.greenIndex * this->size)
+ indexes.blueIndex;
return this->entries[offset];
}
struct LUTSubcube Array3DLUT::lookup_subcube(LUTCoord subcubeOriginCoord) {
struct LUTSubcube result;
result.n000 = this->lookup(subcubeOriginCoord);
result.n001 = this->lookup(LUTCoord(subcubeOriginCoord.redIndex, subcubeOriginCoord.greenIndex, subcubeOriginCoord.blueIndex+1));
result.n010 = this->lookup(LUTCoord(subcubeOriginCoord.redIndex, subcubeOriginCoord.greenIndex+1, subcubeOriginCoord.blueIndex));
result.n011 = this->lookup(LUTCoord(subcubeOriginCoord.redIndex, subcubeOriginCoord.greenIndex+1, subcubeOriginCoord.blueIndex+1));
result.n100 = this->lookup(LUTCoord(subcubeOriginCoord.redIndex+1, subcubeOriginCoord.greenIndex, subcubeOriginCoord.blueIndex));
result.n101 = this->lookup(LUTCoord(subcubeOriginCoord.redIndex+1, subcubeOriginCoord.greenIndex, subcubeOriginCoord.blueIndex+1));
result.n110 = this->lookup(LUTCoord(subcubeOriginCoord.redIndex+1, subcubeOriginCoord.greenIndex+1, subcubeOriginCoord.blueIndex));
result.n111 = this->lookup(LUTCoord(subcubeOriginCoord.redIndex+1, subcubeOriginCoord.greenIndex+1, subcubeOriginCoord.blueIndex+1));
return result;
}
// Ref implem: https://github.com/ampas/CLF/blob/master/python/aces/clf/Array.py
struct CRGB Array3DLUT::lookup3DTetrahedral(CRGB &input) {
LUTCoord subcubeOrigin; // Store the subcube's origin coordinates
float dimensionalDistances[3]; // Distance for each dimensions
for (int i = 0; i < 3; i++) { // for each rgb channel
dimensionalDistances[i] = input.raw[i] / this->step; // Not yet valid. We should remove int part
subcubeOrigin.raw[i] = uint8_t(dimensionalDistances[i]); // we put a guessed coordinate
if (subcubeOrigin.raw[i] > maxSubcubeIndex) {
// But, if we are on an upper border then we must take the
// inner border or the subcube will not exist.
subcubeOrigin.raw[i] = maxSubcubeIndex;
}
dimensionalDistances[i] -= subcubeOrigin.raw[i]; // Remove the int part
}
// Rebind for consistency with Truelight paper
#define fx dimensionalDistances[0]
#define fy dimensionalDistances[1]
#define fz dimensionalDistances[2]
struct CRGB result;
struct LUTSubcube cube = this->lookup_subcube(subcubeOrigin);
if (fx > fy) {
if (fy > fz) {
for (int i=0; i < 3; i++) {
result.raw[i] = (
(1-fx) * cube.n000.raw[i] +
(fx-fy) * cube.n100.raw[i] +
(fy-fz) * cube.n110.raw[i] +
(fz) * cube.n111.raw[i] );
}
} else {
if (fx > fz) {
for (int i=0; i < 3; i++) {
result.raw[i] = (
(1-fx) * cube.n000.raw[i] +
(fx-fz) * cube.n100.raw[i] +
(fz-fy) * cube.n101.raw[i] +
(fy) * cube.n111.raw[i] );
}
} else {
for (int i=0; i < 3; i++) {
result.raw[i] = (
(1-fz) * cube.n000.raw[i] +
(fz-fx) * cube.n001.raw[i] +
(fx-fy) * cube.n101.raw[i] +
(fy) * cube.n111.raw[i] );
}
}
}
} else {
if (fz > fy) {
for (int i=0; i < 3; i++) {
result.raw[i] = (
(1-fz) * cube.n000.raw[i] +
(fz-fy) * cube.n001.raw[i] +
(fy-fx) * cube.n011.raw[i] +
(fx) * cube.n111.raw[i] );
}
} else {
if (fz > fx) {
for (int i=0; i < 3; i++) {
result.raw[i] = (
(1-fy) * cube.n000.raw[i] +
(fy-fz) * cube.n010.raw[i] +
(fz-fx) * cube.n011.raw[i] +
(fx) * cube.n111.raw[i] );
}
} else {
for (int i=0; i < 3; i++) {
result.raw[i] = (
(1-fy) * cube.n000.raw[i] +
(fy-fx) * cube.n010.raw[i] +
(fx-fz) * cube.n011.raw[i] +
(fz) * cube.n111.raw[i] );
}
}
}
}
return result;
} |
Partager