pathfinder/content/src/pattern.rs

99 lines
2.6 KiB
Rust
Raw Normal View History

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