summaryrefslogtreecommitdiff
path: root/image/png/idat.ha
blob: dd2e749a9421d99a95cbe7cba1dc7cb9773d0a95 (plain)
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
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));
};