1use crate::fine::macros::{f32x16_painter, u8x16_painter};
5use crate::fine::{PosExt, Splat4thExt, u8_to_f32};
6use crate::kurbo::Point;
7use crate::peniko::ImageQuality;
8use vello_common::encode::EncodedImage;
9use vello_common::fearless_simd::{Bytes, Simd, SimdBase, SimdFloat, f32x4, f32x16, u8x16, u32x4};
10use vello_common::pixmap::Pixmap;
11use vello_common::simd::element_wise_splat;
12
13#[derive(Debug)]
15pub(crate) struct PlainNNImagePainter<'a, S: Simd> {
16 data: ImagePainterData<'a, S>,
17 y_positions: f32x4<S>,
18 cur_x_pos: f32x4<S>,
19 advance: f32,
20 simd: S,
21}
22
23impl<'a, S: Simd> PlainNNImagePainter<'a, S> {
24 pub(crate) fn new(
25 simd: S,
26 image: &'a EncodedImage,
27 pixmap: &'a Pixmap,
28 start_x: u16,
29 start_y: u16,
30 ) -> Self {
31 let data = ImagePainterData::new(simd, image, pixmap, start_x, start_y);
32
33 let y_positions = extend(
34 simd,
35 f32x4::splat_pos(
36 simd,
37 data.cur_pos.y as f32,
38 data.x_advances.1,
39 data.y_advances.1,
40 ),
41 image.sampler.y_extend,
42 data.height,
43 data.height_inv,
44 );
45
46 let cur_x_pos = f32x4::splat_pos(
47 simd,
48 data.cur_pos.x as f32,
49 data.x_advances.0,
50 data.y_advances.0,
51 );
52
53 Self {
54 data,
55 advance: image.x_advance.x as f32,
56 y_positions,
57 cur_x_pos,
58 simd,
59 }
60 }
61}
62
63impl<S: Simd> Iterator for PlainNNImagePainter<'_, S> {
64 type Item = u8x16<S>;
65
66 #[inline(always)]
67 fn next(&mut self) -> Option<Self::Item> {
68 let x_pos = extend(
69 self.simd,
70 self.cur_x_pos,
71 self.data.image.sampler.x_extend,
72 self.data.width,
73 self.data.width_inv,
74 );
75
76 let samples = sample(self.simd, &self.data, x_pos, self.y_positions);
77
78 self.cur_x_pos += self.advance;
79
80 Some(samples)
81 }
82}
83
84u8x16_painter!(PlainNNImagePainter<'_, S>);
85
86#[derive(Debug)]
88pub(crate) struct NNImagePainter<'a, S: Simd> {
89 data: ImagePainterData<'a, S>,
90 simd: S,
91}
92
93impl<'a, S: Simd> NNImagePainter<'a, S> {
94 pub(crate) fn new(
95 simd: S,
96 image: &'a EncodedImage,
97 pixmap: &'a Pixmap,
98 start_x: u16,
99 start_y: u16,
100 ) -> Self {
101 let data = ImagePainterData::new(simd, image, pixmap, start_x, start_y);
102
103 Self { data, simd }
104 }
105}
106
107impl<S: Simd> Iterator for NNImagePainter<'_, S> {
108 type Item = u8x16<S>;
109
110 fn next(&mut self) -> Option<Self::Item> {
111 let x_positions = extend(
112 self.simd,
113 f32x4::splat_pos(
114 self.simd,
115 self.data.cur_pos.x as f32,
116 self.data.x_advances.0,
117 self.data.y_advances.0,
118 ),
119 self.data.image.sampler.x_extend,
120 self.data.width,
121 self.data.width_inv,
122 );
123
124 let y_positions = extend(
125 self.simd,
126 f32x4::splat_pos(
127 self.simd,
128 self.data.cur_pos.y as f32,
129 self.data.x_advances.1,
130 self.data.y_advances.1,
131 ),
132 self.data.image.sampler.y_extend,
133 self.data.height,
134 self.data.height_inv,
135 );
136
137 let samples = sample(self.simd, &self.data, x_positions, y_positions);
138
139 self.data.cur_pos += self.data.image.x_advance;
140
141 Some(samples)
142 }
143}
144
145u8x16_painter!(NNImagePainter<'_, S>);
146
147#[derive(Debug)]
149pub(crate) struct FilteredImagePainter<'a, S: Simd> {
150 data: ImagePainterData<'a, S>,
151 simd: S,
152}
153
154impl<'a, S: Simd> FilteredImagePainter<'a, S> {
155 pub(crate) fn new(
156 simd: S,
157 image: &'a EncodedImage,
158 pixmap: &'a Pixmap,
159 start_x: u16,
160 start_y: u16,
161 ) -> Self {
162 let data = ImagePainterData::new(simd, image, pixmap, start_x, start_y);
163
164 Self { data, simd }
165 }
166}
167
168impl<S: Simd> Iterator for FilteredImagePainter<'_, S> {
169 type Item = f32x16<S>;
170
171 fn next(&mut self) -> Option<Self::Item> {
172 let x_positions = f32x4::splat_pos(
173 self.simd,
174 self.data.cur_pos.x as f32,
175 self.data.x_advances.0,
176 self.data.y_advances.0,
177 );
178
179 let y_positions = f32x4::splat_pos(
180 self.simd,
181 self.data.cur_pos.y as f32,
182 self.data.x_advances.1,
183 self.data.y_advances.1,
184 );
185
186 #[inline(always)]
199 fn fract<S: Simd>(val: f32x4<S>) -> f32x4<S> {
200 val - val.floor()
201 }
202
203 let x_fract = fract(x_positions + 0.5);
204 let y_fract = fract(y_positions + 0.5);
205
206 let mut interpolated_color = f32x16::splat(self.simd, 0.0);
207
208 let sample = |x_pos: f32x4<S>, y_pos: f32x4<S>| {
209 u8_to_f32(sample(self.simd, &self.data, x_pos, y_pos))
210 };
211
212 macro_rules! extend_x {
213 ($idx:expr,$offsets:expr) => {
214 extend(
215 self.simd,
216 x_positions + $offsets[$idx],
217 self.data.image.sampler.y_extend,
218 self.data.width,
219 self.data.width_inv,
220 )
221 };
222 }
223
224 macro_rules! extend_y {
225 ($idx:expr,$offsets:expr) => {
226 extend(
227 self.simd,
228 y_positions + $offsets[$idx],
229 self.data.image.sampler.y_extend,
230 self.data.height,
231 self.data.height_inv,
232 )
233 };
234 }
235
236 match self.data.image.sampler.quality {
237 ImageQuality::Low => unreachable!(),
238 ImageQuality::Medium => {
239 let cx = [1.0 - x_fract, x_fract];
241 let cy = [1.0 - y_fract, y_fract];
242
243 const OFFSETS: [f32; 2] = [-0.5, 0.5];
248
249 let x_positions = [extend_x!(0, OFFSETS), extend_x!(1, OFFSETS)];
250
251 let y_positions = [extend_y!(0, OFFSETS), extend_y!(1, OFFSETS)];
252
253 for x_idx in 0..2 {
255 let x_positions = x_positions[x_idx];
256
257 for y_idx in 0..2 {
258 let y_positions = y_positions[y_idx];
259 let color_sample = sample(x_positions, y_positions);
260 let w = element_wise_splat(self.simd, cx[x_idx] * cy[y_idx]);
261
262 interpolated_color = w.madd(color_sample, interpolated_color);
263 }
264 }
265
266 interpolated_color *= f32x16::splat(self.simd, 1.0 / 255.0);
267 }
268 ImageQuality::High => {
269 let cx = weights(self.simd, x_fract);
271 let cy = weights(self.simd, y_fract);
272
273 const OFFSETS: [f32; 4] = [-1.5, -0.5, 0.5, 1.5];
274
275 let x_positions = [
276 extend_x!(0, OFFSETS),
277 extend_x!(1, OFFSETS),
278 extend_x!(2, OFFSETS),
279 extend_x!(3, OFFSETS),
280 ];
281
282 let y_positions = [
283 extend_y!(0, OFFSETS),
284 extend_y!(1, OFFSETS),
285 extend_y!(2, OFFSETS),
286 extend_y!(3, OFFSETS),
287 ];
288
289 for x_idx in 0..4 {
294 let x_positions = x_positions[x_idx];
295 for y_idx in 0..4 {
296 let y_positions = y_positions[y_idx];
297
298 let color_sample = sample(x_positions, y_positions);
299 let w = element_wise_splat(self.simd, cx[x_idx] * cy[y_idx]);
300
301 interpolated_color = w.madd(color_sample, interpolated_color);
302 }
303 }
304
305 interpolated_color *= f32x16::splat(self.simd, 1.0 / 255.0);
306
307 let alphas = interpolated_color.splat_4th();
308
309 interpolated_color = interpolated_color
316 .min(f32x16::splat(self.simd, 1.0))
317 .max(f32x16::splat(self.simd, 0.0))
318 .min(alphas);
319 }
320 }
321
322 self.data.cur_pos += self.data.image.x_advance;
323
324 Some(interpolated_color)
325 }
326}
327
328f32x16_painter!(FilteredImagePainter<'_, S>);
329
330#[derive(Debug)]
332pub(crate) struct ImagePainterData<'a, S: Simd> {
333 pub(crate) cur_pos: Point,
334 pub(crate) image: &'a EncodedImage,
335 pub(crate) pixmap: &'a Pixmap,
336 pub(crate) x_advances: (f32, f32),
337 pub(crate) y_advances: (f32, f32),
338 pub(crate) height: f32x4<S>,
339 pub(crate) height_inv: f32x4<S>,
340 pub(crate) width: f32x4<S>,
341 pub(crate) width_inv: f32x4<S>,
342 pub(crate) width_u32: u32x4<S>,
343}
344
345impl<'a, S: Simd> ImagePainterData<'a, S> {
346 pub(crate) fn new(
347 simd: S,
348 image: &'a EncodedImage,
349 pixmap: &'a Pixmap,
350 start_x: u16,
351 start_y: u16,
352 ) -> Self {
353 let width = pixmap.width() as f32;
354 let height = pixmap.height() as f32;
355 let start_pos = image.transform * Point::new(f64::from(start_x), f64::from(start_y));
356
357 let width_inv = f32x4::splat(simd, 1.0 / width);
358 let height_inv = f32x4::splat(simd, 1.0 / height);
359 let width = f32x4::splat(simd, width);
360 let width_u32 = u32x4::splat(simd, pixmap.width() as u32);
361 let height = f32x4::splat(simd, height);
362
363 let x_advances = (image.x_advance.x as f32, image.x_advance.y as f32);
364 let y_advances = (image.y_advance.x as f32, image.y_advance.y as f32);
365
366 Self {
367 cur_pos: start_pos,
368 pixmap,
369 x_advances,
370 y_advances,
371 image,
372 width,
373 height,
374 width_u32,
375 width_inv,
376 height_inv,
377 }
378 }
379}
380
381#[inline(always)]
382pub(crate) fn sample<S: Simd>(
383 simd: S,
384 data: &ImagePainterData<'_, S>,
385 x_positions: f32x4<S>,
386 y_positions: f32x4<S>,
387) -> u8x16<S> {
388 let idx = x_positions.cvt_u32() + y_positions.cvt_u32() * data.width_u32;
389
390 u32x4::from_slice(
391 simd,
392 &[
393 data.pixmap.sample_idx(idx[0]).to_u32(),
394 data.pixmap.sample_idx(idx[1]).to_u32(),
395 data.pixmap.sample_idx(idx[2]).to_u32(),
396 data.pixmap.sample_idx(idx[3]).to_u32(),
397 ],
398 )
399 .reinterpret_u8()
400}
401
402#[inline(always)]
403pub(crate) fn extend<S: Simd>(
404 simd: S,
405 val: f32x4<S>,
406 extend: crate::peniko::Extend,
407 max: f32x4<S>,
408 inv_max: f32x4<S>,
409) -> f32x4<S> {
410 let bias = f32x4::splat(simd, 0.01);
413
414 match extend {
415 crate::peniko::Extend::Pad => val.min(max - bias).max(f32x4::splat(simd, 0.0)),
418 crate::peniko::Extend::Repeat => {
419 max.madd(-(val * inv_max).floor(), val)
421 .min(max - 1.0)
423 }
424 crate::peniko::Extend::Reflect => {
426 let u = val
427 - (val * inv_max * f32x4::splat(simd, 0.5)).floor() * f32x4::splat(simd, 2.0) * max;
428 let s = (u * inv_max).floor();
429 let m = u - f32x4::splat(simd, 2.0) * s * (u - max);
430
431 let bias_in_ulps = s.trunc();
432
433 let m_bits = u32x4::from_bytes(m.to_bytes());
434 let biased_bits = m_bits - bias_in_ulps.cvt_u32();
439 f32x4::from_bytes(biased_bits.to_bytes())
440 .min(max - 1.0)
442 }
443 }
444}
445
446fn weights<S: Simd>(simd: S, fract: f32x4<S>) -> [f32x4<S>; 4] {
448 simd.vectorize(
449 #[inline(always)]
450 || {
451 let s = fract.simd;
452 const MF: [[f32; 4]; 4] = mf_resampler();
453
454 [
455 single_weight(
456 fract,
457 f32x4::splat(s, MF[0][0]),
458 f32x4::splat(s, MF[0][1]),
459 f32x4::splat(s, MF[0][2]),
460 f32x4::splat(s, MF[0][3]),
461 ),
462 single_weight(
463 fract,
464 f32x4::splat(s, MF[1][0]),
465 f32x4::splat(s, MF[1][1]),
466 f32x4::splat(s, MF[1][2]),
467 f32x4::splat(s, MF[1][3]),
468 ),
469 single_weight(
470 fract,
471 f32x4::splat(s, MF[2][0]),
472 f32x4::splat(s, MF[2][1]),
473 f32x4::splat(s, MF[2][2]),
474 f32x4::splat(s, MF[2][3]),
475 ),
476 single_weight(
477 fract,
478 f32x4::splat(s, MF[3][0]),
479 f32x4::splat(s, MF[3][1]),
480 f32x4::splat(s, MF[3][2]),
481 f32x4::splat(s, MF[3][3]),
482 ),
483 ]
484 },
485 )
486}
487
488#[inline(always)]
490fn single_weight<S: Simd>(
491 t: f32x4<S>,
492 a: f32x4<S>,
493 b: f32x4<S>,
494 c: f32x4<S>,
495 d: f32x4<S>,
496) -> f32x4<S> {
497 t.madd(d, c).madd(t, b).madd(t, a)
498}
499
500const fn mf_resampler() -> [[f32; 4]; 4] {
502 cubic_resampler(1.0 / 3.0, 1.0 / 3.0)
503}
504
505const fn cubic_resampler(b: f32, c: f32) -> [[f32; 4]; 4] {
516 [
517 [
518 (1.0 / 6.0) * b,
519 -(3.0 / 6.0) * b - c,
520 (3.0 / 6.0) * b + 2.0 * c,
521 -(1.0 / 6.0) * b - c,
522 ],
523 [
524 1.0 - (2.0 / 6.0) * b,
525 0.0,
526 -3.0 + (12.0 / 6.0) * b + c,
527 2.0 - (9.0 / 6.0) * b - c,
528 ],
529 [
530 (1.0 / 6.0) * b,
531 (3.0 / 6.0) * b + c,
532 3.0 - (15.0 / 6.0) * b - 2.0 * c,
533 -2.0 + (9.0 / 6.0) * b + c,
534 ],
535 [0.0, 0.0, -c, (1.0 / 6.0) * b + c],
536 ]
537}
538
539#[cfg(test)]
540mod tests {
541 use super::*;
542 use vello_common::fearless_simd::Fallback;
543
544 #[test]
545 fn extend_overflow() {
546 let simd = Fallback::new();
547 let max = f32x4::splat(simd, 128.0);
548 let max_inv = 1.0 / max;
549
550 let num = f32x4::splat(simd, 127.00001);
551 let res = extend(simd, num, crate::peniko::Extend::Repeat, max, max_inv);
552
553 assert!(res[0] <= 127.0);
554 }
555}