mod common;
use common::*;
#[test]
fn shadow_blurs_and_bleeds_outward_deterministically() {
let build = || {
let mut scene = Scene::new(40.0, 40.0);
scene.commands.push(SceneCommand::BeginShadow {
shadows: vec![ShadowSpec {
dx: 1.0,
dy: 1.0,
blur: 3.0,
color: Color::srgb(0, 0, 0, 255),
}],
});
scene.commands.push(SceneCommand::FillRect {
x: 15.0,
y: 15.0,
w: 10.0,
h: 10.0,
paint: Paint::solid(Color::srgb(255, 0, 0, 255)),
});
scene.commands.push(SceneCommand::EndShadow);
scene
};
let backend = TinySkiaBackend;
let provider = default_provider();
let img1 = backend
.rasterize(&build(), &provider, &no_assets())
.expect("rasterize must succeed");
let img2 = backend
.rasterize(&build(), &provider, &no_assets())
.expect("rasterize must succeed");
assert_eq!(
img1.rgba, img2.rgba,
"shadow render must be byte-identical across runs"
);
let (cr, _, _, ca) = pixel(&img1.rgba, img1.width, 20, 20);
assert!(ca == 255 && cr > 200, "rect center must stay opaque red");
let (sr, sg, sb, sa) = pixel(&img1.rgba, img1.width, 12, 20);
assert!(
sa > 0,
"shadow must bleed outside the rect (x=12): got alpha {sa}"
);
assert!(
sr < 128 && sg < 128 && sb < 128,
"the bled shadow pixel must be dark: ({sr},{sg},{sb})"
);
let (_, _, _, far_a) = pixel(&img1.rgba, img1.width, 0, 0);
assert_eq!(far_a, 0, "corner far from the shadow must stay transparent");
}
#[test]
fn blend_multiply_layer_darkens_overlap() {
let page = 8.0;
let mut scene = Scene::new(page, page);
scene.commands.push(SceneCommand::PushClip {
x: 0.0,
y: 0.0,
w: page,
h: page,
});
scene.commands.push(SceneCommand::FillRect {
x: 0.0,
y: 0.0,
w: page,
h: page,
paint: Paint::solid(Color::srgb(255, 0, 0, 255)),
});
scene.commands.push(SceneCommand::PushLayer {
opacity: 1.0,
blend_mode: Some(BlendMode::Multiply),
});
scene.commands.push(SceneCommand::FillRect {
x: 0.0,
y: 0.0,
w: page,
h: page,
paint: Paint::solid(Color::srgb(0, 0, 255, 255)),
});
scene.commands.push(SceneCommand::PopLayer);
scene.commands.push(SceneCommand::PopClip);
let backend = TinySkiaBackend;
let provider = default_provider();
let img = backend
.rasterize(&scene, &provider, &no_assets())
.expect("rasterize must succeed");
let (r, g, b, a) = pixel(&img.rgba, img.width, 4, 4);
assert_eq!(a, 255, "overlap must be opaque");
assert!(
r < 16 && g < 16 && b < 16,
"multiply overlap must be near-black (darker than red and blue); got r={r} g={g} b={b}"
);
}
#[test]
fn no_layer_scene_unchanged() {
let scene = make_solid_red_scene(4.0);
let backend = TinySkiaBackend;
let provider = default_provider();
let img = backend
.rasterize(&scene, &provider, &no_assets())
.expect("rasterize must succeed");
assert_eq!(pixel(&img.rgba, img.width, 2, 2), (255, 0, 0, 255));
}
#[test]
fn begin_end_blur_renders_deterministically_and_softens_ink() {
let build = || {
let mut scene = Scene::new(60.0, 60.0);
scene.commands.push(SceneCommand::BeginBlur { radius: 6.0 });
scene.commands.push(SceneCommand::FillRect {
x: 20.0,
y: 20.0,
w: 20.0,
h: 20.0,
paint: Paint::solid(Color::srgb(255, 0, 0, 255)),
});
scene.commands.push(SceneCommand::EndBlur);
scene
};
let build_crisp = || {
let mut scene = Scene::new(60.0, 60.0);
scene.commands.push(SceneCommand::FillRect {
x: 20.0,
y: 20.0,
w: 20.0,
h: 20.0,
paint: Paint::solid(Color::srgb(255, 0, 0, 255)),
});
scene
};
let backend = TinySkiaBackend;
let provider = default_provider();
let img1 = backend
.rasterize(&build(), &provider, &no_assets())
.expect("rasterize must not panic");
let img2 = backend
.rasterize(&build(), &provider, &no_assets())
.expect("rasterize must not panic (second run)");
let img_crisp = backend
.rasterize(&build_crisp(), &provider, &no_assets())
.expect("crisp rasterize must not panic");
assert_eq!(
img1.rgba, img2.rgba,
"blur render must be byte-identical across runs"
);
let (_, _, _, spread_a) = pixel(&img1.rgba, img1.width, 18, 30);
assert!(
spread_a > 0,
"blur must spread ink outside the original rect: alpha at (18,30) = {spread_a}"
);
let (_, _, _, blurred_center_a) = pixel(&img1.rgba, img1.width, 30, 30);
let (_, _, _, crisp_center_a) = pixel(&img_crisp.rgba, img_crisp.width, 30, 30);
assert!(
blurred_center_a < crisp_center_a,
"blur must reduce peak alpha at the rect center: blurred={blurred_center_a} crisp={crisp_center_a}"
);
}
#[test]
fn no_blur_command_scene_is_byte_identical() {
let build = || {
let mut scene = Scene::new(20.0, 20.0);
scene.commands.push(SceneCommand::FillRect {
x: 5.0,
y: 5.0,
w: 10.0,
h: 10.0,
paint: Paint::solid(Color::srgb(0, 128, 255, 200)),
});
scene
};
let backend = TinySkiaBackend;
let provider = default_provider();
let img1 = backend
.rasterize(&build(), &provider, &no_assets())
.expect("rasterize");
let img2 = backend
.rasterize(&build(), &provider, &no_assets())
.expect("rasterize");
assert_eq!(
img1.rgba, img2.rgba,
"non-blur scene must be byte-identical"
);
}