atmos/os_lib/webp/
alpha_blending.rs1const fn channel_shift(i: u32) -> u32 {
6 i * 8
7}
8
9fn blend_channel_nonpremult(
12 src: u32,
13 src_a: u8,
14 dst: u32,
15 dst_a: u8,
16 scale: u32,
17 shift: u32,
18) -> u8 {
19 let src_channel = ((src >> shift) & 0xff) as u8;
20 let dst_channel = ((dst >> shift) & 0xff) as u8;
21 let blend_unscaled =
22 (u32::from(src_channel) * u32::from(src_a)) + (u32::from(dst_channel) * u32::from(dst_a));
23 debug_assert!(u64::from(blend_unscaled) < (1u64 << 32) / u64::from(scale));
24 ((blend_unscaled * scale) >> channel_shift(3)) as u8
25}
26
27fn blend_pixel_nonpremult(src: u32, dst: u32) -> u32 {
29 let src_a = ((src >> channel_shift(3)) & 0xff) as u8;
30
31 if src_a == 0 {
32 dst
33 } else {
34 let dst_a = ((dst >> channel_shift(3)) & 0xff) as u8;
35 let dst_factor_a = div_by_255(u32::from(dst_a) * (255 - u32::from(src_a)));
40 let blend_a = u32::from(src_a) + dst_factor_a;
41 let scale = (1u32 << 24) / blend_a;
42
43 let blend_r =
44 blend_channel_nonpremult(src, src_a, dst, dst_factor_a as u8, scale, channel_shift(0));
45 let blend_g =
46 blend_channel_nonpremult(src, src_a, dst, dst_factor_a as u8, scale, channel_shift(1));
47 let blend_b =
48 blend_channel_nonpremult(src, src_a, dst, dst_factor_a as u8, scale, channel_shift(2));
49 debug_assert!(u32::from(src_a) + dst_factor_a < 256);
50
51 (u32::from(blend_r) << channel_shift(0))
52 | (u32::from(blend_g) << channel_shift(1))
53 | (u32::from(blend_b) << channel_shift(2))
54 | (blend_a << channel_shift(3))
55 }
56}
57
58pub(crate) fn do_alpha_blending(buffer: [u8; 4], canvas: [u8; 4]) -> [u8; 4] {
59 blend_pixel_nonpremult(u32::from_le_bytes(buffer), u32::from_le_bytes(canvas)).to_le_bytes()
64}
65
66#[inline]
73const fn div_by_255(v: u32) -> u32 {
74 (((v + 0x80) >> 8) + v + 0x80) >> 8
75}
76
77