/** * A miminal use of libpng for UIUC's CS418 class. * * See uselibpng.c for implementation notes */ #ifdef __cplusplus extern "C" { #endif #include <stdint.h> /** * RGBA pixel with several ways of looking at it: * * pixel.p[0] is the first (red) channel of the pixel * pixel.red is the red (first) channel of the pixel * pixel.r is the red (first) channel of the pixel */ typedef union { uint8_t p[4]; struct { uint8_t r, g, b, a; }; struct { uint8_t red, green, blue, alpha; }; } pixel_t; /** * A minimal struct for storing PNG data. C/C++ has poor support for 2D arrays, * so use the provided image_xy macro (C) or the Image wrapper class (C++) for access. */ typedef struct { uint32_t height, width; pixel_t *rgba; } image_t; /** * Load a PNG from the given file. * * ~~~~ * image_t *img = load_image("existing_image.png"); * if (img == NULL) { fprintf(stderr, "%s was not a PNG file", "existing_image.png"); } * for(int y = 0; y < img->height; y += 1) { * for(int x = 0; x < img->width; x += 1) { * pixel_t pixel = pixel_xy(img, x, y); * } * } * ~~~~ */ image_t *load_image(const char *filename); /** * Save a PNG to the given file. * * ~~~~ * image_t *img = ... * save_image(img, "new_image.png") * ~~~~ */ void save_image(image_t *img, const char *filename); /** * Allocate an image with the given width and height. * * ~~~~ * image_t *img = new_image(1280, 720); * for(int y = 0; y < img->height; y += 1) { * for(int x = 0; x < img->width; x += 1) { * pixel_xy(img, x, y).red = x & 0xFF; * pixel_xy(img, x, y).g = y & 0xFF; * pixel_xy(img, x, y).p[2] = ((x+y)>>8) & 0xFF; * pixel_xy(img, x, y).alpha = (x^y)&1 ? 0xFF : 0x00; * } * } * save_image(img, "demo_image.png"); * ~~~~ */ image_t *new_image(uint32_t width, uint32_t height); /** * Deallocate memory used to store the image */ void free_image(image_t *img); #ifdef __cplusplus } // end extern "C" #endif #ifdef __cplusplus /** * A class wrapper around the image_t structure and its functions * * ~~~~ * Image img = Image("image_to_read.png"); * * for(uint32_t y=0; y<img.height(); y+=1) { * for(uint32_t x=0; x<img.width(); x+=1) { * img[y][x].red ^= img[y][x].alpha; * img[y][x].g ^= 0xaa; * img[y][x].blue = 0xFF - img[y][x].blue; * img[y][x].p[3] = 0xFF; * } * } * * img.save("image_to_write.png"); * ~~~~ */ class Image { public: /// Create a blank (transparent black) image of the given size Image(uint32_t width, uint32_t height) : data(new_image(width, height)) {} /** * Create an image by reading a PNG file * if you have a std::string, use `Image(string.c_str())` */ Image(const char *filename) : data(load_image(filename)) { } /// destructor ~Image() { free_image(data); } /// array access to image. Use as `img[y][x].red` or the like. Note the y-first order! pixel_t* operator[](uint32_t y) { return &(data->rgba[data->width * y]); } /// store the image in a PNG file void save(const char *filename) const { save_image(data, filename); } /// get the width of the image uint32_t width() const { return data->width; } /// get the height of the image uint32_t height() const { return data->height; } private: image_t* data; }; #else /** * A helper macro for accessing the image. * pixel_xy(img, x, y).red = 255; * opacity = pixel_xy(img, x, y).alpha; */ #define pixel_xy(img,x,y) (img)->rgba[(x) + (y)*(img)->width] #endif