diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index 4652c06e..9da3d29e 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -99,7 +99,7 @@ impl CanvasRenderingContext2D { let mut outline = path.into_outline(); outline.transform(&self.current_state.transform); - let paint = Paint::black(); + let paint = Paint::transparent_black(); let paint = self.current_state.resolve_paint(&paint); let paint_id = self.scene.push_paint(&paint); diff --git a/content/src/effects.rs b/content/src/effects.rs index c20ee417..3db7e9c9 100644 --- a/content/src/effects.rs +++ b/content/src/effects.rs @@ -84,3 +84,14 @@ impl Default for BlendMode { BlendMode::SourceOver } } + +impl BlendMode { + /// Whether the backdrop is irrelevant when applying this blend mode (i.e. destination blend + /// factor is zero). + #[inline] + pub fn occludes_backdrop(self) -> bool { + match self { + BlendMode::SourceOver | BlendMode::Clear => true, + } + } +} diff --git a/renderer/src/builder.rs b/renderer/src/builder.rs index cbd076e9..0de444d3 100644 --- a/renderer/src/builder.rs +++ b/renderer/src/builder.rs @@ -168,6 +168,7 @@ impl<'a> SceneBuilder<'a> { path_index as u16, TilingPathInfo::Draw { paint_metadata: &paint_metadata[paint_id.0 as usize], + blend_mode: path_object.blend_mode(), built_clip_path, }); diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 1c30ea11..6026f29e 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -80,6 +80,11 @@ where paint_texture: Option, layer_framebuffer_stack: Vec>, + // This is a dummy texture consisting solely of a single `rgba(0, 0, 0, 255)` texel. It serves + // as the paint texture when drawing alpha tiles with the Clear blend mode. If this weren't + // used, then the transparent black paint would zero out the alpha mask. + clear_paint_texture: D::Texture, + // Filter shaders filter_basic_program: FilterBasicProgram, filter_basic_vertex_array: FilterBasicVertexArray, @@ -211,6 +216,11 @@ where device.create_texture(TextureFormat::R8, mask_framebuffer_size); let mask_framebuffer = device.create_framebuffer(mask_framebuffer_texture); + let clear_paint_texture = + device.create_texture_from_data(TextureFormat::RGBA8, + Vector2I::splat(1), + TextureDataRef::U8(&[0, 0, 0, 255])); + let window_size = dest_framebuffer.window_size(&device); let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size); @@ -238,6 +248,7 @@ where mask_framebuffer, paint_texture: None, layer_framebuffer_stack: vec![], + clear_paint_texture, filter_basic_program, filter_basic_vertex_array, @@ -610,7 +621,15 @@ where MASK_FRAMEBUFFER_HEIGHT as f32))), ]; - let paint_texture = self.paint_texture.as_ref().unwrap(); + let paint_texture = match blend_mode { + BlendMode::Clear => { + // Use a special dummy paint texture containing `rgba(0, 0, 0, 255)` so that the + // transparent black paint color doesn't zero out the mask. + &self.clear_paint_texture + } + _ => self.paint_texture.as_ref().unwrap(), + }; + textures.push(paint_texture); uniforms.push((&self.alpha_tile_program.paint_texture_uniform, UniformData::TextureUnit(1))); diff --git a/renderer/src/tiles.rs b/renderer/src/tiles.rs index 083bc0ea..6a16ad92 100644 --- a/renderer/src/tiles.rs +++ b/renderer/src/tiles.rs @@ -11,6 +11,7 @@ use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder, SolidTile}; use crate::gpu_data::TileObjectPrimitive; use crate::paint::PaintMetadata; +use pathfinder_content::effects::BlendMode; use pathfinder_content::fill::FillRule; use pathfinder_content::outline::{Contour, Outline, PointIndex}; use pathfinder_content::segment::Segment; @@ -42,7 +43,11 @@ pub(crate) struct Tiler<'a> { #[derive(Clone, Copy)] pub(crate) enum TilingPathInfo<'a> { Clip, - Draw { paint_metadata: &'a PaintMetadata, built_clip_path: Option<&'a BuiltPath> }, + Draw { + paint_metadata: &'a PaintMetadata, + blend_mode: BlendMode, + built_clip_path: Option<&'a BuiltPath>, + }, } impl<'a> Tiler<'a> { @@ -122,10 +127,10 @@ impl<'a> Tiler<'a> { } fn pack_and_cull_draw_path(&mut self) { - let (paint_metadata, built_clip_path) = match self.path_info { + let (paint_metadata, blend_mode, built_clip_path) = match self.path_info { TilingPathInfo::Clip => unreachable!(), - TilingPathInfo::Draw { paint_metadata, built_clip_path } => { - (paint_metadata, built_clip_path) + TilingPathInfo::Draw { paint_metadata, blend_mode, built_clip_path } => { + (paint_metadata, blend_mode, built_clip_path) } }; @@ -174,8 +179,9 @@ impl<'a> Tiler<'a> { (FillRule::EvenOdd, _) => {} } - // Next, if this is a solid tile, record that fact and stop here. - if paint_metadata.is_opaque { + // Next, if this is a solid tile that completely occludes the background, record + // that fact and stop here. + if paint_metadata.is_opaque && blend_mode.occludes_backdrop() { self.object_builder.built_path.solid_tiles.push(SolidTile::new(tile_coords)); continue; }