blob: 0135ddccdfe15a56b4b095beb47ef766b336e77b [file] [log] [blame]
/*
* Libpng interface to twin
*
* Copyright 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>
*
* This Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Twin Library; see the file COPYING. If not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <png.h>
#include "twin_png.h"
#if 0
#include <stdio.h>
#define DEBUG(fmt...) printf(fmt)
#else
#define DEBUG(fmt...)
#endif
typedef struct _twin_png_priv {
int fd;
} twin_png_priv_t;
static void twin_png_read_fn(png_structp png, png_bytep data, png_size_t size)
{
twin_png_priv_t *priv = png_get_io_ptr(png);
ssize_t n;
n = read(priv->fd, data, size);
DEBUG(" twin_png_read_fn size=%d, got=%d\n", size, n);
if (n <= 0 || (png_size_t)n < size)
png_error(png, "end of file !\n");
}
twin_pixmap_t *twin_png_to_pixmap(const char *filepath, twin_format_t fmt)
{
uint8_t signature[8];
int fd, rb = 0;
size_t n;
png_structp png = NULL;
png_infop info = NULL;
twin_pixmap_t *pix = NULL;
twin_png_priv_t priv;
png_uint_32 width, height;
png_uint_32 i;
int depth, ctype, interlace;
png_bytep *rowp = NULL;
DEBUG("png read for %s, format=%d\n", filepath, fmt);
fd = open(filepath, O_RDONLY);
if (fd < 0)
goto fail;
DEBUG("checking signature...\n");
n = read(fd, signature, 8);
if (png_sig_cmp(signature, 0, n) != 0)
goto fail_close;
DEBUG("creating libpng structures...\n");
png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL);
if (png == NULL)
goto fail_close;
info = png_create_info_struct(png);
if (info == NULL)
goto fail_free;
if (setjmp(png_jmpbuf(png))) {
DEBUG("* error callback *\n");
if (pix)
twin_pixmap_destroy(pix);
pix = NULL;
goto fail_free;
}
priv.fd = fd;
png_set_read_fn(png, &priv, twin_png_read_fn);
png_set_sig_bytes(png, n);
DEBUG("reading picture infos ...\n");
png_read_info(png, info);
png_get_IHDR(png, info, &width, &height, &depth, &ctype, &interlace,
int_p_NULL, int_p_NULL);
DEBUG(" 1- size/depth/ctype/int = %ldx%ld/%d/%d/%d\n",
width, height, depth, ctype, interlace);
if (depth == 16)
png_set_strip_16(png);
if (ctype == PNG_COLOR_TYPE_PALETTE)
png_set_palette_to_rgb(png);
if (ctype == PNG_COLOR_TYPE_GRAY && depth < 8)
png_set_gray_1_2_4_to_8(png);
if (png_get_valid(png, info, PNG_INFO_tRNS))
png_set_tRNS_to_alpha(png);
png_get_IHDR(png, info, &width, &height, &depth, &ctype, &interlace,
int_p_NULL, int_p_NULL);
DEBUG(" 2- size/depth/ctype/int = %ldx%ld/%d/%d/%d\n",
width, height, depth, ctype, interlace);
switch(fmt) {
case TWIN_A8:
if (ctype != PNG_COLOR_TYPE_GRAY ||
depth != 8)
goto fail_free;
rb = width;
break;
case TWIN_RGB16:
/* unsupported for now */
goto fail_free;
case TWIN_ARGB32:
if (ctype == PNG_COLOR_TYPE_RGB)
png_set_filler(png, 0xff, PNG_FILLER_BEFORE);
if (ctype == PNG_COLOR_TYPE_GRAY ||
ctype == PNG_COLOR_TYPE_GRAY_ALPHA)
png_set_gray_to_rgb(png);
png_get_IHDR(png, info, &width, &height, &depth, &ctype,
&interlace, int_p_NULL, int_p_NULL);
DEBUG(" 3- size/depth/ctype/int = %ldx%ld/%d/%d/%d\n",
width, height, depth, ctype, interlace);
if (depth != 8)
goto fail_free;
rb = width * 4;
break;
}
DEBUG(" rowbytes = %d\n", rb);
DEBUG("preparing pixmap & row pointer array...\n");
rowp = malloc(height * sizeof(png_bytep));
if (rowp == NULL)
goto fail_free;
pix = twin_pixmap_create(fmt, width, height);
if (pix == NULL)
goto fail_free;
for (i = 0; i < height; i++)
rowp[i] = pix->p.b + rb * i;
DEBUG("reading image...\n");
png_read_image(png, rowp);
png_read_end(png, NULL);
if (fmt == TWIN_ARGB32)
twin_premultiply_alpha(pix);
fail_free:
if (rowp)
free(rowp);
png_destroy_read_struct(&png, &info, png_infopp_NULL);
fail_close:
close(fd);
fail:
return pix;
}