2020-02-11 20:20:21 -05:00
|
|
|
|
// 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 {
|
2020-02-22 00:42:15 -05:00
|
|
|
|
pub source: PatternSource,
|
2020-02-11 20:20:21 -05:00
|
|
|
|
pub repeat: Repeat,
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 00:42:15 -05:00
|
|
|
|
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
|
|
|
|
pub enum PatternSource {
|
|
|
|
|
Image(Image),
|
|
|
|
|
RenderTarget(RenderTargetId),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
|
|
|
|
pub struct RenderTargetId(pub u32);
|
|
|
|
|
|
2020-02-11 20:20:21 -05:00
|
|
|
|
/// 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]
|
2020-02-22 00:42:15 -05:00
|
|
|
|
pub fn new(source: PatternSource, repeat: Repeat) -> Pattern {
|
|
|
|
|
Pattern { source, repeat }
|
2020-02-11 20:20:21 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 00:42:15 -05:00
|
|
|
|
impl PatternSource {
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn is_opaque(&self) -> bool {
|
|
|
|
|
match *self {
|
|
|
|
|
PatternSource::Image(ref image) => image.is_opaque(),
|
|
|
|
|
PatternSource::RenderTarget(_) => {
|
|
|
|
|
// TODO(pcwalton): Maybe do something smarter here?
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn set_opacity(&mut self, alpha: f32) {
|
|
|
|
|
match *self {
|
|
|
|
|
PatternSource::Image(ref mut image) => image.set_opacity(alpha),
|
|
|
|
|
PatternSource::RenderTarget(_) => {
|
|
|
|
|
// TODO(pcwalton): We'll probably have to introduce and use an Opacity filter for
|
|
|
|
|
// this.
|
|
|
|
|
unimplemented!()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-11 20:20:21 -05:00
|
|
|
|
impl Debug for Image {
|
|
|
|
|
#[inline]
|
|
|
|
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
|
|
|
|
write!(formatter, "(image {}×{} px)", self.size.x(), self.size.y())
|
|
|
|
|
}
|
|
|
|
|
}
|