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();
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);

View File

@ -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,
}
}
}

View File

@ -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,
});

View File

@ -80,6 +80,11 @@ where
paint_texture: Option<D::Texture>,
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_basic_program: FilterBasicProgram<D>,
filter_basic_vertex_array: FilterBasicVertexArray<D>,
@ -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)));

View File

@ -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;
}