// pathfinder/content/src/pattern.rs // // Copyright © 2020 The Pathfinder Project Developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Raster image patterns. use crate::effects::PatternFilter; use crate::render_target::RenderTargetId; use crate::util; use pathfinder_color::{self as color, ColorU}; use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::vector::{Vector2I, vec2i}; use std::collections::hash_map::DefaultHasher; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; use std::sync::Arc; #[cfg(feature = "pf-image")] use image::RgbaImage; /// A raster image pattern. #[derive(Clone, PartialEq, Debug)] pub struct Pattern { source: PatternSource, transform: Transform2F, filter: Option, flags: PatternFlags, } #[derive(Clone, PartialEq, Eq, Hash, Debug)] pub enum PatternSource { Image(Image), RenderTarget { id: RenderTargetId, size: Vector2I, } } /// 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)] pub struct Image { size: Vector2I, pixels: Arc>, pixels_hash: u64, is_opaque: bool, } bitflags! { pub struct PatternFlags: u8 { const REPEAT_X = 0x01; const REPEAT_Y = 0x02; const NO_SMOOTHING = 0x04; } } impl Pattern { #[inline] fn from_source(source: PatternSource) -> Pattern { Pattern { source, transform: Transform2F::default(), filter: None, flags: PatternFlags::empty(), } } #[inline] pub fn from_image(image: Image) -> Pattern { Pattern::from_source(PatternSource::Image(image)) } #[inline] pub fn from_render_target(id: RenderTargetId, size: Vector2I) -> Pattern { Pattern::from_source(PatternSource::RenderTarget { id, size }) } #[inline] pub fn transform(&self) -> Transform2F { self.transform } #[inline] pub fn apply_transform(&mut self, transform: Transform2F) { self.transform = transform * self.transform; } #[inline] pub fn size(&self) -> Vector2I { match self.source { PatternSource::Image(ref image) => image.size(), PatternSource::RenderTarget { size, .. } => size, } } #[inline] pub fn filter(&self) -> Option { self.filter } #[inline] pub fn set_filter(&mut self, filter: Option) { self.filter = filter; } #[inline] pub fn repeat_x(&self) -> bool { self.flags.contains(PatternFlags::REPEAT_X) } #[inline] pub fn set_repeat_x(&mut self, repeat_x: bool) { self.flags.set(PatternFlags::REPEAT_X, repeat_x); } #[inline] pub fn repeat_y(&self) -> bool { self.flags.contains(PatternFlags::REPEAT_Y) } #[inline] pub fn set_repeat_y(&mut self, repeat_y: bool) { self.flags.set(PatternFlags::REPEAT_Y, repeat_y); } #[inline] pub fn smoothing_enabled(&self) -> bool { !self.flags.contains(PatternFlags::NO_SMOOTHING) } #[inline] pub fn set_smoothing_enabled(&mut self, enable: bool) { self.flags.set(PatternFlags::NO_SMOOTHING, !enable); } #[inline] pub fn is_opaque(&self) -> bool { self.source.is_opaque() } #[inline] pub fn source(&self) -> &PatternSource { &self.source } } impl Image { #[inline] pub fn new(size: Vector2I, pixels: Arc>) -> Image { assert_eq!(size.x() as usize * size.y() as usize, pixels.len()); let is_opaque = pixels.iter().all(|pixel| pixel.is_opaque()); let mut pixels_hasher = DefaultHasher::new(); pixels.hash(&mut pixels_hasher); let pixels_hash = pixels_hasher.finish(); Image { size, pixels, pixels_hash, 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(vec2i(width as i32, height as i32), Arc::new(pixels)) } #[inline] pub fn size(&self) -> Vector2I { self.size } #[inline] pub fn pixels(&self) -> &Arc> { &self.pixels } #[inline] pub fn is_opaque(&self) -> bool { self.is_opaque } } 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 } } } } impl Debug for Image { #[inline] fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { write!(formatter, "(image {}×{} px)", self.size.x(), self.size.y()) } } impl Hash for Image { fn hash(&self, hasher: &mut H) where H: Hasher { self.size.hash(hasher); self.pixels_hash.hash(hasher); self.is_opaque.hash(hasher); } } impl Eq for Pattern {} impl Hash for Pattern { fn hash(&self, state: &mut H) where H: Hasher { self.source.hash(state); util::hash_transform2f(self.transform, state); self.flags.hash(state); } }