3 Commits

Author SHA1 Message Date
Nico Weber
5cebac774c Reland "Decode JPEGs at reduced size via libjpeg scale_denom"
This is a reland of commit 009b91d9d95d998fa7e8e09c55f716cb42d99546

Original change's description:
> Decode JPEGs at reduced size via libjpeg scale_denom
>
> JPEGs store image data in 8x8 DCT tiles in the frequency domain. To
> restore an 8x8 tile in the image domain, an IDCT is computed, using up
> to 64 coefficients.
>
> If a JPEG with large dimensions is drawn at small scale on screen,
> pdfium used to completely decompress the JPEG to large size, doing a
> full IDCT for each tile, and then scale it down at decode time.
>
> Using scale_denom, pdfium can instead decode the image at 1/8th (or
> 1/4th, 1/2th) its size (in each dimension) by not doing the full IDCT,
> but instead only taking the one DC term (for 1/8th), or 2x2 or 4x4
> coefficents (for 1/4th, 1/2th).
>
> This not just saves work decoding the image, it also outputs a smaller
> image that is faster to downsample when painting it. For most images,
> this shouldn't look perceptively different than downsampling from the
> large bitmap.
>
> This kind of matches what Blink does when decoding JPEGs [1], and
> morally matches what pdfium already does for JPEG2000s after
> https://pdfium-review.googlesource.com/c/pdfium/+/99970.
>
> (Note that CPDF_ImageRenderer::StartLoadDIBBase() in
> cpdf_imagerenderer.cpp passes in the page size as max_size_required,
> which cpdf_dib.cpp uses to compute resolution_levels_to_skip. So image
> decoding currently isn't limited to the size the image is drawn at, but
> by the size of the bitmap the whole page is drawn at. This can be be
> improved later.)
>
> Depending on what data the encoder writes in padding rows / columns for
> images that aren't a multiple of the MCU size, the code might produce
> artifacts in the last row or column. So only do this for images with
> sizes a multiple of the MCU for now, see also https://crbug.com/890745
> and the discussion on
> https://github.com/libjpeg-turbo/libjpeg-turbo/issues/297. This is
> unfortunate, but currently there's no way to do better without hacks.
> (This also matches what Blink does.)
>
> Timing (hyperfine, --render-repeats): a 3072x2304 4:4:4 image drawn onto
> a 64pt page decodes in 40ms at scale_denom 8 vs 84ms at full resolution.
>
> 1:
> https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc;l=430
> ...but evidently this is much harder to hit now then when that code was
> initially added, probably because JPEGs in Chrome now go down the
> DecodeYUV path, which doesn't set scale_denom (yet?).
>
> Depends on new baseline in
> https://pdfium-review.googlesource.com/c/pdfium_tests/+/151570
> and updates DEPS to pick it up.
>
> Plumb the existing resolution_levels_to_skip (computed in
> CPDF_DIB::StartLoadDIBBase from the render device size) into the DCT
> path. CreateDCTDecoder maps it to a libjpeg scale_denom of 1/2/4/8
> (capped at 8, the largest power-of-two DCT scaling libjpeg supports) and
> passes it to JpegModule::CreateDecoder. JpegDecoder sets
> cinfo.scale_denom before jpeg_start_decompress and reports the
> scaled-down output dimensions (ceil(dim/scale_denom), matching
> jpeg_core_output_dimensions). The DIB then adopts the decoder's reduced
> dimensions, mirroring the JPX path. libjpeg performs the reduction
> cheaply inside the IDCT (e.g. 1/8 uses a 1x1 IDCT), so far fewer pixels
> are produced for images shown much smaller than native.
>
> The calls to SetWidth()/SetHeight() are a slight behavior change for all
> JPEG data for invalid files: Previously, the code used width/height from
> the PDF dict, now it gets the size from the decoder. These sizes are
> supposed to match, but if they aren't, pdfium now uses the other one.
> This is arguably better, as that size dictates how much data is actually
> present.
>
> Bug: 531782505
> Change-Id: I66409898d028685813b274e063aedf52200be0dd
> Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/151550
> Commit-Queue: Nico Weber <thakis@chromium.org>
> Reviewed-by: Lei Zhang <thestig@chromium.org>
> Reviewed-by: Nico Weber <thakis@google.com>

