summaryrefslogtreecommitdiff
path: root/image/png/plte.ha
diff options
context:
space:
mode:
Diffstat (limited to 'image/png/plte.ha')
-rw-r--r--image/png/plte.ha95
1 files changed, 95 insertions, 0 deletions
diff --git a/image/png/plte.ha b/image/png/plte.ha
new file mode 100644
index 0000000..a19a263
--- /dev/null
+++ b/image/png/plte.ha
@@ -0,0 +1,95 @@
+use io;
+use memio;
+
+export type plte_reader = struct {
+ src: *reader,
+};
+
+// Returns a new PLTE reader for a [[chunk_reader]].
+export fn new_plte_reader(src: *reader) (plte_reader | invalid) = {
+ assert(chunk_type(src) == PLTE,
+ "Attempted to create PLTE reader for non-PLTE chunk");
+ const length = chunk_length(src);
+ if (length % 3 != 0) {
+ return invalid;
+ };
+ const ncolor = length / 3;
+ if (ncolor < 1 && ncolor > 256) {
+ return invalid;
+ };
+ return plte_reader {
+ src = src,
+ };
+};
+
+// Returns the number of colors defined by this palette. The return value will
+// be between 1 and 256 (inclusive), so should the caller wish to statically
+// allocate a suitable buffer, [256]u32 will suffice for all cases.
+export fn plte_ncolors(pr: *plte_reader) size = {
+ return chunk_length(pr.src) / 3;
+};
+
+// Reads the palette from a PLTE chunk. The caller must provide a buffer equal
+// in length to the return value of [[plte_ncolors]], which will be filled with
+// RGB colors, such that the most significant byte is red and the least
+// significant byte is unused (set to 0xFF). Returns the number of colors in the
+// palette.
+export fn plte_read(pr: *plte_reader, buf: []u32) (size | error) = {
+ const ncolor = plte_ncolors(pr);
+ assert(len(buf) == ncolor, "Invalid buffer size for plte_read");
+
+ static let rbuf: [3 * 256]u8 = [0...];
+ for (let i = 0z; i < ncolor) {
+ let n = (ncolor - i) * 3;
+ if (n > len(rbuf)) {
+ n = len(rbuf);
+ };
+ match (io::readall(pr.src, rbuf[..n])?) {
+ case io::EOF =>
+ return invalid;
+ case size =>
+ yield;
+ };
+ for (let q = 0z; q < n / 3; q += 1) {
+ const r = rbuf[q * 3 + 0]: u32;
+ const g = rbuf[q * 3 + 1]: u32;
+ const b = rbuf[q * 3 + 2]: u32;
+ buf[i + q] = (r << 24) | (g << 16) | (b << 8) | 0xFF;
+ };
+ i += n / 3;
+ };
+ // required so that the checksum gets read
+ assert(io::read(pr.src, rbuf[..1])? is io::EOF);
+ return ncolor;
+};
+
+@test fn plte_reader() void = {
+ const src = memio::fixed(palette_sample);
+ const read = newreader(&src) as reader;
+ assert(nextchunk(&read) as u32 == IHDR);
+ io::copy(io::empty, &read)!;
+
+ for (true) {
+ const ctype = nextchunk(&read) as u32;
+ if (ctype == PLTE) {
+ break;
+ };
+ io::copy(io::empty, &read)!;
+ };
+
+ const plte = new_plte_reader(&read)!;
+
+ let buf: []u32 = alloc([0...], plte_ncolors(&plte));
+ defer free(buf);
+ plte_read(&plte, buf)!;
+
+ const expected: [_]u32 = [
+ 0x2200FFFF, 0x00FFFFFF, 0x8800FFFF, 0x22FF00FF, 0x0099FFFF,
+ 0xFF6600FF, 0xDD00FFFF, 0x77FF00FF, 0xFF0000FF, 0x00FF99FF,
+ 0xDDFF00FF, 0xFF00BBFF, 0xFFBB00FF, 0x0044FFFF, 0x00FF44FF,
+ ];
+ assert(len(expected) == len(buf));
+ for (let i = 0z; i < len(expected); i += 1) {
+ assert(buf[i] == expected[i]);
+ };
+};