pathfinder/content/src/pattern.rs

133 lines
3.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 source: PatternSource,
pub repeat: Repeat,
}
#[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);
/// 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(source: PatternSource, repeat: Repeat) -> Pattern {
Pattern { source, 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 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!()
}
}
}
}
impl Debug for Image {
#[inline]
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
write!(formatter, "(image {}×{} px)", self.size.x(), self.size.y())
}
}