alpha_blend/lib.rs
1//! Alpha blending and compositing in (optionally) zero-dependency Rust.
2//!
3//! ## Examples
4//!
5//! The [`RgbaBlend`] trait defines a method for blending two RGBA colors together, and the
6//! [`BlendMode`] enum provides various blending modes based on Porter-Duff coefficients; for
7//! example [`BlendMode::SourceOver`] blends the source color over the destination color using
8//! [`PorterDuff::SRC_OVER`][].
9//!
10//! [`PorterDuff::SRC_OVER`]: `crate::porter_duff::PorterDuff::SRC_OVER`
11//!
12//! ```rust
13//! use alpha_blend::{rgba::F32x4Rgba, BlendMode, RgbaBlend};
14//!
15//! let src = F32x4Rgba { r: 1.0, g: 0.0, b: 0.0, a: 0.5 }; // Semi-transparent red
16//! let dst = F32x4Rgba { r: 0.0, g: 0.0, b: 1.0, a: 1.0 }; // Opaque blue
17//! let blended = BlendMode::SourceOver.apply(src, dst);
18//!
19//! // A mixed color of red and blue with alpha blending
20//! assert_eq!(blended, F32x4Rgba { r: 0.5, g: 0.0, b: 0.5, a: 0.75 });
21//! ```
22//!
23//! ## Features
24//!
25//! By default, this crate is `no_std` compatible, and uses [`libm`] for some math operations.
26//!
27//! Either `std` or `libm` must be enabled.
28//!
29//! ### `bytemuck`
30//!
31//! Enables the `bytemuck` crate for zero-copy conversions between types.
32//!
33//! ### `libm`
34//!
35//! _This feature is enabled by default._
36//!
37//! Uses the `libm` crate for math operations.
38//!
39//! ### `libm-arch`
40//!
41//! _This feature is enabled by default._
42//!
43//! Enables the `arch` feature of `libm`.
44//!
45//! ### `std`
46//!
47//! Uses the standard library for math operations, such as `f32::round`.
48
49#![cfg_attr(not(feature = "std"), no_std)]
50
51use crate::{porter_duff::PorterDuff, rgba::Rgba};
52
53pub(crate) mod math;
54pub mod porter_duff;
55pub mod rgba;
56pub(crate) mod vec4;
57
58/// Supported blend modes by this crate.
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub enum BlendMode {
61 /// Destination pixels covered by the source pixels are cleared.
62 Clear,
63
64 /// Source pixels are copied to the destination.
65 Source,
66
67 /// Destination pixels are copied to the source.
68 Destination,
69
70 /// Source pixels are copied to the destination, ignoring the alpha channel.
71 SourceOver,
72
73 /// Destination pixels are copied to the source, ignoring the alpha channel.
74 DestinationOver,
75
76 /// Source pixels are copied to the destination, only where the destination is opaque.
77 SourceIn,
78
79 /// Destination pixels are copied to the source, only where the source is opaque.
80 DestinationIn,
81
82 /// Source pixels are copied to the destination, only where the source is opaque.
83 SourceOut,
84
85 /// Destination pixels are copied to the source, only where the destination is opaque.
86 DestinationOut,
87
88 /// Source pixels are copied to the destination, where both source and destination are opaque.
89 SourceAtop,
90
91 /// Destination pixels are copied to the source, where both source and destination are opaque.
92 DestinationAtop,
93
94 /// Source pixels are blended with the destination using the source's alpha channel.
95 Xor,
96
97 /// Source pixels are added to the destination.
98 Plus,
99}
100
101impl BlendMode {
102 /// Returns an [`RgbaBlend`] implementation for this blend mode.
103 #[must_use]
104 fn as_rgba_blend_f32(&self) -> impl RgbaBlend<Channel = f32> {
105 match self {
106 BlendMode::Clear => PorterDuff::CLEAR,
107 BlendMode::Source => PorterDuff::SRC,
108 BlendMode::Destination => PorterDuff::DST,
109 BlendMode::SourceOver => PorterDuff::SRC_OVER,
110 BlendMode::DestinationOver => PorterDuff::DST_OVER,
111 BlendMode::SourceIn => PorterDuff::SRC_IN,
112 BlendMode::DestinationIn => PorterDuff::DST_IN,
113 BlendMode::SourceOut => PorterDuff::SRC_OUT,
114 BlendMode::DestinationOut => PorterDuff::DST_OUT,
115 BlendMode::SourceAtop => PorterDuff::SRC_ATOP,
116 BlendMode::DestinationAtop => PorterDuff::DST_ATOP,
117 BlendMode::Xor => PorterDuff::XOR,
118 BlendMode::Plus => PorterDuff::PLUS,
119 }
120 }
121}
122
123impl RgbaBlend for BlendMode {
124 type Channel = f32;
125
126 fn apply(&self, src: Rgba<Self::Channel>, dst: Rgba<Self::Channel>) -> Rgba<Self::Channel> {
127 self.as_rgba_blend_f32().apply(src, dst)
128 }
129}
130
131/// Blends pixel colors using alpha compositing.
132pub trait RgbaBlend {
133 /// What type of channel this blend mode operates on.
134 type Channel: Copy;
135
136 /// Blends two colors together using this blend mode.
137 fn apply(&self, src: Rgba<Self::Channel>, dst: Rgba<Self::Channel>) -> Rgba<Self::Channel>;
138}