1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
|
/* GdkPixbuf library - Image creation from in-memory buffers
*
* Copyright (C) 1999 The Free Software Foundation
*
* Author: Federico Mena-Quintero <federico@gimp.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <config.h>
#include "gdk-pixbuf.h"
#include "gdk-pixbuf-private.h"
#include "gdk-pixbuf-i18n.h"
#include <stdlib.h>
#include <string.h>
/**
* gdk_pixbuf_new_from_data:
* @data: Image data in 8-bit/sample packed format.
* @colorspace: Colorspace for the image data.
* @has_alpha: Whether the data has an opacity channel.
* @bits_per_sample: Number of bits per sample.
* @width: Width of the image in pixels.
* @height: Height of the image in pixels.
* @rowstride: Distance in bytes between rows.
* @destroy_fn: Function used to free the data when the pixbuf's reference count
* drops to zero, or NULL if the data should not be freed.
* @destroy_fn_data: Closure data to pass to the destroy notification function.
*
* Creates a new #GdkPixbuf out of in-memory image data. Currently only RGB
* images with 8 bits per sample are supported.
*
* Return value: A newly-created #GdkPixbuf structure with a reference count of
* 1.
**/
GdkPixbuf *
gdk_pixbuf_new_from_data (const guchar *data, GdkColorspace colorspace, gboolean has_alpha,
int bits_per_sample, int width, int height, int rowstride,
GdkPixbufDestroyNotify destroy_fn, gpointer destroy_fn_data)
{
GdkPixbuf *pixbuf;
/* Only 8-bit/sample RGB buffers are supported for now */
g_return_val_if_fail (data != NULL, NULL);
g_return_val_if_fail (colorspace == GDK_COLORSPACE_RGB, NULL);
g_return_val_if_fail (bits_per_sample == 8, NULL);
g_return_val_if_fail (width > 0, NULL);
g_return_val_if_fail (height > 0, NULL);
pixbuf = g_object_new (GDK_TYPE_PIXBUF, NULL);
pixbuf->colorspace = colorspace;
pixbuf->n_channels = has_alpha ? 4 : 3;
pixbuf->bits_per_sample = bits_per_sample;
pixbuf->has_alpha = has_alpha ? TRUE : FALSE;
pixbuf->width = width;
pixbuf->height = height;
pixbuf->rowstride = rowstride;
pixbuf->pixels = (guchar *) data;
pixbuf->destroy_fn = destroy_fn;
pixbuf->destroy_fn_data = destroy_fn_data;
return pixbuf;
}
static guint32
read_int (const guchar **p)
{
guint32 num;
/* Note most significant bytes are first in the byte stream */
num =
(*p)[3] |
((*p)[2] << 8) |
((*p)[1] << 16) |
((*p)[0] << 24);
*p += 4;
return num;
}
static gboolean
read_bool (const guchar **p)
{
gboolean val = **p != 0;
++(*p);
return val;
}
static GdkPixbuf*
read_raw_inline (const guchar *data,
gboolean copy_pixels,
int length,
GError **error)
{
GdkPixbuf *pixbuf;
const guchar *p = data;
guint32 rowstride, width, height, colorspace,
n_channels, bits_per_sample;
gboolean has_alpha;
if (length >= 0 && length < 12) {
/* Not enough buffer to hold the width/height/rowstride */
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image data is partially missing"));
return NULL;
}
rowstride = read_int (&p);
width = read_int (&p);
height = read_int (&p);
if (rowstride < width) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image has an incorrect pixel rowstride, perhaps the data was corrupted somehow."));
return NULL; /* bad data from untrusted source. */
}
/* rowstride >= width, so we can trust width */
length -= 12;
/* There's some better way like G_MAXINT/height > rowstride
* but I'm not sure it works, so stick to this for now.
*/
if (((double)height) * ((double)rowstride) > (double)G_MAXINT) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image size is impossibly large, perhaps the data was corrupted somehow"));
return NULL; /* overflow */
}
if (length >= 0 &&
length < (height * rowstride + 13)) {
/* Not enough buffer to hold the remaining header
* information plus the data.
*/
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image data is partially missing, probably it was corrupted somehow."));
return NULL;
}
/* Read the remaining 13 bytes of header information */
has_alpha = read_bool (&p) != FALSE;
colorspace = read_int (&p);
n_channels = read_int (&p);
bits_per_sample = read_int (&p);
if (colorspace != GDK_COLORSPACE_RGB) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image has an unknown colorspace code (%d), perhaps the image data was corrupted"),
colorspace);
return NULL;
}
if (bits_per_sample != 8) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image has an improper number of bits per sample (%d), perhaps the image data was corrupted"),
bits_per_sample);
return NULL;
}
if (has_alpha && n_channels != 4) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image has an improper number of channels (%d), perhaps the image data was corrupted"),
n_channels);
return NULL;
}
if (!has_alpha && n_channels != 3) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image has an improper number of channels (%d), perhaps the image data was corrupted"),
n_channels);
return NULL;
}
if (copy_pixels) {
guchar *pixels;
gint dest_rowstride;
gint row;
pixbuf = gdk_pixbuf_new (colorspace,
has_alpha, bits_per_sample,
width, height);
if (pixbuf == NULL) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Not enough memory to store a %d by %d image; try exiting some applications to free memory."),
width, height);
return NULL;
}
pixels = gdk_pixbuf_get_pixels (pixbuf);
dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
for (row = 0; row < height; row++) {
memcpy (pixels, p, rowstride);
pixels += dest_rowstride;
p += rowstride;
}
} else {
pixbuf = gdk_pixbuf_new_from_data (p,
colorspace,
has_alpha,
bits_per_sample,
width, height,
rowstride,
NULL, NULL);
}
return pixbuf;
}
/**
* gdk_pixbuf_new_from_inline:
* @inline_pixbuf: An inlined GdkPixbuf
* @copy_pixels: whether to copy the pixels out of the inline data, or to use them in-place
* @length: length of the inline data
* @error: return location for error
*
* Create a #GdkPixbuf from a custom format invented to store pixbuf
* data in C program code. This library comes with a program called
* "make-inline-pixbuf" that can write out a variable definition
* containing an inlined pixbuf. This is useful if you want to ship a
* program with images, but don't want to depend on any external
* files.
*
* The inline data format contains the pixels in #GdkPixbuf's native
* format. Since the inline pixbuf is read-only static data, you
* don't need to copy it unless you intend to write to it.
*
* If you create a pixbuf from const inline data compiled into your
* program, it's probably safe to ignore errors, since things will
* always succeed. For non-const inline data, you could get out of
* memory. For untrusted inline data located at runtime, you could
* have corrupt inline data in addition.
*
* Return value: A newly-created #GdkPixbuf structure with a reference count of
* 1, or NULL If error is set.
**/
GdkPixbuf*
gdk_pixbuf_new_from_inline (const guchar *inline_pixbuf,
gboolean copy_pixels,
int length,
GError **error)
{
const guchar *p;
GdkPixbuf *pixbuf;
GdkPixbufInlineFormat format;
if (length >= 0 && length < 8) {
/* not enough bytes to contain even the magic number
* and format code.
*/
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image contained no data."));
return NULL;
}
p = inline_pixbuf;
if (read_int (&p) != GDK_PIXBUF_INLINE_MAGIC_NUMBER) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
_("Image isn't in the correct format (inline GdkPixbuf format)"));
return NULL;
}
format = read_int (&p);
switch (format)
{
case GDK_PIXBUF_INLINE_RAW:
pixbuf = read_raw_inline (p, copy_pixels, length - 8, error);
break;
default:
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
_("This version of the software is unable to read images with type code %d"),
format);
return NULL;
}
return pixbuf;
}
|