Fix handling of solid tiles with the Clear blend mode

This commit is contained in:
Patrick Walton 2020-02-20 18:15:20 -08:00
parent 6acd2d91f7
commit ab55b9509b
5 changed files with 45 additions and 8 deletions

View File

@ -99,7 +99,7 @@ impl CanvasRenderingContext2D {
let mut outline = path.into_outline(); let mut outline = path.into_outline();
outline.transform(&self.current_state.transform); 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 = self.current_state.resolve_paint(&paint);
let paint_id = self.scene.push_paint(&paint); let paint_id = self.scene.push_paint(&paint);

View File

@ -84,3 +84,14 @@ impl Default for BlendMode {
BlendMode::SourceOver 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,
}
}
}

View File

@ -168,6 +168,7 @@ impl<'a> SceneBuilder<'a> {
path_index as u16, path_index as u16,
TilingPathInfo::Draw { TilingPathInfo::Draw {
paint_metadata: &paint_metadata[paint_id.0 as usize], paint_metadata: &paint_metadata[paint_id.0 as usize],
blend_mode: path_object.blend_mode(),
built_clip_path, built_clip_path,
}); });

View File

@ -80,6 +80,11 @@ where
paint_texture: Option<D::Texture>, paint_texture: Option<D::Texture>,
layer_framebuffer_stack: Vec<LayerFramebufferInfo<D>>, layer_framebuffer_stack: Vec<LayerFramebufferInfo<D>>,
// 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 shaders
filter_basic_program: FilterBasicProgram<D>, filter_basic_program: FilterBasicProgram<D>,
filter_basic_vertex_array: FilterBasicVertexArray<D>, filter_basic_vertex_array: FilterBasicVertexArray<D>,
@ -211,6 +216,11 @@ where
device.create_texture(TextureFormat::R8, mask_framebuffer_size); device.create_texture(TextureFormat::R8, mask_framebuffer_size);
let mask_framebuffer = device.create_framebuffer(mask_framebuffer_texture); 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 window_size = dest_framebuffer.window_size(&device);
let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size); let debug_ui_presenter = DebugUIPresenter::new(&device, resources, window_size);
@ -238,6 +248,7 @@ where
mask_framebuffer, mask_framebuffer,
paint_texture: None, paint_texture: None,
layer_framebuffer_stack: vec![], layer_framebuffer_stack: vec![],
clear_paint_texture,
filter_basic_program, filter_basic_program,
filter_basic_vertex_array, filter_basic_vertex_array,
@ -610,7 +621,15 @@ where
MASK_FRAMEBUFFER_HEIGHT as f32))), 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); textures.push(paint_texture);
uniforms.push((&self.alpha_tile_program.paint_texture_uniform, uniforms.push((&self.alpha_tile_program.paint_texture_uniform,
UniformData::TextureUnit(1))); UniformData::TextureUnit(1)));

View File

@ -11,6 +11,7 @@
use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder, SolidTile}; use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder, SolidTile};
use crate::gpu_data::TileObjectPrimitive; use crate::gpu_data::TileObjectPrimitive;
use crate::paint::PaintMetadata; use crate::paint::PaintMetadata;
use pathfinder_content::effects::BlendMode;
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{Contour, Outline, PointIndex}; use pathfinder_content::outline::{Contour, Outline, PointIndex};
use pathfinder_content::segment::Segment; use pathfinder_content::segment::Segment;
@ -42,7 +43,11 @@ pub(crate) struct Tiler<'a> {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub(crate) enum TilingPathInfo<'a> { pub(crate) enum TilingPathInfo<'a> {
Clip, 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> { impl<'a> Tiler<'a> {
@ -122,10 +127,10 @@ impl<'a> Tiler<'a> {
} }
fn pack_and_cull_draw_path(&mut self) { 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::Clip => unreachable!(),
TilingPathInfo::Draw { paint_metadata, built_clip_path } => { TilingPathInfo::Draw { paint_metadata, blend_mode, built_clip_path } => {
(paint_metadata, built_clip_path) (paint_metadata, blend_mode, built_clip_path)
} }
}; };
@ -174,8 +179,9 @@ impl<'a> Tiler<'a> {
(FillRule::EvenOdd, _) => {} (FillRule::EvenOdd, _) => {}
} }
// Next, if this is a solid tile, record that fact and stop here. // Next, if this is a solid tile that completely occludes the background, record
if paint_metadata.is_opaque { // 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)); self.object_builder.built_path.solid_tiles.push(SolidTile::new(tile_coords));
continue; continue;
} }