1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ |
2 | /* GdkPixbuf library - Progressive loader object |
3 | * |
4 | * Copyright (C) 1999 The Free Software Foundation |
5 | * |
6 | * Authors: Mark Crichton <crichton@gimp.org> |
7 | * Miguel de Icaza <miguel@gnu.org> |
8 | * Federico Mena-Quintero <federico@gimp.org> |
9 | * Jonathan Blandford <jrb@redhat.com> |
10 | * |
11 | * This library is free software; you can redistribute it and/or |
12 | * modify it under the terms of the GNU Lesser General Public |
13 | * License as published by the Free Software Foundation; either |
14 | * version 2 of the License, or (at your option) any later version. |
15 | * |
16 | * This library is distributed in the hope that it will be useful, |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
19 | * Lesser General Public License for more details. |
20 | * |
21 | * You should have received a copy of the GNU Lesser General Public |
22 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | #include <string.h> |
27 | |
28 | #include "gdk-pixbuf-private.h" |
29 | #include "gdk-pixbuf-animation.h" |
30 | #include "gdk-pixbuf-scaled-anim.h" |
31 | #include "gdk-pixbuf-loader.h" |
32 | #include "gdk-pixbuf-marshal.h" |
33 | |
34 | /** |
35 | * GdkPixbufLoader: |
36 | * |
37 | * Incremental image loader. |
38 | * |
39 | * `GdkPixbufLoader` provides a way for applications to drive the |
40 | * process of loading an image, by letting them send the image data |
41 | * directly to the loader instead of having the loader read the data |
42 | * from a file. Applications can use this functionality instead of |
43 | * `gdk_pixbuf_new_from_file()` or `gdk_pixbuf_animation_new_from_file()` |
44 | * when they need to parse image data in small chunks. For example, |
45 | * it should be used when reading an image from a (potentially) slow |
46 | * network connection, or when loading an extremely large file. |
47 | * |
48 | * To use `GdkPixbufLoader` to load an image, create a new instance, |
49 | * and call [method@GdkPixbuf.PixbufLoader.write] to send the data |
50 | * to it. When done, [method@GdkPixbuf.PixbufLoader.close] should be |
51 | * called to end the stream and finalize everything. |
52 | * |
53 | * The loader will emit three important signals throughout the process: |
54 | * |
55 | * - [signal@GdkPixbuf.PixbufLoader::size-prepared] will be emitted as |
56 | * soon as the image has enough information to determine the size of |
57 | * the image to be used. If you want to scale the image while loading |
58 | * it, you can call [method@GdkPixbuf.PixbufLoader.set_size] in |
59 | * response to this signal. |
60 | * - [signal@GdkPixbuf.PixbufLoader::area-prepared] will be emitted as |
61 | * soon as the pixbuf of the desired has been allocated. You can obtain |
62 | * the `GdkPixbuf` instance by calling [method@GdkPixbuf.PixbufLoader.get_pixbuf]. |
63 | * If you want to use it, simply acquire a reference to it. You can |
64 | * also call `gdk_pixbuf_loader_get_pixbuf()` later to get the same |
65 | * pixbuf. |
66 | * - [signal@GdkPixbuf.PixbufLoader::area-updated] will be emitted every |
67 | * time a region is updated. This way you can update a partially |
68 | * completed image. Note that you do not know anything about the |
69 | * completeness of an image from the updated area. For example, in an |
70 | * interlaced image you will need to make several passes before the |
71 | * image is done loading. |
72 | * |
73 | * ## Loading an animation |
74 | * |
75 | * Loading an animation is almost as easy as loading an image. Once the |
76 | * first [signal@GdkPixbuf.PixbufLoader::area-prepared] signal has been |
77 | * emitted, you can call [method@GdkPixbuf.PixbufLoader.get_animation] to |
78 | * get the [class@GdkPixbuf.PixbufAnimation] instance, and then call |
79 | * and [method@GdkPixbuf.PixbufAnimation.get_iter] to get a |
80 | * [class@GdkPixbuf.PixbufAnimationIter] to retrieve the pixbuf for the |
81 | * desired time stamp. |
82 | */ |
83 | |
84 | |
85 | enum { |
86 | SIZE_PREPARED, |
87 | AREA_PREPARED, |
88 | AREA_UPDATED, |
89 | CLOSED, |
90 | LAST_SIGNAL |
91 | }; |
92 | |
93 | |
94 | static void gdk_pixbuf_loader_finalize (GObject *loader); |
95 | |
96 | static guint pixbuf_loader_signals[LAST_SIGNAL] = { 0 }; |
97 | |
98 | /* Internal data */ |
99 | |
100 | typedef struct |
101 | { |
102 | GdkPixbufAnimation *animation; |
103 | gboolean closed; |
104 | guchar [SNIFF_BUFFER_SIZE]; |
105 | gint ; |
106 | GdkPixbufModule *image_module; |
107 | gpointer context; |
108 | gint original_width; |
109 | gint original_height; |
110 | gint width; |
111 | gint height; |
112 | gboolean size_fixed; |
113 | gboolean needs_scale; |
114 | gchar *filename; |
115 | } GdkPixbufLoaderPrivate; |
116 | |
117 | G_DEFINE_TYPE (GdkPixbufLoader, gdk_pixbuf_loader, G_TYPE_OBJECT) |
118 | |
119 | |
120 | static void |
121 | gdk_pixbuf_loader_class_init (GdkPixbufLoaderClass *class) |
122 | { |
123 | GObjectClass *object_class; |
124 | |
125 | object_class = (GObjectClass *) class; |
126 | |
127 | object_class->finalize = gdk_pixbuf_loader_finalize; |
128 | |
129 | /** |
130 | * GdkPixbufLoader::size-prepared: |
131 | * @loader: the object which received the signal. |
132 | * @width: the original width of the image |
133 | * @height: the original height of the image |
134 | * |
135 | * This signal is emitted when the pixbuf loader has been fed the |
136 | * initial amount of data that is required to figure out the size |
137 | * of the image that it will create. |
138 | * |
139 | * Applications can call gdk_pixbuf_loader_set_size() in response |
140 | * to this signal to set the desired size to which the image |
141 | * should be scaled. |
142 | */ |
143 | pixbuf_loader_signals[SIZE_PREPARED] = |
144 | g_signal_new (signal_name: "size-prepared" , |
145 | G_TYPE_FROM_CLASS (object_class), |
146 | signal_flags: G_SIGNAL_RUN_LAST, |
147 | G_STRUCT_OFFSET (GdkPixbufLoaderClass, size_prepared), |
148 | NULL, NULL, |
149 | c_marshaller: _gdk_pixbuf_marshal_VOID__INT_INT, |
150 | G_TYPE_NONE, n_params: 2, |
151 | G_TYPE_INT, |
152 | G_TYPE_INT); |
153 | |
154 | /** |
155 | * GdkPixbufLoader::area-prepared: |
156 | * @loader: the object which received the signal. |
157 | * |
158 | * This signal is emitted when the pixbuf loader has allocated the |
159 | * pixbuf in the desired size. |
160 | * |
161 | * After this signal is emitted, applications can call |
162 | * gdk_pixbuf_loader_get_pixbuf() to fetch the partially-loaded |
163 | * pixbuf. |
164 | */ |
165 | pixbuf_loader_signals[AREA_PREPARED] = |
166 | g_signal_new (signal_name: "area-prepared" , |
167 | G_TYPE_FROM_CLASS (object_class), |
168 | signal_flags: G_SIGNAL_RUN_LAST, |
169 | G_STRUCT_OFFSET (GdkPixbufLoaderClass, area_prepared), |
170 | NULL, NULL, |
171 | c_marshaller: _gdk_pixbuf_marshal_VOID__VOID, |
172 | G_TYPE_NONE, n_params: 0); |
173 | |
174 | /** |
175 | * GdkPixbufLoader::area-updated: |
176 | * @loader: the object which received the signal. |
177 | * @x: X offset of upper-left corner of the updated area. |
178 | * @y: Y offset of upper-left corner of the updated area. |
179 | * @width: Width of updated area. |
180 | * @height: Height of updated area. |
181 | * |
182 | * This signal is emitted when a significant area of the image being |
183 | * loaded has been updated. |
184 | * |
185 | * Normally it means that a complete scanline has been read in, but |
186 | * it could be a different area as well. |
187 | * |
188 | * Applications can use this signal to know when to repaint |
189 | * areas of an image that is being loaded. |
190 | */ |
191 | pixbuf_loader_signals[AREA_UPDATED] = |
192 | g_signal_new (signal_name: "area-updated" , |
193 | G_TYPE_FROM_CLASS (object_class), |
194 | signal_flags: G_SIGNAL_RUN_LAST, |
195 | G_STRUCT_OFFSET (GdkPixbufLoaderClass, area_updated), |
196 | NULL, NULL, |
197 | c_marshaller: _gdk_pixbuf_marshal_VOID__INT_INT_INT_INT, |
198 | G_TYPE_NONE, n_params: 4, |
199 | G_TYPE_INT, |
200 | G_TYPE_INT, |
201 | G_TYPE_INT, |
202 | G_TYPE_INT); |
203 | |
204 | /** |
205 | * GdkPixbufLoader::closed: |
206 | * @loader: the object which received the signal. |
207 | * |
208 | * This signal is emitted when gdk_pixbuf_loader_close() is called. |
209 | * |
210 | * It can be used by different parts of an application to receive |
211 | * notification when an image loader is closed by the code that |
212 | * drives it. |
213 | */ |
214 | pixbuf_loader_signals[CLOSED] = |
215 | g_signal_new (signal_name: "closed" , |
216 | G_TYPE_FROM_CLASS (object_class), |
217 | signal_flags: G_SIGNAL_RUN_LAST, |
218 | G_STRUCT_OFFSET (GdkPixbufLoaderClass, closed), |
219 | NULL, NULL, |
220 | c_marshaller: _gdk_pixbuf_marshal_VOID__VOID, |
221 | G_TYPE_NONE, n_params: 0); |
222 | } |
223 | |
224 | static void |
225 | gdk_pixbuf_loader_init (GdkPixbufLoader *loader) |
226 | { |
227 | GdkPixbufLoaderPrivate *priv; |
228 | |
229 | priv = g_new0 (GdkPixbufLoaderPrivate, 1); |
230 | priv->original_width = -1; |
231 | priv->original_height = -1; |
232 | priv->width = -1; |
233 | priv->height = -1; |
234 | |
235 | loader->priv = priv; |
236 | } |
237 | |
238 | static void |
239 | gdk_pixbuf_loader_finalize (GObject *object) |
240 | { |
241 | GdkPixbufLoader *loader; |
242 | GdkPixbufLoaderPrivate *priv = NULL; |
243 | |
244 | loader = GDK_PIXBUF_LOADER (object); |
245 | priv = loader->priv; |
246 | |
247 | if (!priv->closed) { |
248 | g_warning ("GdkPixbufLoader finalized without calling gdk_pixbuf_loader_close() - this is not allowed. You must explicitly end the data stream to the loader before dropping the last reference." ); |
249 | } |
250 | if (priv->animation) |
251 | g_object_unref (object: priv->animation); |
252 | |
253 | g_free (mem: priv->filename); |
254 | |
255 | g_free (mem: priv); |
256 | |
257 | G_OBJECT_CLASS (gdk_pixbuf_loader_parent_class)->finalize (object); |
258 | } |
259 | |
260 | /** |
261 | * gdk_pixbuf_loader_set_size: |
262 | * @loader: A pixbuf loader. |
263 | * @width: The desired width of the image being loaded. |
264 | * @height: The desired height of the image being loaded. |
265 | * |
266 | * Causes the image to be scaled while it is loaded. |
267 | * |
268 | * The desired image size can be determined relative to the original |
269 | * size of the image by calling gdk_pixbuf_loader_set_size() from a |
270 | * signal handler for the ::size-prepared signal. |
271 | * |
272 | * Attempts to set the desired image size are ignored after the |
273 | * emission of the ::size-prepared signal. |
274 | * |
275 | * Since: 2.2 |
276 | */ |
277 | void |
278 | gdk_pixbuf_loader_set_size (GdkPixbufLoader *loader, |
279 | gint width, |
280 | gint height) |
281 | { |
282 | GdkPixbufLoaderPrivate *priv; |
283 | |
284 | g_return_if_fail (GDK_IS_PIXBUF_LOADER (loader)); |
285 | g_return_if_fail (width >= 0 && height >= 0); |
286 | |
287 | priv = GDK_PIXBUF_LOADER (loader)->priv; |
288 | |
289 | if (!priv->size_fixed) |
290 | { |
291 | priv->width = width; |
292 | priv->height = height; |
293 | } |
294 | } |
295 | |
296 | static void |
297 | gdk_pixbuf_loader_size_func (gint *width, gint *height, gpointer loader) |
298 | { |
299 | GdkPixbufLoaderPrivate *priv = GDK_PIXBUF_LOADER (loader)->priv; |
300 | |
301 | priv->original_width = *width; |
302 | priv->original_height = *height; |
303 | |
304 | /* allow calling gdk_pixbuf_loader_set_size() before the signal */ |
305 | if (priv->width == -1 && priv->height == -1) |
306 | { |
307 | priv->width = *width; |
308 | priv->height = *height; |
309 | } |
310 | |
311 | g_signal_emit (instance: loader, signal_id: pixbuf_loader_signals[SIZE_PREPARED], detail: 0, *width, *height); |
312 | priv->size_fixed = TRUE; |
313 | |
314 | *width = priv->width; |
315 | *height = priv->height; |
316 | } |
317 | |
318 | static void |
319 | gdk_pixbuf_loader_prepare (GdkPixbuf *pixbuf, |
320 | GdkPixbufAnimation *anim, |
321 | gpointer loader) |
322 | { |
323 | GdkPixbufLoaderPrivate *priv = GDK_PIXBUF_LOADER (loader)->priv; |
324 | gint width, height; |
325 | g_return_if_fail (pixbuf != NULL); |
326 | |
327 | width = anim ? gdk_pixbuf_animation_get_width (animation: anim) : |
328 | gdk_pixbuf_get_width (pixbuf); |
329 | height = anim ? gdk_pixbuf_animation_get_height (animation: anim) : |
330 | gdk_pixbuf_get_height (pixbuf); |
331 | |
332 | if (!priv->size_fixed) |
333 | { |
334 | gint w = width; |
335 | gint h = height; |
336 | /* Defend against lazy loaders which don't call size_func */ |
337 | gdk_pixbuf_loader_size_func (width: &w, height: &h, loader); |
338 | } |
339 | |
340 | priv->needs_scale = FALSE; |
341 | if (priv->width > 0 && priv->height > 0 && |
342 | (priv->width != width || priv->height != height)) |
343 | priv->needs_scale = TRUE; |
344 | |
345 | if (anim) |
346 | g_object_ref (anim); |
347 | else { |
348 | if (priv->original_width > 0) { |
349 | char *original_width_str = NULL; |
350 | |
351 | original_width_str = g_strdup_printf (format: "%d" , priv->original_width); |
352 | gdk_pixbuf_set_option (pixbuf, key: "original-width" , value: original_width_str); |
353 | g_free (mem: original_width_str); |
354 | } |
355 | |
356 | if (priv->original_height > 0) { |
357 | char *original_height_str = NULL; |
358 | |
359 | original_height_str = g_strdup_printf (format: "%d" , priv->original_height); |
360 | gdk_pixbuf_set_option (pixbuf, key: "original-height" , value: original_height_str); |
361 | g_free (mem: original_height_str); |
362 | } |
363 | |
364 | anim = gdk_pixbuf_non_anim_new (pixbuf); |
365 | } |
366 | |
367 | if (priv->needs_scale && width != 0 && height != 0) { |
368 | priv->animation = GDK_PIXBUF_ANIMATION (_gdk_pixbuf_scaled_anim_new (anim, |
369 | (double) priv->width / width, |
370 | (double) priv->height / height, |
371 | 1.0)); |
372 | g_object_unref (object: anim); |
373 | } |
374 | else |
375 | priv->animation = anim; |
376 | |
377 | if (!priv->needs_scale) |
378 | g_signal_emit (instance: loader, signal_id: pixbuf_loader_signals[AREA_PREPARED], detail: 0); |
379 | } |
380 | |
381 | static void |
382 | gdk_pixbuf_loader_update (GdkPixbuf *pixbuf, |
383 | gint x, |
384 | gint y, |
385 | gint width, |
386 | gint height, |
387 | gpointer loader) |
388 | { |
389 | GdkPixbufLoaderPrivate *priv = GDK_PIXBUF_LOADER (loader)->priv; |
390 | |
391 | if (!priv->needs_scale) |
392 | g_signal_emit (instance: loader, |
393 | signal_id: pixbuf_loader_signals[AREA_UPDATED], |
394 | detail: 0, |
395 | x, y, |
396 | /* sanity check in here. Defend against an errant loader */ |
397 | MIN (width, gdk_pixbuf_animation_get_width (priv->animation)), |
398 | MIN (height, gdk_pixbuf_animation_get_height (priv->animation))); |
399 | } |
400 | |
401 | /* Defense against broken loaders; DO NOT take this as a GError example! */ |
402 | static void |
403 | gdk_pixbuf_loader_ensure_error (GdkPixbufLoader *loader, |
404 | GError **error) |
405 | { |
406 | GdkPixbufLoaderPrivate *priv = loader->priv; |
407 | |
408 | if (error == NULL || *error != NULL) |
409 | return; |
410 | |
411 | g_warning ("Bug! loader '%s' didn't set an error on failure" , |
412 | priv->image_module->module_name); |
413 | g_set_error (err: error, |
414 | GDK_PIXBUF_ERROR, |
415 | code: GDK_PIXBUF_ERROR_FAILED, |
416 | _("Internal error: Image loader module “%s” failed to" |
417 | " complete an operation, but didn’t give a reason for" |
418 | " the failure" ), |
419 | priv->image_module->module_name); |
420 | } |
421 | |
422 | static gint |
423 | gdk_pixbuf_loader_load_module (GdkPixbufLoader *loader, |
424 | const char *image_type, |
425 | GError **error) |
426 | { |
427 | GdkPixbufLoaderPrivate *priv = loader->priv; |
428 | |
429 | if (image_type) |
430 | { |
431 | priv->image_module = _gdk_pixbuf_get_named_module (name: image_type, |
432 | error); |
433 | } |
434 | else |
435 | { |
436 | priv->image_module = _gdk_pixbuf_get_module (buffer: priv->header_buf, |
437 | size: priv->header_buf_offset, |
438 | filename: priv->filename, |
439 | error); |
440 | } |
441 | |
442 | if (priv->image_module == NULL) |
443 | return 0; |
444 | |
445 | if (!_gdk_pixbuf_load_module (image_module: priv->image_module, error)) |
446 | return 0; |
447 | |
448 | if (priv->image_module->module == NULL) |
449 | return 0; |
450 | |
451 | if ((priv->image_module->begin_load == NULL) || |
452 | (priv->image_module->stop_load == NULL) || |
453 | (priv->image_module->load_increment == NULL)) |
454 | { |
455 | g_set_error (err: error, |
456 | GDK_PIXBUF_ERROR, |
457 | code: GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION, |
458 | _("Incremental loading of image type “%s” is not supported" ), |
459 | priv->image_module->module_name); |
460 | |
461 | return 0; |
462 | } |
463 | |
464 | priv->context = priv->image_module->begin_load (gdk_pixbuf_loader_size_func, |
465 | gdk_pixbuf_loader_prepare, |
466 | gdk_pixbuf_loader_update, |
467 | loader, |
468 | error); |
469 | |
470 | if (priv->context == NULL) |
471 | { |
472 | gdk_pixbuf_loader_ensure_error (loader, error); |
473 | return 0; |
474 | } |
475 | |
476 | if (priv->header_buf_offset |
477 | && priv->image_module->load_increment (priv->context, priv->header_buf, priv->header_buf_offset, error)) |
478 | return priv->header_buf_offset; |
479 | |
480 | return 0; |
481 | } |
482 | |
483 | static int |
484 | (GdkPixbufLoader *loader, |
485 | const guchar *buf, |
486 | gsize count, |
487 | GError **error) |
488 | { |
489 | gint n_bytes; |
490 | GdkPixbufLoaderPrivate *priv = loader->priv; |
491 | |
492 | n_bytes = MIN(SNIFF_BUFFER_SIZE - priv->header_buf_offset, count); |
493 | memcpy (dest: priv->header_buf + priv->header_buf_offset, src: buf, n: n_bytes); |
494 | |
495 | priv->header_buf_offset += n_bytes; |
496 | |
497 | if (priv->header_buf_offset >= SNIFF_BUFFER_SIZE) |
498 | { |
499 | if (gdk_pixbuf_loader_load_module (loader, NULL, error) == 0) |
500 | return 0; |
501 | } |
502 | |
503 | return n_bytes; |
504 | } |
505 | |
506 | /** |
507 | * gdk_pixbuf_loader_write: |
508 | * @loader: A pixbuf loader. |
509 | * @buf: (array length=count): Pointer to image data. |
510 | * @count: Length of the @buf buffer in bytes. |
511 | * @error: return location for errors |
512 | * |
513 | * Parses the next `count` bytes in the given image buffer. |
514 | * |
515 | * Return value: `TRUE` if the write was successful, or |
516 | * `FALSE` if the loader cannot parse the buffer |
517 | **/ |
518 | gboolean |
519 | gdk_pixbuf_loader_write (GdkPixbufLoader *loader, |
520 | const guchar *buf, |
521 | gsize count, |
522 | GError **error) |
523 | { |
524 | GdkPixbufLoaderPrivate *priv; |
525 | |
526 | g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), FALSE); |
527 | |
528 | g_return_val_if_fail (buf != NULL, FALSE); |
529 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
530 | |
531 | priv = loader->priv; |
532 | |
533 | /* we expect it's not to be closed */ |
534 | g_return_val_if_fail (priv->closed == FALSE, FALSE); |
535 | |
536 | if (count > 0 && priv->image_module == NULL) |
537 | { |
538 | gint eaten; |
539 | |
540 | eaten = gdk_pixbuf_loader_eat_header_write (loader, buf, count, error); |
541 | if (eaten <= 0) |
542 | goto fail; |
543 | |
544 | count -= eaten; |
545 | buf += eaten; |
546 | } |
547 | |
548 | /* By this point, we expect the image_module to have been loaded. */ |
549 | g_assert (count == 0 || priv->image_module != NULL); |
550 | |
551 | if (count > 0 && priv->image_module->load_increment != NULL) |
552 | { |
553 | if (!priv->image_module->load_increment (priv->context, buf, count, |
554 | error)) |
555 | goto fail; |
556 | } |
557 | |
558 | return TRUE; |
559 | |
560 | fail: |
561 | gdk_pixbuf_loader_ensure_error (loader, error); |
562 | gdk_pixbuf_loader_close (loader, NULL); |
563 | |
564 | return FALSE; |
565 | } |
566 | |
567 | /** |
568 | * gdk_pixbuf_loader_write_bytes: |
569 | * @loader: A pixbuf loader. |
570 | * @buffer: The image data as a `GBytes` buffer. |
571 | * @error: return location for errors |
572 | * |
573 | * Parses the next contents of the given image buffer. |
574 | * |
575 | * Return value: `TRUE` if the write was successful, or `FALSE` if |
576 | * the loader cannot parse the buffer |
577 | * |
578 | * Since: 2.30 |
579 | */ |
580 | gboolean |
581 | gdk_pixbuf_loader_write_bytes (GdkPixbufLoader *loader, |
582 | GBytes *buffer, |
583 | GError **error) |
584 | { |
585 | g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), FALSE); |
586 | |
587 | g_return_val_if_fail (buffer != NULL, FALSE); |
588 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
589 | |
590 | return gdk_pixbuf_loader_write (loader, |
591 | buf: g_bytes_get_data (bytes: buffer, NULL), |
592 | count: g_bytes_get_size (bytes: buffer), |
593 | error); |
594 | } |
595 | |
596 | /** |
597 | * gdk_pixbuf_loader_new: |
598 | * |
599 | * Creates a new pixbuf loader object. |
600 | * |
601 | * Return value: A newly-created pixbuf loader. |
602 | **/ |
603 | GdkPixbufLoader * |
604 | gdk_pixbuf_loader_new (void) |
605 | { |
606 | return g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL); |
607 | } |
608 | |
609 | /** |
610 | * gdk_pixbuf_loader_new_with_type: |
611 | * @image_type: name of the image format to be loaded with the image |
612 | * @error: (allow-none): return location for an allocated #GError, or `NULL` to ignore errors |
613 | * |
614 | * Creates a new pixbuf loader object that always attempts to parse |
615 | * image data as if it were an image of type @image_type, instead of |
616 | * identifying the type automatically. |
617 | * |
618 | * This function is useful if you want an error if the image isn't the |
619 | * expected type; for loading image formats that can't be reliably |
620 | * identified by looking at the data; or if the user manually forces |
621 | * a specific type. |
622 | * |
623 | * The list of supported image formats depends on what image loaders |
624 | * are installed, but typically "png", "jpeg", "gif", "tiff" and |
625 | * "xpm" are among the supported formats. To obtain the full list of |
626 | * supported image formats, call gdk_pixbuf_format_get_name() on each |
627 | * of the #GdkPixbufFormat structs returned by gdk_pixbuf_get_formats(). |
628 | * |
629 | * Return value: A newly-created pixbuf loader. |
630 | **/ |
631 | GdkPixbufLoader * |
632 | gdk_pixbuf_loader_new_with_type (const char *image_type, |
633 | GError **error) |
634 | { |
635 | GdkPixbufLoader *retval; |
636 | GError *tmp; |
637 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
638 | |
639 | retval = g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL); |
640 | |
641 | tmp = NULL; |
642 | gdk_pixbuf_loader_load_module (loader: retval, image_type, error: &tmp); |
643 | if (tmp != NULL) |
644 | { |
645 | g_propagate_error (dest: error, src: tmp); |
646 | gdk_pixbuf_loader_close (loader: retval, NULL); |
647 | g_object_unref (object: retval); |
648 | return NULL; |
649 | } |
650 | |
651 | return retval; |
652 | } |
653 | |
654 | /** |
655 | * gdk_pixbuf_loader_new_with_mime_type: |
656 | * @mime_type: the mime type to be loaded |
657 | * @error: (allow-none): return location for an allocated #GError, or `NULL` to ignore errors |
658 | * |
659 | * Creates a new pixbuf loader object that always attempts to parse |
660 | * image data as if it were an image of MIME type @mime_type, instead of |
661 | * identifying the type automatically. |
662 | * |
663 | * This function is useful if you want an error if the image isn't the |
664 | * expected MIME type; for loading image formats that can't be reliably |
665 | * identified by looking at the data; or if the user manually forces a |
666 | * specific MIME type. |
667 | * |
668 | * The list of supported mime types depends on what image loaders |
669 | * are installed, but typically "image/png", "image/jpeg", "image/gif", |
670 | * "image/tiff" and "image/x-xpixmap" are among the supported mime types. |
671 | * To obtain the full list of supported mime types, call |
672 | * gdk_pixbuf_format_get_mime_types() on each of the #GdkPixbufFormat |
673 | * structs returned by gdk_pixbuf_get_formats(). |
674 | * |
675 | * Return value: A newly-created pixbuf loader. |
676 | * |
677 | * Since: 2.4 |
678 | **/ |
679 | GdkPixbufLoader * |
680 | gdk_pixbuf_loader_new_with_mime_type (const char *mime_type, |
681 | GError **error) |
682 | { |
683 | const char * image_type = NULL; |
684 | char ** mimes; |
685 | |
686 | GdkPixbufLoader *retval; |
687 | GError *tmp; |
688 | |
689 | GSList * formats; |
690 | GdkPixbufFormat *info; |
691 | int i, j, length; |
692 | |
693 | formats = gdk_pixbuf_get_formats (); |
694 | length = g_slist_length (list: formats); |
695 | |
696 | for (i = 0; i < length && image_type == NULL; i++) { |
697 | info = (GdkPixbufFormat *)g_slist_nth_data (list: formats, n: i); |
698 | mimes = info->mime_types; |
699 | |
700 | for (j = 0; mimes[j] != NULL; j++) |
701 | if (g_ascii_strcasecmp (s1: mimes[j], s2: mime_type) == 0) { |
702 | image_type = info->name; |
703 | break; |
704 | } |
705 | } |
706 | |
707 | g_slist_free (list: formats); |
708 | |
709 | retval = g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL); |
710 | |
711 | tmp = NULL; |
712 | gdk_pixbuf_loader_load_module (loader: retval, image_type, error: &tmp); |
713 | if (tmp != NULL) |
714 | { |
715 | g_propagate_error (dest: error, src: tmp); |
716 | gdk_pixbuf_loader_close (loader: retval, NULL); |
717 | g_object_unref (object: retval); |
718 | return NULL; |
719 | } |
720 | |
721 | return retval; |
722 | } |
723 | |
724 | GdkPixbufLoader * |
725 | _gdk_pixbuf_loader_new_with_filename (const char *filename) |
726 | { |
727 | GdkPixbufLoader *retval; |
728 | GdkPixbufLoaderPrivate *priv; |
729 | |
730 | retval = g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL); |
731 | priv = retval->priv; |
732 | priv->filename = g_strdup (str: filename); |
733 | |
734 | return retval; |
735 | } |
736 | |
737 | /** |
738 | * gdk_pixbuf_loader_get_pixbuf: |
739 | * @loader: A pixbuf loader. |
740 | * |
741 | * Queries the #GdkPixbuf that a pixbuf loader is currently creating. |
742 | * |
743 | * In general it only makes sense to call this function after the |
744 | * [signal@GdkPixbuf.PixbufLoader::area-prepared] signal has been |
745 | * emitted by the loader; this means that enough data has been read |
746 | * to know the size of the image that will be allocated. |
747 | * |
748 | * If the loader has not received enough data via gdk_pixbuf_loader_write(), |
749 | * then this function returns `NULL`. |
750 | * |
751 | * The returned pixbuf will be the same in all future calls to the loader, |
752 | * so if you want to keep using it, you should acquire a reference to it. |
753 | * |
754 | * Additionally, if the loader is an animation, it will return the "static |
755 | * image" of the animation (see gdk_pixbuf_animation_get_static_image()). |
756 | * |
757 | * Return value: (transfer none) (nullable): The pixbuf that the loader is |
758 | * creating |
759 | **/ |
760 | GdkPixbuf * |
761 | gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader) |
762 | { |
763 | GdkPixbufLoaderPrivate *priv; |
764 | |
765 | g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL); |
766 | |
767 | priv = loader->priv; |
768 | |
769 | if (priv->animation) |
770 | return gdk_pixbuf_animation_get_static_image (animation: priv->animation); |
771 | else |
772 | return NULL; |
773 | } |
774 | |
775 | /** |
776 | * gdk_pixbuf_loader_get_animation: |
777 | * @loader: A pixbuf loader |
778 | * |
779 | * Queries the #GdkPixbufAnimation that a pixbuf loader is currently creating. |
780 | * |
781 | * In general it only makes sense to call this function after the |
782 | * [signal@GdkPixbuf.PixbufLoader::area-prepared] signal has been emitted by |
783 | * the loader. |
784 | * |
785 | * If the loader doesn't have enough bytes yet, and hasn't emitted the `area-prepared` |
786 | * signal, this function will return `NULL`. |
787 | * |
788 | * Return value: (transfer none) (nullable): The animation that the loader is |
789 | * currently loading |
790 | */ |
791 | GdkPixbufAnimation * |
792 | gdk_pixbuf_loader_get_animation (GdkPixbufLoader *loader) |
793 | { |
794 | GdkPixbufLoaderPrivate *priv; |
795 | |
796 | g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL); |
797 | |
798 | priv = loader->priv; |
799 | |
800 | return priv->animation; |
801 | } |
802 | |
803 | /** |
804 | * gdk_pixbuf_loader_close: |
805 | * @loader: A pixbuf loader. |
806 | * @error: (allow-none): return location for a #GError, or `NULL` to ignore errors |
807 | * |
808 | * Informs a pixbuf loader that no further writes with |
809 | * gdk_pixbuf_loader_write() will occur, so that it can free its |
810 | * internal loading structures. |
811 | * |
812 | * This function also tries to parse any data that hasn't yet been parsed; |
813 | * if the remaining data is partial or corrupt, an error will be returned. |
814 | * |
815 | * If `FALSE` is returned, `error` will be set to an error from the |
816 | * `GDK_PIXBUF_ERROR` or `G_FILE_ERROR` domains. |
817 | * |
818 | * If you're just cancelling a load rather than expecting it to be finished, |
819 | * passing `NULL` for `error` to ignore it is reasonable. |
820 | * |
821 | * Remember that this function does not release a reference on the loader, so |
822 | * you will need to explicitly release any reference you hold. |
823 | * |
824 | * Returns: `TRUE` if all image data written so far was successfully |
825 | * passed out via the update_area signal |
826 | */ |
827 | gboolean |
828 | gdk_pixbuf_loader_close (GdkPixbufLoader *loader, |
829 | GError **error) |
830 | { |
831 | GdkPixbufLoaderPrivate *priv; |
832 | gboolean retval = TRUE; |
833 | |
834 | g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), TRUE); |
835 | g_return_val_if_fail (error == NULL || *error == NULL, TRUE); |
836 | |
837 | priv = loader->priv; |
838 | |
839 | if (priv->closed) |
840 | return TRUE; |
841 | |
842 | /* We have less than SNIFF_BUFFER_SIZE bytes in the image. |
843 | * Flush it, and keep going. |
844 | */ |
845 | if (priv->image_module == NULL) |
846 | { |
847 | GError *tmp = NULL; |
848 | gdk_pixbuf_loader_load_module (loader, NULL, error: &tmp); |
849 | if (tmp != NULL) |
850 | { |
851 | g_propagate_error (dest: error, src: tmp); |
852 | retval = FALSE; |
853 | } |
854 | } |
855 | |
856 | if (priv->image_module && priv->image_module->stop_load && priv->context) |
857 | { |
858 | GError *tmp = NULL; |
859 | if (!priv->image_module->stop_load (priv->context, &tmp) || tmp) |
860 | { |
861 | /* don't call gdk_pixbuf_loader_ensure_error() |
862 | * here, since we might not get an error in the |
863 | * gdk_pixbuf_get_file_info() case |
864 | */ |
865 | if (tmp) { |
866 | if (error && *error == NULL) |
867 | g_propagate_error (dest: error, src: tmp); |
868 | else |
869 | g_error_free (error: tmp); |
870 | } |
871 | retval = FALSE; |
872 | } |
873 | } |
874 | |
875 | priv->closed = TRUE; |
876 | |
877 | if (priv->needs_scale) |
878 | { |
879 | |
880 | g_signal_emit (instance: loader, signal_id: pixbuf_loader_signals[AREA_PREPARED], detail: 0); |
881 | g_signal_emit (instance: loader, signal_id: pixbuf_loader_signals[AREA_UPDATED], detail: 0, |
882 | 0, 0, priv->width, priv->height); |
883 | } |
884 | |
885 | |
886 | g_signal_emit (instance: loader, signal_id: pixbuf_loader_signals[CLOSED], detail: 0); |
887 | |
888 | return retval; |
889 | } |
890 | |
891 | /** |
892 | * gdk_pixbuf_loader_get_format: |
893 | * @loader: A pixbuf loader. |
894 | * |
895 | * Obtains the available information about the format of the |
896 | * currently loading image file. |
897 | * |
898 | * Returns: (nullable) (transfer none): A #GdkPixbufFormat |
899 | * |
900 | * Since: 2.2 |
901 | */ |
902 | GdkPixbufFormat * |
903 | gdk_pixbuf_loader_get_format (GdkPixbufLoader *loader) |
904 | { |
905 | GdkPixbufLoaderPrivate *priv; |
906 | |
907 | g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL); |
908 | |
909 | priv = loader->priv; |
910 | |
911 | if (priv->image_module) |
912 | return _gdk_pixbuf_get_format (image_module: priv->image_module); |
913 | else |
914 | return NULL; |
915 | } |
916 | |