gdk-pixbuf-loader.c 19.3 KB
Newer Older
1
/* GdkPixbuf library - Progressive loader object
2 3 4 5 6 7 8 9 10
 *
 * 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
11
 * modify it under the terms of the GNU Lesser General Public
12 13 14 15 16 17
 * 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
18
 * Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public
21 22 23 24 25
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

Owen Taylor's avatar
Owen Taylor committed
26 27
#include <string.h>

28
#include "gdk-pixbuf-private.h"
29 30
#include "gdk-pixbuf-loader.h"
#include "gdk-pixbuf-io.h"
31
#include "gdk-pixbuf-marshal.h"
32

33
enum {
34
  SIZE_PREPARED,
35
  AREA_PREPARED,
36
  AREA_UPDATED,
37 38
  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_finalize      (GObject                *loader);
45

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

49

50
/* Internal data */
51 52 53

#define LOADER_HEADER_SIZE 128

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


69 70
/**
 * gdk_pixbuf_loader_get_type:
71 72
 * @void:
 *
Matthias Clasen's avatar
Matthias Clasen committed
73
 * Registers the #GdkPixbufLoader class if necessary, and returns the type ID
74
 * associated to it.
75
 *
76
 * Return value: The type ID of the #GdkPixbufLoader class.
77
 **/
78
GType
79 80
gdk_pixbuf_loader_get_type (void)
{
81
  static GType loader_type = 0;
82 83 84
  
  if (!loader_type)
    {
85 86 87 88 89 90 91 92 93 94
      static const GTypeInfo loader_info = {
        sizeof (GdkPixbufLoaderClass),
        (GBaseInitFunc) NULL,
        (GBaseFinalizeFunc) NULL,
        (GClassInitFunc) gdk_pixbuf_loader_class_init,
        NULL,           /* class_finalize */
        NULL,           /* class_data */
        sizeof (GdkPixbufLoader),
        0,              /* n_preallocs */
        (GInstanceInitFunc) gdk_pixbuf_loader_init
95 96
      };
      
97 98 99 100
      loader_type = g_type_register_static (G_TYPE_OBJECT,
                                            "GdkPixbufLoader",
                                            &loader_info,
                                            0);
101 102 103
    }
  
  return loader_type;
104 105 106
}

static void
107
gdk_pixbuf_loader_class_init (GdkPixbufLoaderClass *class)
108
{
109
  GObjectClass *object_class;
110
  
111
  object_class = (GObjectClass *) class;
112
  
113
  parent_class = g_type_class_peek_parent (class);
114
  
115
  object_class->finalize = gdk_pixbuf_loader_finalize;
116

117 118 119 120 121 122 123 124 125 126 127
  pixbuf_loader_signals[SIZE_PREPARED] =
    g_signal_new ("size_prepared",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GdkPixbufLoaderClass, size_prepared),
                  NULL, NULL,
                  gdk_pixbuf_marshal_VOID__INT_INT,
                  G_TYPE_NONE, 2, 
		  G_TYPE_INT,
		  G_TYPE_INT);
  
128
  pixbuf_loader_signals[AREA_PREPARED] =
129 130 131 132 133 134 135
    g_signal_new ("area_prepared",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GdkPixbufLoaderClass, area_prepared),
                  NULL, NULL,
                  gdk_pixbuf_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
136 137
  
  pixbuf_loader_signals[AREA_UPDATED] =
138 139 140 141 142 143 144 145 146 147 148
    g_signal_new ("area_updated",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GdkPixbufLoaderClass, area_updated),
                  NULL, NULL,
                  gdk_pixbuf_marshal_VOID__INT_INT_INT_INT,
                  G_TYPE_NONE, 4,
                  G_TYPE_INT,
                  G_TYPE_INT,
                  G_TYPE_INT,
                  G_TYPE_INT);
149 150
  
  pixbuf_loader_signals[CLOSED] =
