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, see <http://www.gnu.org/licenses/>.
16 */
17#include "config.h"
18
19#include "gdk-pixbuf-private.h"
20#include "gdk-pixdata.h"
21#include <string.h>
22
23G_GNUC_BEGIN_IGNORE_DEPRECATIONS
24
25/**
26 * GdkPixdata:
27 * @magic: magic number. A valid `GdkPixdata` structure must have
28 * `GDK_PIXBUF_MAGIC_NUMBER` here
29 * @length: less than 1 to disable length checks, otherwise
30 * `GDK_PIXDATA_HEADER_LENGTH` plus the length of `pixel_data`
31 * @pixdata_type: information about colorspace, sample width and
32 * encoding, in a `GdkPixdataType`
33 * @rowstride: Distance in bytes between rows
34 * @width: Width of the image in pixels
35 * @height: Height of the image in pixels
36 * @pixel_data: (array) (element-type guint8): `width` x `height`
37 * pixels, encoded according to `pixdata_type` and `rowstride`
38 *
39 * A pixel buffer suitable for serialization and streaming.
40 *
41 * Using `GdkPixdata`, images can be compiled into an application,
42 * making it unnecessary to refer to external image files at runtime.
43 *
44 * `GdkPixbuf` includes a utility named `gdk-pixbuf-csource`, which
45 * can be used to convert image files into `GdkPixdata` structures suitable
46 * for inclusion in C sources. To convert the `GdkPixdata` structures back
47 * into a `GdkPixbuf`, use `gdk_pixbuf_from_pixdata()`.
48 *
49 * Deprecated: 2.32: `GdkPixdata` should not be used any more. `GResource`
50 * should be used to save the original compressed images inside the
51 * program's binary
52 */
53
54#define APPEND g_string_append_printf
55
56/* --- functions --- */
57static guint
58pixdata_get_length (const GdkPixdata *pixdata)
59{
60 guint bpp, length;
61
62 if ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB)
63 bpp = 3;
64 else if ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA)
65 bpp = 4;
66 else
67 return 0; /* invalid format */
68 switch (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK)
69 {
70 guint8 *rle_buffer;
71 guint max_length;
72 case GDK_PIXDATA_ENCODING_RAW:
73 length = pixdata->rowstride * pixdata->height;
74 break;
75 case GDK_PIXDATA_ENCODING_RLE:
76 /* need an RLE walk to determine size */
77 max_length = pixdata->rowstride * pixdata->height;
78 rle_buffer = pixdata->pixel_data;
79 length = 0;
80 while (length < max_length)
81 {
82 guint chunk_length = *(rle_buffer++);
83
84 if (chunk_length & 128)
85 {
86 chunk_length = chunk_length - 128;
87 if (!chunk_length) /* RLE data corrupted */
88 return 0;
89 length += chunk_length * bpp;
90 rle_buffer += bpp;
91 }
92 else
93 {
94 if (!chunk_length) /* RLE data corrupted */
95 return 0;
96 chunk_length *= bpp;
97 length += chunk_length;
98 rle_buffer += chunk_length;
99 }
100 }
101 length = rle_buffer - pixdata->pixel_data;
102 break;
103 default:
104 length = 0;
105 break;
106 }
107 return length;
108}
109
110/**
111 * gdk_pixdata_serialize:
112 * @pixdata: a valid #GdkPixdata structure to serialize.
113 * @stream_length_p: location to store the resulting stream length in.
114 *
115 * Serializes a #GdkPixdata structure into a byte stream.
116 * The byte stream consists of a straightforward writeout of the
117 * #GdkPixdata fields in network byte order, plus the @pixel_data
118 * bytes the structure points to.
119 *
120 * Return value: (array length=stream_length_p) (transfer full): A
121 * newly-allocated string containing the serialized #GdkPixdata
122 * structure.
123 *
124 * Deprecated: 2.32: Use #GResource instead.
125 **/
126guint8* /* free result */
127gdk_pixdata_serialize (const GdkPixdata *pixdata,
128 guint *stream_length_p)
129{
130 guint8 *stream, *s;
131 guint32 *istream;
132 guint length;
133
134 /* check args passing */
135 g_return_val_if_fail (pixdata != NULL, NULL);
136 g_return_val_if_fail (stream_length_p != NULL, NULL);
137 /* check pixdata contents */
138 g_return_val_if_fail (pixdata->magic == GDK_PIXBUF_MAGIC_NUMBER, NULL);
139 g_return_val_if_fail (pixdata->width > 0, NULL);
140 g_return_val_if_fail (pixdata->height > 0, NULL);
141 g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
142 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
143 (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
144 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
145 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
146 (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
147 g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
148
149 length = pixdata_get_length (pixdata);
150
151 /* check length field */
152 g_return_val_if_fail (length != 0, NULL);
153
154 stream = g_malloc (GDK_PIXDATA_HEADER_LENGTH + length);
155 istream = (guint32*) stream;
156
157 /* store header */
158 *istream++ = g_htonl (GDK_PIXBUF_MAGIC_NUMBER);
159 *istream++ = g_htonl (GDK_PIXDATA_HEADER_LENGTH + length);
160 *istream++ = g_htonl (pixdata->pixdata_type);
161 *istream++ = g_htonl (pixdata->rowstride);
162 *istream++ = g_htonl (pixdata->width);
163 *istream++ = g_htonl (pixdata->height);
164
165 /* copy pixel data */
166 s = (guint8*) istream;
167 memcpy (dest: s, src: pixdata->pixel_data, n: length);
168 s += length;
169
170 *stream_length_p = GDK_PIXDATA_HEADER_LENGTH + length;
171 g_assert (s - stream == *stream_length_p); /* paranoid */
172
173 return stream;
174}
175
176#define return_header_corrupt(error) { \
177 g_set_error_literal (error, GDK_PIXBUF_ERROR, \
178 GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image header corrupt")); \
179 return FALSE; \
180}
181#define return_invalid_format(error) { \
182 g_set_error_literal (error, GDK_PIXBUF_ERROR, \
183 GDK_PIXBUF_ERROR_UNKNOWN_TYPE, _("Image format unknown")); \
184 return FALSE; \
185}
186#define return_pixel_corrupt(error) { \
187 g_set_error_literal (error, GDK_PIXBUF_ERROR, \
188 GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Image pixel data corrupt")); \
189 return FALSE; \
190}
191
192static inline const guint8 *
193get_uint32 (const guint8 *stream, guint *result)
194{
195 *result = (stream[0] << 24) + (stream[1] << 16) + (stream[2] << 8) + stream[3];
196 return stream + 4;
197}
198
199/**
200 * gdk_pixdata_deserialize:
201 * @pixdata: a #GdkPixdata structure to be filled in.
202 * @stream_length: length of the stream used for deserialization.
203 * @stream: (array length=stream_length): stream of bytes containing a
204 * serialized #GdkPixdata structure.
205 * @error: #GError location to indicate failures (maybe `NULL` to ignore errors).
206 *
207 * Deserializes (reconstruct) a #GdkPixdata structure from a byte stream.
208 *
209 * The byte stream consists of a straightforward writeout of the
210 * `GdkPixdata` fields in network byte order, plus the `pixel_data`
211 * bytes the structure points to.
212 *
213 * The `pixdata` contents are reconstructed byte by byte and are checked
214 * for validity.
215 *
216 * This function may fail with `GDK_PIXBUF_ERROR_CORRUPT_IMAGE`
217 * or `GDK_PIXBUF_ERROR_UNKNOWN_TYPE`.
218 *
219 * Return value: Upon successful deserialization `TRUE` is returned,
220 * `FALSE` otherwise.
221 *
222 * Deprecated: 2.32: Use `GResource` instead.
223 **/
224gboolean
225gdk_pixdata_deserialize (GdkPixdata *pixdata,
226 guint stream_length,
227 const guint8 *stream,
228 GError **error)
229{
230 guint color_type, sample_width, encoding;
231
232 g_return_val_if_fail (pixdata != NULL, FALSE);
233 if (stream_length < GDK_PIXDATA_HEADER_LENGTH)
234 return_header_corrupt (error);
235 g_return_val_if_fail (stream != NULL, FALSE);
236
237
238 /* deserialize header */
239 stream = get_uint32 (stream, result: &pixdata->magic);
240 stream = get_uint32 (stream, result: (guint32 *)&pixdata->length);
241 if (pixdata->magic != GDK_PIXBUF_MAGIC_NUMBER || pixdata->length < GDK_PIXDATA_HEADER_LENGTH)
242 return_header_corrupt (error);
243 stream = get_uint32 (stream, result: &pixdata->pixdata_type);
244 stream = get_uint32 (stream, result: &pixdata->rowstride);
245 stream = get_uint32 (stream, result: &pixdata->width);
246 stream = get_uint32 (stream, result: &pixdata->height);
247 if (pixdata->width < 1 || pixdata->height < 1 ||
248 pixdata->rowstride < pixdata->width)
249 return_header_corrupt (error);
250 color_type = pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK;
251 sample_width = pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK;
252 encoding = pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK;
253 if ((color_type != GDK_PIXDATA_COLOR_TYPE_RGB &&
254 color_type != GDK_PIXDATA_COLOR_TYPE_RGBA) ||
255 sample_width != GDK_PIXDATA_SAMPLE_WIDTH_8 ||
256 (encoding != GDK_PIXDATA_ENCODING_RAW &&
257 encoding != GDK_PIXDATA_ENCODING_RLE))
258 return_invalid_format (error);
259
260 /* deserialize pixel data */
261 if (stream_length < pixdata->length - GDK_PIXDATA_HEADER_LENGTH)
262 return_pixel_corrupt (error);
263 pixdata->pixel_data = (guint8 *)stream;
264
265 return TRUE;
266}
267
268static gboolean
269diff2_rgb (const guint8 *ip)
270{
271 return ip[0] != ip[3] || ip[1] != ip[4] || ip[2] != ip[5];
272}
273
274static gboolean
275diff2_rgba (const guint8 *ip)
276{
277 return ip[0] != ip[4] || ip[1] != ip[5] || ip[2] != ip[6] || ip[3] != ip[7];
278}
279
280static guint8 * /* dest buffer bound */
281rl_encode_rgbx (guint8 *bp, /* dest buffer */
282 const guint8 *ip, /* image pointer */
283 const guint8 *limit, /* image upper bound */
284 guint n_ch)
285{
286 gboolean (*diff2_pix) (const guint8 *) = n_ch > 3 ? diff2_rgba : diff2_rgb;
287 const guint8 *ilimit = limit - n_ch;
288
289 while (ip < limit)
290 {
291 g_assert (ip < ilimit); /* paranoid */
292
293 if (diff2_pix (ip))
294 {
295 const guint8 *s_ip = ip;
296 guint l = 1;
297
298 ip += n_ch;
299 while (l < 127 && ip < ilimit && diff2_pix (ip))
300 { ip += n_ch; l += 1; }
301 if (ip == ilimit && l < 127)
302 { ip += n_ch; l += 1; }
303 *(bp++) = l;
304 memcpy (dest: bp, src: s_ip, n: l * n_ch);
305 bp += l * n_ch;
306 }
307 else
308 {
309 guint l = 2;
310
311 ip += n_ch;
312 while (l < 127 && ip < ilimit && !diff2_pix (ip))
313 { ip += n_ch; l += 1; }
314 *(bp++) = l | 128;
315 memcpy (dest: bp, src: ip, n: n_ch);
316 ip += n_ch;
317 bp += n_ch;
318 }
319 if (ip == ilimit)
320 {
321 *(bp++) = 1;
322 memcpy (dest: bp, src: ip, n: n_ch);
323 ip += n_ch;
324 bp += n_ch;
325 }
326 }
327
328 return bp;
329}
330
331/* Used as the destroy notification function for gdk_pixbuf_new() */
332static void
333free_buffer (guchar *pixels, gpointer data)
334{
335 g_free (mem: pixels);
336}
337
338/**
339 * gdk_pixdata_from_pixbuf: (skip)
340 * @pixdata: a `GdkPixdata` to fill.
341 * @pixbuf: the data to fill `pixdata` with.
342 * @use_rle: whether to use run-length encoding for the pixel data.
343 *
344 * Converts a `GdkPixbuf` to a `GdkPixdata`.
345 *
346 * If `use_rle` is `TRUE`, the pixel data is run-length encoded into
347 * newly-allocated memory and a pointer to that memory is returned.
348 *
349 * Returns: (nullable) (array) (element-type guint8): If `use_rle` is
350 * `TRUE`, a pointer to the newly-allocated memory for the run-length
351 * encoded pixel data, otherwise `NULL`.
352 *
353 * Deprecated: 2.32: Use #GResource instead.
354 **/
355gpointer
356gdk_pixdata_from_pixbuf (GdkPixdata *pixdata,
357 const GdkPixbuf *pixbuf,
358 gboolean use_rle)
359{
360 gpointer free_me = NULL;
361 guint height, rowstride, encoding, bpp, length;
362 const guint8 *pixels = NULL;
363 guint8 *img_buffer;
364
365 g_return_val_if_fail (pixdata != NULL, NULL);
366 g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
367 g_return_val_if_fail (pixbuf->bits_per_sample == 8, NULL);
368 g_return_val_if_fail ((pixbuf->n_channels == 3 && !pixbuf->has_alpha) ||
369 (pixbuf->n_channels == 4 && pixbuf->has_alpha), NULL);
370 g_return_val_if_fail (pixbuf->rowstride >= pixbuf->width, NULL);
371
372 height = pixbuf->height;
373 rowstride = pixbuf->rowstride;
374 bpp = pixbuf->has_alpha ? 4 : 3;
375 encoding = use_rle && ((rowstride / bpp | height) > 1) ? GDK_PIXDATA_ENCODING_RLE : GDK_PIXDATA_ENCODING_RAW;
376
377 if (encoding == GDK_PIXDATA_ENCODING_RLE)
378 {
379 guint pad, n_bytes = rowstride * height;
380 guint8 *img_buffer_end, *data;
381 GdkPixbuf *buf = NULL;
382
383 if (n_bytes % bpp != 0)
384 {
385 rowstride = pixbuf->width * bpp;
386 n_bytes = rowstride * height;
387 data = g_malloc (n_bytes);
388 buf = gdk_pixbuf_new_from_data (data,
389 colorspace: GDK_COLORSPACE_RGB,
390 has_alpha: pixbuf->has_alpha, bits_per_sample: 8,
391 width: pixbuf->width,
392 height: pixbuf->height,
393 rowstride,
394 destroy_fn: free_buffer, NULL);
395 gdk_pixbuf_copy_area (src_pixbuf: pixbuf, src_x: 0, src_y: 0, width: pixbuf->width, height: pixbuf->height,
396 dest_pixbuf: buf, dest_x: 0, dest_y: 0);
397 }
398 else
399 buf = (GdkPixbuf *)pixbuf;
400
401 pixels = gdk_pixbuf_read_pixels (pixbuf: buf);
402 pad = rowstride;
403 pad = MAX (pad, 130 + n_bytes / 127);
404 data = g_new (guint8, pad + n_bytes);
405 free_me = data;
406 img_buffer = data;
407 img_buffer_end = rl_encode_rgbx (bp: img_buffer,
408 ip: pixels, limit: pixels + n_bytes,
409 n_ch: bpp);
410 length = img_buffer_end - img_buffer;
411 if (buf != pixbuf)
412 g_object_unref (object: buf);
413 }
414 else
415 {
416 img_buffer = (guint8 *) gdk_pixbuf_read_pixels (pixbuf);
417 length = rowstride * height;
418 }
419
420 pixdata->magic = GDK_PIXBUF_MAGIC_NUMBER;
421 pixdata->length = GDK_PIXDATA_HEADER_LENGTH + length;
422 pixdata->pixdata_type = pixbuf->has_alpha ? GDK_PIXDATA_COLOR_TYPE_RGBA : GDK_PIXDATA_COLOR_TYPE_RGB;
423 pixdata->pixdata_type |= GDK_PIXDATA_SAMPLE_WIDTH_8;
424 pixdata->pixdata_type |= encoding;
425 pixdata->rowstride = rowstride;
426 pixdata->width = pixbuf->width;
427 pixdata->height = height;
428 pixdata->pixel_data = img_buffer;
429
430 return free_me;
431}
432
433/* From glib's gmem.c */
434#define SIZE_OVERFLOWS(a,b) (G_UNLIKELY ((b) > 0 && (a) > G_MAXSIZE / (b)))
435
436#define RLE_OVERRUN(offset) (rle_buffer_limit == NULL ? FALSE : rle_buffer + (offset) > rle_buffer_limit)
437
438/**
439 * gdk_pixbuf_from_pixdata:
440 * @pixdata: a #GdkPixdata to convert into a `GdkPixbuf`.
441 * @copy_pixels: whether to copy raw pixel data; run-length encoded
442 * pixel data is always copied.
443 * @error: location to store possible errors.
444 *
445 * Converts a `GdkPixdata` to a `GdkPixbuf`.
446 *
447 * If `copy_pixels` is `TRUE` or if the pixel data is run-length-encoded,
448 * the pixel data is copied into newly-allocated memory; otherwise it is
449 * reused.
450 *
451 * Returns: (transfer full): a new pixbuf
452 *
453 * Deprecated: 2.32: Use `GResource` instead.
454 **/
455GdkPixbuf*
456gdk_pixbuf_from_pixdata (const GdkPixdata *pixdata,
457 gboolean copy_pixels,
458 GError **error)
459{
460 guint encoding, bpp;
461 guint8 *data = NULL;
462
463 g_return_val_if_fail (pixdata != NULL, NULL);
464 g_return_val_if_fail (pixdata->width > 0, NULL);
465 g_return_val_if_fail (pixdata->height > 0, NULL);
466 g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
467 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
468 (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
469 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
470 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
471 (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
472 g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
473
474 bpp = (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ? 3 : 4;
475 encoding = pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK;
476
477 g_debug ("gdk_pixbuf_from_pixdata() called on:");
478 g_debug ("\tEncoding %s", encoding == GDK_PIXDATA_ENCODING_RAW ? "raw" : "rle");
479 g_debug ("\tDimensions: %d x %d", pixdata->width, pixdata->height);
480 g_debug ("\tRowstride: %d, Length: %d", pixdata->rowstride, pixdata->length);
481 g_debug ("\tCopy pixels == %s", copy_pixels ? "true" : "false");
482
483 if (encoding == GDK_PIXDATA_ENCODING_RLE)
484 copy_pixels = TRUE;
485
486 /* Sanity check the length and dimensions */
487 if (SIZE_OVERFLOWS (pixdata->height, pixdata->rowstride))
488 {
489 g_set_error_literal (err: error, GDK_PIXBUF_ERROR,
490 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
491 _("Image pixel data corrupt"));
492 return NULL;
493 }
494
495 if (encoding == GDK_PIXDATA_ENCODING_RAW &&
496 pixdata->length >= 1 &&
497 pixdata->length < pixdata->height * pixdata->rowstride - GDK_PIXDATA_HEADER_LENGTH)
498 {
499 g_set_error_literal (err: error, GDK_PIXBUF_ERROR,
500 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
501 _("Image pixel data corrupt"));
502 return NULL;
503 }
504
505 if (copy_pixels)
506 {
507 data = g_try_malloc_n (n_blocks: pixdata->height, n_block_bytes: pixdata->rowstride);
508 if (!data)
509 {
510 g_set_error (err: error, GDK_PIXBUF_ERROR,
511 code: GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
512 format: g_dngettext(GETTEXT_PACKAGE,
513 msgid: "failed to allocate image buffer of %u byte",
514 msgid_plural: "failed to allocate image buffer of %u bytes",
515 n: pixdata->rowstride * pixdata->height),
516 pixdata->rowstride * pixdata->height);
517 return NULL;
518 }
519 }
520 if (encoding == GDK_PIXDATA_ENCODING_RLE)
521 {
522 const guint8 *rle_buffer = pixdata->pixel_data;
523 guint8 *rle_buffer_limit = NULL;
524 guint8 *image_buffer = data;
525 guint8 *image_limit = data + pixdata->rowstride * pixdata->height;
526 gboolean check_overrun = FALSE;
527
528 if (pixdata->length >= 1)
529 rle_buffer_limit = pixdata->pixel_data + pixdata->length - GDK_PIXDATA_HEADER_LENGTH;
530
531 while (image_buffer < image_limit &&
532 (rle_buffer_limit != NULL || rle_buffer > rle_buffer_limit))
533 {
534 guint length;
535
536 if (RLE_OVERRUN(1))
537 {
538 check_overrun = TRUE;
539 break;
540 }
541
542 length = *(rle_buffer++);
543
544 if (length & 128)
545 {
546 length = length - 128;
547 check_overrun = image_buffer + length * bpp > image_limit;
548 if (check_overrun)
549 length = (image_limit - image_buffer) / bpp;
550 if (RLE_OVERRUN(bpp < 4 ? 3 : 4))
551 {
552 check_overrun = TRUE;
553 break;
554 }
555 if (bpp < 4) /* RGB */
556 do
557 {
558 memcpy (dest: image_buffer, src: rle_buffer, n: 3);
559 image_buffer += 3;
560 }
561 while (--length);
562 else /* RGBA */
563 do
564 {
565 memcpy (dest: image_buffer, src: rle_buffer, n: 4);
566 image_buffer += 4;
567 }
568 while (--length);
569 if (RLE_OVERRUN(bpp))
570 {
571 check_overrun = TRUE;
572 break;
573 }
574 rle_buffer += bpp;
575 }
576 else
577 {
578 length *= bpp;
579 check_overrun = image_buffer + length > image_limit;
580 if (check_overrun)
581 length = image_limit - image_buffer;
582 if (RLE_OVERRUN(length))
583 {
584 check_overrun = TRUE;
585 break;
586 }
587 memcpy (dest: image_buffer, src: rle_buffer, n: length);
588 image_buffer += length;
589 rle_buffer += length;
590 }
591 }
592 if (check_overrun)
593 {
594 g_free (mem: data);
595 g_set_error_literal (err: error, GDK_PIXBUF_ERROR,
596 code: GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
597 _("Image pixel data corrupt"));
598 return NULL;
599 }
600 }
601 else if (copy_pixels)
602 memcpy (dest: data, src: pixdata->pixel_data, n: pixdata->rowstride * pixdata->height);
603 else
604 data = pixdata->pixel_data;
605
606 return gdk_pixbuf_new_from_data (data, colorspace: GDK_COLORSPACE_RGB,
607 has_alpha: (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA,
608 bits_per_sample: 8, width: pixdata->width, height: pixdata->height, rowstride: pixdata->rowstride,
609 destroy_fn: copy_pixels ? (GdkPixbufDestroyNotify) g_free : NULL, destroy_fn_data: data);
610}
611
612typedef struct {
613 /* config */
614 gboolean dump_stream;
615 gboolean dump_struct;
616 gboolean dump_macros;
617 gboolean dump_gtypes;
618 gboolean dump_rle_decoder;
619 const gchar *static_prefix;
620 const gchar *const_prefix;
621 /* runtime */
622 GString *gstring;
623 guint pos;
624 gboolean pad;
625} CSourceData;
626
627static inline void
628save_uchar (CSourceData *cdata,
629 guint8 d)
630{
631 GString *gstring = cdata->gstring;
632
633 if (cdata->pos > 70)
634 {
635 if (cdata->dump_struct || cdata->dump_stream)
636 {
637 g_string_append (string: gstring, val: "\"\n \"");
638 cdata->pos = 3;
639 cdata->pad = FALSE;
640 }
641 if (cdata->dump_macros)
642 {
643 g_string_append (string: gstring, val: "\" \\\n \"");
644 cdata->pos = 3;
645 cdata->pad = FALSE;
646 }
647 }
648 if (d < 33 || d > 126 || d == '?')
649 {
650 APPEND (string: gstring, format: "\\%o", d);
651 cdata->pos += 1 + 1 + (d > 7) + (d > 63);
652 cdata->pad = d < 64;
653 return;
654 }
655 if (d == '\\')
656 {
657 g_string_append (string: gstring, val: "\\\\");
658 cdata->pos += 2;
659 }
660 else if (d == '"')
661 {
662 g_string_append (string: gstring, val: "\\\"");
663 cdata->pos += 2;
664 }
665 else if (cdata->pad && d >= '0' && d <= '9')
666 {
667 g_string_append (string: gstring, val: "\"\"");
668 g_string_append_c (gstring, d);
669 cdata->pos += 3;
670 }
671 else
672 {
673 g_string_append_c (gstring, d);
674 cdata->pos += 1;
675 }
676 cdata->pad = FALSE;
677 return;
678}
679
680static inline void
681save_rle_decoder (GString *gstring,
682 const gchar *macro_name,
683 const gchar *s_uint,
684 const gchar *s_uint_8,
685 guint n_ch)
686{
687 APPEND (string: gstring, format: "#define %s_RUN_LENGTH_DECODE(image_buf, rle_data, size, bpp) do \\\n",
688 macro_name);
689 APPEND (string: gstring, format: "{ %s __bpp; %s *__ip; const %s *__il, *__rd; \\\n", s_uint, s_uint_8, s_uint_8);
690 APPEND (string: gstring, format: " __bpp = (bpp); __ip = (image_buf); __il = __ip + (size) * __bpp; \\\n");
691
692 APPEND (string: gstring, format: " __rd = (rle_data); if (__bpp > 3) { /* RGBA */ \\\n");
693
694 APPEND (string: gstring, format: " while (__ip < __il) { %s __l = *(__rd++); \\\n", s_uint);
695 APPEND (string: gstring, format: " if (__l & 128) { __l = __l - 128; \\\n");
696 APPEND (string: gstring, format: " do { memcpy (__ip, __rd, 4); __ip += 4; } while (--__l); __rd += 4; \\\n");
697 APPEND (string: gstring, format: " } else { __l *= 4; memcpy (__ip, __rd, __l); \\\n");
698 APPEND (string: gstring, format: " __ip += __l; __rd += __l; } } \\\n");
699
700 APPEND (string: gstring, format: " } else { /* RGB */ \\\n");
701
702 APPEND (string: gstring, format: " while (__ip < __il) { %s __l = *(__rd++); \\\n", s_uint);
703 APPEND (string: gstring, format: " if (__l & 128) { __l = __l - 128; \\\n");
704 APPEND (string: gstring, format: " do { memcpy (__ip, __rd, 3); __ip += 3; } while (--__l); __rd += 3; \\\n");
705 APPEND (string: gstring, format: " } else { __l *= 3; memcpy (__ip, __rd, __l); \\\n");
706 APPEND (string: gstring, format: " __ip += __l; __rd += __l; } } \\\n");
707
708 APPEND (string: gstring, format: " } } while (0)\n");
709}
710
711/**
712 * gdk_pixdata_to_csource:
713 * @pixdata: a `GdkPixdata` to convert to C source
714 * @name: used for naming generated data structures or macros
715 * @dump_type: the kind of C source to be generated
716 *
717 * Generates C source code suitable for compiling images directly
718 * into programs.
719 *
720 * GdkPixbuf ships with a program called `gdk-pixbuf-csource`, which offers
721 * a command line interface to this function.
722 *
723 * Returns: (transfer full): a newly-allocated string buffer containing
724 * the C source form of `pixdata`.
725 *
726 * Deprecated: 2.32: Use #GResource instead.
727 **/
728GString*
729gdk_pixdata_to_csource (GdkPixdata *pixdata,
730 const gchar *name,
731 GdkPixdataDumpType dump_type)
732{
733 CSourceData cdata = { 0, };
734 gchar *s_uint_8;
735 guint bpp, width, height, rowstride;
736 gboolean rle_encoded;
737 gchar *macro_name;
738 guint8 *img_buffer, *img_buffer_end, *stream = NULL;
739 guint stream_length;
740 GString *gstring;
741
742 /* check args passing */
743 g_return_val_if_fail (pixdata != NULL, NULL);
744 g_return_val_if_fail (name != NULL, NULL);
745 /* check pixdata contents */
746 g_return_val_if_fail (pixdata->magic == GDK_PIXBUF_MAGIC_NUMBER, NULL);
747 g_return_val_if_fail (pixdata->width > 0, NULL);
748 g_return_val_if_fail (pixdata->height > 0, NULL);
749 g_return_val_if_fail (pixdata->rowstride >= pixdata->width, NULL);
750 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ||
751 (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA, NULL);
752 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_SAMPLE_WIDTH_MASK) == GDK_PIXDATA_SAMPLE_WIDTH_8, NULL);
753 g_return_val_if_fail ((pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RAW ||
754 (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_MASK) == GDK_PIXDATA_ENCODING_RLE, NULL);
755 g_return_val_if_fail (pixdata->pixel_data != NULL, NULL);
756
757 img_buffer = pixdata->pixel_data;
758 if (pixdata->length < 1)
759 img_buffer_end = img_buffer + pixdata_get_length (pixdata);
760 else
761 img_buffer_end = img_buffer + pixdata->length - GDK_PIXDATA_HEADER_LENGTH;
762 g_return_val_if_fail (img_buffer < img_buffer_end, NULL);
763
764 bpp = (pixdata->pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB ? 3 : 4;
765 width = pixdata->width;
766 height = pixdata->height;
767 rowstride = pixdata->rowstride;
768 rle_encoded = (pixdata->pixdata_type & GDK_PIXDATA_ENCODING_RLE) > 0;
769 macro_name = g_ascii_strup (str: name, len: -1);
770
771 cdata.dump_macros = (dump_type & GDK_PIXDATA_DUMP_MACROS) > 0;
772 cdata.dump_struct = (dump_type & GDK_PIXDATA_DUMP_PIXDATA_STRUCT) > 0;
773 cdata.dump_stream = !cdata.dump_macros && !cdata.dump_struct;
774 g_return_val_if_fail (cdata.dump_macros + cdata.dump_struct + cdata.dump_stream == 1, NULL);
775
776 cdata.dump_gtypes = (dump_type & GDK_PIXDATA_DUMP_CTYPES) == 0;
777 cdata.dump_rle_decoder = (dump_type & GDK_PIXDATA_DUMP_RLE_DECODER) > 0;
778 cdata.static_prefix = (dump_type & GDK_PIXDATA_DUMP_STATIC) ? "static " : "";
779 cdata.const_prefix = (dump_type & GDK_PIXDATA_DUMP_CONST) ? "const " : "";
780 gstring = g_string_new (NULL);
781 cdata.gstring = gstring;
782
783 if (!cdata.dump_macros && cdata.dump_gtypes)
784 s_uint_8 = "guint8 ";
785 else if (!cdata.dump_macros)
786 s_uint_8 = "unsigned char";
787 else if (cdata.dump_macros && cdata.dump_gtypes)
788 s_uint_8 = "guint8";
789 else /* cdata.dump_macros && !cdata.dump_gtypes */
790 s_uint_8 = "unsigned char";
791
792 /* initial comment
793 */
794 APPEND (string: gstring,
795 format: "/* GdkPixbuf %s C-Source image dump %s*/\n\n",
796 bpp > 3 ? "RGBA" : "RGB",
797 rle_encoded ? "1-byte-run-length-encoded " : "");
798
799 /* dump RLE decoder for structures
800 */
801 if (cdata.dump_rle_decoder && cdata.dump_struct)
802 save_rle_decoder (gstring,
803 macro_name,
804 s_uint: cdata.dump_gtypes ? "guint" : "unsigned int",
805 s_uint_8: cdata.dump_gtypes ? "guint8" : "unsigned char",
806 n_ch: bpp);
807
808 /* format & size blurbs
809 */
810 if (cdata.dump_macros)
811 {
812 APPEND (string: gstring, format: "#define %s_ROWSTRIDE (%u)\n",
813 macro_name, rowstride);
814 APPEND (string: gstring, format: "#define %s_WIDTH (%u)\n",
815 macro_name, width);
816 APPEND (string: gstring, format: "#define %s_HEIGHT (%u)\n",
817 macro_name, height);
818 APPEND (string: gstring, format: "#define %s_BYTES_PER_PIXEL (%u) /* 3:RGB, 4:RGBA */\n",
819 macro_name, bpp);
820 }
821 if (cdata.dump_struct)
822 {
823 APPEND (string: gstring, format: "%s%sGdkPixdata %s = {\n",
824 cdata.static_prefix, cdata.const_prefix, name);
825 APPEND (string: gstring, format: " 0x%x, /* Pixbuf magic: 'GdkP' */\n",
826 GDK_PIXBUF_MAGIC_NUMBER);
827 APPEND (string: gstring, format: " %d + %lu, /* header length + pixel_data length */\n",
828 GDK_PIXDATA_HEADER_LENGTH,
829 rle_encoded ? (glong)(img_buffer_end - img_buffer) : (glong)rowstride * height);
830 APPEND (string: gstring, format: " 0x%x, /* pixdata_type */\n",
831 pixdata->pixdata_type);
832 APPEND (string: gstring, format: " %u, /* rowstride */\n",
833 rowstride);
834 APPEND (string: gstring, format: " %u, /* width */\n",
835 width);
836 APPEND (string: gstring, format: " %u, /* height */\n",
837 height);
838 APPEND (string: gstring, format: " /* pixel_data: */\n");
839 }
840 if (cdata.dump_stream)
841 {
842 guint pix_length = img_buffer_end - img_buffer;
843
844
845 stream = gdk_pixdata_serialize (pixdata, stream_length_p: &stream_length);
846 img_buffer = stream;
847 img_buffer_end = stream + stream_length;
848
849 APPEND (string: gstring, format: "#ifdef __SUNPRO_C\n");
850 APPEND (string: gstring, format: "#pragma align 4 (%s)\n", name);
851 APPEND (string: gstring, format: "#endif\n");
852
853 APPEND (string: gstring, format: "#ifdef __GNUC__\n");
854 APPEND (string: gstring, format: "%s%s%s %s[] __attribute__ ((__aligned__ (4))) = \n",
855 cdata.static_prefix, cdata.const_prefix,
856 cdata.dump_gtypes ? "guint8" : "unsigned char",
857 name);
858 APPEND (string: gstring, format: "#else\n");
859 APPEND (string: gstring, format: "%s%s%s %s[] = \n",
860 cdata.static_prefix, cdata.const_prefix,
861 cdata.dump_gtypes ? "guint8" : "unsigned char",
862 name);
863 APPEND (string: gstring, format: "#endif\n");
864
865 APPEND (string: gstring, format: "{ \"\"\n /* Pixbuf magic (0x%x) */\n \"",
866 GDK_PIXBUF_MAGIC_NUMBER);
867 cdata.pos = 3;
868 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
869 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
870 APPEND (string: gstring, format: "\"\n /* length: header (%d) + pixel_data (%u) */\n \"",
871 GDK_PIXDATA_HEADER_LENGTH,
872 rle_encoded ? pix_length : rowstride * height);
873 cdata.pos = 3;
874 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
875 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
876 APPEND (string: gstring, format: "\"\n /* pixdata_type (0x%x) */\n \"",
877 pixdata->pixdata_type);
878 cdata.pos = 3;
879 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
880 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
881 APPEND (string: gstring, format: "\"\n /* rowstride (%u) */\n \"",
882 rowstride);
883 cdata.pos = 3;
884 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
885 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
886 APPEND (string: gstring, format: "\"\n /* width (%u) */\n \"", width);
887 cdata.pos = 3;
888 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
889 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
890 APPEND (string: gstring, format: "\"\n /* height (%u) */\n \"", height);
891 cdata.pos = 3;
892 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
893 save_uchar (cdata: &cdata, d: *img_buffer++); save_uchar (cdata: &cdata, d: *img_buffer++);
894 APPEND (string: gstring, format: "\"\n /* pixel_data: */\n");
895 }
896
897 /* pixel_data intro
898 */
899 if (cdata.dump_macros)
900 {
901 APPEND (string: gstring, format: "#define %s_%sPIXEL_DATA ((%s*) \\\n",
902 macro_name,
903 rle_encoded ? "RLE_" : "",
904 s_uint_8);
905 APPEND (string: gstring, format: " \"");
906 cdata.pos = 2;
907 }
908 if (cdata.dump_struct)
909 {
910 APPEND (string: gstring, format: " \"");
911 cdata.pos = 3;
912 }
913 if (cdata.dump_stream)
914 {
915 APPEND (string: gstring, format: " \"");
916 cdata.pos = 3;
917 }
918
919 /* pixel_data
920 */
921 do
922 save_uchar (cdata: &cdata, d: *img_buffer++);
923 while (img_buffer < img_buffer_end);
924
925 /* pixel_data trailer
926 */
927 if (cdata.dump_macros)
928 APPEND (string: gstring, format: "\")\n\n");
929 if (cdata.dump_struct)
930 APPEND (string: gstring, format: "\",\n};\n\n");
931 if (cdata.dump_stream)
932 APPEND (string: gstring, format: "\"};\n\n");
933
934 /* dump RLE decoder for macros
935 */
936 if (cdata.dump_rle_decoder && cdata.dump_macros)
937 save_rle_decoder (gstring,
938 macro_name,
939 s_uint: cdata.dump_gtypes ? "guint" : "unsigned int",
940 s_uint_8: cdata.dump_gtypes ? "guint8" : "unsigned char",
941 n_ch: bpp);
942
943 /* cleanup
944 */
945 g_free (mem: stream);
946 g_free (mem: macro_name);
947
948 return gstring;
949}
950
951/**
952 * gdk_pixbuf_new_from_inline:
953 * @data_length: Length in bytes of the `data` argument or -1 to
954 * disable length checks
955 * @data: (array length=data_length): Byte data containing a
956 * serialized `GdkPixdata` structure
957 * @copy_pixels: Whether to copy the pixel data, or use direct pointers
958 * `data` for the resulting pixbuf
959 * @error: #GError return location, may be `NULL` to ignore errors
960 *
961 * Creates a `GdkPixbuf` from a flat representation that is suitable for
962 * storing as inline data in a program.
963 *
964 * This is useful if you want to ship a program with images, but don't want
965 * to depend on any external files.
966 *
967 * GdkPixbuf ships with a program called `gdk-pixbuf-csource`, which allows
968 * for conversion of `GdkPixbuf`s into such a inline representation.
969 *
970 * In almost all cases, you should pass the `--raw` option to
971 * `gdk-pixbuf-csource`. A sample invocation would be:
972 *
973 * ```
974 * gdk-pixbuf-csource --raw --name=myimage_inline myimage.png
975 * ```
976 *
977 * For the typical case where the inline pixbuf is read-only static data,
978 * you don't need to copy the pixel data unless you intend to write to
979 * it, so you can pass `FALSE` for `copy_pixels`. If you pass `--rle` to
980 * `gdk-pixbuf-csource`, a copy will be made even if `copy_pixels` is `FALSE`,
981 * so using this option is generally a bad idea.
982 *
983 * If you create a pixbuf from const inline data compiled into your
984 * program, it's probably safe to ignore errors and disable length checks,
985 * since things will always succeed:
986 *
987 * ```c
988 * pixbuf = gdk_pixbuf_new_from_inline (-1, myimage_inline, FALSE, NULL);
989 * ```
990 *
991 * For non-const inline data, you could get out of memory. For untrusted
992 * inline data located at runtime, you could have corrupt inline data in
993 * addition.
994 *
995 * Return value: A newly-created pixbuf
996 *
997 * Deprecated: 2.32: Use `GResource` instead.
998 **/
999GdkPixbuf*
1000gdk_pixbuf_new_from_inline (gint data_length,
1001 const guint8 *data,
1002 gboolean copy_pixels,
1003 GError **error)
1004{
1005 GdkPixdata pixdata;
1006
1007 if (data_length != -1)
1008 g_return_val_if_fail (data_length > GDK_PIXDATA_HEADER_LENGTH, NULL);
1009 g_return_val_if_fail (data != NULL, NULL);
1010
1011 if (!gdk_pixdata_deserialize (pixdata: &pixdata, stream_length: data_length, stream: data, error))
1012 return NULL;
1013
1014 return gdk_pixbuf_from_pixdata (pixdata: &pixdata, copy_pixels, error);
1015}
1016

source code of gtk/subprojects/gdk-pixbuf/gdk-pixbuf/gdk-pixdata.c