diff options
Diffstat (limited to 'third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc')
-rw-r--r-- | third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc | 95 |
1 files changed, 57 insertions, 38 deletions
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); |