use std::sync::Arc;
mod common;
use common::*;
#[test]
fn draw_image_stretch_renders() {
let backend = TinySkiaBackend;
let fonts = default_provider();
let assets = swatch_provider();
let scene = swatch_scene();
let img1 = backend
.rasterize(&scene, &fonts, &assets)
.expect("rasterize 1");
let img2 = backend
.rasterize(&scene, &fonts, &assets)
.expect("rasterize 2");
assert_eq!(
img1.rgba, img2.rgba,
"two rasterizes of the same image scene must be byte-identical"
);
let any_ink = (0..img1.height).any(|py| {
(0..img1.width).any(|px| {
let (_, _, _, a) = pixel(&img1.rgba, img1.width, px, py);
a > 0
})
});
assert!(
any_ink,
"DrawImage stretch must rasterize at least one non-transparent pixel"
);
}
#[test]
fn draw_image_ellipse_clip_takes_effect() {
let backend = TinySkiaBackend;
let fonts = default_provider();
let assets = swatch_provider();
let scene = swatch_ellipse_scene();
let img1 = backend
.rasterize(&scene, &fonts, &assets)
.expect("rasterize 1");
let img2 = backend
.rasterize(&scene, &fonts, &assets)
.expect("rasterize 2");
assert_eq!(
img1.rgba, img2.rgba,
"two rasterizes of the ellipse-clipped scene must be byte-identical"
);
let (_, _, _, center_a) = pixel(&img1.rgba, img1.width, 20, 20);
let (_, _, _, corner_a) = pixel(&img1.rgba, img1.width, 0, 0);
assert!(
center_a > 0,
"center pixel must be painted inside the ellipse clip"
);
assert_eq!(
corner_a, 0,
"corner pixel must be clipped out by the ellipse mask"
);
}
#[test]
fn draw_image_missing_asset_is_skipped() {
let backend = TinySkiaBackend;
let fonts = default_provider();
let assets = BytesAssetProvider::new();
let mut scene = Scene::new(20.0, 20.0);
scene.commands.push(SceneCommand::PushClip {
x: 0.0,
y: 0.0,
w: 20.0,
h: 20.0,
});
scene.commands.push(SceneCommand::DrawImage {
x: 0.0,
y: 0.0,
w: 20.0,
h: 20.0,
asset_id: "asset.missing".to_string(),
fit: FitMode::Stretch,
pos_x: 50.0,
pos_y: 50.0,
opacity: 1.0,
clip_shape: None,
src_rect: None,
});
scene.commands.push(SceneCommand::PopClip);
let img = backend
.rasterize(&scene, &fonts, &assets)
.expect("rasterize must succeed even with a missing asset");
let any_opaque = (0..img.height).any(|py| {
(0..img.width).any(|px| {
let (_, _, _, a) = pixel(&img.rgba, img.width, px, py);
a > 0
})
});
assert!(
!any_opaque,
"no pixels should be drawn when the asset is missing"
);
}
#[test]
fn draw_image_svg_asset_renders_red_pixels() {
const RED_SVG: &[u8] = b"<svg xmlns='http://www.w3.org/2000/svg' \
width='10' height='10'>\
<rect width='10' height='10' fill='#ff0000'/>\
</svg>";
let mut assets = BytesAssetProvider::new();
assets.register("asset.red", AssetKind::Svg, Arc::from(RED_SVG));
let mut scene = Scene::new(10.0, 10.0);
scene.commands.push(SceneCommand::PushClip {
x: 0.0,
y: 0.0,
w: 10.0,
h: 10.0,
});
scene.commands.push(SceneCommand::DrawImage {
x: 0.0,
y: 0.0,
w: 10.0,
h: 10.0,
asset_id: "asset.red".to_string(),
fit: FitMode::Stretch,
pos_x: 50.0,
pos_y: 50.0,
opacity: 1.0,
clip_shape: None,
src_rect: None,
});
scene.commands.push(SceneCommand::PopClip);
let backend = TinySkiaBackend;
let fonts = default_provider();
let img = backend
.rasterize(&scene, &fonts, &assets)
.expect("SVG rasterize must succeed");
let (r, g, b, a) = pixel(&img.rgba, img.width, 5, 5);
assert!(a > 0, "center pixel must be opaque after SVG rasterize");
assert!(
r > g && r > b,
"center pixel must be red-dominant; got r={r} g={g} b={b}"
);
}
#[test]
fn draw_image_svg_text_renders_red_pixels() {
const TEXT_SVG: &[u8] = b"<svg xmlns='http://www.w3.org/2000/svg' \
width='200' height='60'>\
<text x='0' y='40' font-size='40' fill='#ff0000'>Hi</text>\
</svg>";
let mut assets = BytesAssetProvider::new();
assets.register("asset.text_svg", AssetKind::Svg, Arc::from(TEXT_SVG));
let mut scene = Scene::new(200.0, 60.0);
scene.commands.push(SceneCommand::PushClip {
x: 0.0,
y: 0.0,
w: 200.0,
h: 60.0,
});
scene.commands.push(SceneCommand::DrawImage {
x: 0.0,
y: 0.0,
w: 200.0,
h: 60.0,
asset_id: "asset.text_svg".to_string(),
fit: FitMode::Stretch,
pos_x: 50.0,
pos_y: 50.0,
opacity: 1.0,
clip_shape: None,
src_rect: None,
});
scene.commands.push(SceneCommand::PopClip);
let backend = TinySkiaBackend;
let fonts = default_provider();
let img = backend
.rasterize(&scene, &fonts, &assets)
.expect("SVG text rasterize must succeed");
let any_red = (0..img.height).any(|py| {
(0..img.width).any(|px| {
let (r, g, b, a) = pixel(&img.rgba, img.width, px, py);
a > 0 && r > g && r > b
})
});
assert!(
any_red,
"SVG <text> must produce at least one red pixel after convert_text + rasterize"
);
}
#[test]
fn draw_image_src_rect_crops_to_blue_column() {
let png = three_column_rgb_png();
let mut assets = BytesAssetProvider::new();
assets.register("asset.cols", AssetKind::Image, png);
let mut scene = Scene::new(4.0, 4.0);
scene.commands.push(SceneCommand::PushClip {
x: 0.0,
y: 0.0,
w: 4.0,
h: 4.0,
});
scene.commands.push(SceneCommand::PushClip {
x: 0.0,
y: 0.0,
w: 4.0,
h: 4.0,
});
scene.commands.push(SceneCommand::DrawImage {
x: 0.0,
y: 0.0,
w: 4.0,
h: 4.0,
asset_id: "asset.cols".to_string(),
fit: FitMode::Stretch,
pos_x: 50.0,
pos_y: 50.0,
opacity: 1.0,
clip_shape: None,
src_rect: Some(SrcRect {
x: 2.0,
y: 0.0,
w: 1.0,
h: 3.0,
}),
});
scene.commands.push(SceneCommand::PopClip);
scene.commands.push(SceneCommand::PopClip);
let backend = TinySkiaBackend;
let fonts = default_provider();
let img = backend
.rasterize(&scene, &fonts, &assets)
.expect("rasterize must succeed");
for py in 0..img.height {
for px_x in 0..img.width {
let (r, g, b, a) = pixel(&img.rgba, img.width, px_x, py);
assert!(a > 0, "pixel ({px_x},{py}) must be opaque (alpha={a})");
assert!(
b > r && b > g,
"pixel ({px_x},{py}) must be blue-dominant after src-rect crop; got r={r} g={g} b={b}"
);
}
}
}