diff --git a/crates/psd/src/lib.rs b/crates/psd/src/lib.rs index b341f84af..91792a837 100644 --- a/crates/psd/src/lib.rs +++ b/crates/psd/src/lib.rs @@ -15,7 +15,6 @@ pub mod placed; mod read; pub mod write; -use std::borrow::Cow; use std::sync::Arc; pub use descriptor::{DValue, Descriptor}; diff --git a/crates/psd/src/limits.rs b/crates/psd/src/limits.rs index 5d6101989..c1c23b390 100644 --- a/crates/psd/src/limits.rs +++ b/crates/psd/src/limits.rs @@ -1,4 +1,4 @@ -use super::*; +use std::borrow::Cow; #[derive(Debug, Clone, PartialEq)] pub enum Error { diff --git a/crates/psd/src/model.rs b/crates/psd/src/model.rs index ffe44d4b0..729b9a3c4 100644 --- a/crates/psd/src/model.rs +++ b/crates/psd/src/model.rs @@ -1,4 +1,4 @@ -use super::*; +use super::{Descriptor, engine_data, placed}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ColorMode { diff --git a/crates/psd/src/parse.rs b/crates/psd/src/parse.rs index 136cc5745..d221b9044 100644 --- a/crates/psd/src/parse.rs +++ b/crates/psd/src/parse.rs @@ -2,7 +2,8 @@ use super::limits::{Error, Result}; use super::model::{ Adjustment, ChannelInfo, ColorMode, Layer, LayerMask, LinkedFile, MAX_GROUP_DEPTH, Node, Rect, Section, SmartObject, SubPath, TextInfo, VectorMask, }; -use super::*; +use super::{Psd, Reader, descriptor, engine_data, placed}; +use std::sync::Arc; /// Whether `bytes` start like a Photoshop document. pub fn is_psd(bytes: &[u8]) -> bool { diff --git a/crates/psd/src/pixels.rs b/crates/psd/src/pixels.rs index d5c0c5993..21a5c8845 100644 --- a/crates/psd/src/pixels.rs +++ b/crates/psd/src/pixels.rs @@ -1,6 +1,9 @@ +#[cfg(test)] +use super::counters; use super::limits::{Budget, DECODE_BUDGET, Error, Result, decode_pixels, finite_sample, inflate, release_bytes, too_large}; -use super::model::{ColorMode, Pixels, Rect}; -use super::*; +use super::model::{ColorMode, Layer, Pixels, Rect}; +use super::{Psd, Reader}; +use std::borrow::Cow; impl Psd { fn bytes_per_sample(&self) -> usize { @@ -29,7 +32,7 @@ impl Psd { pub(crate) fn plane_bytes<'a>(&self, comp: u16, r: &mut Reader<'a>, w: usize, h: usize, budget: &mut Budget) -> Result> { let rowb = self.row_bytes(w); let size = rowb.checked_mul(h).ok_or_else(|| too_large(w, h))?; - Ok(match self.open_planes(comp, r, 1, 1, w, h, budget)? { + Ok(match self.open_planes(comp, r, 1, 1, (w, h), budget)? { PlaneStream::Raw => Cow::Borrowed(r.bytes(size)?), PlaneStream::Rle(counts) => Cow::Owned(Self::unpack_plane(&counts, 0, r, rowb, h, budget)?), PlaneStream::Zip(mut v) => { @@ -44,8 +47,8 @@ impl Psd { /// `w`×`h` samples, of which the first `want` are decoded: the RLE row sizes of every plane, /// or the inflated ZIP data, which holds all planes one after another (charged to `budget` /// before it is inflated). - #[allow(clippy::too_many_arguments)] - fn open_planes(&self, comp: u16, r: &mut Reader, total: usize, want: usize, w: usize, h: usize, budget: &mut Budget) -> Result { + fn open_planes(&self, comp: u16, r: &mut Reader, total: usize, want: usize, size: (usize, usize), budget: &mut Budget) -> Result { + let (w, h) = size; let size = self.row_bytes(w).checked_mul(h).ok_or_else(|| too_large(w, h))?; Ok(match comp { 0 => PlaneStream::Raw, @@ -72,17 +75,16 @@ impl Psd { /// planes are read from `r` in order, raw ones borrowed from the file data, ZIP ones from the /// inflated buffer (un-predicted in place; already charged by `open_planes`), RLE ones charged /// to `budget`. - #[allow(clippy::too_many_arguments)] fn decode_plane<'x, 'a: 'x>( &self, comp: u16, planes: &'x mut PlaneStream, r: &mut Reader<'a>, c: usize, - w: usize, - h: usize, + size: (usize, usize), budget: &mut Budget, ) -> Result> { + let (w, h) = size; let rowb = self.row_bytes(w); let size = rowb.checked_mul(h).ok_or_else(|| too_large(w, h))?; Ok(match planes { @@ -265,9 +267,9 @@ impl Psd { // Opaque black until a channel says otherwise. let mut data = budget.filled(n, [0.0f32, 0.0, 0.0, 1.0])?; let mut black = if self.mode == ColorMode::Cmyk && want > 3 { Some(budget.filled(n, 0.0f32)?) } else { None }; - let mut stream = self.open_planes(comp, &mut r, nch, want, w, h, &mut budget)?; + let mut stream = self.open_planes(comp, &mut r, nch, want, (w, h), &mut budget)?; for c in 0..want { - let raw = self.decode_plane(comp, &mut stream, &mut r, c, w, h, &mut budget)?; + let raw = self.decode_plane(comp, &mut stream, &mut r, c, (w, h), &mut budget)?; self.store_merged_channel(c, &raw, &mut data, &mut black); release_bytes(&mut budget, raw); } @@ -407,12 +409,19 @@ impl Psd { } } } + self.apply_layer_mask(l, out_rect, &mut out, &mut budget)?; + Ok(Pixels { width: ow as u32, height: oh as u32, data: out }) + } + + /// Apply the raster mask to alpha, including its default outside the mask bounds. + fn apply_layer_mask(&self, l: &Layer, out_rect: Rect, out: &mut [[f32; 4]], budget: &mut Budget) -> Result<()> { + let (ow, oh) = (out_rect.width() as usize, out_rect.height() as usize); // Layer mask: multiplies alpha (also outside the mask rect, by its default colour). if let Some(m) = l.mask.filter(|m| !m.disabled) { let mc = l.channels.iter().find(|c| c.id == -2); let (mw, mh) = (m.rect.width() as usize, m.rect.height() as usize); let mask = match mc { - Some(c) if mw > 0 && mh > 0 => Some(self.channel_floats(c.offset, c.len, mw, mh, &mut budget)?), + Some(c) if mw > 0 && mh > 0 => Some(self.channel_floats(c.offset, c.len, mw, mh, budget)?), _ => None, }; let def = m.default_color as f32 / 255.0; @@ -426,7 +435,7 @@ impl Psd { } }; match &mask { - None => scale(&mut out, outside), + None => scale(out, outside), Some(mk) => { // The columns of the output that fall inside the mask rect (the same for every row). let lo = (i64::from(m.rect.left) - i64::from(out_rect.left)).clamp(0, ow as i64); @@ -454,7 +463,7 @@ impl Psd { } } } - Ok(Pixels { width: ow as u32, height: oh as u32, data: out }) + Ok(()) } } diff --git a/crates/psd/src/tests/opt.rs b/crates/psd/src/tests/opt.rs index 5387e42b9..908026d07 100644 --- a/crates/psd/src/tests/opt.rs +++ b/crates/psd/src/tests/opt.rs @@ -1,6 +1,8 @@ //! The optimised decode / mask / placed-bake paths against the implementations they replaced //! (kept below verbatim as references), and counters for the work they no longer do. +use std::borrow::Cow; + use crate::counters::{self, INSIDE, QUAD, SCRATCH}; use crate::placed::{Warp, square_to_quad}; use crate::write::*;