Skip to content
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
40 changes: 0 additions & 40 deletions imaging/src/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,42 +90,6 @@ impl core::error::Error for RenderContentError {
}
}

/// Shared unsupported-feature failures surfaced by renderer traits.
#[derive(Debug)]
pub enum RenderUnsupportedError {
/// An image brush was encountered on a backend that cannot render it.
ImageBrush,
/// A filter configuration could not be translated for the active backend.
Filter,
/// A mask mode or masking primitive is unsupported by the active backend.
Mask,
/// Glyph rendering requested a blend mode or transform unsupported by the active backend.
Glyph,
/// A blurred rounded-rect primitive is unsupported by the active backend.
BlurredRoundedRect,
/// The source used clip/group nesting the active backend cannot represent.
UnbalancedLayerStack,
}

impl core::fmt::Display for RenderUnsupportedError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::ImageBrush => f.write_str("backend does not support image brushes"),
Self::Filter => f.write_str("backend does not support this filter"),
Self::Mask => f.write_str("backend does not support this mask"),
Self::Glyph => f.write_str("backend does not support this glyph operation"),
Self::BlurredRoundedRect => {
f.write_str("backend does not support this blurred rounded rect operation")
}
Self::UnbalancedLayerStack => {
f.write_str("scene uses clip/group nesting unsupported by this backend")
}
}
}
}

impl core::error::Error for RenderUnsupportedError {}

