gtk2/gdk-pixbuf/io-png.c
Sebastian Wilhelmi d99bd97fe8 Remove assorted G_OBJECT casts where unnecessary.
2001-12-13  Sebastian Wilhelmi  <wilhelmi@ira.uka.de>

	* gdk-pixbuf-animation.c, gdk-pixbuf-loader.c, gdk-pixpuf.c,
	io-gif-animation.c, io-gif.c, io-tiff.c, test-loaders.c: Remove
	assorted G_OBJECT casts where unnecessary.

	* gdk-pixbuf-loader.c: Call g_object_ref and g_object_unref
	instead of gdk_pixbuf_animation_ref and gdk_pixbuf_animation_unref
	resp.

	* gdk-pixbuf-csource.c, io-bmp.c, io-gif-animation.c, io-ico.c,
	io-jpeg.c, io-png.c, io-pnm.c, io-ras.c, io-tga.c, io-wbmp.c,
	io-xbm.c, io-xpm.c, test-gdk-pixbuf.c: Dito for gdk_pixbuf_ref and
	gdk_pixbuf_unref.

	* Makefile.am, pixops/Makefile.am: Compile everything with
	-DG_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED

	* gdk-pixdata.c: Use g_ascii_strup() instead of g_strup().

	* io-xpm.c: Use g_ascii_strcasecmp() instead of g_strcasecmp().

	* demos/testpixbuf-drawable.c, demos/testpixbuf-save.c,
	demos/testpixbuf-scale.c, demos/testpixbuf.c: Call g_object_ref
	and g_object_unref instead of gdk_pixbuf_ref and gdk_pixbuf_unref
	resp.
2001-12-13 21:22:12 +00:00

867 lines
29 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/* GdkPixbuf library - PNG image loader
*
* Copyright (C) 1999 Mark Crichton
* Copyright (C) 1999 The Free Software Foundation
*
* Authors: Mark Crichton <crichton@gimp.org>
* Federico Mena-Quintero <federico@gimp.org>
*
* This 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 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <png.h>
#include "gdk-pixbuf-private.h"
#include "gdk-pixbuf-io.h"
static void
setup_png_transformations(png_structp png_read_ptr, png_infop png_info_ptr,
gboolean *fatal_error_occurred,
png_uint_32* width_p, png_uint_32* height_p,
int* color_type_p)
{
png_uint_32 width, height;
int bit_depth, color_type, interlace_type, compression_type, filter_type;
#ifndef G_DISABLE_CHECKS
int channels;
#endif
/* Get the image info */
png_get_IHDR (png_read_ptr, png_info_ptr,
&width, &height,
&bit_depth,
&color_type,
&interlace_type,
&compression_type,
&filter_type);
/* set_expand() basically needs to be called unless
we are already in RGB/RGBA mode
*/
if (color_type == PNG_COLOR_TYPE_PALETTE &&
bit_depth <= 8) {
/* Convert indexed images to RGB */
png_set_expand (png_read_ptr);
} else if (color_type == PNG_COLOR_TYPE_GRAY &&
bit_depth < 8) {
/* Convert grayscale to RGB */
png_set_expand (png_read_ptr);
} else if (png_get_valid (png_read_ptr,
png_info_ptr, PNG_INFO_tRNS)) {
/* If we have transparency header, convert it to alpha
channel */
png_set_expand(png_read_ptr);
} else if (bit_depth < 8) {
/* If we have < 8 scale it up to 8 */
png_set_expand(png_read_ptr);
/* Conceivably, png_set_packing() is a better idea;
* God only knows how libpng works
*/
}
/* If we are 16-bit, convert to 8-bit */
if (bit_depth == 16) {
png_set_strip_16(png_read_ptr);
}
/* If gray scale, convert to RGB */
if (color_type == PNG_COLOR_TYPE_GRAY ||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
png_set_gray_to_rgb(png_read_ptr);
}
/* If interlaced, handle that */
if (interlace_type != PNG_INTERLACE_NONE) {
png_set_interlace_handling(png_read_ptr);
}
/* Update the info the reflect our transformations */
png_read_update_info(png_read_ptr, png_info_ptr);
png_get_IHDR (png_read_ptr, png_info_ptr,
&width, &height,
&bit_depth,
&color_type,
&interlace_type,
&compression_type,
&filter_type);
*width_p = width;
*height_p = height;
*color_type_p = color_type;
#ifndef G_DISABLE_CHECKS
/* Check that the new info is what we want */
if (bit_depth != 8) {
g_warning("Bits per channel of transformed PNG is %d, not 8.", bit_depth);
*fatal_error_occurred = TRUE;
return;
}
if ( ! (color_type == PNG_COLOR_TYPE_RGB ||
color_type == PNG_COLOR_TYPE_RGB_ALPHA) ) {
g_warning("Transformed PNG not RGB or RGBA.");
*fatal_error_occurred = TRUE;
return;
}
channels = png_get_channels(png_read_ptr, png_info_ptr);
if ( ! (channels == 3 || channels == 4) ) {
g_warning("Transformed PNG has %d channels, must be 3 or 4.", channels);
*fatal_error_occurred = TRUE;
return;
}
#endif
}
static void
png_simple_error_callback(png_structp png_save_ptr,
png_const_charp error_msg)
{
GError **error;
error = png_get_error_ptr(png_save_ptr);
/* I don't trust libpng to call the error callback only once,
* so check for already-set error
*/
if (error && *error == NULL) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_FAILED,
_("Fatal error in PNG image file: %s"),
error_msg);
}
}
static void
png_simple_warning_callback(png_structp png_save_ptr,
png_const_charp warning_msg)
{
/* Don't print anything; we should not be dumping junk to
* stderr, since that may be bad for some apps. If it's
* important enough to display, we need to add a GError
* **warning return location wherever we have an error return
* location.
*/
}
/* Destroy notification function for the pixbuf */
static void
free_buffer (guchar *pixels, gpointer data)
{
g_free (pixels);
}
static gboolean
png_text_to_pixbuf_option (png_text text_ptr,
gchar **key,
gchar **value)
{
*value = g_convert (text_ptr.text, -1,
"UTF-8", "ISO-8859-1",
NULL, NULL, NULL);
if (*value) {
*key = g_strconcat ("tEXt::", text_ptr.key, NULL);
return TRUE;
} else {
g_warning ("Couldn't convert tEXt chunk value to UTF-8.");
*key = NULL;
return FALSE;
}
}
/* Shared library entry point */
static GdkPixbuf *
gdk_pixbuf__png_image_load (FILE *f, GError **error)
{
GdkPixbuf *pixbuf;
png_structp png_ptr;
png_infop info_ptr, end_info;
png_textp text_ptr;
gboolean failed = FALSE;
gint i, ctype, bpp;
png_uint_32 w, h;
png_bytepp rows;
guchar *pixels;
gint num_texts;
gchar **options = NULL;
png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,
error,
png_simple_error_callback,
png_simple_warning_callback);
if (!png_ptr)
return NULL;
info_ptr = png_create_info_struct (png_ptr);
if (!info_ptr) {
png_destroy_read_struct (&png_ptr, NULL, NULL);
return NULL;
}
end_info = png_create_info_struct (png_ptr);
if (!end_info) {
png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
return NULL;
}
if (setjmp (png_ptr->jmpbuf)) {
png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
return NULL;
}
png_init_io (png_ptr, f);
png_read_info (png_ptr, info_ptr);
setup_png_transformations(png_ptr, info_ptr, &failed, &w, &h, &ctype);
if (failed) {
png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
return NULL;
}
if (ctype & PNG_COLOR_MASK_ALPHA)
bpp = 4;
else
bpp = 3;
pixels = g_try_malloc (w * h * bpp);
if (!pixels) {
/* Check error NULL, normally this would be broken,
* but libpng makes me want to code defensively.
*/
if (error && *error == NULL) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Insufficient memory to load PNG file"));
}
png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
return NULL;
}
rows = g_new (png_bytep, h);
for (i = 0; i < h; i++)
rows[i] = pixels + i * w * bpp;
png_read_image (png_ptr, rows);
if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_texts)) {
options = g_new (gchar *, num_texts * 2);
for (i = 0; i < num_texts; i++) {
png_text_to_pixbuf_option (text_ptr[i],
options + 2*i,
options + 2*i + 1);
}
}
png_destroy_read_struct (&png_ptr, &info_ptr, &end_info);
g_free (rows);
if (ctype & PNG_COLOR_MASK_ALPHA)
pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
w, h, w * 4,
free_buffer, NULL);
else
pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
w, h, w * 3,
free_buffer, NULL);
if (options) {
for (i = 0; i < num_texts; i++) {
if (pixbuf) {
if (!gdk_pixbuf_set_option (pixbuf,
options[2*i],
options[2*i+1]))
g_warning ("Got multiple tEXt chunks for the same key.");
}
g_free (options[2*i]);
g_free (options[2*i+1]);
}
g_free (options);
}
return pixbuf;
}
/* I wish these avoided the setjmp()/longjmp() crap in libpng instead
just allow you to change the error reporting. */
static void png_error_callback (png_structp png_read_ptr,
png_const_charp error_msg);
static void png_warning_callback(png_structp png_read_ptr,
png_const_charp warning_msg);
/* Called at the start of the progressive load */
static void png_info_callback (png_structp png_read_ptr,
png_infop png_info_ptr);
/* Called for each row; note that you will get duplicate row numbers
for interlaced PNGs */
static void png_row_callback (png_structp png_read_ptr,
png_bytep new_row,
png_uint_32 row_num,
int pass_num);
/* Called after reading the entire image */
static void png_end_callback (png_structp png_read_ptr,
png_infop png_info_ptr);
typedef struct _LoadContext LoadContext;
struct _LoadContext {
png_structp png_read_ptr;
png_infop png_info_ptr;
ModulePreparedNotifyFunc prepare_func;
ModuleUpdatedNotifyFunc update_func;
gpointer notify_user_data;
GdkPixbuf* pixbuf;
/* row number of first row seen, or -1 if none yet seen */
gint first_row_seen_in_chunk;
/* pass number for the first row seen */
gint first_pass_seen_in_chunk;
/* row number of last row seen */
gint last_row_seen_in_chunk;
gint last_pass_seen_in_chunk;
/* highest row number seen */
gint max_row_seen_in_chunk;
guint fatal_error_occurred : 1;
GError **error;
};
static gpointer
gdk_pixbuf__png_image_begin_load (ModulePreparedNotifyFunc prepare_func,
ModuleUpdatedNotifyFunc update_func,
gpointer user_data,
GError **error)
{
LoadContext* lc;
lc = g_new0(LoadContext, 1);
lc->fatal_error_occurred = FALSE;
lc->prepare_func = prepare_func;
lc->update_func = update_func;
lc->notify_user_data = user_data;
lc->first_row_seen_in_chunk = -1;
lc->last_row_seen_in_chunk = -1;
lc->first_pass_seen_in_chunk = -1;
lc->last_pass_seen_in_chunk = -1;
lc->max_row_seen_in_chunk = -1;
lc->error = error;
/* Create the main PNG context struct */
lc->png_read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
lc, /* error/warning callback data */
png_error_callback,
png_warning_callback);
if (lc->png_read_ptr == NULL) {
g_free(lc);
/* error callback should have set the error */
return NULL;
}
if (setjmp (lc->png_read_ptr->jmpbuf)) {
if (lc->png_info_ptr)
png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
g_free(lc);
/* error callback should have set the error */
return NULL;
}
/* Create the auxiliary context struct */
lc->png_info_ptr = png_create_info_struct(lc->png_read_ptr);
if (lc->png_info_ptr == NULL) {
png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
g_free(lc);
/* error callback should have set the error */
return NULL;
}
png_set_progressive_read_fn(lc->png_read_ptr,
lc, /* callback data */
png_info_callback,
png_row_callback,
png_end_callback);
/* We don't want to keep modifying error after returning here,
* it may no longer be valid.
*/
lc->error = NULL;
return lc;
}
static gboolean
gdk_pixbuf__png_image_stop_load (gpointer context, GError **error)
{
LoadContext* lc = context;
g_return_val_if_fail(lc != NULL, TRUE);
/* FIXME this thing needs to report errors if
* we have unused image data
*/
if (lc->pixbuf)
g_object_unref (lc->pixbuf);
png_destroy_read_struct(&lc->png_read_ptr, NULL, NULL);
g_free(lc);
return TRUE;
}
static gboolean
gdk_pixbuf__png_image_load_increment(gpointer context,
const guchar *buf, guint size,
GError **error)
{
LoadContext* lc = context;
g_return_val_if_fail(lc != NULL, FALSE);
/* reset */
lc->first_row_seen_in_chunk = -1;
lc->last_row_seen_in_chunk = -1;
lc->first_pass_seen_in_chunk = -1;
lc->last_pass_seen_in_chunk = -1;
lc->max_row_seen_in_chunk = -1;
lc->error = error;
/* Invokes our callbacks as needed */
if (setjmp (lc->png_read_ptr->jmpbuf)) {
lc->error = NULL;
return FALSE;
} else {
png_process_data(lc->png_read_ptr, lc->png_info_ptr,
(guchar*) buf, size);
}
if (lc->fatal_error_occurred) {
lc->error = NULL;
return FALSE;
} else {
if (lc->first_row_seen_in_chunk >= 0) {
/* We saw at least one row */
gint pass_diff = lc->last_pass_seen_in_chunk - lc->first_pass_seen_in_chunk;
g_assert(pass_diff >= 0);
if (pass_diff == 0) {
/* start and end row were in the same pass */
(lc->update_func)(lc->pixbuf, 0,
lc->first_row_seen_in_chunk,
lc->pixbuf->width,
(lc->last_row_seen_in_chunk -
lc->first_row_seen_in_chunk) + 1,
lc->notify_user_data);
} else if (pass_diff == 1) {
/* We have from the first row seen to
the end of the image (max row
seen), then from the top of the
image to the last row seen */
/* first row to end */
(lc->update_func)(lc->pixbuf, 0,
lc->first_row_seen_in_chunk,
lc->pixbuf->width,
(lc->max_row_seen_in_chunk -
lc->first_row_seen_in_chunk) + 1,
lc->notify_user_data);
/* top to last row */
(lc->update_func)(lc->pixbuf,
0, 0,
lc->pixbuf->width,
lc->last_row_seen_in_chunk + 1,
lc->notify_user_data);
} else {
/* We made at least one entire pass, so update the
whole image */
(lc->update_func)(lc->pixbuf,
0, 0,
lc->pixbuf->width,
lc->max_row_seen_in_chunk + 1,
lc->notify_user_data);
}
}
lc->error = NULL;
return TRUE;
}
}
/* Called at the start of the progressive load, once we have image info */
static void
png_info_callback (png_structp png_read_ptr,
png_infop png_info_ptr)
{
LoadContext* lc;
png_uint_32 width, height;
png_textp png_text_ptr;
int i, num_texts;
int color_type;
gboolean have_alpha = FALSE;
gboolean failed = FALSE;
lc = png_get_progressive_ptr(png_read_ptr);
if (lc->fatal_error_occurred)
return;
setup_png_transformations(lc->png_read_ptr,
lc->png_info_ptr,
&failed,
&width, &height, &color_type);
if (failed) {
lc->fatal_error_occurred = TRUE;
return;
}
/* If we have alpha, set a flag */
if (color_type & PNG_COLOR_MASK_ALPHA)
have_alpha = TRUE;
lc->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, have_alpha, 8, width, height);
if (lc->pixbuf == NULL) {
/* Failed to allocate memory */
lc->fatal_error_occurred = TRUE;
if (lc->error && *lc->error == NULL) {
g_set_error (lc->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Insufficient memory to store a %ld by %ld image; try exiting some applications to reduce memory usage"),
width, height);
}
return;
}
/* Extract tEXt chunks and attach them as pixbuf options */
if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) {
for (i = 0; i < num_texts; i++) {
gchar *key, *value;
if (png_text_to_pixbuf_option (png_text_ptr[i],
&key, &value)) {
gdk_pixbuf_set_option (lc->pixbuf, key, value);
g_free (key);
g_free (value);
}
}
}
/* Notify the client that we are ready to go */
if (lc->prepare_func)
(* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data);
return;
}
/* Called for each row; note that you will get duplicate row numbers
for interlaced PNGs */
static void
png_row_callback (png_structp png_read_ptr,
png_bytep new_row,
png_uint_32 row_num,
int pass_num)
{
LoadContext* lc;
guchar* old_row = NULL;
lc = png_get_progressive_ptr(png_read_ptr);
if (lc->fatal_error_occurred)
return;
if (row_num < 0 || row_num >= lc->pixbuf->height) {
lc->fatal_error_occurred = TRUE;
if (lc->error && *lc->error == NULL) {
g_set_error (lc->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Fatal error reading PNG image file"));
}
return;
}
if (lc->first_row_seen_in_chunk < 0) {
lc->first_row_seen_in_chunk = row_num;
lc->first_pass_seen_in_chunk = pass_num;
}
lc->max_row_seen_in_chunk = MAX(lc->max_row_seen_in_chunk, ((gint)row_num));
lc->last_row_seen_in_chunk = row_num;
lc->last_pass_seen_in_chunk = pass_num;
old_row = lc->pixbuf->pixels + (row_num * lc->pixbuf->rowstride);
png_progressive_combine_row(lc->png_read_ptr, old_row, new_row);
}
/* Called after reading the entire image */
static void
png_end_callback (png_structp png_read_ptr,
png_infop png_info_ptr)
{
LoadContext* lc;
lc = png_get_progressive_ptr(png_read_ptr);
if (lc->fatal_error_occurred)
return;
}
static void
png_error_callback(png_structp png_read_ptr,
png_const_charp error_msg)
{
LoadContext* lc;
lc = png_get_error_ptr(png_read_ptr);
lc->fatal_error_occurred = TRUE;
/* I don't trust libpng to call the error callback only once,
* so check for already-set error
*/
if (lc->error && *lc->error == NULL) {
g_set_error (lc->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Fatal error reading PNG image file: %s"),
error_msg);
}
longjmp (png_read_ptr->jmpbuf, 1);
}
static void
png_warning_callback(png_structp png_read_ptr,
png_const_charp warning_msg)
{
LoadContext* lc;
lc = png_get_error_ptr(png_read_ptr);
/* Don't print anything; we should not be dumping junk to
* stderr, since that may be bad for some apps. If it's
* important enough to display, we need to add a GError
* **warning return location wherever we have an error return
* location.
*/
}
/* Save */
static gboolean
gdk_pixbuf__png_image_save (FILE *f,
GdkPixbuf *pixbuf,
gchar **keys,
gchar **values,
GError **error)
{
png_structp png_ptr;
png_infop info_ptr;
png_textp text_ptr = NULL;
guchar *ptr;
guchar *pixels;
int y;
int i;
png_bytep row_ptr;
png_color_8 sig_bit;
int w, h, rowstride;
int has_alpha;
int bpc;
int num_keys;
num_keys = 0;
if (keys && *keys) {
gchar **kiter;
gchar *key;
int len;
for (kiter = keys; *kiter; kiter++) {
if (strncmp (*kiter, "tEXt::", 6) != 0) {
g_warning ("Bad option name '%s' passed to PNG saver", *kiter);
return FALSE;
}
key = *kiter + 6;
len = strlen (key);
if (len <= 1 || len > 79) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_BAD_OPTION,
_("Keys for PNG tEXt chunks must have at least 1 and at most 79 characters."));
return FALSE;
}
for (i = 0; i < len; i++) {
if ((guchar) key[i] > 127) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_BAD_OPTION,
_("Keys for PNG tEXt chunks must be ASCII characters."));
return FALSE;
}
}
num_keys++;
}
}
if (num_keys > 0) {
text_ptr = g_new0 (png_text, num_keys);
for (i = 0; i < num_keys; i++) {
text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE;
text_ptr[i].key = keys[i] + 6;
text_ptr[i].text = g_convert (values[i], -1,
"ISO-8859-1", "UTF-8",
NULL, &text_ptr[i].text_length,
NULL);
if (!text_ptr[i].text) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_BAD_OPTION,
_("Value for PNG tEXt chunk can not be converted to ISO-8859-1 encoding."));
num_keys = i;
for (i = 0; i < num_keys; i++)
g_free (text_ptr[i].text);
g_free (text_ptr);
return FALSE;
}
}
}
bpc = gdk_pixbuf_get_bits_per_sample (pixbuf);
w = gdk_pixbuf_get_width (pixbuf);
h = gdk_pixbuf_get_height (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
pixels = gdk_pixbuf_get_pixels (pixbuf);
png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
error,
png_simple_error_callback,
png_simple_warning_callback);
g_return_val_if_fail (png_ptr != NULL, FALSE);
info_ptr = png_create_info_struct (png_ptr);
if (info_ptr == NULL) {
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
return FALSE;
}
if (setjmp (png_ptr->jmpbuf)) {
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
return FALSE;
}
if (num_keys > 0) {
png_set_text (png_ptr, info_ptr, text_ptr, num_keys);
}
png_init_io (png_ptr, f);
if (has_alpha) {
png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
} else {
png_set_IHDR (png_ptr, info_ptr, w, h, bpc,
PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
}
sig_bit.red = bpc;
sig_bit.green = bpc;
sig_bit.blue = bpc;
sig_bit.alpha = bpc;
png_set_sBIT (png_ptr, info_ptr, &sig_bit);
png_write_info (png_ptr, info_ptr);
png_set_shift (png_ptr, &sig_bit);
png_set_packing (png_ptr);
ptr = pixels;
for (y = 0; y < h; y++) {
row_ptr = (png_bytep)ptr;
png_write_rows (png_ptr, &row_ptr, 1);
ptr += rowstride;
}
png_write_end (png_ptr, info_ptr);
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
if (num_keys > 0) {
for (i = 0; i < num_keys; i++)
g_free (text_ptr[i].text);
g_free (text_ptr);
}
return TRUE;
}
void
gdk_pixbuf__png_fill_vtable (GdkPixbufModule *module)
{
module->load = gdk_pixbuf__png_image_load;
module->begin_load = gdk_pixbuf__png_image_begin_load;
module->stop_load = gdk_pixbuf__png_image_stop_load;
module->load_increment = gdk_pixbuf__png_image_load_increment;
module->save = gdk_pixbuf__png_image_save;
}