151 152 153 154 155 156 157
    g_signal_new ("closed",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GdkPixbufLoaderClass, closed),
                  NULL, NULL,
                  gdk_pixbuf_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
158 159 160 161 162
}

static void
gdk_pixbuf_loader_init (GdkPixbufLoader *loader)
{
163 164 165
  GdkPixbufLoaderPrivate *priv;
  
  priv = g_new0 (GdkPixbufLoaderPrivate, 1);
166
  loader->priv = priv;
167 168 169
}

static void
170
gdk_pixbuf_loader_finalize (GObject *object)
171
{
172 173 174 175
  GdkPixbufLoader *loader;
  GdkPixbufLoaderPrivate *priv = NULL;
  
  loader = GDK_PIXBUF_LOADER (object);
176
  priv = loader->priv;
177

178
  if (!priv->closed)
179
    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.");
180 181
  
  if (priv->animation)
182
    g_object_unref (priv->animation);
183 184 185
  
  g_free (priv);
  
186
  G_OBJECT_CLASS (parent_class)->finalize (object);
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
/**
 * gdk_pixbuf_loader_set_size:
 * @loader: A pixbuf loader.
 * @width: The desired width of the image being loaded.
 * @height: The desired height of the image being loaded.
 *
 * Causes the image to be scaled while it is loaded. The desired
 * image size can be determined relative to the original size of
 * the image by calling gdk_pixbuf_loader_set_size() from a
 * signal handler for the ::size_prepared signal.
 *
 * Attempts to set the desired image size  are ignored after the 
 * emission of the ::size_prepared signal.
 */
void 
gdk_pixbuf_loader_set_size (GdkPixbufLoader *loader,
			    gint             width,
			    gint             height)
{
  GdkPixbufLoaderPrivate *priv = GDK_PIXBUF_LOADER (loader)->priv;
  g_return_if_fail (width > 0 && height > 0);

  if (!priv->size_fixed) {
    priv->width = width;
    priv->height = height;
  }
}

static void
gdk_pixbuf_loader_size_func (gint *width, gint *height, gpointer loader)
{
  GdkPixbufLoaderPrivate *priv = GDK_PIXBUF_LOADER (loader)->priv;

  /* allow calling gdk_pixbuf_loader_set_size() before the signal */
  if (priv->width == 0 && priv->height == 0) {
    priv->width = *width;
    priv->height = *height;
  }

  g_signal_emit (loader, pixbuf_loader_signals[SIZE_PREPARED], 0, *width, *height);
  priv->size_fixed = TRUE;

  *width = priv->width;
  *height = priv->height;
}

235
static void
Havoc Pennington's avatar
Havoc Pennington committed
236 237 238
gdk_pixbuf_loader_prepare (GdkPixbuf          *pixbuf,
                           GdkPixbufAnimation *anim,
			   gpointer            loader)
239
{
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
  GdkPixbufLoaderPrivate *priv = GDK_PIXBUF_LOADER (loader)->priv;
  g_return_if_fail (pixbuf != NULL);

  if (!priv->size_fixed) {
    /* Defend against lazy loaders which don't call size_func */
    gint width = gdk_pixbuf_get_width (pixbuf);
    gint height = gdk_pixbuf_get_height (pixbuf);

    gdk_pixbuf_loader_size_func (&width, &height, loader);
  }

  priv->needs_scale = FALSE;
  if (priv->width > 0 && priv->height > 0 &&
      (priv->width != gdk_pixbuf_get_width (pixbuf) ||
       priv->height != gdk_pixbuf_get_height (pixbuf)))
    priv->needs_scale = TRUE;
256

Havoc Pennington's avatar
Havoc Pennington committed
257
  if (anim)
258
    g_object_ref (anim);
Havoc Pennington's avatar
Havoc Pennington committed
259 260 261 262
  else
    anim = gdk_pixbuf_non_anim_new (pixbuf);
  
  priv->animation = anim;
263
  
264 265
  if (!priv->needs_scale)
    g_signal_emit (loader, pixbuf_loader_signals[AREA_PREPARED], 0);
266 267
}

268
static void
269
gdk_pixbuf_loader_update (GdkPixbuf *pixbuf,
Havoc Pennington's avatar
Havoc Pennington committed
270 271 272 273
			  gint       x,
			  gint       y,
			  gint       width,
			  gint       height,
274
			  gpointer   loader)
275
{
276 277 278 279 280 281 282 283 284 285
  GdkPixbufLoaderPrivate *priv = GDK_PIXBUF_LOADER (loader)->priv;
  
  if (!priv->needs_scale)
    g_signal_emit (loader,
		   pixbuf_loader_signals[AREA_UPDATED],
		   0,
		   x, y,
		   /* sanity check in here.  Defend against an errant loader */
		   MIN (width, gdk_pixbuf_animation_get_width (priv->animation)),
		   MIN (height, gdk_pixbuf_animation_get_height (priv->animation)));
286 287
}

288
static gint
Havoc Pennington's avatar
Havoc Pennington committed
289 290 291
gdk_pixbuf_loader_load_module (GdkPixbufLoader *loader,
                               const char      *image_type,
                               GError         **error)
292
{
293
  GdkPixbufLoaderPrivate *priv = loader->priv;
294

Havoc Pennington's avatar
Havoc Pennington committed
295 296
  if (image_type)
    {
297 298
      priv->image_module = _gdk_pixbuf_get_named_module (image_type,
                                                         error);
Havoc Pennington's avatar
Havoc Pennington committed
299
    }
300
  else
Havoc Pennington's avatar
Havoc Pennington committed
301
    {
302 303 304 305
      priv->image_module = _gdk_pixbuf_get_module (priv->header_buf,
                                                   priv->header_buf_offset,
                                                   NULL,
                                                   error);
Havoc Pennington's avatar
Havoc Pennington committed
306
    }
307 308 309 310 311
  
  if (priv->image_module == NULL)
    return 0;
  
  if (priv->image_module->module == NULL)
312
    if (!_gdk_pixbuf_load_module (priv->image_module, error))
Havoc Pennington's avatar
Havoc Pennington committed
313
      return 0;
314 315 316 317 318 319 320 321
  
  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))
    {
Havoc Pennington's avatar
Havoc Pennington committed
322 323 324 325
      g_set_error (error,
                   GDK_PIXBUF_ERROR,
                   GDK_PIXBUF_ERROR_UNSUPPORTED_OPERATION,
                   _("Incremental loading of image type '%s' is not supported"),
326
                   priv->image_module->module_name);
Havoc Pennington's avatar
Havoc Pennington committed
327

328 329
      return 0;
    }
