tiny_skia/pipeline/
blitter.rs

1// Copyright 2016 Google Inc.
2// Copyright 2020 Yevhenii Reizner
3//
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7use crate::{BlendMode, Color, LengthU32, Paint, PixmapRef, PremultipliedColorU8, Shader};
8use crate::{ALPHA_U8_OPAQUE, ALPHA_U8_TRANSPARENT};
9
10use crate::alpha_runs::AlphaRun;
11use crate::blitter::{Blitter, Mask};
12use crate::color::AlphaU8;
13use crate::geom::ScreenIntRect;
14use crate::mask::SubMaskRef;
15use crate::math::LENGTH_U32_ONE;
16use crate::pipeline::{self, RasterPipeline, RasterPipelineBuilder};
17use crate::pixmap::SubPixmapMut;
18
19pub struct RasterPipelineBlitter<'a, 'b: 'a> {
20    mask: Option<SubMaskRef<'a>>,
21    pixmap_src: PixmapRef<'a>,
22    pixmap: &'a mut SubPixmapMut<'b>,
23    memset2d_color: Option<PremultipliedColorU8>,
24    blit_anti_h_rp: RasterPipeline,
25    blit_rect_rp: RasterPipeline,
26    blit_mask_rp: RasterPipeline,
27    is_mask: bool,
28}
29
30impl<'a, 'b: 'a> RasterPipelineBlitter<'a, 'b> {
31    pub fn new(
32        paint: &Paint<'a>,
33        mask: Option<SubMaskRef<'a>>,
34        pixmap: &'a mut SubPixmapMut<'b>,
35    ) -> Option<Self> {
36        // Make sure that `mask` has the same size as `pixmap`.
37        if let Some(mask) = mask {
38            if mask.size.width() != pixmap.size.width()
39                || mask.size.height() != pixmap.size.height()
40            {
41                log::warn!("Pixmap and Mask are expected to have the same size");
42                return None;
43            }
44        }
45
46        // Fast-reject.
47        // This is basically SkInterpretXfermode().
48        match paint.blend_mode {
49            // `Destination` keep the pixmap unchanged. Nothing to do here.
50            BlendMode::Destination => return None,
51            BlendMode::DestinationIn if paint.shader.is_opaque() && paint.is_solid_color() => {
52                return None
53            }
54            _ => {}
55        }
56
57        // We can strength-reduce SourceOver into Source when opaque.
58        let mut blend_mode = paint.blend_mode;
59        if paint.shader.is_opaque() && blend_mode == BlendMode::SourceOver && mask.is_none() {
60            blend_mode = BlendMode::Source;
61        }
62
63        // When we're drawing a constant color in Source mode, we can sometimes just memset.
64        let mut memset2d_color = None;
65        if paint.is_solid_color() && blend_mode == BlendMode::Source && mask.is_none() {
66            // Unlike Skia, our shader cannot be constant.
67            // Therefore there is no need to run a raster pipeline to get shader's color.
68            if let Shader::SolidColor(ref color) = paint.shader {
69                memset2d_color = Some(color.premultiply().to_color_u8());
70            }
71        };
72
73        // Clear is just a transparent color memset.
74        if blend_mode == BlendMode::Clear && !paint.anti_alias && mask.is_none() {
75            blend_mode = BlendMode::Source;
76            memset2d_color = Some(PremultipliedColorU8::TRANSPARENT);
77        }
78
79        let blit_anti_h_rp = {
80            let mut p = RasterPipelineBuilder::new();
81            p.set_force_hq_pipeline(paint.force_hq_pipeline);
82            if !paint.shader.push_stages(&mut p) {
83                return None;
84            }
85
86            if mask.is_some() {
87                p.push(pipeline::Stage::MaskU8);
88            }
89
90            if blend_mode.should_pre_scale_coverage() {
91                p.push(pipeline::Stage::Scale1Float);
92                p.push(pipeline::Stage::LoadDestination);
93                if let Some(blend_stage) = blend_mode.to_stage() {
94                    p.push(blend_stage);
95                }
96            } else {
97                p.push(pipeline::Stage::LoadDestination);
98                if let Some(blend_stage) = blend_mode.to_stage() {
99                    p.push(blend_stage);
100                }
101
102                p.push(pipeline::Stage::Lerp1Float);
103            }
104
105            p.push(pipeline::Stage::Store);
106
107            p.compile()
108        };
109
110        let blit_rect_rp = {
111            let mut p = RasterPipelineBuilder::new();
112            p.set_force_hq_pipeline(paint.force_hq_pipeline);
113            if !paint.shader.push_stages(&mut p) {
114                return None;
115            }
116
117            if mask.is_some() {
118                p.push(pipeline::Stage::MaskU8);
119            }
120
121            if blend_mode == BlendMode::SourceOver && mask.is_none() {
122                // TODO: ignore when dither_rate is non-zero
123                p.push(pipeline::Stage::SourceOverRgba);
124            } else {
125                if blend_mode != BlendMode::Source {
126                    p.push(pipeline::Stage::LoadDestination);
127                    if let Some(blend_stage) = blend_mode.to_stage() {
128                        p.push(blend_stage);
129                    }
130                }
131
132                p.push(pipeline::Stage::Store);
133            }
134
135            p.compile()
136        };
137
138        let blit_mask_rp = {
139            let mut p = RasterPipelineBuilder::new();
140            p.set_force_hq_pipeline(paint.force_hq_pipeline);
141            if !paint.shader.push_stages(&mut p) {
142                return None;
143            }
144
145            if mask.is_some() {
146                p.push(pipeline::Stage::MaskU8);
147            }
148
149            if blend_mode.should_pre_scale_coverage() {
150                p.push(pipeline::Stage::ScaleU8);
151                p.push(pipeline::Stage::LoadDestination);
152                if let Some(blend_stage) = blend_mode.to_stage() {
153                    p.push(blend_stage);
154                }
155            } else {
156                p.push(pipeline::Stage::LoadDestination);
157                if let Some(blend_stage) = blend_mode.to_stage() {
158                    p.push(blend_stage);
159                }
160
161                p.push(pipeline::Stage::LerpU8);
162            }
163
164            p.push(pipeline::Stage::Store);
165
166            p.compile()
167        };
168
169        let pixmap_src = match paint.shader {
170            Shader::Pattern(ref patt) => patt.pixmap,
171            // Just a dummy one.
172            _ => PixmapRef::from_bytes(&[0, 0, 0, 0], 1, 1).unwrap(),
173        };
174
175        Some(RasterPipelineBlitter {
176            mask,
177            pixmap_src,
178            pixmap,
179            memset2d_color,
180            blit_anti_h_rp,
181            blit_rect_rp,
182            blit_mask_rp,
183            is_mask: false,
184        })
185    }
186
187    pub fn new_mask(pixmap: &'a mut SubPixmapMut<'b>) -> Option<Self> {
188        let color = Color::WHITE.premultiply();
189
190        let memset2d_color = Some(color.to_color_u8());
191
192        let blit_anti_h_rp = {
193            let mut p = RasterPipelineBuilder::new();
194            p.push_uniform_color(color);
195            p.push(pipeline::Stage::LoadDestinationU8);
196            p.push(pipeline::Stage::Lerp1Float);
197            p.push(pipeline::Stage::StoreU8);
198            p.compile()
199        };
200
201        let blit_rect_rp = {
202            let mut p = RasterPipelineBuilder::new();
203            p.push_uniform_color(color);
204            p.push(pipeline::Stage::StoreU8);
205            p.compile()
206        };
207
208        let blit_mask_rp = {
209            let mut p = RasterPipelineBuilder::new();
210            p.push_uniform_color(color);
211            p.push(pipeline::Stage::LoadDestinationU8);
212            p.push(pipeline::Stage::LerpU8);
213            p.push(pipeline::Stage::StoreU8);
214            p.compile()
215        };
216
217        Some(RasterPipelineBlitter {
218            mask: None,
219            pixmap_src: PixmapRef::from_bytes(&[0, 0, 0, 0], 1, 1).unwrap(),
220            pixmap,
221            memset2d_color,
222            blit_anti_h_rp,
223            blit_rect_rp,
224            blit_mask_rp,
225            is_mask: true,
226        })
227    }
228}
229
230impl Blitter for RasterPipelineBlitter<'_, '_> {
231    fn blit_h(&mut self, x: u32, y: u32, width: LengthU32) {
232        let r = ScreenIntRect::from_xywh_safe(x, y, width, LENGTH_U32_ONE);
233        self.blit_rect(&r);
234    }
235
236    fn blit_anti_h(&mut self, mut x: u32, y: u32, aa: &mut [AlphaU8], runs: &mut [AlphaRun]) {
237        let mask_ctx = self.mask.map(|c| c.mask_ctx()).unwrap_or_default();
238
239        let mut aa_offset = 0;
240        let mut run_offset = 0;
241        let mut run_opt = runs[0];
242        while let Some(run) = run_opt {
243            let width = LengthU32::from(run);
244
245            match aa[aa_offset] {
246                ALPHA_U8_TRANSPARENT => {}
247                ALPHA_U8_OPAQUE => {
248                    self.blit_h(x, y, width);
249                }
250                alpha => {
251                    self.blit_anti_h_rp.ctx.current_coverage = alpha as f32 * (1.0 / 255.0);
252
253                    let rect = ScreenIntRect::from_xywh_safe(x, y, width, LENGTH_U32_ONE);
254                    self.blit_anti_h_rp.run(
255                        &rect,
256                        pipeline::AAMaskCtx::default(),
257                        mask_ctx,
258                        self.pixmap_src,
259                        self.pixmap,
260                    );
261                }
262            }
263
264            x += width.get();
265            run_offset += usize::from(run.get());
266            aa_offset += usize::from(run.get());
267            run_opt = runs[run_offset];
268        }
269    }
270
271    fn blit_v(&mut self, x: u32, y: u32, height: LengthU32, alpha: AlphaU8) {
272        let bounds = ScreenIntRect::from_xywh_safe(x, y, LENGTH_U32_ONE, height);
273
274        let mask = Mask {
275            image: [alpha, alpha],
276            bounds,
277            row_bytes: 0, // so we reuse the 1 "row" for all of height
278        };
279
280        self.blit_mask(&mask, &bounds);
281    }
282
283    fn blit_anti_h2(&mut self, x: u32, y: u32, alpha0: AlphaU8, alpha1: AlphaU8) {
284        let bounds = ScreenIntRect::from_xywh(x, y, 2, 1).unwrap();
285
286        let mask = Mask {
287            image: [alpha0, alpha1],
288            bounds,
289            row_bytes: 2,
290        };
291
292        self.blit_mask(&mask, &bounds);
293    }
294
295    fn blit_anti_v2(&mut self, x: u32, y: u32, alpha0: AlphaU8, alpha1: AlphaU8) {
296        let bounds = ScreenIntRect::from_xywh(x, y, 1, 2).unwrap();
297
298        let mask = Mask {
299            image: [alpha0, alpha1],
300            bounds,
301            row_bytes: 1,
302        };
303
304        self.blit_mask(&mask, &bounds);
305    }
306
307    fn blit_rect(&mut self, rect: &ScreenIntRect) {
308        if let Some(c) = self.memset2d_color {
309            if self.is_mask {
310                for y in 0..rect.height() {
311                    let start = self
312                        .pixmap
313                        .offset(rect.x() as usize, (rect.y() + y) as usize);
314                    let end = start + rect.width() as usize;
315                    self.pixmap.data[start..end]
316                        .iter_mut()
317                        .for_each(|p| *p = c.alpha());
318                }
319            } else {
320                for y in 0..rect.height() {
321                    let start = self
322                        .pixmap
323                        .offset(rect.x() as usize, (rect.y() + y) as usize);
324                    let end = start + rect.width() as usize;
325                    self.pixmap.pixels_mut()[start..end]
326                        .iter_mut()
327                        .for_each(|p| *p = c);
328                }
329            }
330
331            return;
332        }
333
334        let mask_ctx = self.mask.map(|c| c.mask_ctx()).unwrap_or_default();
335
336        self.blit_rect_rp.run(
337            rect,
338            pipeline::AAMaskCtx::default(),
339            mask_ctx,
340            self.pixmap_src,
341            self.pixmap,
342        );
343    }
344
345    fn blit_mask(&mut self, mask: &Mask, clip: &ScreenIntRect) {
346        let aa_mask_ctx = pipeline::AAMaskCtx {
347            pixels: mask.image,
348            stride: mask.row_bytes,
349            shift: (mask.bounds.left() + mask.bounds.top() * mask.row_bytes) as usize,
350        };
351
352        let mask_ctx = self.mask.map(|c| c.mask_ctx()).unwrap_or_default();
353
354        self.blit_mask_rp
355            .run(clip, aa_mask_ctx, mask_ctx, self.pixmap_src, self.pixmap);
356    }
357}