summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas CHOLLET <lucas.chollet@free.fr>2023-03-11 19:51:54 -0500
committerLinus Groh <mail@linusgroh.de>2023-04-03 17:12:27 +0100
commitdf12e705419160f1f0fd9b78073bfadde2ee297c (patch)
treea3f22a10c9fe67937da7b967d1125f9d6d97f8c4
parent62290d57f747d3bdb61164aff73ddc55bd9f4957 (diff)
downloadserenity-df12e705419160f1f0fd9b78073bfadde2ee297c.zip
LibGfx/JPEG: Bring IDCT and YCbCr conversion closer to specification
As mentioned in F.2.1.5 - Inverse DCT (IDCT), the decoder needs to perform a level shift by adding 128. This used to be done in `ycbcr_to_rgb` after the conversion. Now, we do it in `inverse_dct` in order to ensure that the task is done unconditionally. Consequences of this are that we are no longer required to explicitly do it for RGB images and also, the `ycbcr_to_rgb` function is exactly like the specification.
-rw-r--r--Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp57
1 files changed, 30 insertions, 27 deletions
diff --git a/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp b/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp
index d300ac4407..94e737010c 100644
--- a/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp
+++ b/Userland/Libraries/LibGfx/ImageFormats/JPEGLoader.cpp
@@ -1294,10 +1294,33 @@ static void inverse_dct(JPEGLoadingContext const& context, Vector<Macroblock>& m
}
}
}
+
+ // F.2.1.5 - Inverse DCT (IDCT)
+
+ 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);
+ macroblocks[mb_index].k[i * 8 + j] = clamp(macroblocks[mb_index].b[i * 8 + j] + 128, 0, 255);
+ }
+ }
+ }
+ }
+ }
+ }
}
static void ycbcr_to_rgb(JPEGLoadingContext const& context, Vector<Macroblock>& macroblocks)
{
+ // Conversion from YCbCr to RGB isn't specified in the first JPEG specification but in the JFIF extension:
+ // See: https://www.itu.int/rec/dologin_pub.asp?lang=f&id=T-REC-T.871-201105-I!!PDF-E&type=items
+ // 7 - Conversion to and from RGB
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) {
const u32 chroma_block_index = vcursor * context.mblock_meta.hpadded_count + hcursor;
@@ -1315,32 +1338,12 @@ static void ycbcr_to_rgb(JPEGLoadingContext const& context, Vector<Macroblock>&
const u32 chroma_pxrow = (i / context.vsample_factor) + 4 * vfactor_i;
const u32 chroma_pxcol = (j / context.hsample_factor) + 4 * hfactor_i;
const u32 chroma_pixel = chroma_pxrow * 8 + chroma_pxcol;
- int r = y[pixel] + 1.402f * chroma.cr[chroma_pixel] + 128;
- int g = y[pixel] - 0.344f * chroma.cb[chroma_pixel] - 0.714f * chroma.cr[chroma_pixel] + 128;
- int b = y[pixel] + 1.772f * chroma.cb[chroma_pixel] + 128;
- y[pixel] = r < 0 ? 0 : (r > 255 ? 255 : r);
- cb[pixel] = g < 0 ? 0 : (g > 255 ? 255 : g);
- cr[pixel] = b < 0 ? 0 : (b > 255 ? 255 : b);
- }
- }
- }
- }
- }
- }
-}
-
-static void signed_rgb_to_unsigned(JPEGLoadingContext const& context, Vector<Macroblock>& 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);
+ int r = y[pixel] + 1.402f * (chroma.cr[chroma_pixel] - 128);
+ int g = y[pixel] - 0.3441f * (chroma.cb[chroma_pixel] - 128) - 0.7141f * (chroma.cr[chroma_pixel] - 128);
+ int b = y[pixel] + 1.772f * (chroma.cb[chroma_pixel] - 128);
+ y[pixel] = clamp(r, 0, 255);
+ cb[pixel] = clamp(g, 0, 255);
+ cr[pixel] = clamp(b, 0, 255);
}
}
}
@@ -1361,7 +1364,7 @@ static ErrorOr<void> handle_color_transform(JPEGLoadingContext const& context, V
// FIXME: implement CMYK
dbgln("CMYK isn't supported yet");
} else if (context.components.size() == 3) {
- signed_rgb_to_unsigned(context, macroblocks);
+ // Note: components.size() == 3 means that we have an RGB image, so no color transformation is needed.
} else {
return Error::from_string_literal("Wrong number of components for CMYK or RGB, aborting.");
}