Bug: 531782505
Change-Id: I42c141839a21c77855b06caded3c79db2e53ed50
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/151930
Commit-Queue: Nico Weber <thakis@chromium.org>
Reviewed-by: Nico Weber <thakis@google.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
2026-07-13 14:54:20 -07:00
Lei Zhang
fb09d84c50 Revert "Decode JPEGs at reduced size via libjpeg scale_denom"
This reverts commit 009b91d9d95d998fa7e8e09c55f716cb42d99546.

Reason for revert: Signifincantly slows down rendering in some cases.

Failure Link: https://ci.chromium.org/ui/p/chromium/builders/ci/win11-arm64-dbg-tests/12279/overview

Original change's description:
> Decode JPEGs at reduced size via libjpeg scale_denom
>
> JPEGs store image data in 8x8 DCT tiles in the frequency domain. To
> restore an 8x8 tile in the image domain, an IDCT is computed, using up
> to 64 coefficients.
>
> If a JPEG with large dimensions is drawn at small scale on screen,
> pdfium used to completely decompress the JPEG to large size, doing a
> full IDCT for each tile, and then scale it down at decode time.
>
> Using scale_denom, pdfium can instead decode the image at 1/8th (or
> 1/4th, 1/2th) its size (in each dimension) by not doing the full IDCT,
> but instead only taking the one DC term (for 1/8th), or 2x2 or 4x4
> coefficents (for 1/4th, 1/2th).
>
> This not just saves work decoding the image, it also outputs a smaller
> image that is faster to downsample when painting it. For most images,
> this shouldn't look perceptively different than downsampling from the
> large bitmap.
>
> This kind of matches what Blink does when decoding JPEGs [1], and
> morally matches what pdfium already does for JPEG2000s after
> https://pdfium-review.googlesource.com/c/pdfium/+/99970.
>
> (Note that CPDF_ImageRenderer::StartLoadDIBBase() in
> cpdf_imagerenderer.cpp passes in the page size as max_size_required,
> which cpdf_dib.cpp uses to compute resolution_levels_to_skip. So image
> decoding currently isn't limited to the size the image is drawn at, but
> by the size of the bitmap the whole page is drawn at. This can be be
> improved later.)
>
> Depending on what data the encoder writes in padding rows / columns for
> images that aren't a multiple of the MCU size, the code might produce
> artifacts in the last row or column. So only do this for images with
> sizes a multiple of the MCU for now, see also https://crbug.com/890745
> and the discussion on
> https://github.com/libjpeg-turbo/libjpeg-turbo/issues/297. This is
> unfortunate, but currently there's no way to do better without hacks.
> (This also matches what Blink does.)
>
> Timing (hyperfine, --render-repeats): a 3072x2304 4:4:4 image drawn onto
> a 64pt page decodes in 40ms at scale_denom 8 vs 84ms at full resolution.
>
> 1:
> https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc;l=430
> ...but evidently this is much harder to hit now then when that code was
> initially added, probably because JPEGs in Chrome now go down the
> DecodeYUV path, which doesn't set scale_denom (yet?).
>
> Depends on new baseline in
> https://pdfium-review.googlesource.com/c/pdfium_tests/+/151570
> and updates DEPS to pick it up.
>
> Plumb the existing resolution_levels_to_skip (computed in
> CPDF_DIB::StartLoadDIBBase from the render device size) into the DCT
> path. CreateDCTDecoder maps it to a libjpeg scale_denom of 1/2/4/8
> (capped at 8, the largest power-of-two DCT scaling libjpeg supports) and
> passes it to JpegModule::CreateDecoder. JpegDecoder sets
> cinfo.scale_denom before jpeg_start_decompress and reports the
> scaled-down output dimensions (ceil(dim/scale_denom), matching
> jpeg_core_output_dimensions). The DIB then adopts the decoder's reduced
> dimensions, mirroring the JPX path. libjpeg performs the reduction
> cheaply inside the IDCT (e.g. 1/8 uses a 1x1 IDCT), so far fewer pixels
> are produced for images shown much smaller than native.
>
> The calls to SetWidth()/SetHeight() are a slight behavior change for all
> JPEG data for invalid files: Previously, the code used width/height from
> the PDF dict, now it gets the size from the decoder. These sizes are
> supposed to match, but if they aren't, pdfium now uses the other one.
> This is arguably better, as that size dictates how much data is actually
> present.
>
> Bug: 531782505
> Change-Id: I66409898d028685813b274e063aedf52200be0dd
> Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/151550
> Commit-Queue: Nico Weber <thakis@chromium.org>
> Reviewed-by: Lei Zhang <thestig@chromium.org>
> Reviewed-by: Nico Weber <thakis@google.com>

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: 531782505
Change-Id: If8fa628779381543e6243b75b0b07e26ae87ac12
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/151870
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
2026-07-10 16:55:25 -07:00
Nico Weber
009b91d9d9 Decode JPEGs at reduced size via libjpeg scale_denom
JPEGs store image data in 8x8 DCT tiles in the frequency domain. To
restore an 8x8 tile in the image domain, an IDCT is computed, using up
to 64 coefficients.

