Refactor render for strict Clippy checks

This commit is contained in:
Dylan M. Taylor
2026-10-28 11:39:26 +00:00
parent be9b8f360e
commit 072dd02f73
18 changed files with 158 additions and 159 deletions
+6 -7
View File
@@ -446,8 +446,7 @@ mod tests {
doc,
&Cache::new(),
&[Placed { spread: SpreadRef::Doc(0), xf: Affine::IDENTITY }],
200,
140,
(200, 140),
Affine::translate((30.0, 10.0)),
&RenderOptions { blend_space_view: true, background: Some([180, 180, 180, 255]), ..Default::default() },
)
@@ -589,8 +588,7 @@ mod tests {
&doc,
&Cache::new(),
&[Placed { spread: SpreadRef::Doc(0), xf: Affine::IDENTITY }],
200,
140,
(200, 140),
Affine::translate((30.0, 10.0)),
&cyan,
);
@@ -649,10 +647,11 @@ mod tests {
renderer.threads = 0;
let placements = [Placed { spread: SpreadRef::Doc(0), xf: Affine::IDENTITY }];
let options = RenderOptions { paper: false, blend_space_view: true, ..Default::default() };
let out = renderer.render(&doc, &Cache::new(), &placements, 160, 120, Affine::IDENTITY, &options);
let out = renderer.render(&doc, &Cache::new(), &placements, (160, 120), Affine::IDENTITY, &options);
assert!(out.pixel(30, 40)[3].abs_diff(64) <= 1, "half-opacity object inside a half-opacity group is quarter opaque");
let cmy = renderer.render(&doc, &Cache::new(), &placements, 160, 120, Affine::IDENTITY, &RenderOptions { plate: Some(4), ..options.clone() });
let k = renderer.render(&doc, &Cache::new(), &placements, 160, 120, Affine::IDENTITY, &RenderOptions { plate: Some(3), ..options });
let cmy =
renderer.render(&doc, &Cache::new(), &placements, (160, 120), Affine::IDENTITY, &RenderOptions { plate: Some(4), ..options.clone() });
let k = renderer.render(&doc, &Cache::new(), &placements, (160, 120), Affine::IDENTITY, &RenderOptions { plate: Some(3), ..options });
for ((out, cmy), k) in out.pixels.as_chunks::<4>().0.iter().zip(cmy.pixels.as_chunks::<4>().0).zip(k.pixels.as_chunks::<4>().0) {
assert_eq!(cmy[3], k[3], "all ink passes have the same coverage");
assert_eq!(out[3], cmy[3], "conversion preserves coverage");
+1 -1
View File
@@ -1,5 +1,5 @@
use super::types::{Frame, Rendered};
use super::*;
use super::{Affine, BlendMode, Compose, DcBlend, Document, Item, Mix, Rect, RenderContext, peniko, vello_cpu};
const CMYK_N: usize = 17;
+3 -3
View File
@@ -257,8 +257,8 @@ mod tests {
let mut r = Renderer::new();
r.threads = 0;
let opts = RenderOptions::default();
let mut img = r.render(old, &cache, &placed, w, h, view, &opts);
let full = r.render(new, &cache, &placed, w, h, view, &opts);
let mut img = r.render(old, &cache, &placed, (w, h), view, &opts);
let full = r.render(new, &cache, &placed, (w, h), view, &opts);
// Patch the damaged pixels.
let regions = damage(old, new).unwrap();
let u = regions.iter().map(|(_, r)| *r).reduce(|a, b| a.union(b)).unwrap();
@@ -266,7 +266,7 @@ mod tests {
let y0 = ((u.y0 * zoom).floor().max(0.0)) as u32;
let x1 = ((u.x1 * zoom).ceil() as u32).min(w);
let y1 = ((u.y1 * zoom).ceil() as u32).min(h);
let patch = r.render(new, &cache, &placed, x1 - x0, y1 - y0, Affine::translate((-(x0 as f64), -(y0 as f64))) * view, &opts);
let patch = r.render(new, &cache, &placed, (x1 - x0, y1 - y0), Affine::translate((-(x0 as f64), -(y0 as f64))) * view, &opts);
for y in y0..y1 {
for x in x0..x1 {
let pi = (((y - y0) * (x1 - x0) + (x - x0)) * 4) as usize;
+1 -1
View File
@@ -1,5 +1,5 @@
use super::pdf_raster::{is_pdf, pdf_page_size, render_pdf_page};
use super::*;
use super::{Pixmap, vello_cpu};
/// Decode encoded image bytes into a premultiplied pixmap.
pub fn decode_pixmap(bytes: &[u8]) -> Option<Pixmap> {
+4 -1
View File
@@ -1,6 +1,9 @@
use super::color::{HIDDEN_LAYERS, blend_mode, color_of, overprints, rect_overlaps, screen_color, set_fill_paint, spread_blends};
use super::types::{DisplayQuality, Frame};
use super::*;
use super::{
Affine, Arc, BezPath, BlendMode, Compose, ComposedStory, Content, DcBlend, Item, Mix, RenderContext, Renderer, Shape, ShownOn, SpreadRef,
StoryId, Vec2, blendspace, corners, fx, grid, kurbo, peniko,
};
/// An item's fill under its content; an overprinting fill multiplies into what is beneath.
fn draw_body_fill(ctx: &mut RenderContext, f: &Frame, it: &Item, bp: &BezPath, xf: Affine) {
+9 -16
View File
@@ -1,7 +1,9 @@
use super::color::{PLATE, RICH_BLACK, blend_space_view, rect_overlaps};
use super::decode::now;
use super::types::{Frame, FrameStats, MAX_PAGE_PIXELS, MAX_SIDE, Placed, RenderOptions, Rendered, default_threads};
use super::*;
use super::{
Affine, Cache, Document, HashMap, PageSide, Rect, RenderContext, Renderer, Resources, SpreadRef, Vec2, blendspace, kurbo, peniko, text, vello_cpu,
};
impl Renderer {
pub fn new() -> Self {
@@ -37,17 +39,9 @@ impl Renderer {
}
/// Render the given spreads into a `width`×`height` image with `view` (canvas → pixels).
pub fn render(
&mut self,
doc: &Document,
cache: &Cache,
spreads: &[Placed],
width: u32,
height: u32,
view: Affine,
opts: &RenderOptions,
) -> Rendered {
pub fn render(&mut self, doc: &Document, cache: &Cache, spreads: &[Placed], size: (u32, u32), view: Affine, opts: &RenderOptions) -> Rendered {
let start = now();
let (width, height) = size;
RICH_BLACK.with(|r| r.set(opts.rich_black));
PLATE.with(|p| p.set(opts.plate));
if let Some(hit) = self.cmyk_view.as_ref().filter(|entry| entry.matches(doc, spreads, width, height, view, opts, self.threads)) {
@@ -59,7 +53,7 @@ impl Renderer {
// Release the old snapshot before rendering an edited document or resized viewport.
self.cmyk_view = None;
self.cmyk_ran = false;
let image = self.render_uncached(doc, cache, spreads, width, height, view, opts);
let image = self.render_uncached(doc, cache, spreads, size, view, opts);
// Kept only when the ink passes ran: other views render at their usual cost anyway.
if self.cmyk_ran && image.pixels.len() <= blendspace::CachedView::MAX_BYTES {
self.cmyk_view = Some(blendspace::CachedView::new(doc, spreads, view, opts, &image, self.stats, self.threads));
@@ -68,18 +62,17 @@ impl Renderer {
image
}
#[allow(clippy::too_many_arguments)]
fn render_uncached(
&mut self,
doc: &Document,
cache: &Cache,
spreads: &[Placed],
width: u32,
height: u32,
size: (u32, u32),
view: Affine,
opts: &RenderOptions,
) -> Rendered {
let start = now();
let (width, height) = size;
let w = width.clamp(1, MAX_SIDE) as u16;
let h = height.clamp(1, MAX_SIDE) as u16;
let mut ctx = match self.ctx.take() {
@@ -156,6 +149,6 @@ impl Renderer {
let view = Affine::scale(scale) * Affine::translate(-r.origin().to_vec2());
let mut o = opts.clone();
o.background = Some([255, 255, 255, 255]);
Some(self.render(doc, cache, &[Placed { spread: SpreadRef::Doc(si), xf: Affine::translate(Vec2::ZERO) }], w, h, view, &o))
Some(self.render(doc, cache, &[Placed { spread: SpreadRef::Doc(si), xf: Affine::translate(Vec2::ZERO) }], (w, h), view, &o))
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
use super::types::Rendered;
use super::*;
use super::{Affine, Arc, HashMap, Pixmap, Rect, pdf_bounds, pdf_hide_layers, pdf_layers, vello_cpu};
/// MIME type guess for encoded image bytes.
/// Is this a PDF file (placed PDFs are graphics)?
+4 -1
View File
@@ -1,7 +1,10 @@
use super::color::{HIDDEN_LAYERS, PLATE, color_of, overprints, plate_pixmap, screen_color, set_fill_paint};
use super::pdf_raster::{is_pdf, shown_box};
use super::types::{DisplayQuality, Frame};
use super::*;
use super::{
Affine, BezPath, BlendMode, Compose, Item, Mix, Rect, RenderContext, Renderer, Shape, StrokeAlign, StrokeType, colorize, images, kurbo,
pdf_layers, peniko, vello_cpu,
};
impl Renderer {
pub(crate) fn draw_stroke(&mut self, ctx: &mut RenderContext, f: &Frame, it: &Item, bp: &BezPath, xf: Affine) {
+2 -2
View File
@@ -64,11 +64,11 @@ fn very_wide_renders_do_not_panic() {
let doc = Document::new(&NewDocument::default());
let cache = Cache::new();
let mut r = Renderer::new();
let img = r.render(&doc, &cache, &[], 70_000, 4, Affine::IDENTITY, &RenderOptions::default());
let img = r.render(&doc, &cache, &[], (70_000, 4), Affine::IDENTITY, &RenderOptions::default());
assert_eq!(img.width, MAX_SIDE);
let page = [Placed { spread: SpreadRef::Doc(0), xf: Affine::IDENTITY }];
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
let img = r.render(&doc, &cache, &page, 70_000, 64, Affine::scale(120.0), &opts);
let img = r.render(&doc, &cache, &page, (70_000, 64), Affine::scale(120.0), &opts);
assert_eq!(img.width, MAX_SIDE);
assert!(r.render_page(&doc, &cache, 0, 500.0, true, &RenderOptions::default()).is_none());
}
+112 -108
View File
@@ -250,6 +250,117 @@ fn line_box(l: &Line) -> Option<Rect> {
Some(Rect::new(x0 - pad, l.baseline - l.ascent - pad, x1 + pad, bottom + pad))
}
fn draw_text_indicators(ctx: &mut RenderContext, f: &Frame, cs: &ComposedStory, ft: &FrameText, m: Affine) {
// Text in fonts that aren't installed: pink behind the glyphs (screen only).
if f.opts.highlight_missing_fonts && cs.styles.iter().any(|s| s.missing_font) {
ctx.set_transform(m);
ctx.set_paint(screen_color([255, 166, 214], 255));
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| g.adv > 0.0 && cs.styles.get(g.style as usize).is_some_and(|s| s.missing_font)) {
ctx.fill_rect(&kurbo::Rect::new(g.x, l.baseline - l.ascent, g.x + g.adv, l.baseline + l.descent));
}
}
}
if f.opts.highlight_keeps && ft.lines.iter().any(|l| l.keep_violation) {
ctx.set_transform(m);
ctx.set_paint(screen_color([255, 236, 120], 255));
for l in ft.lines.iter().filter(|l| l.keep_violation) {
ctx.fill_rect(&kurbo::Rect::new(l.x0, l.baseline - l.ascent, l.x1, l.baseline + l.descent));
}
}
if f.opts.highlight_hj && ft.lines.iter().any(|l| l.hj > 0) {
ctx.set_transform(m);
for l in ft.lines.iter().filter(|l| l.hj > 0) {
let shade = [0u8, 0xF6, 0xEA, 0xD8][l.hj.min(3) as usize];
ctx.set_paint(screen_color([255, shade, [0u8, 0xB0, 0x70, 0x30][l.hj.min(3) as usize]], 255));
ctx.fill_rect(&kurbo::Rect::new(l.x0, l.baseline - l.ascent, l.end_x.max(l.x0 + 1.0), l.baseline + l.descent));
}
}
if f.opts.highlight_custom_tracking && cs.styles.iter().any(|s| s.custom_tracking) {
ctx.set_transform(m);
ctx.set_paint(screen_color([176, 236, 176], 255));
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| g.adv > 0.0 && cs.styles.get(g.style as usize).is_some_and(|s| s.custom_tracking)) {
ctx.fill_rect(&kurbo::Rect::new(g.x, l.baseline - l.ascent, g.x + g.adv, l.baseline + l.descent));
}
}
}
if f.opts.tag_markers && cs.styles.iter().any(|s| s.xml_tag.is_some()) {
ctx.set_transform(m);
let px = 1.0 / m.determinant().abs().sqrt().max(1e-9);
for l in &ft.lines {
let tag_of = |g: &designcraft_compose::PlacedGlyph| cs.styles.get(g.style as usize).and_then(|s| s.xml_tag.clone());
let gs: Vec<_> = l.glyphs.iter().filter(|g| g.len > 0).collect();
let mut i = 0;
while i < gs.len() {
let Some(tag) = tag_of(gs[i]) else {
i += 1;
continue;
};
let mut j = i + 1;
while j < gs.len() && tag_of(gs[j]).as_deref() == Some(tag.as_str()) {
j += 1;
}
let c = f.doc.xml.tags.iter().find(|t| t.name == tag).map_or([100, 100, 220], |t| t.color);
ctx.set_paint(screen_color([c[0], c[1], c[2]], 255));
let (x0, x1) = (gs[i].x, gs[j - 1].x + gs[j - 1].adv);
let (top, bot) = (l.baseline - l.ascent, l.baseline + l.descent);
let (w, t) = (2.5 * px, px.max(0.3));
// [ before the element, ] after it.
for (x, dir) in [(x0, 1.0), (x1, -1.0)] {
ctx.fill_rect(&kurbo::Rect::new(x - t / 2.0, top, x + t / 2.0, bot));
for y in [top, bot - t] {
let (a, b) = if dir > 0.0 { (x, x + w) } else { (x - w, x) };
ctx.fill_rect(&kurbo::Rect::new(a, y, b, y + t));
}
}
i = j;
}
}
}
if f.opts.change_markup && cs.styles.iter().any(|s| s.inserted) {
ctx.set_transform(m);
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| g.adv > 0.0 && cs.styles.get(g.style as usize).is_some_and(|s| s.inserted)) {
ctx.set_paint(screen_color([120, 190, 255], 110));
ctx.fill_rect(&kurbo::Rect::new(g.x, l.baseline - l.ascent, g.x + g.adv, l.baseline + l.descent));
}
}
}
if f.opts.note_indicators
&& let Some(st) = f.doc.story(cs.story).filter(|st| !st.editorial.is_empty())
{
// A small flag at each note anchor, in the notes' amber.
ctx.set_transform(m);
ctx.set_paint(screen_color([232, 160, 32], 255));
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| st.text.get(g.byte..).is_some_and(|t| t.starts_with(designcraft_doc::NOTE_MARK))) {
let h = l.ascent.max(4.0);
let mut p = BezPath::new();
p.move_to((g.x - h * 0.25, l.baseline - h));
p.line_to((g.x + h * 0.25, l.baseline - h));
p.line_to((g.x, l.baseline - h * 0.55));
p.close_path();
ctx.fill_path(&p);
ctx.fill_rect(&kurbo::Rect::new(g.x - h * 0.03, l.baseline - h * 0.6, g.x + h * 0.03, l.baseline + l.descent * 0.5));
}
}
}
if f.opts.condition_indicators && !f.doc.conditions.is_empty() && cs.styles.iter().any(|s| s.condition.is_some()) {
ctx.set_transform(m);
let px = 1.0 / m.determinant().abs().sqrt().max(1e-9);
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| g.visible && g.adv > 0.0) {
let Some(name) = cs.styles.get(g.style as usize).and_then(|s| s.condition.as_deref()) else { continue };
let Some(c) = f.doc.conditions.iter().find(|c| c.name == name) else { continue };
ctx.set_paint(screen_color([c.color[0], c.color[1], c.color[2]], 255));
let y = l.baseline + l.descent * 0.6;
ctx.fill_rect(&kurbo::Rect::new(g.x, y, g.x + g.adv, y + 2.0 * px));
}
}
}
}
impl Renderer {
pub(crate) fn draw_text(&mut self, ctx: &mut RenderContext, f: &Frame, cs: &Arc<ComposedStory>, ft: &FrameText, xf: Affine) {
let doc = f.doc;
@@ -276,114 +387,7 @@ impl Renderer {
if !ft.tables.is_empty() {
self.draw_tables(ctx, f, ft, xf);
}
// Text in fonts that aren't installed: pink behind the glyphs (screen only).
if f.opts.highlight_missing_fonts && cs.styles.iter().any(|s| s.missing_font) {
ctx.set_transform(m);
ctx.set_paint(screen_color([255, 166, 214], 255));
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| g.adv > 0.0 && cs.styles.get(g.style as usize).is_some_and(|s| s.missing_font)) {
ctx.fill_rect(&kurbo::Rect::new(g.x, l.baseline - l.ascent, g.x + g.adv, l.baseline + l.descent));
}
}
}
if f.opts.highlight_keeps && ft.lines.iter().any(|l| l.keep_violation) {
ctx.set_transform(m);
ctx.set_paint(screen_color([255, 236, 120], 255));
for l in ft.lines.iter().filter(|l| l.keep_violation) {
ctx.fill_rect(&kurbo::Rect::new(l.x0, l.baseline - l.ascent, l.x1, l.baseline + l.descent));
}
}
if f.opts.highlight_hj && ft.lines.iter().any(|l| l.hj > 0) {
ctx.set_transform(m);
for l in ft.lines.iter().filter(|l| l.hj > 0) {
let shade = [0u8, 0xF6, 0xEA, 0xD8][l.hj.min(3) as usize];
ctx.set_paint(screen_color([255, shade, [0u8, 0xB0, 0x70, 0x30][l.hj.min(3) as usize]], 255));
ctx.fill_rect(&kurbo::Rect::new(l.x0, l.baseline - l.ascent, l.end_x.max(l.x0 + 1.0), l.baseline + l.descent));
}
}
if f.opts.highlight_custom_tracking && cs.styles.iter().any(|s| s.custom_tracking) {
ctx.set_transform(m);
ctx.set_paint(screen_color([176, 236, 176], 255));
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| g.adv > 0.0 && cs.styles.get(g.style as usize).is_some_and(|s| s.custom_tracking)) {
ctx.fill_rect(&kurbo::Rect::new(g.x, l.baseline - l.ascent, g.x + g.adv, l.baseline + l.descent));
}
}
}
if f.opts.tag_markers && cs.styles.iter().any(|s| s.xml_tag.is_some()) {
ctx.set_transform(m);
let px = 1.0 / m.determinant().abs().sqrt().max(1e-9);
for l in &ft.lines {
let tag_of = |g: &designcraft_compose::PlacedGlyph| cs.styles.get(g.style as usize).and_then(|s| s.xml_tag.clone());
let gs: Vec<_> = l.glyphs.iter().filter(|g| g.len > 0).collect();
let mut i = 0;
while i < gs.len() {
let Some(tag) = tag_of(gs[i]) else {
i += 1;
continue;
};
let mut j = i + 1;
while j < gs.len() && tag_of(gs[j]).as_deref() == Some(tag.as_str()) {
j += 1;
}
let c = f.doc.xml.tags.iter().find(|t| t.name == tag).map_or([100, 100, 220], |t| t.color);
ctx.set_paint(screen_color([c[0], c[1], c[2]], 255));
let (x0, x1) = (gs[i].x, gs[j - 1].x + gs[j - 1].adv);
let (top, bot) = (l.baseline - l.ascent, l.baseline + l.descent);
let (w, t) = (2.5 * px, px.max(0.3));
// [ before the element, ] after it.
for (x, dir) in [(x0, 1.0), (x1, -1.0)] {
ctx.fill_rect(&kurbo::Rect::new(x - t / 2.0, top, x + t / 2.0, bot));
for y in [top, bot - t] {
let (a, b) = if dir > 0.0 { (x, x + w) } else { (x - w, x) };
ctx.fill_rect(&kurbo::Rect::new(a, y, b, y + t));
}
}
i = j;
}
}
}
if f.opts.change_markup && cs.styles.iter().any(|s| s.inserted) {
ctx.set_transform(m);
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| g.adv > 0.0 && cs.styles.get(g.style as usize).is_some_and(|s| s.inserted)) {
ctx.set_paint(screen_color([120, 190, 255], 110));
ctx.fill_rect(&kurbo::Rect::new(g.x, l.baseline - l.ascent, g.x + g.adv, l.baseline + l.descent));
}
}
}
if f.opts.note_indicators
&& let Some(st) = f.doc.story(cs.story).filter(|st| !st.editorial.is_empty())
{
// A small flag at each note anchor, in the notes' amber.
ctx.set_transform(m);
ctx.set_paint(screen_color([232, 160, 32], 255));
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| st.text.get(g.byte..).is_some_and(|t| t.starts_with(designcraft_doc::NOTE_MARK))) {
let h = l.ascent.max(4.0);
let mut p = BezPath::new();
p.move_to((g.x - h * 0.25, l.baseline - h));
p.line_to((g.x + h * 0.25, l.baseline - h));
p.line_to((g.x, l.baseline - h * 0.55));
p.close_path();
ctx.fill_path(&p);
ctx.fill_rect(&kurbo::Rect::new(g.x - h * 0.03, l.baseline - h * 0.6, g.x + h * 0.03, l.baseline + l.descent * 0.5));
}
}
}
if f.opts.condition_indicators && !f.doc.conditions.is_empty() && cs.styles.iter().any(|s| s.condition.is_some()) {
ctx.set_transform(m);
let px = 1.0 / m.determinant().abs().sqrt().max(1e-9);
for l in &ft.lines {
for g in l.glyphs.iter().filter(|g| g.visible && g.adv > 0.0) {
let Some(name) = cs.styles.get(g.style as usize).and_then(|s| s.condition.as_deref()) else { continue };
let Some(c) = f.doc.conditions.iter().find(|c| c.name == name) else { continue };
ctx.set_paint(screen_color([c.color[0], c.color[1], c.color[2]], 255));
let y = l.baseline + l.descent * 0.6;
ctx.fill_rect(&kurbo::Rect::new(g.x, y, g.x + g.adv, y + 2.0 * px));
}
}
}
draw_text_indicators(ctx, f, cs, ft, m);
let scale = m.determinant().abs().sqrt();
let greek_px = f.opts.greek_below_px;
let mut greek = BezPath::new();
+1 -1
View File
@@ -1,4 +1,4 @@
use super::*;
use super::{Affine, Cache, Document, Item, Rect, SpreadRef, fx};
/// How far (points) an object's soft effects reach outside its geometry.
pub fn effect_outset(it: &Item) -> f64 {
+1 -1
View File
@@ -28,7 +28,7 @@ fn pixel(d: &Document, x: u32, y: u32) -> [u8; 4] {
let mut r = Renderer::new();
r.threads = 0;
let placed = [Placed { spread: SpreadRef::Doc(0), xf: Affine::IDENTITY }];
let img = r.render(d, &Cache::new(), &placed, 200, 100, Affine::IDENTITY, &RenderOptions::default());
let img = r.render(d, &Cache::new(), &placed, (200, 100), Affine::IDENTITY, &RenderOptions::default());
let i = ((y * 200 + x) * 4) as usize;
[img.pixels[i], img.pixels[i + 1], img.pixels[i + 2], img.pixels[i + 3]]
}
+2 -2
View File
@@ -30,7 +30,7 @@ fn render_with(d: &Document, threads: u16) -> Rendered {
let mut r = Renderer::new();
r.threads = threads;
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
r.render(d, &Cache::new(), &[Placed { spread: SpreadRef::Doc(0), xf: Affine::translate(Vec2::ZERO) }], SIZE, SIZE, Affine::IDENTITY, &opts)
r.render(d, &Cache::new(), &[Placed { spread: SpreadRef::Doc(0), xf: Affine::translate(Vec2::ZERO) }], (SIZE, SIZE), Affine::IDENTITY, &opts)
}
fn render(d: &Document) -> Rendered {
@@ -207,7 +207,7 @@ fn render_zoomed(d: &Document) -> Rendered {
let mut r = Renderer::new();
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
let view = Affine::scale(8.0) * Affine::translate(Vec2::new(-70.0, -70.0));
r.render(d, &Cache::new(), &[Placed { spread: SpreadRef::Doc(0), xf: Affine::translate(Vec2::ZERO) }], SIZE, SIZE, view, &opts)
r.render(d, &Cache::new(), &[Placed { spread: SpreadRef::Doc(0), xf: Affine::translate(Vec2::ZERO) }], (SIZE, SIZE), view, &opts)
}
#[test]
+1 -2
View File
@@ -23,8 +23,7 @@ fn a_cmyk_gradient_is_c50_m50_halfway() {
&d,
&Cache::new(),
&[Placed { spread: SpreadRef::Doc(0), xf: Affine::IDENTITY }],
100,
20,
(100, 20),
Affine::IDENTITY,
&RenderOptions { paper: false, background: Some([255; 4]), ..Default::default() },
);
+1 -1
View File
@@ -32,7 +32,7 @@ fn render(d: &Document) -> Rendered {
let mut r = Renderer::new();
r.threads = 0;
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
r.render(d, &Cache::new(), &[Placed { spread: SpreadRef::Doc(0), xf: Affine::translate(Vec2::ZERO) }], 200, 240, Affine::IDENTITY, &opts)
r.render(d, &Cache::new(), &[Placed { spread: SpreadRef::Doc(0), xf: Affine::translate(Vec2::ZERO) }], (200, 240), Affine::IDENTITY, &opts)
}
fn grey(img: &Rendered, x: u32, y: u32) -> u8 {
+7 -7
View File
@@ -61,22 +61,22 @@ fn glyph_paths_are_cached_and_offscreen_text_is_skipped() {
// 200% view of the top-left of the first spread: only a few frames are visible.
let view = Affine::scale(2.0);
let opts = RenderOptions::default();
let a = r.render(&d, &cache, &all, 800, 600, view, &opts);
let a = r.render(&d, &cache, &all, (800, 600), view, &opts);
let drawn = r.stats.glyphs;
assert!(drawn > 100, "glyphs drawn: {drawn}");
assert!(r.cached_frames() <= 4, "only visible frames get glyph paths: {}", r.cached_frames());
// Same pixels from the cache.
let b = r.render(&d, &cache, &all, 800, 600, view, &opts);
let b = r.render(&d, &cache, &all, (800, 600), view, &opts);
assert!(a.pixels == b.pixels);
// Culling: far fewer glyphs than the visible frames hold.
let whole = r.render(&d, &cache, &all, 2400, 1600, Affine::scale(1.0), &opts);
let whole = r.render(&d, &cache, &all, (2400, 1600), Affine::scale(1.0), &opts);
assert!(r.stats.glyphs > drawn * 2, "{} vs {drawn}", r.stats.glyphs);
drop(whole);
// A new composition (edited story) gets fresh paths.
let mut d2 = d.clone();
let sid = *d2.stories.keys().next().unwrap();
d2.set_story_text(sid, "Edited").unwrap();
let c = r.render(&d2, &cache, &all, 800, 600, view, &opts);
let c = r.render(&d2, &cache, &all, (800, 600), view, &opts);
assert!(r.stats.glyphs < 10);
assert!(c.pixels != a.pixels);
}
@@ -88,7 +88,7 @@ fn greeked_text_builds_no_glyph_paths() {
let mut r = Renderer::new();
r.threads = 0;
let opts = RenderOptions { greek_below_px: 6.0, ..Default::default() };
let img = r.render(&d, &cache, &placed(&d), 400, 300, Affine::scale(0.3), &opts);
let img = r.render(&d, &cache, &placed(&d), (400, 300), Affine::scale(0.3), &opts);
assert_eq!(r.stats.glyphs, 0);
assert_eq!(r.cached_frames(), 0);
// The grey bars are drawn.
@@ -106,11 +106,11 @@ fn render_text_heavy_spread_at_100_percent_within_budget() {
// 100% on a HiDPI 2880×1800 canvas = 2 px/pt, centred on the second spread.
let view = Affine::scale(2.0) * Affine::translate((0.0, -828.0));
let opts = RenderOptions { greek_below_px: 6.0, ..Default::default() };
r.render(&d, &cache, &all, 2880, 1800, view, &opts);
r.render(&d, &cache, &all, (2880, 1800), view, &opts);
let mut v: Vec<f64> = (0..9)
.map(|_| {
let t = Instant::now();
drop(r.render(&d, &cache, &all, 2880, 1800, view, &opts));
drop(r.render(&d, &cache, &all, (2880, 1800), view, &opts));
t.elapsed().as_secs_f64() * 1000.0
})
.collect();
+1 -2
View File
@@ -30,8 +30,7 @@ fn render_with(path: PathData, fill: bool, stroke: Stroke, xf: Affine, threads:
&d,
&Cache::new(),
&[Placed { spread: SpreadRef::Doc(0), xf: Affine::IDENTITY }],
96,
96,
(96, 96),
Affine::IDENTITY,
&RenderOptions { paper: false, background, ..Default::default() },
)
+1 -2
View File
@@ -21,8 +21,7 @@ fn text_image(height: f64) -> Rendered {
&d,
&Cache::new(),
&[Placed { spread: SpreadRef::Doc(0), xf: Affine::IDENTITY }],
160,
100,
(160, 100),
Affine::IDENTITY,
&RenderOptions { background: Some([255; 4]), ..Default::default() },
)