From ce5b0230682d914ceaa22ba5ddbfaff44a3938d9 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 18 Oct 2026 14:18:09 -0700 Subject: [PATCH] Type: type edges are drawn offscreen on at most 32,768 pixels a side `Renderer::draw_type_edges` let its offscreen drawing grow to `u16::MAX` pixels a side, the size at which vello_cpu panics. In a frame drawn coarser than the output, a thin strip of type could take more than 32,768 output pixels a side while staying within the pixel budget. The offscreen drawing now uses the frame's own pixels when the output's would be more than `MAX_RASTER_SIDE` a side, and type whose offscreen drawing would still be larger than that is drawn as Sharp, as `fx.rs` limits its offscreen rasters to `MAX_RASTER_SIDE`. --- crates/render/src/lib.rs | 16 +++++++++++----- crates/render/src/tests_typeedges.rs | 17 ++++++++++++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index eeb0a5c35..790874fb4 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -1914,8 +1914,8 @@ impl Renderer { /// strokes in opaque black, which gives the share `c` of each pixel they cover; the pixel's /// color and opacity are then scaled by [`edge_coverage`] of `c` divided by `c`, so the opacity /// of the paints is kept. Where the output's pixels would number more than the frame's there - /// and more than [`fx::PIXEL_BUDGET`] (a frame drawn coarser than the output), the frame's own - /// pixels are used. + /// and more than [`fx::PIXEL_BUDGET`], or be more than [`MAX_RASTER_SIDE`] a side (a frame + /// drawn coarser than the output), the frame's own pixels are used. /// /// The pixels are kept (`type_edges`). In a later frame they are placed without drawing the /// type offscreen while the object, the output's zoom and rotation, the method and edge @@ -1928,7 +1928,8 @@ impl Renderer { /// inline graphics, type whose own fills or strokes include a brush stroke or a blend mode /// other than Normal, type that overprints while Overprint Preview is on, and type that shows a /// substitution highlight on screen. Drawn offscreen, those would blend with nothing or take - /// the method on art that is not the characters'. + /// the method on art that is not the characters'. It also returns false for type whose + /// offscreen drawing would be more than [`MAX_RASTER_SIDE`] pixels a side. fn draw_type_edges(&mut self, ctx: &mut RenderContext, f: &Frame, n: &Node, t: &TextObject, g: &TextGeom) -> bool { if proof::overprints(f.opts) && t.runs.iter().any(|r| r.style.overprint_fill || r.style.overprint_stroke) { return false; @@ -1972,6 +1973,10 @@ impl Renderer { // Whole pixels of the output. let (x0, y0) = (on.x0.floor(), on.y0.floor()); let (Ok(w), Ok(h)) = (u16::try_from((on.x1 - x0).ceil() as u64), u16::try_from((on.y1 - y0).ceil() as u64)) else { return false }; + // (vello_cpu panics drawing near `u16::MAX` pixels a side.) + if u32::from(w) > MAX_RASTER_SIDE || u32::from(h) > MAX_RASTER_SIDE { + return false; + } let view = Affine::translate((-x0, -y0)) * output; let visible = view.inverse().transform_rect_bbox(Rect::new(0.0, 0.0, f64::from(w), f64::from(h))); let px = 1.0 / view.determinant().abs().sqrt().max(1e-12); @@ -2355,10 +2360,11 @@ fn type_reach(n: &Node, t: &TextObject, g: &TextGeom) -> Rect { /// The map from a frame's pixels to the pixels [`Renderer::draw_type_edges`] draws type on, for /// the region `on` of the frame's pixels: `grid`, which maps them to the output's, unless the /// output's pixels there would number more than the frame's and more than [`fx::PIXEL_BUDGET`], -/// and then the frame's own pixels (the identity). +/// or be more than [`MAX_RASTER_SIDE`] a side, and then the frame's own pixels (the identity). fn edge_grid(on: Rect, grid: Affine) -> Affine { let area = |r: Rect| r.width() * r.height(); - if area(grid.transform_rect_bbox(on)) > area(on).max(fx::PIXEL_BUDGET) { Affine::IDENTITY } else { grid } + let (out, side) = (grid.transform_rect_bbox(on), f64::from(MAX_RASTER_SIDE)); + if area(out) > area(on).max(fx::PIXEL_BUDGET) || out.width() > side || out.height() > side { Affine::IDENTITY } else { grid } } /// Pixel `p` of type the anti-aliasing method `m` draws, where the type covers `c` (out of 255) of diff --git a/crates/render/src/tests_typeedges.rs b/crates/render/src/tests_typeedges.rs index 0cd9cca51..e241367ac 100644 --- a/crates/render/src/tests_typeedges.rs +++ b/crates/render/src/tests_typeedges.rs @@ -253,7 +253,8 @@ fn type_with_an_alternate_glyph_highlight_is_drawn_as_sharp() { } /// The type is drawn on the output's pixels unless, in a frame coarser than the output, they -/// would number more than the frame's there and more than the effects' pixel budget. +/// would number more than the frame's there and more than the effects' pixel budget, or be more +/// than `MAX_RASTER_SIDE` a side. #[test] fn type_edges_are_drawn_on_at_most_the_budgets_pixels() { let finer = Affine::scale(0.25); @@ -261,6 +262,20 @@ fn type_edges_are_drawn_on_at_most_the_budgets_pixels() { let coarser = Affine::scale(4.0); assert_eq!(edge_grid(Rect::new(0.0, 0.0, 100.0, 100.0), coarser), coarser, "160,000 pixels"); assert_eq!(edge_grid(Rect::new(0.0, 0.0, 1000.0, 1000.0), coarser), Affine::IDENTITY, "16 million pixels"); + assert_eq!(edge_grid(Rect::new(0.0, 0.0, 10_000.0, 1.0), coarser), Affine::IDENTITY, "40,000 pixels a side"); +} + +/// Type whose offscreen drawing would be more than `MAX_RASTER_SIDE` pixels a side is drawn as +/// Sharp type is. +#[test] +fn type_wider_than_the_raster_limit_is_drawn_as_sharp() { + // "Hamburg" at 25,000%, about 37,000 pixels wide, on four rows through its letters. + let view = Affine::translate((-2_000.0, -12_500.0)) * Affine::scale(250.0); + let draw = |m| Renderer::new().render(&page(m, None), 40_000, 4, view, &RenderOptions::default()).pixels; + let sharp = draw(TypeAntiAlias::Sharp); + let a: Vec = sharp.chunks(4).map(|p| p[3]).collect(); + assert!(partial(&a) > 0, "the rows cross the edges of the letters"); + assert!(draw(TypeAntiAlias::None) == sharp, "None type is drawn as Sharp"); } /// The offscreen drawing takes in the farthest a stroke reaches: a mitered character stroke's