diff options
Diffstat (limited to 'image/png/ihdr.ha')
-rw-r--r-- | image/png/ihdr.ha | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/image/png/ihdr.ha b/image/png/ihdr.ha new file mode 100644 index 0000000..bbb8e3e --- /dev/null +++ b/image/png/ihdr.ha @@ -0,0 +1,114 @@ +use endian; +use io; +use memio; + +// A PNG IHDR chunk. +export type ihdr = struct { + width: u32, + height: u32, + bitdepth: u8, + colortype: colortype, + compression: compression, + filter_mode: filter_mode, + interlace: interlace, +}; + +// Pixel formats supported by PNG files. +export type colortype = enum u8 { + GRAYSCALE = 0, + RGB = 2, + PLTE = 3, + GRAYALPHA = 4, + RGBA = 6, +}; + +// Compression types supported by PNG files. +export type compression = enum u8 { + DEFLATE = 0, +}; + +export type filter_mode = enum u8 { + ADAPTIVE = 0, +}; + +// Interlace types supported by PNG files. +export type interlace = enum u8 { + NONE = 0, + ADAM7 = 1, +}; + +// Reads an [[ihdr]] from a [[reader]]. +export fn ihdr_read(src: *reader) (ihdr | error) = { + assert(chunk_type(src) == IHDR, + "Attempted to call ihdr_read on non-IHDR chunk"); + let buf: [13]u8 = [0...]; + match (io::readall(src, buf)?) { + case io::EOF => + return invalid; + case size => + yield; + }; + // Read checksum + if (!(io::read(src, &[0u8])? is io::EOF)) { + return invalid; + }; + const hdr = ihdr { + width = endian::begetu32(buf[..4]), + height = endian::begetu32(buf[4..8]), + bitdepth = buf[8], + colortype = buf[9]: colortype, + compression = buf[10]: compression, + filter_mode = buf[11]: filter_mode, + interlace = buf[12]: interlace, + }; + if ((hdr.width | hdr.height) & 0x80000000 > 0) { + return invalid; + }; + switch (hdr.colortype) { + case colortype::GRAYSCALE => + if (hdr.bitdepth != 1 && hdr.bitdepth != 2 && hdr.bitdepth != 4 + && hdr.bitdepth != 8 && hdr.bitdepth != 16) { + return invalid; + }; + case colortype::RGB => + if (hdr.bitdepth != 8 && hdr.bitdepth != 16) { + return invalid; + }; + case colortype::PLTE => + if (hdr.bitdepth != 1 && hdr.bitdepth != 2 + && hdr.bitdepth != 4 && hdr.bitdepth != 8) { + return invalid; + }; + case colortype::GRAYALPHA => + if (hdr.bitdepth != 8 && hdr.bitdepth != 16) { + return invalid; + }; + case colortype::RGBA => + if (hdr.bitdepth != 8 && hdr.bitdepth != 16) { + return invalid; + }; + case => + return invalid; + }; + if (hdr.compression != compression::DEFLATE) { + return unsupported; + }; + if (hdr.filter_mode != filter_mode::ADAPTIVE) { + return unsupported; + }; + if (hdr.interlace > interlace::ADAM7) { + return unsupported; + }; + return hdr; +}; + +@test fn ihdr_reader() void = { + const src = memio::fixed(simple_png); + const read = newreader(&src) as reader; + assert(nextchunk(&read) as u32 == IHDR); + const ihdr = ihdr_read(&read)!; + assert(ihdr.width == 4 && ihdr.height == 4); + assert(ihdr.colortype == colortype::RGB); + assert(ihdr.filter_mode == filter_mode::ADAPTIVE); + assert(ihdr.interlace == interlace::NONE); +}; |