If a JPEG with large dimensions is drawn at small scale on screen,
pdfium used to completely decompress the JPEG to large size, doing a
full IDCT for each tile, and then scale it down at decode time.

Using scale_denom, pdfium can instead decode the image at 1/8th (or
1/4th, 1/2th) its size (in each dimension) by not doing the full IDCT,
but instead only taking the one DC term (for 1/8th), or 2x2 or 4x4
coefficents (for 1/4th, 1/2th).

This not just saves work decoding the image, it also outputs a smaller
image that is faster to downsample when painting it. For most images,
this shouldn't look perceptively different than downsampling from the
large bitmap.

This kind of matches what Blink does when decoding JPEGs [1], and
morally matches what pdfium already does for JPEG2000s after
https://pdfium-review.googlesource.com/c/pdfium/+/99970.

(Note that CPDF_ImageRenderer::StartLoadDIBBase() in
cpdf_imagerenderer.cpp passes in the page size as max_size_required,
which cpdf_dib.cpp uses to compute resolution_levels_to_skip. So image
decoding currently isn't limited to the size the image is drawn at, but
by the size of the bitmap the whole page is drawn at. This can be be
improved later.)

Depending on what data the encoder writes in padding rows / columns for
images that aren't a multiple of the MCU size, the code might produce
artifacts in the last row or column. So only do this for images with
sizes a multiple of the MCU for now, see also https://crbug.com/890745
and the discussion on
https://github.com/libjpeg-turbo/libjpeg-turbo/issues/297. This is
unfortunate, but currently there's no way to do better without hacks.
(This also matches what Blink does.)

Timing (hyperfine, --render-repeats): a 3072x2304 4:4:4 image drawn onto
a 64pt page decodes in 40ms at scale_denom 8 vs 84ms at full resolution.

1:
https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder.cc;l=430
...but evidently this is much harder to hit now then when that code was
initially added, probably because JPEGs in Chrome now go down the
DecodeYUV path, which doesn't set scale_denom (yet?).

Depends on new baseline in
https://pdfium-review.googlesource.com/c/pdfium_tests/+/151570
and updates DEPS to pick it up.

Plumb the existing resolution_levels_to_skip (computed in
CPDF_DIB::StartLoadDIBBase from the render device size) into the DCT
path. CreateDCTDecoder maps it to a libjpeg scale_denom of 1/2/4/8
(capped at 8, the largest power-of-two DCT scaling libjpeg supports) and
passes it to JpegModule::CreateDecoder. JpegDecoder sets
cinfo.scale_denom before jpeg_start_decompress and reports the
scaled-down output dimensions (ceil(dim/scale_denom), matching
jpeg_core_output_dimensions). The DIB then adopts the decoder's reduced
dimensions, mirroring the JPX path. libjpeg performs the reduction
cheaply inside the IDCT (e.g. 1/8 uses a 1x1 IDCT), so far fewer pixels
are produced for images shown much smaller than native.

The calls to SetWidth()/SetHeight() are a slight behavior change for all
JPEG data for invalid files: Previously, the code used width/height from
the PDF dict, now it gets the size from the decoder. These sizes are
supposed to match, but if they aren't, pdfium now uses the other one.
This is arguably better, as that size dictates how much data is actually
present.

Bug: 531782505
Change-Id: I66409898d028685813b274e063aedf52200be0dd
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/151550
Commit-Queue: Nico Weber <thakis@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Nico Weber <thakis@google.com>
2026-07-08 12:57:29 -07:00