36 if (a == 0 || b == 0) {
39 const i64 lhs = a < 0 ? -a : a;
40 const i64 rhs = b < 0 ? -b : b;
68 const i64 left =
static_cast<i64>(e) *
static_cast<i64>(i_);
69 const i64 right =
static_cast<i64>(f) *
static_cast<i64>(h);
82 if (denominator < 0) {
83 numerator = -numerator;
84 denominator = -denominator;
86 const i64 half = denominator / 2;
88 return (numerator + half) / denominator;
90 return -((-numerator + half) / denominator);
113 i64 numerator = cofactor_q32;
114 i64 denominator = determinant_q48;
117 if (denominator < 0) {
118 numerator = -numerator;
119 denominator = -denominator;
122 for (
int spent = 0; spent < 32; ++spent) {
123 const i64 magnitude = numerator < 0 ? -numerator : numerator;
124 if (magnitude <= kHalfMax) {
127 denominator = (denominator + 1) / 2;
138 if (denominator < 0) {
139 numerator = -numerator;
140 denominator = -denominator;
142 const i64 half = denominator / 2;
143 if (numerator >= 0) {
144 return (numerator + half) / denominator;
146 return -((-numerator + half) / denominator);
165 if (
y <= 0 || luminance <= 0 ||
x <= 0) {
177 column[1] = luminance;
179 for (
int i = 0; i < 3; ++i) {
182 if (column[i] > 2147483647LL || column[i] < -2147483648LL) {
190 const i64 acc =
static_cast<i64>(row[0]) *
static_cast<i64>(v[0])
191 +
static_cast<i64>(row[1]) *
static_cast<i64>(v[1])
192 +
static_cast<i64>(row[2]) *
static_cast<i64>(v[2]);
193 const i64 rounded = acc >= 0 ? (acc + 32768) >> 16
194 : -((-acc + 32768) >> 16);
199 constexpr i64 kMax = 2147483647;
200 constexpr i64 kMin = -2147483647 - 1;
201 if (rounded > kMax) {
202 return static_cast<i32
>(kMax);
204 if (rounded < kMin) {
205 return static_cast<i32
>(kMin);
207 return static_cast<i32
>(rounded);
213 const i32 a = in[0][0], b = in[0][1], c = in[0][2];
214 const i32 d = in[1][0], e = in[1][1], f = in[1][2];
215 const i32
g = in[2][0], h = in[2][1], i_ = in[2][2];
219 if (!cofactorQ32(e, i_, f, h, &cof[0][0]) ||
220 !cofactorQ32(c, h, b, i_, &cof[0][1]) ||
221 !cofactorQ32(b, f, c, e, &cof[0][2]) ||
222 !cofactorQ32(f,
g, d, i_, &cof[1][0]) ||
223 !cofactorQ32(a, i_, c,
g, &cof[1][1]) ||
224 !cofactorQ32(c, d, a, f, &cof[1][2]) ||
225 !cofactorQ32(d, h, e,
g, &cof[2][0]) ||
226 !cofactorQ32(b,
g, a, h, &cof[2][1]) ||
227 !cofactorQ32(a, e, b, d, &cof[2][2])) {
232 const i32 first_row[3] = {a, b, c};
234 for (
int col = 0; col < 3; ++col) {
235 const i64 term_lhs =
static_cast<i64>(first_row[col]);
236 if (!productFitsI64(term_lhs, cof[col][0])) {
239 const i64 term = term_lhs * cof[col][0];
240 if (!sumFitsI64(determinant, term)) {
245 if (determinant == 0) {
249 for (
int row = 0; row < 3; ++row) {
250 for (
int col = 0; col < 3; ++col) {
251 const i64 value = divideCoefficientQ16(cof[row][col], determinant);
255 const i64 limit =
static_cast<i64>(kMaxCoefficient) << 16;
259 out[row][col] =
static_cast<i32
>(
value);
267 if (out ==
nullptr) {
273 if (!emitterColumnQ16(profile.xy_r, profile.lum_r, red) ||
274 !emitterColumnQ16(profile.xy_g, profile.lum_g, green) ||
275 !emitterColumnQ16(profile.xy_b, profile.lum_b, blue)) {
281 const i32 emitter[3][3] = {
282 {
static_cast<i32
>(red[0]),
static_cast<i32
>(green[0]),
283 static_cast<i32
>(blue[0])},
284 {
static_cast<i32
>(red[1]),
static_cast<i32
>(green[1]),
285 static_cast<i32
>(blue[1])},
286 {
static_cast<i32
>(red[2]),
static_cast<i32
>(green[2]),
287 static_cast<i32
>(blue[2])},
293 if (out ==
nullptr) {
301 const bool negative = (bits >> 31) != 0;
302 const i32 exponent =
static_cast<i32
>((bits >> 23) & 0xFFu);
303 const u32 fraction = bits & 0x7FFFFFu;
304 if (exponent == 0xFF) {
313 const u64 mantissa =
static_cast<u64>(fraction | 0x800000u);
314 const i32 shift = exponent - 134;
320 magnitude = mantissa << shift;
322 const i32 right = -shift;
328 magnitude = (mantissa + (
static_cast<u64>(1) << (right - 1))) >> right;
331 if (magnitude > 0x7FFFFFFFull) {
334 *out = negative ? -
static_cast<i32
>(magnitude) :
static_cast<i32
>(magnitude);
341 return ::fl::roundedDivideQ16(numerator, denominator);
346 return emitterColumnQ16(
xy, luminance, column);
350 return dotSolveRowQ16(row, v);
357 if (out ==
nullptr) {
382 const i32 in[3] = {xyz[0], xyz[1], xyz[2]};
383 drives[0] = dotSolveRowQ16(matrix.
m[0], in);
384 drives[1] = dotSolveRowQ16(matrix.
m[1], in);
385 drives[2] = dotSolveRowQ16(matrix.
m[2], in);
unsigned int xy(unsigned int x, unsigned int y)
bool cofactorQ32(i32 e, i32 i_, i32 f, i32 h, i64 *out) FL_NO_EXCEPT
e * i - f * h, or false if any part of it leaves i64.
bool emitterColumnQ16(const i32(&xy)[2], i32 luminance, i64(&column)[3]) FL_NO_EXCEPT
One emitter's XYZ column at its own luminance, all in s16.16.
i64 roundedDivideQ16(i64 numerator, i64 denominator) FL_NO_EXCEPT
Nearest integer of numerator / denominator, halves away from zero.
bool sumFitsI64(i64 a, i64 b) FL_NO_EXCEPT
Does a + b fit an i64, for operands already known to be in range?
bool productFitsI64(i64 a, i64 b) FL_NO_EXCEPT
Does a * b fit an i64?
i64 roundedDivI64(i64 numerator, i64 denominator) FL_NO_EXCEPT
Nearest integer, halves away from zero.
i32 dotSolveRowQ16(const i32(&row)[3], const i32(&v)[3]) FL_NO_EXCEPT
i64 divideCoefficientQ16(i64 cofactor_q32, i64 determinant_q48) FL_NO_EXCEPT
round(cofactor_q32 * 2^32 / determinant_q48), in s16.16, without a 128-bit intermediate.
constexpr i64 kQ16One
One, in s16.16.
constexpr i32 kMaxCoefficient
Largest coefficient magnitude the build accepts, in whole units.
i32 dotRowQ16(const i32(&row)[3], const i32(&v)[3]) FL_NO_EXCEPT
One s16.16 matrix row against a vector, rounded and saturated.
i64 roundedDivideQ16(i64 numerator, i64 denominator) FL_NO_EXCEPT
The s16.16 building blocks the bind-time builders share.
bool xyzColumnQ16(const i32(&xy)[2], i32 luminance, i64(&column)[3]) FL_NO_EXCEPT
XYZ of chromaticity xy at luminance luminance, all s16.16; false for a non-positive or out-of-simplex...
bool buildRgbSolveMatrixFromQ16(const EmitterChromaticitiesQ16 &profile, EmitterSolveMatrixQ16 *out) FL_NO_EXCEPT
Build the inverse emitter matrix from a Q16 profile, without floats.
constexpr int type_rank< T >::value
InputGamut g FL_NO_EXCEPT
To bit_cast(const From &from) FL_NO_EXCEPT
bool invert3x3Q16(const i32(&in)[3][3], i32(&out)[3][3]) FL_NO_EXCEPT
Inverse of an s16.16 3x3 matrix, in s16.16, computed without floats.
bool buildRgbSolveMatrixQ16(const colorimetric_response::EmitterProfile &profile, EmitterSolveMatrixQ16 *out) FL_NO_EXCEPT
Invert the emitter matrix for a three-emitter profile.
bool q16FromFloatBits(float value, i32 *out) FL_NO_EXCEPT
s16.16 of a float, rounded to nearest, computed from its IEEE-754 bits with integer arithmetic only –...
void solveRgbDrivesQ16(const EmitterSolveMatrixQ16 &matrix, const i32(&xyz)[3], i32(&drives)[3]) FL_NO_EXCEPT
One pixel: XYZ in s16.16 to three emitter drives in s16.16.
Base definition for an LED controller.
Inverse emitter matrix in s16.16, mapping XYZ to three emitter drives.
A three-emitter profile with every field in s16.16.