gdk-pixbuf-loader.c 14.8 KB
Newer Older
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
/* GdkPixbuf library - Main header file
 *
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Authors: Mark Crichton <crichton@gimp.org>
 *          Miguel de Icaza <miguel@gnu.org>
 *          Federico Mena-Quintero <federico@gimp.org>
 *          Jonathan Blandford <jrb@redhat.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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.
 */

26
#include "gdk-pixbuf-private.h"
27
28
#include "gdk-pixbuf-loader.h"
#include "gdk-pixbuf-io.h"
29

Owen Taylor's avatar
Owen Taylor committed
30
31
#include "gtksignal.h"

32
enum {
33
34
35
36
37
38
  AREA_UPDATED,
  AREA_PREPARED,
  FRAME_DONE,
  ANIMATION_DONE,
  CLOSED,
  LAST_SIGNAL
39
};
40
41
42
43


static void gdk_pixbuf_loader_class_init    (GdkPixbufLoaderClass   *klass);
static void gdk_pixbuf_loader_init          (GdkPixbufLoader        *loader);
44
static void gdk_pixbuf_loader_destroy       (GtkObject              *loader);
Owen Taylor's avatar
Owen Taylor committed
45
static void gdk_pixbuf_loader_finalize      (GObject                *loader);
46

47
48
static gpointer parent_class = NULL;
static guint    pixbuf_loader_signals[LAST_SIGNAL] = { 0 };
49

50

51
/* Internal data */
52
53
54

#define LOADER_HEADER_SIZE 128

55
56
57
58
59
60
61
62
63
typedef struct
{
  GdkPixbuf *pixbuf;
  GdkPixbufAnimation *animation;
  gboolean closed;
  guchar header_buf[LOADER_HEADER_SIZE];
  gint header_buf_offset;
  GdkPixbufModule *image_module;
  gpointer context;
64
} GdkPixbufLoaderPrivate;
65
66
67


/* our marshaller */
68
69
70
typedef void (*GtkSignal_NONE__INT_INT_INT_INT) (GtkObject *object,
						 gint arg1, gint arg2, gint arg3, gint arg4,
						 gpointer user_data);
71
72
static void
gtk_marshal_NONE__INT_INT_INT_INT (GtkObject *object, GtkSignalFunc func, gpointer func_data,
73
74
				   GtkArg * args)
{
75
76
77
78
79
80
81
82
83
  GtkSignal_NONE__INT_INT_INT_INT rfunc;
  
  rfunc = (GtkSignal_NONE__INT_INT_INT_INT) func;
  (*rfunc) (object,
	    GTK_VALUE_INT (args[0]),
	    GTK_VALUE_INT (args[1]),
	    GTK_VALUE_INT (args[2]),
	    GTK_VALUE_INT (args[3]),
	    func_data);
84
}
85

86
87
88

/**
 * gdk_pixbuf_loader_get_type:
89
90
 * @void:
 *
Arturo Espinosa's avatar
Arturo Espinosa committed
91
 * Registers the #GdkPixubfLoader class if necessary, and returns the type ID
92
 * associated to it.
93
 *
Arturo Espinosa's avatar
Arturo Espinosa committed
94
 * Return value: The type ID of the #GdkPixbufLoader class.
95
 **/
96
97
98
GtkType
gdk_pixbuf_loader_get_type (void)
{
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
  static GtkType loader_type = 0;
  
  if (!loader_type)
    {
      static const GtkTypeInfo loader_info = {
	"GdkPixbufLoader",
	sizeof (GdkPixbufLoader),
	sizeof (GdkPixbufLoaderClass),
	(GtkClassInitFunc) gdk_pixbuf_loader_class_init,
	(GtkObjectInitFunc) gdk_pixbuf_loader_init,
	/* reserved_1 */ NULL,
	/* reserved_2 */ NULL,
	(GtkClassInitFunc) NULL,
      };
      
      loader_type = gtk_type_unique (GTK_TYPE_OBJECT, &loader_info);
    }
  
  return loader_type;
118
119
120
}

