Skip to content

fix(background): rasterize the heropattern tile at its target scale - #251

Merged
LeadcodeDev merged 0 commit into
fix/heropattern-colour-escapefrom
fix/heropattern-rasterize-scale
Sep 22, 2026
Merged

LeadcodeDev merged 0 commit into
fix/heropattern-colour-escapefrom
fix/heropattern-rasterize-scale

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Severity Medium, category quality. Location: crates/rustmotion/src/engine/render/background.rs:605

Impact

tile_w = def.width * cfg.scale is computed at line 581 and used only for the overscan margin; the pixmap at 605-606 is sized from the unscaled def.width/def.height and resvg renders with Transform::default() (identity). The magnification then happens on the raster, via the shader matrix, with SamplingOptions::default() — which I read in skia-safe-0.99.0/src/core/sampling_options.rs:70-80: filter: FilterMode::Nearest, mipmap: MipmapMode::None. So heropattern with scale: 4 on a 1080p frame draws hard 4x4 pixel blocks from a 20x20 source raster, when the source is vector art that resvg could have rendered crisply at any size. The whole point of shipping 87 SVG patterns is lost above scale 1. It is also wasteful below 1: a scale: 0.25 pattern is rasterized at full size and then point-sampled down, aliasing badly.

Fix

Size the pixmap at the scaled tile ((def.width * cfg.scale).ceil() x (def.height * cfg.scale).ceil(), clamped to a sane maximum) and pass tiny_skia::Transform::from_scale(cfg.scale, cfg.scale) to resvg::render, then drop the shader matrix so the tile is used 1:1. At minimum, pass SamplingOptions::new(FilterMode::Linear, MipmapMode::Linear) instead of the nearest-neighbour default.

Evidence the audit read

let pw = def.width.ceil() as u32;
    let ph = def.height.ceil() as u32;
    let Some(mut pixmap) = tiny_skia::Pixmap::new(pw, ph) else {
        return;
    };
    resvg::render(&tree, tiny_skia::Transform::default(), &mut pixmap.as_mut());
...
    let matrix = if cfg.scale != 1.0 {
        Some(skia_safe::Matrix::scale((cfg.scale, cfg.scale)))
    } else { None };
    let Some(shader) = tile_image.to_shader(
        (skia_safe::TileMode::Repeat, skia_safe::TileMode::Repeat),
        skia_safe::SamplingOptions::default(), matrix.as_ref(),

Stacked on fix/heropattern-colour-escape, which carries the previous finding of this workstream. GitHub shows only this finding's diff; merge in order.

Part of the September 2026 audit remediation chantier. Refs #220 (RM-22).

@LeadcodeDev LeadcodeDev added the bug Something isn't working label Sep 21, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 21, 2026
@LeadcodeDev
LeadcodeDev force-pushed the fix/heropattern-colour-escape branch from 11ed92d to 14f8d7e Compare September 22, 2026 06:11
@LeadcodeDev
LeadcodeDev force-pushed the fix/heropattern-rasterize-scale branch from ae19875 to 578fe99 Compare September 22, 2026 06:11
@LeadcodeDev
LeadcodeDev force-pushed the fix/heropattern-colour-escape branch from 14f8d7e to 3163f12 Compare September 22, 2026 08:35
@LeadcodeDev
LeadcodeDev force-pushed the fix/heropattern-rasterize-scale branch 2 times, most recently from b2934dc to a07e8ad Compare September 22, 2026 08:45
@LeadcodeDev
LeadcodeDev merged commit a07e8ad into fix/heropattern-colour-escape Sep 22, 2026
2 of 3 checks passed
@LeadcodeDev
LeadcodeDev force-pushed the fix/heropattern-colour-escape branch from 3163f12 to c760058 Compare September 22, 2026 08:45
@LeadcodeDev
LeadcodeDev deleted the fix/heropattern-rasterize-scale branch September 29, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant