]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/gdk-pixbuf-loader.c
a147e8b85706fbde98e06ad25ccc89aaab9639d3
[~andy/gtk] / gdk-pixbuf / gdk-pixbuf-loader.c
1 /* GdkPixbuf library - Progressive loader object
2  *
3  * Copyright (C) 1999 The Free Software Foundation
4  *
5  * Authors: Mark Crichton <crichton@gimp.org>
6  *          Miguel de Icaza <miguel@gnu.org>
7  *          Federico Mena-Quintero <federico@gimp.org>
8  *          Jonathan Blandford <jrb@redhat.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 #include <string.h>
27
28 #include "gdk-pixbuf-private.h"
29 #include "gdk-pixbuf-loader.h"
30 #include "gdk-pixbuf-io.h"
31 #include "gdk-pixbuf-marshal.h"
32
33 #include <gobject/gsignal.h>
34
35 enum {
36   AREA_UPDATED,
37   AREA_PREPARED,
38   CLOSED,
39   LAST_SIGNAL
40 };
41
42
43 static void gdk_pixbuf_loader_class_init    (GdkPixbufLoaderClass   *klass);
44 static void gdk_pixbuf_loader_init          (GdkPixbufLoader        *loader);
45 static void gdk_pixbuf_loader_finalize      (GObject                *loader);
46
47 static gpointer parent_class = NULL;
48 static guint    pixbuf_loader_signals[LAST_SIGNAL] = { 0 };
49
50
51 /* Internal data */
52
53 #define LOADER_HEADER_SIZE 128
54
55 typedef struct
56 {
57   GdkPixbufAnimation *animation;
58   gboolean closed;
59   guchar header_buf[LOADER_HEADER_SIZE];
60   gint header_buf_offset;
61   GdkPixbufModule *image_module;
62   gpointer context;
63 } GdkPixbufLoaderPrivate;
64
65
66 /**
67  * gdk_pixbuf_loader_get_type:
68  * @void:
69  *
70  * Registers the #GdkPixubfLoader class if necessary, and returns the type ID
71  * associated to it.
72  *
73  * Return value: The type ID of the #GdkPixbufLoader class.
74  **/
75 GType
76 gdk_pixbuf_loader_get_type (void)
77 {
78   static GType loader_type = 0;
79   
80   if (!loader_type)
81     {
82       static const GTypeInfo loader_info = {
83         sizeof (GdkPixbufLoaderClass),
84         (GBaseInitFunc) NULL,
85         (GBaseFinalizeFunc) NULL,
86         (GClassInitFunc) gdk_pixbuf_loader_class_init,
87         NULL,           /* class_finalize */
88         NULL,           /* class_data */
89         sizeof (GdkPixbufLoader),
90         0,              /* n_preallocs */
91         (GInstanceInitFunc) gdk_pixbuf_loader_init
92       };
93       
94       loader_type = g_type_register_static (G_TYPE_OBJECT,
95                                             "GdkPixbufLoader",
96                                             &loader_info,
97                                             0);
98     }
99   
100   return loader_type;
101 }
102
103 static void
104 gdk_pixbuf_loader_class_init (GdkPixbufLoaderClass *class)
105 {
106   GObjectClass *object_class;
107   
108   object_class = (GObjectClass *) class;
109   
110   parent_class = g_type_class_peek_parent (class);
111   
112   object_class->finalize = gdk_pixbuf_loader_finalize;
113
114   pixbuf_loader_signals[AREA_PREPARED] =
115     g_signal_newc ("area_prepared",
116                    G_TYPE_FROM_CLASS (object_class),
117                    G_SIGNAL_RUN_LAST,
118                    G_STRUCT_OFFSET (GdkPixbufLoaderClass, area_prepared),
119                    NULL, NULL,
120                    gdk_pixbuf_marshal_VOID__VOID,
121                    G_TYPE_NONE, 0);
122   
123   pixbuf_loader_signals[AREA_UPDATED] =
124     g_signal_newc ("area_updated",
125                    G_TYPE_FROM_CLASS (object_class),
126                    G_SIGNAL_RUN_LAST,
127                    G_STRUCT_OFFSET (GdkPixbufLoaderClass, area_updated),
128                    NULL, NULL,
129                    gdk_pixbuf_marshal_VOID__INT_INT_INT_INT,
130                    G_TYPE_NONE, 4,
131                    G_TYPE_INT,
132                    G_TYPE_INT,
133                    G_TYPE_INT,
134                    G_TYPE_INT);
135   
136   pixbuf_loader_signals[CLOSED] =
137     g_signal_newc ("closed",
138                    G_TYPE_FROM_CLASS (object_class),
139                    G_SIGNAL_RUN_LAST,
140                    G_STRUCT_OFFSET (GdkPixbufLoaderClass, closed),
141                    NULL, NULL,
142                    gdk_pixbuf_marshal_VOID__VOID,
143                    G_TYPE_NONE, 0);
144 }
145
146 static void
147 gdk_pixbuf_loader_init (GdkPixbufLoader *loader)
148 {
149   GdkPixbufLoaderPrivate *priv;
150   
151   priv = g_new0 (GdkPixbufLoaderPrivate, 1);
152   loader->priv = priv;
153 }
154
155 static void
156 gdk_pixbuf_loader_finalize (GObject *object)
157 {
158   GdkPixbufLoader *loader;
159   GdkPixbufLoaderPrivate *priv = NULL;
160   
161   loader = GDK_PIXBUF_LOADER (object);
162   priv = loader->priv;
163
164   if (!priv->closed)
165     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.");
166   
167   if (priv->animation)
168     gdk_pixbuf_animation_unref (priv->animation);
169   
170   g_free (priv);
171   
172   G_OBJECT_CLASS (parent_class)->finalize (object);
173 }
174
175 static void
176 gdk_pixbuf_loader_prepare (GdkPixbuf          *pixbuf,
177                            GdkPixbufAnimation *anim,
178                            gpointer            loader)
179 {
180   GdkPixbufLoaderPrivate *priv = NULL;
181   
182   priv = GDK_PIXBUF_LOADER (loader)->priv;
183
184   if (anim)
185     g_object_ref (G_OBJECT (anim));
186   else
187     anim = gdk_pixbuf_non_anim_new (pixbuf);
188   
189   priv->animation = anim;
190   
191   g_signal_emit (G_OBJECT (loader), pixbuf_loader_signals[AREA_PREPARED], 0);
192 }
193
194 static void
195 gdk_pixbuf_loader_update (GdkPixbuf *pixbuf,
196                           gint       x,
197                           gint       y,
198                           gint       width,
199                           gint       height,
200                           gpointer   loader)
201 {
202   GdkPixbufLoaderPrivate *priv = NULL;
203   
204   priv = GDK_PIXBUF_LOADER (loader)->priv;
205   
206   g_signal_emit (G_OBJECT (loader),
207                  pixbuf_loader_signals[AREA_UPDATED],
208                  0,
209                  x, y,
210                  /* sanity check in here.  Defend against an errant loader */
211                  MIN (width, gdk_pixbuf_animation_get_width (priv->animation)),
212                  MIN (height, gdk_pixbuf_animation_get_height (priv->animation)));
213 }
214
215 static gint
216 gdk_pixbuf_loader_load_module (GdkPixbufLoader *loader,
217                                const char      *image_type,
218                                GError         **error)
219 {
220   GdkPixbufLoaderPrivate *priv = loader->priv;
221
222   if (image_type)
223     {
224       priv->image_module = _gdk_pixbuf_get_named_module (image_type,
225                                                          error);
226     }
227   else
228     {
229       priv->image_module = _gdk_pixbuf_get_module (priv->header_buf,
230                                                    priv->header_buf_offset,
231                                                    NULL,
232                                                    error);
233     }
234   
235   if (priv->image_module == NULL)
236     return 0;
237   
238   if (priv->image_module->module == NULL)
239     if (!_gdk_pixbuf_load_module (priv->image_module, error))
240       return 0;
241   
242   if (priv->image_module->module == NULL)
243     return 0;
244   
245   if ((priv->image_module->begin_load == NULL) ||
246       (priv->image_module->stop_load == NULL) ||
247       (priv->image_module->load_increment == NULL))
248     {
249       g_set_error (error,
250                    GDK_PIXBUF_ERROR,
251                    GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
252                    _("Incremental loading of image type '%s' is not supported"),
253                    image_type);
254
255       return 0;
256     }
257   
258   priv->context = priv->image_module->begin_load (gdk_pixbuf_loader_prepare,
259                                                   gdk_pixbuf_loader_update,
260                                                   loader,
261                                                   error);
262   
263   if (priv->context == NULL)
264     {
265       /* Defense against broken loaders; DO NOT take this as a GError
266        * example
267        */
268       if (error && *error == NULL)
269         {
270           g_warning ("Bug! loader '%s' didn't set an error on failure",
271                      priv->image_module->module_name);
272           g_set_error (error,
273                        GDK_PIXBUF_ERROR,
274                        GDK_PIXBUF_ERROR_FAILED,
275                        _("Internal error: Image loader module '%s'"
276                          " failed to begin loading an image, but didn't"
277                          " give a reason for the failure"),
278                        priv->image_module->module_name);
279
280         }
281       
282       return 0;
283     }
284   
285   if (priv->header_buf_offset
286       && priv->image_module->load_increment (priv->context, priv->header_buf, priv->header_buf_offset, error))
287     return priv->header_buf_offset;
288   
289   return 0;
290 }
291
292 static int
293 gdk_pixbuf_loader_eat_header_write (GdkPixbufLoader *loader,
294                                     const guchar    *buf,
295                                     gsize            count,
296                                     GError         **error)
297 {
298   gint n_bytes;
299   GdkPixbufLoaderPrivate *priv = loader->priv;
300   
301   n_bytes = MIN(LOADER_HEADER_SIZE - priv->header_buf_offset, count);
302   memcpy (priv->header_buf + priv->header_buf_offset, buf, n_bytes);
303   
304   priv->header_buf_offset += n_bytes;
305   
306   if (priv->header_buf_offset >= LOADER_HEADER_SIZE)
307     {
308       if (gdk_pixbuf_loader_load_module (loader, NULL, error) == 0)
309         return 0;
310     }
311   
312   return n_bytes;
313 }
314
315 /**
316  * gdk_pixbuf_loader_write:
317  * @loader: A pixbuf loader.
318  * @buf: Pointer to image data.
319  * @count: Length of the @buf buffer in bytes.
320  * @error: return location for errors
321  *
322  * This will cause a pixbuf loader to parse the next @count bytes of
323  * an image.  It will return TRUE if the data was loaded successfully,
324  * and FALSE if an error occurred.  In the latter case, the loader
325  * will be closed, and will not accept further writes. If FALSE is
326  * returned, @error will be set to an error from the #GDK_PIXBUF_ERROR
327  * or #G_FILE_ERROR domains.
328  *
329  * Return value: #TRUE if the write was successful, or #FALSE if the loader
330  * cannot parse the buffer.
331  **/
332 gboolean
333 gdk_pixbuf_loader_write (GdkPixbufLoader *loader,
334                          const guchar    *buf,
335                          gsize            count,
336                          GError         **error)
337 {
338   GdkPixbufLoaderPrivate *priv;
339   
340   g_return_val_if_fail (loader != NULL, FALSE);
341   g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), FALSE);
342   
343   g_return_val_if_fail (buf != NULL, FALSE);
344   g_return_val_if_fail (count >= 0, FALSE);
345   
346   priv = loader->priv;
347
348   /* we expect it's not to be closed */
349   g_return_val_if_fail (priv->closed == FALSE, FALSE);
350   
351   if (priv->image_module == NULL)
352     {
353       gint eaten;
354       
355       eaten = gdk_pixbuf_loader_eat_header_write (loader, buf, count, error);
356       if (eaten <= 0)
357         return FALSE;
358       
359       count -= eaten;
360       buf += eaten;
361     }
362   
363   if (count > 0 && priv->image_module->load_increment)
364     {
365       gboolean retval;
366       retval = priv->image_module->load_increment (priv->context, buf, count,
367                                                    error);
368       if (!retval && error && *error == NULL)
369         {
370           /* Fix up busted image loader */
371           g_warning ("Bug! loader '%s' didn't set an error on failure",
372                      priv->image_module->module_name);
373           g_set_error (error,
374                        GDK_PIXBUF_ERROR,
375                        GDK_PIXBUF_ERROR_FAILED,
376                        _("Internal error: Image loader module '%s'"
377                          " failed to begin loading an image, but didn't"
378                          " give a reason for the failure"),
379                        priv->image_module->module_name);
380         }
381
382       return retval;
383     }
384       
385   return TRUE;
386 }
387
388 /**
389  * gdk_pixbuf_loader_new:
390  *
391  * Creates a new pixbuf loader object.
392  *
393  * Return value: A newly-created pixbuf loader.
394  **/
395 GdkPixbufLoader *
396 gdk_pixbuf_loader_new (void)
397 {
398   return g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL);
399 }
400
401 /**
402  * gdk_pixbuf_loader_new_with_type:
403  * @image_type: name of the image format to be loaded with the image
404  * @error: return location for an allocated #GError, or %NULL to ignore errors
405  *
406  * Creates a new pixbuf loader object that always attempts to parse
407  * image data as if it were an image of type @image_type, instead of
408  * identifying the type automatically. Useful if you want an error if
409  * the image isn't the expected type, for loading image formats
410  * that can't be reliably identified by looking at the data, or if
411  * the user manually forces a specific type.
412  *
413  * Return value: A newly-created pixbuf loader.
414  **/
415 GdkPixbufLoader *
416 gdk_pixbuf_loader_new_with_type (const char *image_type,
417                                  GError    **error)
418 {
419   GdkPixbufLoader *retval;
420   GError *tmp;
421   
422   retval = g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL);
423
424   tmp = NULL;
425   gdk_pixbuf_loader_load_module(retval, image_type, &tmp);
426   if (tmp != NULL)
427     {
428       g_propagate_error (error, tmp);
429       g_object_unref (G_OBJECT (retval));
430       return NULL;
431     }
432
433   return retval;
434 }
435
436 /**
437  * gdk_pixbuf_loader_get_pixbuf:
438  * @loader: A pixbuf loader.
439  *
440  * Queries the GdkPixbuf that a pixbuf loader is currently creating.
441  * In general it only makes sense to call this function afer the
442  * "area_prepared" signal has been emitted by the loader; this means
443  * that enough data has been read to know the size of the image that
444  * will be allocated.  If the loader has not received enough data via
445  * gdk_pixbuf_loader_write(), then this function returns %NULL.  The
446  * returned pixbuf will be the same in all future calls to the loader,
447  * so simply calling gdk_pixbuf_ref() should be sufficient to continue
448  * using it.  Additionally, if the loader is an animation, it will
449  * return the "static image" of the animation
450  * (see gdk_pixbuf_animation_get_static_image()).
451  * 
452  * Return value: The #GdkPixbuf that the loader is creating, or %NULL if not
453  * enough data has been read to determine how to create the image buffer.
454  **/
455 GdkPixbuf *
456 gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader)
457 {
458   GdkPixbufLoaderPrivate *priv;
459   
460   g_return_val_if_fail (loader != NULL, NULL);
461   g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL);
462   
463   priv = loader->priv;
464
465   if (priv->animation)
466     return gdk_pixbuf_animation_get_static_image (priv->animation);
467   else
468     return NULL;
469 }
470
471 /**
472  * gdk_pixbuf_loader_get_animation:
473  * @loader: A pixbuf loader
474  *
475  * Queries the GdkPixbufAnimation that a pixbuf loader is currently creating.
476  * In general it only makes sense to call this function afer the "area_prepared"
477  * signal has been emitted by the loader. If the loader doesn't have enough
478  * bytes yet (hasn't emitted the area_prepared signal) this function will return
479  * %NULL.
480  *
481  * Return value: The GdkPixbufAnimation that the loader is loading, or NULL if
482  not enough data has been read to determine the information.
483 **/
484 GdkPixbufAnimation *
485 gdk_pixbuf_loader_get_animation (GdkPixbufLoader *loader)
486 {
487   GdkPixbufLoaderPrivate *priv;
488   
489   g_return_val_if_fail (loader != NULL, NULL);
490   g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL);
491   
492   priv = loader->priv;
493   
494   return priv->animation;
495 }
496
497 /**
498  * gdk_pixbuf_loader_close:
499  * @loader: A pixbuf loader.
500  * @error: return location for a #GError, or %NULL to ignore errors
501  *
502  * Informs a pixbuf loader that no further writes with
503  * gdk_pixbuf_loader_write() will occur, so that it can free its
504  * internal loading structures. Also, tries to parse any data that
505  * hasn't yet been parsed; if the remaining data is partial or
506  * corrupt, an error will be returned.  If FALSE is returned, @error
507  * will be set to an error from the #GDK_PIXBUF_ERROR or #G_FILE_ERROR
508  * domains. If you're just cancelling a load rather than expecting it
509  * to be finished, passing %NULL for @error to ignore it is
510  * reasonable.
511  *
512  * Returns: %TRUE if all image data written so far was successfully
513             passed out via the update_area signal
514  **/
515 gboolean
516 gdk_pixbuf_loader_close (GdkPixbufLoader *loader,
517                          GError         **error)
518 {
519   GdkPixbufLoaderPrivate *priv;
520   gboolean retval = TRUE;
521   
522   g_return_val_if_fail (loader != NULL, TRUE);
523   g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), TRUE);
524   
525   priv = loader->priv;
526   
527   /* we expect it's not closed */
528   g_return_val_if_fail (priv->closed == FALSE, TRUE);
529   
530   /* We have less the 128 bytes in the image.  Flush it, and keep going. */
531   if (priv->image_module == NULL)
532     gdk_pixbuf_loader_load_module (loader, NULL, NULL);
533   
534   if (priv->image_module && priv->image_module->stop_load && priv->context)
535     retval = priv->image_module->stop_load (priv->context, error);
536   
537   priv->closed = TRUE;
538   
539   g_signal_emit (G_OBJECT (loader), pixbuf_loader_signals[CLOSED], 0);
540
541   return retval;
542 }