/// Shared GPU readback failures surfaced by image renderers.
#[derive(Debug)]
pub enum GpuReadbackError {
Expand Down Expand Up @@ -222,8 +186,6 @@ pub enum ImageRendererError {
Content(RenderContentError),
/// Caller target-related failure.
Target(ImageTargetError),
/// Unsupported-feature failure.
Unsupported(RenderUnsupportedError),
/// GPU readback failure.
Readback(GpuReadbackError),
/// Backend-specific rendering error.
Expand All @@ -243,7 +205,6 @@ impl core::fmt::Display for ImageRendererError {
match self {
Self::Content(error) => core::fmt::Display::fmt(error, f),
Self::Target(error) => core::fmt::Display::fmt(error, f),
Self::Unsupported(error) => core::fmt::Display::fmt(error, f),
Self::Readback(error) => core::fmt::Display::fmt(error, f),
Self::Backend(error) => core::fmt::Display::fmt(error, f),
}
Expand All @@ -255,7 +216,6 @@ impl core::error::Error for ImageRendererError {
match self {
Self::Content(error) => Some(error),
Self::Target(error) => Some(error),
Self::Unsupported(error) => Some(error),
Self::Readback(error) => Some(error),
Self::Backend(error) => Some(error.as_ref()),
}
Expand Down
24 changes: 1 addition & 23 deletions imaging_skia/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ use imaging::{
record::{Scene, ValidateError, replay},
render::{
ImageBufferFormat, ImageBufferTarget, ImageRenderer, ImageRendererError, ImageTargetError,
RenderContentError, RenderSource, RenderUnsupportedError,
RenderContentError, RenderSource,
},
};
use kurbo::{Affine, Shape as _};
Expand Down Expand Up @@ -219,12 +219,6 @@ pub enum Error {
UnsupportedGpuTextureFormat,
/// The target image buffer format cannot be represented through Skia.
UnsupportedImageTargetFormat,
/// An image brush was encountered; this backend does not support it.
UnsupportedImageBrush,
/// A filter configuration could not be translated.
UnsupportedFilter,
/// A glyph run used a per-glyph transform unsupported by this backend.
UnsupportedGlyphTransform,
/// Font bytes could not be loaded by Skia.
InvalidFontData,
/// A glyph identifier could not be represented by Skia's glyph type.
Expand Down Expand Up @@ -1156,13 +1150,6 @@ fn map_image_renderer_error(error: Error) -> ImageRendererError {
Error::InvalidScene(error) => {
ImageRendererError::Content(RenderContentError::InvalidScene(error))
}
Error::UnsupportedImageBrush => {
ImageRendererError::Unsupported(RenderUnsupportedError::ImageBrush)
}
Error::UnsupportedFilter => ImageRendererError::Unsupported(RenderUnsupportedError::Filter),
Error::UnsupportedGlyphTransform => {
ImageRendererError::Unsupported(RenderUnsupportedError::Glyph)
}
Error::InvalidFontData => ImageRendererError::Content(RenderContentError::InvalidFontData),
Error::InvalidGlyphId => ImageRendererError::Content(RenderContentError::InvalidGlyphId),
#[cfg(feature = "gpu")]
Expand Down Expand Up @@ -1207,15 +1194,6 @@ fn map_texture_renderer_error(error: Error) -> TextureRendererError {
Error::InvalidScene(error) => {
TextureRendererError::Content(RenderContentError::InvalidScene(error))
}
Error::UnsupportedImageBrush => {
TextureRendererError::Unsupported(RenderUnsupportedError::ImageBrush)
}
Error::UnsupportedFilter => {
TextureRendererError::Unsupported(RenderUnsupportedError::Filter)
}
Error::UnsupportedGlyphTransform => {
TextureRendererError::Unsupported(RenderUnsupportedError::Glyph)
}
Error::Internal("render width too large" | "render height too large") => {
TextureRendererError::Target(TextureTargetError::DimensionsTooLarge)
}
Expand Down
14 changes: 4 additions & 10 deletions imaging_skia/src/sinks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,6 @@ fn push_group_impl(canvas: &sk::Canvas, state: &mut StreamState, group: GroupRef
} else {
build_filter_chain(group.filters)
};
if !group.filters.is_empty() && filter.is_none() {
state.set_error_once(Error::UnsupportedFilter);
}

let clip_path = group.clip.and_then(|clip| clip_path(canvas, state, clip));
let mut restores = 0_u8;
Expand Down Expand Up @@ -566,13 +563,10 @@ fn draw_masked_group(
paint.set_anti_alias(true);
paint.set_blend_mode(map_blend_mode(&masked.composite.blend));
paint.set_alpha_f(masked.composite.alpha);
if !masked.filters.is_empty() {
if let Some(filter) = build_filter_chain(&masked.filters) {
paint.set_image_filter(filter);
} else {
state.set_error_once(Error::UnsupportedFilter);
return;
}
if !masked.filters.is_empty()
&& let Some(filter) = build_filter_chain(&masked.filters)
{
paint.set_image_filter(filter);
}

let clip_path = masked
Expand Down
29 changes: 1 addition & 28 deletions imaging_vello/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ use imaging::RgbaImage;
use imaging::record::{Scene, ValidateError, replay};
use imaging::render::{
GpuReadbackError, ImageBufferFormat, ImageBufferTarget, ImageRenderer, ImageRendererError,
ImageTargetError, RenderContentError, RenderSource, RenderUnsupportedError,
ImageTargetError, RenderContentError, RenderSource,
};
use imaging_wgpu::{TextureRenderer, TextureRendererError, TextureTargetError, TextureViewTarget};
use kurbo::Rect;
Expand All @@ -146,16 +146,6 @@ pub use scene_sink::VelloSceneSink;
pub enum Error {
/// The scene is invalid (unbalanced stacks).
InvalidScene(ValidateError),
/// An image brush was encountered; this backend does not support it.
UnsupportedImageBrush,
/// A filter configuration could not be translated.
UnsupportedFilter,
/// A mask mode or masking primitive is not supported by this backend.
UnsupportedMask,
/// Glyph draws with non-default blend modes are not supported by this backend yet.
UnsupportedGlyphBlend,
/// Blurred rounded rect draws with non-default blend modes are not supported by this backend yet.
UnsupportedBlurredRoundedRectBlend,
/// The clip/group stack was not well-nested for this backend.
///
/// Vello uses a single layer stack for both clipping and blending; `imaging` tracks these as
Expand Down Expand Up @@ -472,22 +462,6 @@ fn map_texture_renderer_error(error: Error) -> TextureRendererError {
Error::InvalidScene(error) => {
TextureRendererError::Content(RenderContentError::InvalidScene(error))
}
Error::UnsupportedImageBrush => {
TextureRendererError::Unsupported(RenderUnsupportedError::ImageBrush)
}
Error::UnsupportedFilter => {
TextureRendererError::Unsupported(RenderUnsupportedError::Filter)
}
Error::UnsupportedMask => TextureRendererError::Unsupported(RenderUnsupportedError::Mask),
Error::UnsupportedGlyphBlend => {
TextureRendererError::Unsupported(RenderUnsupportedError::Glyph)
}
Error::UnsupportedBlurredRoundedRectBlend => {
TextureRendererError::Unsupported(RenderUnsupportedError::BlurredRoundedRect)
}
Error::UnbalancedLayerStack => {
TextureRendererError::Unsupported(RenderUnsupportedError::UnbalancedLayerStack)
}
Error::Internal("render width too large" | "render height too large") => {
TextureRendererError::Target(TextureTargetError::DimensionsTooLarge)
}
Expand Down Expand Up @@ -522,7 +496,6 @@ fn map_texture_to_image_error(error: TextureRendererError) -> ImageRendererError
))
}
},
TextureRendererError::Unsupported(error) => ImageRendererError::Unsupported(error),
TextureRendererError::Backend(error) => ImageRendererError::Backend(error),
}
}
Expand Down
77 changes: 44 additions & 33 deletions imaging_vello/src/scene_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
use super::Error;
use crate::vello::{self, Glyph as VelloGlyph};
use imaging::{
BlurredRoundedRect, ClipRef, Composite, FillRef, GeometryRef, GlyphRunRef, GroupRef, MaskMode,
PaintSink, StrokeRef,
BlurredRoundedRect, ClipRef, Composite, FillRef, GeometryRef, GlyphRunRef, GroupRef, PaintSink,
StrokeRef,
record::{Scene, replay_transformed},
};
use kurbo::{Affine, Rect};
Expand All @@ -27,7 +27,6 @@ pub struct VelloSceneSink<'a> {
#[derive(Clone, Debug)]
struct PendingMask {
scene: Scene,
mode: MaskMode,
transform: Affine,
}

Expand Down Expand Up @@ -120,15 +119,22 @@ impl<'a> VelloSceneSink<'a> {
glyph_run: GlyphRunRef<'_>,
glyphs: &mut dyn Iterator<Item = imaging::record::Glyph>,
) {
if glyph_run.composite.blend != peniko::BlendMode::default() {
self.set_error_once(Error::UnsupportedGlyphBlend);
return;
}

let Some(paint) = self.brush_to_brush(glyph_run.brush, glyph_run.composite) else {
return;
};

let blended = glyph_run.composite.blend != peniko::BlendMode::default();
if blended {
self.scene.push_layer(
Fill::NonZero,
glyph_run.composite.blend,
1.0,
Affine::IDENTITY,
&self.surface_clip,
);
self.push_group_frame(None);
}

let builder = self
.scene
.draw_glyphs(glyph_run.font)
Expand All @@ -145,12 +151,26 @@ impl<'a> VelloSceneSink<'a> {
y: glyph.y,
});
builder.draw(glyph_run.style, glyphs);

if blended {
if self.pop_group_frame().is_none() {
return;
}
self.scene.pop_layer();
}
}

fn draw_blurred_rounded_rect(&mut self, draw: BlurredRoundedRect) {
if draw.composite.blend != peniko::BlendMode::default() {
self.set_error_once(Error::UnsupportedBlurredRoundedRectBlend);
return;
let blended = draw.composite.blend != peniko::BlendMode::default();
if blended {
self.scene.push_layer(
Fill::NonZero,
draw.composite.blend,
1.0,
draw.transform,
&draw.rect,
);
self.push_group_frame(None);
}
self.scene.draw_blurred_rounded_rect(
draw.transform,
Expand All @@ -159,6 +179,12 @@ impl<'a> VelloSceneSink<'a> {
draw.radius,
draw.std_dev,
);
if blended {
if self.pop_group_frame().is_none() {
return;
}
self.scene.pop_layer();
}
}

fn replay_masked_subscene(&mut self, scene: &Scene, transform: Affine) {
Expand Down Expand Up @@ -213,18 +239,6 @@ impl PaintSink for VelloSceneSink<'_> {
if self.error.is_some() {
return;
}
if !group.filters.is_empty() {
self.set_error_once(Error::UnsupportedFilter);
return;
}
if group
.mask
.as_ref()
.is_some_and(|mask| mask.mask.mode != MaskMode::Luminance)
{
self.set_error_once(Error::UnsupportedMask);
return;
}

if let Some(clip) = group.clip {
match clip {
Expand Down Expand Up @@ -308,7 +322,6 @@ impl PaintSink for VelloSceneSink<'_> {
}
self.push_group_frame(group.mask.map(|mask| PendingMask {
scene: mask.mask.scene.clone(),
mode: mask.mask.mode,
transform: mask.transform,
}));
}
Expand All @@ -321,11 +334,6 @@ impl PaintSink for VelloSceneSink<'_> {
return;
};
if let Some(mask) = mask {
debug_assert_eq!(
mask.mode,
MaskMode::Luminance,
"only luminance masks should reach Vello group-mask replay"
);
self.scene.push_luminance_mask_layer(
Fill::NonZero,
1.0,
Expand Down Expand Up @@ -539,11 +547,12 @@ mod tests {
}

#[test]
fn vello_scene_sink_rejects_filters() {
fn vello_scene_sink_ignores_filters() {
let mut scene = vello::Scene::new();
let mut sink = VelloSceneSink::new(&mut scene, Rect::new(0.0, 0.0, 32.0, 32.0));
sink.push_group(GroupRef::new().with_filters(&[Filter::blur(2.0)]));
assert!(matches!(sink.finish(), Err(Error::UnsupportedFilter)));
sink.pop_group();
assert!(matches!(sink.finish(), Ok(())));
}

#[test]
Expand All @@ -560,13 +569,15 @@ mod tests {
}

#[test]
fn vello_scene_sink_rejects_alpha_masks() {
fn vello_scene_sink_falls_back_for_alpha_masks() {
let mut mask = Scene::new();
mask.fill(FillRef::new(Rect::new(0.0, 0.0, 16.0, 16.0), Color::WHITE));

let mut scene = vello::Scene::new();
let mut sink = VelloSceneSink::new(&mut scene, Rect::new(0.0, 0.0, 32.0, 32.0));
sink.push_group(GroupRef::new().with_mask(MaskRef::new(MaskMode::Alpha, &mask)));
assert!(matches!(sink.finish(), Err(Error::UnsupportedMask)));
sink.fill(FillRef::new(Rect::new(4.0, 4.0, 20.0, 20.0), Color::BLACK));
sink.pop_group();
assert!(matches!(sink.finish(), Ok(())));
}
}
Loading
Loading