Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 8 additions & 17 deletions src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ use super::{Blob, Extend};
#[non_exhaustive]
#[repr(u8)]
pub enum ImageFormat {
/// 32-bit RGBA with 8-bit channels.
/// 32-bit RGBA with 8-bit channels, with a separate alpha.
Rgba8 = 0,
/// 32-bit BGRA with 8-bit channels.
Bgra8 = 1,
/// 32-bit RGBA with 8-bit channels, with premultiplied alpha.
PremulRgba8 = 1,
/// 32-bit BGRA with 8-bit channels, with a separate alpha.
Bgra8 = 2,
/// 32-bit BGRA with 8-bit channels, with premultiplied alpha.
PremulBgra8 = 3,
// NOTICE: If a new value is added, be sure to update the bytemuck CheckedBitPattern impl.
}

Expand All @@ -24,24 +28,13 @@ impl ImageFormat {
#[must_use]
pub fn size_in_bytes(self, width: u32, height: u32) -> Option<usize> {
match self {
Self::Rgba8 | Self::Bgra8 => 4_usize
Self::Rgba8 | Self::PremulRgba8 | Self::Bgra8 | Self::PremulBgra8 => 4_usize
.checked_mul(width as usize)
.and_then(|x| x.checked_mul(height as usize)),
}
}
}

/// Handling of alpha channel.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum ImageAlphaType {
/// Image has separate alpha channel (also called straight/unpremultiplied alpha).
Alpha = 0,
/// Image has colors with premultiplied alpha.
AlphaPremultiplied = 1,
}

