1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
// lib.rs
// Aldaron's Codec Interface - PNG
// Copyright 2017 (c) Aldaron's Tech
// Copyright 2017 (c) Jeron Lau
// Licensed under the MIT LICENSE

extern crate png;

/// The errors that can be returned if `decode()` fails.
#[derive(Debug)]
pub enum DecodeErrorPNG {
	/// Not a PNG file (bad header)
	NotPNG,
	/// Dimensions are not numbers
	BadNum,
	/// Not yet implemented
	GrayscaleNYI,
	/// Not yet implemented
	IndexedNYI,
	/// Not yet implemented
	AGrayscaleNYI,
	/// Bits NYI
	BitsNYI,
}

impl ::std::fmt::Display for DecodeErrorPNG {
	fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
		write!(f, "Couldn't parse PNG because: {}", match *self {
			DecodeErrorPNG::NotPNG => "Not a PNG file (bad header)",
			DecodeErrorPNG::BadNum => "Dimensions are not numbers",
			DecodeErrorPNG::GrayscaleNYI => "NYI: Grayscale",
			DecodeErrorPNG::IndexedNYI => "NYI: Indexed",
			DecodeErrorPNG::AGrayscaleNYI => "NYI: AGrayscale",
			DecodeErrorPNG::BitsNYI => "NYI: bad bits",
		})
	}
}

/// Decode PNG data.  On success, returns image as vector:
/// `[width, height, first BGRA pixel .. last BGRA pixel]`
pub fn decode(png: &'static [u8]) -> Result<Vec<u32>, DecodeErrorPNG> {
	use png::ColorType::*;

	let decoder = png::Decoder::new(png);
	let (info, mut reader) = decoder.read_info().unwrap();

	let mut buf = vec![0; info.buffer_size()];
	reader.next_frame(&mut buf).unwrap();

	let size = (info.width * info.height) as usize;
	let mut out = Vec::with_capacity(size + 2);

	unsafe {
		out.set_len(size + 2);
	}

	out[0] = info.width;
	out[1] = info.height;

	let (color, bit) = reader.output_color_type();

	match color {
		RGB => {
			let mut pixel: [u8; 4] = unsafe {
				::std::mem::uninitialized()
			};

			for i in 0..size {
				pixel[0] = buf[i * 3 + 2];
				pixel[1] = buf[i * 3 + 1];
				pixel[2] = buf[i * 3 + 0];
				pixel[3] = 0xFF;

				out[2 + i] = unsafe {
					::std::mem::transmute(pixel)
				};
			}
		},
		RGBA => {
			let mut pixel: [u8; 4] = unsafe {
				::std::mem::uninitialized()
			};

			for i in 0..size {
				pixel[0] = buf[i * 4 + 2];
				pixel[1] = buf[i * 4 + 1];
				pixel[2] = buf[i * 4 + 0];
				pixel[3] = buf[i * 4 + 3];

				out[2 + i] = unsafe {
					::std::mem::transmute(pixel)
				};
			}
		},
		Grayscale => return Err(DecodeErrorPNG::GrayscaleNYI),
		Indexed => return Err(DecodeErrorPNG::IndexedNYI),
		GrayscaleAlpha => return Err(DecodeErrorPNG::AGrayscaleNYI),
	}

	if bit != png::BitDepth::Eight {
		return Err(DecodeErrorPNG::BitsNYI)
	}

	Ok(out)
}