1use alloc::vec::Vec;
48
49use arrayvec::ArrayVec;
50
51use tiny_skia_path::NormalizedF32;
52
53use crate::{Color, PremultipliedColor, PremultipliedColorU8, SpreadMode};
54use crate::{PixmapRef, Transform};
55
56pub use blitter::RasterPipelineBlitter;
57
58use crate::geom::ScreenIntRect;
59use crate::pixmap::SubPixmapMut;
60use crate::wide::u32x8;
61
62mod blitter;
63#[rustfmt::skip] mod highp;
64#[rustfmt::skip] mod lowp;
65
66const MAX_STAGES: usize = 32; #[allow(dead_code)]
69#[derive(Copy, Clone, Debug)]
70pub enum Stage {
71 MoveSourceToDestination = 0,
72 MoveDestinationToSource,
73 Clamp0,
74 ClampA,
75 Premultiply,
76 UniformColor,
77 SeedShader,
78 LoadDestination,
79 Store,
80 LoadDestinationU8,
81 StoreU8,
82 Gather,
83 LoadMaskU8,
84 MaskU8,
85 ScaleU8,
86 LerpU8,
87 Scale1Float,
88 Lerp1Float,
89 DestinationAtop,
90 DestinationIn,
91 DestinationOut,
92 DestinationOver,
93 SourceAtop,
94 SourceIn,
95 SourceOut,
96 SourceOver,
97 Clear,
98 Modulate,
99 Multiply,
100 Plus,
101 Screen,
102 Xor,
103 ColorBurn,
104 ColorDodge,
105 Darken,
106 Difference,
107 Exclusion,
108 HardLight,
109 Lighten,
110 Overlay,
111 SoftLight,
112 Hue,
113 Saturation,
114 Color,
115 Luminosity,
116 SourceOverRgba,
117 Transform,
118 Reflect,
119 Repeat,
120 Bilinear,
121 Bicubic,
122 PadX1,
123 ReflectX1,
124 RepeatX1,
125 Gradient,
126 EvenlySpaced2StopGradient,
127 XYToUnitAngle,
128 XYToRadius,
129 XYTo2PtConicalFocalOnCircle,
130 XYTo2PtConicalWellBehaved,
131 XYTo2PtConicalSmaller,
132 XYTo2PtConicalGreater,
133 XYTo2PtConicalStrip,
134 Mask2PtConicalNan,
135 Mask2PtConicalDegenerates,
136 ApplyVectorMask,
137 Alter2PtConicalCompensateFocal,
138 Alter2PtConicalUnswap,
139 NegateX,
140 ApplyConcentricScaleBias,
141 GammaExpand2,
142 GammaExpandDestination2,
143 GammaCompress2,
144 GammaExpand22,
145 GammaExpandDestination22,
146 GammaCompress22,
147 GammaExpandSrgb,
148 GammaExpandDestinationSrgb,
149 GammaCompressSrgb,
150}
151
152pub const STAGES_COUNT: usize = Stage::GammaCompressSrgb as usize + 1;
153
154impl PixmapRef<'_> {
155 #[inline(always)]
156 pub(crate) fn gather(&self, index: u32x8) -> [PremultipliedColorU8; highp::STAGE_WIDTH] {
157 let index: [u32; 8] = bytemuck::cast(index);
158 let pixels = self.pixels();
159 [
160 pixels[index[0] as usize],
161 pixels[index[1] as usize],
162 pixels[index[2] as usize],
163 pixels[index[3] as usize],
164 pixels[index[4] as usize],
165 pixels[index[5] as usize],
166 pixels[index[6] as usize],
167 pixels[index[7] as usize],
168 ]
169 }
170}
171
172impl SubPixmapMut<'_> {
173 #[inline(always)]
174 pub(crate) fn offset(&self, dx: usize, dy: usize) -> usize {
175 self.real_width * dy + dx
176 }
177
178 #[inline(always)]
179 pub(crate) fn slice_at_xy(&mut self, dx: usize, dy: usize) -> &mut [PremultipliedColorU8] {
180 let offset = self.offset(dx, dy);
181 &mut self.pixels_mut()[offset..]
182 }
183
184 #[inline(always)]
185 pub(crate) fn slice_mask_at_xy(&mut self, dx: usize, dy: usize) -> &mut [u8] {
186 let offset = self.offset(dx, dy);
187 &mut self.data[offset..]
188 }
189
190 #[inline(always)]
191 pub(crate) fn slice4_at_xy(
192 &mut self,
193 dx: usize,
194 dy: usize,
195 ) -> &mut [PremultipliedColorU8; highp::STAGE_WIDTH] {
196 arrayref::array_mut_ref!(self.pixels_mut(), self.offset(dx, dy), highp::STAGE_WIDTH)
197 }
198
199 #[inline(always)]
200 pub(crate) fn slice16_at_xy(
201 &mut self,
202 dx: usize,
203 dy: usize,
204 ) -> &mut [PremultipliedColorU8; lowp::STAGE_WIDTH] {
205 arrayref::array_mut_ref!(self.pixels_mut(), self.offset(dx, dy), lowp::STAGE_WIDTH)
206 }
207
208 #[inline(always)]
209 pub(crate) fn slice16_mask_at_xy(
210 &mut self,
211 dx: usize,
212 dy: usize,
213 ) -> &mut [u8; lowp::STAGE_WIDTH] {
214 arrayref::array_mut_ref!(self.data, self.offset(dx, dy), lowp::STAGE_WIDTH)
215 }
216}
217
218#[derive(Default, Debug)]
219pub struct AAMaskCtx {
220 pub pixels: [u8; 2],
221 pub stride: u32, pub shift: usize, }
224
225impl AAMaskCtx {
226 #[inline(always)]
227 pub fn copy_at_xy(&self, dx: usize, dy: usize, tail: usize) -> [u8; 2] {
228 let offset = (self.stride as usize * dy + dx) - self.shift;
229 match (offset, tail) {
231 (0, 1) => [self.pixels[0], 0],
232 (0, 2) => [self.pixels[0], self.pixels[1]],
233 (1, 1) => [self.pixels[1], 0],
234 _ => [0, 0], }
236 }
237}
238
239#[derive(Copy, Clone, Debug, Default)]
240pub struct MaskCtx<'a> {
241 pub data: &'a [u8],
242 pub real_width: u32,
243}
244
245impl MaskCtx<'_> {
246 #[inline(always)]
247 fn offset(&self, dx: usize, dy: usize) -> usize {
248 self.real_width as usize * dy + dx
249 }
250}
251
252#[derive(Default)]
253pub struct Context {
254 pub current_coverage: f32,
255 pub sampler: SamplerCtx,
256 pub uniform_color: UniformColorCtx,
257 pub evenly_spaced_2_stop_gradient: EvenlySpaced2StopGradientCtx,
258 pub gradient: GradientCtx,
259 pub two_point_conical_gradient: TwoPointConicalGradientCtx,
260 pub limit_x: TileCtx,
261 pub limit_y: TileCtx,
262 pub transform: Transform,
263}
264
265#[derive(Copy, Clone, Default, Debug)]
266pub struct SamplerCtx {
267 pub spread_mode: SpreadMode,
268 pub inv_width: f32,
269 pub inv_height: f32,
270}
271
272#[derive(Copy, Clone, Default, Debug)]
273pub struct UniformColorCtx {
274 pub r: f32,
275 pub g: f32,
276 pub b: f32,
277 pub a: f32,
278 pub rgba: [u16; 4], }
280
281#[derive(Copy, Clone, Default, Debug)]
284pub struct GradientColor {
285 pub r: f32,
286 pub g: f32,
287 pub b: f32,
288 pub a: f32,
289}
290
291impl GradientColor {
292 pub fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
293 GradientColor { r, g, b, a }
294 }
295}
296
297impl From<Color> for GradientColor {
298 fn from(c: Color) -> Self {
299 GradientColor {
300 r: c.red(),
301 g: c.green(),
302 b: c.blue(),
303 a: c.alpha(),
304 }
305 }
306}
307
308#[derive(Copy, Clone, Default, Debug)]
309pub struct EvenlySpaced2StopGradientCtx {
310 pub factor: GradientColor,
311 pub bias: GradientColor,
312}
313
314#[derive(Clone, Default, Debug)]
315pub struct GradientCtx {
316 pub len: usize,
321 pub factors: Vec<GradientColor>,
322 pub biases: Vec<GradientColor>,
323 pub t_values: Vec<NormalizedF32>,
324}
325
326impl GradientCtx {
327 pub fn push_const_color(&mut self, color: GradientColor) {
328 self.factors.push(GradientColor::new(0.0, 0.0, 0.0, 0.0));
329 self.biases.push(color);
330 }
331}
332
333#[derive(Copy, Clone, Default, Debug)]
334pub struct TwoPointConicalGradientCtx {
335 pub mask: u32x8,
337 pub p0: f32,
338 pub p1: f32,
339}
340
341#[derive(Copy, Clone, Default, Debug)]
342pub struct TileCtx {
343 pub scale: f32,
344 pub inv_scale: f32, }
346
347pub struct RasterPipelineBuilder {
348 stages: ArrayVec<Stage, MAX_STAGES>,
349 force_hq_pipeline: bool,
350 pub ctx: Context,
351}
352
353impl RasterPipelineBuilder {
354 pub fn new() -> Self {
355 RasterPipelineBuilder {
356 stages: ArrayVec::new(),
357 force_hq_pipeline: false,
358 ctx: Context::default(),
359 }
360 }
361
362 pub fn set_force_hq_pipeline(&mut self, hq: bool) {
363 self.force_hq_pipeline = hq;
364 }
365
366 pub fn push(&mut self, stage: Stage) {
367 self.stages.push(stage);
368 }
369
370 pub fn push_transform(&mut self, ts: Transform) {
371 if ts.is_finite() && !ts.is_identity() {
372 self.stages.push(Stage::Transform);
373 self.ctx.transform = ts;
374 }
375 }
376
377 pub fn push_uniform_color(&mut self, c: PremultipliedColor) {
378 let r = c.red();
379 let g = c.green();
380 let b = c.blue();
381 let a = c.alpha();
382 let rgba = [
383 (r * 255.0 + 0.5) as u16,
384 (g * 255.0 + 0.5) as u16,
385 (b * 255.0 + 0.5) as u16,
386 (a * 255.0 + 0.5) as u16,
387 ];
388
389 let ctx = UniformColorCtx { r, g, b, a, rgba };
390
391 self.stages.push(Stage::UniformColor);
392 self.ctx.uniform_color = ctx;
393 }
394
395 pub fn compile(self) -> RasterPipeline {
396 if self.stages.is_empty() {
397 return RasterPipeline {
398 kind: RasterPipelineKind::High {
399 functions: ArrayVec::new(),
400 tail_functions: ArrayVec::new(),
401 },
402 ctx: Context::default(),
403 };
404 }
405
406 let is_lowp_compatible = self
407 .stages
408 .iter()
409 .all(|stage| !lowp::fn_ptr_eq(lowp::STAGES[*stage as usize], lowp::null_fn));
410
411 if self.force_hq_pipeline || !is_lowp_compatible {
412 let mut functions: ArrayVec<_, MAX_STAGES> = self
413 .stages
414 .iter()
415 .map(|stage| highp::STAGES[*stage as usize] as highp::StageFn)
416 .collect();
417 functions.push(highp::just_return as highp::StageFn);
418
419 let mut tail_functions = functions.clone();
425 for fun in &mut tail_functions {
426 if highp::fn_ptr(*fun) == highp::fn_ptr(highp::load_dst) {
427 *fun = highp::load_dst_tail as highp::StageFn;
428 } else if highp::fn_ptr(*fun) == highp::fn_ptr(highp::store) {
429 *fun = highp::store_tail as highp::StageFn;
430 } else if highp::fn_ptr(*fun) == highp::fn_ptr(highp::load_dst_u8) {
431 *fun = highp::load_dst_u8_tail as highp::StageFn;
432 } else if highp::fn_ptr(*fun) == highp::fn_ptr(highp::store_u8) {
433 *fun = highp::store_u8_tail as highp::StageFn;
434 } else if highp::fn_ptr(*fun) == highp::fn_ptr(highp::source_over_rgba) {
435 *fun = highp::source_over_rgba_tail as highp::StageFn;
438 }
439 }
440
441 RasterPipeline {
442 kind: RasterPipelineKind::High {
443 functions,
444 tail_functions,
445 },
446 ctx: self.ctx,
447 }
448 } else {
449 let mut functions: ArrayVec<_, MAX_STAGES> = self
450 .stages
451 .iter()
452 .map(|stage| lowp::STAGES[*stage as usize] as lowp::StageFn)
453 .collect();
454 functions.push(lowp::just_return as lowp::StageFn);
455
456 let mut tail_functions = functions.clone();
458 for fun in &mut tail_functions {
459 if lowp::fn_ptr(*fun) == lowp::fn_ptr(lowp::load_dst) {
460 *fun = lowp::load_dst_tail as lowp::StageFn;
461 } else if lowp::fn_ptr(*fun) == lowp::fn_ptr(lowp::store) {
462 *fun = lowp::store_tail as lowp::StageFn;
463 } else if lowp::fn_ptr(*fun) == lowp::fn_ptr(lowp::load_dst_u8) {
464 *fun = lowp::load_dst_u8_tail as lowp::StageFn;
465 } else if lowp::fn_ptr(*fun) == lowp::fn_ptr(lowp::store_u8) {
466 *fun = lowp::store_u8_tail as lowp::StageFn;
467 } else if lowp::fn_ptr(*fun) == lowp::fn_ptr(lowp::source_over_rgba) {
468 *fun = lowp::source_over_rgba_tail as lowp::StageFn;
471 }
472 }
473
474 RasterPipeline {
475 kind: RasterPipelineKind::Low {
476 functions,
477 tail_functions,
478 },
479 ctx: self.ctx,
480 }
481 }
482 }
483}
484
485pub enum RasterPipelineKind {
486 High {
487 functions: ArrayVec<highp::StageFn, MAX_STAGES>,
488 tail_functions: ArrayVec<highp::StageFn, MAX_STAGES>,
489 },
490 Low {
491 functions: ArrayVec<lowp::StageFn, MAX_STAGES>,
492 tail_functions: ArrayVec<lowp::StageFn, MAX_STAGES>,
493 },
494}
495
496pub struct RasterPipeline {
497 kind: RasterPipelineKind,
498 pub ctx: Context,
499}
500
501impl RasterPipeline {
502 pub fn run(
503 &mut self,
504 rect: &ScreenIntRect,
505 aa_mask_ctx: AAMaskCtx,
506 mask_ctx: MaskCtx,
507 pixmap_src: PixmapRef,
508 pixmap_dst: &mut SubPixmapMut,
509 ) {
510 match self.kind {
511 RasterPipelineKind::High {
512 ref functions,
513 ref tail_functions,
514 } => {
515 highp::start(
516 functions.as_slice(),
517 tail_functions.as_slice(),
518 rect,
519 aa_mask_ctx,
520 mask_ctx,
521 &mut self.ctx,
522 pixmap_src,
523 pixmap_dst,
524 );
525 }
526 RasterPipelineKind::Low {
527 ref functions,
528 ref tail_functions,
529 } => {
530 lowp::start(
531 functions.as_slice(),
532 tail_functions.as_slice(),
533 rect,
534 aa_mask_ctx,
535 mask_ctx,
536 &mut self.ctx,
537 pixmap_dst,
539 );
540 }
541 }
542 }
543}
544
545#[rustfmt::skip]
546#[cfg(test)]
547mod blend_tests {
548 use super::*;
557 use crate::{BlendMode, Color, Pixmap, PremultipliedColorU8};
558 use crate::geom::IntSizeExt;
559
560 macro_rules! test_blend {
561 ($name:ident, $mode:expr, $is_highp:expr, $r:expr, $g:expr, $b:expr, $a:expr) => {
562 #[test]
563 fn $name() {
564 let mut pixmap = Pixmap::new(1, 1).unwrap();
565 pixmap.fill(Color::from_rgba8(50, 127, 150, 200));
566
567 let pixmap_src = PixmapRef::from_bytes(&[0, 0, 0, 0], 1, 1).unwrap();
568
569 let mut p = RasterPipelineBuilder::new();
570 p.set_force_hq_pipeline($is_highp);
571 p.push_uniform_color(Color::from_rgba8(220, 140, 75, 180).premultiply());
572 p.push(Stage::LoadDestination);
573 p.push($mode.to_stage().unwrap());
574 p.push(Stage::Store);
575 let mut p = p.compile();
576 let rect = pixmap.size().to_screen_int_rect(0, 0);
577 p.run(&rect, AAMaskCtx::default(), MaskCtx::default(), pixmap_src,
578 &mut pixmap.as_mut().as_subpixmap());
579
580 assert_eq!(
581 pixmap.as_ref().pixel(0, 0).unwrap(),
582 PremultipliedColorU8::from_rgba($r, $g, $b, $a).unwrap()
583 );
584 }
585 };
586 }
587
588 macro_rules! test_blend_lowp {
589 ($name:ident, $mode:expr, $r:expr, $g:expr, $b:expr, $a:expr) => (
590 test_blend!{$name, $mode, false, $r, $g, $b, $a}
591 )
592 }
593
594 macro_rules! test_blend_highp {
595 ($name:ident, $mode:expr, $r:expr, $g:expr, $b:expr, $a:expr) => (
596 test_blend!{$name, $mode, true, $r, $g, $b, $a}
597 )
598 }
599
600 test_blend_lowp!(clear_lowp, BlendMode::Clear, 0, 0, 0, 0);
601 test_blend_lowp!(destination_lowp, BlendMode::Destination, 39, 100, 118, 200);
603 test_blend_lowp!(source_over_lowp, BlendMode::SourceOver, 167, 129, 88, 239);
604 test_blend_lowp!(destination_over_lowp, BlendMode::DestinationOver, 73, 122, 130, 239);
605 test_blend_lowp!(source_in_lowp, BlendMode::SourceIn, 122, 78, 42, 141);
606 test_blend_lowp!(destination_in_lowp, BlendMode::DestinationIn, 28, 71, 83, 141);
607 test_blend_lowp!(source_out_lowp, BlendMode::SourceOut, 34, 22, 12, 39);
608 test_blend_lowp!(destination_out_lowp, BlendMode::DestinationOut, 12, 30, 35, 59);
609 test_blend_lowp!(source_atop_lowp, BlendMode::SourceAtop, 133, 107, 76, 200);
610 test_blend_lowp!(destination_atop_lowp, BlendMode::DestinationAtop, 61, 92, 95, 180);
611 test_blend_lowp!(xor_lowp, BlendMode::Xor, 45, 51, 46, 98);
612 test_blend_lowp!(plus_lowp, BlendMode::Plus, 194, 199, 171, 255);
613 test_blend_lowp!(modulate_lowp, BlendMode::Modulate, 24, 39, 25, 141);
614 test_blend_lowp!(screen_lowp, BlendMode::Screen, 170, 160, 146, 239);
615 test_blend_lowp!(overlay_lowp, BlendMode::Overlay, 92, 128, 106, 239);
616 test_blend_lowp!(darken_lowp, BlendMode::Darken, 72, 121, 88, 239);
617 test_blend_lowp!(lighten_lowp, BlendMode::Lighten, 166, 128, 129, 239);
618 test_blend_lowp!(hard_light_lowp, BlendMode::HardLight, 154, 128, 95, 239);
621 test_blend_lowp!(difference_lowp, BlendMode::Difference, 138, 57, 87, 239);
623 test_blend_lowp!(exclusion_lowp, BlendMode::Exclusion, 146, 121, 121, 239);
624 test_blend_lowp!(multiply_lowp, BlendMode::Multiply, 69, 90, 71, 238);
625 test_blend_highp!(clear_highp, BlendMode::Clear, 0, 0, 0, 0);
631 test_blend_highp!(destination_highp, BlendMode::Destination, 39, 100, 118, 200);
633 test_blend_highp!(source_over_highp, BlendMode::SourceOver, 167, 128, 88, 239);
634 test_blend_highp!(destination_over_highp, BlendMode::DestinationOver, 72, 121, 129, 239);
635 test_blend_highp!(source_in_highp, BlendMode::SourceIn, 122, 78, 42, 141);
636 test_blend_highp!(destination_in_highp, BlendMode::DestinationIn, 28, 71, 83, 141);
637 test_blend_highp!(source_out_highp, BlendMode::SourceOut, 33, 21, 11, 39);
638 test_blend_highp!(destination_out_highp, BlendMode::DestinationOut, 11, 29, 35, 59);
639 test_blend_highp!(source_atop_highp, BlendMode::SourceAtop, 133, 107, 76, 200);
640 test_blend_highp!(destination_atop_highp, BlendMode::DestinationAtop, 61, 92, 95, 180);
641 test_blend_highp!(xor_highp, BlendMode::Xor, 45, 51, 46, 98);
642 test_blend_highp!(plus_highp, BlendMode::Plus, 194, 199, 171, 255);
643 test_blend_highp!(modulate_highp, BlendMode::Modulate, 24, 39, 24, 141);
644 test_blend_highp!(screen_highp, BlendMode::Screen, 171, 160, 146, 239);
645 test_blend_highp!(overlay_highp, BlendMode::Overlay, 92, 128, 106, 239);
646 test_blend_highp!(darken_highp, BlendMode::Darken, 72, 121, 88, 239);
647 test_blend_highp!(lighten_highp, BlendMode::Lighten, 167, 128, 129, 239);
648 test_blend_highp!(color_dodge_highp, BlendMode::ColorDodge, 186, 192, 164, 239);
649 test_blend_highp!(color_burn_highp, BlendMode::ColorBurn, 54, 63, 46, 239);
650 test_blend_highp!(hard_light_highp, BlendMode::HardLight, 155, 128, 95, 239);
651 test_blend_highp!(soft_light_highp, BlendMode::SoftLight, 98, 124, 115, 239);
652 test_blend_highp!(difference_highp, BlendMode::Difference, 139, 58, 88, 239);
653 test_blend_highp!(exclusion_highp, BlendMode::Exclusion, 147, 121, 122, 239);
654 test_blend_highp!(multiply_highp, BlendMode::Multiply, 69, 89, 71, 239);
655 test_blend_highp!(hue_highp, BlendMode::Hue, 128, 103, 74, 239);
656 test_blend_highp!(saturation_highp, BlendMode::Saturation, 59, 126, 140, 239);
657 test_blend_highp!(color_highp, BlendMode::Color, 139, 100, 60, 239);
658 test_blend_highp!(luminosity_highp, BlendMode::Luminosity, 100, 149, 157, 239);
659}