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); };