diff --git a/Userland/Libraries/LibGfx/PNGLoader.cpp b/Userland/Libraries/LibGfx/PNGLoader.cpp index b970658bf9..5e931cbfff 100644 --- a/Userland/Libraries/LibGfx/PNGLoader.cpp +++ b/Userland/Libraries/LibGfx/PNGLoader.cpp @@ -27,6 +27,31 @@ struct PNG_IHDR { static_assert(AssertSize()); +struct ChromaticitiesAndWhitepoint { + NetworkOrdered white_point_x; + NetworkOrdered white_point_y; + NetworkOrdered red_x; + NetworkOrdered red_y; + NetworkOrdered green_x; + NetworkOrdered green_y; + NetworkOrdered blue_x; + NetworkOrdered blue_y; +}; +static_assert(AssertSize()); + +struct CodingIndependentCodePoints { + u8 color_primaries; + u8 transfer_function; + u8 matrix_coefficients; + u8 video_full_range_flag; +}; +static_assert(AssertSize()); + +struct EmbeddedICCProfile { + StringView profile_name; + ReadonlyBytes compressed_data; +}; + struct Scanline { PNG::FilterType filter; ReadonlyBytes data {}; @@ -67,6 +92,13 @@ enum PngInterlaceMethod { Adam7 = 1 }; +enum RenderingIntent { + Perceptual = 0, + RelativeColorimetric = 1, + Saturation = 2, + AbsoluteColorimetric = 3, +}; + struct PNGLoadingContext { enum State { NotDecoded = 0, @@ -97,6 +129,12 @@ struct PNGLoadingContext { Vector palette_data; Vector palette_transparency_data; + Optional chromaticities_and_whitepoint; + Optional coding_independent_code_points; + Optional gamma; + Optional embedded_icc_profile; + Optional sRGB_rendering_intent; + Checked compute_row_size_for_width(int width) { Checked row_size = width; @@ -841,6 +879,73 @@ static bool process_tRNS(ReadonlyBytes data, PNGLoadingContext& context) return true; } +static bool process_cHRM(ReadonlyBytes data, PNGLoadingContext& context) +{ + // https://www.w3.org/TR/png/#11cHRM + if (data.size() != 32) + return false; + context.chromaticities_and_whitepoint = *bit_cast(data.data()); + return true; +} + +static bool process_cICP(ReadonlyBytes data, PNGLoadingContext& context) +{ + // https://www.w3.org/TR/png/#cICP-chunk + if (data.size() != 4) + return false; + context.coding_independent_code_points = *bit_cast(data.data()); + return true; +} + +static bool process_iCCP(ReadonlyBytes data, PNGLoadingContext& context) +{ + // https://www.w3.org/TR/png/#11iCCP + size_t profile_name_length_max = min(80u, data.size()); + size_t profile_name_length = strnlen((char const*)data.data(), profile_name_length_max); + if (profile_name_length == 0 || profile_name_length == profile_name_length_max) + return false; + + if (data.size() < profile_name_length + 2) + return false; + + u8 compression_method = data[profile_name_length + 1]; + if (compression_method != 0) + return false; + + context.embedded_icc_profile = EmbeddedICCProfile { { data.data(), profile_name_length }, data.slice(profile_name_length + 2) }; + + return true; +} + +static bool process_gAMA(ReadonlyBytes data, PNGLoadingContext& context) +{ + // https://www.w3.org/TR/png/#11gAMA + if (data.size() != 4) + return false; + + u32 gamma = *bit_cast const*>(data.data()); + if (gamma & 0x8000'0000) + return false; + context.gamma = gamma; + + return true; +} + +static bool process_sRGB(ReadonlyBytes data, PNGLoadingContext& context) +{ + // https://www.w3.org/TR/png/#srgb-standard-colour-space + if (data.size() != 1) + return false; + + u8 rendering_intent = data[0]; + if (rendering_intent > 3) + return false; + + context.sRGB_rendering_intent = (RenderingIntent)rendering_intent; + + return true; +} + static bool process_chunk(Streamer& streamer, PNGLoadingContext& context) { u32 chunk_size; @@ -872,6 +977,16 @@ static bool process_chunk(Streamer& streamer, PNGLoadingContext& context) return process_IDAT(chunk_data, context); if (!strcmp((char const*)chunk_type, "PLTE")) return process_PLTE(chunk_data, context); + if (!strcmp((char const*)chunk_type, "cHRM")) + return process_cHRM(chunk_data, context); + if (!strcmp((char const*)chunk_type, "cICP")) + return process_cICP(chunk_data, context); + if (!strcmp((char const*)chunk_type, "iCCP")) + return process_iCCP(chunk_data, context); + if (!strcmp((char const*)chunk_type, "gAMA")) + return process_gAMA(chunk_data, context); + if (!strcmp((char const*)chunk_type, "sRGB")) + return process_sRGB(chunk_data, context); if (!strcmp((char const*)chunk_type, "tRNS")) return process_tRNS(chunk_data, context); return true;