From 2b69ff778f7b8966f7b336a7d71f1a5d7fc7bb9a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 13 Jul 2020 12:36:45 -0700 Subject: [PATCH] Implement gradient wrap/spread modes in SVG. Closes #386. --- content/src/gradient.rs | 14 +++++++++++++- renderer/src/paint.rs | 16 ++++++++++++---- svg/src/lib.rs | 26 ++++++++++++++++++++------ 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/content/src/gradient.rs b/content/src/gradient.rs index 3deb8ba9..05392e13 100644 --- a/content/src/gradient.rs +++ b/content/src/gradient.rs @@ -24,6 +24,7 @@ use std::mem; pub struct Gradient { pub geometry: GradientGeometry, stops: Vec, + pub wrap: GradientWrap, } #[derive(Clone, Copy, PartialEq, Debug)] @@ -49,6 +50,12 @@ pub enum GradientGeometry { } } +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum GradientWrap { + Clamp, + Repeat, +} + impl Eq for Gradient {} impl Hash for Gradient { @@ -90,7 +97,11 @@ impl Hash for ColorStop { impl Gradient { #[inline] pub fn linear(line: LineSegment2F) -> Gradient { - Gradient { geometry: GradientGeometry::Linear(line), stops: Vec::new() } + Gradient { + geometry: GradientGeometry::Linear(line), + stops: Vec::new(), + wrap: GradientWrap::Clamp, + } } #[inline] @@ -104,6 +115,7 @@ impl Gradient { Gradient { geometry: GradientGeometry::Radial { line: line.to_line(), radii, transform }, stops: Vec::new(), + wrap: GradientWrap::Clamp, } } diff --git a/renderer/src/paint.rs b/renderer/src/paint.rs index 25721a07..878f9caf 100644 --- a/renderer/src/paint.rs +++ b/renderer/src/paint.rs @@ -9,13 +9,13 @@ // except according to those terms. use crate::allocator::{AllocationMode, TextureAllocator}; -use crate::gpu_data::{ColorCombineMode, RenderCommand, TextureLocation, TextureMetadataEntry, TexturePageDescriptor}; -use crate::gpu_data::{TexturePageId, TileBatchTexture}; +use crate::gpu_data::{ColorCombineMode, RenderCommand, TextureLocation, TextureMetadataEntry}; +use crate::gpu_data::{TexturePageDescriptor, TexturePageId, TileBatchTexture}; use crate::scene::{RenderTarget, SceneId}; use hashbrown::HashMap; use pathfinder_color::ColorU; use pathfinder_content::effects::{BlendMode, Filter, PatternFilter}; -use pathfinder_content::gradient::{Gradient, GradientGeometry}; +use pathfinder_content::gradient::{Gradient, GradientGeometry, GradientWrap}; use pathfinder_content::pattern::{Pattern, PatternSource}; use pathfinder_content::render_target::RenderTargetId; use pathfinder_geometry::line_segment::LineSegment2F; @@ -368,12 +368,20 @@ impl Palette { let color_texture_metadata = paint.overlay.as_ref().map(|overlay| { match overlay.contents { PaintContents::Gradient(ref gradient) => { + let mut sampling_flags = TextureSamplingFlags::empty(); + match gradient.wrap { + GradientWrap::Repeat => { + sampling_flags.insert(TextureSamplingFlags::REPEAT_U); + } + GradientWrap::Clamp => {} + } + // FIXME(pcwalton): The gradient size might not be big enough. Detect this. let location = gradient_tile_builder.allocate(allocator, gradient); PaintColorTextureMetadata { location, page_scale: allocator.page_scale(location.page), - sampling_flags: TextureSamplingFlags::empty(), + sampling_flags, filter: match gradient.geometry { GradientGeometry::Linear(_) => PaintFilter::None, GradientGeometry::Radial { line, radii, .. } => { diff --git a/svg/src/lib.rs b/svg/src/lib.rs index 79724916..0c223844 100644 --- a/svg/src/lib.rs +++ b/svg/src/lib.rs @@ -17,7 +17,7 @@ use hashbrown::HashMap; use pathfinder_color::ColorU; use pathfinder_content::dash::OutlineDash; use pathfinder_content::fill::FillRule; -use pathfinder_content::gradient::{ColorStop, Gradient}; +use pathfinder_content::gradient::{ColorStop, Gradient, GradientWrap}; use pathfinder_content::outline::Outline; use pathfinder_content::segment::{Segment, SegmentFlags}; use pathfinder_content::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle}; @@ -56,7 +56,6 @@ bitflags! { const UNSUPPORTED_LINK_PAINT = 0x0020; const UNSUPPORTED_FILTER_ATTR = 0x0040; const UNSUPPORTED_MASK_ATTR = 0x0080; - const UNSUPPORTED_GRADIENT_SPREAD_METHOD = 0x0100; } } @@ -237,11 +236,27 @@ impl SVGScene { id: String, usvg_base_gradient: &BaseGradient) { for stop in &usvg_base_gradient.stops { - gradient.add(ColorStop::from_usvg_stop(stop)); + let mut stop = ColorStop::from_usvg_stop(stop); + if usvg_base_gradient.spread_method == SpreadMethod::Reflect { + stop.offset *= 0.5; + } + gradient.add(stop); } - if usvg_base_gradient.spread_method != SpreadMethod::Pad { - self.result_flags.insert(BuildResultFlags::UNSUPPORTED_GRADIENT_SPREAD_METHOD); + // Reflect gradient if necessary. + if usvg_base_gradient.spread_method == SpreadMethod::Reflect { + for stop in &usvg_base_gradient.stops { + let mut stop = ColorStop::from_usvg_stop(stop); + if usvg_base_gradient.spread_method == SpreadMethod::Reflect { + stop.offset = 1.0 - stop.offset * 0.5; + } + gradient.add(stop); + } + } + + match usvg_base_gradient.spread_method { + SpreadMethod::Pad => {} + SpreadMethod::Reflect | SpreadMethod::Repeat => gradient.wrap = GradientWrap::Repeat, } let transform = usvg_transform_to_transform_2d(&usvg_base_gradient.transform); @@ -304,7 +319,6 @@ impl Display for BuildResultFlags { "non-color paint", "filter attribute", "mask attribute", - "gradient spread method", ]; } }