summaryrefslogtreecommitdiffstats
path: root/libdimension/png.c
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2014-08-19 17:10:03 -0400
committerTavian Barnes <tavianator@tavianator.com>2015-10-25 11:03:56 -0400
commit7b09710392d35fb55b52031d447a542d99fc6b4b (patch)
tree270eb927ee8c52ceeb99926ebf4843704775a610 /libdimension/png.c
parent200c86b91ea7063d35be3bffc11c5da53c054653 (diff)
downloaddimension-7b09710392d35fb55b52031d447a542d99fc6b4b.tar.xz
Modularize the libdimension codebase.
Diffstat (limited to 'libdimension/png.c')
-rw-r--r--libdimension/png.c390
1 files changed, 0 insertions, 390 deletions
diff --git a/libdimension/png.c b/libdimension/png.c
deleted file mode 100644
index 6b73738..0000000
--- a/libdimension/png.c
+++ /dev/null
@@ -1,390 +0,0 @@
-/*************************************************************************
- * Copyright (C) 2009-2014 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * This file is part of The Dimension Library. *
- * *
- * The Dimension Library 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. *
- * *
- * The Dimension Library 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/>. *
- *************************************************************************/
-
-/**
- * @file
- * PNG import/export.
- */
-
-#include "dimension-internal.h"
-#include <png.h>
-#include <errno.h>
-#include <setjmp.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-int
-dmnsn_png_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas)
-{
- dmnsn_rgba16_optimize_canvas(pool, canvas);
- return 0;
-}
-
-/// Payload type for PNG write thread callback.
-typedef struct {
- dmnsn_future *future;
- const dmnsn_canvas *canvas;
- FILE *file;
-} dmnsn_png_write_payload;
-
-/// Payload type for PNG read thread callback.
-typedef struct {
- dmnsn_future *future;
- dmnsn_canvas **canvas;
- dmnsn_pool *pool;
- FILE *file;
-} dmnsn_png_read_payload;
-
-/// PNG write thread callback.
-static int dmnsn_png_write_canvas_thread(void *ptr);
-/// PNG read thread callback.
-static int dmnsn_png_read_canvas_thread(void *ptr);
-
-int
-dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file)
-{
- dmnsn_future *future = dmnsn_png_write_canvas_async(canvas, file);
- return dmnsn_future_join(future);
-}
-
-dmnsn_future *
-dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file)
-{
- dmnsn_future *future = dmnsn_new_future();
-
- dmnsn_png_write_payload *payload = DMNSN_MALLOC(dmnsn_png_write_payload);
- payload->future = future;
- payload->canvas = canvas;
- payload->file = file;
-
- // Create the worker thread
- dmnsn_new_thread(future, dmnsn_png_write_canvas_thread, payload);
-
- return future;
-}
-
-// Read a canvas from the PNG file `file'. Return NULL on error.
-dmnsn_canvas *
-dmnsn_png_read_canvas(dmnsn_pool *pool, FILE *file)
-{
- dmnsn_canvas *canvas;
- dmnsn_future *future = dmnsn_png_read_canvas_async(&canvas, pool, file);
- dmnsn_future_join(future);
- return canvas;
-}
-
-// Read a canvas from a png file in the background
-dmnsn_future *
-dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, dmnsn_pool *pool, FILE *file)
-{
- dmnsn_future *future = dmnsn_new_future();
- dmnsn_png_read_payload *payload = DMNSN_MALLOC(dmnsn_png_read_payload);
-
- payload->future = future;
- payload->canvas = canvas;
- *payload->canvas = NULL;
- payload->pool = pool;
- payload->file = file;
-
- // Create the worker thread
- dmnsn_new_thread(future, dmnsn_png_read_canvas_thread, payload);
-
- return future;
-}
-
-//////////////////////
-// Thread callbacks //
-//////////////////////
-
-// Write a PNG file
-static int
-dmnsn_png_write_canvas_thread(void *ptr)
-{
- dmnsn_png_write_payload *payload = ptr;
-
- png_uint_32 width = payload->canvas->width;
- png_uint_32 height = payload->canvas->height;
-
- dmnsn_future_set_total(payload->future, height);
-
- png_structp png_ptr
- = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (!png_ptr) {
- // Couldn't create libpng write struct
- dmnsn_free(payload);
- return -1;
- }
-
- png_infop info_ptr = png_create_info_struct(png_ptr);
- if (!info_ptr) {
- // Couldn't create libpng info struct
- png_destroy_write_struct(&png_ptr, NULL);
- dmnsn_free(payload);
- return -1;
- }
-
- // libpng will longjmp here if it encounters an error from here on
- uint16_t *row = NULL;
- if (setjmp(png_jmpbuf(png_ptr))) {
- // libpng error
- dmnsn_free(row);
- png_destroy_write_struct(&png_ptr, &info_ptr);
- dmnsn_free(payload);
- return -1;
- }
-
- // Associate file with the libpng write struct
- png_init_io(png_ptr, payload->file);
-
- // Set header correctly for 16-bit sRGB image
- 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);
-
- // We think of transparency in the opposite way that PNG does
- png_set_invert_alpha(png_ptr);
-
- // Write the info struct
- png_write_info(png_ptr, info_ptr);
-
- if (dmnsn_is_little_endian()) {
- // We are little-endian; swap the byte order of the pixels
- png_set_swap(png_ptr);
- }
-
- // Check if we can optimize this
- dmnsn_rgba16_optimizer *rgba16 = (dmnsn_rgba16_optimizer *)dmnsn_canvas_find_optimizer(payload->canvas, dmnsn_rgba16_optimizer_fn);
- if (rgba16) {
- for (size_t y = 0; y < height; ++y) {
- // Invert the rows. PNG coordinates are fourth quadrant.
- uint16_t *row_opt = rgba16->data + 4*(height - y - 1)*width;
- png_write_row(png_ptr, (png_bytep)row_opt);
- dmnsn_future_increment(payload->future);
- }
-
- // Finish the PNG file
- png_write_end(png_ptr, info_ptr);
- png_destroy_write_struct(&png_ptr, &info_ptr);
- dmnsn_free(payload);
- return 0;
- }
-
- // Allocate the temporary row of RGBA values
- row = dmnsn_malloc(4*sizeof(uint16_t)*width);
-
- // Write the pixels
- for (size_t y = 0; y < height; ++y) {
- for (size_t x = 0; x < width; ++x) {
- // Invert the rows. PNG coordinates are fourth quadrant.
- dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(payload->canvas,
- x, height - y - 1);
- tcolor = dmnsn_tcolor_remove_filter(tcolor);
- tcolor.c = dmnsn_color_to_sRGB(tcolor.c);
- tcolor = dmnsn_tcolor_clamp(tcolor);
-
- row[4*x] = lround(tcolor.c.R*UINT16_MAX);
- row[4*x + 1] = lround(tcolor.c.G*UINT16_MAX);
- row[4*x + 2] = lround(tcolor.c.B*UINT16_MAX);
- row[4*x + 3] = lround(tcolor.T*UINT16_MAX);
- }
-
- // Write the row
- png_write_row(png_ptr, (png_bytep)row);
- dmnsn_future_increment(payload->future);
- }
-
- // Finish the PNG file
- png_write_end(png_ptr, info_ptr);
-
- dmnsn_free(row);
- png_destroy_write_struct(&png_ptr, &info_ptr);
- dmnsn_free(payload);
- return 0;
-}
-
-/// Thread-specific pointer to the appropriate dmnsn_future* for
-/// dmnsn_png_read_row_callback.
-static __thread dmnsn_future *dmnsn_tl_png_read_future;
-
-/// Callback to increment the progress after a row has been read.
-static void
-dmnsn_png_read_row_callback(png_structp png_ptr, png_uint_32 row, int pass)
-{
- dmnsn_future_increment(dmnsn_tl_png_read_future);
-}
-
-// Read a PNG file
-static int
-dmnsn_png_read_canvas_thread(void *ptr)
-{
- dmnsn_png_read_payload *payload = ptr;
- dmnsn_tl_png_read_future = payload->future;
-
- png_byte header[8];
- if (fread(header, 1, 8, payload->file) != 8) {
- dmnsn_free(payload);
- return -1;
- }
- if (png_sig_cmp(header, 0, 8)) {
- // payload->file is not a PNG file
- dmnsn_free(payload);
- errno = EINVAL;
- return -1;
- }
-
- // Create the libpng read struct
- png_structp png_ptr
- = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
- if (!png_ptr) {
- dmnsn_free(payload);
- return -1;
- }
-
- // Create the libpng info struct
- png_infop info_ptr = png_create_info_struct(png_ptr);
- if (!info_ptr) {
- png_destroy_read_struct(&png_ptr, NULL, NULL);
- dmnsn_free(payload);
- return -1;
- }
-
- // libpng will longjmp here if it encounters an error from here on
- png_bytep image = NULL;
- png_bytep *row_pointers = NULL;
- if (setjmp(png_jmpbuf(png_ptr))) {
- // libpng error
- dmnsn_free(row_pointers);
- dmnsn_free(image);
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- dmnsn_free(payload);
- return -1;
- }
-
- // Associate the read struct with the file, and tell it we've already checked
- // 8 bytes of signature
- png_init_io(png_ptr, payload->file);
- png_set_sig_bytes(png_ptr, 8);
-
- // Read the PNG header into info struct
- png_read_info(png_ptr, info_ptr);
-
- // Get useful information from the info struct
- png_uint_32 width, height;
- int bit_depth, color_type, interlace_type, compression_type, filter_method;
- png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
- &interlace_type, &compression_type, &filter_method);
- int number_of_passes = png_set_interlace_handling(png_ptr);
-
- dmnsn_future_set_total(payload->future, (number_of_passes + 1)*height);
- png_set_read_status_fn(png_ptr, dmnsn_png_read_row_callback);
-
- // - 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);
-
- // Update the info struct
- png_read_update_info(png_ptr, info_ptr);
-
- // Get bytes/image row
- png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
-
- // Allocate the temporary image buffer
- image = dmnsn_malloc(rowbytes*height);
-
- // Allocate and set an array of pointers to rows in image
- row_pointers = dmnsn_malloc(sizeof(png_bytep)*height);
-
- for (size_t y = 0; y < height; ++y) {
- row_pointers[y] = image + y*rowbytes;
- }
-
- // Read the image to memory all at once. At the expense of greater memory
- // use, this handles interlacing for us.
- png_read_image(png_ptr, row_pointers);
-
- // Allocate the canvas
- *payload->canvas = dmnsn_new_canvas(payload->pool, width, height);
-
- // Now we convert the image to our canvas format. This depends on the image
- // bit depth (which has been scaled up to at least 8 or 16), and the presence
- // of an alpha channel.
- for (size_t y = 0; y < height; ++y) {
- for (size_t x = 0; x < width; ++x) {
- dmnsn_tcolor tcolor;
- tcolor.F = 0.0;
-
- if (color_type & PNG_COLOR_MASK_ALPHA) {
- if (bit_depth == 16) {
- png_bytep png_pixel = image + 8*(y*width + x);
- tcolor.c.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX;
- tcolor.c.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX;
- tcolor.c.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX;
- tcolor.T = (double)((png_pixel[6] << 8) + png_pixel[7])/UINT16_MAX;
- } else {
- png_bytep png_pixel = image + 4*(y*width + x);
- tcolor.c.R = (double)png_pixel[0]/UINT8_MAX;
- tcolor.c.G = (double)png_pixel[1]/UINT8_MAX;
- tcolor.c.B = (double)png_pixel[2]/UINT8_MAX;
- tcolor.T = (double)png_pixel[3]/UINT8_MAX;
- }
- } else {
- tcolor.T = 0.0;
-
- if (bit_depth == 16) {
- png_bytep png_pixel = image + 6*(y*width + x);
- tcolor.c.R = (double)((png_pixel[0] << 8) + png_pixel[1])/UINT16_MAX;
- tcolor.c.G = (double)((png_pixel[2] << 8) + png_pixel[3])/UINT16_MAX;
- tcolor.c.B = (double)((png_pixel[4] << 8) + png_pixel[5])/UINT16_MAX;
- } else {
- png_bytep png_pixel = image + 3*(y*width + x);
- tcolor.c.R = (double)png_pixel[0]/UINT8_MAX;
- tcolor.c.G = (double)png_pixel[1]/UINT8_MAX;
- tcolor.c.B = (double)png_pixel[2]/UINT8_MAX;
- }
- }
-
- tcolor.c = dmnsn_color_from_sRGB(tcolor.c);
- dmnsn_canvas_set_pixel(*payload->canvas, x, height - y - 1, tcolor);
- }
-
- dmnsn_future_increment(payload->future);
- }
-
- dmnsn_free(row_pointers);
- dmnsn_free(image);
- png_read_end(png_ptr, NULL);
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- dmnsn_free(payload);
- return 0;
-}