From 7b09710392d35fb55b52031d447a542d99fc6b4b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 19 Aug 2014 17:10:03 -0400 Subject: Modularize the libdimension codebase. --- libdimension/canvas/canvas.c | 92 ++++++++++ libdimension/canvas/gl-stubs.c | 48 +++++ libdimension/canvas/gl.c | 113 ++++++++++++ libdimension/canvas/png-stubs.c | 62 +++++++ libdimension/canvas/png.c | 393 ++++++++++++++++++++++++++++++++++++++++ libdimension/canvas/rgba.c | 95 ++++++++++ 6 files changed, 803 insertions(+) create mode 100644 libdimension/canvas/canvas.c create mode 100644 libdimension/canvas/gl-stubs.c create mode 100644 libdimension/canvas/gl.c create mode 100644 libdimension/canvas/png-stubs.c create mode 100644 libdimension/canvas/png.c create mode 100644 libdimension/canvas/rgba.c (limited to 'libdimension/canvas') diff --git a/libdimension/canvas/canvas.c b/libdimension/canvas/canvas.c new file mode 100644 index 0000000..350cd60 --- /dev/null +++ b/libdimension/canvas/canvas.c @@ -0,0 +1,92 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * 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 * + * . * + *************************************************************************/ + +/** + * @file + * Canveses. + */ + +#include "dimension/canvas.h" + +dmnsn_canvas * +dmnsn_new_canvas(dmnsn_pool *pool, size_t width, size_t height) +{ + dmnsn_canvas *canvas = DMNSN_PALLOC(pool, dmnsn_canvas); + canvas->width = width; + canvas->height = height; + canvas->optimizers = DMNSN_PALLOC_ARRAY(pool, dmnsn_canvas_optimizer *); + canvas->pixels = dmnsn_palloc(pool, sizeof(dmnsn_tcolor)*width*height); + return canvas; +} + +void +dmnsn_init_canvas_optimizer(dmnsn_canvas_optimizer *optimizer) +{ + optimizer->optimizer_fn = NULL; +} + +// Set a canvas optimizer +void +dmnsn_canvas_optimize(dmnsn_canvas *canvas, const dmnsn_canvas_optimizer *optimizer) +{ + dmnsn_array_push(canvas->optimizers, &optimizer); +} + +// Find an optimizer if it's already installed +dmnsn_canvas_optimizer * +dmnsn_canvas_find_optimizer(const dmnsn_canvas *canvas, dmnsn_canvas_optimizer_fn *optimizer_fn) +{ + DMNSN_ARRAY_FOREACH (dmnsn_canvas_optimizer **, i, canvas->optimizers) { + if ((*i)->optimizer_fn == optimizer_fn) { + return *i; + } + } + + return NULL; +} + +// Set the value of a pixel +void +dmnsn_canvas_set_pixel(dmnsn_canvas *canvas, size_t x, size_t y, + dmnsn_tcolor tcolor) +{ + dmnsn_assert(x < canvas->width && y < canvas->height, + "Canvas access out of bounds."); + dmnsn_assert(!dmnsn_tcolor_isnan(tcolor), "Pixel has NaN component."); + + // Set the pixel + canvas->pixels[y*canvas->width + x] = tcolor; + + // Call the optimizers + DMNSN_ARRAY_FOREACH (dmnsn_canvas_optimizer **, i, canvas->optimizers) { + (*i)->optimizer_fn(*i, canvas, x, y); + } +} + +// Fill a canvas with a solid color +void +dmnsn_canvas_clear(dmnsn_canvas *canvas, dmnsn_tcolor tcolor) +{ + for (size_t x = 0; x < canvas->width; ++x) { + for (size_t y = 0; y < canvas->height; ++y) { + dmnsn_canvas_set_pixel(canvas, x, y, tcolor); + } + } +} diff --git a/libdimension/canvas/gl-stubs.c b/libdimension/canvas/gl-stubs.c new file mode 100644 index 0000000..df31308 --- /dev/null +++ b/libdimension/canvas/gl-stubs.c @@ -0,0 +1,48 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * 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 * + * . * + *************************************************************************/ + +/** + * @file + * Stubs for GL functions when compiled with --disable-gl. + */ + +#include "dimension.h" +#include + +int +dmnsn_gl_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + errno = ENOSYS; + return -1; +} + +int +dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) +{ + errno = ENOSYS; + return -1; +} + +int +dmnsn_gl_read_canvas(dmnsn_canvas *canvas, size_t x0, size_t y0) +{ + errno = ENOSYS; + return NULL; +} diff --git a/libdimension/canvas/gl.c b/libdimension/canvas/gl.c new file mode 100644 index 0000000..900f53c --- /dev/null +++ b/libdimension/canvas/gl.c @@ -0,0 +1,113 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * 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 * + * . * + *************************************************************************/ + +/** + * @file + * OpenGL import/export. + */ + +#include "internal/rgba.h" +#include "dimension/canvas.h" +#include +#include +#include +#include + +// Optimize canvas for GL drawing +int +dmnsn_gl_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + dmnsn_rgba8_optimize_canvas(pool, canvas); + return 0; +} + +// Write canvas to GL framebuffer. Returns 0 on success, nonzero on failure +int +dmnsn_gl_write_canvas(const dmnsn_canvas *canvas) +{ + size_t width = canvas->width; + size_t height = canvas->height; + + // Check if we can optimize this + dmnsn_rgba8_optimizer *rgba8 = (dmnsn_rgba8_optimizer *)dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba8_optimizer_fn); + if (rgba8) { + glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgba8->data); + return glGetError() == GL_NO_ERROR ? 0 : 1; + } + + // We couldn't, so transform the canvas to RGB now + GLubyte *pixels = dmnsn_malloc(4*width*height*sizeof(GLubyte)); + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + GLubyte *pixel = pixels + 4*(y*width + x); + + dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); + tcolor = dmnsn_tcolor_remove_filter(tcolor); + tcolor.c = dmnsn_color_to_sRGB(tcolor.c); + tcolor = dmnsn_tcolor_clamp(tcolor); + + pixel[0] = lround(tcolor.c.R*UINT8_MAX); + pixel[1] = lround(tcolor.c.G*UINT8_MAX); + pixel[2] = lround(tcolor.c.B*UINT8_MAX); + pixel[3] = lround(tcolor.T*UINT8_MAX); + } + } + + glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + dmnsn_free(pixels); + return glGetError() == GL_NO_ERROR ? 0 : 1; +} + +// Read a canvas from a GL framebuffer. Returns NULL on failure. +int +dmnsn_gl_read_canvas(dmnsn_canvas *canvas, size_t x0, size_t y0) +{ + size_t width = canvas->width; + size_t height = canvas->height; + + // Array of 16-bit ints in RGBA order + GLushort *pixels = dmnsn_malloc(4*width*height*sizeof(GLushort)); + glReadPixels(x0, y0, width, height, GL_RGBA, GL_UNSIGNED_SHORT, pixels); + if (glGetError() != GL_NO_ERROR) { + dmnsn_free(pixels); + return -1; + } + + for (size_t y = 0; y < height; ++y) { + for (size_t x = 0; x < width; ++x) { + GLushort *pixel = pixels + 4*(y*width + x); + + dmnsn_tcolor tcolor = dmnsn_new_tcolor5( + (double)pixel[0]/UINT16_MAX, + (double)pixel[1]/UINT16_MAX, + (double)pixel[2]/UINT16_MAX, + (double)pixel[3]/UINT16_MAX, + 0.0 + ); + tcolor.c = dmnsn_color_from_sRGB(tcolor.c); + dmnsn_canvas_set_pixel(canvas, x, y, tcolor); + } + } + + dmnsn_free(pixels); + return 0; +} diff --git a/libdimension/canvas/png-stubs.c b/libdimension/canvas/png-stubs.c new file mode 100644 index 0000000..9c752a5 --- /dev/null +++ b/libdimension/canvas/png-stubs.c @@ -0,0 +1,62 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * 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 * + * . * + *************************************************************************/ + +/** + * @file + * Stubs for PNG functions when compiled with --disable-png. + */ + +#include "dimension.h" +#include + +int +dmnsn_png_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + errno = ENOSYS; + return -1; +} + +int +dmnsn_png_write_canvas(const dmnsn_canvas *canvas, FILE *file) +{ + errno = ENOSYS; + return -1; +} + +dmnsn_future * +dmnsn_png_write_canvas_async(const dmnsn_canvas *canvas, FILE *file) +{ + errno = ENOSYS; + return NULL; +} + +dmnsn_canvas * +dmnsn_png_read_canvas(dmnsn_pool *pool, FILE *file) +{ + errno = ENOSYS; + return NULL; +} + +dmnsn_future * +dmnsn_png_read_canvas_async(dmnsn_canvas **canvas, dmnsn_pool *pool, FILE *file) +{ + errno = ENOSYS; + return NULL; +} diff --git a/libdimension/canvas/png.c b/libdimension/canvas/png.c new file mode 100644 index 0000000..b1d29a3 --- /dev/null +++ b/libdimension/canvas/png.c @@ -0,0 +1,393 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes * + * * + * 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 * + * . * + *************************************************************************/ + +/** + * @file + * PNG import/export. + */ + +#include "internal/concurrency.h" +#include "internal/platform.h" +#include "internal/rgba.h" +#include "dimension/canvas.h" +#include +#include +#include +#include +#include + +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; +} diff --git a/libdimension/canvas/rgba.c b/libdimension/canvas/rgba.c new file mode 100644 index 0000000..4abf457 --- /dev/null +++ b/libdimension/canvas/rgba.c @@ -0,0 +1,95 @@ +/************************************************************************* + * Copyright (C) 2010-2014 Tavian Barnes * + * * + * 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 * + * . * + *************************************************************************/ + +/** + * @file + * 16-bit RGBA canvas optimizer. + */ + +#include "internal/rgba.h" +#include + +void +dmnsn_rgba8_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + if (dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba8_optimizer_fn)) { + return; + } + + size_t ndata = 4*canvas->width*canvas->height; + dmnsn_rgba8_optimizer *rgba8 = dmnsn_palloc(pool, sizeof(dmnsn_rgba8_optimizer) + ndata*sizeof(uint8_t)); + + dmnsn_canvas_optimizer *optimizer = &rgba8->optimizer; + dmnsn_init_canvas_optimizer(optimizer); + optimizer->optimizer_fn = dmnsn_rgba8_optimizer_fn; + + dmnsn_canvas_optimize(canvas, optimizer); +} + +void +dmnsn_rgba16_optimize_canvas(dmnsn_pool *pool, dmnsn_canvas *canvas) +{ + if (dmnsn_canvas_find_optimizer(canvas, dmnsn_rgba16_optimizer_fn)) { + return; + } + + size_t ndata = 4*canvas->width*canvas->height; + dmnsn_rgba16_optimizer *rgba16 = dmnsn_palloc(pool, sizeof(dmnsn_rgba16_optimizer) + ndata*sizeof(uint16_t)); + + dmnsn_canvas_optimizer *optimizer = &rgba16->optimizer; + dmnsn_init_canvas_optimizer(optimizer); + optimizer->optimizer_fn = dmnsn_rgba16_optimizer_fn; + + dmnsn_canvas_optimize(canvas, optimizer); +} + +void +dmnsn_rgba8_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y) +{ + dmnsn_rgba8_optimizer *rgba8 = (dmnsn_rgba8_optimizer *)optimizer; + + uint8_t *pixel = rgba8->data + 4*(y*canvas->width + x); + dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); + tcolor = dmnsn_tcolor_remove_filter(tcolor); + tcolor.c = dmnsn_color_to_sRGB(tcolor.c); + tcolor = dmnsn_tcolor_clamp(tcolor); + + pixel[0] = lround(tcolor.c.R*UINT8_MAX); + pixel[1] = lround(tcolor.c.G*UINT8_MAX); + pixel[2] = lround(tcolor.c.B*UINT8_MAX); + pixel[3] = lround(tcolor.T*UINT8_MAX); +} + +void +dmnsn_rgba16_optimizer_fn(dmnsn_canvas_optimizer *optimizer, const dmnsn_canvas *canvas, size_t x, size_t y) +{ + dmnsn_rgba16_optimizer *rgba16 = (dmnsn_rgba16_optimizer *)optimizer; + + uint16_t *pixel = rgba16->data + 4*(y*canvas->width + x); + dmnsn_tcolor tcolor = dmnsn_canvas_get_pixel(canvas, x, y); + tcolor = dmnsn_tcolor_remove_filter(tcolor); + tcolor.c = dmnsn_color_to_sRGB(tcolor.c); + tcolor = dmnsn_tcolor_clamp(tcolor); + + pixel[0] = lround(tcolor.c.R*UINT16_MAX); + pixel[1] = lround(tcolor.c.G*UINT16_MAX); + pixel[2] = lround(tcolor.c.B*UINT16_MAX); + pixel[3] = lround(tcolor.T*UINT16_MAX); +} -- cgit v1.2.3