mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
crates/render/src/raster.rs: main's tests plus the PR's #2970 test after the knockout test vendor/README.md: main's rows with the PR's rewording of hayro patch 18
This commit is contained in:
@@ -6484,6 +6484,62 @@ trailer << /Root 1 0 R >>
|
||||
check(&render_as(ko, &tiny, false), 101, 101, [128, 128, 255], "the tiny image in a final render");
|
||||
}
|
||||
|
||||
/// #2970: a page's objects composite with a transparent backdrop and the page group is then
|
||||
/// put on the paper, so a separable blend mode only blends where something was drawn. A
|
||||
/// green fill with `/BM /Screen` (or Lighten, Difference…) half over a red square is yellow
|
||||
/// (or whatever the mode gives) over the red, and plain green over the empty page: blending
|
||||
/// with the white paper made it white or magenta.
|
||||
#[test]
|
||||
fn blend_modes_over_an_empty_part_of_the_page_keep_their_colour() {
|
||||
let px = |p: &RenderedPage, x: u32, y: u32| p.rgba[((y * p.width + x) * 4) as usize..][..4].to_vec();
|
||||
for (mode, over_red) in [
|
||||
("Normal", Some([0, 255, 0])),
|
||||
("Multiply", Some([0, 0, 0])),
|
||||
("Screen", Some([255, 255, 0])),
|
||||
("Lighten", Some([255, 255, 0])),
|
||||
("Difference", Some([255, 255, 0])),
|
||||
("Exclusion", Some([255, 255, 0])),
|
||||
("ColorDodge", Some([255, 0, 0])),
|
||||
("Overlay", Some([255, 0, 0])),
|
||||
// The non-separable modes mix hue, saturation and luminosity; over an empty part of
|
||||
// the page they too must keep the source colour.
|
||||
("Hue", None),
|
||||
("Saturation", None),
|
||||
("Color", None),
|
||||
("Luminosity", None),
|
||||
] {
|
||||
let content = "1 0 0 rg 20 20 150 150 re f /G1 gs 0 1 0 rg 100 50 150 120 re f";
|
||||
let pdf = format!(
|
||||
"%PDF-1.7
|
||||
1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj
|
||||
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
|
||||
3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 300 200] /Contents 4 0 R /Resources << /ExtGState << /G1 5 0 R >> >> >> endobj
|
||||
4 0 obj << /Length {} >> stream
|
||||
{content}
|
||||
endstream endobj
|
||||
5 0 obj << /Type /ExtGState /BM /{mode} >> endobj
|
||||
trailer << /Root 1 0 R >>
|
||||
%%EOF",
|
||||
content.len()
|
||||
);
|
||||
let p = PageRenderer::new(Arc::new(pdf.into_bytes()), RenderConfig::default()).render(RenderRequest {
|
||||
page: 0,
|
||||
kind: RequestKind::Pixels,
|
||||
tile: None,
|
||||
scale: 1.0,
|
||||
tag: 0,
|
||||
..Default::default()
|
||||
});
|
||||
assert!(p.error.is_none(), "{mode}: {:?}", p.error);
|
||||
// Pixel rows run top down: PDF (210, 100) and (130, 100) are rows 100 from the top.
|
||||
assert_eq!(px(&p, 210, 100), vec![0, 255, 0, 255], "{mode} over the empty page");
|
||||
if let Some(over_red) = over_red {
|
||||
assert_eq!(px(&p, 130, 100)[..3], over_red, "{mode} over the red square");
|
||||
}
|
||||
assert_eq!(px(&p, 280, 10), vec![255, 255, 255, 255], "{mode}: the paper stays white");
|
||||
}
|
||||
}
|
||||
|
||||
/// Reading colours replace *document* colours, never annotation colours (issue #1548): a
|
||||
/// highlight keeps its `/C`, so on a dark background the content under it shows through the
|
||||
/// Multiply blend in the highlight's colour instead of being darkened into invisibility.
|
||||
|
||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+23
-10
@@ -254,15 +254,20 @@ pub fn render_into<'a>(
|
||||
interpreter_settings.preview,
|
||||
);
|
||||
|
||||
// PdfCraft patch (18): the page renders as one transparency group with the background
|
||||
// painted *inside* it, as the initial backdrop. A group isolates what it wraps from what
|
||||
// was painted before it, so with the background outside, a Multiply highlight blended
|
||||
// only with the group's transparent backdrop and stayed its own colour — over a dark
|
||||
// reading-colours background the whole quad became one flat bar and the words under it
|
||||
// vanished. Inside the group the highlight multiplies with the background too: the words
|
||||
// keep the highlight's colour and the band between them takes the background (white
|
||||
// backgrounds are unaffected — multiplying with white is the identity).
|
||||
device.push_transparency_group(1.0, None, BlendMode::Normal);
|
||||
// PdfCraft patch (18): the page renders as one transparency group (the page group). Its
|
||||
// objects composite with an initially transparent backdrop, and the group is then put on
|
||||
// the paper (ISO 32000-2, page group), so a blend mode only blends where something was
|
||||
// drawn: a /BM /Screen fill over an empty part of the page keeps its colour (#2970).
|
||||
// Under reading colours the background is painted *inside* the group instead, as its
|
||||
// initial backdrop, so a Multiply highlight blends with the dark background: outside it,
|
||||
// the highlight kept its own flat colour and the words under it vanished into the band
|
||||
// (#1548). Reading colours replace the page's colours anyway; for document colours the
|
||||
// background stays outside, or every separable mode but Normal, Multiply, Darken and
|
||||
// HardLight would blend with the white paper.
|
||||
let backdrop_inside = render_settings.color_replacement.is_some();
|
||||
if backdrop_inside {
|
||||
device.push_transparency_group(1.0, None, BlendMode::Normal);
|
||||
}
|
||||
|
||||
device.ctx.set_paint(render_settings.bg_color);
|
||||
device
|
||||
@@ -274,11 +279,19 @@ pub fn render_into<'a>(
|
||||
path: clip_path,
|
||||
fill: FillRule::NonZero,
|
||||
});
|
||||
if !backdrop_inside {
|
||||
device.push_transparency_group(1.0, None, BlendMode::Normal);
|
||||
}
|
||||
|
||||
interpret_page(page, &mut state, &mut device);
|
||||
|
||||
if !backdrop_inside {
|
||||
device.pop_transparency_group();
|
||||
}
|
||||
device.pop_clip_path();
|
||||
device.pop_transparency_group();
|
||||
if backdrop_inside {
|
||||
device.pop_transparency_group();
|
||||
}
|
||||
|
||||
let mut resources = vello_cpu::Resources::default();
|
||||
device.ctx.render_to_buffer(
|
||||
|
||||
Reference in New Issue
Block a user