330

331 332 333 334 335
    priv->context = priv->image_module->begin_load (gdk_pixbuf_loader_size_func,
						    gdk_pixbuf_loader_prepare,
						    gdk_pixbuf_loader_update,
						    loader,
						    error);
336 337 338
  
  if (priv->context == NULL)
    {
Havoc Pennington's avatar
Havoc Pennington committed
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
      /* Defense against broken loaders; DO NOT take this as a GError
       * example
       */
      if (error && *error == NULL)
        {
          g_warning ("Bug! loader '%s' didn't set an error on failure",
                     priv->image_module->module_name);
          g_set_error (error,
                       GDK_PIXBUF_ERROR,
                       GDK_PIXBUF_ERROR_FAILED,
                       _("Internal error: Image loader module '%s'"
                         " failed to begin loading an image, but didn't"
                         " give a reason for the failure"),
                       priv->image_module->module_name);

        }
      
356 357 358
      return 0;
    }
  
359
  if (priv->header_buf_offset
Havoc Pennington's avatar
Havoc Pennington committed
360
      && priv->image_module->load_increment (priv->context, priv->header_buf, priv->header_buf_offset, error))
361 362 363
    return priv->header_buf_offset;
  
  return 0;
364 365 366
}

static int
367 368
gdk_pixbuf_loader_eat_header_write (GdkPixbufLoader *loader,
				    const guchar    *buf,
Havoc Pennington's avatar
Havoc Pennington committed
369 370
				    gsize            count,
                                    GError         **error)
