* M2: a shrunk image on a turned or mirrored page snaps to whole pixels (#5530)
hayro patch 14 (`snap_minified_image`) shrinks an interpolated image once
to its whole-pixel device rectangle and draws that 1:1, instead of
shrinking to a rounded-up size and resampling again at a fractional
scale and offset. It only accepted an upright transform (a > 0, d > 0,
b = c = 0), so every image on a page under /Rotate 90, 180 or 270 -- and
any image placed turned or mirrored by its own matrix -- still took the
double resample. That is #5530: a scanned page that matches PDFium
upright drifts and softens when the same page is rotated.
The snap now accepts every transform whose axes lie along the device's
(the eight quarter-turns and mirrors) and returns the 1:1 transform
that draws the shrunk raster from the snapped corner, one source-axis
step a whole device pixel, in whichever direction that axis runs.
Skewed and freely rotated images keep the old path.
On a synthetic page (crop box and scale on whole pixels, image at
fractional coordinates), /Rotate 90, 180 and 270 renders turned back
differed from the upright render in 72% of pixels, by up to 200 grey
levels; now they are identical. Non-interpolated images were already
identical and stay so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M2: snap only when the dropped skew is invisible, and only a resize that succeeded
Review of #5640:
- The snap judged an image axis-aligned by a tolerance on its
coefficients (1e-4 of the scale). For a very wide image a skew that
small still moves the far edge by more than a pixel (40 000 samples
at 4e-5: 1.6 px), and the snap flattened it. It now measures what it
drops: how far each off-axis term moves the far edges, at most a
hundredth of a device pixel (SNAP_SKEW). Upright and turned alike.
- A resize that fails hands back the source raster at source size; the
1:1 snapped transform then drew it at that size. The snapped
transform is now used only when the shrink produced the snapped
size; otherwise the full transform, as before.
- The mirrored case of the edge test drew a uniform image, which looks
the same mirrored; it now also mirrors a half-black image and checks
which side the black lands on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M2: the turned-page test draws every kind of image the snap reaches
The /Rotate oracle test drew one 8-bit grey image. Stencil masks (CCITT
scans often are one), 1-bit images and images with a soft mask reach
the snap through other paths, so it now draws all five: 8-bit and
1-bit grey, a stencil mask, a soft mask of the image's size and one of
another size. On main, every interpolated kind is off by 113-217 levels
over 63-73% of the image when turned, and so is a non-interpolated
image with a smaller soft mask (110 levels, 16%): hayro patch 15
filters such a mask whatever /Interpolate says, so it is snapped
upright and was not when turned. All are identical with the fix; the
other non-interpolated kinds were already identical and stay as guards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>