-
Notifications
You must be signed in to change notification settings - Fork 204
Description
Summary
Images downscaled by more than 2× display severe visual artifacts including pixelation, jagged edges, and loss of fine details like text and thin lines.
Technical Cause
When a transform has scale < 1.0 (downscaling), the renderer inverts it to map from output space back to source image space. The x_advance and y_advance vectors are calculated from this inverted transform to determine how far to step in the source image for each output pixel.
Example with scale(0.125) (8× downscale):
- User transform:
scale(0.125)— render 800px image at 100px width - Internal transform:
scale(0.125).inverse()=scale(8.0)— maps output→source - Advances computed:
x_advance = (8.0, 0.0),y_advance = (0.0, 8.0) - During rasterization:
- The SIMD rasterizer processes 4 rows at once, calculating positions as
[0, 1, 2, 3] × y_advance + start_pos - For output starting at position (10, 5) → source position (80, 40)
- Four SIMD lanes sample at y-positions spaced 8 pixels apart: [40, 48, 56, 64]
- Bilinear filter applies fixed offsets
[-0.5, 0.5], sampling 2 pixels at each position - Result: Samples only 8 pixels total; should ideally sample 32 pixels (4 rows × 8 pixels per row) — 75% of data skipped
- The SIMD rasterizer processes 4 rows at once, calculating positions as
The x_advance and y_advance correctly determine where to sample based on the scale factor, but the sampling kernel remains fixed at 2×2 (bilinear) or 4×4 (bicubic) pixels regardless of how large the advance steps are. For proper 8× downscaling, each output pixel should ideally average an 8×8 block (64 pixels), but instead samples only 4-16 pixels, skipping 60-48 pixels respectively and causing severe aliasing and loss of fine details.
Solution
- Add progressive downsampling for better image quality at small scales