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); };