Implement the even-odd fill rule

This commit is contained in:
Patrick Walton 2020-02-17 14:44:48 -08:00
parent 243f2cc9b2
commit 3ad1c25796
19 changed files with 382 additions and 71 deletions

View File

@ -15,6 +15,7 @@ use gl;
use pathfinder_canvas::{CanvasFontContext, CanvasRenderingContext2D, FillStyle, LineJoin, Path2D};
use pathfinder_canvas::{TextAlign, TextMetrics};
use pathfinder_color::{ColorF, ColorU};
use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::ArcDirection;
use pathfinder_content::stroke::LineCap;
use pathfinder_geometry::rect::{RectF, RectI};
@ -372,7 +373,8 @@ pub unsafe extern "C" fn PFCanvasSetStrokeStyle(canvas: PFCanvasRef,
/// first.
#[no_mangle]
pub unsafe extern "C" fn PFCanvasFillPath(canvas: PFCanvasRef, path: PFPathRef) {
(*canvas).fill_path(*Box::from_raw(path))
// TODO(pcwalton): Expose fill rules to the C API.
(*canvas).fill_path(*Box::from_raw(path), FillRule::Winding)
}
/// This function automatically destroys the path. If you wish to use the path again, clone it

View File

@ -12,6 +12,7 @@
use pathfinder_color::ColorU;
use pathfinder_content::dash::OutlineDash;
use pathfinder_content::fill::FillRule;
use pathfinder_content::gradient::Gradient;
use pathfinder_content::outline::{ArcDirection, Contour, Outline};
use pathfinder_content::pattern::Pattern;
@ -80,7 +81,7 @@ impl CanvasRenderingContext2D {
pub fn fill_rect(&mut self, rect: RectF) {
let mut path = Path2D::new();
path.rect(rect);
self.fill_path(path);
self.fill_path(path, FillRule::Winding);
}
#[inline]
@ -156,14 +157,14 @@ impl CanvasRenderingContext2D {
// Drawing paths
#[inline]
pub fn fill_path(&mut self, path: Path2D) {
pub fn fill_path(&mut self, path: Path2D, fill_rule: FillRule) {
let mut outline = path.into_outline();
outline.transform(&self.current_state.transform);
let paint = self.current_state.resolve_paint(&self.current_state.fill_paint);
let paint_id = self.scene.push_paint(&paint);
self.push_path(outline, paint_id);
self.push_path(outline, paint_id, fill_rule);
}
#[inline]
@ -196,18 +197,20 @@ impl CanvasRenderingContext2D {
outline = stroke_to_fill.into_outline();
outline.transform(&self.current_state.transform);
self.push_path(outline, paint_id);
self.push_path(outline, paint_id, FillRule::Winding);
}
pub fn clip_path(&mut self, path: Path2D) {
pub fn clip_path(&mut self, path: Path2D, fill_rule: FillRule) {
let mut outline = path.into_outline();
outline.transform(&self.current_state.transform);
let clip_path_id = self.scene.push_clip_path(ClipPath::new(outline, String::new()));
let clip_path_id = self.scene
.push_clip_path(ClipPath::new(outline, fill_rule, String::new()));
self.current_state.clip_path = Some(clip_path_id);
}
fn push_path(&mut self, outline: Outline, paint_id: PaintId) {
fn push_path(&mut self, outline: Outline, paint_id: PaintId, fill_rule: FillRule) {
let clip_path = self.current_state.clip_path;
if !self.current_state.shadow_paint.is_fully_transparent() {
@ -216,10 +219,14 @@ impl CanvasRenderingContext2D {
let mut outline = outline.clone();
outline.transform(&Transform2F::from_translation(self.current_state.shadow_offset));
self.scene.push_path(DrawPath::new(outline, paint_id, clip_path, String::new()))
self.scene.push_path(DrawPath::new(outline,
paint_id,
clip_path,
fill_rule,
String::new()))
}
self.scene.push_path(DrawPath::new(outline, paint_id, clip_path, String::new()))
self.scene.push_path(DrawPath::new(outline, paint_id, clip_path, fill_rule, String::new()))
}
// Transformations

17
content/src/fill.rs Normal file
View File

@ -0,0 +1,17 @@
// pathfinder/content/src/fill.rs
//
// Copyright © 2019 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Fill rules.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum FillRule {
Winding,
EvenOdd,
}

View File

@ -19,6 +19,7 @@ extern crate log;
pub mod clip;
pub mod dash;
pub mod fill;
pub mod gradient;
pub mod orientation;
pub mod outline;

View File

@ -701,7 +701,8 @@ impl MetalDevice {
if blit_command_encoder.is_none() {
blit_command_encoder = Some(command_buffer.new_blit_command_encoder());
}
let blit_command_encoder = blit_command_encoder.as_ref().unwrap();
let blit_command_encoder =
blit_command_encoder.as_ref().expect("Where's the blit command encoder?");
blit_command_encoder.synchronize_resource(&texture.texture);
texture.dirty.set(false);
}
@ -725,9 +726,10 @@ impl MetalDevice {
.descriptor));
// Create render pipeline state.
let pipeline_color_attachment = render_pipeline_descriptor.color_attachments()
.object_at(0)
.unwrap();
let pipeline_color_attachment =
render_pipeline_descriptor.color_attachments()
.object_at(0)
.expect("Where's the color attachment?");
self.prepare_pipeline_color_attachment_for_render(pipeline_color_attachment,
render_state);
@ -753,7 +755,9 @@ impl MetalDevice {
.enumerate() {
let real_index = vertex_buffer_index as u64 + FIRST_VERTEX_BUFFER_INDEX;
let buffer = vertex_buffer.buffer.borrow();
let buffer = buffer.as_ref().map(|buffer| buffer.as_ref()).unwrap();
let buffer = buffer.as_ref()
.map(|buffer| buffer.as_ref())
.expect("Where's the vertex buffer?");
encoder.set_vertex_buffer(real_index, Some(buffer), 0);
encoder.use_resource(buffer, MTLResourceUsage::Read);
}

View File

@ -20,6 +20,7 @@ use crate::scene::Scene;
use crate::tile_map::DenseTileMap;
use crate::tiles::{self, TILE_HEIGHT, TILE_WIDTH, Tiler, TilingPathInfo};
use crate::z_buffer::ZBuffer;
use pathfinder_content::fill::FillRule;
use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8};
use pathfinder_geometry::vector::{Vector2F, Vector2I};
use pathfinder_geometry::rect::{RectF, RectI};
@ -52,6 +53,7 @@ pub(crate) struct BuiltPath {
pub mask_tiles: Vec<MaskTile>,
pub alpha_tiles: Vec<AlphaTile>,
pub tiles: DenseTileMap<TileObjectPrimitive>,
pub fill_rule: FillRule,
}
impl<'a> SceneBuilder<'a> {
@ -122,6 +124,7 @@ impl<'a> SceneBuilder<'a> {
let mut tiler = Tiler::new(self,
&outline,
path_object.fill_rule(),
view_box,
path_index as u16,
TilingPathInfo::Clip);
@ -150,6 +153,7 @@ impl<'a> SceneBuilder<'a> {
let mut tiler = Tiler::new(self,
&outline,
path_object.fill_rule(),
view_box,
path_index as u16,
TilingPathInfo::Draw {
@ -165,14 +169,18 @@ impl<'a> SceneBuilder<'a> {
fn cull_tiles(&self, built_clip_paths: Vec<BuiltPath>, built_draw_paths: Vec<BuiltPath>)
-> CulledTiles {
let mut culled_tiles = CulledTiles { mask_tiles: vec![], alpha_tiles: vec![] };
let mut culled_tiles = CulledTiles {
mask_winding_tiles: vec![],
mask_evenodd_tiles: vec![],
alpha_tiles: vec![],
};
for built_clip_path in built_clip_paths {
culled_tiles.mask_tiles.extend_from_slice(&built_clip_path.mask_tiles);
culled_tiles.push_mask_tiles(&built_clip_path);
}
for built_draw_path in built_draw_paths {
culled_tiles.mask_tiles.extend_from_slice(&built_draw_path.mask_tiles);
culled_tiles.push_mask_tiles(&built_draw_path);
for alpha_tile in built_draw_path.alpha_tiles {
let alpha_tile_coords = alpha_tile.upper_left.tile_position();
@ -191,9 +199,20 @@ impl<'a> SceneBuilder<'a> {
let solid_tiles = self.z_buffer.build_solid_tiles(&self.scene.paths,
paint_metadata,
0..path_count);
if !culled_tiles.mask_tiles.is_empty() {
self.listener.send(RenderCommand::RenderMaskTiles(culled_tiles.mask_tiles));
if !culled_tiles.mask_winding_tiles.is_empty() {
self.listener.send(RenderCommand::RenderMaskTiles {
tiles: culled_tiles.mask_winding_tiles,
fill_rule: FillRule::Winding,
});
}
if !culled_tiles.mask_evenodd_tiles.is_empty() {
self.listener.send(RenderCommand::RenderMaskTiles {
tiles: culled_tiles.mask_evenodd_tiles,
fill_rule: FillRule::EvenOdd,
});
}
if !solid_tiles.is_empty() {
self.listener.send(RenderCommand::DrawSolidTiles(solid_tiles));
}
@ -218,7 +237,8 @@ impl<'a> SceneBuilder<'a> {
}
struct CulledTiles {
mask_tiles: Vec<MaskTile>,
mask_winding_tiles: Vec<MaskTile>,
mask_evenodd_tiles: Vec<MaskTile>,
alpha_tiles: Vec<AlphaTile>,
}
@ -231,11 +251,11 @@ pub struct TileStats {
// Utilities for built objects
impl ObjectBuilder {
pub(crate) fn new(bounds: RectF) -> ObjectBuilder {
pub(crate) fn new(bounds: RectF, fill_rule: FillRule) -> ObjectBuilder {
let tile_rect = tiles::round_rect_out_to_tile_bounds(bounds);
let tiles = DenseTileMap::new(tile_rect);
ObjectBuilder {
built_path: BuiltPath { mask_tiles: vec![], alpha_tiles: vec![], tiles },
built_path: BuiltPath { mask_tiles: vec![], alpha_tiles: vec![], tiles, fill_rule },
bounds,
fills: vec![],
}
@ -533,3 +553,12 @@ fn calculate_mask_uv(tile_index: u16, tile_offset: Vector2I) -> Vector2I {
let mask_uv = Vector2I::new(mask_u, mask_v) + tile_offset;
mask_uv.to_f32().scale(mask_scale).to_i32()
}
impl CulledTiles {
fn push_mask_tiles(&mut self, built_path: &BuiltPath) {
match built_path.fill_rule {
FillRule::Winding => self.mask_winding_tiles.extend_from_slice(&built_path.mask_tiles),
FillRule::EvenOdd => self.mask_evenodd_tiles.extend_from_slice(&built_path.mask_tiles),
}
}
}

View File

@ -11,7 +11,7 @@
use crate::gpu::debug::DebugUIPresenter;
use crate::gpu::options::{DestFramebuffer, RendererOptions};
use crate::gpu::shaders::{FillProgram, AlphaTileProgram, AlphaTileVertexArray, FillVertexArray};
use crate::gpu::shaders::{MAX_FILLS_PER_BATCH, MaskWindingTileProgram, MaskWindingTileVertexArray};
use crate::gpu::shaders::{MAX_FILLS_PER_BATCH, MaskTileProgram, MaskTileVertexArray};
use crate::gpu::shaders::{PostprocessProgram, PostprocessVertexArray, ReprojectionProgram};
use crate::gpu::shaders::{ReprojectionVertexArray, SolidTileProgram, SolidTileVertexArray};
use crate::gpu::shaders::{StencilProgram, StencilVertexArray};
@ -20,6 +20,7 @@ use crate::gpu_data::{RenderCommand, SolidTileVertex};
use crate::post::DefringingKernel;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_color::{self as color, ColorF, ColorU};
use pathfinder_content::fill::FillRule;
use pathfinder_geometry::vector::{Vector2I, Vector4F};
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F;
@ -57,10 +58,12 @@ where
dest_framebuffer: DestFramebuffer<D>,
options: RendererOptions,
fill_program: FillProgram<D>,
mask_winding_tile_program: MaskWindingTileProgram<D>,
mask_winding_tile_program: MaskTileProgram<D>,
mask_evenodd_tile_program: MaskTileProgram<D>,
solid_tile_program: SolidTileProgram<D>,
alpha_tile_program: AlphaTileProgram<D>,
mask_winding_tile_vertex_array: MaskWindingTileVertexArray<D>,
mask_winding_tile_vertex_array: MaskTileVertexArray<D>,
mask_evenodd_tile_vertex_array: MaskTileVertexArray<D>,
solid_tile_vertex_array: SolidTileVertexArray<D>,
alpha_tile_vertex_array: AlphaTileVertexArray<D>,
area_lut_texture: D::Texture,
@ -113,7 +116,12 @@ where
options: RendererOptions)
-> Renderer<D> {
let fill_program = FillProgram::new(&device, resources);
let mask_winding_tile_program = MaskWindingTileProgram::new(&device, resources);
let mask_winding_tile_program = MaskTileProgram::new(FillRule::Winding,
&device,
resources);
let mask_evenodd_tile_program = MaskTileProgram::new(FillRule::EvenOdd,
&device,
resources);
let solid_tile_program = SolidTileProgram::new(&device, resources);
let alpha_tile_program = AlphaTileProgram::new(&device, resources);
let postprocess_program = PostprocessProgram::new(&device, resources);
@ -145,11 +153,16 @@ where
&quad_vertex_positions_buffer,
&quad_vertex_indices_buffer,
);
let mask_winding_tile_vertex_array = MaskWindingTileVertexArray::new(
let mask_winding_tile_vertex_array = MaskTileVertexArray::new(
&device,
&mask_winding_tile_program,
&quads_vertex_indices_buffer,
);
let mask_evenodd_tile_vertex_array = MaskTileVertexArray::new(
&device,
&mask_evenodd_tile_program,
&quads_vertex_indices_buffer,
);
let alpha_tile_vertex_array = AlphaTileVertexArray::new(
&device,
&alpha_tile_program,
@ -196,9 +209,11 @@ where
options,
fill_program,
mask_winding_tile_program,
mask_evenodd_tile_program,
solid_tile_program,
alpha_tile_program,
mask_winding_tile_vertex_array,
mask_evenodd_tile_vertex_array,
solid_tile_vertex_array,
alpha_tile_vertex_array,
area_lut_texture,
@ -257,10 +272,10 @@ where
self.draw_buffered_fills();
self.begin_composite_timer_query();
}
RenderCommand::RenderMaskTiles(ref mask_tiles) => {
RenderCommand::RenderMaskTiles { tiles: ref mask_tiles, fill_rule } => {
let count = mask_tiles.len();
self.upload_mask_tiles(mask_tiles);
self.draw_mask_tiles(count as u32);
self.upload_mask_tiles(mask_tiles, fill_rule);
self.draw_mask_tiles(count as u32, fill_rule);
}
RenderCommand::DrawSolidTiles(ref solid_tile_vertices) => {
let count = solid_tile_vertices.len() / 4;
@ -397,9 +412,14 @@ where
TextureDataRef::U8(texels));
}
fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile]) {
fn upload_mask_tiles(&mut self, mask_tiles: &[MaskTile], fill_rule: FillRule) {
let vertex_array = match fill_rule {
FillRule::Winding => &self.mask_winding_tile_vertex_array,
FillRule::EvenOdd => &self.mask_evenodd_tile_vertex_array,
};
self.device.allocate_buffer(
&self.mask_winding_tile_vertex_array.vertex_buffer,
&vertex_array.vertex_buffer,
BufferData::Memory(&mask_tiles),
BufferTarget::Vertex,
BufferUploadMode::Dynamic,
@ -529,7 +549,7 @@ where
Transform4F::from_scale(scale).translate(Vector4F::new(-1.0, 1.0, 0.0, 1.0))
}
fn draw_mask_tiles(&mut self, tile_count: u32) {
fn draw_mask_tiles(&mut self, tile_count: u32, fill_rule: FillRule) {
let clear_color =
if self.framebuffer_flags
.contains(FramebufferFlags::MUST_PRESERVE_MASK_FRAMEBUFFER_CONTENTS) {
@ -538,10 +558,19 @@ where
Some(ColorF::new(1.0, 1.0, 1.0, 1.0))
};
let (mask_tile_program, mask_tile_vertex_array) = match fill_rule {
FillRule::Winding => {
(&self.mask_winding_tile_program, &self.mask_winding_tile_vertex_array)
}
FillRule::EvenOdd => {
(&self.mask_evenodd_tile_program, &self.mask_evenodd_tile_vertex_array)
}
};
self.device.draw_elements(tile_count * 6, &RenderState {
target: &RenderTarget::Framebuffer(&self.mask_framebuffer),
program: &self.mask_winding_tile_program.program,
vertex_array: &self.mask_winding_tile_vertex_array.vertex_array,
program: &mask_tile_program.program,
vertex_array: &mask_tile_vertex_array.vertex_array,
primitive: Primitive::Triangles,
textures: &[self.device.framebuffer_texture(&self.fill_framebuffer)],
uniforms: &[
@ -550,7 +579,6 @@ where
],
viewport: self.mask_viewport(),
options: RenderOptions {
// TODO(pcwalton): MIN blending for masks.
blend: Some(BlendState {
func: BlendFunc::RGBOneAlphaOne,
op: BlendOp::Min,

View File

@ -9,6 +9,7 @@
// except according to those terms.
use crate::gpu_data::FillBatchPrimitive;
use pathfinder_content::fill::FillRule;
use pathfinder_gpu::{BufferData, BufferTarget, BufferUploadMode, Device, VertexAttrClass};
use pathfinder_gpu::{VertexAttrDescriptor, VertexAttrType};
use pathfinder_gpu::resources::ResourceLoader;
@ -120,23 +121,23 @@ where
}
}
pub struct MaskWindingTileVertexArray<D> where D: Device {
pub struct MaskTileVertexArray<D> where D: Device {
pub vertex_array: D::VertexArray,
pub vertex_buffer: D::Buffer,
}
impl<D> MaskWindingTileVertexArray<D> where D: Device {
impl<D> MaskTileVertexArray<D> where D: Device {
pub fn new(device: &D,
mask_winding_tile_program: &MaskWindingTileProgram<D>,
mask_tile_program: &MaskTileProgram<D>,
quads_vertex_indices_buffer: &D::Buffer)
-> MaskWindingTileVertexArray<D> {
-> MaskTileVertexArray<D> {
let (vertex_array, vertex_buffer) = (device.create_vertex_array(), device.create_buffer());
let position_attr = device.get_vertex_attr(&mask_winding_tile_program.program, "Position")
let position_attr = device.get_vertex_attr(&mask_tile_program.program, "Position")
.unwrap();
let fill_tex_coord_attr = device.get_vertex_attr(&mask_winding_tile_program.program,
let fill_tex_coord_attr = device.get_vertex_attr(&mask_tile_program.program,
"FillTexCoord").unwrap();
let backdrop_attr = device.get_vertex_attr(&mask_winding_tile_program.program, "Backdrop")
let backdrop_attr = device.get_vertex_attr(&mask_tile_program.program, "Backdrop")
.unwrap();
device.bind_buffer(&vertex_array, &vertex_buffer, BufferTarget::Vertex);
@ -169,7 +170,7 @@ impl<D> MaskWindingTileVertexArray<D> where D: Device {
});
device.bind_buffer(&vertex_array, quads_vertex_indices_buffer, BufferTarget::Index);
MaskWindingTileVertexArray { vertex_array, vertex_buffer }
MaskTileVertexArray { vertex_array, vertex_buffer }
}
}
@ -308,16 +309,26 @@ where
}
}
pub struct MaskWindingTileProgram<D> where D: Device {
pub struct MaskTileProgram<D> where D: Device {
pub program: D::Program,
pub fill_texture_uniform: D::Uniform,
}
impl<D> MaskWindingTileProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> MaskWindingTileProgram<D> {
let program = device.create_program(resources, "mask_winding");
impl<D> MaskTileProgram<D> where D: Device {
pub fn new(fill_rule: FillRule, device: &D, resources: &dyn ResourceLoader)
-> MaskTileProgram<D> {
let program_name = match fill_rule {
FillRule::Winding => "mask_winding",
FillRule::EvenOdd => "mask_evenodd",
};
let program = device.create_program_from_shader_names(resources,
program_name,
"mask",
program_name);
let fill_texture_uniform = device.get_uniform(&program, "FillTexture");
MaskWindingTileProgram { program, fill_texture_uniform }
MaskTileProgram { program, fill_texture_uniform }
}
}

View File

@ -12,6 +12,7 @@
use crate::options::BoundingQuad;
use pathfinder_color::ColorU;
use pathfinder_content::fill::FillRule;
use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8};
use pathfinder_geometry::vector::Vector2I;
use std::fmt::{Debug, Formatter, Result as DebugResult};
@ -22,7 +23,7 @@ pub enum RenderCommand {
AddPaintData(PaintData),
AddFills(Vec<FillBatchPrimitive>),
FlushFills,
RenderMaskTiles(Vec<MaskTile>),
RenderMaskTiles { tiles: Vec<MaskTile>, fill_rule: FillRule },
DrawAlphaTiles(Vec<AlphaTile>),
DrawSolidTiles(Vec<SolidTileVertex>),
Finish { build_time: Duration },
@ -121,8 +122,8 @@ impl Debug for RenderCommand {
}
RenderCommand::AddFills(ref fills) => write!(formatter, "AddFills(x{})", fills.len()),
RenderCommand::FlushFills => write!(formatter, "FlushFills"),
RenderCommand::RenderMaskTiles(ref tiles) => {
write!(formatter, "RenderMaskTiles(x{})", tiles.len())
RenderCommand::RenderMaskTiles { ref tiles, fill_rule } => {
write!(formatter, "RenderMaskTiles(x{}, {:?})", tiles.len(), fill_rule)
}
RenderCommand::DrawAlphaTiles(ref tiles) => {
write!(formatter, "DrawAlphaTiles(x{})", tiles.len())

View File

@ -16,6 +16,7 @@ use crate::options::{BuildOptions, PreparedBuildOptions};
use crate::options::{PreparedRenderTransform, RenderCommandListener};
use crate::paint::{Paint, PaintId, PaintInfo, Palette};
use pathfinder_color::ColorU;
use pathfinder_content::fill::FillRule;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
@ -214,12 +215,14 @@ pub struct DrawPath {
outline: Outline,
paint: PaintId,
clip_path: Option<ClipPathId>,
fill_rule: FillRule,
name: String,
}
#[derive(Clone, Debug)]
pub struct ClipPath {
outline: Outline,
fill_rule: FillRule,
name: String,
}
@ -228,9 +231,13 @@ pub struct ClipPathId(pub u32);
impl DrawPath {
#[inline]
pub fn new(outline: Outline, paint: PaintId, clip_path: Option<ClipPathId>, name: String)
pub fn new(outline: Outline,
paint: PaintId,
clip_path: Option<ClipPathId>,
fill_rule: FillRule,
name: String)
-> DrawPath {
DrawPath { outline, paint, clip_path, name }
DrawPath { outline, paint, clip_path, fill_rule, name }
}
#[inline]
@ -247,16 +254,26 @@ impl DrawPath {
pub(crate) fn paint(&self) -> PaintId {
self.paint
}
#[inline]
pub(crate) fn fill_rule(&self) -> FillRule {
self.fill_rule
}
}
impl ClipPath {
#[inline]
pub fn new(outline: Outline, name: String) -> ClipPath {
ClipPath { outline, name }
pub fn new(outline: Outline, fill_rule: FillRule, name: String) -> ClipPath {
ClipPath { outline, fill_rule, name }
}
#[inline]
pub fn outline(&self) -> &Outline {
&self.outline
}
#[inline]
pub(crate) fn fill_rule(&self) -> FillRule {
self.fill_rule
}
}

View File

@ -11,6 +11,7 @@
use crate::builder::{BuiltPath, ObjectBuilder, SceneBuilder};
use crate::gpu_data::TileObjectPrimitive;
use crate::paint::PaintMetadata;
use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::{Contour, Outline, PointIndex};
use pathfinder_content::segment::Segment;
use pathfinder_content::sorted_vector::SortedVector;
@ -49,6 +50,7 @@ impl<'a> Tiler<'a> {
pub(crate) fn new(
scene_builder: &'a SceneBuilder<'a>,
outline: &'a Outline,
fill_rule: FillRule,
view_box: RectF,
object_index: u16,
path_info: TilingPathInfo<'a>,
@ -57,7 +59,7 @@ impl<'a> Tiler<'a> {
.bounds()
.intersection(view_box)
.unwrap_or(RectF::default());
let object_builder = ObjectBuilder::new(bounds);
let object_builder = ObjectBuilder::new(bounds, fill_rule);
Tiler {
scene_builder,
@ -162,10 +164,14 @@ impl<'a> Tiler<'a> {
};
if clip_tile.is_none() && draw_tile.is_solid() {
// This is a simple case, so we can try some optimizations. First, blank tiles
// are always skipped.
if draw_tile.backdrop == 0 {
continue;
// This is the simple case of a solid tile with no clip, so there are optimization
// opportunities. First, tiles that must be blank per the fill rule are always
// skipped.
match (self.object_builder.built_path.fill_rule, draw_tile.backdrop) {
(FillRule::Winding, 0) => continue,
(FillRule::Winding, _) => {}
(FillRule::EvenOdd, backdrop) if backdrop % 2 == 0 => continue,
(FillRule::EvenOdd, _) => {}
}
// Next, if this is a solid tile, just poke it into the Z-buffer. We don't need

View File

@ -0,0 +1,34 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
precision highp float;
in vec2 aPosition;
in vec2 aFillTexCoord;
in int aBackdrop;
out vec2 vFillTexCoord;
out float vBackdrop;
void main(){
vec2 position = mix(vec2(- 1.0), vec2(1.0), aPosition);
vFillTexCoord = aFillTexCoord;
vBackdrop = float(aBackdrop);
gl_Position = vec4(position, 0.0, 1.0);
}

View File

@ -0,0 +1,28 @@
#version {{version}}
// Automatically generated from files in pathfinder/shaders/. Do not edit!
precision highp float;
uniform sampler2D uFillTexture;
in vec2 vFillTexCoord;
in float vBackdrop;
out vec4 oFragColor;
void main(){
float alpha = texture(uFillTexture, vFillTexCoord). r + vBackdrop;
oFragColor = vec4(1.0 - abs(1.0 - mod(alpha, 2.0)));
}

View File

@ -0,0 +1,31 @@
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float2 vFillTexCoord [[user(locn0)]];
float vBackdrop [[user(locn1)]];
float4 gl_Position [[position]];
};
struct main0_in
{
float2 aPosition [[attribute(0)]];
float2 aFillTexCoord [[attribute(1)]];
int aBackdrop [[attribute(2)]];
};
vertex main0_out main0(main0_in in [[stage_in]])
{
main0_out out = {};
float2 position = mix(float2(-1.0), float2(1.0), in.aPosition);
position.y = -position.y;
out.vFillTexCoord = in.aFillTexCoord;
out.vBackdrop = float(in.aBackdrop);
out.gl_Position = float4(position, 0.0, 1.0);
return out;
}

View File

@ -0,0 +1,40 @@
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct spvDescriptorSetBuffer0
{
texture2d<float> uFillTexture [[id(0)]];
sampler uFillTextureSmplr [[id(1)]];
};
struct main0_out
{
float4 oFragColor [[color(0)]];
};
struct main0_in
{
float2 vFillTexCoord [[user(locn0)]];
float vBackdrop [[user(locn1)]];
};
// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()
template<typename Tx, typename Ty>
Tx mod(Tx x, Ty y)
{
return x - y * floor(x / y);
}
fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]])
{
main0_out out = {};
float alpha = spvDescriptorSet0.uFillTexture.sample(spvDescriptorSet0.uFillTextureSmplr, in.vFillTexCoord).x + in.vBackdrop;
out.oFragColor = float4(1.0 - abs(1.0 - mod(alpha, 2.0)));
return out;
}

View File

@ -11,8 +11,9 @@ SHADERS=\
demo_ground.vs.glsl \
fill.fs.glsl \
fill.vs.glsl \
mask.vs.glsl \
mask_evenodd.fs.glsl \
mask_winding.fs.glsl \
mask_winding.vs.glsl \
post.fs.glsl \
post.vs.glsl \
reproject.fs.glsl \

View File

@ -0,0 +1,25 @@
#version 330
// pathfinder/shaders/mask_evenodd.fs.glsl
//
// Copyright © 2020 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
precision highp float;
uniform sampler2D uFillTexture;
in vec2 vFillTexCoord;
in float vBackdrop;
out vec4 oFragColor;
void main() {
float alpha = texture(uFillTexture, vFillTexCoord).r + vBackdrop;
oFragColor = vec4(1.0 - abs(1.0 - mod(alpha, 2.0)));
}

View File

@ -15,6 +15,7 @@ extern crate bitflags;
use hashbrown::HashMap;
use pathfinder_color::ColorU;
use pathfinder_content::fill::FillRule;
use pathfinder_content::outline::Outline;
use pathfinder_content::segment::{Segment, SegmentFlags};
use pathfinder_content::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle};
@ -27,9 +28,10 @@ use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, Scene};
use std::fmt::{Display, Formatter, Result as FormatResult};
use std::mem;
use usvg::{Color as SvgColor, LineCap as UsvgLineCap, LineJoin as UsvgLineJoin, Node, NodeExt};
use usvg::{NodeKind, Opacity, Paint as UsvgPaint, PathSegment as UsvgPathSegment};
use usvg::{Rect as UsvgRect, Transform as UsvgTransform, Tree, Visibility};
use usvg::{Color as SvgColor, FillRule as UsvgFillRule, LineCap as UsvgLineCap};
use usvg::{LineJoin as UsvgLineJoin, Node, NodeExt, NodeKind, Opacity, Paint as UsvgPaint};
use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, Transform as UsvgTransform};
use usvg::{Tree, Visibility};
const HAIRLINE_STROKE_WIDTH: f32 = 0.0333;
@ -134,7 +136,12 @@ impl BuiltSVG {
let outline = Outline::from_segments(path);
let name = format!("Fill({})", node.id());
self.push_draw_path(outline, name, &state, &fill.paint, fill.opacity);
self.push_draw_path(outline,
name,
&state,
&fill.paint,
fill.opacity,
fill.rule);
}
if let Some(ref stroke) = path.stroke {
@ -154,7 +161,12 @@ impl BuiltSVG {
outline.transform(&state.transform);
let name = format!("Stroke({})", node.id());
self.push_draw_path(outline, name, &state, &stroke.paint, stroke.opacity);
self.push_draw_path(outline,
name,
&state,
&stroke.paint,
stroke.opacity,
UsvgFillRule::NonZero);
}
}
NodeKind::Path(..) => {}
@ -167,7 +179,8 @@ impl BuiltSVG {
if let Some(clip_outline) = clip_outline {
let name = format!("ClipPath({})", node.id());
let clip_path = ClipPath::new(clip_outline, name);
// FIXME(pcwalton): Is the winding fill rule correct to use?
let clip_path = ClipPath::new(clip_outline, FillRule::Winding, name);
let clip_path_id = self.scene.push_clip_path(clip_path);
self.clip_paths.insert(node.id().to_owned(), clip_path_id);
}
@ -215,11 +228,13 @@ impl BuiltSVG {
name: String,
state: &State,
paint: &UsvgPaint,
opacity: Opacity) {
opacity: Opacity,
fill_rule: UsvgFillRule) {
let style = self.scene.push_paint(&Paint::from_svg_paint(paint,
opacity,
&mut self.result_flags));
self.scene.push_path(DrawPath::new(outline, style, state.clip_path, name));
let fill_rule = FillRule::from_usvg_fill_rule(fill_rule);
self.scene.push_path(DrawPath::new(outline, style, state.clip_path, fill_rule, name));
}
}
@ -435,6 +450,20 @@ impl LineJoinExt for LineJoin {
}
}
trait FillRuleExt {
fn from_usvg_fill_rule(usvg_fill_rule: UsvgFillRule) -> Self;
}
impl FillRuleExt for FillRule {
#[inline]
fn from_usvg_fill_rule(usvg_fill_rule: UsvgFillRule) -> FillRule {
match usvg_fill_rule {
UsvgFillRule::NonZero => FillRule::Winding,
UsvgFillRule::EvenOdd => FillRule::EvenOdd,
}
}
}
#[derive(Clone)]
struct State {
// Where paths are being appended to.