gtk/gdk-pixbuf/gdk-pixdata.c

924 lines
30 KiB
C
Raw Normal View History

/* GdkPixbuf library - GdkPixdata - functions for inlined pixbuf handling
* Copyright (C) 1999, 2001 Tim Janik
*
* 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 "gdk-pixbuf-alias.h"
#include "gdk-pixdata.h"
#include "gdk-pixbuf-private.h"
Clip the retrieved image data to the screen, using a server grab to avoid 2001-06-28 Havoc Pennington <hp@pobox.com> * gdk/x11/gdkimage-x11.c (_gdk_x11_get_image): Clip the retrieved image data to the screen, using a server grab to avoid race conditions. * gtk/gtkitemfactory.c (gtk_item_factory_create_item): remove check for NULL return from gtk_image_new_from_stock(), it never returns NULL. (gtk_item_factory_create_item): fix bug where we parsed the stock ID as an inline pixbuf * gtk/gtktext.c (gtk_text_key_press): numeric keypad support * gtk/gtkspinbutton.c (gtk_spin_button_key_press): numeric keypad support (should be using binding set here) * gtk/gtkoptionmenu.c (gtk_option_menu_key_press): numeric keypad support (should be using binding set here) * gtk/gtkmenushell.c (gtk_menu_shell_class_init): numeric keypad support * gtk/gtkmenu.c (gtk_menu_class_init): numeric keypad support * gtk/gtkmenubar.c (gtk_menu_bar_class_init): numeric keypad * gtk/gtklistitem.c (gtk_list_item_class_init): numeric keypad * gtk/gtkimcontextsimple.c (gtk_im_context_simple_filter_keypress): keypad * gtk/gtkfilesel.c (gtk_file_selection_key_press): keypad * gtk/gtkentry.c (gtk_entry_class_init): numeric keypad fixes * gtk/gtkctree.c (gtk_ctree_class_init): numeric keypad support * gtk/gtkcolorsel.c (palette_activate): keypad support (of course, should be binding-setted) * gtk/gtkwindow.c (gtk_window_class_init): numeric keypad fixes * gtk/gtkclist.c (gtk_clist_class_init): numeric keypad fixes * gtk/gtkcalendar.c: numeric keypad fixes * gtk/gtktextview.c (gtk_text_view_class_init): numeric keypad support * gdk/gdkwindow.c (gdk_window_get_clip_region): fix infinite loop screwup * gdk/gdkpixbuf-render.c (gdk_pixbuf_render_to_drawable_alpha): clip the render area to the drawable's clip region in advance, so we don't get data from the server that we don't need. * gdk/gdkpixbuf-render.c (gdk_pixbuf_render_to_drawable_alpha): check return value of gdk_pixbuf_get_from_drawable(), fall back to bilevel alpha if we can't get the pixbuf to composite against. * gdk/gdkdraw.c (gdk_drawable_get_image): set the image colormap * gdk/gdkimage.c (gdk_image_get_colormap): add gdk_image_set_colormap, gdk_image_get_colormap * gdk/gdkpixbuf-drawable.c (rgbconvert): Change all converters to take a region of the image, instead of converting the entire image. * gtk/gtkwidget.h (struct _GtkWidgetClass): add show_help keybinding signal. Add default bindings for it. Add default handler for show_help that shows the tooltip for the widget. * gtk/gtkdialog.c (gtk_dialog_class_init): add binding set and "close" keybinding signal, remove key press handler. * gtk/gtktooltips.c (gtk_tooltips_set_colors): Just remove this, it's not our usual practice to leave a deprecated function around with a runtime warning, plus we don't want it to appear in docs, plus if we make them yellow no one will want to change them anyhow.
2001-06-29 01:59:02 +00:00
#include <string.h>
#define APPEND g_string_append_printf
/* --- functions --- */
static guint
pixdata_get_length (const GdkPixdata *pixdata)
{
guint bpp, length;
if ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB)
bpp = 3;
else if ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA)
bpp = 4;
else
return 0; /* invalid format */
switch (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK)
{
guint8 *rle_buffer;
guint max_length;
case GDK_PIXDATA_ENCODING_RAW:
length = pixdata->rowstride * pixdata->height;
break;
case GDK_PIXDATA_ENCODING_RLE:
/* need an RLE walk to determine size */
max_length = pixdata->rowstride * pixdata->height;
rle_buffer = pixdata->pixel_data;
length = 0;
while (length < max_length)
{
guint chunk_length = *(rle_buffer++);
if (chunk_length & 128)
{
chunk_length = chunk_length - 128;
if (!chunk_length) /* RLE data corrupted */
return 0;
length += chunk_length * bpp;
rle_buffer += bpp;
}
else
{
if (!chunk_length) /* RLE data corrupted */
return 0;
chunk_length *= bpp;
length += chunk_length;
rle_buffer += chunk_length;
}
}
length = rle_buffer - pixdata->pixel_data;
break;
default:
length = 0;
break;
}
return length;
}
/**
* gdk_pixdata_serialize:
* @pixdata: a valid #GdkPixdata structure to serialize.
* @stream_length_p: location to store the resulting stream length in.
*
* Serializes a #GdkPixdata structure into a byte stream.
* The byte stream consists of a straightforward writeout of the
* #GdkPixdata fields in network byte order, plus the @pixel_data
* bytes the structure points to.
*
* Return value: A newly-allocated string containing the serialized
* #GdkPixdata structure.
**/
guint8* /* free result */
gdk_pixdata_serialize (const GdkPixdata *pixdata,
guint *stream_length_p)
{
guint8 *stream, *s;
guint32 *istream;
guint length;
/* check args passing */
g_return_val_if_fail (pixdata != NULL, NULL);
g_return_val_if_fail (stream_length_p != NULL, NULL);
/* check pixdata contents */
g_return_val_if_fail (pixdata->magic == GDK_PIXBUF_MAGIC_NUMBER, NULL);
g_return_val_if_fail (pixdata->width > 0, NULL);
g_return_val_if_fail (pixdata->height > 0, NULL);
g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
(pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
(pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
length = pixdata_get_length (pixdata);
/* check length field */
g_return_val_if_fail (length != 0, NULL);
stream = g_malloc (GDK_PIXDATA_HEADER_LENGTH + length);
istream = (guint32*) stream;
/* store header */
*istream++ = g_htonl (GDK_PIXBUF_MAGIC_NUMBER);
*istream++ = g_htonl (GDK_PIXDATA_HEADER_LENGTH + length);
*istream++ = g_htonl (pixdata->pixdata_type);
*istream++ = g_htonl (pixdata->rowstride);
*istream++ = g_htonl (pixdata->width);
*istream++ = g_htonl (pixdata->height);
/* copy pixel data */
s = (guint8*) istream;
memcpy (s, pixdata->pixel_data, length);
s += length;
*stream_length_p = GDK_PIXDATA_HEADER_LENGTH + length;
g_assert (s - stream == *stream_length_p); /* paranoid */
return stream;
}
#define return_header_corrupt(error) { \
g_set_error (error, GDK_PIXBUF_ERROR, \
GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image header corrupt")); \
return FALSE; \
}
#define return_invalid_format(error) { \
g_set_error (error, GDK_PIXBUF_ERROR, \
GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image format unknown")); \
return FALSE; \
}
#define return_pixel_corrupt(error) { \
g_set_error (error, GDK_PIXBUF_ERROR, \
GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image pixel data corrupt")); \
return FALSE; \
}
static inline const guint8 *
get_uint32 (const guint8 *stream, guint *result)
{
*result = (stream[0] << 24) + (stream[1] << 16) + (stream[2] << 8) + stream[3];
return stream + 4;
}
/**
* gdk_pixdata_deserialize:
* @pixdata: a #GdkPixdata structure to be filled in.
* @stream_length: length of the stream used for deserialization.
* @stream: stream of bytes containing a serialized #GdkPixdata structure.
* @error: #GError location to indicate failures (maybe %NULL to ignore errors).
*
* Deserializes (reconstruct) a #GdkPixdata structure from a byte stream.
* The byte stream consists of a straightforward writeout of the
* #GdkPixdata fields in network byte order, plus the @pixel_data
* bytes the structure points to.
* The @pixdata contents are reconstructed byte by byte and are checked
* for validity. This function may fail with %GDK_PIXBUF_CORRUPT_IMAGE
* or %GDK_PIXBUF_ERROR_UNKNOWN_TYPE.
*
* Return value: Upon successful deserialization %TRUE is returned,
* %FALSE otherwise.
**/
gboolean
gdk_pixdata_deserialize (GdkPixdata *pixdata,
guint stream_length,
const guint8 *stream,
GError **error)
{
guint color_type, sample_width, encoding;
g_return_val_if_fail (pixdata != NULL, FALSE);
if (stream_length < GDK_PIXDATA_HEADER_LENGTH)
return_header_corrupt (error);
g_return_val_if_fail (stream != NULL, FALSE);
/* deserialize header */
stream = get_uint32 (stream, &pixdata->magic);
stream = get_uint32 (stream, (guint32 *)&pixdata->length);
if (pixdata->magic != GDK_PIXBUF_MAGIC_NUMBER || pixdata->length < GDK_PIXDATA_HEADER_LENGTH)
return_header_corrupt (error);
stream = get_uint32 (stream, &pixdata->pixdata_type);
stream = get_uint32 (stream, &pixdata->rowstride);
stream = get_uint32 (stream, &pixdata->width);
stream = get_uint32 (stream, &pixdata->height);
if (pixdata->width < 1 || pixdata->height < 1 ||
pixdata->rowstride < pixdata->width)
return_header_corrupt (error);
color_type = pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK;
sample_width = pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK;
encoding = pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK;
if ((color_type != GDK_PIXDATA_COLOR_TYPE_RGB &&
color_type != GDK_PIXDATA_COLOR_TYPE_RGBA) ||
sample_width != GDK_PIXDATA_SAMPLE_WIDTH_8 ||
(encoding != GDK_PIXDATA_ENCODING_RAW &&
encoding != GDK_PIXDATA_ENCODING_RLE))
return_invalid_format (error);
/* deserialize pixel data */
if (stream_length < pixdata->length - GDK_PIXDATA_HEADER_LENGTH)
return_pixel_corrupt (error);
pixdata->pixel_data = (guint8 *)stream;
return TRUE;
}
static gboolean
diff2_rgb (guint8 *ip)
{
return ip[0] != ip[3] || ip[1] != ip[4] || ip[2] != ip[5];
}
static gboolean
diff2_rgba (guint8 *ip)
{
return ip[0] != ip[4] || ip[1] != ip[5] || ip[2] != ip[6] || ip[3] != ip[7];
}
static guint8* /* dest buffer bound */
rl_encode_rgbx (guint8 *bp, /* dest buffer */
guint8 *ip, /* image pointer */
guint8 *limit, /* image upper bound */
guint n_ch)
{
gboolean (*diff2_pix) (guint8 *) = n_ch > 3 ? diff2_rgba : diff2_rgb;
guint8 *ilimit = limit - n_ch;
while (ip < limit)
{
g_assert (ip < ilimit); /* paranoid */
if (diff2_pix (ip))
{
guint8 *s_ip = ip;
guint l = 1;
ip += n_ch;
while (l < 127 && ip < ilimit && diff2_pix (ip))
{ ip += n_ch; l += 1; }
if (ip == ilimit && l < 127)
{ ip += n_ch; l += 1; }
*(bp++) = l;
memcpy (bp, s_ip, l * n_ch);
bp += l * n_ch;
}
else
{
guint l = 2;
ip += n_ch;
while (l < 127 && ip < ilimit && !diff2_pix (ip))
{ ip += n_ch; l += 1; }
*(bp++) = l | 128;
memcpy (bp, ip, n_ch);
ip += n_ch;
bp += n_ch;
}
if (ip == ilimit)
{
*(bp++) = 1;
memcpy (bp, ip, n_ch);
ip += n_ch;
bp += n_ch;
}
}
return bp;
}
/* Used as the destroy notification function for gdk_pixbuf_new() */
static void
free_buffer (guchar *pixels, gpointer data)
{
g_free (pixels);
}
/**
* gdk_pixdata_from_pixbuf:
* @pixdata: a #GdkPixdata to fill.
* @pixbuf: the data to fill @pixdata with.
* @use_rle: whether to use run-length encoding for the pixel data.
*
* Converts a #GdkPixbuf to a #GdkPixdata. If @use_rle is %TRUE, the
* pixel data is run-length encoded into newly-allocated memory and a
* pointer to that memory is returned.
*
* Returns: If @ure_rle is %TRUE, a pointer to the newly-allocated memory
* for the run-length encoded pixel data, otherwise %NULL.
**/
gpointer
gdk_pixdata_from_pixbuf (GdkPixdata *pixdata,
const GdkPixbuf *pixbuf,
gboolean use_rle)
{
gpointer free_me = NULL;
guint height, rowstride, encoding, bpp, length;
guint8 *img_buffer;
g_return_val_if_fail (pixdata != NULL, NULL);
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
g_return_val_if_fail (pixbuf->bits_per_sample == 8, NULL);
g_return_val_if_fail ((pixbuf->n_channels == 3 && !pixbuf->has_alpha) ||
(pixbuf->n_channels == 4 && pixbuf->has_alpha), NULL);
g_return_val_if_fail (pixbuf->rowstride >= pixbuf->width, NULL);
height = pixbuf->height;
rowstride = pixbuf->rowstride;
encoding = use_rle ? GDK_PIXDATA_ENCODING_RLE : GDK_PIXDATA_ENCODING_RAW;
bpp = pixbuf->has_alpha ? 4 : 3;
if (encoding == GDK_PIXDATA_ENCODING_RLE)
{
guint pad, n_bytes = rowstride * height;
guint8 *img_buffer_end, *data;
GdkPixbuf *buf = NULL;
if (n_bytes % bpp != 0)
{
rowstride = pixbuf->width * bpp;
n_bytes = rowstride * height;
data = g_malloc (n_bytes);
buf = gdk_pixbuf_new_from_data (data,
GDK_COLORSPACE_RGB,
pixbuf->has_alpha, 8,
pixbuf->width,
pixbuf->height,
rowstride,
free_buffer, NULL);
gdk_pixbuf_copy_area (pixbuf, 0, 0, pixbuf->width, pixbuf->height,
buf, 0, 0);
}
else
buf = (GdkPixbuf *)pixbuf;
pad = rowstride;
pad = MAX (pad, 130 + n_bytes / 127);
data = g_new (guint8, pad + n_bytes);
free_me = data;
img_buffer = data;
img_buffer_end = rl_encode_rgbx (img_buffer,
buf->pixels, buf->pixels + n_bytes,
bpp);
length = img_buffer_end - img_buffer;
if (buf != pixbuf)
g_object_unref (buf);
}
else
{
img_buffer = pixbuf->pixels;
length = rowstride * height;
}
pixdata->magic = GDK_PIXBUF_MAGIC_NUMBER;
pixdata->length = GDK_PIXDATA_HEADER_LENGTH + length;
pixdata->pixdata_type = pixbuf->has_alpha ? GDK_PIXDATA_COLOR_TYPE_RGBA : GDK_PIXDATA_COLOR_TYPE_RGB;
pixdata->pixdata_type |= GDK_PIXDATA_SAMPLE_WIDTH_8;
pixdata->pixdata_type |= encoding;
pixdata->rowstride = rowstride;
pixdata->width = pixbuf->width;
pixdata->height = height;
pixdata->pixel_data = img_buffer;
return free_me;
}
/**
* gdk_pixbuf_from_pixdata:
* @pixdata: a #GdkPixdata to convert into a #GdkPixbuf.
* @copy_pixels: whether to copy raw pixel data; run-length encoded
* pixel data is always copied.
* @error: location to store possible errors.
*
* Converts a #GdkPixdata to a #GdkPixbuf. If @copy_pixels is %TRUE or
* if the pixel data is run-length-encoded, the pixel data is copied into
* newly-allocated memory; otherwise it is reused.
*
* Returns: a new #GdkPixbuf.
**/
GdkPixbuf*
gdk_pixbuf_from_pixdata (const GdkPixdata *pixdata,
gboolean copy_pixels,
GError **error)
{
guint encoding, bpp;
guint8 *data = NULL;
g_return_val_if_fail (pixdata != NULL, NULL);
g_return_val_if_fail (pixdata->width > 0, NULL);
g_return_val_if_fail (pixdata->height > 0, NULL);
g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
(pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
(pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
bpp = (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ? 3 : 4;
encoding = pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK;
if (encoding == GDK_PIXDATA_ENCODING_RLE)
copy_pixels = TRUE;
if (copy_pixels)
{
data = g_try_malloc (pixdata->rowstride * pixdata->height);
if (!data)
{
g_set_error (error, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
2003-12-31 16:02:22 +00:00
ngettext("failed to allocate image buffer of %u byte",
"failed to allocate image buffer of %u bytes",
pixdata->rowstride * pixdata->height),
pixdata->rowstride * pixdata->height);
return NULL;
}
}
if (encoding == GDK_PIXDATA_ENCODING_RLE)
{
const guint8 *rle_buffer = pixdata->pixel_data;
guint8 *image_buffer = data;
guint8 *image_limit = data + pixdata->rowstride * pixdata->height;
gboolean check_overrun = FALSE;
while (image_buffer < image_limit)
{
guint length = *(rle_buffer++);
if (length & 128)
{
length = length - 128;
check_overrun = image_buffer + length * bpp > image_limit;
if (check_overrun)
length = (image_limit - image_buffer) / bpp;
if (bpp < 4) /* RGB */
do
{
memcpy (image_buffer, rle_buffer, 3);
image_buffer += 3;
}
while (--length);
else /* RGBA */
do
{
memcpy (image_buffer, rle_buffer, 4);
image_buffer += 4;
}
while (--length);
rle_buffer += bpp;
}
else
{
length *= bpp;
check_overrun = image_buffer + length > image_limit;
if (check_overrun)
length = image_limit - image_buffer;
memcpy (image_buffer, rle_buffer, length);
image_buffer += length;
rle_buffer += length;
}
}
if (check_overrun)
{
g_free (data);
g_set_error (error, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image pixel data corrupt"));
return NULL;
}
}
else if (copy_pixels)
memcpy (data, pixdata->pixel_data, pixdata->rowstride * pixdata->height);
else
data = pixdata->pixel_data;
return gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB,
(pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA,
8, pixdata->width, pixdata->height, pixdata->rowstride,
copy_pixels ? (GdkPixbufDestroyNotify) g_free : NULL, data);
}
typedef struct {
/* config */
gboolean dump_stream;
gboolean dump_struct;
gboolean dump_macros;
gboolean dump_gtypes;
gboolean dump_rle_decoder;
const gchar *static_prefix;
const gchar *const_prefix;
/* runtime */
GString *gstring;
guint pos;
gboolean pad;
} CSourceData;
static inline void
save_uchar (CSourceData *cdata,
guint8 d)
{
GString *gstring = cdata->gstring;
if (cdata->pos > 70)
{
if (cdata->dump_struct || cdata->dump_stream)
{
g_string_append (gstring, "\"\n \"");
cdata->pos = 3;
cdata->pad = FALSE;
}
if (cdata->dump_macros)
{
g_string_append (gstring, "\" \\\n \"");
cdata->pos = 3;
cdata->pad = FALSE;
}
}
if (d < 33 || d > 126 || d == '?')
{
APPEND (gstring, "\\%o", d);
cdata->pos += 1 + 1 + (d > 7) + (d > 63);
cdata->pad = d < 64;
return;
}
if (d == '\\')
{
g_string_append (gstring, "\\\\");
cdata->pos += 2;
}
else if (d == '"')
{
g_string_append (gstring, "\\\"");
cdata->pos += 2;
}
else if (cdata->pad && d >= '0' && d <= '9')
{
g_string_append (gstring, "\"\"");
g_string_append_c (gstring, d);
cdata->pos += 3;
}
else
{
g_string_append_c (gstring, d);
cdata->pos += 1;
}
cdata->pad = FALSE;
return;
}
static inline void
save_rle_decoder (GString *gstring,
const gchar *macro_name,
const gchar *s_uint,
const gchar *s_uint_8,
guint n_ch)
{
APPEND (gstring, "#define %s_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \\\n",
macro_name);
APPEND (gstring, "{ %s __bpp; %s *__ip; const %s *__il, *__rd; \\\n", s_uint, s_uint_8, s_uint_8);
APPEND (gstring, " __bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \\\n");
APPEND (gstring, " __rd = (rle_data); if (__bpp > 3) { /* RGBA */ \\\n");
APPEND (gstring, " while (__ip < __il) { %s __l = *(__rd++); \\\n", s_uint);
APPEND (gstring, " if (__l & 128) { __l = __l - 128; \\\n");
APPEND (gstring, " do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \\\n");
APPEND (gstring, " } else { __l *= 4; memcpy (__ip, __rd, __l); \\\n");
APPEND (gstring, " __ip += __l; __rd += __l; } } \\\n");
APPEND (gstring, " } else { /* RGB */ \\\n");
APPEND (gstring, " while (__ip < __il) { %s __l = *(__rd++); \\\n", s_uint);
APPEND (gstring, " if (__l & 128) { __l = __l - 128; \\\n");
APPEND (gstring, " do { memcpy (__ip, __rd, 3); __ip += 3; } while (--__l); __rd += 3; \\\n");
APPEND (gstring, " } else { __l *= 3; memcpy (__ip, __rd, __l); \\\n");
APPEND (gstring, " __ip += __l; __rd += __l; } } \\\n");
APPEND (gstring, " } } while (0)\n");
}
/**
* gdk_pixdata_to_csource:
* @pixdata: a #GdkPixdata to convert to C source.
* @name: used for naming generated data structures or macros.
* @dump_type: a #GdkPixdataDumpType determining the kind of C
* source to be generated.
*
* Generates C source code suitable for compiling images directly
* into programs.
*
* GTK+ ships with a program called <command>gdk-pixbuf-csource</command>
* which offers a command line interface to this function.
*
* Returns: a newly-allocated string containing the C source form
* of @pixdata.
**/
GString*
gdk_pixdata_to_csource (GdkPixdata *pixdata,
const gchar *name,
GdkPixdataDumpType dump_type)
{
CSourceData cdata = { 0, };
gchar *s_uint_8, *s_uint_32, *s_uint, *s_char, *s_null;
guint bpp, width, height, rowstride;
gboolean rle_encoded;
gchar *macro_name;
guint8 *img_buffer, *img_buffer_end, *stream = NULL;
guint stream_length;
GString *gstring;
/* check args passing */
g_return_val_if_fail (pixdata != NULL, NULL);
g_return_val_if_fail (name != NULL, NULL);
/* check pixdata contents */
g_return_val_if_fail (pixdata->magic == GDK_PIXBUF_MAGIC_NUMBER, NULL);
g_return_val_if_fail (pixdata->width > 0, NULL);
g_return_val_if_fail (pixdata->height > 0, NULL);
g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
(pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
(pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
img_buffer = pixdata->pixel_data;
if (pixdata->length < 1)
img_buffer_end = img_buffer + pixdata_get_length (pixdata);
else
img_buffer_end = img_buffer + pixdata->length - GDK_PIXDATA_HEADER_LENGTH;
g_return_val_if_fail (img_buffer < img_buffer_end, NULL);
bpp = (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ? 3 : 4;
width = pixdata->width;
height = pixdata->height;
rowstride = pixdata->rowstride;
rle_encoded = (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_RLE) > 0;
macro_name = g_ascii_strup (name, -1);
cdata.dump_macros = (dump_type & GDK_PIXDATA_DUMP_MACROS) > 0;
cdata.dump_struct = (dump_type & GDK_PIXDATA_DUMP_PIXDATA_STRUCT) > 0;
cdata.dump_stream = !cdata.dump_macros && !cdata.dump_struct;
g_return_val_if_fail (cdata.dump_macros + cdata.dump_struct + cdata.dump_stream == 1, NULL);
cdata.dump_gtypes = (dump_type & GDK_PIXDATA_DUMP_CTYPES) == 0;
cdata.dump_rle_decoder = (dump_type & GDK_PIXDATA_DUMP_RLE_DECODER) > 0;
cdata.static_prefix = (dump_type & GDK_PIXDATA_DUMP_STATIC) ? "static " : "";
cdata.const_prefix = (dump_type & GDK_PIXDATA_DUMP_CONST) ? "const " : "";
gstring = g_string_new (NULL);
cdata.gstring = gstring;
if (!cdata.dump_macros && cdata.dump_gtypes)
{
s_uint_8 = "guint8 ";
s_uint_32 = "guint32";
s_uint = "guint ";
s_char = "gchar ";
s_null = "NULL";
}
else if (!cdata.dump_macros)
{
s_uint_8 = "unsigned char";
s_uint_32 = "unsigned int ";
s_uint = "unsigned int ";
s_char = "char ";
s_null = "(char*) 0";
}
else if (cdata.dump_macros && cdata.dump_gtypes)
{
s_uint_8 = "guint8";
s_uint_32 = "guint32";
s_uint = "guint";
s_char = "gchar";
s_null = "NULL";
}
else /* cdata.dump_macros && !cdata.dump_gtypes */
{
s_uint_8 = "unsigned char";
s_uint_32 = "unsigned int";
s_uint = "unsigned int";
s_char = "char";
s_null = "(char*) 0";
}
/* initial comment
*/
APPEND (gstring,
"/* GdkPixbuf %s C-Source image dump %s*/\n\n",
bpp > 3 ? "RGBA" : "RGB",
rle_encoded ? "1-byte-run-length-encoded " : "");
/* dump RLE decoder for structures
*/
if (cdata.dump_rle_decoder && cdata.dump_struct)
save_rle_decoder (gstring,
macro_name,
cdata.dump_gtypes ? "guint" : "unsigned int",
cdata.dump_gtypes ? "guint8" : "unsigned char",
bpp);
/* format & size blurbs
*/
if (cdata.dump_macros)
{
APPEND (gstring, "#define %s_ROWSTRIDE (%u)\n",
macro_name, rowstride);
APPEND (gstring, "#define %s_WIDTH (%u)\n",
macro_name, width);
APPEND (gstring, "#define %s_HEIGHT (%u)\n",
macro_name, height);
APPEND (gstring, "#define %s_BYTES_PER_PIXEL (%u) /* 3:RGB, 4:RGBA */\n",
macro_name, bpp);
}
if (cdata.dump_struct)
{
APPEND (gstring, "%s%sGdkPixdata %s = {\n",
cdata.static_prefix, cdata.const_prefix, name);
APPEND (gstring, " 0x%x, /* Pixbuf magic: 'GdkP' */\n",
GDK_PIXBUF_MAGIC_NUMBER);
APPEND (gstring, " %u + %lu, /* header length + pixel_data length */\n",
GDK_PIXDATA_HEADER_LENGTH,
rle_encoded ? (glong)(img_buffer_end - img_buffer) : (glong)rowstride * height);
APPEND (gstring, " 0x%x, /* pixdata_type */\n",
pixdata->pixdata_type);
APPEND (gstring, " %u, /* rowstride */\n",
rowstride);
APPEND (gstring, " %u, /* width */\n",
width);
APPEND (gstring, " %u, /* height */\n",
height);
APPEND (gstring, " /* pixel_data: */\n");
}
if (cdata.dump_stream)
{
guint pix_length = img_buffer_end - img_buffer;
stream = gdk_pixdata_serialize (pixdata, &stream_length);
img_buffer = stream;
img_buffer_end = stream + stream_length;
APPEND (gstring, "#ifdef __SUNPRO_C\n");
APPEND (gstring, "#pragma align 4 (%s)\n", name);
APPEND (gstring, "#endif\n");
APPEND (gstring, "#ifdef __GNUC__\n");
APPEND (gstring, "%s%s%s %s[] __attribute__ ((__aligned__ (4))) = \n",
cdata.static_prefix, cdata.const_prefix,
cdata.dump_gtypes ? "guint8" : "unsigned char",
name);
APPEND (gstring, "#else\n");
APPEND (gstring, "%s%s%s %s[] = \n",
cdata.static_prefix, cdata.const_prefix,
cdata.dump_gtypes ? "guint8" : "unsigned char",
name);
APPEND (gstring, "#endif\n");
APPEND (gstring, "{ \"\"\n /* Pixbuf magic (0x%x) */\n \"",
GDK_PIXBUF_MAGIC_NUMBER);
cdata.pos = 3;
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
APPEND (gstring, "\"\n /* length: header (%u) + pixel_data (%u) */\n \"",
GDK_PIXDATA_HEADER_LENGTH,
rle_encoded ? pix_length : rowstride * height);
cdata.pos = 3;
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
APPEND (gstring, "\"\n /* pixdata_type (0x%x) */\n \"",
pixdata->pixdata_type);
cdata.pos = 3;
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
APPEND (gstring, "\"\n /* rowstride (%u) */\n \"",
rowstride);
cdata.pos = 3;
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
APPEND (gstring, "\"\n /* width (%u) */\n \"", width);
cdata.pos = 3;
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
APPEND (gstring, "\"\n /* height (%u) */\n \"", height);
cdata.pos = 3;
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
APPEND (gstring, "\"\n /* pixel_data: */\n");
}
/* pixel_data intro
*/
if (cdata.dump_macros)
{
APPEND (gstring, "#define %s_%sPIXEL_DATA ((%s*) \\\n",
macro_name,
rle_encoded ? "RLE_" : "",
s_uint_8);
APPEND (gstring, " \"");
cdata.pos = 2;
}
if (cdata.dump_struct)
{
APPEND (gstring, " \"");
cdata.pos = 3;
}
if (cdata.dump_stream)
{
APPEND (gstring, " \"");
cdata.pos = 3;
}
/* pixel_data
*/
do
save_uchar (&cdata, *img_buffer++);
while (img_buffer < img_buffer_end);
/* pixel_data trailer
*/
if (cdata.dump_macros)
APPEND (gstring, "\")\n\n");
if (cdata.dump_struct)
APPEND (gstring, "\",\n};\n\n");
if (cdata.dump_stream)
APPEND (gstring, "\"};\n\n");
/* dump RLE decoder for macros
*/
if (cdata.dump_rle_decoder && cdata.dump_macros)
save_rle_decoder (gstring,
macro_name,
cdata.dump_gtypes ? "guint" : "unsigned int",
cdata.dump_gtypes ? "guint8" : "unsigned char",
bpp);
/* cleanup
*/
g_free (stream);
g_free (macro_name);
return gstring;
}
/**
* gdk_pixbuf_new_from_inline:
* @data_length: Length in bytes of the @data argument or -1 to
* disable length checks
* @data: Byte data containing a serialized #GdkPixdata structure
* @copy_pixels: Whether to copy the pixel data, or use direct pointers
* @data for the resulting pixbuf
* @error: #GError return location, may be %NULL to ignore errors
*
* Create a #GdkPixbuf from a flat representation that is suitable for
* storing as inline data in a program. This is useful if you want to
* ship a program with images, but don't want to depend on any
* external files.
*
* GTK+ ships with a program called <command>gdk-pixbuf-csource</command>
* which allows for conversion of #GdkPixbufs into such a inline representation.
* In almost all cases, you should pass the <option>--raw</option> flag to
* <command>gdk-pixbuf-csource</command>. A sample invocation would be:
*
* <informalexample><programlisting>
* gdk-pixbuf-csource --raw --name=myimage_inline myimage.png
* </programlisting></informalexample>
*
* For the typical case where the inline pixbuf is read-only static data,
* you don't need to copy the pixel data unless you intend to write to
* it, so you can pass %FALSE for @copy_pixels. (If you pass
* <option>--rle</option> to <command>gdk-pixbuf-csource</command>, a copy
* will be made even if @copy_pixels is %FALSE, so using this option is
* generally a bad idea.)
*
* If you create a pixbuf from const inline data compiled into your
* program, it's probably safe to ignore errors and disable length checks,
* since things will always succeed:
* <informalexample><programlisting>
* pixbuf = gdk_pixbuf_new_from_inline (-1, myimage_inline, FALSE, NULL);
* </programlisting></informalexample>
*
* For non-const inline data, you could get out of memory. For untrusted
* inline data located at runtime, you could have corrupt inline data in
* addition.
*
* Return value: A newly-created #GdkPixbuf structure with a reference,
* count of 1, or %NULL if an error occurred.
**/
GdkPixbuf*
gdk_pixbuf_new_from_inline (gint data_length,
const guint8 *data,
gboolean copy_pixels,
GError **error)
{
GdkPixdata pixdata;
if (data_length != -1)
g_return_val_if_fail (data_length > GDK_PIXDATA_HEADER_LENGTH, NULL);
g_return_val_if_fail (data != NULL, NULL);
if (!gdk_pixdata_deserialize (&pixdata, data_length, data, error))
return NULL;
return gdk_pixbuf_from_pixdata (&pixdata, copy_pixels, error);
}