Skip to main content

vello_cpu/fine/lowp/
gradient.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::peniko;
5use core::slice::ChunksExact;
6use vello_common::encode::EncodedGradient;
7use vello_common::fearless_simd::*;
8
9/// An accelerated gradient painter for u8.
10///
11/// Assumes that the gradient has no undefined positions.
12#[derive(Debug)]
13pub(crate) struct GradientPainter<'a, S: Simd> {
14    gradient: &'a EncodedGradient,
15    lut: &'a [[u8; 4]],
16    t_vals: ChunksExact<'a, f32>,
17    scale_factor: f32x16<S>,
18    simd: S,
19}
20
21impl<'a, S: Simd> GradientPainter<'a, S> {
22    pub(crate) fn new(simd: S, gradient: &'a EncodedGradient, t_vals: &'a [f32]) -> Self {
23        let lut = gradient.u8_lut(simd);
24        let scale_factor = f32x16::splat(simd, lut.scale_factor());
25
26        Self {
27            gradient,
28            scale_factor,
29            lut: lut.lut(),
30            t_vals: t_vals.chunks_exact(16),
31            simd,
32        }
33    }
34}
35
36impl<S: Simd> Iterator for GradientPainter<'_, S> {
37    type Item = u8x64<S>;
38
39    #[inline(always)]
40    fn next(&mut self) -> Option<Self::Item> {
41        let extend = self.gradient.extend;
42        let pos = f32x16::from_slice(self.simd, self.t_vals.next()?);
43        let t_vals = apply_extend(pos, extend);
44        let indices = (t_vals * self.scale_factor).to_int::<u32x16<S>>();
45
46        let mut vals = [0_u8; 64];
47        for (val, idx) in vals.chunks_exact_mut(4).zip(*indices) {
48            val.copy_from_slice(&self.lut[idx as usize]);
49        }
50
51        Some(u8x64::from_slice(self.simd, &vals))
52    }
53}
54
55impl<S: Simd> crate::fine::Painter for GradientPainter<'_, S> {
56    fn paint_u8(&mut self, buf: &mut [u8]) {
57        self.simd.vectorize(
58            #[inline(always)]
59            || {
60                for chunk in buf.chunks_exact_mut(64) {
61                    chunk.copy_from_slice(self.next().unwrap().as_slice());
62                }
63            },
64        );
65    }
66
67    fn paint_f32(&mut self, _: &mut [f32]) {
68        unimplemented!()
69    }
70}
71
72// TODO: Maybe delete this method and use `apply_extend` from highp by splitting into two f32x8.
73#[inline(always)]
74pub(crate) fn apply_extend<S: Simd>(val: f32x16<S>, extend: peniko::Extend) -> f32x16<S> {
75    match extend {
76        peniko::Extend::Pad => val.max(0.0).min(1.0),
77        peniko::Extend::Repeat => (val - val.floor()).fract(),
78        // See <https://github.com/google/skia/blob/220738774f7a0ce4a6c7bd17519a336e5e5dea5b/src/opts/SkRasterPipeline_opts.h#L6472-L6475>
79        peniko::Extend::Reflect => ((val - 1.0) - 2.0 * ((val - 1.0) * 0.5).floor() - 1.0)
80            .abs()
81            .max(0.0)
82            .min(1.0),
83    }
84}