Implement `fill-opacity` and `stroke-opacity` in SVG

This commit is contained in:
Patrick Walton 2019-05-14 18:09:01 -07:00
parent 25a6c33a1a
commit 0deb12b3a2
5 changed files with 58 additions and 28 deletions

View File

@ -82,8 +82,15 @@ impl<'a> SceneBuilder<'a> {
let path_object = &scene.paths[path_index]; let path_object = &scene.paths[path_index];
let outline = scene.apply_render_options(path_object.outline(), built_options); let outline = scene.apply_render_options(path_object.outline(), built_options);
let paint_id = path_object.paint(); let paint_id = path_object.paint();
let object_is_opaque = scene.paints[paint_id.0 as usize].is_opaque();
let mut tiler = Tiler::new(self,
&outline,
view_box,
path_index as u16,
paint_id,
object_is_opaque);
let mut tiler = Tiler::new(self, &outline, view_box, paint_id, path_index as u16);
tiler.generate_tiles(); tiler.generate_tiles();
self.listener.send(RenderCommand::AddFills(tiler.built_object.fills)); self.listener.send(RenderCommand::AddFills(tiler.built_object.fills));

View File

@ -9,12 +9,27 @@
// except according to those terms. // except according to those terms.
use crate::gpu_data::PaintData; use crate::gpu_data::PaintData;
use crate::scene::{PaintId, Scene}; use crate::scene::Scene;
use pathfinder_geometry::basic::point::Point2DI32; use pathfinder_geometry::basic::point::Point2DI32;
use pathfinder_geometry::color::ColorU;
const PAINT_TEXTURE_WIDTH: i32 = 256; const PAINT_TEXTURE_WIDTH: i32 = 256;
const PAINT_TEXTURE_HEIGHT: i32 = 256; const PAINT_TEXTURE_HEIGHT: i32 = 256;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Paint {
pub color: ColorU,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct PaintId(pub u16);
impl Paint {
pub(crate) fn is_opaque(&self) -> bool {
self.color.a == 255
}
}
impl Scene { impl Scene {
pub fn build_paint_data(&self) -> PaintData { pub fn build_paint_data(&self) -> PaintData {
let size = Point2DI32::new(PAINT_TEXTURE_WIDTH, PAINT_TEXTURE_HEIGHT); let size = Point2DI32::new(PAINT_TEXTURE_WIDTH, PAINT_TEXTURE_HEIGHT);

View File

@ -14,6 +14,7 @@ use crate::builder::SceneBuilder;
use crate::concurrent::executor::Executor; use crate::concurrent::executor::Executor;
use crate::options::{PreparedRenderOptions, PreparedRenderTransform}; use crate::options::{PreparedRenderOptions, PreparedRenderTransform};
use crate::options::{RenderCommandListener, RenderOptions}; use crate::options::{RenderCommandListener, RenderOptions};
use crate::paint::{Paint, PaintId};
use hashbrown::HashMap; use hashbrown::HashMap;
use pathfinder_geometry::basic::point::Point2DF32; use pathfinder_geometry::basic::point::Point2DF32;
use pathfinder_geometry::basic::rect::RectF32; use pathfinder_geometry::basic::rect::RectF32;
@ -222,11 +223,3 @@ impl PathObject {
self.paint self.paint
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Paint {
pub color: ColorU,
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct PaintId(pub u16);

View File

@ -10,8 +10,7 @@
use crate::builder::SceneBuilder; use crate::builder::SceneBuilder;
use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, TileObjectPrimitive}; use crate::gpu_data::{AlphaTileBatchPrimitive, BuiltObject, TileObjectPrimitive};
use crate::paint; use crate::paint::{self, PaintId};
use crate::scene::PaintId;
use crate::sorted_vector::SortedVector; use crate::sorted_vector::SortedVector;
use pathfinder_geometry::basic::line_segment::LineSegmentF32; use pathfinder_geometry::basic::line_segment::LineSegmentF32;
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32}; use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32};
@ -33,6 +32,7 @@ pub(crate) struct Tiler<'a> {
pub built_object: BuiltObject, pub built_object: BuiltObject,
paint_id: PaintId, paint_id: PaintId,
object_index: u16, object_index: u16,
object_is_opaque: bool,
point_queue: SortedVector<QueuedEndpoint>, point_queue: SortedVector<QueuedEndpoint>,
active_edges: SortedVector<ActiveEdge>, active_edges: SortedVector<ActiveEdge>,
@ -45,8 +45,9 @@ impl<'a> Tiler<'a> {
builder: &'a SceneBuilder<'a>, builder: &'a SceneBuilder<'a>,
outline: &'a Outline, outline: &'a Outline,
view_box: RectF32, view_box: RectF32,
paint_id: PaintId,
object_index: u16, object_index: u16,
paint_id: PaintId,
object_is_opaque: bool,
) -> Tiler<'a> { ) -> Tiler<'a> {
let bounds = outline let bounds = outline
.bounds() .bounds()
@ -58,8 +59,9 @@ impl<'a> Tiler<'a> {
builder, builder,
outline, outline,
built_object, built_object,
paint_id,
object_index, object_index,
paint_id,
object_is_opaque,
point_queue: SortedVector::new(), point_queue: SortedVector::new(),
active_edges: SortedVector::new(), active_edges: SortedVector::new(),
@ -112,11 +114,18 @@ impl<'a> Tiler<'a> {
let tile_coords = self let tile_coords = self
.built_object .built_object
.local_tile_index_to_coords(tile_index as u32); .local_tile_index_to_coords(tile_index as u32);
if tile.is_solid() { if tile.is_solid() {
if tile.backdrop != 0 { // Blank tiles are always skipped.
self.builder.z_buffer.update(tile_coords, self.object_index); if tile.backdrop == 0 {
continue;
}
// If this is a solid tile, poke it into the Z-buffer and stop here.
if self.object_is_opaque {
self.builder.z_buffer.update(tile_coords, self.object_index);
continue;
} }
continue;
} }
let origin_uv = paint::paint_id_to_tex_coords(self.paint_id); let origin_uv = paint::paint_id_to_tex_coords(self.paint_id);

View File

@ -21,10 +21,11 @@ use pathfinder_geometry::color::ColorU;
use pathfinder_geometry::outline::Outline; use pathfinder_geometry::outline::Outline;
use pathfinder_geometry::segment::{Segment, SegmentFlags}; use pathfinder_geometry::segment::{Segment, SegmentFlags};
use pathfinder_geometry::stroke::OutlineStrokeToFill; use pathfinder_geometry::stroke::OutlineStrokeToFill;
use pathfinder_renderer::scene::{Paint, PathObject, Scene}; use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{PathObject, Scene};
use std::fmt::{Display, Formatter, Result as FormatResult}; use std::fmt::{Display, Formatter, Result as FormatResult};
use std::mem; use std::mem;
use usvg::{Color as SvgColor, Node, NodeExt, NodeKind, Paint as UsvgPaint}; use usvg::{Color as SvgColor, Node, NodeExt, NodeKind, Opacity, Paint as UsvgPaint};
use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, Transform as UsvgTransform}; use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, Transform as UsvgTransform};
use usvg::{Tree, Visibility}; use usvg::{Tree, Visibility};
@ -114,9 +115,11 @@ impl BuiltSVG {
} }
NodeKind::Path(ref path) if path.visibility == Visibility::Visible => { NodeKind::Path(ref path) if path.visibility == Visibility::Visible => {
if let Some(ref fill) = path.fill { if let Some(ref fill) = path.fill {
let style = self let style = self.scene.push_paint(&Paint::from_svg_paint(
.scene &fill.paint,
.push_paint(&Paint::from_svg_paint(&fill.paint, &mut self.result_flags)); fill.opacity,
&mut self.result_flags,
));
let path = UsvgPathToSegments::new(path.segments.iter().cloned()); let path = UsvgPathToSegments::new(path.segments.iter().cloned());
let path = Transform2DF32PathIter::new(path, &transform); let path = Transform2DF32PathIter::new(path, &transform);
@ -129,6 +132,7 @@ impl BuiltSVG {
if let Some(ref stroke) = path.stroke { if let Some(ref stroke) = path.stroke {
let style = self.scene.push_paint(&Paint::from_svg_paint( let style = self.scene.push_paint(&Paint::from_svg_paint(
&stroke.paint, &stroke.paint,
stroke.opacity,
&mut self.result_flags, &mut self.result_flags,
)); ));
let stroke_width = f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH); let stroke_width = f32::max(stroke.width.value() as f32, HAIRLINE_STROKE_WIDTH);
@ -235,15 +239,17 @@ impl Display for BuildResultFlags {
} }
trait PaintExt { trait PaintExt {
fn from_svg_paint(svg_paint: &UsvgPaint, result_flags: &mut BuildResultFlags) -> Self; fn from_svg_paint(svg_paint: &UsvgPaint, opacity: Opacity, result_flags: &mut BuildResultFlags)
-> Self;
} }
impl PaintExt for Paint { impl PaintExt for Paint {
#[inline] #[inline]
fn from_svg_paint(svg_paint: &UsvgPaint, result_flags: &mut BuildResultFlags) -> Paint { fn from_svg_paint(svg_paint: &UsvgPaint, opacity: Opacity, result_flags: &mut BuildResultFlags)
-> Paint {
Paint { Paint {
color: match *svg_paint { color: match *svg_paint {
UsvgPaint::Color(color) => ColorU::from_svg_color(color), UsvgPaint::Color(color) => ColorU::from_svg_color(color, opacity),
UsvgPaint::Link(_) => { UsvgPaint::Link(_) => {
// TODO(pcwalton) // TODO(pcwalton)
result_flags.insert(BuildResultFlags::UNSUPPORTED_LINK_PAINT); result_flags.insert(BuildResultFlags::UNSUPPORTED_LINK_PAINT);
@ -358,17 +364,17 @@ where
} }
trait ColorUExt { trait ColorUExt {
fn from_svg_color(svg_color: SvgColor) -> Self; fn from_svg_color(svg_color: SvgColor, opacity: Opacity) -> Self;
} }
impl ColorUExt for ColorU { impl ColorUExt for ColorU {
#[inline] #[inline]
fn from_svg_color(svg_color: SvgColor) -> ColorU { fn from_svg_color(svg_color: SvgColor, opacity: Opacity) -> ColorU {
ColorU { ColorU {
r: svg_color.red, r: svg_color.red,
g: svg_color.green, g: svg_color.green,
b: svg_color.blue, b: svg_color.blue,
a: 255, a: (opacity.value() * 255.0).round() as u8,
} }
} }
} }