summaryrefslogtreecommitdiff
path: root/image/png/ihdr.ha
diff options
context:
space:
mode:
authorLassi Pulkkinen <lassi@pulk.fi>2024-10-31 03:11:21 +0200
committerLassi Pulkkinen <lassi@pulk.fi>2024-10-31 03:51:35 +0200
commitae44478b30d890fe0fb04022f44d474dcdcc3f9d (patch)
tree5f462459ae4b47d22114eed717d1382d08cf4dfe /image/png/ihdr.ha
Initial commit (import old repo)HEADmain
Diffstat (limited to 'image/png/ihdr.ha')
-rw-r--r--image/png/ihdr.ha114
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);
+};