Fix some transform issues and implement transformed radial gradients in SVG.

In particular, this fixes the usvg-to-Pathfinder transform conversion and the
definition of `Transform2F::row_major()`.

Makes `drops.svg` from the MPVG samples work.
This commit is contained in:
Patrick Walton 2020-04-20 13:03:49 -07:00
parent d8875efd61
commit 6c996981a3
11 changed files with 202 additions and 165 deletions

1
Cargo.lock generated
View File

@ -1764,6 +1764,7 @@ dependencies = [
"pathfinder_content 0.5.0",
"pathfinder_geometry 0.5.1",
"pathfinder_renderer 0.5.0",
"pathfinder_simd 0.5.0",
"usvg 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]

View File

@ -12,6 +12,7 @@ use crate::sorted_vector::SortedVector;
use crate::util;
use pathfinder_color::ColorU;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::util as geometry_util;
use pathfinder_simd::default::F32x2;
@ -22,14 +23,7 @@ use std::mem;
#[derive(Clone, PartialEq, Debug)]
pub struct Gradient {
/// The line this gradient runs along.
///
/// If this is a radial gradient, this is the line that connects the two circles. It may have
/// zero-length in the case of simple radial gradients.
pub line: LineSegment2F,
/// For radial gradients, the radii of the start and endpoints respectively. If this is a
/// linear gradient, this is `None`.
pub radii: Option<F32x2>,
pub geometry: GradientGeometry,
stops: SortedVector<ColorStop>,
}
@ -39,17 +33,43 @@ pub struct ColorStop {
pub color: ColorU,
}
#[derive(Clone, PartialEq, Debug)]
pub enum GradientGeometry {
Linear(LineSegment2F),
Radial {
/// The line that connects the two circles. It may have zero length for simple radial
/// gradients.
line: LineSegment2F,
/// The radii of the two circles. The first value may be zero.
radii: F32x2,
/// Transform from radial gradient space into screen space.
///
/// Like `gradientTransform` in SVG. Note that this is the inverse of Cairo's gradient
/// transform.
transform: Transform2F,
}
}
impl Eq for Gradient {}
impl Hash for Gradient {
fn hash<H>(&self, state: &mut H) where H: Hasher {
util::hash_line_segment(self.line, state);
match self.radii {
None => (0).hash(state),
Some(radii) => {
match self.geometry {
GradientGeometry::Linear(line) => {
(0).hash(state);
util::hash_line_segment(line, state);
}
GradientGeometry::Radial { line, radii, transform } => {
(1).hash(state);
util::hash_line_segment(line, state);
util::hash_f32(radii.x(), state);
util::hash_f32(radii.y(), state);
util::hash_f32(transform.m11(), state);
util::hash_f32(transform.m12(), state);
util::hash_f32(transform.m13(), state);
util::hash_f32(transform.m21(), state);
util::hash_f32(transform.m22(), state);
util::hash_f32(transform.m23(), state);
}
}
self.stops.hash(state);
@ -71,7 +91,7 @@ impl Hash for ColorStop {
impl Gradient {
#[inline]
pub fn linear(line: LineSegment2F) -> Gradient {
Gradient { line, radii: None, stops: SortedVector::new() }
Gradient { geometry: GradientGeometry::Linear(line), stops: SortedVector::new() }
}
#[inline]
@ -81,7 +101,11 @@ impl Gradient {
#[inline]
pub fn radial<L>(line: L, radii: F32x2) -> Gradient where L: RadialGradientLine {
Gradient { line: line.to_line(), radii: Some(radii), stops: SortedVector::new() }
let transform = Transform2F::default();
Gradient {
geometry: GradientGeometry::Radial { line: line.to_line(), radii, transform },
stops: SortedVector::new(),
}
}
#[inline]
@ -95,26 +119,6 @@ impl Gradient {
self.add(ColorStop::new(color, offset))
}
#[inline]
pub fn line(&self) -> LineSegment2F {
self.line
}
#[inline]
pub fn set_line(&mut self, line: LineSegment2F) {
self.line = line
}
#[inline]
pub fn radii(&self) -> Option<F32x2> {
self.radii
}
#[inline]
pub fn set_radii(&mut self, radii: Option<F32x2>) {
self.radii = radii
}
#[inline]
pub fn stops(&self) -> &[ColorStop] {
&self.stops.array
@ -160,6 +164,19 @@ impl Gradient {
pub fn is_fully_transparent(&self) -> bool {
self.stops.array.iter().all(|stop| stop.color.is_fully_transparent())
}
pub fn apply_transform(&mut self, new_transform: Transform2F) {
if new_transform.is_identity() {
return;
}
match self.geometry {
GradientGeometry::Linear(ref mut line) => *line = new_transform * *line,
GradientGeometry::Radial { ref mut transform, .. } => {
*transform = new_transform * *transform
}
}
}
}
impl ColorStop {

View File

@ -205,6 +205,11 @@ impl LineSegment2F {
dx * dx + dy * dy
}
#[inline]
pub fn length(self) -> f32 {
self.square_length().sqrt()
}
#[inline]
pub fn vector(self) -> Vector2F {
self.to() - self.from()

View File

@ -184,10 +184,10 @@ impl Transform2F {
}
#[inline]
pub fn row_major(m11: f32, m12: f32, m21: f32, m22: f32, m31: f32, m32: f32) -> Transform2F {
pub fn row_major(m11: f32, m12: f32, m13: f32, m21: f32, m22: f32, m23: f32) -> Transform2F {
Transform2F {
matrix: Matrix2x2F::row_major(m11, m12, m21, m22),
vector: Vector2F::new(m31, m32),
vector: Vector2F::new(m13, m23),
}
}
@ -242,11 +242,11 @@ impl Transform2F {
self.matrix.m22()
}
#[inline]
pub fn m31(&self) -> f32 {
pub fn m13(&self) -> f32 {
self.vector.x()
}
#[inline]
pub fn m32(&self) -> f32 {
pub fn m23(&self) -> f32 {
self.vector.y()
}

View File

@ -504,8 +504,8 @@ where
f16::from_f32(entry.color_0_transform.m21()),
f16::from_f32(entry.color_0_transform.m12()),
f16::from_f32(entry.color_0_transform.m22()),
f16::from_f32(entry.color_0_transform.m31()),
f16::from_f32(entry.color_0_transform.m32()),
f16::from_f32(entry.color_0_transform.m13()),
f16::from_f32(entry.color_0_transform.m23()),
f16::default(),
f16::default(),
f16::from_f32(base_color.r()),

View File

@ -15,15 +15,16 @@ use crate::scene::{RenderTarget, SceneId};
use hashbrown::HashMap;
use pathfinder_color::ColorU;
use pathfinder_content::effects::{Filter, PatternFilter};
use pathfinder_content::gradient::Gradient;
use pathfinder_content::gradient::{Gradient, GradientGeometry};
use pathfinder_content::pattern::{Pattern, PatternSource};
use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, Vector2I, vec2f, vec2i};
use pathfinder_gpu::TextureSamplingFlags;
use pathfinder_simd::default::F32x2;
use pathfinder_simd::default::{F32x2, F32x4};
use std::f32;
use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
@ -81,10 +82,7 @@ pub enum PaintCompositeOp {
impl Debug for PaintContents {
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
match *self {
PaintContents::Gradient(_) => {
// TODO(pcwalton)
write!(formatter, "(gradient)")
}
PaintContents::Gradient(ref gradient) => gradient.fmt(formatter),
PaintContents::Pattern(ref pattern) => pattern.fmt(formatter),
}
}
@ -185,12 +183,7 @@ impl Paint {
if let Some(ref mut overlay) = self.overlay {
match overlay.contents {
PaintContents::Gradient(ref mut gradient) => {
gradient.set_line(*transform * gradient.line());
if let Some(radii) = gradient.radii() {
gradient.set_radii(Some(radii * transform.extract_scale().0));
}
}
PaintContents::Gradient(ref mut gradient) => gradient.apply_transform(*transform),
PaintContents::Pattern(ref mut pattern) => pattern.apply_transform(*transform),
}
}
@ -300,6 +293,8 @@ pub struct PaintMetadata {
pub struct PaintColorTextureMetadata {
/// The location of the paint.
pub location: TextureLocation,
/// The scale for the page this paint is on.
pub page_scale: Vector2F,
/// The transform to apply to screen coordinates to translate them into UVs.
pub transform: Transform2F,
/// The sampling mode for the texture.
@ -373,13 +368,15 @@ impl Palette {
match overlay.contents {
PaintContents::Gradient(ref gradient) => {
// FIXME(pcwalton): The gradient size might not be big enough. Detect this.
let location = gradient_tile_builder.allocate(allocator, gradient);
PaintColorTextureMetadata {
location: gradient_tile_builder.allocate(allocator, gradient),
location,
page_scale: allocator.page_scale(location.page),
sampling_flags: TextureSamplingFlags::empty(),
filter: match gradient.radii() {
None => PaintFilter::None,
Some(radii) => {
PaintFilter::RadialGradient { line: gradient.line(), radii }
filter: match gradient.geometry {
GradientGeometry::Linear(_) => PaintFilter::None,
GradientGeometry::Radial { line, radii, .. } => {
PaintFilter::RadialGradient { line, radii }
}
},
transform: Transform2F::default(),
@ -424,6 +421,7 @@ impl Palette {
PaintColorTextureMetadata {
location,
page_scale: allocator.page_scale(location.page),
sampling_flags,
filter,
transform: Transform2F::default(),
@ -455,36 +453,27 @@ impl Palette {
metadata but no overlay?")
.contents {
PaintContents::Gradient(Gradient {
line: gradient_line,
radii: None,
geometry: GradientGeometry::Linear(gradient_line),
..
}) => {
// Project gradient line onto (0.0-1.0, v0).
let v0 = texture_rect.to_f32().center().y() * texture_scale.y();
let length_inv = 1.0 / gradient_line.square_length();
let (p0, d) = (gradient_line.from(), gradient_line.vector());
Transform2F {
matrix: Matrix2x2F::row_major(
d.x(), d.y(), 0.0, 0.0).scale(length_inv),
vector: Vector2F::new(-p0.dot(d) * length_inv, v0),
} * render_transform
}
PaintContents::Gradient(Gradient { radii: Some(_), .. }) => {
let texture_origin_uv = rect_to_inset_uv(texture_rect, texture_scale).origin();
let gradient_tile_scale = texture_scale * (GRADIENT_TILE_LENGTH - 1) as
f32;
Transform2F {
matrix: Matrix2x2F::from_scale(gradient_tile_scale),
vector: texture_origin_uv,
} * render_transform
let dp = gradient_line.vector();
let m0 = dp.0.concat_xy_xy(dp.0) / F32x4::splat(gradient_line.square_length());
let m13 = m0.zw() * -gradient_line.from().0;
Transform2F::row_major(m0.x(), m0.y(), m13.x() + m13.y(), 0.0, 0.0, v0)
}
PaintContents::Gradient(Gradient {
geometry: GradientGeometry::Radial { ref transform, .. },
..
}) => transform.inverse(),
PaintContents::Pattern(ref pattern) => {
match pattern.source() {
PatternSource::Image(_) => {
let texture_origin_uv =
rect_to_uv(texture_rect, texture_scale).origin();
Transform2F::from_translation(texture_origin_uv) *
Transform2F::from_scale(texture_scale) *
pattern.transform().inverse() * render_transform
Transform2F::from_scale(texture_scale).translate(texture_origin_uv) *
pattern.transform().inverse()
}
PatternSource::RenderTarget { .. } => {
// FIXME(pcwalton): Only do this in GL, not Metal!
@ -492,11 +481,12 @@ impl Palette {
rect_to_uv(texture_rect, texture_scale).lower_left();
Transform2F::from_translation(texture_origin_uv) *
Transform2F::from_scale(texture_scale * vec2f(1.0, -1.0)) *
pattern.transform().inverse() * render_transform
pattern.transform().inverse()
}
}
}
}
};
color_texture_metadata.transform *= render_transform;
}
// Create texture metadata.
@ -610,8 +600,10 @@ impl PaintMetadata {
match color_metadata.filter {
PaintFilter::None => Filter::None,
PaintFilter::RadialGradient { line, radii } => {
let uv_origin = color_metadata.transform.vector;
Filter::RadialGradient { line, radii, uv_origin }
let uv_rect = rect_to_uv(color_metadata.location.rect,
color_metadata.page_scale).contract(
vec2f(0.0, color_metadata.page_scale.y() * 0.5));
Filter::RadialGradient { line, radii, uv_origin: uv_rect.origin() }
}
PaintFilter::PatternFilter(pattern_filter) => {
Filter::PatternFilter(pattern_filter)
@ -630,10 +622,6 @@ fn rect_to_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
rect.to_f32() * texture_scale
}
fn rect_to_inset_uv(rect: RectI, texture_scale: Vector2F) -> RectF {
rect_to_uv(rect, texture_scale).contract(texture_scale * 0.5)
}
// Gradient allocation
struct GradientTileBuilder {

View File

@ -310,11 +310,7 @@ vec4 filterRadialGradient(vec2 colorTexCoord,
vec2 lineFrom = filterParams0 . xy, lineVector = filterParams0 . zw;
vec2 radii = filterParams1 . xy, uvOrigin = filterParams1 . zw;
fragCoord . y = framebufferSize . y - fragCoord . y;
vec2 dP = fragCoord - lineFrom, dC = lineVector;
vec2 dP = colorTexCoord - lineFrom, dC = lineVector;
float dR = radii . y - radii . x;
float a = dot(dC, dC)- dR * dR;

View File

@ -26,6 +26,8 @@ struct spvDescriptorSetBuffer0
constant int* uCtrl [[id(15)]];
};
constant float3 _1040 = {};
struct main0_out
{
float4 oFragColor [[color(0)]];
@ -41,12 +43,11 @@ struct main0_in
// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()
template<typename Tx, typename Ty>
inline Tx mod(Tx x, Ty y)
Tx mod(Tx x, Ty y)
{
return x - y * floor(x / y);
}
static inline __attribute__((always_inline))
float sampleMask(thread const float& maskAlpha, thread const texture2d<float> maskTexture, thread const sampler maskTextureSmplr, thread const float3& maskTexCoord, thread const int& maskCtrl)
{
if (maskCtrl == 0)
@ -65,14 +66,13 @@ float sampleMask(thread const float& maskAlpha, thread const texture2d<float> ma
return fast::min(maskAlpha, coverage);
}
static inline __attribute__((always_inline))
float4 filterRadialGradient(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTextureSize, thread const float2& fragCoord, thread const float2& framebufferSize, thread const float4& filterParams0, thread const float4& filterParams1)
{
float2 lineFrom = filterParams0.xy;
float2 lineVector = filterParams0.zw;
float2 radii = filterParams1.xy;
float2 uvOrigin = filterParams1.zw;
float2 dP = fragCoord - lineFrom;
float2 dP = colorTexCoord - lineFrom;
float2 dC = lineVector;
float dR = radii.y - radii.x;
float a = dot(dC, dC) - (dR * dR);
@ -102,7 +102,6 @@ float4 filterRadialGradient(thread const float2& colorTexCoord, thread const tex
return color;
}
static inline __attribute__((always_inline))
float4 filterBlur(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTextureSize, thread const float4& filterParams0, thread const float4& filterParams1)
{
float2 srcOffsetScale = filterParams0.xy / colorTextureSize;
@ -127,13 +126,11 @@ float4 filterBlur(thread const float2& colorTexCoord, thread const texture2d<flo
return color / float4(gaussSum);
}
static inline __attribute__((always_inline))
float filterTextSample1Tap(thread const float& offset, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTexCoord)
{
return colorTexture.sample(colorTextureSmplr, (colorTexCoord + float2(offset, 0.0))).x;
}
static inline __attribute__((always_inline))
void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCenter, thread float4& outAlphaRight, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTexCoord, thread const float4& kernel0, thread const float& onePixel)
{
bool wide = kernel0.x > 0.0;
@ -178,19 +175,16 @@ void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCen
outAlphaRight = float4(filterTextSample1Tap(param_10, colorTexture, colorTextureSmplr, param_11), filterTextSample1Tap(param_12, colorTexture, colorTextureSmplr, param_13), filterTextSample1Tap(param_14, colorTexture, colorTextureSmplr, param_15), _294);
}
static inline __attribute__((always_inline))
float filterTextConvolve7Tap(thread const float4& alpha0, thread const float3& alpha1, thread const float4& kernel0)
{
return dot(alpha0, kernel0) + dot(alpha1, kernel0.zyx);
}
static inline __attribute__((always_inline))
float filterTextGammaCorrectChannel(thread const float& bgColor, thread const float& fgColor, thread const texture2d<float> gammaLUT, thread const sampler gammaLUTSmplr)
{
return gammaLUT.sample(gammaLUTSmplr, float2(fgColor, 1.0 - bgColor)).x;
}
static inline __attribute__((always_inline))
float3 filterTextGammaCorrect(thread const float3& bgColor, thread const float3& fgColor, thread const texture2d<float> gammaLUT, thread const sampler gammaLUTSmplr)
{
float param = bgColor.x;
@ -202,7 +196,6 @@ float3 filterTextGammaCorrect(thread const float3& bgColor, thread const float3&
return float3(filterTextGammaCorrectChannel(param, param_1, gammaLUT, gammaLUTSmplr), filterTextGammaCorrectChannel(param_2, param_3, gammaLUT, gammaLUTSmplr), filterTextGammaCorrectChannel(param_4, param_5, gammaLUT, gammaLUTSmplr));
}
static inline __attribute__((always_inline))
float4 filterText(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const texture2d<float> gammaLUT, thread const sampler gammaLUTSmplr, thread const float2& colorTextureSize, thread const float4& filterParams0, thread const float4& filterParams1, thread const float4& filterParams2)
{
float4 kernel0 = filterParams0;
@ -249,20 +242,17 @@ float4 filterText(thread const float2& colorTexCoord, thread const texture2d<flo
return float4(mix(bgColor, fgColor, alpha), 1.0);
}
static inline __attribute__((always_inline))
float4 sampleColor(thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTexCoord)
{
return colorTexture.sample(colorTextureSmplr, colorTexCoord);
}
static inline __attribute__((always_inline))
float4 filterNone(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr)
{
float2 param = colorTexCoord;
return sampleColor(colorTexture, colorTextureSmplr, param);
}
static inline __attribute__((always_inline))
float4 filterColor(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const texture2d<float> gammaLUT, thread const sampler gammaLUTSmplr, thread const float2& colorTextureSize, thread const float2& fragCoord, thread const float2& framebufferSize, thread const float4& filterParams0, thread const float4& filterParams1, thread const float4& filterParams2, thread const int& colorFilter)
{
switch (colorFilter)
@ -299,7 +289,6 @@ float4 filterColor(thread const float2& colorTexCoord, thread const texture2d<fl
return filterNone(param_15, colorTexture, colorTextureSmplr);
}
static inline __attribute__((always_inline))
float4 combineColor0(thread const float4& destColor, thread const float4& srcColor, thread const int& op)
{
switch (op)
@ -316,13 +305,11 @@ float4 combineColor0(thread const float4& destColor, thread const float4& srcCol
return destColor;
}
static inline __attribute__((always_inline))
float3 compositeScreen(thread const float3& destColor, thread const float3& srcColor)
{
return (destColor + srcColor) - (destColor * srcColor);
}
static inline __attribute__((always_inline))
float3 compositeSelect(thread const bool3& cond, thread const float3& ifTrue, thread const float3& ifFalse)
{
float _724;
@ -355,7 +342,6 @@ float3 compositeSelect(thread const bool3& cond, thread const float3& ifTrue, th
return float3(_724, _735, _746);
}
static inline __attribute__((always_inline))
float3 compositeHardLight(thread const float3& destColor, thread const float3& srcColor)
{
float3 param = destColor;
@ -366,7 +352,6 @@ float3 compositeHardLight(thread const float3& destColor, thread const float3& s
return compositeSelect(param_2, param_3, param_4);
}
static inline __attribute__((always_inline))
float3 compositeColorDodge(thread const float3& destColor, thread const float3& srcColor)
{
bool3 destZero = destColor == float3(0.0);
@ -380,7 +365,6 @@ float3 compositeColorDodge(thread const float3& destColor, thread const float3&
return compositeSelect(param_3, param_4, param_5);
}
static inline __attribute__((always_inline))
float3 compositeSoftLight(thread const float3& destColor, thread const float3& srcColor)
{
bool3 param = destColor <= float3(0.25);
@ -394,7 +378,6 @@ float3 compositeSoftLight(thread const float3& destColor, thread const float3& s
return destColor + (((srcColor * 2.0) - float3(1.0)) * factor);
}
static inline __attribute__((always_inline))
float compositeDivide(thread const float& num, thread const float& denom)
{
float _760;
@ -409,7 +392,6 @@ float compositeDivide(thread const float& num, thread const float& denom)
return _760;
}
static inline __attribute__((always_inline))
float3 compositeRGBToHSL(thread const float3& rgb)
{
float v = fast::max(fast::max(rgb.x, rgb.y), rgb.z);
@ -444,7 +426,6 @@ float3 compositeRGBToHSL(thread const float3& rgb)
return float3(h, s, l);
}
static inline __attribute__((always_inline))
float3 compositeHSL(thread const float3& destColor, thread const float3& srcColor, thread const int& op)
{
switch (op)
@ -468,7 +449,6 @@ float3 compositeHSL(thread const float3& destColor, thread const float3& srcColo
}
}
static inline __attribute__((always_inline))
float3 compositeHSLToRGB(thread const float3& hsl)
{
float a = hsl.y * fast::min(hsl.z, 1.0 - hsl.z);
@ -476,7 +456,6 @@ float3 compositeHSLToRGB(thread const float3& hsl)
return hsl.zzz - (fast::clamp(fast::min(ks - float3(3.0), float3(9.0) - ks), float3(-1.0), float3(1.0)) * a);
}
static inline __attribute__((always_inline))
float3 compositeRGB(thread const float3& destColor, thread const float3& srcColor, thread const int& op)
{
switch (op)
@ -554,7 +533,6 @@ float3 compositeRGB(thread const float3& destColor, thread const float3& srcColo
return srcColor;
}
static inline __attribute__((always_inline))
float4 composite(thread const float4& srcColor, thread const texture2d<float> destTexture, thread const sampler destTextureSmplr, thread const float2& destTextureSize, thread const float2& fragCoord, thread const int& op)
{
if (op == 0)
@ -570,7 +548,6 @@ float4 composite(thread const float4& srcColor, thread const texture2d<float> de
return float4(((srcColor.xyz * (srcColor.w * (1.0 - destColor.w))) + (blendedRGB * (srcColor.w * destColor.w))) + (destColor.xyz * (1.0 - srcColor.w)), 1.0);
}
static inline __attribute__((always_inline))
void calculateColor(thread const int& ctrl, thread texture2d<float> uMaskTexture0, thread const sampler uMaskTexture0Smplr, thread float3& vMaskTexCoord0, thread texture2d<float> uMaskTexture1, thread const sampler uMaskTexture1Smplr, thread float3& vMaskTexCoord1, thread float4& vBaseColor, thread float2& vColorTexCoord0, thread texture2d<float> uColorTexture0, thread const sampler uColorTexture0Smplr, thread texture2d<float> uGammaLUT, thread const sampler uGammaLUTSmplr, thread float2 uColorTexture0Size, thread float4& gl_FragCoord, thread float2 uFramebufferSize, thread float4 uFilterParams0, thread float4 uFilterParams1, thread float4 uFilterParams2, thread texture2d<float> uDestTexture, thread const sampler uDestTextureSmplr, thread float4& oFragColor)
{
int maskCtrl0 = (ctrl >> 0) & 3;

View File

@ -294,7 +294,7 @@ vec4 filterText(vec2 colorTexCoord,
// opposite sign
//
// | x y z w
// --------------+-------------------------------------------
// --------------+-----------------------------------------------------
// filterParams0 | lineFrom.x lineFrom.y lineVector.x lineVector.y
// filterParams1 | radii.x radii.y uvOrigin.x uvOrigin.y
// filterParams2 | - - - -
@ -308,11 +308,7 @@ vec4 filterRadialGradient(vec2 colorTexCoord,
vec2 lineFrom = filterParams0.xy, lineVector = filterParams0.zw;
vec2 radii = filterParams1.xy, uvOrigin = filterParams1.zw;
#ifndef PF_ORIGIN_UPPER_LEFT
fragCoord.y = framebufferSize.y - fragCoord.y;
#endif
vec2 dP = fragCoord - lineFrom, dC = lineVector;
vec2 dP = colorTexCoord - lineFrom, dC = lineVector;
float dR = radii.y - radii.x;
float a = dot(dC, dC) - dR * dR;

View File

@ -29,3 +29,7 @@ version = "0.5"
[dependencies.pathfinder_renderer]
path = "../renderer"
version = "0.5"
[dependencies.pathfinder_simd]
path = "../simd"
version = "0.5"

View File

@ -16,6 +16,7 @@ extern crate bitflags;
use hashbrown::HashMap;
use pathfinder_color::ColorU;
use pathfinder_content::fill::FillRule;
use pathfinder_content::gradient::{ColorStop, Gradient};
use pathfinder_content::outline::Outline;
use pathfinder_content::segment::{Segment, SegmentFlags};
use pathfinder_content::stroke::{LineCap, LineJoin, OutlineStrokeToFill, StrokeStyle};
@ -26,11 +27,12 @@ use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_renderer::paint::Paint;
use pathfinder_renderer::scene::{ClipPath, ClipPathId, DrawPath, Scene};
use pathfinder_simd::default::F32x2;
use std::fmt::{Display, Formatter, Result as FormatResult};
use usvg::{Color as SvgColor, FillRule as UsvgFillRule, LineCap as UsvgLineCap};
use usvg::{BaseGradient, 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};
use usvg::{PathSegment as UsvgPathSegment, Rect as UsvgRect, SpreadMethod, Stop};
use usvg::{Transform as UsvgTransform, Tree, Visibility};
const HAIRLINE_STROKE_WIDTH: f32 = 0.0333;
@ -38,24 +40,26 @@ pub struct BuiltSVG {
pub scene: Scene,
pub result_flags: BuildResultFlags,
pub clip_paths: HashMap<String, ClipPathId>,
gradients: HashMap<String, GradientInfo>,
}
bitflags! {
// NB: If you change this, make sure to update the `Display`
// implementation as well.
pub struct BuildResultFlags: u16 {
const UNSUPPORTED_FILTER_NODE = 0x0001;
const UNSUPPORTED_IMAGE_NODE = 0x0002;
const UNSUPPORTED_LINEAR_GRADIENT_NODE = 0x0004;
const UNSUPPORTED_MASK_NODE = 0x0008;
const UNSUPPORTED_PATTERN_NODE = 0x0010;
const UNSUPPORTED_RADIAL_GRADIENT_NODE = 0x0020;
const UNSUPPORTED_NESTED_SVG_NODE = 0x0040;
const UNSUPPORTED_MULTIPLE_CLIP_PATHS = 0x0080;
const UNSUPPORTED_LINK_PAINT = 0x0100;
const UNSUPPORTED_FILTER_ATTR = 0x0200;
const UNSUPPORTED_MASK_ATTR = 0x0400;
const UNSUPPORTED_STROKE_DASH = 0x0800;
const UNSUPPORTED_FILTER_NODE = 0x0001;
const UNSUPPORTED_IMAGE_NODE = 0x0002;
const UNSUPPORTED_LINEAR_GRADIENT_NODE = 0x0004;
const UNSUPPORTED_MASK_NODE = 0x0008;
const UNSUPPORTED_PATTERN_NODE = 0x0010;
const UNSUPPORTED_RADIAL_GRADIENT_NODE = 0x0020;
const UNSUPPORTED_NESTED_SVG_NODE = 0x0040;
const UNSUPPORTED_MULTIPLE_CLIP_PATHS = 0x0080;
const UNSUPPORTED_LINK_PAINT = 0x0100;
const UNSUPPORTED_FILTER_ATTR = 0x0200;
const UNSUPPORTED_MASK_ATTR = 0x0400;
const UNSUPPORTED_STROKE_DASH = 0x0800;
const UNSUPPORTED_GRADIENT_SPREAD_METHOD = 0x1000;
}
}
@ -74,6 +78,7 @@ impl BuiltSVG {
scene,
result_flags: BuildResultFlags::empty(),
clip_paths: HashMap::new(),
gradients: HashMap::new(),
};
let root = &tree.root();
@ -197,6 +202,23 @@ impl BuiltSVG {
self.process_node(&kid, &state, clip_outline);
}
}
NodeKind::LinearGradient(ref svg_linear_gradient) => {
let from = vec2f(svg_linear_gradient.x1 as f32, svg_linear_gradient.y1 as f32);
let to = vec2f(svg_linear_gradient.x2 as f32, svg_linear_gradient.y2 as f32);
let gradient = Gradient::linear_from_points(from, to);
self.add_gradient(gradient,
svg_linear_gradient.id.clone(),
&svg_linear_gradient.base)
}
NodeKind::RadialGradient(ref svg_radial_gradient) => {
let from = vec2f(svg_radial_gradient.fx as f32, svg_radial_gradient.fy as f32);
let to = vec2f(svg_radial_gradient.cx as f32, svg_radial_gradient.cy as f32);
let radii = F32x2::new(0.0, svg_radial_gradient.r.value() as f32);
let gradient = Gradient::radial(LineSegment2F::new(from, to), radii);
self.add_gradient(gradient,
svg_radial_gradient.id.clone(),
&svg_radial_gradient.base)
}
NodeKind::Filter(..) => {
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_FILTER_NODE);
@ -205,10 +227,6 @@ impl BuiltSVG {
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_IMAGE_NODE);
}
NodeKind::LinearGradient(..) => {
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_LINEAR_GRADIENT_NODE);
}
NodeKind::Mask(..) => {
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_MASK_NODE);
@ -217,10 +235,6 @@ impl BuiltSVG {
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_PATTERN_NODE);
}
NodeKind::RadialGradient(..) => {
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_RADIAL_GRADIENT_NODE);
}
NodeKind::Svg(..) => {
self.result_flags
.insert(BuildResultFlags::UNSUPPORTED_NESTED_SVG_NODE);
@ -228,6 +242,24 @@ impl BuiltSVG {
}
}
fn add_gradient(&mut self,
mut gradient: Gradient,
id: String,
usvg_base_gradient: &BaseGradient) {
for stop in &usvg_base_gradient.stops {
gradient.add(ColorStop::from_usvg_stop(stop));
}
if usvg_base_gradient.spread_method != SpreadMethod::Pad {
self.result_flags.insert(BuildResultFlags::UNSUPPORTED_GRADIENT_SPREAD_METHOD);
}
let transform = usvg_transform_to_transform_2d(&usvg_base_gradient.transform);
// TODO(pcwalton): What should we do with `gradientUnits`?
self.gradients.insert(id, GradientInfo { gradient, transform });
}
fn push_draw_path(&mut self,
mut outline: Outline,
name: String,
@ -239,6 +271,7 @@ impl BuiltSVG {
let paint = Paint::from_svg_paint(paint,
&state.transform,
opacity,
&self.gradients,
&mut self.result_flags);
let style = self.scene.push_paint(&paint);
let fill_rule = FillRule::from_usvg_fill_rule(fill_rule);
@ -293,6 +326,7 @@ trait PaintExt {
fn from_svg_paint(svg_paint: &UsvgPaint,
transform: &Transform2F,
opacity: Opacity,
gradients: &HashMap<String, GradientInfo>,
result_flags: &mut BuildResultFlags)
-> Self;
}
@ -302,18 +336,26 @@ impl PaintExt for Paint {
fn from_svg_paint(svg_paint: &UsvgPaint,
transform: &Transform2F,
opacity: Opacity,
gradients: &HashMap<String, GradientInfo>,
result_flags: &mut BuildResultFlags)
-> Paint {
// TODO(pcwalton): Support gradients.
let mut paint = Paint::from_color(match *svg_paint {
UsvgPaint::Color(color) => ColorU::from_svg_color(color),
UsvgPaint::Link(_) => {
// TODO(pcwalton)
result_flags.insert(BuildResultFlags::UNSUPPORTED_LINK_PAINT);
ColorU::black()
let mut paint;
match *svg_paint {
UsvgPaint::Color(color) => paint = Paint::from_color(ColorU::from_svg_color(color)),
UsvgPaint::Link(ref id) => {
match gradients.get(id) {
Some(ref gradient_info) => {
paint = Paint::from_gradient(gradient_info.gradient.clone());
paint.apply_transform(&(*transform * gradient_info.transform));
}
None => {
// TODO(pcwalton)
result_flags.insert(BuildResultFlags::UNSUPPORTED_LINK_PAINT);
paint = Paint::from_color(ColorU::black());
}
}
}
});
paint.apply_transform(transform);
}
let mut base_color = paint.base_color().to_f32();
base_color.set_a(base_color.a() * opacity.value() as f32);
@ -329,14 +371,8 @@ fn usvg_rect_to_euclid_rect(rect: &UsvgRect) -> RectF {
}
fn usvg_transform_to_transform_2d(transform: &UsvgTransform) -> Transform2F {
Transform2F::row_major(
transform.a as f32,
transform.b as f32,
transform.c as f32,
transform.d as f32,
transform.e as f32,
transform.f as f32,
)
Transform2F::row_major(transform.a as f32, transform.c as f32, transform.e as f32,
transform.b as f32, transform.d as f32, transform.f as f32)
}
struct UsvgPathToSegments<I>
@ -477,6 +513,18 @@ impl FillRuleExt for FillRule {
}
}
trait ColorStopExt {
fn from_usvg_stop(usvg_stop: &Stop) -> Self;
}
impl ColorStopExt for ColorStop {
fn from_usvg_stop(usvg_stop: &Stop) -> ColorStop {
let mut color = ColorU::from_svg_color(usvg_stop.color);
color.a = (usvg_stop.opacity.value() * 255.0) as u8;
ColorStop::new(color, usvg_stop.offset.value() as f32)
}
}
#[derive(Clone)]
struct State {
// Where paths are being appended to.
@ -503,3 +551,8 @@ enum PathDestination {
Defs,
Clip,
}
struct GradientInfo {
gradient: Gradient,
transform: Transform2F,
}