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 | |
23 | G_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 --- */ |
57 | static guint |
58 | pixdata_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 | **/ |
126 | guint8* /* free result */ |
127 | gdk_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 (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 | |
192 | static inline const guint8 * |
193 | get_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 | **/ |
224 | gboolean |
225 | gdk_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 | |
268 | static gboolean |
269 | diff2_rgb (const guint8 *ip) |
270 | { |
271 | return ip[0] != ip[3] || ip[1] != ip[4] || ip[2] != ip[5]; |
272 | } |
273 | |
274 | static gboolean |
275 | diff2_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 | |
280 | static guint8 * /* dest buffer bound */ |
281 | rl_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() */ |
332 | static void |
333 | free_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 | **/ |
355 | gpointer |
356 | gdk_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 | **/ |
455 | GdkPixbuf* |
456 | gdk_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 | |
612 | typedef 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 | |
627 | static inline void |
628 | save_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 | |
680 | static inline void |
681 | save_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 | **/ |
728 | GString* |
729 | gdk_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 | **/ |
999 | GdkPixbuf* |
1000 | gdk_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 | |