summaryrefslogtreecommitdiffstats
path: root/compare/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'compare/image.c')
-rw-r--r--compare/image.c240
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);
+}