summaryrefslogtreecommitdiffstats
path: root/libdimension-png/png.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdimension-png/png.c')
-rw-r--r--libdimension-png/png.c263
1 files changed, 263 insertions, 0 deletions
diff --git a/libdimension-png/png.c b/libdimension-png/png.c
new file mode 100644
index 0000000..750b5af
--- /dev/null
+++ b/libdimension-png/png.c
@@ -0,0 +1,263 @@
+/*************************************************************************
+ * Copyright (C) 2008 Tavian Barnes <tavianator@gmail.com> *
+ * *
+ * This file is part of Dimension. *
+ * *
+ * Dimension is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU Lesser General Public License as published *
+ * by the Free Software Foundation; either version 3 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * Dimension is distributed in the hope that it will be useful, but *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+ * Lesser General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Lesser General Public *
+ * License along with this program. If not, see *
+ * <http://www.gnu.org/licenses/>. *
+ *************************************************************************/
+
+#include "dimension-png.h"
+#include <png.h>
+#include <setjmp.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+int
+dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file)
+{
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 width, height;
+ unsigned int x, y;
+ uint16_t *row = NULL;
+ dmnsn_pixel *pixel;
+
+ width = canvas->x;
+ height = canvas->y;
+
+ if (!file) return 1;
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) return 1;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ return 1;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ if (row) free(row);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return 1;
+ }
+
+ png_init_io(png_ptr, file);
+
+ png_set_IHDR(png_ptr, info_ptr, width, height, 16,
+ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
+
+ png_set_invert_alpha(png_ptr);
+ png_write_info(png_ptr, info_ptr);
+
+ if (htonl(1) != 1) {
+ /* We are little-endian */
+ png_set_swap(png_ptr);
+ }
+
+ row = malloc(4*sizeof(uint16_t)*width);
+ if (!row) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return 1;
+ }
+
+ for (y = 0; y < height; ++y) {
+ for (x = 0; x < width; ++x) {
+ /* Invert the rows. PNG coordinates are fourth quadrant. */
+ pixel = canvas->pixels + (height - y - 1)*width + x;
+
+ row[4*x] = pixel->r;
+ row[4*x + 1] = pixel->g;
+ row[4*x + 2] = pixel->b;
+
+ if (pixel->a > pixel->t) {
+ row[4*x + 3] = pixel->a;
+ } else {
+ row[4*x + 3] = pixel->t;
+ }
+ }
+ png_write_row(png_ptr, (png_bytep)row);
+ }
+
+ png_write_end(png_ptr, info_ptr);
+
+ free(row);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return 0;
+}
+
+int
+dmnsn_png_read_canvas(dmnsn_canvas **canvas, FILE *file)
+{
+ png_byte header[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 width, height, rowbytes;
+ int bit_depth, color_type, interlace_type, compression_type, filter_method;
+ png_bytep image = NULL;
+ png_bytep *row_pointers = NULL;
+ unsigned int x, y;
+ dmnsn_pixel *pixel;
+ png_bytep png_pixel;
+
+ /* Ensure that *canvas can always be deleted. */
+ *canvas = NULL;
+
+ if (!file) return 1;
+
+ fread(header, 1, 8, file);
+ if (png_sig_cmp(header, 0, 8)) return 1;
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr) return 1;
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ return 1;
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ if (row_pointers) free(row_pointers);
+ if (image) free(image);
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return 1;
+ }
+
+ png_init_io(png_ptr, file);
+ png_set_sig_bytes(png_ptr, 8);
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
+ &interlace_type, &compression_type, &filter_method);
+
+ /*
+ * - Convert paletted images to RGB.
+ * - Convert a tRNS chunk to an alpha channel
+ * - Convert grayscale to RGB
+ * - Invert the alpha channel
+ */
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(png_ptr);
+ }
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(png_ptr);
+ }
+ if (color_type == PNG_COLOR_TYPE_GRAY
+ || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_set_gray_to_rgb(png_ptr);
+ }
+ png_set_invert_alpha(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+ rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+
+ image = malloc(rowbytes*height);
+ if (!image) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return 1;
+ }
+
+ row_pointers = malloc(sizeof(png_bytep)*height);
+ if (!row_pointers) {
+ free(image);
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return 1;
+ }
+
+ for (y = 0; y < height; ++y) {
+ row_pointers[y] = image + y*rowbytes;
+ }
+
+ png_read_image(png_ptr, row_pointers);
+
+ *canvas = dmnsn_new_canvas(width, height);
+ if (!*canvas) {
+ free(row_pointers);
+ free(image);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return 1;
+ }
+
+ if (bit_depth == 16) {
+ if (color_type & PNG_COLOR_MASK_ALPHA) {
+ for (x = 0; x < width; ++x) {
+ for (y = 0; y < height; ++y) {
+ pixel = (*canvas)->pixels + (height - y - 1)*width + x;
+ png_pixel = image + 8*(y*width + x);
+
+ pixel->r = (png_pixel[0] << UINT16_C(8)) + png_pixel[1];
+ pixel->g = (png_pixel[2] << UINT16_C(8)) + png_pixel[3];
+ pixel->b = (png_pixel[4] << UINT16_C(8)) + png_pixel[5];
+ pixel->a = (png_pixel[6] << UINT16_C(8)) + png_pixel[7];
+ pixel->t = 0;
+ }
+ }
+ } else {
+ for (x = 0; x < width; ++x) {
+ for (y = 0; y < height; ++y) {
+ pixel = (*canvas)->pixels + (height - y - 1)*width + x;
+ png_pixel = image + 6*(y*width + x);
+
+ pixel->r = (png_pixel[0] << UINT16_C(8)) + png_pixel[1];
+ pixel->g = (png_pixel[2] << UINT16_C(8)) + png_pixel[3];
+ pixel->b = (png_pixel[4] << UINT16_C(8)) + png_pixel[5];
+ pixel->a = 0;
+ pixel->t = 0;
+ }
+ }
+ }
+ } else {
+ /* Bit depth is 8 */
+ if (color_type & PNG_COLOR_MASK_ALPHA) {
+ for (x = 0; x < width; ++x) {
+ for (y = 0; y < height; ++y) {
+ pixel = (*canvas)->pixels + (height - y - 1)*width + x;
+ png_pixel = image + 4*(y*width + x);
+
+ pixel->r = png_pixel[0] << UINT16_C(8);
+ pixel->g = png_pixel[1] << UINT16_C(8);
+ pixel->b = png_pixel[2] << UINT16_C(8);
+ pixel->a = png_pixel[3] << UINT16_C(8);
+ pixel->t = 0;
+ }
+ }
+ } else {
+ for (x = 0; x < width; ++x) {
+ for (y = 0; y < height; ++y) {
+ pixel = (*canvas)->pixels + (height - y - 1)*width + x;
+ png_pixel = image + 3*(y*width + x);
+
+ pixel->r = png_pixel[0] << UINT16_C(8);
+ pixel->g = png_pixel[1] << UINT16_C(8);
+ pixel->b = png_pixel[2] << UINT16_C(8);
+ pixel->a = 0;
+ pixel->t = 0;
+ }
+ }
+ }
+ }
+
+ free(row_pointers);
+ free(image);
+ png_read_end(png_ptr, NULL);
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ return 0;
+}