#include #include #include #include #include #include #include typedef unsigned char byte; static void abort_(const char *s, ...) { va_list args; va_start(args, s); vfprintf(stderr, s, args); fprintf(stderr, "\n"); va_end(args); abort(); } struct image_info { uint32_t width; uint32_t height; byte color_type; byte bit_depth; byte** data; }; static void image_destroy(struct image_info* info) { uint32_t y; for (y = 0; y < info->height; y++) { free(info->data[y]); } free(info->data); } static struct image_info read_png_file(const char* file_name) { struct image_info info; FILE* fp; png_byte header[8]; png_structp png_ptr; png_infop info_ptr; size_t rowbytes; uint32_t y; /* open file and test for it being a png */ fp = fopen(file_name, "rb"); if (!fp) { abort_("[read_png_file] File %s could not be opened for reading", file_name); } fread(header, 1, sizeof(header), fp); if (png_sig_cmp(header, 0, sizeof(header))) { abort_("[read_png_file] File %s is not recognized as a PNG file", file_name); } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { abort_("[read_png_file] png_create_read_struct failed"); } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { abort_("[read_png_file] png_create_info_struct failed"); } png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); info.width = png_get_image_width(png_ptr, info_ptr); info.height = png_get_image_height(png_ptr, info_ptr); info.color_type = png_get_color_type(png_ptr, info_ptr); info.bit_depth = png_get_bit_depth(png_ptr, info_ptr); (void)png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); info.data = (byte**)malloc(sizeof(byte*) * info.height); rowbytes = png_get_rowbytes(png_ptr, info_ptr); for (y = 0; y < info.height; y++) { info.data[y] = (byte*)malloc(rowbytes); } png_read_image(png_ptr, info.data); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); fclose(fp); return info; } static void write_png_file(const char* file_name, const struct image_info* info) { FILE* fp; png_structp png_ptr; png_infop info_ptr; fp = fopen(file_name, "wb"); if (!fp) { abort_("[write_png_file] File %s could not be opened for writing", file_name); } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { abort_("[write_png_file] png_create_write_struct failed"); } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { abort_("[write_png_file] png_create_info_struct failed"); } png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, info->width, info->height, info->bit_depth, info->color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info(png_ptr, info_ptr); png_write_image(png_ptr, info->data); png_write_end(png_ptr, NULL); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); } static uint32_t getpixel(const struct image_info* info, uint32_t x, uint32_t y) { uint32_t pixel; uint32_t stride; byte* row = info->data[y]; switch (info->color_type) { case PNG_COLOR_TYPE_RGB: stride = 3; break; case PNG_COLOR_TYPE_RGBA: stride = 4; break; default: abort_("Unknown color type"); } pixel = ((uint32_t)row[x * stride + 0] << 16) | ((uint32_t)row[x * stride + 1] << 8) | ((uint32_t)row[x * stride + 2]); return pixel; } static void putpixel(struct image_info* info, uint32_t x, uint32_t y, uint32_t color) { uint32_t stride; byte* row = info->data[y]; switch (info->color_type) { case PNG_COLOR_TYPE_RGB: stride = 3; break; case PNG_COLOR_TYPE_RGBA: stride = 4; break; default: abort_("Unknown color type"); } row[x * stride + 0] = (color >> 16) & 0xff; row[x * stride + 1] = (color >> 8) & 0xff; row[x * stride + 2] = (color) & 0xff; if (stride == 4) { row[x * stride + 4] = 0xff; } } static float lerp(float s, float e, float t) { return s + (e - s) * t; } static float blerp(float c00, float c10, float c01, float c11, float tx, float ty) { return lerp(lerp(c00, c10, tx), lerp(c01, c11, tx), ty); } static uint8_t getbyte(uint32_t value, int n) { return (value >> (n * 8)) & 0xff; } static struct image_info scale_image(const struct image_info* src, uint32_t new_w, uint32_t new_h) { png_structp png_ptr; png_infop info_ptr; struct image_info scaled; uint32_t x, y; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { abort_("[scale_image] png_create_write_struct failed"); } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { abort_("[scale_image] png_create_info_struct failed"); } png_set_IHDR(png_ptr, info_ptr, new_w, new_h, src->bit_depth, src->color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); scaled.width = new_w; scaled.height = new_h; scaled.color_type = src->color_type; scaled.bit_depth = src->bit_depth; scaled.data = (byte**)malloc(sizeof(byte*) * new_h); for (y = 0; y < new_h; y++) { scaled.data[y] = (byte*)malloc(png_get_rowbytes(png_ptr, info_ptr)); } for (y = 0; y < new_h; ++y) { for (x = 0; x < new_w; ++x) { float gx = x / (float)new_w * (src->width - 1); float gy = y / (float)new_h * (src->height - 1); int gxi = (int)gx; int gyi = (int)gy; uint32_t result = 0; uint32_t c00 = getpixel(src, gxi, gyi); uint32_t c10 = getpixel(src, gxi + 1, gyi); uint32_t c01 = getpixel(src, gxi, gyi + 1); uint32_t c11 = getpixel(src, gxi + 1, gyi + 1); int i; for (i = 0; i < 3; ++i) { result |= (uint8_t)blerp(getbyte(c00, i), getbyte(c10, i), getbyte(c01, i), getbyte(c11, i), gx - gxi, gy - gyi) << (8 * i); } putpixel(&scaled, x, y, result); } } png_destroy_write_struct(&png_ptr, &info_ptr); return scaled; } static void usage(const char* argv0) { fprintf(stderr, "Usage: %s WxH \n", argv0); exit(1); } int main(int argc, char** argv) { uint32_t w, h; struct image_info orig; struct image_info scaled; if (argc != 3) { usage(argv[0]); } if (sscanf(argv[1], "%ux%u", &w, &h) != 2) { usage(argv[0]); } orig = read_png_file(argv[2]); scaled = scale_image(&orig, w, h); write_png_file(argv[2], &scaled); image_destroy(&scaled); image_destroy(&orig); return 0; }