static void
121
gdk_pixbuf_loader_class_init (GdkPixbufLoaderClass *class)
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
  GObjectClass *gobject_class;
  GtkObjectClass *object_class;
  
  object_class = (GtkObjectClass *) class;
  gobject_class = (GObjectClass *) class;
  
  parent_class = gtk_type_class (GTK_TYPE_OBJECT);
  
  pixbuf_loader_signals[AREA_PREPARED] =
    gtk_signal_new ("area_prepared",
		    GTK_RUN_LAST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GdkPixbufLoaderClass, area_prepared),
		    gtk_marshal_NONE__NONE,
		    GTK_TYPE_NONE, 0);
  
  pixbuf_loader_signals[AREA_UPDATED] =
    gtk_signal_new ("area_updated",
		    GTK_RUN_LAST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GdkPixbufLoaderClass, area_updated),
		    gtk_marshal_NONE__INT_INT_INT_INT,
		    GTK_TYPE_NONE, 4,
		    GTK_TYPE_INT,
		    GTK_TYPE_INT,
		    GTK_TYPE_INT,
		    GTK_TYPE_INT);
  
  pixbuf_loader_signals[FRAME_DONE] =
    gtk_signal_new ("frame_done",
		    GTK_RUN_LAST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GdkPixbufLoaderClass, frame_done),
		    gtk_marshal_NONE__POINTER,
		    GTK_TYPE_NONE, 1,
		    GTK_TYPE_POINTER);
  
  pixbuf_loader_signals[ANIMATION_DONE] =
    gtk_signal_new ("animation_done",
		    GTK_RUN_LAST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GdkPixbufLoaderClass, animation_done),
		    gtk_marshal_NONE__NONE,
		    GTK_TYPE_NONE, 0);
  
  pixbuf_loader_signals[CLOSED] =
    gtk_signal_new ("closed",
		    GTK_RUN_LAST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GdkPixbufLoaderClass, closed),
		    gtk_marshal_NONE__NONE,
		    GTK_TYPE_NONE, 0);
  
  gtk_object_class_add_signals (object_class, pixbuf_loader_signals, LAST_SIGNAL);
  
  object_class->destroy = gdk_pixbuf_loader_destroy;
  gobject_class->finalize = gdk_pixbuf_loader_finalize;
180
181
182
183
184
}

static void
gdk_pixbuf_loader_init (GdkPixbufLoader *loader)
{
185
186
187
188
  GdkPixbufLoaderPrivate *priv;
  
  priv = g_new0 (GdkPixbufLoaderPrivate, 1);
  loader->private = priv;
189
190
191
}

static void
Arturo Espinosa's avatar
Arturo Espinosa committed
192
gdk_pixbuf_loader_destroy (GtkObject *object)
193
{
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
  GdkPixbufLoader *loader;
  GdkPixbufLoaderPrivate *priv = NULL;
  
  g_return_if_fail (object != NULL);
  g_return_if_fail (GDK_IS_PIXBUF_LOADER (object));
  
  loader = GDK_PIXBUF_LOADER (object);
  priv = loader->private;
  
  if (!priv->closed)
    gdk_pixbuf_loader_close (loader);
  
  if (priv->animation)
    gdk_pixbuf_animation_unref (priv->animation);
  if (priv->pixbuf)
    gdk_pixbuf_unref (priv->pixbuf);
  
  if (GTK_OBJECT_CLASS (parent_class)->destroy)
    GTK_OBJECT_CLASS (parent_class)->destroy (object);
213
214
215
}

