Implement basic support for non-repeating image patterns.
This commit is contained in:
parent
aad467e716
commit
90445bac7e
|
@ -172,6 +172,11 @@ name = "build_const"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -1071,6 +1076,18 @@ dependencies = [
|
||||||
"tiff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tiff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "image"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inflate"
|
name = "inflate"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
@ -1605,6 +1622,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"image 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pathfinder_color 0.1.0",
|
"pathfinder_color 0.1.0",
|
||||||
"pathfinder_geometry 0.4.0",
|
"pathfinder_geometry 0.4.0",
|
||||||
|
@ -2713,6 +2731,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
|
"checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a"
|
||||||
"checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
"checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||||
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
|
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
|
||||||
|
"checksum bytemuck 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431"
|
||||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||||
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
||||||
"checksum calloop 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160"
|
"checksum calloop 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160"
|
||||||
|
@ -2800,6 +2819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e"
|
"checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e"
|
||||||
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||||
"checksum image 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)" = "53cb19c4e35102e5c6fb9ade5e0e236c5588424dc171a849af3141bf0b47768a"
|
"checksum image 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)" = "53cb19c4e35102e5c6fb9ade5e0e236c5588424dc171a849af3141bf0b47768a"
|
||||||
|
"checksum image 0.23.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef4e336ec01a678e7ab692914c641181528e8656451e6252f8f9e33728882eaf"
|
||||||
"checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff"
|
"checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff"
|
||||||
"checksum instant 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c346c299e3fe8ef94dc10c2c0253d858a69aac1245157a3bf4125915d528caf"
|
"checksum instant 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6c346c299e3fe8ef94dc10c2c0253d858a69aac1245157a3bf4125915d528caf"
|
||||||
"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||||
|
|
|
@ -14,6 +14,7 @@ use pathfinder_color::ColorU;
|
||||||
use pathfinder_content::dash::OutlineDash;
|
use pathfinder_content::dash::OutlineDash;
|
||||||
use pathfinder_content::gradient::Gradient;
|
use pathfinder_content::gradient::Gradient;
|
||||||
use pathfinder_content::outline::{ArcDirection, Contour, Outline};
|
use pathfinder_content::outline::{ArcDirection, Contour, Outline};
|
||||||
|
use pathfinder_content::pattern::Pattern;
|
||||||
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};
|
||||||
use pathfinder_geometry::line_segment::LineSegment2F;
|
use pathfinder_geometry::line_segment::LineSegment2F;
|
||||||
|
@ -427,6 +428,7 @@ impl Path2D {
|
||||||
pub enum FillStyle {
|
pub enum FillStyle {
|
||||||
Color(ColorU),
|
Color(ColorU),
|
||||||
Gradient(Gradient),
|
Gradient(Gradient),
|
||||||
|
Pattern(Pattern),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FillStyle {
|
impl FillStyle {
|
||||||
|
@ -434,6 +436,7 @@ impl FillStyle {
|
||||||
match self {
|
match self {
|
||||||
FillStyle::Color(color) => Paint::Color(color),
|
FillStyle::Color(color) => Paint::Color(color),
|
||||||
FillStyle::Gradient(gradient) => Paint::Gradient(gradient),
|
FillStyle::Gradient(gradient) => Paint::Gradient(gradient),
|
||||||
|
FillStyle::Pattern(pattern) => Paint::Pattern(pattern),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -456,4 +459,3 @@ pub enum LineJoin {
|
||||||
Bevel,
|
Bevel,
|
||||||
Round,
|
Round,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
|
|
||||||
use pathfinder_simd::default::F32x4;
|
use pathfinder_simd::default::F32x4;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
// TODO(pcwalton): Maybe this should be a u32?
|
// TODO(pcwalton): Maybe this should be a u32? Need to be aware of endianness issues if we do that.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct ColorU {
|
pub struct ColorU {
|
||||||
pub r: u8,
|
pub r: u8,
|
||||||
pub g: u8,
|
pub g: u8,
|
||||||
|
@ -51,6 +53,16 @@ impl ColorU {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn white() -> ColorU {
|
||||||
|
ColorU {
|
||||||
|
r: 255,
|
||||||
|
g: 255,
|
||||||
|
b: 255,
|
||||||
|
a: 255,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn to_f32(&self) -> ColorF {
|
pub fn to_f32(&self) -> ColorF {
|
||||||
let color = F32x4::new(self.r as f32, self.g as f32, self.b as f32, self.a as f32);
|
let color = F32x4::new(self.r as f32, self.g as f32, self.b as f32, self.a as f32);
|
||||||
|
@ -148,3 +160,24 @@ impl Debug for ColorF {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn color_slice_to_u8_slice(slice: &[ColorU]) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(slice.as_ptr() as *const u8, slice.len() * 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn u8_slice_to_color_slice(slice: &[u8]) -> &[ColorU] {
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(slice.len() % 4, 0);
|
||||||
|
slice::from_raw_parts(slice.as_ptr() as *const ColorU, slice.len() / 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Do this without a copy?
|
||||||
|
#[inline]
|
||||||
|
pub fn u8_vec_to_color_vec(buffer: Vec<u8>) -> Vec<ColorU> {
|
||||||
|
u8_slice_to_color_slice(&buffer).to_vec()
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,16 @@ bitflags = "1.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
smallvec = "1.2"
|
smallvec = "1.2"
|
||||||
|
|
||||||
|
[dependencies.image]
|
||||||
|
version = "0.23"
|
||||||
|
default-features = false
|
||||||
|
features = []
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["pf-image"]
|
||||||
|
pf-image = ["image"]
|
||||||
|
|
||||||
[dependencies.pathfinder_color]
|
[dependencies.pathfinder_color]
|
||||||
path = "../color"
|
path = "../color"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// pathfinder/geometry/src/gradient.rs
|
// pathfinder/content/src/gradient.rs
|
||||||
//
|
//
|
||||||
// Copyright © 2020 The Pathfinder Project Developers.
|
// Copyright © 2020 The Pathfinder Project Developers.
|
||||||
//
|
//
|
||||||
|
@ -55,6 +55,7 @@ impl Hash for Gradient {
|
||||||
hash_f32(end_radius, state);
|
hash_f32(end_radius, state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stops.hash(state);
|
self.stops.hash(state);
|
||||||
|
|
||||||
fn hash_line_segment<H>(line_segment: LineSegment2F, state: &mut H) where H: Hasher {
|
fn hash_line_segment<H>(line_segment: LineSegment2F, state: &mut H) where H: Hasher {
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub mod dash;
|
||||||
pub mod gradient;
|
pub mod gradient;
|
||||||
pub mod orientation;
|
pub mod orientation;
|
||||||
pub mod outline;
|
pub mod outline;
|
||||||
|
pub mod pattern;
|
||||||
pub mod segment;
|
pub mod segment;
|
||||||
pub mod sorted_vector;
|
pub mod sorted_vector;
|
||||||
pub mod stroke;
|
pub mod stroke;
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
// pathfinder/content/src/pattern.rs
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Raster image patterns.
|
||||||
|
|
||||||
|
use pathfinder_color::{self as color, ColorU};
|
||||||
|
use pathfinder_geometry::vector::Vector2I;
|
||||||
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
|
#[cfg(feature = "pf-image")]
|
||||||
|
use image::RgbaImage;
|
||||||
|
|
||||||
|
/// A raster image pattern.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct Pattern {
|
||||||
|
pub image: Image,
|
||||||
|
pub repeat: Repeat,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RGBA, non-premultiplied.
|
||||||
|
// FIXME(pcwalton): Hash the pixel contents so that we don't have to compare every pixel!
|
||||||
|
// TODO(pcwalton): Should the pixels be premultiplied?
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Image {
|
||||||
|
size: Vector2I,
|
||||||
|
pixels: Vec<ColorU>,
|
||||||
|
is_opaque: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct Repeat: u8 {
|
||||||
|
const X = 0x01;
|
||||||
|
const Y = 0x02;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pattern {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(image: Image, repeat: Repeat) -> Pattern {
|
||||||
|
Pattern { image, repeat }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Image {
|
||||||
|
#[inline]
|
||||||
|
pub fn new(size: Vector2I, pixels: Vec<ColorU>) -> Image {
|
||||||
|
assert_eq!(size.x() as usize * size.y() as usize, pixels.len());
|
||||||
|
let is_opaque = pixels.iter().all(|pixel| pixel.is_opaque());
|
||||||
|
Image { size, pixels, is_opaque }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "pf-image")]
|
||||||
|
pub fn from_image_buffer(image_buffer: RgbaImage) -> Image {
|
||||||
|
let (width, height) = image_buffer.dimensions();
|
||||||
|
let pixels = color::u8_vec_to_color_vec(image_buffer.into_raw());
|
||||||
|
Image::new(Vector2I::new(width as i32, height as i32), pixels)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn size(&self) -> Vector2I {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn pixels(&self) -> &[ColorU] {
|
||||||
|
&self.pixels
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_opaque(&self) -> bool {
|
||||||
|
self.is_opaque
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_opacity(&mut self, alpha: f32) {
|
||||||
|
debug_assert!(alpha >= 0.0 && alpha <= 1.0);
|
||||||
|
if alpha == 1.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Go four pixels at a time with SIMD.
|
||||||
|
self.pixels.iter_mut().for_each(|pixel| pixel.a = (pixel.a as f32 * alpha).round() as u8);
|
||||||
|
self.is_opaque = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Image {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
write!(formatter, "(image {}×{} px)", self.size.x(), self.size.y())
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@
|
||||||
//! A SIMD-optimized point type.
|
//! A SIMD-optimized point type.
|
||||||
|
|
||||||
use pathfinder_simd::default::{F32x2, F32x4, I32x2};
|
use pathfinder_simd::default::{F32x2, F32x4, I32x2};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
||||||
|
|
||||||
/// 2D points with 32-bit floating point coordinates.
|
/// 2D points with 32-bit floating point coordinates.
|
||||||
|
@ -306,6 +307,16 @@ impl PartialEq for Vector2I {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eq for Vector2I {}
|
||||||
|
|
||||||
|
impl Hash for Vector2I {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H>(&self, state: &mut H) where H: Hasher {
|
||||||
|
self.x().hash(state);
|
||||||
|
self.y().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 3D points.
|
/// 3D points.
|
||||||
///
|
///
|
||||||
/// The w value in the SIMD vector is always 0.0.
|
/// The w value in the SIMD vector is always 0.0.
|
||||||
|
|
|
@ -42,7 +42,8 @@ impl TextureAllocator {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn allocate(&mut self, requested_size: Vector2I) -> Option<TextureLocation> {
|
pub fn allocate(&mut self, requested_size: Vector2I) -> Option<TextureLocation> {
|
||||||
let requested_length = requested_size.x().max(requested_size.y()) as u32;
|
let requested_length =
|
||||||
|
(requested_size.x().max(requested_size.y()) as u32).next_power_of_two();
|
||||||
self.root.allocate(Vector2I::default(), self.size, requested_length)
|
self.root.allocate(Vector2I::default(), self.size, requested_length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::gpu::options::{DestFramebuffer, RendererOptions};
|
||||||
use crate::gpu_data::{AlphaTile, FillBatchPrimitive, PaintData, RenderCommand, SolidTileVertex};
|
use crate::gpu_data::{AlphaTile, FillBatchPrimitive, PaintData, RenderCommand, SolidTileVertex};
|
||||||
use crate::post::DefringingKernel;
|
use crate::post::DefringingKernel;
|
||||||
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
use pathfinder_color::ColorF;
|
use pathfinder_color::{self as color, ColorF, ColorU};
|
||||||
use pathfinder_geometry::vector::{Vector2I, Vector4F};
|
use pathfinder_geometry::vector::{Vector2I, Vector4F};
|
||||||
use pathfinder_geometry::rect::RectI;
|
use pathfinder_geometry::rect::RectI;
|
||||||
use pathfinder_geometry::transform3d::Transform4F;
|
use pathfinder_geometry::transform3d::Transform4F;
|
||||||
|
@ -353,10 +353,11 @@ where
|
||||||
fn upload_paint_data(&mut self, paint_data: &PaintData) {
|
fn upload_paint_data(&mut self, paint_data: &PaintData) {
|
||||||
// FIXME(pcwalton): This is a hack. We shouldn't be generating paint data at all on the
|
// FIXME(pcwalton): This is a hack. We shouldn't be generating paint data at all on the
|
||||||
// renderer side.
|
// renderer side.
|
||||||
let (paint_size, paint_texels): (Vector2I, &[u8]);
|
let (paint_size, paint_texels): (Vector2I, &[ColorU]);
|
||||||
|
let dummy_paint = [ColorU::white(); 1];
|
||||||
if self.postprocess_options.is_some() {
|
if self.postprocess_options.is_some() {
|
||||||
paint_size = Vector2I::splat(1);
|
paint_size = Vector2I::splat(1);
|
||||||
paint_texels = &[255; 4];
|
paint_texels = &dummy_paint;
|
||||||
} else {
|
} else {
|
||||||
paint_size = paint_data.size;
|
paint_size = paint_data.size;
|
||||||
paint_texels = &paint_data.texels;
|
paint_texels = &paint_data.texels;
|
||||||
|
@ -370,9 +371,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let texels = color::color_slice_to_u8_slice(paint_texels);
|
||||||
self.device.upload_to_texture(self.paint_texture.as_ref().unwrap(),
|
self.device.upload_to_texture(self.paint_texture.as_ref().unwrap(),
|
||||||
RectI::new(Vector2I::default(), paint_size),
|
RectI::new(Vector2I::default(), paint_size),
|
||||||
TextureDataRef::U8(paint_texels));
|
TextureDataRef::U8(texels));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upload_solid_tiles(&mut self, solid_tile_vertices: &[SolidTileVertex]) {
|
fn upload_solid_tiles(&mut self, solid_tile_vertices: &[SolidTileVertex]) {
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
use crate::options::BoundingQuad;
|
use crate::options::BoundingQuad;
|
||||||
use crate::tile_map::DenseTileMap;
|
use crate::tile_map::DenseTileMap;
|
||||||
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8};
|
use pathfinder_geometry::line_segment::{LineSegmentU4, LineSegmentU8};
|
||||||
use pathfinder_geometry::rect::RectF;
|
use pathfinder_geometry::rect::RectF;
|
||||||
use pathfinder_geometry::vector::Vector2I;
|
use pathfinder_geometry::vector::Vector2I;
|
||||||
|
@ -39,7 +40,7 @@ pub enum RenderCommand {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PaintData {
|
pub struct PaintData {
|
||||||
pub size: Vector2I,
|
pub size: Vector2I,
|
||||||
pub texels: Vec<u8>,
|
pub texels: Vec<ColorU>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
|
|
@ -14,6 +14,7 @@ use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use pathfinder_color::ColorU;
|
use pathfinder_color::ColorU;
|
||||||
use pathfinder_content::gradient::{Gradient, GradientGeometry};
|
use pathfinder_content::gradient::{Gradient, GradientGeometry};
|
||||||
|
use pathfinder_content::pattern::Pattern;
|
||||||
use pathfinder_geometry::rect::{RectF, RectI};
|
use pathfinder_geometry::rect::{RectF, RectI};
|
||||||
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
|
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
|
||||||
use pathfinder_geometry::util;
|
use pathfinder_geometry::util;
|
||||||
|
@ -43,6 +44,7 @@ pub struct Palette {
|
||||||
pub enum Paint {
|
pub enum Paint {
|
||||||
Color(ColorU),
|
Color(ColorU),
|
||||||
Gradient(Gradient),
|
Gradient(Gradient),
|
||||||
|
Pattern(Pattern),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||||
|
@ -59,6 +61,7 @@ impl Debug for Paint {
|
||||||
// TODO(pcwalton)
|
// TODO(pcwalton)
|
||||||
write!(formatter, "(gradient)")
|
write!(formatter, "(gradient)")
|
||||||
}
|
}
|
||||||
|
Paint::Pattern(ref pattern) => pattern.fmt(formatter),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +90,7 @@ impl Paint {
|
||||||
Paint::Gradient(ref gradient) => {
|
Paint::Gradient(ref gradient) => {
|
||||||
gradient.stops().iter().all(|stop| stop.color.is_opaque())
|
gradient.stops().iter().all(|stop| stop.color.is_opaque())
|
||||||
}
|
}
|
||||||
|
Paint::Pattern(ref pattern) => pattern.image.is_opaque(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +100,10 @@ impl Paint {
|
||||||
Paint::Gradient(ref gradient) => {
|
Paint::Gradient(ref gradient) => {
|
||||||
gradient.stops().iter().all(|stop| stop.color.is_fully_transparent())
|
gradient.stops().iter().all(|stop| stop.color.is_fully_transparent())
|
||||||
}
|
}
|
||||||
|
Paint::Pattern(_) => {
|
||||||
|
// TODO(pcwalton): Should we support this?
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +115,7 @@ impl Paint {
|
||||||
match *self {
|
match *self {
|
||||||
Paint::Color(ref mut color) => color.a = (color.a as f32 * alpha).round() as u8,
|
Paint::Color(ref mut color) => color.a = (color.a as f32 * alpha).round() as u8,
|
||||||
Paint::Gradient(ref mut gradient) => gradient.set_opacity(alpha),
|
Paint::Gradient(ref mut gradient) => gradient.set_opacity(alpha),
|
||||||
|
Paint::Pattern(ref mut pattern) => pattern.image.set_opacity(alpha),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,42 +156,50 @@ impl Palette {
|
||||||
pub fn build_paint_info(&self, view_box_size: Vector2I) -> PaintInfo {
|
pub fn build_paint_info(&self, view_box_size: Vector2I) -> 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;
|
||||||
let (mut texels, mut metadata) = (vec![0; area * 4], vec![]);
|
let (mut texels, mut metadata) = (vec![ColorU::default(); area], vec![]);
|
||||||
|
|
||||||
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 {
|
||||||
let (texture_location, tex_transform);
|
let (tex_location, tex_transform);
|
||||||
match paint {
|
match paint {
|
||||||
Paint::Color(color) => {
|
Paint::Color(color) => {
|
||||||
texture_location = solid_color_tile_builder.allocate(&mut allocator);
|
tex_location = solid_color_tile_builder.allocate(&mut allocator);
|
||||||
let vector = rect_to_inset_uv(texture_location.rect).origin();
|
let vector = rect_to_inset_uv(tex_location.rect).origin();
|
||||||
tex_transform = Transform2F { matrix: Matrix2x2F(F32x4::default()), vector };
|
tex_transform = Transform2F { matrix: Matrix2x2F(F32x4::default()), vector };
|
||||||
put_pixel(&mut texels, texture_location.rect.origin(), *color);
|
put_pixel(&mut texels, tex_location.rect.origin(), *color);
|
||||||
}
|
}
|
||||||
Paint::Gradient(ref gradient) => {
|
Paint::Gradient(ref gradient) => {
|
||||||
// TODO(pcwalton): Optimize this:
|
// TODO(pcwalton): Optimize this:
|
||||||
// 1. Use repeating/clamp on the sides.
|
// 1. Use repeating/clamp on the sides.
|
||||||
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
// 2. Choose an optimal size for the gradient that minimizes memory usage while
|
||||||
// retaining quality.
|
// retaining quality.
|
||||||
texture_location =
|
tex_location = allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32))
|
||||||
allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32))
|
|
||||||
.expect("Failed to allocate space for the gradient!");
|
.expect("Failed to allocate space for the gradient!");
|
||||||
|
|
||||||
tex_transform =
|
tex_transform =
|
||||||
Transform2F::from_translation(rect_to_uv(texture_location.rect).origin()) *
|
Transform2F::from_translation(rect_to_uv(tex_location.rect).origin()) *
|
||||||
Transform2F::from_scale(Vector2F::splat(GRADIENT_TILE_SCALE) /
|
Transform2F::from_scale(Vector2F::splat(GRADIENT_TILE_SCALE) /
|
||||||
view_box_size.to_f32());
|
view_box_size.to_f32());
|
||||||
|
|
||||||
self.build_paint_info_for_gradient(gradient,
|
self.build_paint_info_for_gradient(gradient,
|
||||||
texture_location,
|
tex_location,
|
||||||
&tex_transform,
|
&tex_transform,
|
||||||
&mut texels);
|
&mut texels);
|
||||||
}
|
}
|
||||||
|
Paint::Pattern(ref pattern) => {
|
||||||
|
tex_location = allocator.allocate(pattern.image.size())
|
||||||
|
.expect("Failed to allocate space for the image!");
|
||||||
|
tex_transform =
|
||||||
|
Transform2F::from_translation(rect_to_uv(tex_location.rect).origin()) *
|
||||||
|
Transform2F::from_uniform_scale(PAINT_TEXTURE_SCALE);
|
||||||
|
|
||||||
|
self.build_paint_info_for_pattern(pattern, tex_location, &mut texels);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.push(PaintMetadata {
|
metadata.push(PaintMetadata {
|
||||||
tex_rect: texture_location.rect,
|
tex_rect: tex_location.rect,
|
||||||
tex_transform,
|
tex_transform,
|
||||||
is_opaque: paint.is_opaque(),
|
is_opaque: paint.is_opaque(),
|
||||||
});
|
});
|
||||||
|
@ -197,7 +214,7 @@ impl Palette {
|
||||||
gradient: &Gradient,
|
gradient: &Gradient,
|
||||||
texture_location: TextureLocation,
|
texture_location: TextureLocation,
|
||||||
tex_transform: &Transform2F,
|
tex_transform: &Transform2F,
|
||||||
texels: &mut [u8]) {
|
texels: &mut [ColorU]) {
|
||||||
match *gradient.geometry() {
|
match *gradient.geometry() {
|
||||||
GradientGeometry::Linear(gradient_line) => {
|
GradientGeometry::Linear(gradient_line) => {
|
||||||
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
|
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
|
||||||
|
@ -219,6 +236,7 @@ impl Palette {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GradientGeometry::Radial { line: gradient_line, start_radius, end_radius } => {
|
GradientGeometry::Radial { line: gradient_line, start_radius, end_radius } => {
|
||||||
// FIXME(pcwalton): Paint transparent if line has zero size and radii are equal,
|
// FIXME(pcwalton): Paint transparent if line has zero size and radii are equal,
|
||||||
// per spec.
|
// per spec.
|
||||||
|
@ -245,6 +263,22 @@ impl Palette {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_paint_info_for_pattern(&self,
|
||||||
|
pattern: &Pattern,
|
||||||
|
texture_location: TextureLocation,
|
||||||
|
texels: &mut [ColorU]) {
|
||||||
|
let image_size = pattern.image.size();
|
||||||
|
for y in 0..image_size.y() {
|
||||||
|
let dest_origin = texture_location.rect.origin() + Vector2I::new(0, y);
|
||||||
|
let dest_start_index = paint_texel_index(dest_origin);
|
||||||
|
let src_start_index = y as usize * image_size.x() as usize;
|
||||||
|
let dest_end_index = dest_start_index + image_size.x() as usize;
|
||||||
|
let src_end_index = src_start_index + image_size.x() as usize;
|
||||||
|
texels[dest_start_index..dest_end_index].copy_from_slice(
|
||||||
|
&pattern.image.pixels()[src_start_index..src_end_index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintMetadata {
|
impl PaintMetadata {
|
||||||
|
@ -256,13 +290,12 @@ impl PaintMetadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_pixel(texels: &mut [u8], position: Vector2I, color: ColorU) {
|
fn paint_texel_index(position: Vector2I) -> usize {
|
||||||
let index = (position.y() as usize * PAINT_TEXTURE_LENGTH as usize +
|
position.y() as usize * PAINT_TEXTURE_LENGTH as usize + position.x() as usize
|
||||||
position.x() as usize) * 4;
|
}
|
||||||
texels[index + 0] = color.r;
|
|
||||||
texels[index + 1] = color.g;
|
fn put_pixel(texels: &mut [ColorU], position: Vector2I, color: ColorU) {
|
||||||
texels[index + 2] = color.b;
|
texels[paint_texel_index(position)] = color
|
||||||
texels[index + 3] = color.a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rect_to_uv(rect: RectI) -> RectF {
|
fn rect_to_uv(rect: RectI) -> RectF {
|
||||||
|
|
|
@ -149,6 +149,7 @@ impl Scene {
|
||||||
match self.palette.paints[first_paint_id.0 as usize] {
|
match self.palette.paints[first_paint_id.0 as usize] {
|
||||||
Paint::Color(color) => Some(color),
|
Paint::Color(color) => Some(color),
|
||||||
Paint::Gradient(_) => None,
|
Paint::Gradient(_) => None,
|
||||||
|
Paint::Pattern(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue