2007-04-09 05:55:29 -07:00
|
|
|
#include "loader_common.h"
|
2004-11-01 01:45:31 -08:00
|
|
|
#include <png.h>
|
|
|
|
|
2007-08-24 19:33:51 -07:00
|
|
|
/* this is a quick sample png loader module... nice and small isn't it? */
|
2004-11-01 01:45:31 -08:00
|
|
|
|
|
|
|
/* PNG stuff */
|
|
|
|
#define PNG_BYTES_TO_CHECK 4
|
|
|
|
|
2020-02-29 02:08:27 -08:00
|
|
|
typedef struct {
|
|
|
|
unsigned char buf[PNG_BYTES_TO_CHECK];
|
|
|
|
unsigned char **lines;
|
|
|
|
} ImLib_PNG_data;
|
|
|
|
|
2004-11-01 01:45:31 -08:00
|
|
|
static void
|
|
|
|
comment_free(ImlibImage * im, void *data)
|
|
|
|
{
|
|
|
|
free(data);
|
|
|
|
}
|
|
|
|
|
2019-11-26 08:48:52 -08:00
|
|
|
int
|
|
|
|
load2(ImlibImage * im, int load_data)
|
2004-11-01 01:45:31 -08:00
|
|
|
{
|
2019-11-29 23:58:46 -08:00
|
|
|
int rc;
|
2004-11-01 01:45:31 -08:00
|
|
|
png_uint_32 w32, h32;
|
|
|
|
int w, h;
|
2016-03-16 11:55:41 -07:00
|
|
|
char hasa;
|
2004-11-01 01:45:31 -08:00
|
|
|
png_structp png_ptr = NULL;
|
|
|
|
png_infop info_ptr = NULL;
|
|
|
|
int bit_depth, color_type, interlace_type;
|
2020-02-29 02:08:27 -08:00
|
|
|
ImLib_PNG_data pdata;
|
2019-11-29 23:58:46 -08:00
|
|
|
int i;
|
2004-11-01 01:45:31 -08:00
|
|
|
|
|
|
|
/* read header */
|
2019-11-29 23:58:46 -08:00
|
|
|
rc = LOAD_FAIL;
|
2020-02-29 02:08:27 -08:00
|
|
|
pdata.lines = NULL;
|
2004-11-01 01:45:31 -08:00
|
|
|
|
2019-11-26 08:48:52 -08:00
|
|
|
if (fread(pdata.buf, 1, PNG_BYTES_TO_CHECK, im->fp) != PNG_BYTES_TO_CHECK)
|
2019-11-29 23:58:46 -08:00
|
|
|
goto quit;
|
|
|
|
|
2020-02-29 02:08:27 -08:00
|
|
|
if (png_sig_cmp(pdata.buf, 0, PNG_BYTES_TO_CHECK))
|
2019-11-29 23:58:46 -08:00
|
|
|
goto quit;
|
|
|
|
|
2019-11-26 08:48:52 -08:00
|
|
|
rewind(im->fp);
|
|
|
|
|
2019-09-29 00:44:02 -07:00
|
|
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
|
|
if (!png_ptr)
|
2019-11-29 23:58:46 -08:00
|
|
|
goto quit;
|
|
|
|
|
2019-09-29 00:44:02 -07:00
|
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
|
|
if (!info_ptr)
|
2019-11-29 23:58:46 -08:00
|
|
|
goto quit;
|
|
|
|
|
2020-02-28 21:47:18 -08:00
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
2020-02-29 02:08:27 -08:00
|
|
|
{
|
|
|
|
rc = LOAD_FAIL;
|
|
|
|
goto quit;
|
|
|
|
}
|
2019-11-29 23:58:46 -08:00
|
|
|
|
2019-11-26 08:48:52 -08:00
|
|
|
png_init_io(png_ptr, im->fp);
|
2019-09-29 00:44:02 -07:00
|
|
|
png_read_info(png_ptr, info_ptr);
|
|
|
|
png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32),
|
|
|
|
(png_uint_32 *) (&h32), &bit_depth, &color_type,
|
|
|
|
&interlace_type, NULL, NULL);
|
|
|
|
if (!IMAGE_DIMENSIONS_OK(w32, h32))
|
2019-11-29 23:58:46 -08:00
|
|
|
goto quit;
|
|
|
|
|
2019-09-29 00:44:02 -07:00
|
|
|
im->w = (int)w32;
|
|
|
|
im->h = (int)h32;
|
2019-11-29 23:58:46 -08:00
|
|
|
|
2020-02-29 02:08:27 -08:00
|
|
|
hasa = 0;
|
2019-09-29 00:44:02 -07:00
|
|
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
|
|
|
hasa = 1;
|
|
|
|
if (color_type == PNG_COLOR_TYPE_RGB_ALPHA)
|
|
|
|
hasa = 1;
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
|
|
|
hasa = 1;
|
|
|
|
if (hasa)
|
|
|
|
SET_FLAG(im->flags, F_HAS_ALPHA);
|
|
|
|
else
|
|
|
|
UNSET_FLAG(im->flags, F_HAS_ALPHA);
|
|
|
|
|
2019-11-29 23:58:46 -08:00
|
|
|
if (!load_data)
|
2004-11-01 01:45:31 -08:00
|
|
|
{
|
2019-11-29 23:58:46 -08:00
|
|
|
rc = LOAD_SUCCESS;
|
|
|
|
goto quit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Load data */
|
|
|
|
|
|
|
|
w = im->w;
|
|
|
|
h = im->h;
|
|
|
|
|
|
|
|
/* Prep for transformations... ultimately we want ARGB */
|
|
|
|
/* expand palette -> RGB if necessary */
|
|
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
|
|
png_set_palette_to_rgb(png_ptr);
|
|
|
|
/* expand gray (w/reduced bits) -> 8-bit RGB if necessary */
|
|
|
|
if ((color_type == PNG_COLOR_TYPE_GRAY) ||
|
|
|
|
(color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
|
|
|
|
{
|
|
|
|
png_set_gray_to_rgb(png_ptr);
|
|
|
|
if (bit_depth < 8)
|
|
|
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
|
|
|
}
|
|
|
|
/* expand transparency entry -> alpha channel if present */
|
|
|
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
|
|
|
|
png_set_tRNS_to_alpha(png_ptr);
|
|
|
|
/* reduce 16bit color -> 8bit color if necessary */
|
|
|
|
if (bit_depth > 8)
|
|
|
|
png_set_strip_16(png_ptr);
|
|
|
|
/* pack all pixels to byte boundaries */
|
|
|
|
png_set_packing(png_ptr);
|
2007-08-24 19:33:51 -07:00
|
|
|
|
|
|
|
/* note from raster: */
|
2004-11-01 01:45:31 -08:00
|
|
|
/* thanks to mustapha for helping debug this on PPC Linux remotely by */
|
2007-08-24 19:33:51 -07:00
|
|
|
/* sending across screenshots all the time and me figuring out from them */
|
2004-11-01 01:45:31 -08:00
|
|
|
/* what the hell was up with the colors */
|
2007-08-24 19:33:51 -07:00
|
|
|
/* now png loading should work on big-endian machines nicely */
|
2004-11-01 01:45:31 -08:00
|
|
|
#ifdef WORDS_BIGENDIAN
|
2019-11-29 23:58:46 -08:00
|
|
|
png_set_swap_alpha(png_ptr);
|
|
|
|
if (!hasa)
|
|
|
|
png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE);
|
2004-11-01 01:45:31 -08:00
|
|
|
#else
|
2019-11-29 23:58:46 -08:00
|
|
|
png_set_bgr(png_ptr);
|
|
|
|
if (!hasa)
|
|
|
|
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
|
2004-11-01 01:45:31 -08:00
|
|
|
#endif
|
2007-08-24 19:33:51 -07:00
|
|
|
|
2019-11-29 23:58:46 -08:00
|
|
|
if (!__imlib_AllocateData(im))
|
|
|
|
goto quit;
|
2004-11-01 01:45:31 -08:00
|
|
|
|
2020-02-29 02:08:27 -08:00
|
|
|
pdata.lines = malloc(h * sizeof(unsigned char *));
|
|
|
|
if (!pdata.lines)
|
2019-11-29 23:58:46 -08:00
|
|
|
goto quit;
|
2004-11-01 01:45:31 -08:00
|
|
|
|
2019-11-29 23:58:46 -08:00
|
|
|
for (i = 0; i < h; i++)
|
2020-02-29 02:08:27 -08:00
|
|
|
pdata.lines[i] = ((unsigned char *)(im->data)) + (i * w * sizeof(DATA32));
|
2019-11-29 23:58:46 -08:00
|
|
|
|
2019-11-30 07:22:55 -08:00
|
|
|
if (im->lc)
|
2019-11-29 23:58:46 -08:00
|
|
|
{
|
2019-11-30 07:22:55 -08:00
|
|
|
int y, pass, n_passes, nrows = 1;
|
2019-11-29 23:58:46 -08:00
|
|
|
|
2019-11-30 07:22:55 -08:00
|
|
|
n_passes = png_set_interlace_handling(png_ptr);
|
|
|
|
for (pass = 0; pass < n_passes; pass++)
|
2019-11-29 23:58:46 -08:00
|
|
|
{
|
2019-11-30 07:22:55 -08:00
|
|
|
__imlib_LoadProgressSetPass(im, pass, n_passes);
|
|
|
|
|
2019-11-29 23:58:46 -08:00
|
|
|
for (y = 0; y < h; y += nrows)
|
2004-11-01 01:45:31 -08:00
|
|
|
{
|
2020-02-29 02:08:27 -08:00
|
|
|
png_read_rows(png_ptr, &pdata.lines[y], NULL, nrows);
|
2004-11-01 01:45:31 -08:00
|
|
|
|
2019-11-30 07:22:55 -08:00
|
|
|
if (__imlib_LoadProgressRows(im, y, nrows))
|
2019-11-29 23:58:46 -08:00
|
|
|
{
|
2019-11-30 07:22:55 -08:00
|
|
|
rc = LOAD_BREAK;
|
|
|
|
goto quit1;
|
2004-11-01 01:45:31 -08:00
|
|
|
}
|
2019-11-29 23:58:46 -08:00
|
|
|
}
|
2004-11-01 01:45:31 -08:00
|
|
|
}
|
|
|
|
}
|
2019-11-29 23:58:46 -08:00
|
|
|
else
|
|
|
|
{
|
2020-02-29 02:08:27 -08:00
|
|
|
png_read_image(png_ptr, pdata.lines);
|
2019-11-29 23:58:46 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
rc = LOAD_SUCCESS;
|
|
|
|
|
2004-11-01 01:45:31 -08:00
|
|
|
#ifdef PNG_TEXT_SUPPORTED
|
|
|
|
{
|
|
|
|
png_textp text;
|
|
|
|
int num;
|
|
|
|
|
|
|
|
num = 0;
|
|
|
|
png_get_text(png_ptr, info_ptr, &text, &num);
|
|
|
|
for (i = 0; i < num; i++)
|
|
|
|
{
|
|
|
|
if (!strcmp(text[i].key, "Imlib2-Comment"))
|
|
|
|
__imlib_AttachTag(im, "comment", 0, strdup(text[i].text),
|
|
|
|
comment_free);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2019-11-29 23:58:46 -08:00
|
|
|
|
|
|
|
quit1:
|
|
|
|
png_read_end(png_ptr, info_ptr);
|
|
|
|
quit:
|
2020-02-29 02:08:27 -08:00
|
|
|
free(pdata.lines);
|
2019-11-29 23:58:46 -08:00
|
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
|
|
|
if (rc <= 0)
|
|
|
|
__imlib_FreeData(im);
|
|
|
|
|
|
|
|
return rc;
|
2004-11-01 01:45:31 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
char
|
|
|
|
save(ImlibImage * im, ImlibProgressFunction progress, char progress_granularity)
|
|
|
|
{
|
2019-12-04 08:25:16 -08:00
|
|
|
int rc;
|
2004-11-01 01:45:31 -08:00
|
|
|
FILE *f;
|
|
|
|
png_structp png_ptr;
|
|
|
|
png_infop info_ptr;
|
|
|
|
DATA32 *ptr;
|
2011-02-19 11:33:58 -08:00
|
|
|
int x, y, j, interlace;
|
2016-03-16 11:55:41 -07:00
|
|
|
png_bytep row_ptr, data;
|
2004-11-01 01:45:31 -08:00
|
|
|
png_color_8 sig_bit;
|
|
|
|
ImlibImageTag *tag;
|
2019-12-05 11:27:08 -08:00
|
|
|
int quality = 75, compression = 3;
|
|
|
|
int pass, n_passes = 1;
|
2004-11-01 01:45:31 -08:00
|
|
|
|
|
|
|
f = fopen(im->real_file, "wb");
|
|
|
|
if (!f)
|
2019-12-04 08:25:16 -08:00
|
|
|
return LOAD_FAIL;
|
|
|
|
|
|
|
|
rc = LOAD_FAIL;
|
|
|
|
info_ptr = NULL;
|
|
|
|
data = NULL;
|
|
|
|
|
2004-11-01 01:45:31 -08:00
|
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
|
|
if (!png_ptr)
|
2019-12-04 08:25:16 -08:00
|
|
|
goto quit;
|
|
|
|
|
2004-11-01 01:45:31 -08:00
|
|
|
info_ptr = png_create_info_struct(png_ptr);
|
2010-08-21 06:52:25 -07:00
|
|
|
if (!info_ptr)
|
2019-12-04 08:25:16 -08:00
|
|
|
goto quit;
|
|
|
|
|
2011-02-19 11:23:57 -08:00
|
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
2019-12-04 08:25:16 -08:00
|
|
|
goto quit;
|
2004-11-01 01:45:31 -08:00
|
|
|
|
|
|
|
/* check whether we should use interlacing */
|
2011-02-19 11:33:58 -08:00
|
|
|
interlace = PNG_INTERLACE_NONE;
|
2004-11-01 01:45:31 -08:00
|
|
|
if ((tag = __imlib_GetTag(im, "interlacing")) && tag->val)
|
|
|
|
{
|
|
|
|
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
|
2011-04-09 02:24:44 -07:00
|
|
|
interlace = PNG_INTERLACE_ADAM7;
|
2004-11-01 01:45:31 -08:00
|
|
|
#endif
|
|
|
|
}
|
2007-08-24 19:33:51 -07:00
|
|
|
|
2004-11-01 01:45:31 -08:00
|
|
|
png_init_io(png_ptr, f);
|
|
|
|
if (im->flags & F_HAS_ALPHA)
|
|
|
|
{
|
|
|
|
png_set_IHDR(png_ptr, info_ptr, im->w, im->h, 8,
|
2011-02-19 11:33:58 -08:00
|
|
|
PNG_COLOR_TYPE_RGB_ALPHA, interlace,
|
2004-11-01 01:45:31 -08:00
|
|
|
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
png_set_swap_alpha(png_ptr);
|
|
|
|
#else
|
|
|
|
png_set_bgr(png_ptr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
png_set_IHDR(png_ptr, info_ptr, im->w, im->h, 8, PNG_COLOR_TYPE_RGB,
|
2011-02-19 11:33:58 -08:00
|
|
|
interlace, PNG_COMPRESSION_TYPE_BASE,
|
2004-11-01 01:45:31 -08:00
|
|
|
PNG_FILTER_TYPE_BASE);
|
|
|
|
data = malloc(im->w * 3 * sizeof(char));
|
|
|
|
}
|
|
|
|
sig_bit.red = 8;
|
|
|
|
sig_bit.green = 8;
|
|
|
|
sig_bit.blue = 8;
|
|
|
|
sig_bit.alpha = 8;
|
|
|
|
png_set_sBIT(png_ptr, info_ptr, &sig_bit);
|
|
|
|
/* quality */
|
|
|
|
tag = __imlib_GetTag(im, "quality");
|
|
|
|
if (tag)
|
|
|
|
{
|
|
|
|
quality = tag->val;
|
|
|
|
if (quality < 1)
|
|
|
|
quality = 1;
|
|
|
|
if (quality > 99)
|
|
|
|
quality = 99;
|
|
|
|
}
|
|
|
|
/* convert to compression */
|
|
|
|
quality = quality / 10;
|
|
|
|
compression = 9 - quality;
|
|
|
|
/* compression */
|
|
|
|
tag = __imlib_GetTag(im, "compression");
|
|
|
|
if (tag)
|
|
|
|
compression = tag->val;
|
|
|
|
if (compression < 0)
|
|
|
|
compression = 0;
|
|
|
|
if (compression > 9)
|
|
|
|
compression = 9;
|
|
|
|
tag = __imlib_GetTag(im, "comment");
|
|
|
|
if (tag)
|
|
|
|
{
|
|
|
|
#ifdef PNG_TEXT_SUPPORTED
|
|
|
|
png_text text;
|
|
|
|
|
2013-07-02 13:47:17 -07:00
|
|
|
text.key = (char *)"Imlib2-Comment";
|
2004-11-01 01:45:31 -08:00
|
|
|
text.text = tag->data;
|
|
|
|
text.compression = PNG_TEXT_COMPRESSION_zTXt;
|
|
|
|
png_set_text(png_ptr, info_ptr, &(text), 1);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
png_set_compression_level(png_ptr, compression);
|
|
|
|
png_write_info(png_ptr, info_ptr);
|
|
|
|
png_set_shift(png_ptr, &sig_bit);
|
|
|
|
png_set_packing(png_ptr);
|
|
|
|
|
2011-02-19 11:33:58 -08:00
|
|
|
#ifdef PNG_WRITE_INTERLACING_SUPPORTED
|
2019-12-05 11:27:08 -08:00
|
|
|
n_passes = png_set_interlace_handling(png_ptr);
|
2011-02-19 11:33:58 -08:00
|
|
|
#endif
|
|
|
|
|
2019-12-05 11:27:08 -08:00
|
|
|
for (pass = 0; pass < n_passes; pass++)
|
2004-11-01 01:45:31 -08:00
|
|
|
{
|
2011-04-09 02:24:44 -07:00
|
|
|
ptr = im->data;
|
2004-11-01 01:45:31 -08:00
|
|
|
|
2019-12-05 11:27:08 -08:00
|
|
|
if (im->lc)
|
|
|
|
__imlib_LoadProgressSetPass(im, pass, n_passes);
|
|
|
|
|
2011-04-09 02:24:44 -07:00
|
|
|
for (y = 0; y < im->h; y++)
|
|
|
|
{
|
|
|
|
if (im->flags & F_HAS_ALPHA)
|
|
|
|
row_ptr = (png_bytep) ptr;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (j = 0, x = 0; x < im->w; x++)
|
|
|
|
{
|
2019-11-08 22:48:34 -08:00
|
|
|
DATA32 pixel = ptr[x];
|
|
|
|
|
|
|
|
data[j++] = PIXEL_R(pixel);
|
|
|
|
data[j++] = PIXEL_G(pixel);
|
|
|
|
data[j++] = PIXEL_B(pixel);
|
2011-04-09 02:24:44 -07:00
|
|
|
}
|
|
|
|
row_ptr = (png_bytep) data;
|
|
|
|
}
|
|
|
|
png_write_rows(png_ptr, &row_ptr, 1);
|
2019-11-08 22:48:34 -08:00
|
|
|
|
2019-12-05 11:27:08 -08:00
|
|
|
if (im->lc && __imlib_LoadProgressRows(im, y, 1))
|
2011-04-09 02:24:44 -07:00
|
|
|
{
|
2019-12-05 11:27:08 -08:00
|
|
|
rc = LOAD_BREAK;
|
|
|
|
goto quit;
|
2011-04-09 02:24:44 -07:00
|
|
|
}
|
2019-12-05 11:27:08 -08:00
|
|
|
|
2011-04-09 02:24:44 -07:00
|
|
|
ptr += im->w;
|
|
|
|
}
|
|
|
|
}
|
2019-12-04 08:25:16 -08:00
|
|
|
|
|
|
|
rc = LOAD_SUCCESS;
|
|
|
|
|
|
|
|
quit:
|
|
|
|
free(data);
|
2004-11-01 01:45:31 -08:00
|
|
|
png_write_end(png_ptr, info_ptr);
|
|
|
|
png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr);
|
2019-12-04 08:25:16 -08:00
|
|
|
if (info_ptr)
|
|
|
|
png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr);
|
|
|
|
if (png_ptr)
|
|
|
|
png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
|
2004-11-01 01:45:31 -08:00
|
|
|
|
|
|
|
fclose(f);
|
2019-12-04 08:25:16 -08:00
|
|
|
|
|
|
|
return rc;
|
2004-11-01 01:45:31 -08:00
|
|
|
}
|
|
|
|
|
2007-08-24 19:33:51 -07:00
|
|
|
/* fills the ImlibLoader struct with a string array of format file */
|
2004-11-01 01:45:31 -08:00
|
|
|
/* extensions this loader can load. eg: */
|
|
|
|
/* loader->formats = { "jpeg", "jpg"}; */
|
|
|
|
/* giving permutations is a good idea. case sensitivity is irrelevant */
|
|
|
|
/* your loader CAN load more than one format if it likes - like: */
|
|
|
|
/* loader->formats = { "gif", "png", "jpeg", "jpg"} */
|
|
|
|
/* if it can load those formats. */
|
|
|
|
void
|
|
|
|
formats(ImlibLoader * l)
|
|
|
|
{
|
2013-07-02 13:47:17 -07:00
|
|
|
static const char *const list_formats[] = { "png" };
|
2020-01-03 12:18:21 -08:00
|
|
|
__imlib_LoaderSetFormats(l, list_formats,
|
|
|
|
sizeof(list_formats) / sizeof(char *));
|
2004-11-01 01:45:31 -08:00
|
|
|
}
|