Skip to content

feat(content): add ContentInterpreter::with_image_downscale - #17

Open
lFLouSel wants to merge 1 commit into
Xero-Team:mainfrom
lFLouSel:feat/with-image-downscale
Open

lFLouSel wants to merge 1 commit into
Xero-Team:mainfrom
lFLouSel:feat/with-image-downscale

Conversation

@lFLouSel

@lFLouSel lFLouSel commented Aug 5, 2026

Copy link
Copy Markdown

Motivation

CAD sheets and scans embed very large rasters (e.g. a 2769x3019 background, or an A0 scan) that occupy a far smaller area on the page. The decoded image is cached at full source resolution -- tens to hundreds of MB held in the image cache even though the renderer immediately minifies it on draw, and re-minifies on every repeated Do.

Change

with_image_downscale(render_scale) opts in: after decode (and mask fold), an image whose device footprint under the current CTM is below 0.5x per axis -- exactly the point where the CPU backend already box-downscales on draw -- is pre-shrunk to that target with the same box filter (box_downscale_rgba, mirroring the renderer's own).

The rasterized output is therefore byte-identical; only the cached image (and repeated per-draw downscaling) shrinks. 0.0 (default) disables the pass, so behaviour is unchanged unless a caller opts in with the same scale it hands the renderer.

Test

image_downscale_to_footprint_shrinks_oversized_and_is_a_noop_otherwise exercises the resize helper directly: a 0.2x-minified 1000px image shrinks to its 200px footprint (and its RGBA buffer length matches), a 0.8x image (above the threshold) is left alone, and scale 0 disables the pass.

Testing notes (please read)

This test targets the maybe_downscale_to_footprint helper directly, which is deterministic and reliable. It does not assert the full decode -> cache path end-to-end (interpreting a real PDF with an oversized image and inspecting the cached image dimensions), because that needs a decodable image fixture and access to the ImageCache internals, which is awkward to assert headlessly. If you'd prefer an integration test at that level (or a different threshold/behaviour), I'm happy to adjust.

@lFLouSel
lFLouSel requested a review from YUZHEthefool as a code owner August 5, 2026 13:46
Pre-downscale decoded images to their on-page device footprint before caching.

Motivation: CAD sheets and scans embed very large rasters (e.g. a 2769x3019
background, or an A0 scan) that occupy a far smaller area on the page. The
decoded image is cached at full source resolution -- tens to hundreds of MB
held in the image cache even though the renderer immediately minifies it on
draw, and re-minifies on every repeated `Do`.

with_image_downscale(render_scale) opts in: after decode (and mask fold), an
image whose device footprint under the current CTM is below 0.5x per axis --
exactly the point where the CPU backend already box-downscales on draw -- is
pre-shrunk to that target with the same box filter. The rasterized output is
therefore byte-identical; only the cached image (and repeated per-draw
downscaling) shrinks. 0.0 (default) disables the pass, so behaviour is
unchanged unless a caller opts in with the same scale it hands the renderer.

Test: image_downscale_to_footprint_shrinks_oversized_and_is_a_noop_otherwise
exercises the resize helper directly -- a 0.2x-minified 1000px image shrinks to
its 200px footprint, a 0.8x image (above threshold) is left alone, and scale 0
disables it.
@github-actions github-actions Bot added area: content Content, display lists, fonts, images, and color size: S 50-199 changed lines, excluding Cargo.lock enhancement New feature or request labels Aug 5, 2026

@YUZHEthefool YUZHEthefool left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the optimization work. I cannot merge this version yet because it downsamples the first decoded image before caching it only by ObjectId. The same Image XObject may be painted under several CTMs: a later larger paint will reuse the first small raster, while a later smaller paint will double-filter it. Please retain the decoded source image and cache renderer-specific derivatives by target size, with a small-then-large two-CTM regression test.

Doing the resample in the interpreter also changes semantics for non-CPU consumers such as WGPU, SVG, and PPTX; the claimed byte-identical behavior is not true across backends. Please either move it to a backend-specific optimization with defined output behavior, or redesign the cache at the renderer boundary.

@xero-team-bot

xero-team-bot Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

⚠️ 此 PR 已与目标分支冲突,需要 rebase。

git fetch origin main
git rebase origin/main
# 解决冲突后
git push --force-with-lease

冲突解决后会自动移除 needs-rebase 标签。
_(Xero-Team/zpdf · 由 xero-bot 自动检测)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: content Content, display lists, fonts, images, and color enhancement New feature or request needs-rebase size: S 50-199 changed lines, excluding Cargo.lock

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants