rasterrocket_render/pipe/
simple.rs1use std::cell::RefCell;
8
9use crate::pipe::{self, PipeSrc, PipeState};
10#[cfg(all(target_arch = "x86_64", feature = "simd-avx2"))]
11use crate::simd;
12use crate::types::BlendMode;
13use color::Pixel;
14
15thread_local! {
17 static PAT_BUF: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };
18}
19
20pub(crate) fn render_span_simple<P: Pixel>(
30 pipe: &PipeState<'_>,
31 src: &PipeSrc<'_>,
32 dst_pixels: &mut [u8],
33 dst_alpha: Option<&mut [u8]>,
34 x0: i32,
35 x1: i32,
36 y: i32,
37) {
38 debug_assert_eq!(pipe.blend_mode, BlendMode::Normal);
39 debug_assert_eq!(pipe.a_input, 255);
40
41 #[expect(
42 clippy::cast_sign_loss,
43 reason = "x1 >= x0 is a precondition, so x1 - x0 + 1 >= 1 > 0"
44 )]
45 let count = (x1 - x0 + 1) as usize;
46 let ncomps = P::BYTES;
47
48 match src {
49 PipeSrc::Solid(color) => {
50 debug_assert_eq!(color.len(), ncomps, "solid color length must match ncomps");
51 let mut applied = [0u8; 8]; debug_assert!(
54 ncomps <= 8,
55 "ncomps {ncomps} > 8; only modes up to DeviceN8 supported"
56 );
57 pipe::apply_transfer_pixel(pipe, color, &mut applied[..ncomps]);
58 let applied = &applied[..ncomps];
59
60 #[cfg(all(target_arch = "x86_64", feature = "simd-avx2"))]
61 {
62 match ncomps {
63 1 => simd::blend_solid_gray8(dst_pixels, applied[0], count),
64 3 => simd::blend_solid_rgb8(
65 dst_pixels,
66 [applied[0], applied[1], applied[2]],
67 count,
68 ),
69 _ => {
70 for chunk in dst_pixels.chunks_exact_mut(ncomps) {
71 chunk.copy_from_slice(applied);
72 }
73 }
74 }
75 }
76 #[cfg(not(all(target_arch = "x86_64", feature = "simd-avx2")))]
77 {
78 for chunk in dst_pixels.chunks_exact_mut(ncomps) {
79 chunk.copy_from_slice(applied);
80 }
81 }
82 }
83 PipeSrc::Pattern(pat) => {
84 PAT_BUF.with(|cell| {
85 let mut buf = cell.borrow_mut();
86 buf.resize(count * ncomps, 0);
87 pat.fill_span(y, x0, x1, &mut buf);
88 for chunk in buf.chunks_exact_mut(ncomps) {
89 pipe::apply_transfer_in_place(pipe, chunk);
90 }
91 dst_pixels.copy_from_slice(&buf);
92 });
93 }
94 }
95
96 debug_assert_eq!(
99 pipe.overprint_mask, 0xFFFF_FFFF,
100 "simple pipe reached with overprint_mask {:#010x}; route through render_span_general",
101 pipe.overprint_mask,
102 );
103
104 if let Some(alpha) = dst_alpha {
106 debug_assert_eq!(alpha.len(), count);
107 for a in alpha.iter_mut() {
108 *a = 255;
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use crate::state::TransferSet;
117 use color::{Rgb8, TransferLut};
118
119 fn opaque_normal_pipe() -> PipeState<'static> {
120 PipeState {
121 blend_mode: BlendMode::Normal,
122 a_input: 255,
123 overprint_mask: 0xFFFF_FFFF,
124 overprint_additive: false,
125 transfer: TransferSet::identity_rgb(),
126 soft_mask: None,
127 alpha0: None,
128 knockout: false,
129 knockout_opacity: 255,
130 non_isolated_group: false,
131 }
132 }
133
134 #[test]
135 fn solid_fills_all_pixels() {
136 let pipe = opaque_normal_pipe();
137 let color = [100u8, 150, 200];
138 let src = PipeSrc::Solid(&color);
139 let count = 5usize;
140 let mut dst = vec![0u8; count * 3];
141 let mut alpha = vec![0u8; count];
142
143 render_span_simple::<Rgb8>(&pipe, &src, &mut dst, Some(&mut alpha), 0, 4, 0);
144
145 for i in 0..count {
146 assert_eq!(&dst[i * 3..i * 3 + 3], &color, "pixel {i}");
147 assert_eq!(alpha[i], 255, "alpha {i}");
148 }
149 }
150
151 #[test]
152 fn transfer_applied_to_solid() {
153 static DN: [[u8; 256]; 8] = [TransferLut::IDENTITY.0; 8];
157 let id = TransferLut::IDENTITY.as_array();
158 let inv = TransferLut::INVERTED.as_array();
159
160 let transfer = TransferSet {
161 rgb: [inv; 3],
162 gray: id,
163 cmyk: [id; 4],
164 device_n: &DN,
165 };
166
167 let pipe = PipeState {
168 blend_mode: BlendMode::Normal,
169 a_input: 255,
170 overprint_mask: 0xFFFF_FFFF,
171 overprint_additive: false,
172 transfer,
173 soft_mask: None,
174 alpha0: None,
175 knockout: false,
176 knockout_opacity: 255,
177 non_isolated_group: false,
178 };
179
180 let color = [100u8, 150, 200];
181 let src = PipeSrc::Solid(&color);
182 let mut dst = vec![0u8; 3];
183 let mut alpha = vec![0u8; 1];
184 render_span_simple::<Rgb8>(&pipe, &src, &mut dst, Some(&mut alpha), 0, 0, 0);
185
186 assert_eq!(dst[0], 255 - 100, "R transfer inverted");
187 assert_eq!(dst[1], 255 - 150, "G transfer inverted");
188 assert_eq!(dst[2], 255 - 200, "B transfer inverted");
189 }
190
191 #[test]
192 fn no_alpha_plane_works() {
193 let pipe = opaque_normal_pipe();
194 let color = [10u8, 20, 30];
195 let src = PipeSrc::Solid(&color);
196 let mut dst = vec![0u8; 3];
197 render_span_simple::<Rgb8>(&pipe, &src, &mut dst, None, 0, 0, 0);
198 assert_eq!(&dst, &[10, 20, 30]);
199 }
200}