| 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 | |