From a47d775be67b32c58949c3f997a24f6dfcabd8d9 Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Sat, 4 Mar 2023 21:37:11 -0500 Subject: [PATCH] LibGfx/JPEG: Support RGB JPEGs By default, JPEGs use YCbCr to encode colors however other encoding exist. This patch adds the logic to determine which transformation is needed and support for RGB encoded JPEGs :^). --- Userland/Libraries/LibGfx/JPEGLoader.cpp | 71 +++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibGfx/JPEGLoader.cpp b/Userland/Libraries/LibGfx/JPEGLoader.cpp index f39aa99375..491a66ded9 100644 --- a/Userland/Libraries/LibGfx/JPEGLoader.cpp +++ b/Userland/Libraries/LibGfx/JPEGLoader.cpp @@ -1246,6 +1246,75 @@ static void ycbcr_to_rgb(JPEGLoadingContext const& context, Vector& } } +static void signed_rgb_to_unsigned(JPEGLoadingContext const& context, Vector& macroblocks) +{ + for (u32 vcursor = 0; vcursor < context.mblock_meta.vcount; vcursor += context.vsample_factor) { + for (u32 hcursor = 0; hcursor < context.mblock_meta.hcount; hcursor += context.hsample_factor) { + for (u8 vfactor_i = 0; vfactor_i < context.vsample_factor; ++vfactor_i) { + for (u8 hfactor_i = 0; hfactor_i < context.hsample_factor; ++hfactor_i) { + u32 mb_index = (vcursor + vfactor_i) * context.mblock_meta.hpadded_count + (hcursor + hfactor_i); + for (u8 i = 0; i < 8; ++i) { + for (u8 j = 0; j < 8; ++j) { + macroblocks[mb_index].r[i * 8 + j] = clamp(macroblocks[mb_index].r[i * 8 + j] + 128, 0, 255); + macroblocks[mb_index].g[i * 8 + j] = clamp(macroblocks[mb_index].g[i * 8 + j] + 128, 0, 255); + macroblocks[mb_index].b[i * 8 + j] = clamp(macroblocks[mb_index].b[i * 8 + j] + 128, 0, 255); + } + } + } + } + } + } +} + +static ErrorOr handle_color_transform(JPEGLoadingContext const& context, Vector& macroblocks) +{ + if (context.color_transform.has_value()) { + // https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.872-201206-I!!PDF-E&type=items + // 6.5.3 - APP14 marker segment for colour encoding + + switch (*context.color_transform) { + case ColorTransform::CmykOrRgb: + if (context.components.size() == 4) { + // FIXME: implement CMYK + dbgln("CMYK isn't supported yet"); + } else if (context.components.size() == 3) { + signed_rgb_to_unsigned(context, macroblocks); + } else { + return Error::from_string_literal("Wrong number of components for CMYK or RGB, aborting."); + } + break; + case ColorTransform::YCbCr: + ycbcr_to_rgb(context, macroblocks); + break; + case ColorTransform::YCCK: + // FIXME: implement YCCK + dbgln("YCCK isn't supported yet"); + break; + } + + return {}; + } + + // No App14 segment is present, assuming : + // - 1 components means grayscale + // - 3 components means YCbCr + // - 4 components means CMYK + if (context.components.size() == 4) { + // FIXME: implement CMYK + dbgln("CMYK isn't supported yet"); + } + if (context.components.size() == 3) + ycbcr_to_rgb(context, macroblocks); + + if (context.components.size() == 1) { + // FIXME: This is what we used to do for grayscale, + // we should at least document it and maybe change it. + ycbcr_to_rgb(context, macroblocks); + } + + return {}; +} + static ErrorOr compose_bitmap(JPEGLoadingContext& context, Vector const& macroblocks) { context.bitmap = TRY(Bitmap::create(BitmapFormat::BGRx8888, { context.frame.width, context.frame.height })); @@ -1457,7 +1526,7 @@ static ErrorOr decode_jpeg(JPEGLoadingContext& context) auto macroblocks = TRY(construct_macroblocks(context)); dequantize(context, macroblocks); inverse_dct(context, macroblocks); - ycbcr_to_rgb(context, macroblocks); + TRY(handle_color_transform(context, macroblocks)); TRY(compose_bitmap(context, macroblocks)); context.stream.clear(); return {};