]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/gdk-pixdata.c
Support for separately installed loaders. (#77486)
[~andy/gtk] / gdk-pixbuf / gdk-pixdata.c
1 /* GdkPixbuf library - GdkPixdata - functions for inlined pixbuf handling
2  * Copyright (C) 1999, 2001 Tim Janik
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include "gdk-pixdata.h"
20
21 #include "gdk-pixbuf-private.h"
22 #include <string.h>
23
24 #define APPEND g_string_append_printf
25
26 /* --- functions --- */
27 static guint
28 pixdata_get_length (const GdkPixdata *pixdata)
29 {
30   guint bpp, length;
31
32   if ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB)
33     bpp = 3;
34   else if ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA)
35     bpp = 4;
36   else
37     return 0;   /* invalid format */
38   switch (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK)
39     {
40       guint8 *rle_buffer;
41       guint max_length;
42     case GDK_PIXDATA_ENCODING_RAW:
43       length = pixdata->rowstride * pixdata->height;
44       break;
45     case GDK_PIXDATA_ENCODING_RLE:
46       /* need an RLE walk to determine size */
47       max_length = pixdata->rowstride * pixdata->height;
48       rle_buffer = pixdata->pixel_data;
49       length = 0;
50       while (length < max_length)
51         {
52           guint chunk_length = *(rle_buffer++);
53
54           if (chunk_length & 128)
55             {
56               chunk_length = chunk_length - 128;
57               if (!chunk_length)        /* RLE data corrupted */
58                 return 0;
59               length += chunk_length * bpp;
60               rle_buffer += bpp;
61             }
62           else
63             {
64               if (!chunk_length)        /* RLE data corrupted */
65                 return 0;
66               chunk_length *= bpp;
67               length += chunk_length;
68               rle_buffer += chunk_length;
69             }
70         }
71       length = rle_buffer - pixdata->pixel_data;
72       break;
73     default:
74       length = 0;
75       break;
76     }
77   return length;
78 }
79
80 /**
81  * gdk_pixdata_serialize:
82  * @pixdata: a valid #GdkPixdata structure to serialize.
83  * @stream_length_p: location to store the resulting stream length in.
84  *
85  * Serializes a #GdkPixdata structure into a byte stream.
86  * The byte stream consists of a straightforward writeout of the
87  * #GdkPixdata fields in network byte order, plus the @pixel_data
88  * bytes the structure points to.
89  *
90  * Return value: A newly-allocated string containing the serialized
91  * #GdkPixdata structure.
92  **/
93 guint8* /* free result */
94 gdk_pixdata_serialize (const GdkPixdata *pixdata,
95                        guint            *stream_length_p)
96 {
97   guint8 *stream, *s;
98   guint32 *istream;
99   guint length;
100
101   /* check args passing */
102   g_return_val_if_fail (pixdata != NULL, NULL);
103   g_return_val_if_fail (stream_length_p != NULL, NULL);
104   /* check pixdata contents */
105   g_return_val_if_fail (pixdata->magic == GDK_PIXBUF_MAGIC_NUMBER, NULL);
106   g_return_val_if_fail (pixdata->width > 0, NULL);
107   g_return_val_if_fail (pixdata->height > 0, NULL);
108   g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
109   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
110                         (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
111   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
112   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
113                         (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
114   g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
115
116   length = pixdata_get_length (pixdata);
117
118   /* check length field */
119   g_return_val_if_fail (length != 0, NULL);
120   
121   stream = g_malloc (GDK_PIXDATA_HEADER_LENGTH + length);
122   istream = (guint32*) stream;
123
124   /* store header */
125   *istream++ = g_htonl (GDK_PIXBUF_MAGIC_NUMBER);
126   *istream++ = g_htonl (GDK_PIXDATA_HEADER_LENGTH + length);
127   *istream++ = g_htonl (pixdata->pixdata_type);
128   *istream++ = g_htonl (pixdata->rowstride);
129   *istream++ = g_htonl (pixdata->width);
130   *istream++ = g_htonl (pixdata->height);
131
132   /* copy pixel data */
133   s = (guint8*) istream;
134   memcpy (s, pixdata->pixel_data, length);
135   s += length;
136
137   *stream_length_p = GDK_PIXDATA_HEADER_LENGTH + length;
138   g_assert (s - stream == *stream_length_p);    /* paranoid */
139
140   return stream;
141 }
142
143 #define return_header_corrupt(error)    { \
144   g_set_error (error, GDK_PIXBUF_ERROR, \
145                GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image header corrupt")); \
146   return FALSE; \
147 }
148 #define return_invalid_format(error)    { \
149   g_set_error (error, GDK_PIXBUF_ERROR, \
150                GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image format unknown")); \
151   return FALSE; \
152 }
153 #define return_pixel_corrupt(error)     { \
154   g_set_error (error, GDK_PIXBUF_ERROR, \
155                GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image pixel data corrupt")); \
156   return FALSE; \
157 }
158
159 static inline const guint8 *
160 get_uint32 (const guint8 *stream, guint *result)
161 {
162   *result = (stream[0] << 24) + (stream[1] << 16) + (stream[2] << 8) + stream[3];
163   return stream + 4;
164 }
165
166 /**
167  * gdk_pixdata_deserialize:
168  * @pixdata: a #GdkPixdata structure to be filled in.
169  * @stream_length: length of the stream used for deserialization.
170  * @stream: stream of bytes containing a serialized #GdkPixdata structure.
171  * @error: #GError location to indicate failures (maybe %NULL to ignore errors).
172  *
173  * Deserializes (reconstruct) a #GdkPixdata structure from a byte stream.
174  * The byte stream consists of a straightforward writeout of the
175  * #GdkPixdata fields in network byte order, plus the @pixel_data
176  * bytes the structure points to.
177  * The @pixdata contents are reconstructed byte by byte and are checked
178  * for validity. This function may fail with %GDK_PIXBUF_CORRUPT_IMAGE
179  * or %GDK_PIXBUF_ERROR_UNKNOWN_TYPE.
180  *
181  * Return value: Upon successful deserialization %TRUE is returned,
182  * %FALSE otherwise.
183  **/
184 gboolean
185 gdk_pixdata_deserialize (GdkPixdata   *pixdata,
186                          guint         stream_length,
187                          const guint8 *stream,
188                          GError      **error)
189 {
190   guint color_type, sample_width, encoding;
191
192   g_return_val_if_fail (pixdata != NULL, FALSE);
193   if (stream_length < GDK_PIXDATA_HEADER_LENGTH)
194     return_header_corrupt (error);
195   g_return_val_if_fail (stream != NULL, FALSE);
196
197
198   /* deserialize header */
199   stream = get_uint32 (stream, &pixdata->magic);
200   stream = get_uint32 (stream, &pixdata->length);
201   if (pixdata->magic != GDK_PIXBUF_MAGIC_NUMBER || pixdata->length < GDK_PIXDATA_HEADER_LENGTH)
202     return_header_corrupt (error);
203   stream = get_uint32 (stream, &pixdata->pixdata_type);
204   stream = get_uint32 (stream, &pixdata->rowstride);
205   stream = get_uint32 (stream, &pixdata->width);
206   stream = get_uint32 (stream, &pixdata->height);
207   if (pixdata->width < 1 || pixdata->height < 1 ||
208       pixdata->rowstride < pixdata->width)
209     return_header_corrupt (error);
210   color_type = pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK;
211   sample_width = pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK;
212   encoding = pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK;
213   if ((color_type != GDK_PIXDATA_COLOR_TYPE_RGB &&
214        color_type != GDK_PIXDATA_COLOR_TYPE_RGBA) ||
215       sample_width != GDK_PIXDATA_SAMPLE_WIDTH_8 ||
216       (encoding != GDK_PIXDATA_ENCODING_RAW &&
217        encoding != GDK_PIXDATA_ENCODING_RLE))
218     return_invalid_format (error);
219
220   /* deserialize pixel data */
221   if (stream_length < pixdata->length - GDK_PIXDATA_HEADER_LENGTH)
222     return_pixel_corrupt (error);
223   pixdata->pixel_data = (guint8 *)stream;
224
225   return TRUE;
226 }
227
228 static gboolean
229 diff2_rgb (guint8 *ip)
230 {
231   return ip[0] != ip[3] || ip[1] != ip[4] || ip[2] != ip[5];
232 }
233
234 static gboolean
235 diff2_rgba (guint8 *ip)
236 {
237   return ip[0] != ip[4] || ip[1] != ip[5] || ip[2] != ip[6] || ip[3] != ip[7];
238 }
239
240 static guint8*                  /* dest buffer bound */
241 rl_encode_rgbx (guint8 *bp,     /* dest buffer */
242                 guint8 *ip,     /* image pointer */
243                 guint8 *limit,  /* image upper bound */
244                 guint   n_ch)
245 {
246   gboolean (*diff2_pix) (guint8 *) = n_ch > 3 ? diff2_rgba : diff2_rgb;
247   guint8 *ilimit = limit - n_ch;
248
249   while (ip < limit)
250     {
251       g_assert (ip < ilimit); /* paranoid */
252
253       if (diff2_pix (ip))
254         {
255           guint8 *s_ip = ip;
256           guint l = 1;
257
258           ip += n_ch;
259           while (l < 127 && ip < ilimit && diff2_pix (ip))
260             { ip += n_ch; l += 1; }
261           if (ip == ilimit && l < 127)
262             { ip += n_ch; l += 1; }
263           *(bp++) = l;
264           memcpy (bp, s_ip, l * n_ch);
265           bp += l * n_ch;
266         }
267       else
268         {
269           guint l = 2;
270
271           ip += n_ch;
272           while (l < 127 && ip < ilimit && !diff2_pix (ip))
273             { ip += n_ch; l += 1; }
274           *(bp++) = l | 128;
275           memcpy (bp, ip, n_ch);
276           ip += n_ch;
277           bp += n_ch;
278         }
279       if (ip == ilimit)
280         {
281           *(bp++) = 1;
282           memcpy (bp, ip, n_ch);
283           ip += n_ch;
284           bp += n_ch;
285         }
286     }
287
288   return bp;
289 }
290
291 /**
292  * gdk_pixdata_from_pixbuf:
293  * @pixdata: a #GdkPixdata to fill.
294  * @pixbuf: the data to fill @pixdata with.
295  * @use_rle: whether to use run-length encoding for the pixel data.
296  *
297  * Converts a #GdkPixbuf to a #GdkPixdata. If @use_rle is %TRUE, the
298  * pixel data is run-length encoded into newly-allocated memory and a 
299  * pointer to that memory is returned. 
300  *
301  * Returns: If @ure_rle is %TRUE, a pointer to the newly-allocated memory 
302  *   for the run-length encoded pixel data, otherwise %NULL.
303  **/
304 gpointer
305 gdk_pixdata_from_pixbuf (GdkPixdata      *pixdata,
306                          const GdkPixbuf *pixbuf,
307                          gboolean         use_rle)
308 {
309   gpointer free_me = NULL;
310   guint height, rowstride, encoding, bpp, length;
311   guint8 *img_buffer;
312
313   g_return_val_if_fail (pixdata != NULL, NULL);
314   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
315   g_return_val_if_fail (pixbuf->bits_per_sample == 8, NULL);
316   g_return_val_if_fail ((pixbuf->n_channels == 3 && !pixbuf->has_alpha) ||
317                         (pixbuf->n_channels == 4 && pixbuf->has_alpha), NULL);
318   g_return_val_if_fail (pixbuf->rowstride >= pixbuf->width, NULL);
319
320   height = pixbuf->height;
321   rowstride = pixbuf->rowstride;
322   encoding = use_rle ? GDK_PIXDATA_ENCODING_RLE : GDK_PIXDATA_ENCODING_RAW;
323   bpp = pixbuf->has_alpha ? 4 : 3;
324
325   if (encoding == GDK_PIXDATA_ENCODING_RLE)
326     {
327       guint pad, n_bytes = rowstride * height;
328       guint8 *img_buffer_end, *data;
329
330       pad = rowstride;
331       pad = MAX (pad, 130 + n_bytes / 127);
332       data = g_new (guint8, pad + n_bytes);
333       free_me = data;
334       img_buffer = data;
335       img_buffer_end = rl_encode_rgbx (img_buffer,
336                                        pixbuf->pixels, pixbuf->pixels + n_bytes,
337                                        bpp);
338       length = img_buffer_end - img_buffer;
339     }
340   else
341     {
342       img_buffer = pixbuf->pixels;
343       length = rowstride * height;
344     }
345
346   pixdata->magic = GDK_PIXBUF_MAGIC_NUMBER;
347   pixdata->length = GDK_PIXDATA_HEADER_LENGTH + length;
348   pixdata->pixdata_type = pixbuf->has_alpha ? GDK_PIXDATA_COLOR_TYPE_RGBA : GDK_PIXDATA_COLOR_TYPE_RGB;
349   pixdata->pixdata_type |= GDK_PIXDATA_SAMPLE_WIDTH_8;
350   pixdata->pixdata_type |= encoding;
351   pixdata->rowstride = rowstride;
352   pixdata->width = pixbuf->width;
353   pixdata->height = height;
354   pixdata->pixel_data = img_buffer;
355
356   return free_me;
357 }
358
359 /**
360  * gdk_pixbuf_from_pixdata:
361  * @pixdata: a #GdkPixdata to convert into a #GdkPixbuf.
362  * @copy_pixels: whether to copy raw pixel data; run-length encoded
363  *     pixel data is always copied.
364  * @error: location to store possible errors.
365  * 
366  * Converts a #GdkPixdata to a #GdkPixbuf. If @copy_pixels is %TRUE or
367  * if the pixel data is run-length-encoded, the pixel data is copied into
368  * newly-allocated memory; otherwise it is reused.
369  *
370  * Returns: a new #GdkPixbuf.
371  **/
372 GdkPixbuf*
373 gdk_pixbuf_from_pixdata (const GdkPixdata *pixdata,
374                          gboolean          copy_pixels,
375                          GError          **error)
376 {
377   guint encoding, bpp;
378   guint8 *data = NULL;
379
380   g_return_val_if_fail (pixdata != NULL, NULL);
381   g_return_val_if_fail (pixdata->width > 0, NULL);
382   g_return_val_if_fail (pixdata->height > 0, NULL);
383   g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
384   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
385                         (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
386   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
387   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
388                         (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
389   g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
390
391   bpp = (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ? 3 : 4;
392   encoding = pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK;
393   if (encoding == GDK_PIXDATA_ENCODING_RLE)
394     copy_pixels = TRUE;
395   if (copy_pixels)
396     {
397       data = g_try_malloc (pixdata->rowstride * pixdata->height);
398       if (!data)
399         {
400           g_set_error (error, GDK_PIXBUF_ERROR,
401                        GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
402                        _("failed to allocate image buffer of %u bytes"),
403                        pixdata->rowstride * pixdata->height);
404           return NULL;
405         }
406     }
407   if (encoding == GDK_PIXDATA_ENCODING_RLE)
408     {
409       const guint8 *rle_buffer = pixdata->pixel_data;
410       guint8 *image_buffer = data;
411       guint8 *image_limit = data + pixdata->rowstride * pixdata->height;
412       gboolean check_overrun = FALSE;
413
414       while (image_buffer < image_limit)
415         {
416           guint length = *(rle_buffer++);
417
418           if (length & 128)
419             {
420               length = length - 128;
421               check_overrun = image_buffer + length * bpp > image_limit;
422               if (check_overrun)
423                 length = (image_limit - image_buffer) / bpp;
424               if (bpp < 4)      /* RGB */
425                 do
426                   {
427                     memcpy (image_buffer, rle_buffer, 3);
428                     image_buffer += 3;
429                   }
430                 while (--length);
431               else              /* RGBA */
432                 do
433                   {
434                     memcpy (image_buffer, rle_buffer, 4);
435                     image_buffer += 4;
436                   }
437                 while (--length);
438               rle_buffer += bpp;
439             }
440           else
441             {
442               length *= bpp;
443               check_overrun = image_buffer + length > image_limit;
444               if (check_overrun)
445                 length = image_limit - image_buffer;
446               memcpy (image_buffer, rle_buffer, length);
447               image_buffer += length;
448               rle_buffer += length;
449             }
450         }
451       if (check_overrun)
452         {
453           g_free (data);
454           g_set_error (error, GDK_PIXBUF_ERROR,
455                        GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
456                        _("Image pixel data corrupt"));
457           return NULL;
458         }
459     }
460   else if (copy_pixels)
461     memcpy (data, pixdata->pixel_data, pixdata->rowstride * pixdata->height);
462   else
463     data = pixdata->pixel_data;
464
465   return gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB,
466                                    (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA,
467                                    8, pixdata->width, pixdata->height, pixdata->rowstride,
468                                    copy_pixels ? (GdkPixbufDestroyNotify) g_free : NULL, data);
469 }
470
471 typedef struct {
472   /* config */
473   gboolean     dump_stream;
474   gboolean     dump_struct;
475   gboolean     dump_macros;
476   gboolean     dump_gtypes;
477   gboolean     dump_rle_decoder;
478   const gchar *static_prefix;
479   const gchar *const_prefix;
480   /* runtime */
481   GString *gstring;
482   guint    pos;
483   gboolean pad;
484 } CSourceData;
485
486 static inline void
487 save_uchar (CSourceData *cdata,
488             guint8       d)
489 {
490   GString *gstring = cdata->gstring;
491
492   if (cdata->pos > 70)
493     {
494       if (cdata->dump_struct || cdata->dump_stream)
495         {
496           g_string_append (gstring, "\"\n  \"");
497           cdata->pos = 3;
498           cdata->pad = FALSE;
499         }
500       if (cdata->dump_macros)
501         {
502           g_string_append (gstring, "\" \\\n  \"");
503           cdata->pos = 3;
504           cdata->pad = FALSE;
505         }
506     }
507   if (d < 33 || d > 126 || d == '?')
508     {
509       APPEND (gstring, "\\%o", d);
510       cdata->pos += 1 + 1 + (d > 7) + (d > 63);
511       cdata->pad = d < 64;
512       return;
513     }
514   if (d == '\\')
515     {
516       g_string_append (gstring, "\\\\");
517       cdata->pos += 2;
518     }
519   else if (d == '"')
520     {
521       g_string_append (gstring, "\\\"");
522       cdata->pos += 2;
523     }
524   else if (cdata->pad && d >= '0' && d <= '9')
525     {
526       g_string_append (gstring, "\"\"");
527       g_string_append_c (gstring, d);
528       cdata->pos += 3;
529     }
530   else
531     {
532       g_string_append_c (gstring, d);
533       cdata->pos += 1;
534     }
535   cdata->pad = FALSE;
536   return;
537 }
538
539 static inline void
540 save_rle_decoder (GString     *gstring,
541                   const gchar *macro_name,
542                   const gchar *s_uint,
543                   const gchar *s_uint_8,
544                   guint        n_ch)
545 {
546   APPEND (gstring, "#define %s_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \\\n",
547           macro_name);
548   APPEND (gstring, "{ %s __bpp; %s *__ip; const %s *__il, *__rd; \\\n", s_uint, s_uint_8, s_uint_8);
549   APPEND (gstring, "  __bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \\\n");
550   
551   APPEND (gstring, "  __rd = (rle_data); if (__bpp > 3) { /* RGBA */ \\\n");
552   
553   APPEND (gstring, "    while (__ip < __il) { %s __l = *(__rd++); \\\n", s_uint);
554   APPEND (gstring, "      if (__l & 128) { __l = __l - 128; \\\n");
555   APPEND (gstring, "        do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \\\n");
556   APPEND (gstring, "      } else { __l *= 4; memcpy (__ip, __rd, __l); \\\n");
557   APPEND (gstring, "               __ip += __l; __rd += __l; } } \\\n");
558   
559   APPEND (gstring, "  } else { /* RGB */ \\\n");
560   
561   APPEND (gstring, "    while (__ip < __il) { %s __l = *(__rd++); \\\n", s_uint);
562   APPEND (gstring, "      if (__l & 128) { __l = __l - 128; \\\n");
563   APPEND (gstring, "        do { memcpy (__ip, __rd, 3); __ip += 3; } while (--__l); __rd += 3; \\\n");
564   APPEND (gstring, "      } else { __l *= 3; memcpy (__ip, __rd, __l); \\\n");
565   APPEND (gstring, "               __ip += __l; __rd += __l; } } \\\n");
566   
567   APPEND (gstring, "  } } while (0)\n");
568 }
569
570 /**
571  * gdk_pixdata_to_csource:
572  * @pixdata: a #GdkPixdata to convert to C source.
573  * @name: used for naming generated data structures or macros.
574  * @dump_type: a #GdkPixdataDumpType determining the kind of C
575  *   source to be generated.
576  *
577  * Generates C source code suitable for compiling images directly 
578  * into programs. 
579  *
580  * GTK+ ships with a program called <command>gdk-pixbuf-csource</command> 
581  * which offers a cmdline interface to this functions.
582  *
583  * Returns: a newly-allocated string containing the C source form
584  *   of @pixdata.
585  **/
586 GString*
587 gdk_pixdata_to_csource (GdkPixdata        *pixdata,
588                         const gchar       *name,
589                         GdkPixdataDumpType dump_type)
590 {
591   CSourceData cdata = { 0, };
592   gchar *s_uint_8, *s_uint_32, *s_uint, *s_char, *s_null;
593   guint bpp, width, height, rowstride;
594   gboolean rle_encoded;
595   gchar *macro_name;
596   guint8 *img_buffer, *img_buffer_end, *stream = NULL;
597   guint stream_length;
598   GString *gstring;
599   
600   /* check args passing */
601   g_return_val_if_fail (pixdata != NULL, NULL);
602   g_return_val_if_fail (name != NULL, NULL);
603   /* check pixdata contents */
604   g_return_val_if_fail (pixdata->magic == GDK_PIXBUF_MAGIC_NUMBER, NULL);
605   g_return_val_if_fail (pixdata->width > 0, NULL);
606   g_return_val_if_fail (pixdata->height > 0, NULL);
607   g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
608   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
609                         (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
610   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
611   g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
612                         (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
613   g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
614
615   img_buffer = pixdata->pixel_data;
616   if (pixdata->length < 1)
617     img_buffer_end = img_buffer + pixdata_get_length (pixdata);
618   else
619     img_buffer_end = img_buffer + pixdata->length - GDK_PIXDATA_HEADER_LENGTH;
620   g_return_val_if_fail (img_buffer < img_buffer_end, NULL);
621
622   bpp = (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ? 3 : 4;
623   width = pixdata->width;
624   height = pixdata->height;
625   rowstride = pixdata->rowstride;
626   rle_encoded = (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_RLE) > 0;
627   macro_name = g_ascii_strup (name, -1);
628
629   cdata.dump_macros = (dump_type & GDK_PIXDATA_DUMP_MACROS) > 0;
630   cdata.dump_struct = (dump_type & GDK_PIXDATA_DUMP_PIXDATA_STRUCT) > 0;
631   cdata.dump_stream = !cdata.dump_macros && !cdata.dump_struct;
632   g_return_val_if_fail (cdata.dump_macros + cdata.dump_struct + cdata.dump_stream == 1, NULL);
633
634   cdata.dump_gtypes = (dump_type & GDK_PIXDATA_DUMP_CTYPES) == 0;
635   cdata.dump_rle_decoder = (dump_type & GDK_PIXDATA_DUMP_RLE_DECODER) > 0;
636   cdata.static_prefix = (dump_type & GDK_PIXDATA_DUMP_STATIC) ? "static " : "";
637   cdata.const_prefix = (dump_type & GDK_PIXDATA_DUMP_CONST) ? "const " : "";
638   gstring = g_string_new ("");
639   cdata.gstring = gstring;
640
641   if (!cdata.dump_macros && cdata.dump_gtypes)
642     {
643       s_uint_8 =  "guint8 ";
644       s_uint_32 = "guint32";
645       s_uint =    "guint  ";
646       s_char =    "gchar  ";
647       s_null =    "NULL";
648     }
649   else if (!cdata.dump_macros)
650     {
651       s_uint_8 =  "unsigned char";
652       s_uint_32 = "unsigned int ";
653       s_uint =    "unsigned int ";
654       s_char =    "char         ";
655       s_null =    "(char*) 0";
656     }
657   else if (cdata.dump_macros && cdata.dump_gtypes)
658     {
659       s_uint_8 =  "guint8";
660       s_uint_32 = "guint32";
661       s_uint  =   "guint";
662       s_char =    "gchar";
663       s_null =    "NULL";
664     }
665   else /* cdata.dump_macros && !cdata.dump_gtypes */
666     {
667       s_uint_8 =  "unsigned char";
668       s_uint_32 = "unsigned int";
669       s_uint =    "unsigned int";
670       s_char =    "char";
671       s_null =    "(char*) 0";
672     }
673
674   /* initial comment
675    */
676   APPEND (gstring,
677           "/* GdkPixbuf %s C-Source image dump %s*/\n\n",
678           bpp > 3 ? "RGBA" : "RGB",
679           rle_encoded ? "1-byte-run-length-encoded " : "");
680   
681   /* dump RLE decoder for structures
682    */
683   if (cdata.dump_rle_decoder && cdata.dump_struct)
684     save_rle_decoder (gstring,
685                       macro_name,
686                       cdata.dump_gtypes ? "guint" : "unsigned int",
687                       cdata.dump_gtypes ? "guint8" : "unsigned char",
688                       bpp);
689
690   /* format & size blurbs
691    */
692   if (cdata.dump_macros)
693     {
694       APPEND (gstring, "#define %s_ROWSTRIDE (%u)\n",
695               macro_name, rowstride);
696       APPEND (gstring, "#define %s_WIDTH (%u)\n",
697               macro_name, width);
698       APPEND (gstring, "#define %s_HEIGHT (%u)\n",
699               macro_name, height);
700       APPEND (gstring, "#define %s_BYTES_PER_PIXEL (%u) /* 3:RGB, 4:RGBA */\n",
701               macro_name, bpp);
702     }
703   if (cdata.dump_struct)
704     {
705       APPEND (gstring, "%s%sGdkPixdata %s = {\n",
706               cdata.static_prefix, cdata.const_prefix, name);
707       APPEND (gstring, "  0x%x, /* Pixbuf magic: 'GdkP' */\n",
708               GDK_PIXBUF_MAGIC_NUMBER);
709       APPEND (gstring, "  %u + %lu, /* header length + pixel_data length */\n",
710               GDK_PIXDATA_HEADER_LENGTH,
711               rle_encoded ? (glong)(img_buffer_end - img_buffer) : (glong)rowstride * height);
712       APPEND (gstring, "  0x%x, /* pixdata_type */\n",
713               pixdata->pixdata_type);
714       APPEND (gstring, "  %u, /* rowstride */\n",
715               rowstride);
716       APPEND (gstring, "  %u, /* width */\n",
717               width);
718       APPEND (gstring, "  %u, /* height */\n",
719               height);
720       APPEND (gstring, "  /* pixel_data: */\n");
721     }
722   if (cdata.dump_stream)
723     {
724       guint pix_length = img_buffer_end - img_buffer;
725       
726       stream = gdk_pixdata_serialize (pixdata, &stream_length);
727       img_buffer = stream;
728       img_buffer_end = stream + stream_length;
729
730       APPEND (gstring, "%s%s%s %s[] = \n",
731               cdata.static_prefix, cdata.const_prefix,
732               cdata.dump_gtypes ? "guint8" : "unsigned char",
733               name);
734       APPEND (gstring, "{ \"\"\n  /* Pixbuf magic (0x%x) */\n  \"",
735               GDK_PIXBUF_MAGIC_NUMBER);
736       cdata.pos = 3;
737       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
738       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
739       APPEND (gstring, "\"\n  /* length: header (%u) + pixel_data (%u) */\n  \"",
740               GDK_PIXDATA_HEADER_LENGTH,
741               rle_encoded ? pix_length : rowstride * height);
742       cdata.pos = 3;
743       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
744       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
745       APPEND (gstring, "\"\n  /* pixdata_type (0x%x) */\n  \"",
746               pixdata->pixdata_type);
747       cdata.pos = 3;
748       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
749       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
750       APPEND (gstring, "\"\n  /* rowstride (%u) */\n  \"",
751               rowstride);
752       cdata.pos = 3;
753       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
754       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
755       APPEND (gstring, "\"\n  /* width (%u) */\n  \"", width);
756       cdata.pos = 3;
757       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
758       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
759       APPEND (gstring, "\"\n  /* height (%u) */\n  \"", height);
760       cdata.pos = 3;
761       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
762       save_uchar (&cdata, *img_buffer++); save_uchar (&cdata, *img_buffer++);
763       APPEND (gstring, "\"\n  /* pixel_data: */\n");
764     }
765
766   /* pixel_data intro
767    */
768   if (cdata.dump_macros)
769     {
770       APPEND (gstring, "#define %s_%sPIXEL_DATA ((%s*) \\\n",
771               macro_name,
772               rle_encoded ? "RLE_" : "",
773               s_uint_8);
774       APPEND (gstring, "  \"");
775       cdata.pos = 2;
776     }
777   if (cdata.dump_struct)
778     {
779       APPEND (gstring, "  \"");
780       cdata.pos = 3;
781     }
782   if (cdata.dump_stream)
783     {
784       APPEND (gstring, "  \"");
785       cdata.pos = 3;
786     }
787     
788   /* pixel_data
789    */
790   do
791     save_uchar (&cdata, *img_buffer++);
792   while (img_buffer < img_buffer_end);
793
794   /* pixel_data trailer
795    */
796   if (cdata.dump_macros)
797     APPEND (gstring, "\")\n\n");
798   if (cdata.dump_struct)
799     APPEND (gstring, "\",\n};\n\n");
800   if (cdata.dump_stream)
801     APPEND (gstring, "\"};\n\n");
802
803   /* dump RLE decoder for macros
804    */
805   if (cdata.dump_rle_decoder && cdata.dump_macros)
806     save_rle_decoder (gstring,
807                       macro_name,
808                       cdata.dump_gtypes ? "guint" : "unsigned int",
809                       cdata.dump_gtypes ? "guint8" : "unsigned char",
810                       bpp);
811
812   /* cleanup
813    */
814   g_free (stream);
815   g_free (macro_name);
816     
817   return gstring;
818 }
819
820 /**
821  * gdk_pixbuf_new_from_inline:
822  * @data_length: Length in bytes of the @data argument
823  * @data: Byte data containing a serialized #GdkPixdata structure
824  * @copy_pixels: Whether to copy the pixel data, or use direct pointers
825  *               @data for the resulting pixbuf
826  * @error: #GError return location, may be %NULL to ignore errors
827  *
828  * Create a #GdkPixbuf from a flat representation that is suitable for
829  * storing as inline data in a program. This is useful if you want to
830  * ship a program with images, but don't want to depend on any
831  * external files.
832  *
833  * GTK+ ships with a program called <command>gdk-pixbuf-csource</command> 
834  * which allows for conversion of #GdkPixbufs into such a inline representation.
835  * In almost all cases, you should pass the <option>--raw</option> flag to
836  * <command>gdk-pixbuf-csource</command>. A sample invocation would be:
837  *
838  * <informalexample><programlisting>
839  *  gdk-pixbuf-csource --raw --name=myimage_inline myimage.png
840  * </programlisting></informalexample>
841  * 
842  * For the typical case where the inline pixbuf is read-only static data,
843  * you don't need to copy the pixel data unless you intend to write to
844  * it, so you can pass %FALSE for @copy_pixels.  (If you pass 
845  * <option>--rle</option> to <command>gdk-pixbuf-csource</command>, a copy 
846  * will be made even if @copy_pixels is %FALSE, so using this option is 
847  * generally a bad idea.)
848  *
849  * If you create a pixbuf from const inline data compiled into your
850  * program, it's probably safe to ignore errors, since things will
851  * always succeed.  For non-const inline data, you could get out of
852  * memory. For untrusted inline data located at runtime, you could
853  * have corrupt inline data in addition.
854  *
855  * Return value: A newly-created #GdkPixbuf structure with a reference,
856  *   count of 1, or %NULL if error is set.
857  **/
858 GdkPixbuf*
859 gdk_pixbuf_new_from_inline (gint          data_length,
860                             const guint8 *data,
861                             gboolean      copy_pixels,
862                             GError      **error)
863 {
864   GdkPixdata pixdata;
865
866   if (data_length != -1)
867     g_return_val_if_fail (data_length > GDK_PIXDATA_HEADER_LENGTH, NULL);
868   g_return_val_if_fail (data != NULL, NULL);
869
870   if (!gdk_pixdata_deserialize (&pixdata, data_length, data, error))
871     return NULL;
872
873   return gdk_pixbuf_from_pixdata (&pixdata, copy_pixels, error);
874 }