diff --git a/src/mask.rs b/src/mask.rs index 420a813..f24273a 100644 --- a/src/mask.rs +++ b/src/mask.rs @@ -14,7 +14,6 @@ use tiny_skia_path::{IntRect, IntSize, Path, Scalar, Transform}; use crate::geom::IntSizeExt; use crate::painter::DrawTiler; use crate::pipeline::RasterPipelineBlitter; -use crate::pixmap::SubPixmapMut; use crate::scan; use crate::{FillRule, PixmapRef}; @@ -157,21 +156,19 @@ impl Mask { }) } - pub(crate) fn as_subpixmap(&mut self) -> SubPixmapMut<'_> { - SubPixmapMut { - size: self.size, + pub(crate) fn as_submask_mut(&mut self) -> SubMaskMut<'_> { + SubMaskMut { real_width: self.size.width() as usize, data: &mut self.data, } } - pub(crate) fn subpixmap(&mut self, rect: IntRect) -> Option> { + pub(crate) fn submask_mut(&mut self, rect: IntRect) -> Option> { let rect = self.size.to_int_rect(0, 0).intersect(&rect)?; let row_bytes = self.width() as usize; let offset = rect.top() as usize * row_bytes + rect.left() as usize; - Some(SubPixmapMut { - size: rect.size(), + Some(SubMaskMut { real_width: self.size.width() as usize, data: &mut self.data[offset..], }) @@ -294,12 +291,12 @@ impl Mask { }; let clip_rect = tile.size().to_screen_int_rect(0, 0); - let mut subpix = match self.subpixmap(tile.to_int_rect()) { + let mut submask = match self.submask_mut(tile.to_int_rect()) { Some(v) => v, None => continue, // technically unreachable }; - let mut blitter = match RasterPipelineBlitter::new_mask(&mut subpix) { + let mut blitter = match RasterPipelineBlitter::new_mask(&mut submask) { Some(v) => v, None => continue, // nothing to do, all good }; @@ -321,8 +318,8 @@ impl Mask { } } else { let clip_rect = self.size().to_screen_int_rect(0, 0); - let mut subpix = self.as_subpixmap(); - let mut blitter = match RasterPipelineBlitter::new_mask(&mut subpix) { + let mut submask = self.as_submask_mut(); + let mut blitter = match RasterPipelineBlitter::new_mask(&mut submask) { Some(v) => v, None => return, // nothing to do, all good }; @@ -402,3 +399,8 @@ impl<'a> SubMaskRef<'a> { } } } + +pub(crate) struct SubMaskMut<'a> { + pub(crate) data: &'a mut [u8], + pub(crate) real_width: usize, +} diff --git a/src/painter.rs b/src/painter.rs index 624e00a..bbbed97 100644 --- a/src/painter.rs +++ b/src/painter.rs @@ -10,7 +10,7 @@ use tiny_skia_path::{PathStroker, Scalar, SCALAR_MAX}; use crate::geom::ScreenIntRect; use crate::mask::SubMaskRef; -use crate::pipeline::{RasterPipelineBlitter, RasterPipelineBuilder}; +use crate::pipeline::{GenericPixmapMut, RasterPipelineBlitter, RasterPipelineBuilder}; use crate::pixmap::SubPixmapMut; use crate::scan; @@ -536,7 +536,7 @@ impl PixmapMut<'_> { pipeline::AAMaskCtx::default(), mask.as_submask().mask_ctx(), pixmap_src, - &mut self.as_subpixmap(), + GenericPixmapMut::from_pixmap(&mut self.as_subpixmap()), ); } } diff --git a/src/pipeline/blitter.rs b/src/pipeline/blitter.rs index 794c3d0..2fb0c36 100644 --- a/src/pipeline/blitter.rs +++ b/src/pipeline/blitter.rs @@ -11,20 +11,33 @@ use crate::alpha_runs::AlphaRun; use crate::blitter::{Blitter, Mask}; use crate::color::AlphaU8; use crate::geom::ScreenIntRect; -use crate::mask::SubMaskRef; +use crate::mask::{SubMaskMut, SubMaskRef}; use crate::math::LENGTH_U32_ONE; -use crate::pipeline::{self, RasterPipeline, RasterPipelineBuilder}; +use crate::pipeline::{self, GenericPixmapMut, RasterPipeline, RasterPipelineBuilder}; use crate::pixmap::SubPixmapMut; +enum PipelineDest<'a, 'b: 'a> { + Mask(&'a mut SubMaskMut<'b>), + Pixmap(&'a mut SubPixmapMut<'b>), +} + +impl<'a: 'c, 'b: 'a, 'c> PipelineDest<'a, 'b> { + fn to_generic_pixmap(&'c mut self) -> GenericPixmapMut<'c> { + match self { + PipelineDest::Mask(mask) => GenericPixmapMut::from_mask(mask), + PipelineDest::Pixmap(pixmap) => GenericPixmapMut::from_pixmap(pixmap), + } + } +} + pub struct RasterPipelineBlitter<'a, 'b: 'a> { mask: Option>, pixmap_src: PixmapRef<'a>, - pixmap: &'a mut SubPixmapMut<'b>, + pixmap_dst: PipelineDest<'a, 'b>, memset2d_color: Option, blit_anti_h_rp: RasterPipeline, blit_rect_rp: RasterPipeline, blit_mask_rp: RasterPipeline, - is_mask: bool, } impl<'a, 'b: 'a> RasterPipelineBlitter<'a, 'b> { @@ -202,16 +215,15 @@ impl<'a, 'b: 'a> RasterPipelineBlitter<'a, 'b> { Some(RasterPipelineBlitter { mask, pixmap_src, - pixmap, + pixmap_dst: PipelineDest::Pixmap(pixmap), memset2d_color, blit_anti_h_rp, blit_rect_rp, blit_mask_rp, - is_mask: false, }) } - pub fn new_mask(pixmap: &'a mut SubPixmapMut<'b>) -> Option { + pub fn new_mask(mask: &'a mut SubMaskMut<'b>) -> Option { let color = Color::WHITE.premultiply(); let memset2d_color = Some(color.to_color_u8()); @@ -244,12 +256,11 @@ impl<'a, 'b: 'a> RasterPipelineBlitter<'a, 'b> { Some(RasterPipelineBlitter { mask: None, pixmap_src: PixmapRef::from_bytes(&[0, 0, 0, 0], 1, 1).unwrap(), - pixmap, + pixmap_dst: PipelineDest::Mask(mask), memset2d_color, blit_anti_h_rp, blit_rect_rp, blit_mask_rp, - is_mask: true, }) } } @@ -283,7 +294,7 @@ impl Blitter for RasterPipelineBlitter<'_, '_> { pipeline::AAMaskCtx::default(), mask_ctx, self.pixmap_src, - self.pixmap, + self.pixmap_dst.to_generic_pixmap(), ); } } @@ -333,25 +344,23 @@ impl Blitter for RasterPipelineBlitter<'_, '_> { fn blit_rect(&mut self, rect: &ScreenIntRect) { if let Some(c) = self.memset2d_color { - if self.is_mask { - for y in 0..rect.height() { - let start = self - .pixmap - .offset(rect.x() as usize, (rect.y() + y) as usize); - let end = start + rect.width() as usize; - self.pixmap.data[start..end] - .iter_mut() - .for_each(|p| *p = c.alpha()); + match &mut self.pixmap_dst { + PipelineDest::Mask(mask) => { + for y in 0..rect.height() { + let start = mask.real_width * ((rect.y() + y) as usize) + rect.x() as usize; + let end = start + rect.width() as usize; + mask.data[start..end].fill(c.alpha()); + } } - } else { - for y in 0..rect.height() { - let start = self - .pixmap - .offset(rect.x() as usize, (rect.y() + y) as usize); - let end = start + rect.width() as usize; - self.pixmap.pixels_mut()[start..end] - .iter_mut() - .for_each(|p| *p = c); + PipelineDest::Pixmap(pixmap) => { + for y in 0..rect.height() { + let start = + 4 * pixmap.real_width * (rect.y() + y) as usize + 4 * rect.x() as usize; + let row_pixels = bytemuck::cast_slice_mut::<_, PremultipliedColorU8>( + &mut pixmap.data[start..start + 4 * rect.width() as usize], + ); + row_pixels.fill(c); + } } } @@ -365,7 +374,7 @@ impl Blitter for RasterPipelineBlitter<'_, '_> { pipeline::AAMaskCtx::default(), mask_ctx, self.pixmap_src, - self.pixmap, + self.pixmap_dst.to_generic_pixmap(), ); } @@ -378,7 +387,12 @@ impl Blitter for RasterPipelineBlitter<'_, '_> { let mask_ctx = self.mask.map(|c| c.mask_ctx()).unwrap_or_default(); - self.blit_mask_rp - .run(clip, aa_mask_ctx, mask_ctx, self.pixmap_src, self.pixmap); + self.blit_mask_rp.run( + clip, + aa_mask_ctx, + mask_ctx, + self.pixmap_src, + self.pixmap_dst.to_generic_pixmap(), + ); } } diff --git a/src/pipeline/highp.rs b/src/pipeline/highp.rs index 214e31a..ba9c12c 100644 --- a/src/pipeline/highp.rs +++ b/src/pipeline/highp.rs @@ -18,7 +18,7 @@ and we're using a manual implementation. use crate::{PremultipliedColorU8, SpreadMode, PixmapRef}; use crate::geom::ScreenIntRect; -use crate::pixmap::SubPixmapMut; +use crate::pipeline::GenericPixmapMut; use crate::wide::{f32x8, i32x8, u32x8}; pub const STAGE_WIDTH: usize = 8; @@ -29,7 +29,7 @@ pub struct Pipeline<'a, 'b: 'a> { index: usize, functions: &'a [StageFn], pixmap_src: PixmapRef<'a>, - pixmap_dst: &'a mut SubPixmapMut<'b>, + pixmap_dst: GenericPixmapMut<'b>, ctx: &'a mut super::Context, // TODO: remove mut mask_ctx: super::MaskCtx<'a>, aa_mask_ctx: super::AAMaskCtx, @@ -151,7 +151,7 @@ pub fn start( mask_ctx: super::MaskCtx, ctx: &mut super::Context, pixmap_src: PixmapRef, - pixmap_dst: &mut SubPixmapMut, + pixmap_dst: GenericPixmapMut, ) { let mut p = Pipeline { index: 0, diff --git a/src/pipeline/lowp.rs b/src/pipeline/lowp.rs index fc1a6c5..1f55e7b 100644 --- a/src/pipeline/lowp.rs +++ b/src/pipeline/lowp.rs @@ -30,7 +30,7 @@ On ARM AArch64 the story is different and explicit SIMD make our code up to 2-3x use crate::PremultipliedColorU8; -use crate::pixmap::SubPixmapMut; +use crate::pipeline::GenericPixmapMut; use crate::wide::{f32x8, u16x16, f32x16}; use crate::geom::ScreenIntRect; @@ -41,7 +41,7 @@ pub type StageFn = fn(p: &mut Pipeline); pub struct Pipeline<'a, 'b: 'a> { index: usize, functions: &'a [StageFn], - pixmap: &'a mut SubPixmapMut<'b>, + pixmap: GenericPixmapMut<'b>, mask_ctx: super::MaskCtx<'a>, aa_mask_ctx: super::AAMaskCtx, ctx: &'a mut super::Context, @@ -169,7 +169,7 @@ pub fn start( aa_mask_ctx: super::AAMaskCtx, mask_ctx: super::MaskCtx, ctx: &mut super::Context, - pixmap: &mut SubPixmapMut, + pixmap: GenericPixmapMut, ) { let mut p = Pipeline { index: 0, diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index b3612ee..2dcca8e 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -50,6 +50,7 @@ use arrayvec::ArrayVec; use tiny_skia_path::NormalizedF32; +use crate::mask::SubMaskMut; use crate::{Color, PremultipliedColor, PremultipliedColorU8, SpreadMode}; use crate::{PixmapRef, Transform}; @@ -169,7 +170,27 @@ impl PixmapRef<'_> { } } -impl SubPixmapMut<'_> { +/// The target of a pipeline, can be either from a PixmapMut or a MaskMut. The pixel format +/// is left implicit. +pub(crate) struct GenericPixmapMut<'a> { + pub(crate) data: &'a mut [u8], + pub(crate) real_width: usize, +} + +impl<'b> GenericPixmapMut<'b> { + pub(crate) fn from_pixmap<'a: 'b>(pixmap: &'b mut SubPixmapMut<'a>) -> Self { + Self { + data: pixmap.data, + real_width: pixmap.real_width, + } + } + pub(crate) fn from_mask<'a: 'b>(mask: &'b mut SubMaskMut<'a>) -> Self { + Self { + data: mask.data, + real_width: mask.real_width, + } + } + #[inline(always)] pub(crate) fn offset(&self, dx: usize, dy: usize) -> usize { self.real_width * dy + dx @@ -178,13 +199,7 @@ impl SubPixmapMut<'_> { #[inline(always)] pub(crate) fn slice_at_xy(&mut self, dx: usize, dy: usize) -> &mut [PremultipliedColorU8] { let offset = self.offset(dx, dy); - &mut self.pixels_mut()[offset..] - } - - #[inline(always)] - pub(crate) fn slice_mask_at_xy(&mut self, dx: usize, dy: usize) -> &mut [u8] { - let offset = self.offset(dx, dy); - &mut self.data[offset..] + &mut bytemuck::cast_slice_mut(self.data)[offset..] } #[inline(always)] @@ -194,7 +209,9 @@ impl SubPixmapMut<'_> { dy: usize, ) -> &mut [PremultipliedColorU8; highp::STAGE_WIDTH] { let offset = self.offset(dx, dy); - self.pixels_mut()[offset..].first_chunk_mut().unwrap() + bytemuck::cast_slice_mut(self.data)[offset..] + .first_chunk_mut() + .unwrap() } #[inline(always)] @@ -204,7 +221,15 @@ impl SubPixmapMut<'_> { dy: usize, ) -> &mut [PremultipliedColorU8; lowp::STAGE_WIDTH] { let offset = self.offset(dx, dy); - self.pixels_mut()[offset..].first_chunk_mut().unwrap() + bytemuck::cast_slice_mut(self.data)[offset..] + .first_chunk_mut() + .unwrap() + } + + #[inline(always)] + pub(crate) fn slice_mask_at_xy(&mut self, dx: usize, dy: usize) -> &mut [u8] { + let offset = self.offset(dx, dy); + &mut self.data[offset..] } #[inline(always)] @@ -508,7 +533,7 @@ impl RasterPipeline { aa_mask_ctx: AAMaskCtx, mask_ctx: MaskCtx, pixmap_src: PixmapRef, - pixmap_dst: &mut SubPixmapMut, + pixmap_dst: GenericPixmapMut, ) { match self.kind { RasterPipelineKind::High { @@ -578,7 +603,7 @@ mod blend_tests { let mut p = p.compile(); let rect = pixmap.size().to_screen_int_rect(0, 0); p.run(&rect, AAMaskCtx::default(), MaskCtx::default(), pixmap_src, - &mut pixmap.as_mut().as_subpixmap()); + GenericPixmapMut::from_pixmap(&mut pixmap.as_mut().as_subpixmap())); assert_eq!( pixmap.as_ref().pixel(0, 0).unwrap(), diff --git a/src/pixmap.rs b/src/pixmap.rs index e576ef2..fc5faed 100644 --- a/src/pixmap.rs +++ b/src/pixmap.rs @@ -580,13 +580,6 @@ pub struct SubPixmapMut<'a> { pub real_width: usize, } -impl SubPixmapMut<'_> { - /// Returns a mutable slice of pixels. - pub fn pixels_mut(&mut self) -> &mut [PremultipliedColorU8] { - bytemuck::cast_slice_mut(self.data) - } -} - /// Returns minimum bytes per row as usize. /// /// Pixmap's maximum value for row bytes must fit in 31 bits.