/* 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 "gdk-pixdata.h" #include "gdk-pixbuf-private.h" #include "gdk-pixbuf-i18n.h" #include #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, &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; } /** * 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; 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, pixbuf->pixels, pixbuf->pixels + n_bytes, bpp); length = img_buffer_end - img_buffer; } 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, _("failed to allocate image buffer of %u bytes"), 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) { 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 gdk-pixbuf-csource which offers * a cmdline interface to this functions. * * 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; 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_strdup (name); g_strup (macro_name); 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 (""); 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 + %u, /* header length + pixel_data length */\n", GDK_PIXDATA_HEADER_LENGTH, rle_encoded ? img_buffer_end - img_buffer : 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_end = stream + stream_length; APPEND (gstring, "%s%s%s %s[] = \n", cdata.static_prefix, cdata.const_prefix, cdata.dump_gtypes ? "guint8" : "unsigned char", name); APPEND (gstring, "( \"\"\n /* Pixbuf magic (0x%x) */\n \"", GDK_PIXBUF_MAGIC_NUMBER); cdata.pos = 3; save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); 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, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); APPEND (gstring, "\"\n /* pixdata_type (0x%x) */\n \"", pixdata->pixdata_type); cdata.pos = 3; save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); APPEND (gstring, "\"\n /* rowstride (%u) */\n \"", rowstride); cdata.pos = 3; save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); APPEND (gstring, "\"\n /* width (%u) */\n \"", width); cdata.pos = 3; save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); APPEND (gstring, "\"\n /* height (%u) */\n \"", height); cdata.pos = 3; save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); save_uchar (&cdata, *stream++); APPEND (gstring, "\"\n /* pixel_data: */\n"); img_buffer = stream; } /* 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 (macro_name); return gstring; } /** * gdk_pixbuf_new_from_inline: * @data_length: Length in bytes of the @data argument * @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 gdk-pixbuf-csource which allows * for conversion of #GdkPixbufs into such a inline representation. * In almost all cases, you should pass the --raw flag to * gdk-pixbuf-csource. A sample invocation would be: * * gdk-pixbuf-csource --raw --name=myimage_inline myimage.png * * 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 --rle to * gdk-pixbuf-csource, 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, since things will * always succeed. 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 error is set. **/ 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); }