diff options
author | Lassi Pulkkinen <lassi@pulk.fi> | 2024-10-31 03:11:21 +0200 |
---|---|---|
committer | Lassi Pulkkinen <lassi@pulk.fi> | 2024-10-31 03:51:35 +0200 |
commit | ae44478b30d890fe0fb04022f44d474dcdcc3f9d (patch) | |
tree | 5f462459ae4b47d22114eed717d1382d08cf4dfe /image/png/plte.ha |
Diffstat (limited to 'image/png/plte.ha')
-rw-r--r-- | image/png/plte.ha | 95 |
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]); + }; +}; |