371
{
372
  gint n_bytes;
373
  GdkPixbufLoaderPrivate *priv = loader->priv;
374 375 376 377 378 379 380 381
  
  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)
    {
Havoc Pennington's avatar
Havoc Pennington committed
382
      if (gdk_pixbuf_loader_load_module (loader, NULL, error) == 0)
383 384 385 386
	return 0;
    }
  
  return n_bytes;
387 388
}

389 390
/**
 * gdk_pixbuf_loader_write:
391
 * @loader: A pixbuf loader.
Federico Mena Quintero's avatar
Federico Mena Quintero committed
392 393
 * @buf: Pointer to image data.
 * @count: Length of the @buf buffer in bytes.
Havoc Pennington's avatar
Havoc Pennington committed
394
 * @error: return location for errors
395
 *
Havoc Pennington's avatar
Havoc Pennington committed
396
 * This will cause a pixbuf loader to parse the next @count bytes of
Matthias Clasen's avatar
Matthias Clasen committed
397 398 399
 * 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. If %FALSE is
Havoc Pennington's avatar
Havoc Pennington committed
400
 * returned, @error will be set to an error from the #GDK_PIXBUF_ERROR
401
 * or #G_FILE_ERROR domains.
402
 *
Matthias Clasen's avatar
Matthias Clasen committed
403
 * Return value: %TRUE if the write was successful, or %FALSE if the loader
Federico Mena Quintero's avatar
Federico Mena Quintero committed
404
 * cannot parse the buffer.
405 406
 **/
gboolean
407 408
gdk_pixbuf_loader_write (GdkPixbufLoader *loader,
			 const guchar    *buf,
Havoc Pennington's avatar
Havoc Pennington committed
409 410
			 gsize            count,
                         GError         **error)
411
{
412 413 414 415 416 417 418 419
  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);
  
420
  priv = loader->priv;
421

422 423 424 425 426 427 428
  /* we expect it's not to be closed */
  g_return_val_if_fail (priv->closed == FALSE, FALSE);
  
  if (priv->image_module == NULL)
    {
      gint eaten;
      
Havoc Pennington's avatar
Havoc Pennington committed
429
      eaten = gdk_pixbuf_loader_eat_header_write (loader, buf, count, error);
430 431 432 433 434 435 436 437
      if (eaten <= 0)
	return FALSE;
      
      count -= eaten;
      buf += eaten;
    }
  
  if (count > 0 && priv->image_module->load_increment)
Havoc Pennington's avatar
Havoc Pennington committed
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
    {
      gboolean retval;
      retval = priv->image_module->load_increment (priv->context, buf, count,
                                                   error);
      if (!retval && error && *error == NULL)
        {
          /* Fix up busted image loader */
          g_warning ("Bug! loader '%s' didn't set an error on failure",
                     priv->image_module->module_name);
          g_set_error (error,
                       GDK_PIXBUF_ERROR,
                       GDK_PIXBUF_ERROR_FAILED,
                       _("Internal error: Image loader module '%s'"
                         " failed to begin loading an image, but didn't"
                         " give a reason for the failure"),
                       priv->image_module->module_name);
        }

      return retval;
    }
      
459
  return TRUE;
460 461
}

462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
/**
 * gdk_pixbuf_loader_new:
 *
 * Creates a new pixbuf loader object.
 *
 * Return value: A newly-created pixbuf loader.
 **/
GdkPixbufLoader *
gdk_pixbuf_loader_new (void)
{
  return g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL);
}

