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/load.ha |
Diffstat (limited to 'image/png/load.ha')
-rw-r--r-- | image/png/load.ha | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/image/png/load.ha b/image/png/load.ha new file mode 100644 index 0000000..d3578f8 --- /dev/null +++ b/image/png/load.ha @@ -0,0 +1,88 @@ +use errors; +use io; +use memio; + +// A decoded PNG image. +export type image = struct { + // Image header + ihdr: ihdr, + // RGBA palette data (if present, otherwise an empty slice) + palette: []u32, + // Raw pixel data + pixels: []u8, +}; + +// Reads a PNG image from an [[io::handle]], returning the decoded header, +// palette (if present), and raw pixel data. The caller must pass the return +// value to [[image_finish]] to discard resources associated with the image. +export fn load(in: io::handle) (image | error) = { + let success = false; + + const reader = newreader(in)?; + + match (nextchunk(&reader)?) { + case io::EOF => + return invalid; + case let ctype: u32 => + if (ctype != IHDR) { + return invalid; + }; + }; + const ihdr = ihdr_read(&reader)?; + let pixels: ([]u8 | void) = void; + defer if (!success && pixels is []u8) free(pixels as []u8); + let palette: []u32 = []; + defer if (!success) free(palette); + + for (true) match (nextchunk(&reader)?) { + case io::EOF => + return invalid; + case let ctype: u32 => + switch (ctype) { + case IHDR => + return invalid; + case PLTE => + if (len(palette) != 0) { + return invalid; + }; + let plte = new_plte_reader(&reader)?; + palette = alloc([0...], plte_ncolors(&plte)); + plte_read(&plte, palette)?; + case IDAT => + if (pixels is []u8) { + return invalid; + }; + let pixbuf: []u8 = alloc([0...], decoder_bufsiz(&ihdr)); + defer free(pixbuf); + let idat = new_idat_reader(&reader); + let dec = newdecoder(&idat, &ihdr, pixbuf)?; + defer io::close(&dec)!; + pixels = io::drain(&dec)?; + case IEND => + iend_read(&reader)?; + break; + case => + if (is_critical(ctype)) { + return unsupported; + }; + io::copy(io::empty, &reader)?; + }; + }; + + if (pixels is void) { + return invalid; + }; + + success = true; + return image { + ihdr = ihdr, + palette = palette, + pixels = pixels as []u8, + }; +}; + +// Frees resources associated with an [[image]]. +export fn image_finish(img: *image) void = { + free(img.palette); + free(img.pixels); +}; |