Implement basic support for non-repeating image patterns.

This commit is contained in:
Patrick Walton 2020-02-11 17:20:21 -08:00
parent aad467e716
commit 90445bac7e
13 changed files with 242 additions and 28 deletions

20
Cargo.lock generated
View File

@ -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"

View File

@ -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,
} }

View File

@ -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()
}

View File

@ -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"

View File

@ -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 {

View File

@ -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;

98
content/src/pattern.rs Normal file
View File

@ -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())
}
}

View File

@ -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.

View File

@ -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)
} }

View File

@ -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]) {

View File

@ -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)]

View File

@ -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 {

View File

@ -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,
} }
} }