diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index 5ee64398e..dea7cf764 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -1958,11 +1958,12 @@ impl Renderer { /// the type is drawn offscreen again. /// /// Returns false, having drawn nothing, for type that is drawn as Sharp draws it: type with - /// 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'. It also returns false for type whose - /// offscreen drawing would be more than [`MAX_RASTER_SIDE`] pixels a side. + /// inline graphics, type with color glyphs (bitmap or COLR), 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'. 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 { if t.runs.iter().any(|r| r.inline.is_some()) || n.appearance.items.iter().any(|i| i.visible() && (i.blend() != normal || brush(i))) { return false; } + // Color glyphs paint their own pixels and layers, whose opacity is not the characters' coverage. + if !g.images.is_empty() || !g.colours.is_empty() { + return false; + } if f.opts.highlight_substitutions { let setup = &f.doc.setup; let shows = |on: bool, path: &BezPath| on && !path.elements().is_empty(); diff --git a/crates/render/src/tests_typeedges.rs b/crates/render/src/tests_typeedges.rs index 855f20d46..7d5219539 100644 --- a/crates/render/src/tests_typeedges.rs +++ b/crates/render/src/tests_typeedges.rs @@ -201,6 +201,39 @@ fn type_with_an_inline_graphic_is_drawn_as_sharp() { } } +/// Type with a color glyph (a bitmap in `sbix` or `CBDT`, or COLR v0 or v1 layers) is drawn as +/// Sharp draws it, so the glyph's own opacity is kept. +#[test] +fn type_with_a_color_glyph_is_drawn_as_sharp() { + use vectorcraft_text::test_fonts::{COLOUR_CHAR, colour_bitmap_family, colour_bitmap_font, colr_family, colr_font}; + let _adding = crate::tests_fontchange::adding_fonts(); + let fonts = [ + (colour_bitmap_family(true), colour_bitmap_font(true)), + (colour_bitmap_family(false), colour_bitmap_font(false)), + (colr_family(false), colr_font(false)), + (colr_family(true), colr_font(true)), + ]; + for (family, font) in fonts { + let Some(font) = font else { panic!("{family}: the test font is built") }; + vectorcraft_text::FontDb::global().add_font(font); + let draw = |m: TypeAntiAlias| { + let mut d = Document::new(160.0, 160.0); + let style = CharStyle { font_family: family.into(), size: 100.0, ..Default::default() }; + let mut t = TextObject::point(Point::new(20.3, 120.4), &COLOUR_CHAR.to_string(), style); + t.anti_alias = m; + let n = Node::new(d.alloc_id(), NodeKind::Text(Box::new(t))); + let l = d.layers[0].id; + d.insert(Some(l), 0, n).unwrap(); + Renderer::new().render(&d, 160, 160, Affine::IDENTITY, &RenderOptions::default()).pixels + }; + let sharp = draw(TypeAntiAlias::Sharp); + assert!(sharp.chunks(4).any(|p| p[3] > 0), "{family}: the glyph is drawn"); + for m in [TypeAntiAlias::None, TypeAntiAlias::Crisp, TypeAntiAlias::Strong] { + assert!(draw(m) == sharp, "{family}, {m:?}"); + } + } +} + /// In a supersampled export, None type in a group drawn offscreen (it isolates art that blends) /// still has hard edges on the exported pixels, wherever the group's layer starts. #[test] diff --git a/docs/mcp.md b/docs/mcp.md index 3a7f1b313..3bdebb39d 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -4251,8 +4251,9 @@ method does, on the pixels of the picture being drawn (the canvas at its zoom, a - `strong` draws a pixel the type covers by a share c at `2c`, at most 1, so the type looks heavier. The opacity of the type's paints stays as it is inside the characters. Type is drawn as Sharp draws it when it holds -inline graphics, when its own fills or strokes in the Appearance panel include a brush stroke or a blend mode other than -Normal, when it overprints while Overprint Preview is on, and on the canvas while it shows a substitution highlight. +inline graphics or color glyphs (bitmap or COLR), when its own fills or strokes in the Appearance panel include a brush +stroke or a blend mode other than Normal, when it overprints while Overprint Preview is on, and on the canvas while it +shows a substitution highlight. With Preferences › General › Anti-aliased Artwork off, the canvas draws all its art with hard edges, type included, and so does an export whose own Anti-aliasing option is None. SVG, PDF and EPS export are unchanged and carry no setting. Reset Panel sets Sharp. Saved as `antiAlias` only when not `sharp`. One undo step.