builtin-programs/image/png-lib.folk

When the image library is /imageLib/ {
set cc [C]
$cc extend $imageLib
$cc endcflags -lpng
$cc include <stdlib.h>
$cc include <string.h>

$cc code {
    #undef EXTERN
    #include <png.h>
    #include <stdint.h>

void png(FILE* dest, uint8_t* data, uint32_t components, uint32_t bytesPerRow, uint32_t width, uint32_t height) {
    png_structp png_w = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    png_infop info_w = png_create_info_struct(png_w);

    if (components == 3)
        png_set_IHDR(png_w, info_w, width, height, 8, PNG_COLOR_TYPE_RGB,
            PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
            PNG_FILTER_TYPE_DEFAULT);
    else if (components == 1)
        png_set_IHDR(png_w, info_w, width, height, 8, PNG_COLOR_TYPE_GRAY,
            PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
            PNG_FILTER_TYPE_DEFAULT);
    else FOLK_ERROR("Unsupported number of components");

    png_bytep* row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * height);
    for (int i = 0; i < height; i++) {
        row_pointers[i] = data + i * bytesPerRow;
    }

    png_init_io(png_w, dest);
    png_set_rows(png_w, info_w, row_pointers);
    png_write_png(png_w, info_w, PNG_TRANSFORM_IDENTITY, NULL);

    free(row_pointers);
}

}

$cc proc saveAsPng {Image im char* filename} void {
    FILE* out = fopen(filename, "wb");
    FOLK_ENSURE(out != NULL);
    png(out, im.data, im.components, im.bytesPerRow, im.width, im.height);
    fclose(out);
}

$cc proc loadPng {char* filename} Image {
    FILE* file = fopen(filename, "rb");
    if (!file) {
        FOLK_ERROR("Error opening file: %s\n", filename);
    }

    png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png) {
        FOLK_ERROR("Error reading png from file: %s it's not a png\n", filename);
    }

    png_infop info = png_create_info_struct(png);
    if(!info) {
        FOLK_ERROR("Error reading png from file: %s no info?\n", filename);
    }

    if(setjmp(png_jmpbuf(png))) {
        FOLK_ERROR("Error reading png from file: %s setjmp error?\n", filename);
    }

    png_init_io(png, file);
    png_read_info(png, info);

    Image ret;
    ret.width = png_get_image_width(png, info);
    ret.height = png_get_image_height(png, info);
    int bytes_per_pixel =  png_get_channels(png, info);
    ret.components = png_get_channels(png, info);
    ret.bytesPerRow = ret.width * bytes_per_pixel;
    ret.data = malloc(ret.bytesPerRow * ret.height);

    // Iterate over the rows and read the image data into the buffer.
    for (int i = 0; i < ret.height; i++) {
        png_read_row(png, ret.data + (i * ret.bytesPerRow), NULL);
    }

    // Close the PNG file.
    png_destroy_read_struct(&png, &info, NULL);

    if (ret.components == 4) {
        // Transcode from RGBA to RGB (we don't support RGBA yet.)
        for(int i=0; i < ret.width*ret.height; i++) {
            int r = ret.data[i*4+0],
                g = ret.data[i*4+1],
                b = ret.data[i*4+2],
                a = ret.data[i*4+3];

            ret.data[i*3+0] = r * a / 255;
            ret.data[i*3+1] = g * a / 255;
            ret.data[i*3+2] = b * a / 255;
        }
        ret.components = 3;
        ret.bytesPerRow = ret.width * ret.components;
    }

    return ret;
}

set pngLib [$cc compile]
Claim the png library is $pngLib
}