summaryrefslogtreecommitdiff
path: root/image/png/idat.ha
diff options
context:
space:
mode:
Diffstat (limited to 'image/png/idat.ha')
-rw-r--r--image/png/idat.ha82
1 files changed, 82 insertions, 0 deletions
diff --git a/image/png/idat.ha b/image/png/idat.ha
new file mode 100644
index 0000000..dd2e749
--- /dev/null
+++ b/image/png/idat.ha
@@ -0,0 +1,82 @@
+use bufio;
+use bytes;
+use io;
+use memio;
+
+export type idat_reader = struct {
+ vtable: io::stream,
+ src: *reader,
+};
+
+// Returns a new IDAT reader, which reads consecutive IDAT chunks from a
+// [[reader]] and outputs their raw (compressed) contents as a continuous data
+// stream. Instead of reading from this directly, users are advised to construct
+// a [[decoder]] with [[newdecoder]], and use that to read the uncompressed
+// pixel data.
+//
+// This stream does not need to be closed.
+export fn new_idat_reader(src: *reader) idat_reader = {
+ assert(chunk_type(src) == IDAT,
+ "Attempted to create IDAT reader for non-IDAT chunk");
+ return idat_reader {
+ vtable = &idat_reader_vtable,
+ src = src,
+ };
+};
+
+const idat_reader_vtable: io::vtable = io::vtable {
+ reader = &idat_read,
+ ...
+};
+
+fn idat_read(
+ st: *io::stream,
+ buf: []u8,
+) (size | io::EOF | io::error) = {
+ let st = st: *idat_reader;
+
+ assert(st.vtable == &idat_reader_vtable);
+
+ for (true) match (io::read(st.src, buf)?) {
+ case io::EOF =>
+ match (nextchunk(st.src)) {
+ case let err: (invalid | unsupported) =>
+ return wraperror(err);
+ case let err: io::error =>
+ return err;
+ case io::EOF =>
+ return io::EOF;
+ case let ctype: u32 =>
+ if (ctype != IDAT) {
+ return io::EOF;
+ };
+ };
+ case let z: size =>
+ return z;
+ };
+};
+
+@test fn idat_reader() void = {
+ const src = memio::fixed(no_filtering);
+ const read = newreader(&src) as reader;
+ assert(nextchunk(&read) as u32 == IHDR);
+ const ihdr = ihdr_read(&read)!;
+
+ let pixbuf: []u8 = alloc([0...], decoder_bufsiz(&ihdr));
+ defer free(pixbuf);
+
+ for (true) {
+ const ctype = nextchunk(&read) as u32;
+ if (ctype == IDAT) {
+ break;
+ };
+ io::copy(io::empty, &read)!;
+ };
+
+ let idat = new_idat_reader(&read);
+ let dec = newdecoder(&idat, &ihdr, pixbuf)!;
+ defer io::close(&dec)!;
+ const pixels = io::drain(&dec)!;
+ defer free(pixels);
+ assert(bytes::equal(pixels, no_filtering_data));
+};