Skip to main content

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(paint.colorspace, &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(stage) = paint.colorspace.expand_dest_stage() {
94                    p.push(stage);
95                }
96                if let Some(blend_stage) = blend_mode.to_stage() {
97                    p.push(blend_stage);
98                }
99            } else {
100                p.push(pipeline::Stage::LoadDestination);
101                if let Some(stage) = paint.colorspace.expand_dest_stage() {
102                    p.push(stage);
103                }
104                if let Some(blend_stage) = blend_mode.to_stage() {
105                    p.push(blend_stage);
106                }
107
108                p.push(pipeline::Stage::Lerp1Float);
109            }
110
111            if let Some(stage) = paint.colorspace.compress_stage() {
112                p.push(stage);
113            }
114            p.push(pipeline::Stage::Store);
115
116            p.compile()
117        };
118
119        let blit_rect_rp = {
120            let mut p = RasterPipelineBuilder::new();
121            p.set_force_hq_pipeline(paint.force_hq_pipeline);
122            if !paint.shader.push_stages(paint.colorspace, &mut p) {
123                return None;
124            }
125
126            if mask.is_some() {
127                p.push(pipeline::Stage::MaskU8);
128            }
129
130            if blend_mode == BlendMode::SourceOver && mask.is_none() {
131                if let Some(stage) = paint.colorspace.compress_stage() {
132                    p.push(stage);
133                }
134                // TODO: ignore when dither_rate is non-zero
135                p.push(pipeline::Stage::SourceOverRgba);
136            } else {
137                if blend_mode != BlendMode::Source {
138                    p.push(pipeline::Stage::LoadDestination);
139                    if let Some(blend_stage) = blend_mode.to_stage() {
140                        if let Some(stage) = paint.colorspace.expand_dest_stage() {
141                            p.push(stage);
142                        }
143                        p.push(blend_stage);
144                    }
145                }
146
147                if let Some(stage) = paint.colorspace.compress_stage() {
148                    p.push(stage);
149                }
150                p.push(pipeline::Stage::Store);
151            }
152
153            p.compile()
154        };
155
156        let blit_mask_rp = {
157            let mut p = RasterPipelineBuilder::new();
158            p.set_force_hq_pipeline(paint.force_hq_pipeline);
159            if !paint.shader.push_stages(paint.colorspace, &mut p) {
160                return None;
161            }
162
163            if mask.is_some() {
164                p.push(pipeline::Stage::MaskU8);
165            }
166
167            if blend_mode.should_pre_scale_coverage() {
168                p.push(pipeline::Stage::ScaleU8);
169                p.push(pipeline::Stage::LoadDestination);
170                if let Some(stage) = paint.colorspace.expand_dest_stage() {
171                    p.push(stage);
172                }
173                if let Some(blend_stage) = blend_mode.to_stage() {
174                    p.push(blend_stage);
175                }
176            } else {
177                p.push(pipeline::Stage::LoadDestination);
178                if let Some(stage) = paint.colorspace.expand_dest_stage() {
179                    p.push(stage);
180                }
181                if let Some(blend_stage) = blend_mode.to_stage() {
182                    p.push(blend_stage);
183                }
184
185                p.push(pipeline::Stage::LerpU8);
186            }
187
188            if let Some(stage) = paint.colorspace.compress_stage() {
189                p.push(stage);
190            }
191            p.push(pipeline::Stage::Store);
192
193            p.compile()
194        };
195
196        let pixmap_src = match paint.shader {
197            Shader::Pattern(ref patt) => patt.pixmap,
198            // Just a dummy one.
199            _ => PixmapRef::from_bytes(&[0, 0, 0, 0], 1, 1).unwrap(),
200        };
201
202        Some(RasterPipelineBlitter {
203            mask,
204            pixmap_src,
205            pixmap,
206            memset2d_color,
207            blit_anti_h_rp,
208            blit_rect_rp,
209            blit_mask_rp,
210            is_mask: false,
211        })
212    }
213
214    pub fn new_mask(pixmap: &'a mut SubPixmapMut<'b>) -> Option<Self> {
215        let color = Color::WHITE.premultiply();
216
217        let memset2d_color = Some(color.to_color_u8());
218
219        let blit_anti_h_rp = {
220            let mut p = RasterPipelineBuilder::new();
221            p.push_uniform_color(color);
222            p.push(pipeline::Stage::LoadDestinationU8);
223            p.push(pipeline::Stage::Lerp1Float);
224            p.push(pipeline::Stage::StoreU8);
225            p.compile()
226        };
227
228        let blit_rect_rp = {
229            let mut p = RasterPipelineBuilder::new();
230            p.push_uniform_color(color);
231            p.push(pipeline::Stage::StoreU8);
232            p.compile()
233        };
234
235        let blit_mask_rp = {
236            let mut p = RasterPipelineBuilder::new();
237            p.push_uniform_color(color);
238            p.push(pipeline::Stage::LoadDestinationU8);
239            p.push(pipeline::Stage::LerpU8);
240            p.push(pipeline::Stage::StoreU8);
241            p.compile()
242        };
243
244        Some(RasterPipelineBlitter {
245            mask: None,
246            pixmap_src: PixmapRef::from_bytes(&[0, 0, 0, 0], 1, 1).unwrap(),
247            pixmap,
248            memset2d_color,
249            blit_anti_h_rp,
250            blit_rect_rp,
251            blit_mask_rp,
252            is_mask: true,
253        })
254    }
255}
256
257impl Blitter for RasterPipelineBlitter<'_, '_> {
258    fn blit_h(&mut self, x: u32, y: u32, width: LengthU32) {
259        let r = ScreenIntRect::from_xywh_safe(x, y, width, LENGTH_U32_ONE);
260        self.blit_rect(&r);
261    }
262
263    fn blit_anti_h(&mut self, mut x: u32, y: u32, aa: &mut [AlphaU8], runs: &mut [AlphaRun]) {
264        let mask_ctx = self.mask.map(|c| c.mask_ctx()).unwrap_or_default();
265
266        let mut aa_offset = 0;
267        let mut run_offset = 0;
268        let mut run_opt = runs[0];
269        while let Some(run) = run_opt {
270            let width = LengthU32::from(run);
271
272            match aa[aa_offset] {
273                ALPHA_U8_TRANSPARENT => {}
274                ALPHA_U8_OPAQUE => {
275                    self.blit_h(x, y, width);
276                }
277                alpha => {
278                    self.blit_anti_h_rp.ctx.current_coverage = alpha as f32 * (1.0 / 255.0);
279
280                    let rect = ScreenIntRect::from_xywh_safe(x, y, width, LENGTH_U32_ONE);
281                    self.blit_anti_h_rp.run(
282                        &rect,
283                        pipeline::AAMaskCtx::default(),
284                        mask_ctx,
285                        self.pixmap_src,
286                        self.pixmap,
287                    );
288                }
289            }
290
291            x += width.get();
292            run_offset += usize::from(run.get());
293            aa_offset += usize::from(run.get());
294            run_opt = runs[run_offset];
295        }
296    }
297
298    fn blit_v(&mut self, x: u32, y: u32, height: LengthU32, alpha: AlphaU8) {
299        let bounds = ScreenIntRect::from_xywh_safe(x, y, LENGTH_U32_ONE, height);
300
301        let mask = Mask {
302            image: [alpha, alpha],
303            bounds,
304            row_bytes: 0, // so we reuse the 1 "row" for all of height
305        };
306
307        self.blit_mask(&mask, &bounds);
308    }
309
310    fn blit_anti_h2(&mut self, x: u32, y: u32, alpha0: AlphaU8, alpha1: AlphaU8) {
311        let bounds = ScreenIntRect::from_xywh(x, y, 2, 1).unwrap();
312
313        let mask = Mask {
314            image: [alpha0, alpha1],
315            bounds,
316            row_bytes: 2,
317        };
318
319        self.blit_mask(&mask, &bounds);
320    }
321
322    fn blit_anti_v2(&mut self, x: u32, y: u32, alpha0: AlphaU8, alpha1: AlphaU8) {
323        let bounds = ScreenIntRect::from_xywh(x, y, 1, 2).unwrap();
324
325        let mask = Mask {
326            image: [alpha0, alpha1],
327            bounds,
328            row_bytes: 1,
329        };
330
331        self.blit_mask(&mask, &bounds);
332    }
333
334    fn blit_rect(&mut self, rect: &ScreenIntRect) {
335        if let Some(c) = self.memset2d_color {
336            if self.is_mask {
337                for y in 0..rect.height() {
338                    let start = self
339                        .pixmap
340                        .offset(rect.x() as usize, (rect.y() + y) as usize);
341                    let end = start + rect.width() as usize;
342                    self.pixmap.data[start..end]
343                        .iter_mut()
344                        .for_each(|p| *p = c.alpha());
345                }
346            } else {
347                for y in 0..rect.height() {
348                    let start = self
349                        .pixmap
350                        .offset(rect.x() as usize, (rect.y() + y) as usize);
351                    let end = start + rect.width() as usize;
352                    self.pixmap.pixels_mut()[start..end]
353                        .iter_mut()
354                        .for_each(|p| *p = c);
355                }
356            }
357
358            return;
359        }
360
361        let mask_ctx = self.mask.map(|c| c.mask_ctx()).unwrap_or_default();
362
363        self.blit_rect_rp.run(
364            rect,
365            pipeline::AAMaskCtx::default(),
366            mask_ctx,
367            self.pixmap_src,
368            self.pixmap,
369        );
370    }
371
372    fn blit_mask(&mut self, mask: &Mask, clip: &ScreenIntRect) {
373        let aa_mask_ctx = pipeline::AAMaskCtx {
374            pixels: mask.image,
375            stride: mask.row_bytes,
376            shift: (mask.bounds.left() + mask.bounds.top() * mask.row_bytes) as usize,
377        };
378
379        let mask_ctx = self.mask.map(|c| c.mask_ctx()).unwrap_or_default();
380
381        self.blit_mask_rp
382            .run(clip, aa_mask_ctx, mask_ctx, self.pixmap_src, self.pixmap);
383    }
384}