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 | |
7 | use crate::{BlendMode, Color, LengthU32, Paint, PixmapRef, PremultipliedColorU8, Shader}; |
8 | use crate::{ALPHA_U8_OPAQUE, ALPHA_U8_TRANSPARENT}; |
9 | |
10 | use crate::alpha_runs::AlphaRun; |
11 | use crate::blitter::{Blitter, Mask}; |
12 | use crate::color::AlphaU8; |
13 | use crate::geom::ScreenIntRect; |
14 | use crate::mask::SubMaskRef; |
15 | use crate::math::LENGTH_U32_ONE; |
16 | use crate::pipeline::{self, RasterPipeline, RasterPipelineBuilder}; |
17 | use crate::pixmap::SubPixmapMut; |
18 | |
19 | pub 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 | |
30 | impl<'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 | |
230 | impl 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 | } |
358 | |