Add an API for gradients to the canvas frontend, not implemented yet
This commit is contained in:
parent
9a2e4f0b6d
commit
706b6dbd1d
|
@ -358,13 +358,15 @@ pub unsafe extern "C" fn PFCanvasSetTextAlign(canvas: PFCanvasRef, new_text_alig
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn PFCanvasSetFillStyle(canvas: PFCanvasRef, fill_style: PFFillStyleRef) {
|
pub unsafe extern "C" fn PFCanvasSetFillStyle(canvas: PFCanvasRef, fill_style: PFFillStyleRef) {
|
||||||
(*canvas).set_fill_style(*fill_style)
|
// FIXME(pcwalton): Avoid the copy?
|
||||||
|
(*canvas).set_fill_style((*fill_style).clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "C" fn PFCanvasSetStrokeStyle(canvas: PFCanvasRef,
|
pub unsafe extern "C" fn PFCanvasSetStrokeStyle(canvas: PFCanvasRef,
|
||||||
stroke_style: PFFillStyleRef) {
|
stroke_style: PFFillStyleRef) {
|
||||||
(*canvas).set_stroke_style(*stroke_style)
|
// FIXME(pcwalton): Avoid the copy?
|
||||||
|
(*canvas).set_stroke_style((*stroke_style).clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function automatically destroys the path. If you wish to use the path again, clone it
|
/// This function automatically destroys the path. If you wish to use the path again, clone it
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_content::dash::OutlineDash;
|
use pathfinder_content::dash::OutlineDash;
|
||||||
|
use pathfinder_content::gradient::Gradient;
|
||||||
use pathfinder_content::outline::{ArcDirection, Contour, Outline};
|
use pathfinder_content::outline::{ArcDirection, Contour, Outline};
|
||||||
use pathfinder_content::stroke::{LineCap, LineJoin as StrokeLineJoin};
|
use pathfinder_content::stroke::{LineCap, LineJoin as StrokeLineJoin};
|
||||||
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
|
use pathfinder_content::stroke::{OutlineStrokeToFill, StrokeStyle};
|
||||||
|
@ -21,6 +22,7 @@ use pathfinder_geometry::rect::RectF;
|
||||||
use pathfinder_geometry::transform2d::Transform2F;
|
use pathfinder_geometry::transform2d::Transform2F;
|
||||||
use pathfinder_renderer::paint::{Paint, PaintId};
|
use pathfinder_renderer::paint::{Paint, PaintId};
|
||||||
use pathfinder_renderer::scene::{PathObject, Scene};
|
use pathfinder_renderer::scene::{PathObject, Scene};
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -129,19 +131,19 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_fill_style(&mut self, new_fill_style: FillStyle) {
|
pub fn set_fill_style(&mut self, new_fill_style: FillStyle) {
|
||||||
self.current_state.fill_paint = new_fill_style.to_paint();
|
self.current_state.fill_paint = new_fill_style.into_paint();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_stroke_style(&mut self, new_stroke_style: FillStyle) {
|
pub fn set_stroke_style(&mut self, new_stroke_style: FillStyle) {
|
||||||
self.current_state.stroke_paint = new_stroke_style.to_paint();
|
self.current_state.stroke_paint = new_stroke_style.into_paint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shadows
|
// Shadows
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_shadow_color(&mut self, new_shadow_color: ColorU) {
|
pub fn set_shadow_color(&mut self, new_shadow_color: ColorU) {
|
||||||
self.current_state.shadow_paint = Paint { color: new_shadow_color };
|
self.current_state.shadow_paint = Paint::Color(new_shadow_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -156,7 +158,7 @@ impl CanvasRenderingContext2D {
|
||||||
let mut outline = path.into_outline();
|
let mut outline = path.into_outline();
|
||||||
outline.transform(&self.current_state.transform);
|
outline.transform(&self.current_state.transform);
|
||||||
|
|
||||||
let paint = self.current_state.resolve_paint(self.current_state.fill_paint);
|
let paint = self.current_state.resolve_paint(&self.current_state.fill_paint);
|
||||||
let paint_id = self.scene.push_paint(&paint);
|
let paint_id = self.scene.push_paint(&paint);
|
||||||
|
|
||||||
self.push_path(outline, paint_id);
|
self.push_path(outline, paint_id);
|
||||||
|
@ -164,7 +166,7 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stroke_path(&mut self, path: Path2D) {
|
pub fn stroke_path(&mut self, path: Path2D) {
|
||||||
let paint = self.current_state.resolve_paint(self.current_state.stroke_paint);
|
let paint = self.current_state.resolve_paint(&self.current_state.stroke_paint);
|
||||||
let paint_id = self.scene.push_paint(&paint);
|
let paint_id = self.scene.push_paint(&paint);
|
||||||
|
|
||||||
let mut stroke_style = self.current_state.resolve_stroke_style();
|
let mut stroke_style = self.current_state.resolve_stroke_style();
|
||||||
|
@ -195,7 +197,7 @@ impl CanvasRenderingContext2D {
|
||||||
|
|
||||||
fn push_path(&mut self, outline: Outline, paint_id: PaintId) {
|
fn push_path(&mut self, outline: Outline, paint_id: PaintId) {
|
||||||
if !self.current_state.shadow_paint.is_fully_transparent() {
|
if !self.current_state.shadow_paint.is_fully_transparent() {
|
||||||
let paint = self.current_state.resolve_paint(self.current_state.shadow_paint);
|
let paint = self.current_state.resolve_paint(&self.current_state.shadow_paint);
|
||||||
let paint_id = self.scene.push_paint(&paint);
|
let paint_id = self.scene.push_paint(&paint);
|
||||||
|
|
||||||
let mut outline = outline.clone();
|
let mut outline = outline.clone();
|
||||||
|
@ -281,18 +283,23 @@ impl State {
|
||||||
miter_limit: 10.0,
|
miter_limit: 10.0,
|
||||||
line_dash: vec![],
|
line_dash: vec![],
|
||||||
line_dash_offset: 0.0,
|
line_dash_offset: 0.0,
|
||||||
fill_paint: Paint { color: ColorU::black() },
|
fill_paint: Paint::black(),
|
||||||
stroke_paint: Paint { color: ColorU::black() },
|
stroke_paint: Paint::black(),
|
||||||
shadow_paint: Paint { color: ColorU::transparent_black() },
|
shadow_paint: Paint::transparent_black(),
|
||||||
shadow_offset: Vector2F::default(),
|
shadow_offset: Vector2F::default(),
|
||||||
text_align: TextAlign::Left,
|
text_align: TextAlign::Left,
|
||||||
global_alpha: 1.0,
|
global_alpha: 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_paint(&self, mut paint: Paint) -> Paint {
|
fn resolve_paint<'a>(&self, paint: &'a Paint) -> Cow<'a, Paint> {
|
||||||
paint.color.a = (paint.color.a as f32 * self.global_alpha).round() as u8;
|
if self.global_alpha == 1.0 {
|
||||||
paint
|
return Cow::Borrowed(paint);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paint = (*paint).clone();
|
||||||
|
paint.set_opacity(self.global_alpha);
|
||||||
|
Cow::Owned(paint)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_stroke_style(&self) -> StrokeStyle {
|
fn resolve_stroke_style(&self) -> StrokeStyle {
|
||||||
|
@ -415,16 +422,18 @@ impl Path2D {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pcwalton): Gradients.
|
#[derive(Clone)]
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum FillStyle {
|
pub enum FillStyle {
|
||||||
Color(ColorU),
|
Color(ColorU),
|
||||||
|
Gradient(Gradient),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FillStyle {
|
impl FillStyle {
|
||||||
#[inline]
|
fn into_paint(self) -> Paint {
|
||||||
fn to_paint(&self) -> Paint {
|
match self {
|
||||||
match *self { FillStyle::Color(color) => Paint { color } }
|
FillStyle::Color(color) => Paint::Color(color),
|
||||||
|
FillStyle::Gradient(gradient) => Paint::Gradient(gradient),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,11 @@ impl ColorU {
|
||||||
ColorF(color * F32x4::splat(1.0 / 255.0))
|
ColorF(color * F32x4::splat(1.0 / 255.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_opaque(&self) -> bool {
|
||||||
|
self.a == !0
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_fully_transparent(&self) -> bool {
|
pub fn is_fully_transparent(&self) -> bool {
|
||||||
self.a == 0
|
self.a == 0
|
||||||
|
|
|
@ -10,27 +10,97 @@
|
||||||
|
|
||||||
use crate::sorted_vector::SortedVector;
|
use crate::sorted_vector::SortedVector;
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use std::cmp::PartialOrd;
|
use pathfinder_geometry::line_segment::LineSegment2F;
|
||||||
|
use pathfinder_simd::default::F32x4;
|
||||||
|
use std::cmp::{self, Ordering, PartialOrd};
|
||||||
|
use std::convert;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct Gradient {
|
pub struct Gradient {
|
||||||
|
line: LineSegment2F,
|
||||||
stops: SortedVector<ColorStop>,
|
stops: SortedVector<ColorStop>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
|
||||||
pub struct ColorStop {
|
pub struct ColorStop {
|
||||||
pub offset: f32,
|
|
||||||
pub color: ColorU,
|
pub color: ColorU,
|
||||||
|
pub offset: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Gradient {}
|
||||||
|
|
||||||
|
impl Hash for Gradient {
|
||||||
|
fn hash<H>(&self, state: &mut H) where H: Hasher {
|
||||||
|
unsafe {
|
||||||
|
let data: [u32; 4] = mem::transmute::<F32x4, [u32; 4]>(self.line.0);
|
||||||
|
data.hash(state);
|
||||||
|
self.stops.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for ColorStop {}
|
||||||
|
|
||||||
|
impl Hash for ColorStop {
|
||||||
|
fn hash<H>(&self, state: &mut H) where H: Hasher {
|
||||||
|
unsafe {
|
||||||
|
self.color.hash(state);
|
||||||
|
let offset = mem::transmute::<f32, u32>(self.offset);
|
||||||
|
offset.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gradient {
|
impl Gradient {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(line: LineSegment2F) -> Gradient {
|
||||||
|
Gradient { line, stops: SortedVector::new() }
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn add_color_stop(&mut self, stop: ColorStop) {
|
pub fn add_color_stop(&mut self, stop: ColorStop) {
|
||||||
self.stops.push(stop);
|
self.stops.push(stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn line(&self) -> LineSegment2F {
|
||||||
|
self.line
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn stops(&self) -> &[ColorStop] {
|
pub fn stops(&self) -> &[ColorStop] {
|
||||||
&self.stops.array
|
&self.stops.array
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sample(&self, t: f32) -> ColorU {
|
||||||
|
if self.stops.is_empty() {
|
||||||
|
return ColorU::transparent_black();
|
||||||
|
}
|
||||||
|
|
||||||
|
let lower_index = self.stops.binary_search_by(|stop| {
|
||||||
|
stop.offset.partial_cmp(&t).unwrap_or(Ordering::Less)
|
||||||
|
}).unwrap_or_else(convert::identity);
|
||||||
|
let upper_index = cmp::min(lower_index + 1, self.stops.len() - 1);
|
||||||
|
|
||||||
|
let lower_stop = &self.stops.array[lower_index];
|
||||||
|
let upper_stop = &self.stops.array[upper_index];
|
||||||
|
|
||||||
|
let denom = upper_stop.offset - lower_stop.offset;
|
||||||
|
if denom == 0.0 {
|
||||||
|
return lower_stop.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
lower_stop.color
|
||||||
|
.to_f32()
|
||||||
|
.lerp(upper_stop.color.to_f32(), (t - lower_stop.offset) / denom)
|
||||||
|
.to_u8()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_opacity(&mut self, alpha: f32) {
|
||||||
|
for stop in &mut self.stops.array {
|
||||||
|
stop.color.a = (stop.color.a as f32 * alpha).round() as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::convert;
|
use std::convert;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct SortedVector<T>
|
pub struct SortedVector<T>
|
||||||
where
|
where
|
||||||
T: PartialOrd,
|
T: PartialOrd,
|
||||||
|
@ -32,7 +32,7 @@ where
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn push(&mut self, value: T) {
|
pub fn push(&mut self, value: T) {
|
||||||
let index = self.array.binary_search_by(|other| {
|
let index = self.binary_search_by(|other| {
|
||||||
other.partial_cmp(&value).unwrap_or(Ordering::Less)
|
other.partial_cmp(&value).unwrap_or(Ordering::Less)
|
||||||
}).unwrap_or_else(convert::identity);
|
}).unwrap_or_else(convert::identity);
|
||||||
self.array.insert(index, value);
|
self.array.insert(index, value);
|
||||||
|
@ -58,6 +58,17 @@ where
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.array.is_empty()
|
self.array.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.array.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result<usize, usize>
|
||||||
|
where F: FnMut(&'a T) -> Ordering {
|
||||||
|
self.array.binary_search_by(f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -9,10 +9,11 @@
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use pathfinder_content::segment::SegmentKind;
|
use pathfinder_content::segment::SegmentKind;
|
||||||
|
use pathfinder_renderer::paint::Paint;
|
||||||
use pathfinder_renderer::scene::Scene;
|
use pathfinder_renderer::scene::Scene;
|
||||||
use pathfinder_geometry::vector::Vector2F;
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
use std::io::{self, Write};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
|
||||||
mod pdf;
|
mod pdf;
|
||||||
use pdf::Pdf;
|
use pdf::Pdf;
|
||||||
|
@ -57,11 +58,7 @@ fn export_svg<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
|
||||||
if !name.is_empty() {
|
if !name.is_empty() {
|
||||||
write!(writer, " id=\"{}\"", name)?;
|
write!(writer, " id=\"{}\"", name)?;
|
||||||
}
|
}
|
||||||
writeln!(
|
writeln!(writer, " fill=\"{:?}\" d=\"{:?}\" />", paint, outline)?;
|
||||||
writer,
|
|
||||||
" fill=\"{:?}\" d=\"{:?}\" />",
|
|
||||||
paint.color, outline
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
writeln!(writer, "</svg>")?;
|
writeln!(writer, "</svg>")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -79,7 +76,12 @@ fn export_pdf<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (paint, outline, _) in scene.paths() {
|
for (paint, outline, _) in scene.paths() {
|
||||||
pdf.set_fill_color(paint.color);
|
match paint {
|
||||||
|
Paint::Color(color) => pdf.set_fill_color(*color),
|
||||||
|
Paint::Gradient(_) => {
|
||||||
|
// TODO(pcwalton): Gradients.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for contour in outline.contours() {
|
for contour in outline.contours() {
|
||||||
for (segment_index, segment) in contour.iter().enumerate() {
|
for (segment_index, segment) in contour.iter().enumerate() {
|
||||||
|
@ -140,7 +142,7 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
|
||||||
} else {
|
} else {
|
||||||
writeln!(writer, "newpath")?;
|
writeln!(writer, "newpath")?;
|
||||||
}
|
}
|
||||||
let color = paint.color.to_f32();
|
|
||||||
for contour in outline.contours() {
|
for contour in outline.contours() {
|
||||||
for (segment_index, segment) in contour.iter().enumerate() {
|
for (segment_index, segment) in contour.iter().enumerate() {
|
||||||
if segment_index == 0 {
|
if segment_index == 0 {
|
||||||
|
@ -174,7 +176,16 @@ fn export_ps<W: Write>(scene: &Scene, writer: &mut W) -> io::Result<()> {
|
||||||
writeln!(writer, "closepath")?;
|
writeln!(writer, "closepath")?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
writeln!(writer, "{} {} {} setrgbcolor", color.r(), color.g(), color.b())?;
|
|
||||||
|
match paint {
|
||||||
|
Paint::Color(color) => {
|
||||||
|
writeln!(writer, "{} {} {} setrgbcolor", color.r, color.g, color.b)?;
|
||||||
|
}
|
||||||
|
Paint::Gradient(_) => {
|
||||||
|
// TODO(pcwalton): Gradients.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writeln!(writer, "fill")?;
|
writeln!(writer, "fill")?;
|
||||||
}
|
}
|
||||||
writeln!(writer, "showpage")?;
|
writeln!(writer, "showpage")?;
|
||||||
|
|
|
@ -10,12 +10,14 @@
|
||||||
|
|
||||||
use crate::allocator::{TextureAllocator, TextureLocation};
|
use crate::allocator::{TextureAllocator, TextureLocation};
|
||||||
use crate::gpu_data::PaintData;
|
use crate::gpu_data::PaintData;
|
||||||
use crate::scene::Scene;
|
use hashbrown::HashMap;
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
|
use pathfinder_content::gradient::Gradient;
|
||||||
use pathfinder_geometry::rect::RectI;
|
use pathfinder_geometry::rect::RectI;
|
||||||
use pathfinder_geometry::transform2d::{Matrix2x2I, Transform2I};
|
use pathfinder_geometry::transform2d::{Matrix2x2I, Transform2I};
|
||||||
use pathfinder_geometry::vector::Vector2I;
|
use pathfinder_geometry::vector::Vector2I;
|
||||||
use pathfinder_simd::default::I32x4;
|
use pathfinder_simd::default::I32x4;
|
||||||
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
const PAINT_TEXTURE_LENGTH: u32 = 1024;
|
const PAINT_TEXTURE_LENGTH: u32 = 1024;
|
||||||
const PAINT_TEXTURE_SCALE: u32 = 65536 / PAINT_TEXTURE_LENGTH;
|
const PAINT_TEXTURE_SCALE: u32 = 65536 / PAINT_TEXTURE_LENGTH;
|
||||||
|
@ -23,23 +25,81 @@ const PAINT_TEXTURE_SCALE: u32 = 65536 / PAINT_TEXTURE_LENGTH;
|
||||||
const SOLID_COLOR_TILE_LENGTH: u32 = 16;
|
const SOLID_COLOR_TILE_LENGTH: u32 = 16;
|
||||||
const MAX_SOLID_COLORS_PER_TILE: u32 = SOLID_COLOR_TILE_LENGTH * SOLID_COLOR_TILE_LENGTH;
|
const MAX_SOLID_COLORS_PER_TILE: u32 = SOLID_COLOR_TILE_LENGTH * SOLID_COLOR_TILE_LENGTH;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone)]
|
||||||
pub struct Paint {
|
pub struct Palette {
|
||||||
pub color: ColorU,
|
pub(crate) paints: Vec<Paint>,
|
||||||
|
cache: HashMap<Paint, PaintId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Paint {
|
||||||
|
Color(ColorU),
|
||||||
|
Gradient(Gradient),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
pub struct PaintId(pub u16);
|
pub struct PaintId(pub u16);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct GradientId(pub u32);
|
||||||
|
|
||||||
|
impl Debug for Paint {
|
||||||
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Paint::Color(color) => color.fmt(formatter),
|
||||||
|
Paint::Gradient(_) => {
|
||||||
|
// TODO(pcwalton)
|
||||||
|
write!(formatter, "(gradient)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Palette {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Palette {
|
||||||
|
Palette { paints: vec![], cache: HashMap::new() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Paint {
|
impl Paint {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_opaque(&self) -> bool {
|
pub fn black() -> Paint {
|
||||||
self.color.a == 255
|
Paint::Color(ColorU::black())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
pub fn transparent_black() -> Paint {
|
||||||
|
Paint::Color(ColorU::transparent_black())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_opaque(&self) -> bool {
|
||||||
|
match *self {
|
||||||
|
Paint::Color(color) => color.is_opaque(),
|
||||||
|
Paint::Gradient(ref gradient) => {
|
||||||
|
gradient.stops().iter().all(|stop| stop.color.is_opaque())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_fully_transparent(&self) -> bool {
|
pub fn is_fully_transparent(&self) -> bool {
|
||||||
self.color.is_fully_transparent()
|
match *self {
|
||||||
|
Paint::Color(color) => color.is_opaque(),
|
||||||
|
Paint::Gradient(ref gradient) => {
|
||||||
|
gradient.stops().iter().all(|stop| stop.color.is_fully_transparent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_opacity(&mut self, alpha: f32) {
|
||||||
|
if alpha == 1.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Paint::Color(ref mut color) => color.a = (color.a as f32 * alpha).round() as u8,
|
||||||
|
Paint::Gradient(ref mut gradient) => gradient.set_opacity(alpha),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,7 +120,19 @@ pub struct PaintMetadata {
|
||||||
pub is_opaque: bool,
|
pub is_opaque: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scene {
|
impl Palette {
|
||||||
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||||
|
pub fn push_paint(&mut self, paint: &Paint) -> PaintId {
|
||||||
|
if let Some(paint_id) = self.cache.get(paint) {
|
||||||
|
return *paint_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let paint_id = PaintId(self.paints.len() as u16);
|
||||||
|
self.cache.insert((*paint).clone(), paint_id);
|
||||||
|
self.paints.push((*paint).clone());
|
||||||
|
paint_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_paint_info(&self) -> PaintInfo {
|
pub fn build_paint_info(&self) -> PaintInfo {
|
||||||
let mut allocator = TextureAllocator::new(PAINT_TEXTURE_LENGTH);
|
let mut allocator = TextureAllocator::new(PAINT_TEXTURE_LENGTH);
|
||||||
let area = PAINT_TEXTURE_LENGTH as usize * PAINT_TEXTURE_LENGTH as usize;
|
let area = PAINT_TEXTURE_LENGTH as usize * PAINT_TEXTURE_LENGTH as usize;
|
||||||
|
@ -68,14 +140,21 @@ impl Scene {
|
||||||
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
let mut solid_color_tile_builder = SolidColorTileBuilder::new();
|
||||||
|
|
||||||
for paint in &self.paints {
|
for paint in &self.paints {
|
||||||
// TODO(pcwalton): Handle other paint types.
|
let tex_transform;
|
||||||
let texture_location = solid_color_tile_builder.allocate(&mut allocator);
|
match paint {
|
||||||
put_pixel(&mut texels, texture_location.rect.origin(), paint.color);
|
Paint::Color(color) => {
|
||||||
let tex_transform = Transform2I {
|
// TODO(pcwalton): Handle other paint types.
|
||||||
matrix: Matrix2x2I(I32x4::default()),
|
let texture_location = solid_color_tile_builder.allocate(&mut allocator);
|
||||||
vector: texture_location.rect.origin().scale(PAINT_TEXTURE_SCALE as i32) +
|
put_pixel(&mut texels, texture_location.rect.origin(), *color);
|
||||||
Vector2I::splat(PAINT_TEXTURE_SCALE as i32 / 2),
|
tex_transform = Transform2I {
|
||||||
};
|
matrix: Matrix2x2I(I32x4::default()),
|
||||||
|
vector: texture_location.rect.origin().scale(PAINT_TEXTURE_SCALE as i32) +
|
||||||
|
Vector2I::splat(PAINT_TEXTURE_SCALE as i32 / 2),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Paint::Gradient(_) => unimplemented!(),
|
||||||
|
}
|
||||||
|
|
||||||
metadata.push(PaintMetadata { tex_transform, is_opaque: paint.is_opaque() });
|
metadata.push(PaintMetadata { tex_transform, is_opaque: paint.is_opaque() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,7 @@ use crate::builder::SceneBuilder;
|
||||||
use crate::concurrent::executor::Executor;
|
use crate::concurrent::executor::Executor;
|
||||||
use crate::options::{BuildOptions, PreparedBuildOptions};
|
use crate::options::{BuildOptions, PreparedBuildOptions};
|
||||||
use crate::options::{PreparedRenderTransform, RenderCommandListener};
|
use crate::options::{PreparedRenderTransform, RenderCommandListener};
|
||||||
use crate::paint::{Paint, PaintId};
|
use crate::paint::{Paint, PaintId, PaintInfo, Palette};
|
||||||
use hashbrown::HashMap;
|
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_geometry::vector::Vector2F;
|
use pathfinder_geometry::vector::Vector2F;
|
||||||
use pathfinder_geometry::rect::RectF;
|
use pathfinder_geometry::rect::RectF;
|
||||||
|
@ -25,8 +24,7 @@ use pathfinder_content::outline::Outline;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
pub(crate) paths: Vec<PathObject>,
|
pub(crate) paths: Vec<PathObject>,
|
||||||
pub(crate) paints: Vec<Paint>,
|
palette: Palette,
|
||||||
paint_cache: HashMap<Paint, PaintId>,
|
|
||||||
bounds: RectF,
|
bounds: RectF,
|
||||||
view_box: RectF,
|
view_box: RectF,
|
||||||
}
|
}
|
||||||
|
@ -36,8 +34,7 @@ impl Scene {
|
||||||
pub fn new() -> Scene {
|
pub fn new() -> Scene {
|
||||||
Scene {
|
Scene {
|
||||||
paths: vec![],
|
paths: vec![],
|
||||||
paints: vec![],
|
palette: Palette::new(),
|
||||||
paint_cache: HashMap::new(),
|
|
||||||
bounds: RectF::default(),
|
bounds: RectF::default(),
|
||||||
view_box: RectF::default(),
|
view_box: RectF::default(),
|
||||||
}
|
}
|
||||||
|
@ -48,16 +45,14 @@ impl Scene {
|
||||||
self.paths.push(path);
|
self.paths.push(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn build_paint_info(&self) -> PaintInfo {
|
||||||
|
self.palette.build_paint_info()
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||||
pub fn push_paint(&mut self, paint: &Paint) -> PaintId {
|
pub fn push_paint(&mut self, paint: &Paint) -> PaintId {
|
||||||
if let Some(paint_id) = self.paint_cache.get(paint) {
|
self.palette.push_paint(paint)
|
||||||
return *paint_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
let paint_id = PaintId(self.paints.len() as u16);
|
|
||||||
self.paint_cache.insert(*paint, paint_id);
|
|
||||||
self.paints.push(*paint);
|
|
||||||
paint_id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -150,7 +145,11 @@ impl Scene {
|
||||||
.any(|path_object| path_object.paint != first_paint_id) {
|
.any(|path_object| path_object.paint != first_paint_id) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(self.paints[first_paint_id.0 as usize].color)
|
|
||||||
|
match self.palette.paints[first_paint_id.0 as usize] {
|
||||||
|
Paint::Color(color) => Some(color),
|
||||||
|
Paint::Gradient(_) => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -190,7 +189,7 @@ impl<'a> Iterator for PathIter<'a> {
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let item = self.scene.paths.get(self.pos).map(|path_object| {
|
let item = self.scene.paths.get(self.pos).map(|path_object| {
|
||||||
(
|
(
|
||||||
self.scene.paints.get(path_object.paint.0 as usize).unwrap(),
|
self.scene.palette.paints.get(path_object.paint.0 as usize).unwrap(),
|
||||||
&path_object.outline,
|
&path_object.outline,
|
||||||
&*path_object.name
|
&*path_object.name
|
||||||
)
|
)
|
||||||
|
|
|
@ -246,16 +246,15 @@ impl PaintExt for Paint {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_svg_paint(svg_paint: &UsvgPaint, opacity: Opacity, result_flags: &mut BuildResultFlags)
|
fn from_svg_paint(svg_paint: &UsvgPaint, opacity: Opacity, result_flags: &mut BuildResultFlags)
|
||||||
-> Paint {
|
-> Paint {
|
||||||
Paint {
|
// TODO(pcwalton): Support gradients.
|
||||||
color: match *svg_paint {
|
Paint::Color(match *svg_paint {
|
||||||
UsvgPaint::Color(color) => ColorU::from_svg_color(color, opacity),
|
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);
|
||||||
ColorU::black()
|
ColorU::black()
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ use crate::{Twips, Point2};
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_content::stroke::{LineJoin, LineCap};
|
use pathfinder_content::stroke::{LineJoin, LineCap};
|
||||||
use pathfinder_renderer::paint::Paint;
|
use pathfinder_renderer::paint::Paint;
|
||||||
use std::mem;
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::mem;
|
||||||
use swf_tree::tags::DefineShape;
|
use swf_tree::tags::DefineShape;
|
||||||
use swf_tree::{CapStyle, FillStyle, JoinStyle, LineStyle, ShapeRecord, StraightSRgba8, Vector2D};
|
use swf_tree::{CapStyle, FillStyle, JoinStyle, LineStyle, ShapeRecord, StraightSRgba8, Vector2D};
|
||||||
use swf_tree::{fill_styles, join_styles, shape_records};
|
use swf_tree::{fill_styles, join_styles, shape_records};
|
||||||
|
@ -149,10 +149,10 @@ impl StyleLayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fill(&self) -> Paint {
|
pub(crate) fn fill(&self) -> &Paint {
|
||||||
match &self.fill {
|
match &self.fill {
|
||||||
PaintOrLine::Paint(paint) => *paint,
|
PaintOrLine::Paint(ref paint) => paint,
|
||||||
PaintOrLine::Line(line) => line.color,
|
PaintOrLine::Line(line) => &line.color,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,16 +253,7 @@ fn get_new_styles<'a>(
|
||||||
a
|
a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) => {
|
) => Some(PaintOrLine::Paint(Paint::Color(ColorU { r: *r, g: *g, b: *b, a: *a }))),
|
||||||
Some(PaintOrLine::Paint(Paint {
|
|
||||||
color: ColorU {
|
|
||||||
r: *r,
|
|
||||||
g: *g,
|
|
||||||
b: *b,
|
|
||||||
a: *a
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
_ => unimplemented!("Unimplemented fill style")
|
_ => unimplemented!("Unimplemented fill style")
|
||||||
}
|
}
|
||||||
}).chain(
|
}).chain(
|
||||||
|
@ -295,7 +286,7 @@ fn get_new_styles<'a>(
|
||||||
// assert_eq!(start_cap, end_cap);
|
// assert_eq!(start_cap, end_cap);
|
||||||
Some(PaintOrLine::Line(SwfLineStyle {
|
Some(PaintOrLine::Line(SwfLineStyle {
|
||||||
width: Twips(*width as i32),
|
width: Twips(*width as i32),
|
||||||
color: Paint { color: ColorU { r: *r, g: *g, b: *b, a: *a } },
|
color: Paint::Color(ColorU { r: *r, g: *g, b: *b, a: *a }),
|
||||||
join: match join {
|
join: match join {
|
||||||
JoinStyle::Bevel => LineJoin::Bevel,
|
JoinStyle::Bevel => LineJoin::Bevel,
|
||||||
JoinStyle::Round => LineJoin::Round,
|
JoinStyle::Round => LineJoin::Round,
|
||||||
|
|
Loading…
Reference in New Issue