/**
 * gdk_pixbuf_loader_new_with_type:
Havoc Pennington's avatar
Havoc Pennington committed
477 478
 * @image_type: name of the image format to be loaded with the image
 * @error: return location for an allocated #GError, or %NULL to ignore errors
479
 *
Havoc Pennington's avatar
Havoc Pennington committed
480 481 482 483 484 485
 * Creates a new pixbuf loader object that always attempts to parse
 * image data as if it were an image of type @image_type, instead of
 * identifying the type automatically. Useful if you want an error if
 * the image isn't the expected type, for loading image formats
 * that can't be reliably identified by looking at the data, or if
 * the user manually forces a specific type.
486 487 488 489
 *
 * Return value: A newly-created pixbuf loader.
 **/
GdkPixbufLoader *
Havoc Pennington's avatar
Havoc Pennington committed
490 491
gdk_pixbuf_loader_new_with_type (const char *image_type,
                                 GError    **error)
492 493
{
  GdkPixbufLoader *retval;
Havoc Pennington's avatar
Havoc Pennington committed
494 495
  GError *tmp;
  
496 497
  retval = g_object_new (GDK_TYPE_PIXBUF_LOADER, NULL);

Havoc Pennington's avatar
Havoc Pennington committed
498 499 500 501 502
  tmp = NULL;
  gdk_pixbuf_loader_load_module(retval, image_type, &tmp);
  if (tmp != NULL)
    {
      g_propagate_error (error, tmp);
503
      g_object_unref (retval);
Havoc Pennington's avatar
Havoc Pennington committed
504 505
      return NULL;
    }
506 507 508 509

  return retval;
}

510 511
/**
 * gdk_pixbuf_loader_get_pixbuf:
512
 * @loader: A pixbuf loader.
513
 *
Matthias Clasen's avatar
Matthias Clasen committed
514
 * Queries the #GdkPixbuf that a pixbuf loader is currently creating.
515
 * In general it only makes sense to call this function after the
Havoc Pennington's avatar
Havoc Pennington committed
516 517 518
 * "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
Havoc Pennington's avatar
Havoc Pennington committed
519
 * gdk_pixbuf_loader_write(), then this function returns %NULL.  The
Havoc Pennington's avatar
Havoc Pennington committed
520
 * returned pixbuf will be the same in all future calls to the loader,
521
 * so simply calling g_object_ref() should be sufficient to continue
Havoc Pennington's avatar
Havoc Pennington committed
522
 * using it.  Additionally, if the loader is an animation, it will
Havoc Pennington's avatar
Havoc Pennington committed
523 524
 * return the "static image" of the animation
 * (see gdk_pixbuf_animation_get_static_image()).
Havoc Pennington's avatar
Havoc Pennington committed
525
 * 
Havoc Pennington's avatar
Havoc Pennington committed
526
 * Return value: The #GdkPixbuf that the loader is creating, or %NULL if not
Federico Mena Quintero's avatar
Federico Mena Quintero committed
527
 * enough data has been read to determine how to create the image buffer.
528 529 530 531
 **/
GdkPixbuf *
gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader)
{
532 533 534 535 536
  GdkPixbufLoaderPrivate *priv;
  
  g_return_val_if_fail (loader != NULL, NULL);
  g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL);
  
537
  priv = loader->priv;
Havoc Pennington's avatar
Havoc Pennington committed
538 539

  if (priv->animation)
Havoc Pennington's avatar
Havoc Pennington committed
540 541 542
    return gdk_pixbuf_animation_get_static_image (priv->animation);
  else
    return NULL;
543 544
}

545 546 547
/**
 * gdk_pixbuf_loader_get_animation:
 * @loader: A pixbuf loader
548
 *
Matthias Clasen's avatar
Matthias Clasen committed
549
 * Queries the #GdkPixbufAnimation that a pixbuf loader is currently creating.
550
 * In general it only makes sense to call this function after the "area_prepared"
Havoc Pennington's avatar
Havoc Pennington committed
551
 * signal has been emitted by the loader. If the loader doesn't have enough
Matthias Clasen's avatar
Matthias Clasen committed
552 553
 * bytes yet (hasn't emitted the "area_prepared" signal) this function will 
 * return %NULL.
554
 *
Matthias Clasen's avatar
Matthias Clasen committed
555
 * Return value: The #GdkPixbufAnimation that the loader is loading, or %NULL if
556
 not enough data has been read to determine the information.
557
**/
558 559 560
GdkPixbufAnimation *
gdk_pixbuf_loader_get_animation (GdkPixbufLoader *loader)
{
561 562 563 564 565
  GdkPixbufLoaderPrivate *priv;
  
  g_return_val_if_fail (loader != NULL, NULL);
  g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), NULL);
  
