diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
commit | fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch) | |
tree | 4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /third_party/jpeg-xl/lib/jxl/cms | |
parent | Releasing progress-linux version 124.0.1-1~progress7.99u1. (diff) | |
download | firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip |
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/jpeg-xl/lib/jxl/cms')
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h | 4 | ||||
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc | 95 | ||||
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h | 179 | ||||
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/cms/opsin_params.h | 27 | ||||
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h | 6 | ||||
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h | 52 | ||||
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc | 30 |
7 files changed, 215 insertions, 178 deletions
diff --git a/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h b/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h index db61f820ca..3b7ae09d6f 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h +++ b/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h @@ -96,6 +96,8 @@ enum class RenderingIntent : uint32_t { // Chromaticity (Y is omitted because it is 1 for white points and implicit for // primaries) struct CIExy { + CIExy() = default; + CIExy(double x, double y) : x(x), y(y) {} double x = 0.0; double y = 0.0; }; @@ -516,7 +518,7 @@ struct ColorEncoding { JXL_RETURN_IF_ERROR(cms.set_fields_from_icc(cms.set_fields_data, new_icc.data(), new_icc.size(), &external, &new_cmyk)); - cmyk = new_cmyk; + cmyk = static_cast<bool>(new_cmyk); JXL_RETURN_IF_ERROR(FromExternal(external)); icc = std::move(new_icc); return true; diff --git a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc index dd00b8b81f..e35b3ce172 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc +++ b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc @@ -307,7 +307,7 @@ int DoColorSpaceTransform(void* t, size_t thread, const float* buf_src, #if JPEGXL_ENABLE_SKCMS -JXL_MUST_USE_RESULT CIExy CIExyFromXYZ(const float XYZ[3]) { +JXL_MUST_USE_RESULT CIExy CIExyFromXYZ(const Color& XYZ) { const float factor = 1.f / (XYZ[0] + XYZ[1] + XYZ[2]); CIExy xy; xy.x = XYZ[0] * factor; @@ -405,8 +405,8 @@ ColorSpace ColorSpaceFromProfile(const skcms_ICCProfile& profile) { } // vector_out := matmul(matrix, vector_in) -void MatrixProduct(const skcms_Matrix3x3& matrix, const float vector_in[3], - float vector_out[3]) { +void MatrixProduct(const skcms_Matrix3x3& matrix, const Color& vector_in, + Color& vector_out) { for (int i = 0; i < 3; ++i) { vector_out[i] = 0; for (int j = 0; j < 3; ++j) { @@ -418,8 +418,8 @@ void MatrixProduct(const skcms_Matrix3x3& matrix, const float vector_in[3], // Returns white point that was specified when creating the profile. JXL_MUST_USE_RESULT Status UnadaptedWhitePoint(const skcms_ICCProfile& profile, CIExy* out) { - float media_white_point_XYZ[3]; - if (!skcms_GetWTPT(&profile, media_white_point_XYZ)) { + Color media_white_point_XYZ; + if (!skcms_GetWTPT(&profile, media_white_point_XYZ.data())) { return JXL_FAILURE("ICC profile does not contain WhitePoint tag"); } skcms_Matrix3x3 CHAD; @@ -435,7 +435,7 @@ JXL_MUST_USE_RESULT Status UnadaptedWhitePoint(const skcms_ICCProfile& profile, if (!skcms_Matrix3x3_invert(&CHAD, &inverse_CHAD)) { return JXL_FAILURE("Non-invertible ChromaticAdaptation matrix"); } - float unadapted_white_point_XYZ[3]; + Color unadapted_white_point_XYZ; MatrixProduct(inverse_CHAD, media_white_point_XYZ, unadapted_white_point_XYZ); *out = CIExyFromXYZ(unadapted_white_point_XYZ); return true; @@ -445,7 +445,8 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile, const CIExy& wp_unadapted, ColorEncoding* c) { if (!c->HasPrimaries()) return true; - skcms_Matrix3x3 CHAD, inverse_CHAD; + skcms_Matrix3x3 CHAD; + skcms_Matrix3x3 inverse_CHAD; if (skcms_GetCHAD(&profile, &CHAD)) { JXL_RETURN_IF_ERROR(skcms_Matrix3x3_invert(&CHAD, &inverse_CHAD)); } else { @@ -457,11 +458,12 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile, {{0.9869929, -0.1470543, 0.1599627}, {0.4323053, 0.5183603, 0.0492912}, {-0.0085287, 0.0400428, 0.9684867}}}; - static constexpr float kWpD50XYZ[3] = {0.96420288, 1.0, 0.82490540}; - float wp_unadapted_XYZ[3]; + static constexpr Color kWpD50XYZ{0.96420288, 1.0, 0.82490540}; + Color wp_unadapted_XYZ; JXL_RETURN_IF_ERROR( CIEXYZFromWhiteCIExy(wp_unadapted.x, wp_unadapted.y, wp_unadapted_XYZ)); - float wp_D50_LMS[3], wp_unadapted_LMS[3]; + Color wp_D50_LMS; + Color wp_unadapted_LMS; MatrixProduct(kLMSFromXYZ, kWpD50XYZ, wp_D50_LMS); MatrixProduct(kLMSFromXYZ, wp_unadapted_XYZ, wp_unadapted_LMS); inverse_CHAD = {{{wp_unadapted_LMS[0] / wp_D50_LMS[0], 0, 0}, @@ -471,16 +473,16 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile, inverse_CHAD = skcms_Matrix3x3_concat(&inverse_CHAD, &kLMSFromXYZ); } - float XYZ[3]; + Color XYZ; PrimariesCIExy primaries; CIExy* const chromaticities[] = {&primaries.r, &primaries.g, &primaries.b}; for (int i = 0; i < 3; ++i) { float RGB[3] = {}; RGB[i] = 1; skcms_Transform(RGB, skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Opaque, - &profile, XYZ, skcms_PixelFormat_RGB_fff, + &profile, XYZ.data(), skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Opaque, skcms_XYZD50_profile(), 1); - float unadapted_XYZ[3]; + Color unadapted_XYZ; MatrixProduct(inverse_CHAD, XYZ, unadapted_XYZ); *chromaticities[i] = CIExyFromXYZ(unadapted_XYZ); } @@ -829,17 +831,17 @@ Status GetPrimariesLuminances(const ColorEncoding& encoding, // From there, by multiplying each total by its corresponding y, we get Y for // that primary. - float white_XYZ[3]; + Color white_XYZ; CIExy wp = encoding.GetWhitePoint(); JXL_RETURN_IF_ERROR(CIEXYZFromWhiteCIExy(wp.x, wp.y, white_XYZ)); const PrimariesCIExy primaries = encoding.GetPrimaries(); - double chromaticities[3][3] = { - {primaries.r.x, primaries.g.x, primaries.b.x}, - {primaries.r.y, primaries.g.y, primaries.b.y}, - {1 - primaries.r.x - primaries.r.y, 1 - primaries.g.x - primaries.g.y, - 1 - primaries.b.x - primaries.b.y}}; - JXL_RETURN_IF_ERROR(Inv3x3Matrix(&chromaticities[0][0])); + Matrix3x3d chromaticities{ + {{primaries.r.x, primaries.g.x, primaries.b.x}, + {primaries.r.y, primaries.g.y, primaries.b.y}, + {1 - primaries.r.x - primaries.r.y, 1 - primaries.g.x - primaries.g.y, + 1 - primaries.b.x - primaries.b.y}}}; + JXL_RETURN_IF_ERROR(Inv3x3Matrix(chromaticities)); const double ys[3] = {primaries.r.y, primaries.g.y, primaries.b.y}; for (size_t i = 0; i < 3; ++i) { luminances[i] = ys[i] * (chromaticities[i][0] * white_XYZ[0] + @@ -971,14 +973,31 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data, JXL_RETURN_IF_ERROR(skcms_Parse(icc_data, icc_size, &profile)); // skcms does not return the rendering intent, so get it from the file. It - // is encoded as big-endian 32-bit integer in bytes 60..63. - uint32_t rendering_intent32 = icc_data[67]; - if (rendering_intent32 > 3 || icc_data[64] != 0 || icc_data[65] != 0 || - icc_data[66] != 0) { - return JXL_FAILURE("Invalid rendering intent %u\n", rendering_intent32); + // should be encoded as big-endian 32-bit integer in bytes 60..63. + uint32_t big_endian_rendering_intent = icc_data[67] + (icc_data[66] << 8) + + (icc_data[65] << 16) + + (icc_data[64] << 24); + // Some files encode rendering intent as little endian, which is not spec + // compliant. However we accept those with a warning. + uint32_t little_endian_rendering_intent = (icc_data[67] << 24) + + (icc_data[66] << 16) + + (icc_data[65] << 8) + icc_data[64]; + uint32_t candidate_rendering_intent = + std::min(big_endian_rendering_intent, little_endian_rendering_intent); + if (candidate_rendering_intent != big_endian_rendering_intent) { + JXL_WARNING( + "Invalid rendering intent bytes: [0x%02X 0x%02X 0x%02X 0x%02X], " + "assuming %u was meant", + icc_data[64], icc_data[65], icc_data[66], icc_data[67], + candidate_rendering_intent); + } + if (candidate_rendering_intent > 3) { + return JXL_FAILURE("Invalid rendering intent %u\n", + candidate_rendering_intent); } // ICC and RenderingIntent have the same values (0..3). - c_enc.rendering_intent = static_cast<RenderingIntent>(rendering_intent32); + c_enc.rendering_intent = + static_cast<RenderingIntent>(candidate_rendering_intent); if (profile.has_CICP && ApplyCICP(profile.CICP.color_primaries, @@ -986,11 +1005,11 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data, profile.CICP.matrix_coefficients, profile.CICP.video_full_range_flag, &c_enc)) { *c = c_enc.ToExternal(); - return true; + return JXL_TRUE; } c_enc.color_space = ColorSpaceFromProfile(profile); - *cmyk = (profile.data_color_space == skcms_Signature_CMYK); + *cmyk = TO_JXL_BOOL(profile.data_color_space == skcms_Signature_CMYK); CIExy wp_unadapted; JXL_RETURN_IF_ERROR(UnadaptedWhitePoint(profile, &wp_unadapted)); @@ -1026,14 +1045,14 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data, ApplyCICP(cicp_buffer[8], cicp_buffer[9], cicp_buffer[10], cicp_buffer[11], &c_enc)) { *c = c_enc.ToExternal(); - return true; + return JXL_TRUE; } c_enc.color_space = ColorSpaceFromProfile(profile); if (cmsGetColorSpace(profile.get()) == cmsSigCmykData) { *cmyk = JXL_TRUE; *c = c_enc.ToExternal(); - return true; + return JXL_TRUE; } const cmsCIEXYZ wp_unadapted = UnadaptedWhitePoint(context, profile, c_enc); @@ -1049,7 +1068,7 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data, #endif // JPEGXL_ENABLE_SKCMS *c = c_enc.ToExternal(); - return true; + return JXL_TRUE; } } // namespace @@ -1084,9 +1103,10 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize, const JxlColorProfile* input, const JxlColorProfile* output, float intensity_target) { JXL_ASSERT(init_data != nullptr); - auto cms = static_cast<const JxlCmsInterface*>(init_data); + const auto* cms = static_cast<const JxlCmsInterface*>(init_data); auto t = jxl::make_unique<JxlCms>(); - IccBytes icc_src, icc_dst; + IccBytes icc_src; + IccBytes icc_dst; if (input->icc.size == 0) { JXL_NOTIFY_ERROR("JxlCmsInit: empty input ICC"); return nullptr; @@ -1259,8 +1279,6 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize, // Not including alpha channel (copied separately). const size_t channels_src = (c_src.cmyk ? 4 : c_src.Channels()); const size_t channels_dst = c_dst.Channels(); - JXL_CHECK(channels_src == channels_dst || - (channels_src == 4 && channels_dst == 3)); #if JXL_CMS_VERBOSE printf("Channels: %" PRIuS "; Threads: %" PRIuS "\n", channels_src, num_threads); @@ -1294,13 +1312,14 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize, // outputs (or vice versa), we use floating point input/output. t->channels_src = channels_src; t->channels_dst = channels_dst; +#if !JPEGXL_ENABLE_SKCMS size_t actual_channels_src = channels_src; size_t actual_channels_dst = channels_dst; -#if JPEGXL_ENABLE_SKCMS +#else // SkiaCMS doesn't support grayscale float buffers, so we create space for RGB // float buffers anyway. - actual_channels_src = (channels_src == 4 ? 4 : 3); - actual_channels_dst = 3; + size_t actual_channels_src = (channels_src == 4 ? 4 : 3); + size_t actual_channels_dst = 3; #endif AllocateBuffer(xsize * actual_channels_src, num_threads, &t->src_storage, &t->buf_src); diff --git a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h index c00fe82d8c..7f59e688d0 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h +++ b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h @@ -41,7 +41,7 @@ enum class ExtraTF { }; static Status PrimariesToXYZ(float rx, float ry, float gx, float gy, float bx, - float by, float wx, float wy, float matrix[9]) { + float by, float wx, float wy, Matrix3x3& matrix) { bool ok = (wx >= 0) && (wx <= 1) && (wy > 0) && (wy <= 1); if (!ok) { return JXL_FAILURE("Invalid white point"); @@ -49,51 +49,48 @@ static Status PrimariesToXYZ(float rx, float ry, float gx, float gy, float bx, // TODO(lode): also require rx, ry, gx, gy, bx, to be in range 0-1? ICC // profiles in theory forbid negative XYZ values, but in practice the ACES P0 // color space uses a negative y for the blue primary. - float primaries[9] = { - rx, gx, bx, ry, gy, by, 1.0f - rx - ry, 1.0f - gx - gy, 1.0f - bx - by}; - float primaries_inv[9]; - memcpy(primaries_inv, primaries, sizeof(float) * 9); + Matrix3x3 primaries{{{rx, gx, bx}, + {ry, gy, by}, + {1.0f - rx - ry, 1.0f - gx - gy, 1.0f - bx - by}}}; + Matrix3x3 primaries_inv; + primaries_inv = primaries; JXL_RETURN_IF_ERROR(Inv3x3Matrix(primaries_inv)); - float w[3] = {wx / wy, 1.0f, (1.0f - wx - wy) / wy}; + Vector3 w{wx / wy, 1.0f, (1.0f - wx - wy) / wy}; // 1 / tiny float can still overflow JXL_RETURN_IF_ERROR(std::isfinite(w[0]) && std::isfinite(w[2])); - float xyz[3]; + Vector3 xyz; Mul3x3Vector(primaries_inv, w, xyz); - float a[9] = { - xyz[0], 0, 0, 0, xyz[1], 0, 0, 0, xyz[2], - }; + Matrix3x3 a{{{xyz[0], 0, 0}, {0, xyz[1], 0}, {0, 0, xyz[2]}}}; Mul3x3Matrix(primaries, a, matrix); return true; } /* Chromatic adaptation matrices*/ -constexpr float kBradford[9] = { - 0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f, - 0.0367f, 0.0389f, -0.0685f, 1.0296f, -}; -constexpr float kBradfordInv[9] = { - 0.9869929f, -0.1470543f, 0.1599627f, 0.4323053f, 0.5183603f, - 0.0492912f, -0.0085287f, 0.0400428f, 0.9684867f, -}; +constexpr Matrix3x3 kBradford{{{0.8951f, 0.2664f, -0.1614f}, + {-0.7502f, 1.7135f, 0.0367f}, + {0.0389f, -0.0685f, 1.0296f}}}; +constexpr Matrix3x3 kBradfordInv{{{0.9869929f, -0.1470543f, 0.1599627f}, + {0.4323053f, 0.5183603f, 0.0492912f}, + {-0.0085287f, 0.0400428f, 0.9684867f}}}; // Adapts whitepoint x, y to D50 -static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) { +static Status AdaptToXYZD50(float wx, float wy, Matrix3x3& matrix) { bool ok = (wx >= 0) && (wx <= 1) && (wy > 0) && (wy <= 1); if (!ok) { // Out of range values can cause division through zero // further down with the bradford adaptation too. return JXL_FAILURE("Invalid white point"); } - float w[3] = {wx / wy, 1.0f, (1.0f - wx - wy) / wy}; + Vector3 w{wx / wy, 1.0f, (1.0f - wx - wy) / wy}; // 1 / tiny float can still overflow JXL_RETURN_IF_ERROR(std::isfinite(w[0]) && std::isfinite(w[2])); - float w50[3] = {0.96422f, 1.0f, 0.82521f}; + Vector3 w50{0.96422f, 1.0f, 0.82521f}; - float lms[3]; - float lms50[3]; + Vector3 lms; + Vector3 lms50; Mul3x3Vector(kBradford, w, lms); Mul3x3Vector(kBradford, w50, lms50); @@ -101,15 +98,15 @@ static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) { if (lms[0] == 0 || lms[1] == 0 || lms[2] == 0) { return JXL_FAILURE("Invalid white point"); } - float a[9] = { - // /----> 0, 1, 2, 3, /----> 4, 5, 6, 7, /----> 8, - lms50[0] / lms[0], 0, 0, 0, lms50[1] / lms[1], 0, 0, 0, lms50[2] / lms[2], - }; - if (!std::isfinite(a[0]) || !std::isfinite(a[4]) || !std::isfinite(a[8])) { + Matrix3x3 a{{{lms50[0] / lms[0], 0, 0}, + {0, lms50[1] / lms[1], 0}, + {0, 0, lms50[2] / lms[2]}}}; + if (!std::isfinite(a[0][0]) || !std::isfinite(a[1][1]) || + !std::isfinite(a[2][2])) { return JXL_FAILURE("Invalid white point"); } - float b[9]; + Matrix3x3 b; Mul3x3Matrix(a, kBradford, b); Mul3x3Matrix(kBradfordInv, b, matrix); @@ -118,10 +115,10 @@ static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) { static Status PrimariesToXYZD50(float rx, float ry, float gx, float gy, float bx, float by, float wx, float wy, - float matrix[9]) { - float toXYZ[9]; + Matrix3x3& matrix) { + Matrix3x3 toXYZ; JXL_RETURN_IF_ERROR(PrimariesToXYZ(rx, ry, gx, gy, bx, by, wx, wy, toXYZ)); - float d50[9]; + Matrix3x3 d50; JXL_RETURN_IF_ERROR(AdaptToXYZD50(wx, wy, d50)); Mul3x3Matrix(d50, toXYZ, matrix); @@ -130,14 +127,13 @@ static Status PrimariesToXYZD50(float rx, float ry, float gx, float gy, static Status ToneMapPixel(const JxlColorEncoding& c, const float in[3], uint8_t pcslab_out[3]) { - float primaries_XYZ[9]; + Matrix3x3 primaries_XYZ; JXL_RETURN_IF_ERROR(PrimariesToXYZ( c.primaries_red_xy[0], c.primaries_red_xy[1], c.primaries_green_xy[0], c.primaries_green_xy[1], c.primaries_blue_xy[0], c.primaries_blue_xy[1], c.white_point_xy[0], c.white_point_xy[1], primaries_XYZ)); - const float luminances[3] = {primaries_XYZ[3], primaries_XYZ[4], - primaries_XYZ[5]}; - float linear[3]; + const Vector3 luminances = primaries_XYZ[1]; + Color linear; JxlTransferFunction tf = c.transfer_function; if (tf == JXL_TRANSFER_FUNCTION_PQ) { for (size_t i = 0; i < 3; ++i) { @@ -151,25 +147,25 @@ static Status ToneMapPixel(const JxlColorEncoding& c, const float in[3], } if (tf == JXL_TRANSFER_FUNCTION_PQ) { Rec2408ToneMapperBase tone_mapper({0, 10000}, {0, 250}, luminances); - tone_mapper.ToneMap(&linear[0], &linear[1], &linear[2]); + tone_mapper.ToneMap(linear); } else { HlgOOTF_Base ootf(/*source_luminance=*/300, /*target_luminance=*/80, luminances); - ootf.Apply(&linear[0], &linear[1], &linear[2]); + ootf.Apply(linear); } - GamutMapScalar(&linear[0], &linear[1], &linear[2], luminances, + GamutMapScalar(linear, luminances, /*preserve_saturation=*/0.3f); - float chad[9]; + Matrix3x3 chad; JXL_RETURN_IF_ERROR( AdaptToXYZD50(c.white_point_xy[0], c.white_point_xy[1], chad)); - float to_xyzd50[9]; + Matrix3x3 to_xyzd50; Mul3x3Matrix(chad, primaries_XYZ, to_xyzd50); - float xyz[3] = {0, 0, 0}; + Vector3 xyz{0, 0, 0}; for (size_t xyz_c = 0; xyz_c < 3; ++xyz_c) { for (size_t rgb_c = 0; rgb_c < 3; ++rgb_c) { - xyz[xyz_c] += linear[rgb_c] * to_xyzd50[3 * xyz_c + rgb_c]; + xyz[xyz_c] += linear[rgb_c] * to_xyzd50[xyz_c][rgb_c]; } } @@ -206,7 +202,7 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf, JXL_ASSERT(N <= 4096); // ICC MFT2 only allows 4K entries JXL_ASSERT(tf == ExtraTF::kPQ || tf == ExtraTF::kHLG); - static constexpr float kLuminances[] = {1.f / 3, 1.f / 3, 1.f / 3}; + static constexpr Vector3 kLuminances{1.f / 3, 1.f / 3, 1.f / 3}; Rec2408ToneMapperBase tone_mapper({0, kPQIntensityTarget}, {0, kDefaultIntensityTarget}, kLuminances); // No point using float - LCMS converts to 16-bit for A2B/MFT. @@ -220,9 +216,10 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf, : TF_PQ_Base::DisplayFromEncoded(kPQIntensityTarget, dx); if (tone_map && tf == ExtraTF::kPQ && kPQIntensityTarget > kDefaultIntensityTarget) { - float r = y * 10000 / kPQIntensityTarget, g = r, b = r; - tone_mapper.ToneMap(&r, &g, &b); - y = r; + float l = y * 10000 / kPQIntensityTarget; + Color gray{l, l, l}; + tone_mapper.ToneMap(gray); + y = gray[0]; } JXL_ASSERT(y >= 0.0); // Clamp to table range - necessary for HLG. @@ -233,7 +230,7 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf, return table; } -static Status CIEXYZFromWhiteCIExy(double wx, double wy, float XYZ[3]) { +static Status CIEXYZFromWhiteCIExy(double wx, double wy, Color& XYZ) { // Target Y = 1. if (std::abs(wy) < 1e-12) return JXL_FAILURE("Y value is too small"); const float factor = 1 / wy; @@ -292,10 +289,18 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16]) 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, }; - uint32_t a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476; + uint32_t a0 = 0x67452301; + uint32_t b0 = 0xefcdab89; + uint32_t c0 = 0x98badcfe; + uint32_t d0 = 0x10325476; for (size_t i = 0; i < data64.size(); i += 64) { - uint32_t a = a0, b = b0, c = c0, d = d0, f, g; + uint32_t a = a0; + uint32_t b = b0; + uint32_t c = c0; + uint32_t d = d0; + uint32_t f; + uint32_t g; for (size_t j = 0; j < 64; j++) { if (j < 16) { f = (b & c) | ((~b) & d); @@ -310,8 +315,10 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16]) f = c ^ (b | (~d)); g = (7 * j) & 0xf; } - uint32_t dg0 = data64[i + g * 4 + 0], dg1 = data64[i + g * 4 + 1], - dg2 = data64[i + g * 4 + 2], dg3 = data64[i + g * 4 + 3]; + uint32_t dg0 = data64[i + g * 4 + 0]; + uint32_t dg1 = data64[i + g * 4 + 1]; + uint32_t dg2 = data64[i + g * 4 + 2]; + uint32_t dg3 = data64[i + g * 4 + 3]; uint32_t u = dg0 | (dg1 << 8u) | (dg2 << 16u) | (dg3 << 24u); f += a + sineparts[j] + u; a = d; @@ -342,23 +349,23 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16]) sum[15] = d0 >> 24u; } -static Status CreateICCChadMatrix(double wx, double wy, float result[9]) { - float m[9]; +static Status CreateICCChadMatrix(double wx, double wy, Matrix3x3& result) { + Matrix3x3 m; if (wy == 0) { // WhitePoint can not be pitch-black. return JXL_FAILURE("Invalid WhitePoint"); } JXL_RETURN_IF_ERROR(AdaptToXYZD50(wx, wy, m)); - memcpy(result, m, sizeof(float) * 9); + result = m; return true; } // Creates RGB to XYZ matrix given RGB primaries and whitepoint in xy. static Status CreateICCRGBMatrix(double rx, double ry, double gx, double gy, double bx, double by, double wx, double wy, - float result[9]) { - float m[9]; + Matrix3x3& result) { + Matrix3x3 m; JXL_RETURN_IF_ERROR(PrimariesToXYZD50(rx, ry, gx, gy, bx, by, wx, wy, m)); - memcpy(result, m, sizeof(float) * 9); + result = m; return true; } @@ -433,8 +440,12 @@ static Status CreateICCHeader(const JxlColorEncoding& c, // Three uint32_t's date/time encoding. // TODO(lode): encode actual date and time, this is a placeholder - uint32_t year = 2019, month = 12, day = 1; - uint32_t hour = 0, minute = 0, second = 0; + uint32_t year = 2019; + uint32_t month = 12; + uint32_t day = 1; + uint32_t hour = 0; + uint32_t minute = 0; + uint32_t second = 0; WriteICCUint16(year, 24, header); WriteICCUint16(month, 26, header); WriteICCUint16(day, 28, header); @@ -491,13 +502,13 @@ static void CreateICCMlucTag(const std::string& text, WriteICCTag("enUS", tags->size(), tags); WriteICCUint32(text.size() * 2, tags->size(), tags); WriteICCUint32(28, tags->size(), tags); - for (size_t i = 0; i < text.size(); i++) { + for (char c : text) { tags->push_back(0); // prepend 0 for UTF-16 - tags->push_back(text[i]); + tags->push_back(c); } } -static Status CreateICCXYZTag(float xyz[3], std::vector<uint8_t>* tags) { +static Status CreateICCXYZTag(const Color& xyz, std::vector<uint8_t>* tags) { WriteICCTag("XYZ ", tags->size(), tags); WriteICCUint32(0, tags->size(), tags); for (size_t i = 0; i < 3; ++i) { @@ -506,11 +517,14 @@ static Status CreateICCXYZTag(float xyz[3], std::vector<uint8_t>* tags) { return true; } -static Status CreateICCChadTag(float chad[9], std::vector<uint8_t>* tags) { +static Status CreateICCChadTag(const Matrix3x3& chad, + std::vector<uint8_t>* tags) { WriteICCTag("sf32", tags->size(), tags); WriteICCUint32(0, tags->size(), tags); - for (size_t i = 0; i < 9; i++) { - JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(chad[i], tags->size(), tags)); + for (size_t j = 0; j < 3; j++) { + for (size_t i = 0; i < 3; i++) { + JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(chad[j][i], tags->size(), tags)); + } } return true; } @@ -573,8 +587,8 @@ static Status CreateICCCurvParaTag(std::vector<float> params, size_t curve_type, WriteICCUint32(0, tags->size(), tags); WriteICCUint16(curve_type, tags->size(), tags); WriteICCUint16(0, tags->size(), tags); - for (size_t i = 0; i < params.size(); i++) { - JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(params[i], tags->size(), tags)); + for (float param : params) { + JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(param, tags->size(), tags)); } return true; } @@ -649,8 +663,8 @@ static Status CreateICCLutAtoBTagForXYB(std::vector<uint8_t>* tags) { -0.050022, 0.5683655, -0.018344, -1.387676, 1.1145555, 0.6857255}; // 12 * 4 = 48 bytes - for (size_t i = 0; i < 9; ++i) { - JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(matrix[i], tags->size(), tags)); + for (double v : matrix) { + JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(v, tags->size(), tags)); } for (size_t i = 0; i < 3; ++i) { float intercept = 0; @@ -880,7 +894,9 @@ static std::string ColorEncodingDescriptionImpl(const JxlColorEncoding& c) { static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, std::vector<uint8_t>* icc) { - std::vector<uint8_t> header, tagtable, tags; + std::vector<uint8_t> header; + std::vector<uint8_t> tagtable; + std::vector<uint8_t> tags; JxlTransferFunction tf = c.transfer_function; if (c.color_space == JXL_COLOR_SPACE_UNKNOWN || tf == JXL_TRANSFER_FUNCTION_UNKNOWN) { @@ -910,7 +926,8 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, // tag count, deferred to later WriteICCUint32(0, tagtable.size(), &tagtable); - size_t tag_offset = 0, tag_size = 0; + size_t tag_offset = 0; + size_t tag_size = 0; CreateICCMlucTag(ColorEncodingDescriptionImpl(c), &tags); FinalizeICCTag(&tags, &tag_offset, &tag_size); @@ -923,12 +940,12 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, // TODO(eustas): isn't it the other way round: gray image has d50 WhitePoint? if (c.color_space == JXL_COLOR_SPACE_GRAY) { - float wtpt[3]; + Color wtpt; JXL_RETURN_IF_ERROR( CIEXYZFromWhiteCIExy(c.white_point_xy[0], c.white_point_xy[1], wtpt)); JXL_RETURN_IF_ERROR(CreateICCXYZTag(wtpt, &tags)); } else { - float d50[3] = {0.964203, 1.0, 0.824905}; + Color d50{0.964203, 1.0, 0.824905}; JXL_RETURN_IF_ERROR(CreateICCXYZTag(d50, &tags)); } FinalizeICCTag(&tags, &tag_offset, &tag_size); @@ -936,7 +953,7 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, if (c.color_space != JXL_COLOR_SPACE_GRAY) { // Chromatic adaptation matrix - float chad[9]; + Matrix3x3 chad; JXL_RETURN_IF_ERROR( CreateICCChadMatrix(c.white_point_xy[0], c.white_point_xy[1], chad)); @@ -949,14 +966,14 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, MaybeCreateICCCICPTag(c, &tags, &tag_offset, &tag_size, &tagtable, &offsets); - float m[9]; + Matrix3x3 m; JXL_RETURN_IF_ERROR(CreateICCRGBMatrix( c.primaries_red_xy[0], c.primaries_red_xy[1], c.primaries_green_xy[0], c.primaries_green_xy[1], c.primaries_blue_xy[0], c.primaries_blue_xy[1], c.white_point_xy[0], c.white_point_xy[1], m)); - float r[3] = {m[0], m[3], m[6]}; - float g[3] = {m[1], m[4], m[7]}; - float b[3] = {m[2], m[5], m[8]}; + Color r{m[0][0], m[1][0], m[2][0]}; + Color g{m[0][1], m[1][1], m[2][1]}; + Color b{m[0][2], m[1][2], m[2][2]}; JXL_RETURN_IF_ERROR(CreateICCXYZTag(r, &tags)); FinalizeICCTag(&tags, &tag_offset, &tag_size); @@ -1042,8 +1059,8 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, WriteICCUint32(header.size() + tagtable.size() + tags.size(), 0, &header); *icc = header; - Bytes(tagtable).AppendTo(icc); - Bytes(tags).AppendTo(icc); + Bytes(tagtable).AppendTo(*icc); + Bytes(tags).AppendTo(*icc); // The MD5 checksum must be computed on the profile with profile flags, // rendering intent, and region of the checksum itself, set to 0. diff --git a/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h b/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h index 48e8e254f7..69dcd0b512 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h +++ b/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h @@ -6,7 +6,7 @@ #ifndef LIB_JXL_CMS_OPSIN_PARAMS_H_ #define LIB_JXL_CMS_OPSIN_PARAMS_H_ -#include <array> +#include "lib/jxl/base/matrix_ops.h" // Constants that define the XYB color space. @@ -35,21 +35,20 @@ constexpr float kOpsinAbsorbanceBias1 = kOpsinAbsorbanceBias0; constexpr float kOpsinAbsorbanceBias2 = kOpsinAbsorbanceBias0; // Opsin absorbance matrix is now frozen. -constexpr std::array<float, 9> kOpsinAbsorbanceMatrix = { - kM00, kM01, kM02, kM10, kM11, kM12, kM20, kM21, kM22, -}; +constexpr Matrix3x3 kOpsinAbsorbanceMatrix{ + {{kM00, kM01, kM02}, {kM10, kM11, kM12}, {kM20, kM21, kM22}}}; -constexpr std::array<float, 9> kDefaultInverseOpsinAbsorbanceMatrix = { - 11.031566901960783f, -9.866943921568629f, -0.16462299647058826f, - -3.254147380392157f, 4.418770392156863f, -0.16462299647058826f, - -3.6588512862745097f, 2.7129230470588235f, 1.9459282392156863f}; +constexpr Matrix3x3 kDefaultInverseOpsinAbsorbanceMatrix{ + {{11.031566901960783f, -9.866943921568629f, -0.16462299647058826f}, + {-3.254147380392157f, 4.418770392156863f, -0.16462299647058826f}, + {-3.6588512862745097f, 2.7129230470588235f, 1.9459282392156863f}}}; // Must be the inverse matrix of kOpsinAbsorbanceMatrix and match the spec. -static inline const float* DefaultInverseOpsinAbsorbanceMatrix() { - return kDefaultInverseOpsinAbsorbanceMatrix.data(); +static inline const Matrix3x3& DefaultInverseOpsinAbsorbanceMatrix() { + return kDefaultInverseOpsinAbsorbanceMatrix; } -constexpr std::array<float, 3> kOpsinAbsorbanceBias = { +constexpr Vector3 kOpsinAbsorbanceBias = { kOpsinAbsorbanceBias0, kOpsinAbsorbanceBias1, kOpsinAbsorbanceBias2, @@ -63,14 +62,14 @@ constexpr float kScaledXYBOffset0 = 0.015386134f; constexpr float kScaledXYBOffset1 = 0.0f; constexpr float kScaledXYBOffset2 = 0.27770459f; -constexpr std::array<float, 3> kScaledXYBOffset = { - kScaledXYBOffset0, kScaledXYBOffset1, kScaledXYBOffset2}; +constexpr Vector3 kScaledXYBOffset = {kScaledXYBOffset0, kScaledXYBOffset1, + kScaledXYBOffset2}; constexpr float kScaledXYBScale0 = 22.995788804f; constexpr float kScaledXYBScale1 = 1.183000077f; constexpr float kScaledXYBScale2 = 1.502141333f; -constexpr std::array<float, 3> kScaledXYBScale = { +constexpr Vector3 kScaledXYBScale = { kScaledXYBScale0, kScaledXYBScale1, kScaledXYBScale2, diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h index 3d94ccea12..7d09beed32 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h +++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h @@ -100,14 +100,14 @@ class HlgOOTF : HlgOOTF_Base { using HlgOOTF_Base::HlgOOTF_Base; static HlgOOTF FromSceneLight(float display_luminance, - const float primaries_luminances[3]) { + const Vector3& primaries_luminances) { return HlgOOTF(/*gamma=*/1.2f * std::pow(1.111f, std::log2(display_luminance / 1000.f)), primaries_luminances); } static HlgOOTF ToSceneLight(float display_luminance, - const float primaries_luminances[3]) { + const Vector3& primaries_luminances) { return HlgOOTF( /*gamma=*/(1 / 1.2f) * std::pow(1.111f, -std::log2(display_luminance / 1000.f)), @@ -132,7 +132,7 @@ class HlgOOTF : HlgOOTF_Base { }; template <typename V> -void GamutMap(V* red, V* green, V* blue, const float primaries_luminances[3], +void GamutMap(V* red, V* green, V* blue, const Vector3& primaries_luminances, float preserve_saturation = 0.1f) { hwy::HWY_NAMESPACE::DFromV<V> df; const V luminance = diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h index a114109ea6..81f301a31d 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h +++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h @@ -12,6 +12,7 @@ #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/matrix_ops.h" #include "lib/jxl/cms/transfer_functions.h" namespace jxl { @@ -20,7 +21,7 @@ class Rec2408ToneMapperBase { public: explicit Rec2408ToneMapperBase(std::pair<float, float> source_range, std::pair<float, float> target_range, - const float primaries_luminances[3]) + const Vector3& primaries_luminances) : source_range_(source_range), target_range_(target_range), red_Y_(primaries_luminances[0]), @@ -28,10 +29,10 @@ class Rec2408ToneMapperBase { blue_Y_(primaries_luminances[2]) {} // TODO(eustas): test me - void ToneMap(float* red, float* green, float* blue) const { + void ToneMap(Color& rgb) const { const float luminance = source_range_.second * - (red_Y_ * *red + green_Y_ * *green + blue_Y_ * *blue); + (red_Y_ * rgb[0] + green_Y_ * rgb[1] + blue_Y_ * rgb[2]); const float normalized_pq = std::min(1.f, (InvEOTF(luminance) - pq_mastering_min_) * inv_pq_mastering_range_); @@ -49,8 +50,8 @@ class Rec2408ToneMapperBase { const float ratio = new_luminance / std::max(luminance, min_luminance); const float cap = new_luminance * inv_target_peak_; const float multiplier = ratio * normalizer_; - for (float* const val : {red, green, blue}) { - *val = use_cap ? cap : *val * multiplier; + for (size_t idx : {0, 1, 2}) { + rgb[idx] = use_cap ? cap : rgb[idx] * multiplier; } } @@ -96,23 +97,24 @@ class Rec2408ToneMapperBase { class HlgOOTF_Base { public: explicit HlgOOTF_Base(float source_luminance, float target_luminance, - const float primaries_luminances[3]) + const Vector3& primaries_luminances) : HlgOOTF_Base(/*gamma=*/std::pow(1.111f, std::log2(target_luminance / source_luminance)), primaries_luminances) {} // TODO(eustas): test me - void Apply(float* red, float* green, float* blue) const { + void Apply(Color& rgb) const { if (!apply_ootf_) return; - const float luminance = red_Y_ * *red + green_Y_ * *green + blue_Y_ * *blue; + const float luminance = + red_Y_ * rgb[0] + green_Y_ * rgb[1] + blue_Y_ * rgb[2]; const float ratio = std::min<float>(powf(luminance, exponent_), 1e9); - *red *= ratio; - *green *= ratio; - *blue *= ratio; + rgb[0] *= ratio; + rgb[1] *= ratio; + rgb[2] *= ratio; } protected: - explicit HlgOOTF_Base(float gamma, const float luminances[3]) + explicit HlgOOTF_Base(float gamma, const Vector3& luminances) : exponent_(gamma - 1), red_Y_(luminances[0]), green_Y_(luminances[1]), @@ -124,13 +126,12 @@ class HlgOOTF_Base { const float blue_Y_; }; -static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green, - float* blue, - const float primaries_luminances[3], +static JXL_MAYBE_UNUSED void GamutMapScalar(Color& rgb, + const Vector3& primaries_luminances, float preserve_saturation = 0.1f) { - const float luminance = primaries_luminances[0] * *red + - primaries_luminances[1] * *green + - primaries_luminances[2] * *blue; + const float luminance = primaries_luminances[0] * rgb[0] + + primaries_luminances[1] * rgb[1] + + primaries_luminances[2] * rgb[2]; // Desaturate out-of-gamut pixels. This is done by mixing each pixel // with just enough gray of the target luminance to make all @@ -142,8 +143,8 @@ static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green, // done by mixing in yet more gray. That will desaturate it further. float gray_mix_saturation = 0.0f; float gray_mix_luminance = 0.0f; - for (const float* ch : {red, green, blue}) { - const float& val = *ch; + for (size_t idx : {0, 1, 2}) { + const float& val = rgb[idx]; const float val_minus_gray = val - luminance; const float inv_val_minus_gray = 1.0f / ((val_minus_gray == 0.0f) ? 1.0f : val_minus_gray); @@ -162,15 +163,14 @@ static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green, Clamp1((preserve_saturation * (gray_mix_saturation - gray_mix_luminance) + gray_mix_luminance), 0.0f, 1.0f); - for (float* const ch : {red, green, blue}) { - float& val = *ch; + for (size_t idx : {0, 1, 2}) { + float& val = rgb[idx]; val = gray_mix * (luminance - val) + val; } - const float max_clr = std::max({1.0f, *red, *green, *blue}); + const float max_clr = std::max({1.0f, rgb[0], rgb[1], rgb[2]}); const float normalizer = 1.0f / max_clr; - for (float* const ch : {red, green, blue}) { - float& val = *ch; - val *= normalizer; + for (size_t idx : {0, 1, 2}) { + rgb[idx] *= normalizer; } } diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc index dda2bbb0aa..76165d26e7 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc +++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc @@ -30,17 +30,17 @@ HWY_NOINLINE void TestRec2408ToneMap() { for (size_t i = 0; i < kNumTrials; i++) { float src = 11000.0 + rng.UniformF(-150.0f, 150.0f); float tgt = 250 + rng.UniformF(-5.0f, 5.0f); - float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), - rng.UniformF(0.2f, 0.4f)}; - float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), - rng.UniformF(0.0f, 1.0f)}; + Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), + rng.UniformF(0.2f, 0.4f)}; + Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), + rng.UniformF(0.0f, 1.0f)}; Rec2408ToneMapper<decltype(d)> tone_mapper({0, src}, {0, tgt}, luminances); auto r = Set(d, rgb[0]); auto g = Set(d, rgb[1]); auto b = Set(d, rgb[2]); tone_mapper.ToneMap(&r, &g, &b); Rec2408ToneMapperBase tone_mapper_base({0, src}, {0, tgt}, luminances); - tone_mapper_base.ToneMap(&rgb[0], &rgb[1], &rgb[2]); + tone_mapper_base.ToneMap(rgb); const float actual_r = GetLane(r); const float expected_r = rgb[0]; const float abs_err_r = std::abs(expected_r - actual_r); @@ -66,17 +66,17 @@ HWY_NOINLINE void TestHlgOotfApply() { for (size_t i = 0; i < kNumTrials; i++) { float src = 300.0 + rng.UniformF(-50.0f, 50.0f); float tgt = 80 + rng.UniformF(-5.0f, 5.0f); - float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), - rng.UniformF(0.2f, 0.4f)}; - float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), - rng.UniformF(0.0f, 1.0f)}; + Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), + rng.UniformF(0.2f, 0.4f)}; + Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), + rng.UniformF(0.0f, 1.0f)}; HlgOOTF ootf(src, tgt, luminances); auto r = Set(d, rgb[0]); auto g = Set(d, rgb[1]); auto b = Set(d, rgb[2]); ootf.Apply(&r, &g, &b); HlgOOTF_Base ootf_base(src, tgt, luminances); - ootf_base.Apply(&rgb[0], &rgb[1], &rgb[2]); + ootf_base.Apply(rgb); const float actual_r = GetLane(r); const float expected_r = rgb[0]; const float abs_err_r = std::abs(expected_r - actual_r); @@ -101,15 +101,15 @@ HWY_NOINLINE void TestGamutMap() { HWY_FULL(float) d; for (size_t i = 0; i < kNumTrials; i++) { float preserve_saturation = rng.UniformF(0.2f, 0.4f); - float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), - rng.UniformF(0.2f, 0.4f)}; - float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), - rng.UniformF(0.0f, 1.0f)}; + Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), + rng.UniformF(0.2f, 0.4f)}; + Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), + rng.UniformF(0.0f, 1.0f)}; auto r = Set(d, rgb[0]); auto g = Set(d, rgb[1]); auto b = Set(d, rgb[2]); GamutMap(&r, &g, &b, luminances, preserve_saturation); - GamutMapScalar(&rgb[0], &rgb[1], &rgb[2], luminances, preserve_saturation); + GamutMapScalar(rgb, luminances, preserve_saturation); const float actual_r = GetLane(r); const float expected_r = rgb[0]; const float abs_err_r = std::abs(expected_r - actual_r); |