Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better image sampling #722

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 88 additions & 1 deletion examples/scenes/src/test_scenes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,15 @@ export_scenes!(
mmark(crate::mmark::MMark::new(80_000), "mmark", false),
many_draw_objects(many_draw_objects),
blurred_rounded_rect(blurred_rounded_rect),
image_sampling(image_sampling),
image_extend_modes(image_extend_modes)
);

/// Implementations for the test scenes.
/// In a module because the exported [`ExampleScene`](crate::ExampleScene) creation functions use the same names.
mod impls {
use std::f64::consts::PI;
use std::f64::consts::{FRAC_1_SQRT_2, PI};
use std::sync::Arc;

use crate::SceneParams;
use kurbo::RoundedRect;
Expand Down Expand Up @@ -1761,4 +1764,88 @@ mod impls {
std_dev,
);
}

pub(super) fn image_sampling(scene: &mut Scene, params: &mut SceneParams) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might as well make this a test as well.

params.resolution = Some(Vec2::new(1100., 1100.));
params.base_color = Some(Color::WHITE);
let mut blob: Vec<u8> = Vec::new();
[Color::RED, Color::BLUE, Color::CYAN, Color::MAGENTA]
.iter()
.for_each(|c| {
let b = c.to_premul_u32().to_ne_bytes();
blob.push(b[3]);
blob.push(b[2]);
blob.push(b[1]);
blob.push(b[0]);
});
let data = vello::peniko::Blob::new(Arc::new(blob));
let image = vello::peniko::Image::new(data, vello::peniko::Format::Rgba8, 2, 2);

scene.draw_image(
&image,
Affine::scale(200.).then_translate((100., 100.).into()),
);
scene.draw_image(
&image,
Affine::translate((-1., -1.))
// 45° rotation
.then_rotate(PI / 4.)
.then_translate((1., 1.).into())
// So the major axis is sqrt(2.) larger
.then_scale(200. * FRAC_1_SQRT_2)
.then_translate((100., 600.0).into()),
);
scene.draw_image(
&image,
Affine::scale_non_uniform(100., 200.).then_translate((600.0, 100.0).into()),
);
scene.draw_image(
&image,
Affine::skew(0.1, 0.25)
.then_scale(200.0)
.then_translate((600.0, 600.0).into()),
);
}

pub(super) fn image_extend_modes(scene: &mut Scene, params: &mut SceneParams) {
params.resolution = Some(Vec2::new(1500., 1500.));
params.base_color = Some(Color::WHITE);
let mut blob: Vec<u8> = Vec::new();
[Color::RED, Color::BLUE, Color::CYAN, Color::MAGENTA]
.iter()
.for_each(|c| {
let b = c.to_premul_u32().to_ne_bytes();
blob.push(b[3]);
blob.push(b[2]);
blob.push(b[1]);
blob.push(b[0]);
});
let data = vello::peniko::Blob::new(Arc::new(blob));
let image = vello::peniko::Image::new(data, vello::peniko::Format::Rgba8, 2, 2);
let image = image.with_extend(Extend::Pad);
// Pad extend mode
scene.fill(
Fill::NonZero,
Affine::scale(100.).then_translate((100., 100.).into()),
&image,
Some(Affine::translate((2., 2.)).then_scale(100.)),
&Rect::new(0., 0., 6., 6.),
);
let image = image.with_extend(Extend::Reflect);
scene.fill(
Fill::NonZero,
Affine::scale(100.).then_translate((100., 800.).into()),
&image,
Some(Affine::translate((2., 2.))),
&Rect::new(0., 0., 6., 6.),
);
let image = image.with_extend(Extend::Repeat);
scene.fill(
Fill::NonZero,
Affine::scale(100.).then_translate((800., 100.).into()),
&image,
Some(Affine::translate((2., 2.))),
&Rect::new(0., 0., 6., 6.),
);
}
}
5 changes: 4 additions & 1 deletion vello_shaders/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ fn main() {
let mut shaders = match compile::ShaderInfo::from_default() {
Ok(s) => s,
Err(err) => {
eprintln!("{err}");
let formatted = err.to_string();
for line in formatted.lines() {
println!("cargo:warning={line}");
}
return;
}
};
Expand Down
31 changes: 17 additions & 14 deletions vello_shaders/shader/fine.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -1160,22 +1160,25 @@ fn main(
}
case CMD_IMAGE: {
let image = read_image(cmd_ix);
let atlas_extents = image.atlas_offset + image.extents;
let atlas_max = image.atlas_offset + image.extents - vec2(1.0);
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
let my_xy = vec2(xy.x + f32(i), xy.y);
let atlas_uv = image.matrx.xy * my_xy.x + image.matrx.zw * my_xy.y + image.xlat + image.atlas_offset;
// This currently clips to the image bounds. TODO: extend modes
if all(atlas_uv < atlas_extents) && area[i] != 0.0 {
let uv_quad = vec4(max(floor(atlas_uv), image.atlas_offset), min(ceil(atlas_uv), atlas_extents));
let uv_frac = fract(atlas_uv);
let a = premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.xy), 0));
let b = premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.xw), 0));
let c = premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.zy), 0));
let d = premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.zw), 0));
let fg_rgba = mix(mix(a, b, uv_frac.y), mix(c, d, uv_frac.y), uv_frac.x);
let fg_i = fg_rgba * area[i];
rgba[i] = rgba[i] * (1.0 - fg_i.a) + fg_i;
}
let atlas_uv = image.matrx.xy * my_xy.x + image.matrx.zw * my_xy.y + image.xlat + image.atlas_offset - vec2(0.5);
// This currently only implements the Pad extend mode
// TODO: Support repeat and reflect
// TODO: If the image couldn't be added to the atlas (i.e. was too big), this isn't robust
let atlas_uv_clamped = clamp(atlas_uv, image.atlas_offset, atlas_max);
// We know that the floor and ceil are within the atlas area because atlas_max and
// atlas_offset are integers
let uv_quad = vec4(floor(atlas_uv_clamped), ceil(atlas_uv_clamped));
let uv_frac = fract(atlas_uv);
let a = premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.xy), 0));
let b = premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.xw), 0));
let c = premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.zy), 0));
let d = premul_alpha(textureLoad(image_atlas, vec2<i32>(uv_quad.zw), 0));
let fg_rgba = mix(mix(a, b, uv_frac.y), mix(c, d, uv_frac.y), uv_frac.x);
let fg_i = fg_rgba * area[i];
rgba[i] = rgba[i] * (1.0 - fg_i.a) + fg_i;
}
cmd_ix += 2u;
}
Expand Down
Loading