mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Merge pull request #1134 from vangel76/psd-simplify-dedupe
# Conflicts: # crates/psd/src/tests.rs
This commit is contained in:
commit
a55af3b235
6 files changed
+231
-142
No files matched your search
@@ -248,7 +248,11 @@ fn read_value(r: &mut Reader, ty: &[u8; 4], depth: usize) -> Result<DValue> {
|
||||
|
||||
// ---------------------------------------------------------------- writing
|
||||
|
||||
fn put_u32(out: &mut Vec<u8>, v: u32) {
|
||||
pub(crate) fn put_u16(out: &mut Vec<u8>, v: u16) {
|
||||
out.extend_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
|
||||
pub(crate) fn put_u32(out: &mut Vec<u8>, v: u32) {
|
||||
out.extend_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
|
||||
@@ -261,14 +265,21 @@ fn put_id(out: &mut Vec<u8>, id: &str) {
|
||||
out.extend_from_slice(id.as_bytes());
|
||||
}
|
||||
|
||||
pub(crate) fn put_unicode(out: &mut Vec<u8>, s: &str) {
|
||||
let v: Vec<u16> = s.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
/// A length-prefixed UTF-16BE string (the length counts code units, including the NUL when
|
||||
/// `terminated`).
|
||||
pub(crate) fn put_utf16(out: &mut Vec<u8>, s: &str, terminated: bool) {
|
||||
let v: Vec<u16> = s.encode_utf16().chain(terminated.then_some(0)).collect();
|
||||
put_u32(out, v.len() as u32);
|
||||
for c in v {
|
||||
out.extend_from_slice(&c.to_be_bytes());
|
||||
put_u16(out, c);
|
||||
}
|
||||
}
|
||||
|
||||
/// A descriptor Unicode string: NUL-terminated.
|
||||
pub(crate) fn put_unicode(out: &mut Vec<u8>, s: &str) {
|
||||
put_utf16(out, s, true);
|
||||
}
|
||||
|
||||
/// Serialise a descriptor (without the version field).
|
||||
pub fn write_descriptor(out: &mut Vec<u8>, d: &Descriptor) {
|
||||
put_unicode(out, &d.class_name);
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
//! tree of dictionaries (`<< /Key value >>`), arrays (`[ ]`), numbers, names, booleans and
|
||||
//! strings (`(þÿ` + UTF-16BE with backslash escapes `)`). Read best-effort.
|
||||
|
||||
/// Bytes that end a bare token or a `/Name` (besides whitespace).
|
||||
const DELIMITERS: &[u8] = b"[]<>/(";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum EValue {
|
||||
Dict(Vec<(String, EValue)>),
|
||||
@@ -121,7 +124,7 @@ impl P<'_> {
|
||||
b'(' => Some(EValue::Str(self.string())),
|
||||
_ => {
|
||||
let start = self.i;
|
||||
while self.i < self.b.len() && !self.b[self.i].is_ascii_whitespace() && !b"[]<>/(".contains(&self.b[self.i]) {
|
||||
while self.i < self.b.len() && !self.b[self.i].is_ascii_whitespace() && !DELIMITERS.contains(&self.b[self.i]) {
|
||||
self.i += 1;
|
||||
}
|
||||
let tok = std::str::from_utf8(&self.b[start..self.i]).unwrap_or("");
|
||||
@@ -144,7 +147,7 @@ impl P<'_> {
|
||||
fn name(&mut self) -> String {
|
||||
self.i += 1; // '/'
|
||||
let start = self.i;
|
||||
while self.i < self.b.len() && !self.b[self.i].is_ascii_whitespace() && !b"[]<>/(".contains(&self.b[self.i]) {
|
||||
while self.i < self.b.len() && !self.b[self.i].is_ascii_whitespace() && !DELIMITERS.contains(&self.b[self.i]) {
|
||||
self.i += 1;
|
||||
}
|
||||
String::from_utf8_lossy(&self.b[start..self.i]).into_owned()
|
||||
|
||||
+61
-68
@@ -677,27 +677,12 @@ impl Psd {
|
||||
let _dver = r.u32()?;
|
||||
let d = descriptor::read_descriptor(&mut r)?;
|
||||
let uuid = d.text("Idnt").unwrap_or_default().to_string();
|
||||
let nums = |k: &str| -> Option<Vec<f64>> {
|
||||
match d.get(k)? {
|
||||
DValue::List(l) => Some(
|
||||
l.iter()
|
||||
.filter_map(|v| match v {
|
||||
DValue::Double(x) | DValue::UnitFloat(_, x) => Some(*x),
|
||||
DValue::Integer(i) => Some(*i as f64),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
DValue::UnitFloats(_, v) => Some(v.clone()),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
let t = nums("Trnf").filter(|t| t.len() == 8).unwrap_or_else(|| vec![0.0; 8]);
|
||||
let t = placed::eight(&d, "Trnf").unwrap_or([0.0; 8]);
|
||||
// The non-affine transform when present (perspective), else the transform.
|
||||
let t = nums("nonAffineTransform").filter(|t| t.len() == 8).unwrap_or(t);
|
||||
let t = placed::eight(&d, "nonAffineTransform").unwrap_or(t);
|
||||
let size = d.obj("Sz ").and_then(|s| Some([s.num("Wdth")?, s.num("Hght")?]));
|
||||
let warp = d.obj("warp").and_then(placed::Warp::read);
|
||||
l.smart_object = Some(SmartObject { uuid, quad: [[t[0], t[1]], [t[2], t[3]], [t[4], t[5]], [t[6], t[7]]], size, warp });
|
||||
l.smart_object = Some(SmartObject { uuid, quad: placed::quad_from(t), size, warp });
|
||||
}
|
||||
b"PlLd" if l.smart_object.is_none() => {
|
||||
let _id = r.tag()?;
|
||||
@@ -712,7 +697,7 @@ impl Psd {
|
||||
for v in &mut t {
|
||||
*v = r.f64_finite()?;
|
||||
}
|
||||
l.smart_object = Some(SmartObject { uuid, quad: [[t[0], t[1]], [t[2], t[3]], [t[4], t[5]], [t[6], t[7]]], size: None, warp: None });
|
||||
l.smart_object = Some(SmartObject { uuid, quad: placed::quad_from(t), size: None, warp: None });
|
||||
}
|
||||
b"vmsk" | b"vsms" => {
|
||||
let _ver = r.u32()?;
|
||||
@@ -833,32 +818,60 @@ impl Psd {
|
||||
let mut r = Reader::at(&self.data[..end], offset);
|
||||
let comp = r.u16()?;
|
||||
decode_pixels(w, h)?;
|
||||
let mut planes = self.open_planes(comp, &mut r, 1, 1, w, h)?;
|
||||
if let PlaneStream::Zip(v) = &mut planes {
|
||||
v.resize(self.row_bytes(w) * h, 0);
|
||||
}
|
||||
let raw = self.decode_plane(comp, &planes, &mut r, 0, w, h)?;
|
||||
Ok(self.samples(&raw, w, h))
|
||||
}
|
||||
|
||||
/// Prepare the pixel data at `r` (just after the compression field) of `total` planes of
|
||||
/// `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.
|
||||
fn open_planes(&self, comp: u16, r: &mut Reader, total: usize, want: usize, w: usize, h: usize) -> Result<PlaneStream> {
|
||||
let rowb = self.row_bytes(w);
|
||||
let raw = match comp {
|
||||
0 => r.bytes(rowb * h)?.to_vec(),
|
||||
Ok(match comp {
|
||||
0 => PlaneStream::Raw,
|
||||
1 => {
|
||||
let mut counts = Vec::with_capacity(h.min(r.remaining() / 2));
|
||||
for _ in 0..h {
|
||||
let n = total.saturating_mul(h);
|
||||
let mut counts = Vec::with_capacity(n.min(r.remaining() / 2));
|
||||
for _ in 0..n {
|
||||
counts.push(if self.psb { r.u32()? as usize } else { r.u16()? as usize });
|
||||
}
|
||||
let mut out = Vec::with_capacity(rowb * h);
|
||||
for c in counts {
|
||||
unpack_bits(r.bytes(c)?, rowb, &mut out)?;
|
||||
}
|
||||
out
|
||||
PlaneStream::Rle(counts)
|
||||
}
|
||||
2 | 3 => {
|
||||
let rest = r.bytes(r.remaining())?;
|
||||
let mut v = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(rest, rowb * h).map_err(|e| Error::Invalid(format!("zip: {e:?}")))?;
|
||||
v.resize(rowb * h, 0);
|
||||
if comp == 3 {
|
||||
self.unpredict(&mut v, w, h);
|
||||
}
|
||||
v
|
||||
let v = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(rest, want * rowb * h).map_err(|e| Error::Invalid(format!("zip: {e:?}")))?;
|
||||
PlaneStream::Zip(v)
|
||||
}
|
||||
c => return Err(Error::Unsupported(format!("compression {c}"))),
|
||||
};
|
||||
Ok(self.samples(&raw, w, h))
|
||||
})
|
||||
}
|
||||
|
||||
/// The raw bytes (`row_bytes(w)` × `h`, ZIP prediction undone) of plane `c`; raw and RLE
|
||||
/// planes are read from `r` in order.
|
||||
fn decode_plane(&self, comp: u16, planes: &PlaneStream, r: &mut Reader, c: usize, w: usize, h: usize) -> Result<Vec<u8>> {
|
||||
let rowb = self.row_bytes(w);
|
||||
Ok(match planes {
|
||||
PlaneStream::Raw => r.bytes(rowb * h)?.to_vec(),
|
||||
PlaneStream::Rle(counts) => {
|
||||
let mut out = Vec::with_capacity(rowb * h);
|
||||
for row in 0..h {
|
||||
let n = *counts.get(c * h + row).ok_or(Error::Truncated)?;
|
||||
unpack_bits(r.bytes(n)?, rowb, &mut out)?;
|
||||
}
|
||||
out
|
||||
}
|
||||
PlaneStream::Zip(v) => {
|
||||
let mut plane = v.get(c * rowb * h..(c + 1) * rowb * h).ok_or(Error::Truncated)?.to_vec();
|
||||
if comp == 3 {
|
||||
self.unpredict(&mut plane, w, h);
|
||||
}
|
||||
plane
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn row_bytes(&self, w: usize) -> usize {
|
||||
@@ -968,39 +981,10 @@ impl Psd {
|
||||
let nch = self.channels as usize;
|
||||
let cc = self.mode.color_channels();
|
||||
let want = (cc + usize::from(self.merged_alpha)).min(nch);
|
||||
let rowb = self.row_bytes(w);
|
||||
let mut planes: Vec<Option<Vec<f32>>> = vec![None; nch];
|
||||
match comp {
|
||||
0 => {
|
||||
for p in planes.iter_mut().take(want) {
|
||||
*p = Some(self.samples(r.bytes(rowb * h)?, w, h));
|
||||
}
|
||||
}
|
||||
1 => {
|
||||
let mut counts = Vec::with_capacity((nch * h).min(r.remaining() / 2));
|
||||
for _ in 0..nch * h {
|
||||
counts.push(if self.psb { r.u32()? as usize } else { r.u16()? as usize });
|
||||
}
|
||||
for (c, p) in planes.iter_mut().enumerate().take(want) {
|
||||
let mut out = Vec::with_capacity(rowb * h);
|
||||
for row in 0..h {
|
||||
unpack_bits(r.bytes(counts[c * h + row])?, rowb, &mut out)?;
|
||||
}
|
||||
*p = Some(self.samples(&out, w, h));
|
||||
}
|
||||
}
|
||||
2 | 3 => {
|
||||
let rest = r.bytes(r.remaining())?;
|
||||
let v = miniz_oxide::inflate::decompress_to_vec_zlib_with_limit(rest, want * rowb * h).map_err(|e| Error::Invalid(format!("zip: {e:?}")))?;
|
||||
for (c, p) in planes.iter_mut().enumerate().take(want) {
|
||||
let mut plane = v.get(c * rowb * h..(c + 1) * rowb * h).ok_or(Error::Truncated)?.to_vec();
|
||||
if comp == 3 {
|
||||
self.unpredict(&mut plane, w, h);
|
||||
}
|
||||
*p = Some(self.samples(&plane, w, h));
|
||||
}
|
||||
}
|
||||
c => return Err(Error::Unsupported(format!("compression {c}"))),
|
||||
let stream = self.open_planes(comp, &mut r, nch, want, w, h)?;
|
||||
for (c, p) in planes.iter_mut().enumerate().take(want) {
|
||||
*p = Some(self.samples(&self.decode_plane(comp, &stream, &mut r, c, w, h)?, w, h));
|
||||
}
|
||||
let alpha = if self.merged_alpha && nch > cc { planes[cc].take() } else { None };
|
||||
let mut data = Vec::with_capacity(w * h);
|
||||
@@ -1096,6 +1080,15 @@ impl Psd {
|
||||
}
|
||||
}
|
||||
|
||||
/// The pixel data of a run of planes, as prepared by `Psd::open_planes`.
|
||||
enum PlaneStream {
|
||||
Raw,
|
||||
/// RLE row byte counts of every plane, plane after plane.
|
||||
Rle(Vec<usize>),
|
||||
/// The inflated ZIP data of all planes.
|
||||
Zip(Vec<u8>),
|
||||
}
|
||||
|
||||
/// The next additional-information block at `*pos` (signature `8BIM`/`8B64`, key, length):
|
||||
/// returns (key, data start, data end). Skips up to 3 padding bytes before a signature.
|
||||
fn next_block(data: &[u8], pos: &mut usize, end: usize, psb: bool) -> Option<([u8; 4], usize, usize)> {
|
||||
|
||||
@@ -35,7 +35,7 @@ pub struct Warp {
|
||||
pub mesh: Vec<[f64; 2]>,
|
||||
}
|
||||
|
||||
fn floats(v: Option<&DValue>) -> Vec<f64> {
|
||||
pub(crate) fn floats(v: Option<&DValue>) -> Vec<f64> {
|
||||
match v {
|
||||
Some(DValue::UnitFloats(_, v)) => v.clone(),
|
||||
Some(DValue::List(l)) => l
|
||||
@@ -50,6 +50,16 @@ fn floats(v: Option<&DValue>) -> Vec<f64> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The eight numbers of a transform entry (`Trnf`, `nonAffineTransform`), when it has exactly eight.
|
||||
pub(crate) fn eight(d: &Descriptor, key: &str) -> Option<[f64; 8]> {
|
||||
floats(d.get(key)).try_into().ok()
|
||||
}
|
||||
|
||||
/// The corner quad (top-left, top-right, bottom-right, bottom-left) from x0 y0 … x3 y3.
|
||||
pub(crate) fn quad_from(t: [f64; 8]) -> [[f64; 2]; 4] {
|
||||
[[t[0], t[1]], [t[2], t[3]], [t[4], t[5]], [t[6], t[7]]]
|
||||
}
|
||||
|
||||
impl Warp {
|
||||
/// Read a `warp` descriptor; `None` for no warp.
|
||||
pub fn read(d: &Descriptor) -> Option<Warp> {
|
||||
|
||||
@@ -351,3 +351,86 @@ fn rect_new_near_i32_max_saturates() {
|
||||
assert_eq!((r.right, r.bottom), (i32::MAX, i32::MAX));
|
||||
assert!(r.is_empty());
|
||||
}
|
||||
|
||||
/// A ZIP plane body for the decoder: the plane delta-coded per row when `predict`.
|
||||
fn zip_body(raw: &[u8], w: usize, depth: u16, predict: bool) -> Vec<u8> {
|
||||
if !predict {
|
||||
return raw.to_vec();
|
||||
}
|
||||
let mut out = raw.to_vec();
|
||||
let bps = usize::from(depth / 8);
|
||||
for row in out.chunks_exact_mut(w * bps) {
|
||||
if depth == 16 {
|
||||
let mut prev = 0u16;
|
||||
for c in row.as_chunks_mut::<2>().0 {
|
||||
let x = u16::from_be_bytes([c[0], c[1]]);
|
||||
c.copy_from_slice(&x.wrapping_sub(prev).to_be_bytes());
|
||||
prev = x;
|
||||
}
|
||||
} else {
|
||||
for i in (1..row.len()).rev() {
|
||||
row[i] = row[i].wrapping_sub(row[i - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
/// The same document with every channel and the merged image re-encoded as ZIP (`comp` 2) or
|
||||
/// ZIP with prediction (3), appended to the file data.
|
||||
fn rezip(p: &Psd, comp: u16) -> Psd {
|
||||
let mut z = p.clone();
|
||||
let mut data = p.data.to_vec();
|
||||
let (depth, predict) = (p.depth, comp == 3);
|
||||
let deflate = |body: &[u8]| {
|
||||
let mut v = comp.to_be_bytes().to_vec();
|
||||
v.extend(miniz_oxide::deflate::compress_to_vec_zlib(body, 6));
|
||||
v
|
||||
};
|
||||
for (l, zl) in p.layers.iter().zip(&mut z.layers) {
|
||||
for (c, zc) in l.channels.iter().zip(&mut zl.channels) {
|
||||
let w = if c.id == -2 { l.mask.map_or(0, |m| m.rect.width() as usize) } else { l.rect.width() as usize };
|
||||
let raw = &p.data[c.offset + 2..c.offset + c.len];
|
||||
zc.offset = data.len();
|
||||
data.extend(deflate(&zip_body(raw, w, depth, predict)));
|
||||
zc.len = data.len() - zc.offset;
|
||||
}
|
||||
}
|
||||
// The merged image: one ZIP stream holding all planes.
|
||||
let (w, h) = (p.width as usize, p.height as usize);
|
||||
let plane = w * h * usize::from(depth / 8);
|
||||
let mut all = vec![];
|
||||
for c in 0..usize::from(p.channels) {
|
||||
let at = p.merged_offset + 2 + c * plane;
|
||||
all.extend(zip_body(&p.data[at..at + plane], w, depth, predict));
|
||||
}
|
||||
z.merged_offset = data.len();
|
||||
data.extend(deflate(&all));
|
||||
z.data = data.into();
|
||||
z
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zip_channels_decode_like_raw_ones() {
|
||||
for depth in [8, 16] {
|
||||
let raw = Psd::parse(write(&doc_with_layers(depth, ColorMode::Rgb, false))).unwrap();
|
||||
for comp in [2, 3] {
|
||||
let zip = rezip(&raw, comp);
|
||||
assert_eq!(zip.composite().unwrap().data, raw.composite().unwrap().data, "composite {depth} bit, compression {comp}");
|
||||
for (i, canvas) in [(0, true), (0, false), (1, false), (2, false), (2, true)] {
|
||||
let (want, got) = (raw.layer_pixels(i, canvas).unwrap(), zip.layer_pixels(i, canvas).unwrap());
|
||||
assert_eq!(got.data, want.data, "layer {i} canvas {canvas}, {depth} bit, compression {comp}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rle_composite_matches_the_raw_one() {
|
||||
for depth in [8, 16] {
|
||||
let raw = Psd::parse(write(&doc_with_layers(depth, ColorMode::Rgb, false))).unwrap();
|
||||
let rle = Psd::parse(write(&doc_with_layers(depth, ColorMode::Rgb, true))).unwrap();
|
||||
assert_eq!(rle.composite().unwrap().data, raw.composite().unwrap().data, "{depth} bit");
|
||||
assert_eq!(rle.layer_pixels(2, true).unwrap().data, raw.layer_pixels(2, true).unwrap().data, "{depth} bit");
|
||||
}
|
||||
}
|
||||
+56
-67
@@ -3,7 +3,7 @@
|
||||
//! additional-information blocks (helpers below build text, effects, fill and adjustment
|
||||
//! blocks). Used to generate test fixtures; not a full exporter.
|
||||
|
||||
use crate::descriptor::{self, DValue, Descriptor};
|
||||
use crate::descriptor::{self, DValue, Descriptor, put_u16, put_u32, put_utf16};
|
||||
use crate::{ColorMode, Rect, Section, engine_data, pack_bits};
|
||||
|
||||
/// A layer mask for [`WLayer`].
|
||||
@@ -114,13 +114,6 @@ impl WDoc {
|
||||
}
|
||||
}
|
||||
|
||||
fn u16b(out: &mut Vec<u8>, v: u16) {
|
||||
out.extend_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
fn u32b(out: &mut Vec<u8>, v: u32) {
|
||||
out.extend_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
|
||||
/// Colour channels for a straight RGB pixel in the document mode.
|
||||
fn mode_channels(mode: ColorMode, p: [f32; 4]) -> Vec<f32> {
|
||||
match mode {
|
||||
@@ -139,7 +132,7 @@ fn encode_samples(v: &[f32], depth: u16) -> Vec<u8> {
|
||||
for &x in v {
|
||||
let x = x.clamp(0.0, 1.0);
|
||||
if depth == 16 {
|
||||
u16b(&mut out, (x * 65535.0).round() as u16);
|
||||
put_u16(&mut out, (x * 65535.0).round() as u16);
|
||||
} else {
|
||||
out.push((x * 255.0).round() as u8);
|
||||
}
|
||||
@@ -153,14 +146,14 @@ fn channel_data(v: &[f32], w: usize, h: usize, depth: u16, rle: bool) -> Vec<u8>
|
||||
let mut out = vec![];
|
||||
let rowb = raw.len() / h.max(1);
|
||||
if !rle || w == 0 || h == 0 {
|
||||
u16b(&mut out, 0);
|
||||
put_u16(&mut out, 0);
|
||||
out.extend_from_slice(&raw);
|
||||
return out;
|
||||
}
|
||||
u16b(&mut out, 1);
|
||||
put_u16(&mut out, 1);
|
||||
let rows: Vec<Vec<u8>> = raw.chunks(rowb.max(1)).map(pack_bits).collect();
|
||||
for r in &rows {
|
||||
u16b(&mut out, r.len() as u16);
|
||||
put_u16(&mut out, r.len() as u16);
|
||||
}
|
||||
for r in rows {
|
||||
out.extend_from_slice(&r);
|
||||
@@ -180,7 +173,7 @@ fn block(out: &mut Vec<u8>, key: &[u8; 4], data: &[u8]) {
|
||||
out.extend_from_slice(b"8BIM");
|
||||
out.extend_from_slice(key);
|
||||
let padded = data.len().div_ceil(2) * 2;
|
||||
u32b(out, padded as u32);
|
||||
put_u32(out, padded as u32);
|
||||
out.extend_from_slice(data);
|
||||
out.resize(out.len() + padded - data.len(), 0);
|
||||
}
|
||||
@@ -208,10 +201,10 @@ fn layer_info(doc: &WDoc) -> Vec<u8> {
|
||||
for v in [l.rect.top, l.rect.left, l.rect.bottom, l.rect.right] {
|
||||
out.extend_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
u16b(&mut out, chans.len() as u16);
|
||||
put_u16(&mut out, chans.len() as u16);
|
||||
for (id, d) in &chans {
|
||||
out.extend_from_slice(&id.to_be_bytes());
|
||||
u32b(&mut out, d.len() as u32);
|
||||
put_u32(&mut out, d.len() as u32);
|
||||
}
|
||||
out.extend_from_slice(b"8BIM");
|
||||
out.extend_from_slice(&l.blend);
|
||||
@@ -226,7 +219,7 @@ fn layer_info(doc: &WDoc) -> Vec<u8> {
|
||||
let mut extra = vec![];
|
||||
match &l.mask {
|
||||
Some(m) => {
|
||||
u32b(&mut extra, 20);
|
||||
put_u32(&mut extra, 20);
|
||||
for v in [m.rect.top, m.rect.left, m.rect.bottom, m.rect.right] {
|
||||
extra.extend_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
@@ -234,9 +227,9 @@ fn layer_info(doc: &WDoc) -> Vec<u8> {
|
||||
extra.push(if m.disabled { 2 } else { 0 });
|
||||
extra.extend_from_slice(&[0, 0]);
|
||||
}
|
||||
None => u32b(&mut extra, 0),
|
||||
None => put_u32(&mut extra, 0),
|
||||
}
|
||||
u32b(&mut extra, 0); // blending ranges
|
||||
put_u32(&mut extra, 0); // blending ranges
|
||||
pascal4(&mut extra, &l.name);
|
||||
let luni = unicode_plain(&l.name);
|
||||
block(&mut extra, b"luni", &luni);
|
||||
@@ -244,7 +237,7 @@ fn layer_info(doc: &WDoc) -> Vec<u8> {
|
||||
Section::Layer => {}
|
||||
s => {
|
||||
let mut d = vec![];
|
||||
u32b(
|
||||
put_u32(
|
||||
&mut d,
|
||||
match s {
|
||||
Section::OpenFolder => 1,
|
||||
@@ -260,7 +253,7 @@ fn layer_info(doc: &WDoc) -> Vec<u8> {
|
||||
for (k, d) in &l.blocks {
|
||||
block(&mut extra, k, d);
|
||||
}
|
||||
u32b(&mut out, extra.len() as u32);
|
||||
put_u32(&mut out, extra.len() as u32);
|
||||
out.extend_from_slice(&extra);
|
||||
chan_data.push(chans);
|
||||
}
|
||||
@@ -275,13 +268,10 @@ fn layer_info(doc: &WDoc) -> Vec<u8> {
|
||||
out
|
||||
}
|
||||
|
||||
/// A Unicode string without the terminating NUL (the layer-name `luni` form).
|
||||
fn unicode_plain(s: &str) -> Vec<u8> {
|
||||
let v: Vec<u16> = s.encode_utf16().collect();
|
||||
let mut out = vec![];
|
||||
u32b(&mut out, v.len() as u32);
|
||||
for c in v {
|
||||
u16b(&mut out, c);
|
||||
}
|
||||
put_utf16(&mut out, s, false);
|
||||
out
|
||||
}
|
||||
|
||||
@@ -313,58 +303,58 @@ pub fn write(doc: &WDoc) -> Vec<u8> {
|
||||
let cc = doc.mode.color_channels();
|
||||
let mut out = vec![];
|
||||
out.extend_from_slice(b"8BPS");
|
||||
u16b(&mut out, 1);
|
||||
put_u16(&mut out, 1);
|
||||
out.extend_from_slice(&[0; 6]);
|
||||
u16b(&mut out, (cc + 1) as u16);
|
||||
u32b(&mut out, doc.height);
|
||||
u32b(&mut out, doc.width);
|
||||
u16b(&mut out, doc.depth);
|
||||
u16b(&mut out, doc.mode.to_u16());
|
||||
u32b(&mut out, 0); // colour mode data
|
||||
u32b(&mut out, 0); // image resources
|
||||
put_u16(&mut out, (cc + 1) as u16);
|
||||
put_u32(&mut out, doc.height);
|
||||
put_u32(&mut out, doc.width);
|
||||
put_u16(&mut out, doc.depth);
|
||||
put_u16(&mut out, doc.mode.to_u16());
|
||||
put_u32(&mut out, 0); // colour mode data
|
||||
put_u32(&mut out, 0); // image resources
|
||||
// Layer and mask information.
|
||||
let mut lm = vec![];
|
||||
if !doc.layers.is_empty() {
|
||||
let li = layer_info(doc);
|
||||
if doc.depth == 16 {
|
||||
u32b(&mut lm, 0);
|
||||
u32b(&mut lm, 0); // global layer mask info
|
||||
put_u32(&mut lm, 0);
|
||||
put_u32(&mut lm, 0); // global layer mask info
|
||||
let mut padded = li;
|
||||
while !padded.len().is_multiple_of(4) {
|
||||
padded.push(0);
|
||||
}
|
||||
lm.extend_from_slice(b"8BIM");
|
||||
lm.extend_from_slice(b"Lr16");
|
||||
u32b(&mut lm, padded.len() as u32);
|
||||
put_u32(&mut lm, padded.len() as u32);
|
||||
lm.extend_from_slice(&padded);
|
||||
} else {
|
||||
u32b(&mut lm, li.len() as u32);
|
||||
put_u32(&mut lm, li.len() as u32);
|
||||
lm.extend_from_slice(&li);
|
||||
u32b(&mut lm, 0);
|
||||
put_u32(&mut lm, 0);
|
||||
}
|
||||
if !doc.linked.is_empty() {
|
||||
let d = linked_block(&doc.linked);
|
||||
lm.extend_from_slice(b"8BIM");
|
||||
lm.extend_from_slice(b"lnk2");
|
||||
u32b(&mut lm, d.len() as u32);
|
||||
put_u32(&mut lm, d.len() as u32);
|
||||
lm.extend_from_slice(&d);
|
||||
}
|
||||
}
|
||||
u32b(&mut out, lm.len() as u32);
|
||||
put_u32(&mut out, lm.len() as u32);
|
||||
out.extend_from_slice(&lm);
|
||||
// Merged image.
|
||||
let comp = doc.composite.clone().unwrap_or_else(|| flatten(doc));
|
||||
let (w, h) = (doc.width as usize, doc.height as usize);
|
||||
let w = doc.width as usize;
|
||||
let planes: Vec<Vec<f32>> = comp.iter().map(|p| mode_channels(doc.mode, *p)).collect();
|
||||
let mut chans: Vec<Vec<f32>> = (0..cc).map(|c| planes.iter().map(|v| v[c]).collect()).collect();
|
||||
chans.push(comp.iter().map(|p| p[3]).collect());
|
||||
if doc.rle {
|
||||
u16b(&mut out, 1);
|
||||
put_u16(&mut out, 1);
|
||||
let rows: Vec<Vec<Vec<u8>>> =
|
||||
chans.iter().map(|c| encode_samples(c, doc.depth).chunks((w * if doc.depth == 16 { 2 } else { 1 }).max(1)).map(pack_bits).collect()).collect();
|
||||
for c in &rows {
|
||||
for r in c {
|
||||
u16b(&mut out, r.len() as u16);
|
||||
put_u16(&mut out, r.len() as u16);
|
||||
}
|
||||
}
|
||||
for c in rows {
|
||||
@@ -373,12 +363,11 @@ pub fn write(doc: &WDoc) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
u16b(&mut out, 0);
|
||||
put_u16(&mut out, 0);
|
||||
for c in &chans {
|
||||
out.extend_from_slice(&encode_samples(c, doc.depth));
|
||||
}
|
||||
}
|
||||
let _ = h;
|
||||
out
|
||||
}
|
||||
|
||||
@@ -388,7 +377,7 @@ fn linked_block(files: &[(String, String, [u8; 4], Vec<u8>)]) -> Vec<u8> {
|
||||
for (uuid, name, ty, data) in files {
|
||||
let mut rec = vec![];
|
||||
rec.extend_from_slice(b"liFD");
|
||||
u32b(&mut rec, 7);
|
||||
put_u32(&mut rec, 7);
|
||||
rec.push(uuid.len() as u8);
|
||||
rec.extend_from_slice(uuid.as_bytes());
|
||||
rec.extend_from_slice(&unicode_plain(name));
|
||||
@@ -397,7 +386,7 @@ fn linked_block(files: &[(String, String, [u8; 4], Vec<u8>)]) -> Vec<u8> {
|
||||
rec.extend_from_slice(&(data.len() as u64).to_be_bytes());
|
||||
rec.push(0); // no file-open descriptor
|
||||
rec.extend_from_slice(data);
|
||||
u32b(&mut rec, 0); // child document id (empty unicode string)
|
||||
put_u32(&mut rec, 0); // child document id (empty unicode string)
|
||||
rec.extend_from_slice(&0f64.to_be_bytes()); // asset modification time
|
||||
rec.push(0); // asset locked state
|
||||
out.extend_from_slice(&(rec.len() as u64).to_be_bytes());
|
||||
@@ -462,8 +451,8 @@ pub fn smart_object_block_warped(uuid: &str, quad: [[f64; 2]; 4], size: [f64; 2]
|
||||
d = d.with("warp", DValue::Descriptor(w));
|
||||
}
|
||||
let mut out = b"soLD".to_vec();
|
||||
u32b(&mut out, 4);
|
||||
u32b(&mut out, 16);
|
||||
put_u32(&mut out, 4);
|
||||
put_u32(&mut out, 16);
|
||||
descriptor::write_descriptor(&mut out, &d);
|
||||
(*b"SoLd", out)
|
||||
}
|
||||
@@ -471,12 +460,12 @@ pub fn smart_object_block_warped(uuid: &str, quad: [[f64; 2]; 4], size: [f64; 2]
|
||||
/// `TySh`: a type layer with one style run; the text origin sits at `origin`.
|
||||
pub fn text_block(text: &str, origin: [f64; 2], font: &str, size: f64, color: [f64; 3], justification: u8) -> ([u8; 4], Vec<u8>) {
|
||||
let mut d = vec![];
|
||||
u16b(&mut d, 1);
|
||||
put_u16(&mut d, 1);
|
||||
for v in [1.0, 0.0, 0.0, 1.0, origin[0], origin[1]] {
|
||||
d.extend_from_slice(&f64::to_be_bytes(v));
|
||||
}
|
||||
u16b(&mut d, 50);
|
||||
u32b(&mut d, 16);
|
||||
put_u16(&mut d, 50);
|
||||
put_u32(&mut d, 16);
|
||||
let desc = Descriptor::new("TxLr")
|
||||
.with("Txt ", DValue::Text(text.replace('\n', "\r")))
|
||||
.with("textGridding", DValue::Enum("textGridding".into(), "None".into()))
|
||||
@@ -484,11 +473,11 @@ pub fn text_block(text: &str, origin: [f64; 2], font: &str, size: f64, color: [f
|
||||
.with("EngineData", DValue::Raw(engine_data::minimal(text, font, size, color, justification)));
|
||||
descriptor::write_descriptor(&mut d, &desc);
|
||||
// Warp: version, descriptor version, an empty warp descriptor.
|
||||
u16b(&mut d, 1);
|
||||
u32b(&mut d, 16);
|
||||
put_u16(&mut d, 1);
|
||||
put_u32(&mut d, 16);
|
||||
descriptor::write_descriptor(&mut d, &Descriptor::new("warp").with("warpStyle", DValue::Enum("warpStyle".into(), "warpNone".into())));
|
||||
for _ in 0..4 {
|
||||
u32b(&mut d, 0);
|
||||
put_u32(&mut d, 0);
|
||||
}
|
||||
(*b"TySh", d)
|
||||
}
|
||||
@@ -496,8 +485,8 @@ pub fn text_block(text: &str, origin: [f64; 2], font: &str, size: f64, color: [f
|
||||
/// `lfx2`: layer effects from an effects descriptor (`null` class with `DrSh`, `FrFX`…).
|
||||
pub fn effects_block(desc: &Descriptor) -> ([u8; 4], Vec<u8>) {
|
||||
let mut d = vec![];
|
||||
u32b(&mut d, 0);
|
||||
u32b(&mut d, 16);
|
||||
put_u32(&mut d, 0);
|
||||
put_u32(&mut d, 16);
|
||||
descriptor::write_descriptor(&mut d, desc);
|
||||
(*b"lfx2", d)
|
||||
}
|
||||
@@ -505,7 +494,7 @@ pub fn effects_block(desc: &Descriptor) -> ([u8; 4], Vec<u8>) {
|
||||
/// `SoCo`: solid colour fill.
|
||||
pub fn solid_color_block(rgb: [f64; 3]) -> ([u8; 4], Vec<u8>) {
|
||||
let mut d = vec![];
|
||||
u32b(&mut d, 16);
|
||||
put_u32(&mut d, 16);
|
||||
descriptor::write_descriptor(&mut d, &Descriptor::new("null").with("Clr ", DValue::Descriptor(Descriptor::rgb(rgb))));
|
||||
(*b"SoCo", d)
|
||||
}
|
||||
@@ -513,7 +502,7 @@ pub fn solid_color_block(rgb: [f64; 3]) -> ([u8; 4], Vec<u8>) {
|
||||
/// `hue2`: Hue/Saturation (master).
|
||||
pub fn hue_sat_block(hue: i16, sat: i16, light: i16) -> ([u8; 4], Vec<u8>) {
|
||||
let mut d = vec![];
|
||||
u16b(&mut d, 2);
|
||||
put_u16(&mut d, 2);
|
||||
d.extend_from_slice(&[0, 0]);
|
||||
for v in [0i16, 0, 0, hue, sat, light] {
|
||||
d.extend_from_slice(&v.to_be_bytes());
|
||||
@@ -526,7 +515,7 @@ pub fn hue_sat_block(hue: i16, sat: i16, light: i16) -> ([u8; 4], Vec<u8>) {
|
||||
/// `levl`: Levels (composite record).
|
||||
pub fn levels_block(in_black: i16, in_white: i16, out_black: i16, out_white: i16, gamma: f64) -> ([u8; 4], Vec<u8>) {
|
||||
let mut d = vec![];
|
||||
u16b(&mut d, 2);
|
||||
put_u16(&mut d, 2);
|
||||
for v in [in_black, in_white, out_black, out_white, (gamma * 100.0).round() as i16] {
|
||||
d.extend_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
@@ -553,7 +542,7 @@ pub fn brightness_block(brightness: i16, contrast: i16) -> ([u8; 4], Vec<u8>) {
|
||||
|
||||
pub fn exposure_block(exposure: f32, offset: f32, gamma: f32) -> ([u8; 4], Vec<u8>) {
|
||||
let mut d = vec![];
|
||||
u16b(&mut d, 1);
|
||||
put_u16(&mut d, 1);
|
||||
for v in [exposure, offset, gamma] {
|
||||
d.extend_from_slice(&v.to_be_bytes());
|
||||
}
|
||||
@@ -563,18 +552,18 @@ pub fn exposure_block(exposure: f32, offset: f32, gamma: f32) -> ([u8; 4], Vec<u
|
||||
/// `vmsk`: a vector mask from closed polygons/curves given as (in, anchor, out) knots in pixels.
|
||||
pub fn vector_mask_block(subpaths: &[Vec<[[f64; 2]; 3]>], width: u32, height: u32, inverted: bool) -> ([u8; 4], Vec<u8>) {
|
||||
let mut d = vec![];
|
||||
u32b(&mut d, 3);
|
||||
u32b(&mut d, inverted as u32);
|
||||
put_u32(&mut d, 3);
|
||||
put_u32(&mut d, inverted as u32);
|
||||
// Path fill rule record.
|
||||
u16b(&mut d, 6);
|
||||
put_u16(&mut d, 6);
|
||||
d.resize(d.len() + 24, 0);
|
||||
let fx = |v: f64| ((v * (1 << 24) as f64).round() as i32).to_be_bytes();
|
||||
for sp in subpaths {
|
||||
u16b(&mut d, 0);
|
||||
u16b(&mut d, sp.len() as u16);
|
||||
put_u16(&mut d, 0);
|
||||
put_u16(&mut d, sp.len() as u16);
|
||||
d.resize(d.len() + 22, 0);
|
||||
for k in sp {
|
||||
u16b(&mut d, 1);
|
||||
put_u16(&mut d, 1);
|
||||
for p in k {
|
||||
d.extend_from_slice(&fx(p[1] / height as f64));
|
||||
d.extend_from_slice(&fx(p[0] / width as f64));
|
||||
|
||||
Reference in new issue
Block a user