566
  priv = loader->priv;
567 568
  
  return priv->animation;
569 570
}

571 572
/**
 * gdk_pixbuf_loader_close:
573
 * @loader: A pixbuf loader.
574 575 576 577 578 579
 * @error: return location for a #GError, or %NULL to ignore errors
 *
 * Informs a pixbuf loader that no further writes with
 * gdk_pixbuf_loader_write() will occur, so that it can free its
 * internal loading structures. Also, tries to parse any data that
 * hasn't yet been parsed; if the remaining data is partial or
Matthias Clasen's avatar
Matthias Clasen committed
580
 * corrupt, an error will be returned.  If %FALSE is returned, @error
581 582 583 584
 * will be set to an error from the #GDK_PIXBUF_ERROR or #G_FILE_ERROR
 * domains. If you're just cancelling a load rather than expecting it
 * to be finished, passing %NULL for @error to ignore it is
 * reasonable.
585
 *
586 587
 * Returns: %TRUE if all image data written so far was successfully
            passed out via the update_area signal
588
 **/
589 590 591
gboolean
gdk_pixbuf_loader_close (GdkPixbufLoader *loader,
                         GError         **error)
592
{
593
  GdkPixbufLoaderPrivate *priv;
594
  gboolean retval = TRUE;
595
  
596 597
  g_return_val_if_fail (loader != NULL, TRUE);
  g_return_val_if_fail (GDK_IS_PIXBUF_LOADER (loader), TRUE);
598
  
599
  priv = loader->priv;
600 601
  
  /* we expect it's not closed */
602
  g_return_val_if_fail (priv->closed == FALSE, TRUE);
603 604 605
  
  /* We have less the 128 bytes in the image.  Flush it, and keep going. */
  if (priv->image_module == NULL)
606 607 608 609 610 611 612 613 614
    {
      GError *tmp = NULL;
      gdk_pixbuf_loader_load_module (loader, NULL, &tmp);
      if (tmp != NULL)
	{
	  g_propagate_error (error, tmp);
	  retval = FALSE;
	}
    }  
615

616 617 618 619
  if (priv->image_module && priv->image_module->stop_load && priv->context) {
    if (!priv->image_module->stop_load (priv->context, error))
      retval = FALSE;
  }
620 621
  
  priv->closed = TRUE;
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641

  if (priv->needs_scale) {
    GdkPixbuf *tmp, *pixbuf;

    tmp = gdk_pixbuf_animation_get_static_image (priv->animation);
    g_object_ref (tmp);
    pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, tmp->has_alpha, 8, priv->width, priv->height);
    g_object_unref (priv->animation);
    priv->animation = gdk_pixbuf_non_anim_new (pixbuf);
    g_signal_emit (loader, pixbuf_loader_signals[AREA_PREPARED], 0);
    gdk_pixbuf_scale (tmp, pixbuf, 0, 0, priv->width, priv->height, 0, 0,
		      (double) priv->width / tmp->width,
		      (double) priv->height / tmp->height,
		      GDK_INTERP_BILINEAR); 
    g_object_unref (tmp);

    g_signal_emit (loader, pixbuf_loader_signals[AREA_UPDATED], 0, 
		   0, 0, priv->width, priv->height);
  }

642
  
643
  g_signal_emit (loader, pixbuf_loader_signals[CLOSED], 0);
644 645

  return retval;
646
}
647 648


649 650 651 652