diff options
Diffstat (limited to 'compare/image.c')
-rw-r--r-- | compare/image.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/compare/image.c b/compare/image.c new file mode 100644 index 0000000..5263b93 --- /dev/null +++ b/compare/image.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2017 Vabishchevich Nikolay <vabnick@gmail.com> + * + * This file is part of libass. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "image.h" +#include <png.h> + + +bool read_png(const char *path, Image16 *img) +{ + FILE *fp = fopen(path, "rb"); + if (!fp) + return false; + + png_structp png = + png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) { + fclose(fp); + return false; + } + + png_infop info = png_create_info_struct(png); + if (!info) { + png_destroy_read_struct(&png, NULL, NULL); + fclose(fp); + return false; + } + + png_byte *volatile buf = NULL; + png_byte **volatile rows = NULL; + if (setjmp(png_jmpbuf(png))) { + free(buf); + free(rows); + png_destroy_read_struct(&png, &info, NULL); + fclose(fp); + return false; + } + + png_init_io(png, fp); + png_read_info(png, info); + + uint32_t w = png_get_image_width(png, info); + uint32_t h = png_get_image_height(png, info); + int type = png_get_color_type(png, info); + int depth = png_get_bit_depth(png, info); + + if (w > 0xFFFF || h > 0xFFFF || type != PNG_COLOR_TYPE_RGBA) { + png_destroy_read_struct(&png, &info, NULL); + fclose(fp); + return false; + } + + ptrdiff_t stride = 8 * w; + buf = malloc(stride * h); + rows = malloc(h * sizeof(png_byte *)); + if (!buf || !rows) { + free(buf); + free(rows); + png_destroy_read_struct(&png, &info, NULL); + fclose(fp); + return false; + } + + png_byte *ptr = buf; + ptrdiff_t half = 4 * w; + if (depth == 8) + ptr += half; + else + png_set_swap(png); + + for (uint32_t i = 0; i < h; i++) { + rows[i] = ptr; + ptr += stride; + } + + png_read_image(png, rows); + png_read_end(png, NULL); + + free(rows); + png_destroy_read_struct(&png, &info, NULL); + fclose(fp); + + // convert to premultiplied with inverted alpha + if (depth == 8) { + uint8_t *ptr = (uint8_t *) buf; + for (uint32_t y = 0; y < h; y++) { + for (uint32_t x = 0; x < w; x++) { + uint8_t r = ptr[half + 4 * x + 0]; + uint8_t g = ptr[half + 4 * x + 1]; + uint8_t b = ptr[half + 4 * x + 2]; + uint8_t a = ptr[half + 4 * x + 3]; + uint16_t ra = (uint16_t) r * a; + uint16_t ga = (uint16_t) g * a; + uint16_t ba = (uint16_t) b * a; + ptr[8 * x + 0] = ptr[8 * x + 1] = (ra + (ra >> 8) + 128) >> 8; + ptr[8 * x + 2] = ptr[8 * x + 3] = (ga + (ga >> 8) + 128) >> 8; + ptr[8 * x + 4] = ptr[8 * x + 5] = (ba + (ba >> 8) + 128) >> 8; + ptr[8 * x + 6] = ptr[8 * x + 7] = ~a; + } + ptr += stride; + } + } else { + uint16_t *ptr = (uint16_t *) buf; + for (uint32_t y = 0; y < h; y++) { + for (uint32_t x = 0; x < w; x++) { + uint16_t r = ptr[4 * x + 0]; + uint16_t g = ptr[4 * x + 1]; + uint16_t b = ptr[4 * x + 2]; + uint16_t a = ptr[4 * x + 3]; + uint32_t ra = (uint32_t) r * a; + uint32_t ga = (uint32_t) g * a; + uint32_t ba = (uint32_t) b * a; + ptr[4 * x + 0] = (ra + (ra >> 16) + (1 << 15)) >> 16; + ptr[4 * x + 1] = (ga + (ga >> 16) + (1 << 15)) >> 16; + ptr[4 * x + 2] = (ba + (ba >> 16) + (1 << 15)) >> 16; + ptr[4 * x + 3] = ~a; + } + ptr += half; + } + } + + img->width = w; + img->height = h; + img->buffer = (uint16_t *) buf; + return true; +} + +static bool write_png(const char *path, uint32_t width, uint32_t height, + ptrdiff_t stride, const void *buffer, int depth) +{ + FILE *fp = fopen(path, "wb"); + if (!fp) + return false; + + png_structp png = + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png) { + fclose(fp); + return false; + } + + png_infop info = png_create_info_struct(png); + if (!info) { + png_destroy_write_struct(&png, NULL); + fclose(fp); + return false; + } + + png_byte **rows = malloc(height * sizeof(png_byte *)); + if (!rows) { + png_destroy_write_struct(&png, &info); + fclose(fp); + return false; + } + + png_byte *ptr = (png_byte *) buffer; + for (uint32_t i = 0; i < height; i++) { + rows[i] = (png_byte *) ptr; + ptr += stride; + } + + if (setjmp(png_jmpbuf(png))) { + free(rows); + png_destroy_write_struct(&png, &info); + fclose(fp); + return false; + } + + png_init_io(png, fp); + png_set_IHDR(png, info, width, height, depth, + PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, info); + + if (depth > 8) + png_set_swap(png); + png_write_image(png, rows); + png_write_end(png, NULL); + + free(rows); + png_destroy_write_struct(&png, &info); + fclose(fp); + return true; +} + +bool write_png8(const char *path, Image8 *img) +{ + uint8_t *ptr = img->buffer; + size_t size = (size_t) img->width * img->height; + for (size_t i = 0; i < size; i++) { + uint8_t alpha = ~ptr[3]; + if (alpha) { + const uint32_t offs = (uint32_t) 1 << 15; + uint32_t inv = ((uint32_t) 255 << 16) / alpha + 1; + // equivalent to (255 * ptr[k] + alpha / 2) / alpha + ptr[0] = (ptr[0] * inv + offs) >> 16; + ptr[1] = (ptr[1] * inv + offs) >> 16; + ptr[2] = (ptr[2] * inv + offs) >> 16; + } + ptr[3] = alpha; + ptr += 4; + } + return write_png(path, img->width, img->height, + 4 * img->width, img->buffer, 8); +} + +bool write_png16(const char *path, Image16 *img) +{ + uint16_t *ptr = img->buffer; + size_t size = (size_t) img->width * img->height; + for (size_t i = 0; i < size; i++) { + uint16_t alpha = ~ptr[3]; + if (alpha) { + const uint64_t offs = (uint64_t) 1 << 32; + uint64_t inv = ((uint64_t) 65535 << 33) / alpha + 1; + // equivalent to (65535 * ptr[k] + alpha / 2) / alpha + ptr[0] = (ptr[0] * inv + offs) >> 33; + ptr[1] = (ptr[1] * inv + offs) >> 33; + ptr[2] = (ptr[2] * inv + offs) >> 33; + } + ptr[3] = alpha; + ptr += 4; + } + return write_png(path, img->width, img->height, + 8 * img->width, img->buffer, 16); +} |