1use alloc::vec::Vec;
4
5pub const SIGNATURE: [u8; 8] = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
7
8#[derive(Debug)]
10pub enum DecodeError {
11 UnsupportedFileFormat,
13 ConversionFailed,
15 InvalidSignature,
17 UnsupportedPixelFormat,
19 UnsupportedFeature,
21 LimitExceeded,
23 IndexOutOfBounds,
25 CorruptData,
27 UnexpectedEof,
29}
30
31impl From<yazi::Error> for DecodeError {
32 fn from(_: yazi::Error) -> Self {
33 Self::CorruptData
34 }
35}
36
37#[derive(Copy, Clone, PartialEq, Eq, Debug)]
39pub enum ColorType {
40 Greyscale = 0,
41 GreyscaleAlpha = 4,
42 Indexed = 3,
43 TrueColor = 2,
44 TrueColorAlpha = 6,
45}
46
47#[derive(Copy, Clone, Debug)]
49pub struct Header {
50 pub width: u32,
51 pub height: u32,
52 pub color_type: ColorType,
53 pub depth: u8,
54 pub interlaced: bool,
55}
56
57impl Header {
58 pub fn decode(png: &[u8]) -> Option<Self> {
60 const IHDR: u32 = 73 << 24 | 72 << 16 | 68 << 8 | 82;
61 if png.len() < 33 || !check_signature(png) {
62 return None;
63 }
64 let mut o = 8;
65 let len = get_u32be(png, o);
66 if len != 13 || get_u32be(png, o + 4) != IHDR {
67 return None;
68 }
69 o += 4;
70 let width = get_u32be(png, o + 4);
71 let height = get_u32be(png, o + 8);
72 let depth = png[o + 12];
73 let color_type = png[o + 13];
74 let compression_method = png[o + 14];
75 let filter_method = png[o + 15];
76 let interlace_method = png[o + 16];
77 if compression_method != 0
78 || filter_method != 0
79 || (interlace_method != 0 && interlace_method != 1)
80 {
81 return None;
82 }
83 let _crc = get_u32be(png, o + 17);
84 let d = depth;
85 use ColorType::*;
86 let color_type = match color_type {
87 0 => Greyscale,
88 2 => TrueColor,
89 3 => Indexed,
90 4 => GreyscaleAlpha,
91 6 => TrueColorAlpha,
92 _ => return None,
93 };
94 match color_type {
95 Greyscale | Indexed => {
96 if d != 1 && d != 2 && d != 4 && d != 8 && d != 16 {
97 return None;
98 }
99 if color_type == Indexed && d == 16 {
100 return None;
101 }
102 }
103 TrueColor | TrueColorAlpha | GreyscaleAlpha => {
104 if d != 8 && d != 16 {
105 return None;
106 }
107 }
108 };
109 Some(Self {
110 width,
111 height,
112 color_type,
113 depth,
114 interlaced: interlace_method != 0,
115 })
116 }
117}
118
119pub fn check_signature(png: &[u8]) -> bool {
121 if png.len() >= 8 {
122 for i in 0..8 {
123 if png[i] != SIGNATURE[i] {
124 return false;
125 }
126 }
127 return true;
128 }
129 false
130}
131
132pub fn decode(
133 data: &[u8],
134 scratch: &mut Vec<u8>,
135 target: &mut [u8],
136) -> Result<(u32, u32, bool), DecodeError> {
137 let mut state = State::new(data, scratch)?;
138 let w = state.header.width;
139 let h = state.header.height;
140 if w == 0 || h == 0 {
141 return Ok((w, h, false));
142 }
143 let decomp_len = scratch.len();
144 scratch.resize(decomp_len + state.extra_bytes, 0);
145 let (decomp, extra) = scratch.split_at_mut(decomp_len);
146 if target.len() < (w * h * 4) as usize {
147 return Err(DecodeError::LimitExceeded);
148 }
149 state.trunc_16 = true;
150 state.expand_alpha = true;
151 decode_data::<EmitRgba8>(&mut state, decomp, extra, target).ok_or(DecodeError::CorruptData)?;
152 Ok((w, h, state.has_alpha))
153}
154
155struct State<'a> {
156 header: Header,
157 data: &'a [u8],
158 palette: &'a [u8],
159 trans: &'a [u8],
160 gamma: Option<f32>,
161 effective_depth: u8,
162 bpp: usize,
163 channels: usize,
164 pitch: usize,
165 extra_bytes: usize,
166 bwidth: usize,
167 has_alpha: bool,
168 trunc_16: bool,
169 expand_alpha: bool,
170}
171
172impl<'a> State<'a> {
173 fn new(data: &'a [u8], decomp: &mut Vec<u8>) -> Result<Self, DecodeError> {
174 let header = Header::decode(data).ok_or(DecodeError::CorruptData)?;
175 let mut this = Self {
176 header,
177 data,
178 palette: &[],
179 trans: &[],
180 gamma: None,
181 effective_depth: header.depth,
182 bpp: 0,
183 channels: 0,
184 pitch: 0,
185 extra_bytes: 0,
186 bwidth: 0,
187 has_alpha: false,
188 trunc_16: false,
189 expand_alpha: false,
190 };
191 let w = header.width as usize;
192 let h = header.height as usize;
193 if w == 0 || h == 0 {
194 return Ok(this);
195 }
196 use ColorType::*;
197 let (channels, has_alpha) = match header.color_type {
198 TrueColor => (3, false),
199 TrueColorAlpha => (4, true),
200 GreyscaleAlpha => (2, true),
201 Greyscale => (1, false),
202 Indexed => (1, false),
203 };
204 this.has_alpha = has_alpha;
205 this.bpp = this.header.depth as usize * channels;
206 this.pitch = (w * this.bpp + 7) / 8;
207 this.bwidth = (this.bpp + 7) / 8;
208 this.extra_bytes = this.pitch * 2 + w * 8;
209 decomp.clear();
210 decomp.reserve(this.extra_bytes + (this.pitch + 1) * h);
211 let limit = data.len();
212 let mut offset = 33;
213 let mut dec = yazi::Decoder::new();
214 dec.set_format(yazi::Format::Zlib);
215 let mut stream = dec.stream_into_vec(decomp);
216 loop {
217 if offset + 8 > limit {
218 return Err(DecodeError::CorruptData);
219 }
220 let len = get_u32be(data, offset) as usize;
221 offset += 4;
222 let ty = get_u32be(data, offset);
223 offset += 4;
224 if offset + len > limit {
225 return Err(DecodeError::CorruptData);
226 }
227 let bytes = data
228 .get(offset..offset + len)
229 .ok_or(DecodeError::CorruptData)?;
230 const PLTE: u32 = chunk_name(b"PLTE");
231 const TRNS: u32 = chunk_name(b"tRNS");
232 const IDAT: u32 = chunk_name(b"IDAT");
233 const GAMA: u32 = chunk_name(b"gAMA");
234 const IEND: u32 = chunk_name(b"IEND");
235 match ty {
236 PLTE => this.palette = bytes,
237 TRNS => this.trans = bytes,
238 IDAT => {
239 stream.write(bytes)?;
240 }
241 GAMA => {
242 if bytes.len() > 4 && this.gamma.is_none() {
243 this.gamma = Some(get_u32be(bytes, 0) as f32 / 100000.);
244 }
245 }
246 IEND => break,
247 _ => {}
248 }
249 offset += len + 4;
250 }
251 stream.finish()?;
252 if header.color_type == Indexed {
253 if this.palette.is_empty() {
254 return Err(DecodeError::CorruptData);
255 }
256 if !this.trans.is_empty() {
257 this.has_alpha = true;
258 }
259 }
260 Ok(this)
261 }
262}
263
264fn decode_data<E: Emit>(
265 state: &mut State,
266 decomp: &mut [u8],
267 extra: &mut [u8],
268 target: &mut [u8],
269) -> Option<()> {
270 let has_palette = !state.palette.is_empty();
271 let w = state.header.width as usize;
272 let h = state.header.height as usize;
273 let (mut line, extra) = extra.split_at_mut(state.pitch);
274 let (mut prev_line, out_line) = extra.split_at_mut(state.pitch);
275 let depth = state.header.depth;
276 let bpp = state.bpp;
277 let pitch = state.pitch;
278 let bwidth = state.bwidth;
279 let trunc_16 = state.trunc_16;
280 if state.header.interlaced {
281 const ROW_START: [u8; 7] = [0, 0, 4, 0, 2, 0, 1];
282 const ROW_INCREMENT: [u8; 7] = [8, 8, 8, 4, 4, 2, 2];
283 const COL_START: [u8; 7] = [0, 4, 0, 2, 0, 1, 0];
284 const COL_INCREMENT: [u8; 7] = [8, 8, 4, 4, 2, 2, 1];
285 let mut pass = 0;
286 let mut y = 0;
287 let mut offset = 0;
288 loop {
289 let cols = match pass {
290 0 => (w + 7) / 8,
291 1 => (w + 3) / 8,
292 2 => (w + 3) / 4,
293 3 => (w + 1) / 4,
294 4 => (w + 1) / 2,
295 5 => w / 2,
296 6 => w,
297 _ => return None,
298 };
299 if cols == 0 {
300 pass += 1;
301 continue;
302 }
303 let start = COL_START[pass] as usize;
304 let inc = COL_INCREMENT[pass] as usize;
305 let row_inc = ROW_INCREMENT[pass] as usize;
306 while y < h {
307 let pitch = (cols * bpp + 7) / 8;
308 let end = offset + pitch + 1;
309 let source = decomp.get(offset..end)?;
310 offset = end;
311 let ty = source[0];
312 defilter(
313 ty,
314 source.get(1..)?,
315 line.get_mut(..pitch)?,
316 prev_line.get(..pitch)?,
317 bwidth,
318 )?;
319 if depth == 8 {
320 E::emit(state, line, target, start, y, w, inc, cols)?;
321 } else {
322 normalize(line, out_line, depth, has_palette, cols, trunc_16)?;
323 E::emit(state, out_line, target, start, y, w, inc, cols)?;
324 }
325 core::mem::swap(&mut prev_line, &mut line);
326 y += row_inc;
327 }
328 if pass == 6 {
329 break;
330 }
331 pass += 1;
332 y = ROW_START[pass] as usize;
333 for byte in prev_line.iter_mut() {
334 *byte = 0;
335 }
336 }
337 } else if depth == 8 {
338 for y in 0..h {
339 let offset = y * (pitch + 1);
340 let end = offset + pitch + 1;
341 let source = decomp.get(offset..end)?;
342 let ty = *source.get(0)?;
343 defilter(ty, source.get(1..)?, line, prev_line, bwidth)?;
344 E::emit(state, line, target, 0, y, w, 1, w)?;
345 core::mem::swap(&mut prev_line, &mut line);
346 }
347 } else {
348 for y in 0..h {
349 let offset = y * (pitch + 1);
350 let end = offset + pitch + 1;
351 let source = decomp.get(offset..end)?;
352 let ty = *source.get(0)?;
353 defilter(ty, source.get(1..)?, line, prev_line, bwidth)?;
354 normalize(line, out_line, depth, has_palette, w, trunc_16)?;
355 E::emit(state, out_line, target, 0, y, w, 1, w)?;
356 core::mem::swap(&mut prev_line, &mut line);
357 }
358 }
359 Some(())
360}
361
362#[cfg(target_endian = "little")]
363const IS_LITTLE_ENDIAN: bool = true;
364
365#[cfg(not(target_endian = "little"))]
366const IS_LITTLE_ENDIAN: bool = false;
367
368fn defilter(ty: u8, source: &[u8], dest: &mut [u8], last: &[u8], bwidth: usize) -> Option<()> {
369 let len = source.len();
370 match ty {
371 0 => {
372 dest.copy_from_slice(source);
373 }
374 1 => {
375 dest.get_mut(..bwidth)?
376 .copy_from_slice(source.get(..bwidth)?);
377 for i in bwidth..len {
378 dest[i] = source[i].wrapping_add(dest[i - bwidth]);
379 }
380 }
381 2 => {
382 for ((dest, source), last) in dest.iter_mut().zip(source.iter()).zip(last.iter()) {
383 *dest = source.wrapping_add(*last);
384 }
385 }
386 3 => {
387 for i in 0..bwidth {
388 dest[i] = source[i].wrapping_add((last[i] as u32 / 2) as u8);
389 }
390 for i in bwidth..len {
391 dest[i] = source[i].wrapping_add(
392 ((dest[i - bwidth] as u32).wrapping_add(last[i] as u32) / 2) as u8,
393 );
394 }
395 }
396 4 => {
397 for i in 0..bwidth {
398 dest[i] = source[i].wrapping_add(last[i]);
399 }
400 for i in bwidth..len {
401 dest[i] =
402 source[i].wrapping_add(paeth(dest[i - bwidth], last[i], last[i - bwidth]));
403 }
404 }
405 _ => return None,
406 }
407 Some(())
408}
409
410fn normalize(
411 source: &[u8],
412 dest: &mut [u8],
413 depth: u8,
414 palette: bool,
415 width: usize,
416 trunc_16: bool,
417) -> Option<()> {
418 match depth {
419 16 => {
420 if trunc_16 {
421 for (i, d) in dest.iter_mut().enumerate() {
422 *d = source[i * 2];
423 }
424 } else if IS_LITTLE_ENDIAN {
425 for (s, d) in source.chunks(2).zip(dest.chunks_mut(2)) {
426 d[1] = s[0];
427 d[0] = s[1];
428 }
429 } else {
430 dest.copy_from_slice(source);
431 }
432 }
433 8 => {
434 dest.copy_from_slice(source);
435 }
436 4 => {
437 let conv = if !palette { 17 } else { 1 };
438 for (i, d) in dest.get_mut(..width)?.iter_mut().enumerate() {
439 *d = (source[i / 2] >> (4 - i % 2 * 4) & 15) * conv;
440 }
441 }
442 2 => {
443 let conv = if !palette { 85 } else { 1 };
444 for (i, d) in dest.get_mut(..width)?.iter_mut().enumerate() {
445 *d = (source[i / 4] >> (6 - i % 4 * 2) & 3) * conv;
446 }
447 }
448 1 => {
449 let conv = if !palette { 255 } else { 1 };
450 for (i, d) in dest.get_mut(..width)?.iter_mut().enumerate() {
451 *d = (source[i / 8] >> (7 - i % 8) & 1) * conv;
452 }
453 }
454 _ => {}
455 }
456 Some(())
457}
458
459trait Emit {
460 fn emit(
461 state: &State,
462 source: &[u8],
463 image: &mut [u8],
464 x: usize,
465 y: usize,
466 width: usize,
467 inc: usize,
468 len: usize,
469 ) -> Option<()>;
470}
471
472struct EmitRgba8;
473
474impl Emit for EmitRgba8 {
475 fn emit(
476 state: &State,
477 source: &[u8],
478 image: &mut [u8],
479 x: usize,
480 y: usize,
481 width: usize,
482 inc: usize,
483 len: usize,
484 ) -> Option<()> {
485 use ColorType::*;
486 let src = source;
487 let mut out = y * width * 4 + x * 4;
488 let mut i = 0;
489 match state.header.color_type {
490 Indexed => {
491 let palette = state.palette;
492 let trans = state.trans;
493 let palette_len = palette.len();
494 let trans_len = trans.len();
495 for _ in 0..len {
496 let t = src[i] as usize;
497 let p = t * 3;
498 if p + 2 >= palette_len {
499 image[out] = 0;
500 image[out + 1] = 0;
501 image[out + 2] = 0;
502 } else {
503 image[out] = palette[p];
504 image[out + 1] = palette[p + 1];
505 image[out + 2] = palette[p + 2];
506 }
507 if t >= trans_len {
508 image[out + 3] = 255;
509 } else {
510 image[out + 3] = trans[t];
511 }
512 i += 1;
513 out += 4 * inc;
514 }
515 }
516 TrueColor => {
517 for _ in 0..len {
518 image[out] = src[i];
519 image[out + 1] = src[i + 1];
520 image[out + 2] = src[i + 2];
521 image[out + 3] = 255;
522 i += 3;
523 out += 4 * inc;
524 }
525 }
526 TrueColorAlpha => {
527 for _ in 0..len {
528 image[out] = src[i];
529 image[out + 1] = src[i + 1];
530 image[out + 2] = src[i + 2];
531 image[out + 3] = src[i + 3];
532 i += 4;
533 out += 4 * inc;
534 }
535 }
536 Greyscale => {
537 for c in src[..len].iter().copied() {
538 image[out] = c;
539 image[out + 1] = c;
540 image[out + 2] = c;
541 image[out + 3] = 255;
542 out += 4 * inc;
543 }
544 }
545 GreyscaleAlpha => {
546 let mut i = 0;
547 for _ in 0..len {
548 let c = src[i];
549 image[out] = c;
550 image[out + 1] = c;
551 image[out + 2] = c;
552 image[out + 3] = src[i + 1];
553 i += 2;
554 out += 4 * inc;
555 }
556 }
557 }
558 Some(())
559 }
560}
561
562#[inline(always)]
563fn paeth(a: u8, b: u8, c: u8) -> u8 {
564 let pa = ((b as i32).wrapping_sub(c as i32)).abs();
565 let pb = ((a as i32).wrapping_sub(c as i32)).abs();
566 let pc = ((a as i32)
567 .wrapping_add(b as i32)
568 .wrapping_sub(c as i32)
569 .wrapping_sub(c as i32))
570 .abs();
571 if pc < pa && pc < pb {
572 c
573 } else if pb < pa {
574 b
575 } else {
576 a
577 }
578}
579
580fn get_u32be(buf: &[u8], offset: usize) -> u32 {
581 (buf[offset] as u32) << 24
582 | (buf[offset + 1] as u32) << 16
583 | (buf[offset + 2] as u32) << 8
584 | buf[offset + 3] as u32
585}
586
587const fn chunk_name(bytes: &[u8; 4]) -> u32 {
588 (bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 | bytes[3] as u32
589}