static void
Owen Taylor's avatar
Owen Taylor committed
216
gdk_pixbuf_loader_finalize (GObject *object)
217
{
218
219
220
221
222
223
224
225
226
227
  GdkPixbufLoader *loader;
  GdkPixbufLoaderPrivate *priv = NULL;
  
  loader = GDK_PIXBUF_LOADER (object);
  priv = loader->private;
  
  g_free (priv);
  
  if (G_OBJECT_CLASS (parent_class)->finalize)
    G_OBJECT_CLASS (parent_class)->finalize (object);
228
229
}

230
static void
231
232
gdk_pixbuf_loader_prepare (GdkPixbuf *pixbuf,
			   gpointer   loader)
233
{
234
235
236
237
238
239
240
241
242
  GdkPixbufLoaderPrivate *priv = NULL;
  
  priv = GDK_PIXBUF_LOADER (loader)->private;
  gdk_pixbuf_ref (pixbuf);

  g_assert (priv->pixbuf == NULL);
  
  priv->pixbuf = pixbuf;
  gtk_signal_emit (GTK_OBJECT (loader), pixbuf_loader_signals[AREA_PREPARED]);
243
244
}

245
static void
246
247
248
249
250
251
gdk_pixbuf_loader_update (GdkPixbuf *pixbuf,
			  guint      x,
			  guint      y,
			  guint      width,
			  guint      height,
			  gpointer   loader)
252
{
253
254
255
256
257
258
259
260
261
262
  GdkPixbufLoaderPrivate *priv = NULL;
  
  priv = GDK_PIXBUF_LOADER (loader)->private;
  
  gtk_signal_emit (GTK_OBJECT (loader),
		   pixbuf_loader_signals[AREA_UPDATED],
		   x, y,
		   /* sanity check in here.  Defend against an errant loader */
		   MIN (width, gdk_pixbuf_get_width (priv->pixbuf)),
		   MIN (height, gdk_pixbuf_get_height (priv->pixbuf)));
263
264
}

265
static void
266
267
gdk_pixbuf_loader_frame_done (GdkPixbufFrame *frame,
			      gpointer        loader)
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
  GdkPixbufLoaderPrivate *priv = NULL;
  
  priv = GDK_PIXBUF_LOADER (loader)->private;
  
  priv->pixbuf = NULL;
  
  if (priv->animation == NULL)
    {
      priv->animation = GDK_PIXBUF_ANIMATION (g_type_create_instance (GDK_TYPE_PIXBUF_ANIMATION));
      
      priv->animation->n_frames = 0;
      priv->animation->width  = gdk_pixbuf_get_width  (frame->pixbuf) + frame->x_offset;
      priv->animation->height = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset;
    }
  else
    {
      int w, h;
      
      /* update bbox size */
      w = gdk_pixbuf_get_width (frame->pixbuf) + frame->x_offset;
      h = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset;
      
      if (w > priv->animation->width) {
	priv->animation->width = w;
      }
      if (h > priv->animation->height) {
	priv->animation->height = h;
      }
    }
  
  priv->animation->frames = g_list_append (priv->animation->frames, frame);
  priv->animation->n_frames++;
  gtk_signal_emit (GTK_OBJECT (loader),
		   pixbuf_loader_signals[FRAME_DONE],
		   frame);
304
305
306
}

static void
307
308
gdk_pixbuf_loader_animation_done (GdkPixbuf *pixbuf,
				  gpointer   loader)
309
{
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  GdkPixbufLoaderPrivate *priv = NULL;
  GdkPixbufFrame    *frame;
  GList *current = NULL;
  gint h, w;
  
  priv = GDK_PIXBUF_LOADER (loader)->private;
  priv->pixbuf = NULL;
  
  current = gdk_pixbuf_animation_get_frames (priv->animation);
  
  while (current)
    {
      frame = (GdkPixbufFrame *) current->data;
      
      /* update bbox size */
      w = gdk_pixbuf_get_width (frame->pixbuf) + frame->x_offset;
      h = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset;
      
      if (w > priv->animation->width)
	priv->animation->width = w;
      if (h > priv->animation->height)
	priv->animation->height = h;
      current = current->next;
    }
  
  gtk_signal_emit (GTK_OBJECT (loader), pixbuf_loader_signals[ANIMATION_DONE]);
336
337
}

