| 945 | } |
| 946 | |
| 947 | void lookup_lut(const LutTable& lut, const float xy_t[2], float Y_t, |
| 948 | float out_rgbw[4]) FL_NOEXCEPT { |
| 949 | // Defend against degenerate LUTs (empty table, unbuilt cells, zero span). |
| 950 | // build_lut() returns an empty LutTable on bad input — without these |
| 951 | // guards the divide-by-zero and OOB reads would corrupt random memory. |
| 952 | out_rgbw[0] = out_rgbw[1] = out_rgbw[2] = out_rgbw[3] = 0.0f; |
| 953 | if (lut.N < 2 || lut.cells.get() == nullptr) { |
| 954 | return; |
| 955 | } |
| 956 | const int N = lut.N; |
| 957 | const float dx = lut.xy_max[0] - lut.xy_min[0]; |
| 958 | const float dy = lut.xy_max[1] - lut.xy_min[1]; |
| 959 | if (dx <= 0.0f || dy <= 0.0f) { |
| 960 | return; |
| 961 | } |
| 962 | float x_norm = (xy_t[0] - lut.xy_min[0]) / dx * (N - 1); |
| 963 | float y_norm = (xy_t[1] - lut.xy_min[1]) / dy * (N - 1); |
| 964 | x_norm = fl::clamp(x_norm, 0.0f, static_cast<float>(N - 1) - 1e-4f); |
| 965 | y_norm = fl::clamp(y_norm, 0.0f, static_cast<float>(N - 1) - 1e-4f); |
| 966 | const int i = static_cast<int>(x_norm); |
| 967 | const int j = static_cast<int>(y_norm); |
| 968 | const float fx = x_norm - i; |
| 969 | const float fy = y_norm - j; |
| 970 | |
| 971 | const i16* base = lut.cells.get(); |
| 972 | const int stride = |
| 973 | (lut.interp == LutInterp::Hermite) ? kLutStrideHermite : kLutStrideBilinear; |
| 974 | const i16* c00 = &base[(j * N + i) * stride]; |
| 975 | const i16* c10 = &base[(j * N + i + 1) * stride]; |
| 976 | const i16* c01 = &base[((j + 1) * N + i) * stride]; |
| 977 | const i16* c11 = &base[((j + 1) * N + i + 1) * stride]; |
| 978 | |
| 979 | const float inv_Q = 1.0f / static_cast<float>(kLutQ); |
| 980 | |
| 981 | if (lut.interp == LutInterp::Hermite) { |
| 982 | // Bicubic Hermite with no fxy cross term. Basis is computed via the |
| 983 | // shared header helper `hermite_basis` so the test suite exercises the |
| 984 | // same evaluator (CodeRabbit #2707). |
| 985 | float bx[4], by[4]; |
| 986 | hermite_basis(fx, bx); |
| 987 | hermite_basis(fy, by); |
| 988 | const float h00x = bx[0], h01x = bx[1], h10x = bx[2], h11x = bx[3]; |
| 989 | const float h00y = by[0], h01y = by[1], h10y = by[2], h11y = by[3]; |
| 990 | |
| 991 | for (int k = 0; k < 4; ++k) { |
| 992 | const float v00 = c00[k] * inv_Q; |
| 993 | const float v10 = c10[k] * inv_Q; |
| 994 | const float v01 = c01[k] * inv_Q; |
| 995 | const float v11 = c11[k] * inv_Q; |
| 996 | const float dx00 = c00[4 + k] * inv_Q; |
| 997 | const float dx10 = c10[4 + k] * inv_Q; |
| 998 | const float dx01 = c01[4 + k] * inv_Q; |
| 999 | const float dx11 = c11[4 + k] * inv_Q; |
| 1000 | const float dy00 = c00[8 + k] * inv_Q; |
| 1001 | const float dy10 = c10[8 + k] * inv_Q; |
| 1002 | const float dy01 = c01[8 + k] * inv_Q; |
| 1003 | const float dy11 = c11[8 + k] * inv_Q; |
| 1004 |
no test coverage detected