mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-29 04:46:50 -06:00
Merge fix/621 into batch 13
This commit is contained in:
@@ -116,7 +116,7 @@ pub(crate) fn rule(r: figcraft_render::FillRule) -> FillRule {
|
||||
}
|
||||
|
||||
/// The renderer's stroke fill rule: even-odd ring paths for aligned strokes, else non-zero.
|
||||
fn stroke_rule(n: &Node, closed: bool) -> FillRule {
|
||||
pub(crate) fn stroke_rule(n: &Node, closed: bool) -> FillRule {
|
||||
if n.stroke.sides.is_some() || (n.stroke.dashes.is_empty() && closed && n.stroke.align != StrokeAlign::Center) {
|
||||
FillRule::EvenOdd
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::collections::BTreeSet;
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
|
||||
use figcraft_doc::{Node, NodeKind, StrokeAlign, StrokeProps};
|
||||
use figcraft_doc::{Node, NodeKind};
|
||||
use figcraft_geom::{Affine, BezPath};
|
||||
use figcraft_text::fontdb::FontBytes;
|
||||
use figcraft_text::{Face, Glyph, TextLayout};
|
||||
@@ -16,7 +16,7 @@ use krilla::text::{Font, GlyphId, KrillaGlyph};
|
||||
|
||||
use figcraft_color::{GradientKind, Paint, PaintKind};
|
||||
|
||||
use crate::draw::{Exporter, above, affine_ok, blend, to_xf, vector_paint};
|
||||
use crate::draw::{Exporter, above, affine_ok, blend, stroke_rule, to_xf, vector_paint};
|
||||
use crate::raster::Raster;
|
||||
|
||||
/// Glyphs of one face and size on one line, written as one text run.
|
||||
@@ -84,11 +84,11 @@ pub(crate) fn draw_text(ex: &mut Exporter, s: &mut Surface, n: &Node, xf: Affine
|
||||
}
|
||||
}
|
||||
ex.place_inner(s, inner);
|
||||
let props = StrokeProps { align: StrokeAlign::Center, ..n.stroke.clone() };
|
||||
// Glyph outlines are closed: inside and outside strokes clip to them, as on the canvas (#615).
|
||||
for outlines in &stroked {
|
||||
let area = figcraft_pathops::stroke_center(outlines, props.weight, &props);
|
||||
let area = figcraft_render::draw::stroke_area(n, outlines, true);
|
||||
for p in &n.strokes {
|
||||
ex.fill(s, &area, FillRule::NonZero, p, w, h, gxf);
|
||||
ex.fill(s, &area, stroke_rule(n, true), p, w, h, gxf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -621,11 +621,11 @@ fn draw_content(ctx: &Ctx, pix: &mut Pixmap, n: &Node, xf: Affine, depth: usize,
|
||||
f(pix);
|
||||
}
|
||||
if n.has_visible_stroke() {
|
||||
let props = figcraft_doc::StrokeProps { align: StrokeAlign::Center, ..n.stroke.clone() };
|
||||
// Glyph outlines are closed: inside and outside strokes clip to them (#615).
|
||||
for path in keys.iter().filter_map(|k| by_style.get(k)) {
|
||||
let s = figcraft_pathops::stroke_center(path, props.weight, &props);
|
||||
let s = stroke_area(n, path, true);
|
||||
if let Some(ts) = ts_path(&s) {
|
||||
fill_with(pix, &ts, &n.strokes, w, h, gxf, FillRule::Winding, ctx, None);
|
||||
fill_with(pix, &ts, &n.strokes, w, h, gxf, stroke_rule(n, true), ctx, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,3 +630,35 @@ fn changing_the_colour_profile_converts_or_keeps_values() {
|
||||
let seen = draw(&kept);
|
||||
assert!(seen[0] > before[0] && seen[2] < before[2], "kept values look more saturated: {seen:?} vs {before:?}");
|
||||
}
|
||||
|
||||
// ---- strokes on text ----
|
||||
|
||||
#[test]
|
||||
fn text_strokes_follow_their_alignment() {
|
||||
// #615: as on other shapes (and in SVG export), an inside stroke stays in the glyphs, a centre
|
||||
// one reaches half its weight past them and an outside one all of it.
|
||||
figcraft_text::scan_system_fonts();
|
||||
let left_edge = |align: StrokeAlign| -> Option<u32> {
|
||||
let (mut d, p) = page();
|
||||
let mut td = figcraft_doc::TextData::new("I");
|
||||
td.style.font_size = 120.0;
|
||||
let t = d.create(p, NodeKind::Text(td), "T", 60.0, 30.0, 1.0, 1.0).unwrap();
|
||||
let n = d.get_mut(t).unwrap();
|
||||
n.fills.clear();
|
||||
n.strokes = vec![Paint::solid(hex("#000000"))];
|
||||
n.stroke.weight = 12.0;
|
||||
n.stroke.align = align;
|
||||
let (w, h) = figcraft_text::auto_size(n)?;
|
||||
(n.width, n.height) = (w, h);
|
||||
let pix = render(&d);
|
||||
(0..200).find(|x| (0..200).any(|y| px(&pix, *x, y)[0] < 128))
|
||||
};
|
||||
let (Some(inside), Some(centre), Some(outside)) =
|
||||
(left_edge(StrokeAlign::Inside), left_edge(StrokeAlign::Center), left_edge(StrokeAlign::Outside))
|
||||
else {
|
||||
eprintln!("no font to draw text with; skipping");
|
||||
return;
|
||||
};
|
||||
assert!((5..=7).contains(&(inside - centre)), "centre reaches half the weight past inside: {inside} → {centre}");
|
||||
assert!((5..=7).contains(&(centre - outside)), "outside reaches the rest: {centre} → {outside}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user