Skip to content
Merged
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ This release has an [MSRV] of 1.82.

- Derive `Eq` and `Hash` on `InterpolationAlphaSpace`. ([#148][] by [@sagudev][])

### Removed

- Breaking change: `Mix::Clip` has been removed; it was previously deprecated in v0.5.0. ([#124][] by [@DJMcNab][])

## [0.5.0][] (2025-10-01)

This release has an [MSRV] of 1.82.
Expand Down Expand Up @@ -182,6 +186,7 @@ This release has an [MSRV] of 1.70.
[#120]: https://github.com/linebender/peniko/pull/120
[#121]: https://github.com/linebender/peniko/pull/121
[#123]: https://github.com/linebender/peniko/pull/123
[#124]: https://github.com/linebender/peniko/pull/124
[#126]: https://github.com/linebender/peniko/pull/126
[#127]: https://github.com/linebender/peniko/pull/127
[#129]: https://github.com/linebender/peniko/pull/129
Expand Down
13 changes: 2 additions & 11 deletions src/blend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,7 @@ pub enum Mix {
///
/// ![](https://www.w3.org/TR/compositing-1/examples/luminosity.png)
Luminosity = 15,
/// `Clip` was similar to `Normal`, but was optimised for clipping (by avoiding
/// blending in areas where there was no clip path).
///
/// This optimisation is however unrelated to mixing, and so will no longer
/// be indicated with this enum.
///
/// If you were using this with Vello, you should use the (new) `push_clip_layer` function instead.
#[deprecated(note = "Use `push_clip_layer` instead.", since = "0.5.0")]
Clip = 128,
// NOTICE: If a new value is added, be sure to update the bytemuck CheckedBitPattern impl.
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the `bytemuck::Contiguous` impl.
}

/// Defines the layer composition function for a [blend operation](BlendMode).
Expand Down Expand Up @@ -159,7 +150,7 @@ pub enum Compose {
/// Allows two elements to cross fade by changing their opacities from 0 to 1 on one
/// element and 1 to 0 on the other element.
PlusLighter = 13,
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the bytemuck impl.
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the `bytemuck::Contiguous` impl.
}

/// Blend mode consisting of [color mixing](Mix) and [composition functions](Compose).
Expand Down
1 change: 1 addition & 0 deletions src/brush.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,5 @@ pub enum Extend {
Repeat = 1,
/// Extends the image by reflecting the brush.
Reflect = 2,
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the `bytemuck::Contiguous` impl.
}
5 changes: 3 additions & 2 deletions src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub enum ImageFormat {
Rgba8 = 0,
/// 32-bit BGRA with 8-bit channels.
Bgra8 = 1,
// NOTICE: If a new value is added, be sure to update the bytemuck CheckedBitPattern impl.
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the `bytemuck::Contiguous` impl.
}

impl ImageFormat {
Expand All @@ -40,6 +40,7 @@ pub enum ImageAlphaType {
Alpha = 0,
/// Image has colors with premultiplied alpha.
AlphaPremultiplied = 1,
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the `bytemuck::Contiguous` impl.
}

/// Defines the desired quality for sampling an image.
Expand All @@ -60,7 +61,7 @@ pub enum ImageQuality {
///
/// This is typically bicubic sampling.
High = 2,
// NOTICE: If a new value is added, be sure to update the bytemuck CheckedBitPattern impl.
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the `bytemuck::Contiguous` impl.
}

/// Owned shareable image resource.
Expand Down
124 changes: 87 additions & 37 deletions src/impl_bytemuck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ unsafe impl bytemuck::Contiguous for Fill {
}

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

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

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

fn is_valid_bit_pattern(bits: &u8) -> bool {
Expand All @@ -99,20 +99,20 @@ unsafe impl bytemuck::checked::CheckedBitPattern for ImageFormat {

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

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

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

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

fn is_valid_bit_pattern(bits: &u8) -> bool {
Expand All @@ -124,10 +124,10 @@ unsafe impl bytemuck::checked::CheckedBitPattern for ImageAlphaType {

// Safety: The enum is `repr(u8)`. All values are `u8` and fall within
// the min and max values.
unsafe impl bytemuck::Contiguous for ImageAlphaType {
unsafe impl bytemuck::Contiguous for ImageFormat {
type Int = u8;
const MIN_VALUE: u8 = Self::Alpha as u8;
const MAX_VALUE: u8 = Self::AlphaPremultiplied as 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.
Expand Down Expand Up @@ -165,12 +165,21 @@ unsafe impl bytemuck::Zeroable for Mix {}
unsafe impl bytemuck::checked::CheckedBitPattern for Mix {
type Bits = u8;

#[expect(deprecated, reason = "Mix::Clip is still a valid bit pattern for now.")]
fn is_valid_bit_pattern(bits: &u8) -> bool {
*bits <= Self::Luminosity as u8 || *bits == Self::Clip as u8
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 Mix {
type Int = u8;
const MIN_VALUE: u8 = Self::Normal as u8;
const MAX_VALUE: u8 = Self::Luminosity as u8;
}

#[cfg(test)]
mod tests {
use crate::{Compose, Extend, Fill, ImageAlphaType, ImageFormat, ImageQuality, Mix};
Expand Down Expand Up @@ -200,7 +209,7 @@ mod tests {
Ok(&ImageAlphaType::AlphaPremultiplied),
try_from_bytes::<ImageAlphaType>(valid_one)
);
assert!(try_from_bytes::<ImageFormat>(invalid).is_err());
assert!(try_from_bytes::<ImageAlphaType>(invalid).is_err());

assert_eq!(
Ok(&ImageFormat::Rgba8),
Expand Down Expand Up @@ -242,20 +251,29 @@ mod tests {

assert_eq!(None, Fill::from_integer(255));

let image_format_1 = ImageFormat::Rgba8;
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_format_1 = ImageFormat::Rgba8;
let image_format_2 = ImageFormat::from_integer(image_format_1.into_integer());
assert_eq!(Some(image_format_1), image_format_2);

assert_eq!(None, ImageFormat::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);

assert_eq!(None, ImageQuality::from_integer(255));

let mix_1 = Mix::Multiply;
let mix_2 = Mix::from_integer(mix_1.into_integer());
assert_eq!(Some(mix_1), mix_2);

assert_eq!(None, Mix::from_integer(255));
}

#[test]
Expand All @@ -269,12 +287,12 @@ mod tests {
let fill = Fill::zeroed();
assert_eq!(fill, Fill::NonZero);

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_format = ImageFormat::zeroed();
assert_eq!(image_format, ImageFormat::Rgba8);

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

Expand Down Expand Up @@ -324,6 +342,20 @@ 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 [`ImageFormat`] is not trivially incorrect.
const _: () = {
let mut value = 0;
Expand All @@ -338,12 +370,12 @@ mod tests {
}
};

/// Tests that the [`Contiguous`] impl for [`ImageAlphaType`] is not trivially incorrect.
/// Tests that the [`Contiguous`] impl for [`ImageQuality`] 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()) };
while value <= ImageQuality::MAX_VALUE {
// Safety: In a const context, therefore if this makes an invalid ImageQuality, that will be detected.
let it: ImageQuality = 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!();
Expand All @@ -352,7 +384,7 @@ mod tests {
}
};

/// Tests that the [`Contiguous`] impl for [`ImageQuality`] is not trivially incorrect.
/// Tests that the [`Contiguous`] impl for [`Mix`] is not trivially incorrect.
const _: () = {
let mut value = 0;
while value <= ImageQuality::MAX_VALUE {
Expand Down Expand Up @@ -426,41 +458,41 @@ mod doctests {
/// ```
const _FILL: () = {};

/// Validates that any new variants in `ImageFormat` has led to a change in the `Contiguous` impl.
/// 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::ImageFormat;
/// use peniko::ImageAlphaType;
/// const {
/// let value = ImageFormat::MAX_VALUE + 1;
/// let it: ImageFormat = unsafe { core::ptr::read((&raw const value).cast()) };
/// 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_FORMAT: () = {};
const _IMAGE_ALPHA_TYPE: () = {};

/// Validates that any new variants in `ImageAlphaType` has led to a change in the `Contiguous` impl.
/// Validates that any new variants in `ImageFormat` 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;
/// use peniko::ImageFormat;
/// const {
/// let value = ImageAlphaType::MAX_VALUE + 1;
/// let it: ImageAlphaType = unsafe { core::ptr::read((&raw const value).cast()) };
/// let value = ImageFormat::MAX_VALUE + 1;
/// let it: ImageFormat = 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: () = {};
const _IMAGE_FORMAT: () = {};

/// 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.
Expand All @@ -479,4 +511,22 @@ mod doctests {
/// }
/// ```
const _IMAGE_QUALITY: () = {};

/// Validates that any new variants in `Mix` 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::Mix;
/// const {
/// let value = Mix::MAX_VALUE + 1;
/// let it: Mix = 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 _MIX: () = {};
}
2 changes: 1 addition & 1 deletion src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub enum Fill {
/// number state can be stored in only one bit (and so the winding numbers for
/// several pixels can be packed extremely efficiently).
EvenOdd = 1,
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the bytemuck impl.
// NOTICE: If a new value is added, be sure to modify `MAX_VALUE` in the `bytemuck::Contiguous` impl.
}

/// Describes draw style-- either a [fill](Fill) or [stroke](Stroke).
Expand Down