From 402b6ac9fcc1052332cd60bfff141506a21608e7 Mon Sep 17 00:00:00 2001 From: Daniel McNab <36049421+DJMcNab@users.noreply.github.com> Date: Wed, 3 Sep 2025 13:19:05 +0100 Subject: [PATCH] Move `ImageAlphaType` into being Formats instead --- src/image.rs | 25 +++++--------- src/impl_bytemuck.rs | 81 +++----------------------------------------- src/lib.rs | 4 +-- 3 files changed, 13 insertions(+), 97 deletions(-) diff --git a/src/image.rs b/src/image.rs index 3b9aaea..468d4e5 100644 --- a/src/image.rs +++ b/src/image.rs @@ -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. } @@ -24,24 +28,13 @@ impl ImageFormat { #[must_use] pub fn size_in_bytes(self, width: u32, height: u32) -> Option { 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))] @@ -71,8 +64,6 @@ pub struct ImageData { pub data: Blob, /// 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. diff --git a/src/impl_bytemuck.rs b/src/impl_bytemuck.rs index e914669..9e82b04 100644 --- a/src/impl_bytemuck.rs +++ b/src/impl_bytemuck.rs @@ -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 {} @@ -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. @@ -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; @@ -191,14 +166,6 @@ mod tests { assert_eq!(Ok(&Fill::EvenOdd), try_from_bytes::(valid_one)); assert!(try_from_bytes::(invalid).is_err()); - assert_eq!( - Ok(&ImageAlphaType::Alpha), - try_from_bytes::(valid_zero) - ); - assert_eq!( - Ok(&ImageAlphaType::AlphaPremultiplied), - try_from_bytes::(valid_one) - ); assert!(try_from_bytes::(invalid).is_err()); assert_eq!( @@ -206,7 +173,7 @@ mod tests { try_from_bytes::(valid_zero) ); assert_eq!( - Ok(&ImageFormat::Bgra8), + Ok(&ImageFormat::PremulRgba8), try_from_bytes::(valid_one) ); assert!(try_from_bytes::(invalid).is_err()); @@ -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); @@ -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); @@ -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; @@ -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. diff --git a/src/lib.rs b/src/lib.rs index ec022f8..710c908 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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`].