338
339
/**
 * gdk_pixbuf_loader_new:
340
 *
341
 * Creates a new pixbuf loader object.
342
 *
343
344
345
 * Return value: A newly-created pixbuf loader.
 **/
GdkPixbufLoader *
346
347
gdk_pixbuf_loader_new (void)
{
348
  return g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL);
349
350
}

351
352
static gint
gdk_pixbuf_loader_load_module (GdkPixbufLoader *loader)
353
{
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
  GdkPixbufLoaderPrivate *priv = loader->private;
  
  priv->image_module = gdk_pixbuf_get_module (priv->header_buf, priv->header_buf_offset);
  
  if (priv->image_module == NULL)
    return 0;
  
  if (priv->image_module->module == NULL)
    gdk_pixbuf_load_module (priv->image_module);
  
  if (priv->image_module->module == NULL)
    return 0;
  
  if ((priv->image_module->begin_load == NULL) ||
      (priv->image_module->stop_load == NULL) ||
      (priv->image_module->load_increment == NULL))
    {
      g_warning (G_STRLOC ": module %s does not support incremental loading.\n",
		 priv->image_module->module_name);
      return 0;
    }
  
  priv->context = priv->image_module->begin_load (gdk_pixbuf_loader_prepare,
						  gdk_pixbuf_loader_update,
						  gdk_pixbuf_loader_frame_done,
						  gdk_pixbuf_loader_animation_done,
						  loader);
  
  if (priv->context == NULL)
    {
      g_warning (G_STRLOC ": Failed to begin progressive load");
      return 0;
    }
  
  if (priv->image_module->load_increment (priv->context, priv->header_buf, priv->header_buf_offset))
    return priv->header_buf_offset;
  
  return 0;
392
393
394
}

static int
395
396
397
gdk_pixbuf_loader_eat_header_write (GdkPixbufLoader *loader,
				    const guchar    *buf,
				    gsize            count)
398
{
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  gint n_bytes;
  GdkPixbufLoaderPrivate *priv = loader->private;
  
  n_bytes = MIN(LOADER_HEADER_SIZE - priv->header_buf_offset, count);
  memcpy (priv->header_buf + priv->header_buf_offset, buf, n_bytes);
  
  priv->header_buf_offset += n_bytes;
  
  if (priv->header_buf_offset >= LOADER_HEADER_SIZE)
    {
      if (gdk_pixbuf_loader_load_module (loader) == 0)
	return 0;
    }
  
  return n_bytes;
414
415
}

416
417
/**
 * gdk_pixbuf_loader_write:
418
 * @loader: A pixbuf loader.
Federico Mena Quintero's avatar
Federico Mena Quintero committed
419
420
 * @buf: Pointer to image data.
 * @count: Length of the @buf buffer in bytes.
421
 *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
422
423
424
425
 * This will cause a pixbuf loader to parse the next @count bytes of an image.
 * It will return TRUE if the data was loaded successfully, and FALSE if an
 * error occurred.  In the latter case, the loader will be closed, and will not
 * accept further writes.
426
 *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
427
428
 * Return value: #TRUE if the write was successful, or #FALSE if the loader
 * cannot parse the buffer.
429
430
 **/
gboolean
431
432
433
gdk_pixbuf_loader_write (GdkPixbufLoader *loader,
			 const guchar    *buf,
			 gsize            count)