/// Defines the desired quality for sampling an image.
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -71,8 +64,6 @@ pub struct ImageData {
pub data: Blob<u8>,
/// Pixel format of the image.
pub format: ImageFormat,
/// Encoding of alpha in the image pixels.
pub alpha_type: ImageAlphaType,
/// Width of the image.
pub width: u32,
/// Height of the image.
Expand Down
81 changes: 4 additions & 77 deletions src/impl_bytemuck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#![allow(unsafe_code, reason = "unsafe is required for bytemuck unsafe impls")]

use crate::{Compose, Extend, Fill, ImageAlphaType, ImageFormat, ImageQuality, Mix};
use crate::{Compose, Extend, Fill, ImageFormat, ImageQuality, Mix};

// Safety: The enum is `repr(u8)` and has only fieldless variants.
unsafe impl bytemuck::NoUninit for Compose {}
Expand Down Expand Up @@ -102,32 +102,7 @@ unsafe impl bytemuck::checked::CheckedBitPattern for ImageFormat {
unsafe impl bytemuck::Contiguous for ImageFormat {
type Int = u8;
const MIN_VALUE: u8 = Self::Rgba8 as u8;
const MAX_VALUE: u8 = Self::Bgra8 as u8;
}

// Safety: The enum is `repr(u8)` and has only fieldless variants.
unsafe impl bytemuck::NoUninit for ImageAlphaType {}

// Safety: The enum is `repr(u8)` and `0` is a valid value.
unsafe impl bytemuck::Zeroable for ImageAlphaType {}

// Safety: The enum is `repr(u8)`.
unsafe impl bytemuck::checked::CheckedBitPattern for ImageAlphaType {
type Bits = u8;

fn is_valid_bit_pattern(bits: &u8) -> bool {
use bytemuck::Contiguous;
// Don't need to compare against MIN_VALUE as this is u8 and 0 is the MIN_VALUE.
*bits <= Self::MAX_VALUE
}
}

// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
// the min and max values.
unsafe impl bytemuck::Contiguous for ImageAlphaType {
type Int = u8;
const MIN_VALUE: u8 = Self::Alpha as u8;
const MAX_VALUE: u8 = Self::AlphaPremultiplied as u8;
const MAX_VALUE: u8 = Self::PremulBgra8 as u8;
}

// Safety: The enum is `repr(u8)` and has only fieldless variants.
Expand Down Expand Up @@ -172,7 +147,7 @@ unsafe impl bytemuck::checked::CheckedBitPattern for Mix {

#[cfg(test)]
mod tests {
use crate::{Compose, Extend, Fill, ImageAlphaType, ImageFormat, ImageQuality, Mix};
use crate::{Compose, Extend, Fill, ImageFormat, ImageQuality, Mix};
use bytemuck::{checked::try_from_bytes, Contiguous, Zeroable};
use core::ptr;

Expand All @@ -191,22 +166,14 @@ mod tests {
assert_eq!(Ok(&Fill::EvenOdd), try_from_bytes::<Fill>(valid_one));
assert!(try_from_bytes::<Fill>(invalid).is_err());

assert_eq!(
Ok(&ImageAlphaType::Alpha),
try_from_bytes::<ImageAlphaType>(valid_zero)
);
assert_eq!(
Ok(&ImageAlphaType::AlphaPremultiplied),
try_from_bytes::<ImageAlphaType>(valid_one)
);
assert!(try_from_bytes::<ImageFormat>(invalid).is_err());

assert_eq!(
Ok(&ImageFormat::Rgba8),
try_from_bytes::<ImageFormat>(valid_zero)
);
assert_eq!(
Ok(&ImageFormat::Bgra8),
Ok(&ImageFormat::PremulRgba8),
try_from_bytes::<ImageFormat>(valid_one)
);
assert!(try_from_bytes::<ImageFormat>(invalid).is_err());
Expand Down Expand Up @@ -245,11 +212,6 @@ mod tests {
let image_format_2 = ImageFormat::from_integer(image_format_1.into_integer());
assert_eq!(Some(image_format_1), image_format_2);

let image_alpha_type_1 = ImageAlphaType::Alpha;
let image_alpha_type_2 = ImageAlphaType::from_integer(image_alpha_type_1.into_integer());
assert_eq!(Some(image_alpha_type_1), image_alpha_type_2);
assert_eq!(None, ImageAlphaType::from_integer(255));

let image_quality_1 = ImageQuality::Low;
let image_quality_2 = ImageQuality::from_integer(image_quality_1.into_integer());
assert_eq!(Some(image_quality_1), image_quality_2);
Expand All @@ -271,9 +233,6 @@ mod tests {
let image_format = ImageFormat::zeroed();
assert_eq!(image_format, ImageFormat::Rgba8);

let image_alpha_type = ImageAlphaType::zeroed();
assert_eq!(image_alpha_type, ImageAlphaType::Alpha);

let image_quality = ImageQuality::zeroed();
assert_eq!(image_quality, ImageQuality::Low);

Expand Down Expand Up @@ -337,20 +296,6 @@ mod tests {
}
};

/// Tests that the [`Contiguous`] impl for [`ImageAlphaType`] is not trivially incorrect.
const _: () = {
let mut value = 0;
while value <= ImageAlphaType::MAX_VALUE {
// Safety: In a const context, therefore if this makes an invalid ImageFormat, that will be detected.
let it: ImageAlphaType = unsafe { ptr::read((&raw const value).cast()) };
// Evaluate the enum value to ensure it actually has a valid tag
if it as u8 != value {
unreachable!();
}
value += 1;
}
};

/// Tests that the [`Contiguous`] impl for [`ImageQuality`] is not trivially incorrect.
const _: () = {
let mut value = 0;
Expand Down Expand Up @@ -443,24 +388,6 @@ mod doctests {
/// ```
const _IMAGE_FORMAT: () = {};

/// Validates that any new variants in `ImageAlphaType` has led to a change in the `Contiguous` impl.
/// Note that to test this robustly, we'd need 256 tests, which is impractical.
/// We make the assumption that all new variants will maintain contiguousness.
///
/// ```compile_fail,E0080
/// use bytemuck::Contiguous;
/// use peniko::ImageAlphaType;
/// const {
/// let value = ImageAlphaType::MAX_VALUE + 1;
/// let it: ImageAlphaType = unsafe { core::ptr::read((&raw const value).cast()) };
/// // Evaluate the enum value to ensure it actually has an invalid tag
/// if it as u8 != value {
/// unreachable!();
/// }
/// }
/// ```
const _IMAGE_ALPHA_TYPE: () = {};

/// Validates that any new variants in `ImageQuality` has led to a change in the `Contiguous` impl.
/// Note that to test this robustly, we'd need 256 tests, which is impractical.
/// We make the assumption that all new variants will maintain contiguousness.
Expand Down
4 changes: 1 addition & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ pub use gradient::{
ColorStop, ColorStops, ColorStopsSource, Gradient, GradientKind, LinearGradientPosition,
RadialGradientPosition, SweepGradientPosition,
};
pub use image::{
ImageAlphaType, ImageBrush, ImageBrushRef, ImageData, ImageFormat, ImageQuality, ImageSampler,
};
pub use image::{ImageBrush, ImageBrushRef, ImageData, ImageFormat, ImageQuality, ImageSampler};
pub use style::{Fill, Style, StyleRef};

/// A convenient alias for the color type used for [`Brush`].
Expand Down
Loading