434
{
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  GdkPixbufLoaderPrivate *priv;
  
  g_return_val_if_fail (loader != NULL, FALSE);
  g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), FALSE);
  
  g_return_val_if_fail (buf != NULL, FALSE);
  g_return_val_if_fail (count >= 0, FALSE);
  
  priv = loader->private;
  
  /* we expect it's not to be closed */
  g_return_val_if_fail (priv->closed == FALSE, FALSE);
  
  if (priv->image_module == NULL)
    {
      gint eaten;
      
      eaten = gdk_pixbuf_loader_eat_header_write(loader, buf, count);
      if (eaten <= 0)
	return FALSE;
      
      count -= eaten;
      buf += eaten;
    }
  
  if (count > 0 && priv->image_module->load_increment)
    return priv->image_module->load_increment (priv->context, buf, count);
  
  return TRUE;
464
465
466
467
}

/**
 * gdk_pixbuf_loader_get_pixbuf:
468
 * @loader: A pixbuf loader.
469
 *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
470
471
472
473
474
475
476
 * Queries the GdkPixbuf that a pixbuf loader is currently creating.  In general
 * it only makes sense to call this function afer the "area_prepared" signal has
 * been emitted by the loader; this means that enough data has been read to know
 * the size of the image that will be allocated.  If the loader has not received
 * enough data via gdk_pixbuf_loader_write(), then this function returns NULL.
 * The returned pixbuf will be the same in all future calls to the loader, so
 * simply calling gdk_pixbuf_ref() should be sufficient to continue using it.
477
 *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
478
479
 * Return value: The GdkPixbuf that the loader is creating, or NULL if not
 * enough data has been read to determine how to create the image buffer.
480
481
482
483
 **/
GdkPixbuf *
gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader)
{
484
485
486
487
488
489
490
491
  GdkPixbufLoaderPrivate *priv;
  
  g_return_val_if_fail (loader != NULL, NULL);
  g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL);
  
  priv = loader->private;
  
  return priv->pixbuf;
492
493
}

494
495
496
/**
 * gdk_pixbuf_loader_get_animation:
 * @loader: A pixbuf loader
497
 *
498
499
 * Queries the GdkPixbufAnimation that a pixbuf loader is currently creating.
 * In general it only makes sense to call this function afer the "area_prepared"
500
501
502
 * signal has been emitted by the loader.  If the image is not an animation,
 * then it will return NULL.
 *
503
504
 * Return value: The GdkPixbufAnimation that the loader is loading, or NULL if
 not enough data has been read to determine the information.
505
**/
506
507
508
GdkPixbufAnimation *
gdk_pixbuf_loader_get_animation (GdkPixbufLoader *loader)
{
509
510
511
512
513
514
515
516
  GdkPixbufLoaderPrivate *priv;
  
  g_return_val_if_fail (loader != NULL, NULL);
  g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL);
  
  priv = loader->private;
  
  return priv->animation;
517
518
}

519
520
/**
 * gdk_pixbuf_loader_close:
521
 * @loader: A pixbuf loader.
522
 *
Federico Mena Quintero's avatar
Federico Mena Quintero committed
523
524
 * Informs a pixbuf loader that no further writes with gdk_pixbuf_load_write()
 * will occur, so that it can free its internal loading structures.
525
526
527
528
 **/
void
gdk_pixbuf_loader_close (GdkPixbufLoader *loader)
{
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
  GdkPixbufLoaderPrivate *priv;
  
  g_return_if_fail (loader != NULL);
  g_return_if_fail (GDK_IS_PIXBUF_LOADER (loader));
  
  priv = loader->private;
  
  /* we expect it's not closed */
  g_return_if_fail (priv->closed == FALSE);
  
  /* We have less the 128 bytes in the image.  Flush it, and keep going. */
  if (priv->image_module == NULL)
    gdk_pixbuf_loader_load_module (loader);
  
  if (priv->image_module && priv->image_module->stop_load)
    priv->image_module->stop_load (priv->context);
  
  priv->closed = TRUE;
  
  gtk_signal_emit (GTK_OBJECT (loader), pixbuf_loader_signals[CLOSED]);
549
}