]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-gdip-utils.c
Don't do funny casts to avoid compiler warnings
[~andy/gtk] / gdk-pixbuf / io-gdip-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* GdkPixbuf library - Win32 GDI+ Pixbuf Loader
3  *
4  * Copyright (C) 2008 Dominic Lachowicz
5  * Copyright (C) 2008 Alberto Ruiz
6  *
7  * Authors: Dominic Lachowicz <domlachowicz@gmail.com>
8  *          Alberto Ruiz <aruiz@gnome.org>
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  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #define INITGUID
25 #include <ole2.h>
26
27 #include "io-gdip-utils.h"
28 #include "io-gdip-native.h"
29 #include "io-gdip-propertytags.h"
30 #include "io-gdip-animation.h"
31
32 #define LOAD_BUFFER_SIZE 65536
33
34 static GdiplusStartupFunc GdiplusStartup;
35 static GdipCreateBitmapFromStreamFunc GdipCreateBitmapFromStream;
36 static GdipBitmapGetPixelFunc GdipBitmapGetPixel;
37 static GdipGetImageHeightFunc GdipGetImageHeight;
38 static GdipDisposeImageFunc GdipDisposeImage;
39 static GdipGetImageFlagsFunc GdipGetImageFlags;
40 static GdipGetImageWidthFunc GdipGetImageWidth;
41 static GdipImageGetFrameCountFunc GdipImageGetFrameCount;
42 static GdipImageSelectActiveFrameFunc GdipImageSelectActiveFrame;
43 static GdipGetPropertyItemSizeFunc GdipGetPropertyItemSize;
44 static GdipGetPropertyItemFunc GdipGetPropertyItem;
45 static GdipGetPropertyCountFunc GdipGetPropertyCount;
46 static GdipGetPropertyIdListFunc GdipGetPropertyIdList;
47 static GdipCreateBitmapFromScan0Func GdipCreateBitmapFromScan0;
48 static GdipSaveImageToStreamFunc GdipSaveImageToStream;
49 static GdipBitmapSetPixelFunc GdipBitmapSetPixel;
50 static GdipDrawImageIFunc GdipDrawImageI;
51 static GdipGetImageGraphicsContextFunc GdipGetImageGraphicsContext;
52 static GdipFlushFunc GdipFlush;
53 static GdipGraphicsClearFunc GdipGraphicsClear;
54 static GdipBitmapSetResolutionFunc GdipBitmapSetResolution;
55 static GdipGetImageHorizontalResolutionFunc GdipGetImageHorizontalResolution;
56 static GdipGetImageVerticalResolutionFunc GdipGetImageVerticalResolution;
57 static GdipLoadImageFromStreamFunc GdipLoadImageFromStream;
58 static GdipDeleteGraphicsFunc GdipDeleteGraphics;
59 static GdipGetImageEncodersFunc GdipGetImageEncoders;
60 static GdipGetImageEncodersSizeFunc GdipGetImageEncodersSize;
61
62 DEFINE_GUID(FrameDimensionTime, 0x6aedbd6d,0x3fb5,0x418a,0x83,0xa6,0x7f,0x45,0x22,0x9d,0xc8,0x72);
63 DEFINE_GUID(FrameDimensionPage, 0x7462dc86,0x6180,0x4c7e,0x8e,0x3f,0xee,0x73,0x33,0xa7,0xa4,0x83);
64
65 static void
66 gdip_set_error_from_hresult (GError **error, gint code, HRESULT hr, const char *format)
67 {
68   gchar *msg;
69   
70   msg = g_win32_error_message (hr);
71   
72   if (msg) {
73     g_set_error (error, GDK_PIXBUF_ERROR, code, format, msg);
74     g_free (msg);
75   }
76 }
77
78 static void
79 gdip_set_error_from_gpstatus (GError **error, gint code, GpStatus status)
80 {
81   const char *msg;
82
83   switch (status)
84     {
85 #define CASE(x) case x: msg = #x; break
86     CASE (GenericError);
87     CASE (InvalidParameter);
88     CASE (OutOfMemory);
89     CASE (ObjectBusy);
90     CASE (InsufficientBuffer);
91     CASE (NotImplemented);
92     CASE (Win32Error);
93     CASE (WrongState);
94     CASE (Aborted);
95     CASE (FileNotFound);
96     CASE (ValueOverflow);
97     CASE (AccessDenied);
98     CASE (UnknownImageFormat);
99     CASE (FontFamilyNotFound);
100     CASE (FontStyleNotFound);
101     CASE (NotTrueTypeFont);
102     CASE (UnsupportedGdiplusVersion);
103     CASE (GdiplusNotInitialized);
104     CASE (PropertyNotFound);
105     CASE (PropertyNotSupported);
106     CASE (ProfileNotFound);
107     default:
108       msg = "Unknown error";
109     }
110   g_set_error_literal (error, GDK_PIXBUF_ERROR, code, msg);
111 }
112
113 static gboolean
114 gdip_init (void)
115 {
116   GdiplusStartupInput input;
117   ULONG_PTR gdiplusToken = 0;
118   static HINSTANCE gdipluslib = NULL;
119
120   if (!gdipluslib)
121     gdipluslib = LoadLibrary ("gdiplus.dll");
122   else
123     return TRUE; /* gdip_init() is idempotent */
124
125   if (!gdipluslib)
126     return FALSE;
127
128 #define LOOKUP(func) \
129   G_STMT_START { \
130     func = (func##Func) GetProcAddress (gdipluslib, #func); \
131     if (!func) {\
132       g_warning ("Couldn't find GDI+ function %s\n", #func); \
133       return FALSE; \
134     } \
135   } G_STMT_END
136
137   LOOKUP (GdiplusStartup);
138   LOOKUP (GdipCreateBitmapFromStream);
139   LOOKUP (GdipBitmapGetPixel);
140   LOOKUP (GdipGetImageHeight);
141   LOOKUP (GdipDisposeImage);
142   LOOKUP (GdipGetImageFlags);
143   LOOKUP (GdipGetImageWidth);
144   LOOKUP (GdipImageGetFrameCount);
145   LOOKUP (GdipImageSelectActiveFrame);
146   LOOKUP (GdipGetPropertyItemSize);
147   LOOKUP (GdipGetPropertyItem);
148   LOOKUP (GdipGetPropertyCount);
149   LOOKUP (GdipGetPropertyIdList);
150   LOOKUP (GdipCreateBitmapFromScan0);
151   LOOKUP (GdipSaveImageToStream);
152   LOOKUP (GdipBitmapSetPixel);
153   LOOKUP (GdipDrawImageI);
154   LOOKUP (GdipGetImageGraphicsContext);
155   LOOKUP (GdipFlush);
156   LOOKUP (GdipGraphicsClear);
157   LOOKUP (GdipBitmapSetResolution);
158   LOOKUP (GdipGetImageHorizontalResolution);
159   LOOKUP (GdipGetImageVerticalResolution);
160   LOOKUP (GdipLoadImageFromStream);
161   LOOKUP (GdipDeleteGraphics);
162   LOOKUP (GdipGetImageEncoders);
163   LOOKUP (GdipGetImageEncodersSize);
164
165 #undef LOOKUP
166
167   input.GdiplusVersion = 1;
168   input.DebugEventCallback = NULL;
169   input.SuppressBackgroundThread = input.SuppressExternalCodecs = FALSE;
170   
171   return (GdiplusStartup (&gdiplusToken, &input, NULL) == 0 ? TRUE : FALSE);
172 }
173
174 static gboolean
175 GetEncoderClsid (const WCHAR *format, CLSID *pClsid)
176 {
177   UINT num, size;
178   int j;
179   ImageCodecInfo *pImageCodecInfo;
180     
181   if (Ok != GdipGetImageEncodersSize (&num, &size))
182     return FALSE;
183     
184   pImageCodecInfo = (ImageCodecInfo *) g_malloc (size);
185     
186   if (Ok != GdipGetImageEncoders (num, size, pImageCodecInfo)) {
187     g_free (pImageCodecInfo);
188     return FALSE;
189   }
190
191   for (j = 0; j < num; j++) {
192     if (wcscmp (pImageCodecInfo[j].MimeType, format) == 0) {
193       *pClsid = pImageCodecInfo[j].Clsid;
194       g_free (pImageCodecInfo);
195       return TRUE;
196     }
197   }
198  
199   g_free (pImageCodecInfo);
200
201   return FALSE;
202 }
203
204 static HGLOBAL
205 gdip_buffer_to_hglobal (const gchar *buffer, size_t size, GError **error)
206 {
207   HGLOBAL hg = NULL;
208
209   hg = GlobalAlloc (GPTR, size);
210
211   if (!hg) {
212     gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, GetLastError (), _("Could not allocate memory: %s"));
213     return NULL;
214   }
215
216   CopyMemory (hg, buffer, size);
217
218   return hg;
219 }
220
221 static gboolean
222 gdip_save_bitmap_to_callback (GpBitmap *bitmap,
223                               const CLSID *format,
224                               const EncoderParameters *encoder_params,
225                               GdkPixbufSaveFunc save_func,
226                               gpointer user_data,
227                               GError **error)
228 {
229   HRESULT hr;  
230   IStream *streamOut = NULL;
231   gboolean success = FALSE;
232   guint64 zero = 0;
233   GpStatus status;
234
235   hr = CreateStreamOnHGlobal (NULL, TRUE, &streamOut);
236   if (!SUCCEEDED (hr)) {
237     gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s"));
238     return FALSE;
239   }
240
241   status = GdipSaveImageToStream ((GpImage *)bitmap, streamOut, format, encoder_params);
242   if (Ok != status) {
243     gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
244     IStream_Release (streamOut);
245     return FALSE;
246   }
247
248   /* seek back to the beginning of the stream */
249   hr = IStream_Seek (streamOut, *(LARGE_INTEGER *)&zero, STREAM_SEEK_SET, NULL);
250   if (!SUCCEEDED (hr)) {
251     gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not seek stream: %s"));
252     IStream_Release (streamOut);
253     return FALSE;
254   }
255   
256   for (;;) {
257     char buffer[LOAD_BUFFER_SIZE];
258     ULONG nread;
259     
260     hr = IStream_Read (streamOut, buffer, sizeof(buffer), &nread);
261     if (!SUCCEEDED (hr))
262       {
263         gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not read from stream: %s"));
264         break;
265       }
266     else if (0 == nread) {
267       success = TRUE; /* EOF */
268       break;
269     }
270     else if (!(*save_func) (buffer, nread, error, user_data))
271       break;
272   }
273   
274   IStream_Release (streamOut);
275   
276   return success;
277 }                     
278
279 static GpBitmap *
280 gdip_pixbuf_to_bitmap (GdkPixbuf *pixbuf)
281 {
282   GpBitmap *bitmap = NULL;
283
284   int width, height, stride, n_channels;
285   guint8 *pixels;
286
287   width = gdk_pixbuf_get_width (pixbuf);
288   height = gdk_pixbuf_get_height (pixbuf);
289   stride = gdk_pixbuf_get_rowstride (pixbuf);
290   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
291   pixels = gdk_pixbuf_get_pixels (pixbuf);
292
293   if (n_channels == 3 || n_channels == 4) {
294     /* rgbX. need to convert to argb. pass a null data to get an empty bitmap */
295     GdipCreateBitmapFromScan0 (width, height, 0, PixelFormat32bppARGB, NULL, &bitmap);
296     
297     if (bitmap) {
298       int x, y;
299       
300       for (y = 0; y < height; y++) {
301         for (x = 0; x < width; x++) {
302           ARGB p;
303           guint8 alpha;
304           guchar *base = pixels + (y * stride + (x * n_channels));
305           
306           if (n_channels == 4)
307             alpha = base[3];
308           else
309             alpha = 0xff;
310                   
311           if (alpha == 0) 
312             p = 0;
313           else {
314             guint8 red = base[0];
315             guint8 green = base[1];
316             guint8 blue = base[2];
317             
318             p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
319           }
320           
321           GdipBitmapSetPixel (bitmap, x, y, p);
322         }
323       }
324     }
325   }
326   else {
327     g_warning ("Unsupported number of channels: %d\n", n_channels);
328   }
329   
330   return bitmap;
331 }
332
333 static GpBitmap *
334 gdip_buffer_to_bitmap (const gchar *buffer, size_t size, GError **error)
335 {
336   HRESULT hr;
337   HGLOBAL hg = NULL;
338   GpBitmap *bitmap = NULL;
339   IStream *stream = NULL;
340   GpStatus status;
341
342   hg = gdip_buffer_to_hglobal (buffer, size, error);
343
344   if (!hg)
345     return NULL;
346
347   hr = CreateStreamOnHGlobal (hg, FALSE, (LPSTREAM *)&stream);
348
349   if (!SUCCEEDED (hr)) {
350     gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s"));
351     GlobalFree (hg);
352     return NULL;
353   }
354   
355   status = GdipCreateBitmapFromStream (stream, &bitmap);
356
357   if (Ok != status)
358     gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
359
360   IStream_Release (stream);
361   GlobalFree (hg);
362
363   return bitmap;
364 }
365
366 static GpImage *
367 gdip_buffer_to_image (const gchar *buffer, size_t size, GError **error)
368 {
369   HRESULT hr;
370   HGLOBAL hg = NULL;
371   GpImage *image = NULL;
372   IStream *stream = NULL;
373   GpStatus status;
374
375   hg = gdip_buffer_to_hglobal (buffer, size, error);
376
377   if (!hg)
378     return NULL;
379
380   hr = CreateStreamOnHGlobal (hg, FALSE, (LPSTREAM *)&stream);
381
382   if (!SUCCEEDED (hr)) {
383     gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s"));
384     GlobalFree (hg);
385     return NULL;
386   }
387   
388   status = GdipLoadImageFromStream (stream, &image);
389
390   if (Ok != status)
391     gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
392
393   IStream_Release (stream);
394   GlobalFree (hg);
395
396   return image;
397 }
398
399 static void
400 gdip_bitmap_get_size (GpBitmap *bitmap, guint *width, guint *height)
401 {
402   if (bitmap == NULL || width == NULL || height == NULL)
403     return;
404
405   *width = *height = 0;
406
407   GdipGetImageWidth ((GpImage *) bitmap, width);
408   GdipGetImageHeight ((GpImage *) bitmap, height);
409 }
410
411 static void
412 gdip_bitmap_get_has_alpha (GpBitmap *bitmap, gboolean *has_alpha)
413 {
414   guint flags = 0;
415
416   if (bitmap == NULL || has_alpha == NULL)
417     return;
418
419   GdipGetImageFlags ((GpImage *) bitmap, &flags);
420   *has_alpha = (flags & ImageFlagsHasAlpha);
421 }
422
423 static gboolean
424 gdip_bitmap_get_n_frames (GpBitmap *bitmap, guint *n_frames, gboolean timeDimension)
425 {
426   if (bitmap == NULL || n_frames == NULL)
427     return FALSE;
428
429   *n_frames = 1;
430
431   return (Ok == GdipImageGetFrameCount ((GpImage *) bitmap, (timeDimension ? &FrameDimensionTime : &FrameDimensionPage), n_frames));
432 }
433
434 static gboolean
435 gdip_bitmap_select_frame (GpBitmap *bitmap, guint frame, gboolean timeDimension)
436 {
437   if (bitmap == NULL)
438     return FALSE;
439
440   return (Ok == GdipImageSelectActiveFrame ((GpImage *)bitmap, (timeDimension ? &FrameDimensionTime : &FrameDimensionPage), frame));
441 }
442
443 static gboolean
444 gdip_bitmap_get_property_as_string (GpBitmap *bitmap, guint propertyId, gchar **str)
445 {
446   guint item_size;
447   gboolean success = FALSE;
448
449   if (bitmap == NULL || str == NULL)
450     return FALSE;
451
452   *str = 0;
453
454   if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, propertyId, &item_size)) {
455     PropertyItem *item;
456     
457     item = (PropertyItem *)g_try_malloc (item_size);
458     if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, propertyId, item_size, item)) {
459       GString *gstr;
460       int i;
461       
462       gstr = g_string_new (NULL);
463       
464       success = TRUE;
465       switch (item->type) {
466       case PropertyTagTypeByte:
467         for (i = 0; i < item->length / sizeof(guint8); i++) {
468           guint8 *bytes = (guint8 *)item->value;
469           
470           if (gstr->len != 0)
471             g_string_append_c(gstr, ',');
472           g_string_append_printf (gstr, "%u", (guint32)bytes[i]);
473         }
474         break;
475         
476       case PropertyTagTypeASCII:
477         g_string_append_len (gstr, (const char *)item->value, item->length);
478         break;
479         
480       case PropertyTagTypeShort:
481         for (i = 0; i < item->length / sizeof(guint16); i++) {
482           guint16 *shorts = (guint16 *)item->value;
483           
484           if (gstr->len != 0)
485             g_string_append_c (gstr, ',');
486           g_string_append_printf (gstr, "%u", (guint32)shorts[i]);
487         }
488         break;
489         
490       case PropertyTagTypeLong:
491         for (i = 0; i < item->length / sizeof(guint32); i++) {
492           guint32 *longs = (guint32 *)item->value;
493           
494           if (gstr->len != 0)
495             g_string_append_c (gstr, ',');
496           g_string_append_printf (gstr, "%u", longs[i]);
497         }
498         break;
499
500       case PropertyTagTypeSLONG:
501         for (i = 0; i < item->length / sizeof(guint32); i++) {
502           gint32 *longs = (gint32 *)item->value;
503           
504           if (gstr->len != 0)
505             g_string_append_c (gstr, ',');
506           g_string_append_printf (gstr, "%d", longs[i]);
507         }
508         break;
509         
510       default:
511         success = FALSE;
512         break;
513       }
514       
515       if (gstr->len > 0)
516         *str = g_string_free (gstr, FALSE);
517       else
518         g_string_free (gstr, TRUE);
519     }
520     
521     g_free (item);
522   }
523   
524   return success;
525 }
526
527 static gboolean
528 gdip_bitmap_get_frame_delay (GpBitmap *bitmap, guint *delay)
529 {
530   guint item_size;
531   gboolean success = FALSE;
532
533   if (bitmap == NULL || delay == NULL)
534     return FALSE;
535
536   *delay = 0;
537
538   if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, PropertyTagFrameDelay, &item_size)) {
539     PropertyItem *item;
540     
541     item = (PropertyItem *)g_try_malloc (item_size);
542     if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, PropertyTagFrameDelay, item_size, item)) {
543       /* PropertyTagFrameDelay. Time delay, in hundredths of a second, between two frames in an animated GIF image. */
544       *delay = *((long *)item->value);
545       success = TRUE;
546     }
547     
548     g_free (item);
549   }
550   
551   return success;
552 }
553
554 static gboolean
555 gdip_bitmap_get_n_loops (GpBitmap *bitmap, guint *loops)
556 {
557   guint item_size;
558   gboolean success = FALSE;
559
560   if (bitmap == NULL || loops == NULL)
561     return FALSE;
562
563   *loops = 1;
564
565   /* PropertyTagLoopCount. 0 == infinitely */
566   if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, PropertyTagLoopCount, &item_size)) {
567     PropertyItem *item;
568     
569     item = (PropertyItem *)g_try_malloc (item_size);
570     if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, PropertyTagLoopCount, item_size, item)) {
571       *loops = *((short *)item->value);
572       success = TRUE;
573     }
574     
575     g_free (item);
576   }
577   
578   return success;
579 }
580
581 /*************************************************************************/
582 /*************************************************************************/
583
584 struct _GdipContext {
585   GdkPixbufModuleUpdatedFunc  updated_func;
586   GdkPixbufModulePreparedFunc prepared_func;
587   GdkPixbufModuleSizeFunc     size_func;
588
589   gpointer                    user_data;
590
591   GByteArray                 *buffer;
592 };
593 typedef struct _GdipContext GdipContext;
594
595 static void
596 destroy_gdipcontext (GdipContext *context)
597 {
598   if (context != NULL) {
599     g_byte_array_free (context->buffer, TRUE);
600     g_free (context);
601   }
602 }
603
604 static void
605 emit_updated (GdipContext *context, GdkPixbuf *pixbuf)
606 {
607   if (context->updated_func)
608     (*context->updated_func) (pixbuf,
609                               0, 0,
610                               gdk_pixbuf_get_width (pixbuf),
611                               gdk_pixbuf_get_height (pixbuf),
612                               context->user_data);
613 }
614
615 static void
616 emit_prepared (GdipContext *context, GdkPixbuf *pixbuf, GdkPixbufAnimation *anim)
617 {
618   if (context->prepared_func)
619     (*context->prepared_func) (pixbuf, anim, context->user_data);
620 }
621
622 static gpointer
623 gdk_pixbuf__gdip_image_begin_load (GdkPixbufModuleSizeFunc size_func,
624                                    GdkPixbufModulePreparedFunc prepared_func,
625                                    GdkPixbufModuleUpdatedFunc  updated_func,
626                                    gpointer user_data,
627                                    GError **error)
628 {
629   GdipContext *context = g_new0 (GdipContext, 1);
630
631   context->size_func     = size_func;
632   context->prepared_func = prepared_func;
633   context->updated_func  = updated_func;
634   context->user_data     = user_data;
635   context->buffer        = g_byte_array_new ();
636
637   return context;
638 }
639
640 static gboolean
641 gdk_pixbuf__gdip_image_load_increment (gpointer data,
642                                        const guchar *buf, guint size,
643                                        GError **error)
644 {
645   GdipContext *context = (GdipContext *)data;
646   GByteArray *image_buffer = context->buffer;
647
648   g_byte_array_append (image_buffer, (guint8 *)buf, size);
649
650   return TRUE;
651 }
652
653 static GdkPixbuf *
654 gdip_bitmap_to_pixbuf (GpBitmap *bitmap)
655 {
656   GdkPixbuf *pixbuf = NULL;
657   guchar *cursor = NULL;
658   gint rowstride;
659   gboolean has_alpha = FALSE;
660   gint n_channels = 0;
661   gchar *option;
662
663   guint width = 0, height = 0, x, y;
664
665   gdip_bitmap_get_size (bitmap, &width, &height);
666   gdip_bitmap_get_has_alpha (bitmap, &has_alpha);
667
668   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, width, height);
669
670   if (!pixbuf)
671     return NULL;
672
673   rowstride = gdk_pixbuf_get_rowstride (pixbuf);
674   cursor = gdk_pixbuf_get_pixels (pixbuf);
675   n_channels = gdk_pixbuf_get_n_channels (pixbuf);
676
677   for (y = 0; y < height; y++) {
678     for (x = 0; x < width; x++) {
679       ARGB pixel;
680       guchar *b = cursor + (y * rowstride + (x * n_channels));
681       
682       if (Ok != GdipBitmapGetPixel (bitmap, x, y, &pixel)) {
683         g_object_unref (pixbuf);
684         return NULL;
685       }
686       
687       b[0] = (pixel & 0xff0000) >> 16;
688       b[1] = (pixel & 0x00ff00) >> 8;
689       b[2] = (pixel & 0x0000ff) >> 0;
690       
691       if (has_alpha)      
692         b[3] = (pixel & 0xff000000) >> 24;
693     }
694   }
695
696   if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagOrientation, &option)) {
697     gdk_pixbuf_set_option (pixbuf, "orientation", option);
698     g_free (option);
699   }
700
701   if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagArtist, &option)) {
702     gdk_pixbuf_set_option (pixbuf, "Author", option);
703     g_free (option);
704   }
705
706   if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagImageTitle, &option)) {
707     gdk_pixbuf_set_option (pixbuf, "Title", option);
708     g_free (option);
709   }
710
711   return pixbuf;
712 }
713
714 static gboolean
715 stop_load (GpBitmap *bitmap, GdipContext *context, GError **error)
716 {
717   guint       n_frames = 1, i;
718   GdkPixbufGdipAnim *animation = NULL;
719
720   gdip_bitmap_get_n_frames (bitmap, &n_frames, TRUE);
721
722   for (i = 0; i < n_frames; i++) {
723     GdkPixbuf *pixbuf = NULL;
724     GdkPixbufFrame *frame;
725     guint frame_delay = 0;
726
727     gdip_bitmap_select_frame (bitmap, i, TRUE);
728     
729     pixbuf = gdip_bitmap_to_pixbuf (bitmap);
730     
731     if (!pixbuf) {
732       if (animation != NULL)
733         g_object_unref (G_OBJECT (animation));
734
735       destroy_gdipcontext (context);
736       g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't create pixbuf"));
737       return FALSE;
738     }
739     
740     if (animation == NULL) {
741       guint n_loops = 1;
742
743       animation = g_object_new (GDK_TYPE_PIXBUF_GDIP_ANIM, NULL);
744       gdip_bitmap_get_n_loops (bitmap, &n_loops);
745       animation->loop = n_loops;
746     }
747
748     frame = g_new (GdkPixbufFrame, 1);
749     frame->pixbuf = pixbuf;
750
751     gdip_bitmap_get_frame_delay (bitmap, &frame_delay);
752   
753     animation->n_frames++;
754     animation->frames = g_list_append (animation->frames, frame);
755
756     animation->width = gdk_pixbuf_get_width (pixbuf);
757     animation->height = gdk_pixbuf_get_height (pixbuf);
758
759     /* GIF delay is in hundredths, we want thousandths */
760     frame->delay_time = frame_delay * 10;
761     frame->elapsed = animation->total_time;
762     
763     /* Some GIFs apparently have delay time of 0,
764      * that crashes everything so set it to "fast".
765      * Also, timeouts less than 20 or so just lock up
766      * the app or make the animation choppy, so fix them.
767      */
768     if (frame->delay_time < 20)
769       frame->delay_time = 20; /* 20 = "fast" */
770
771     animation->total_time += frame->delay_time;
772
773     if (i == 0)
774       emit_prepared (context, pixbuf, GDK_PIXBUF_ANIMATION (animation));
775
776     emit_updated (context, pixbuf);
777   }
778
779   if (animation != NULL)
780     g_object_unref (G_OBJECT (animation));
781
782   destroy_gdipcontext (context);
783   
784   return TRUE;
785 }
786
787 static gboolean
788 gdk_pixbuf__gdip_image_stop_load (gpointer data, GError **error)
789 {
790   GdipContext *context = (GdipContext *)data;
791   GpBitmap    *bitmap = NULL;
792   GByteArray *image_buffer = context->buffer;
793
794   bitmap = gdip_buffer_to_bitmap ((gchar *)image_buffer->data, image_buffer->len, error);
795
796   if (!bitmap) {
797     destroy_gdipcontext (context);
798     g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't load bitmap"));
799     return FALSE;
800   }
801
802   return stop_load (bitmap, context, error);
803 }
804
805 static gboolean
806 gdk_pixbuf__gdip_image_stop_vector_load (gpointer data, GError **error)
807 {
808   GdipContext *context = (GdipContext *)data;
809   GByteArray *image_buffer = context->buffer;
810
811   GpImage *metafile;
812   GpGraphics *graphics;
813   GpBitmap *bitmap;
814   GpStatus status;
815   float metafile_xres, metafile_yres;
816   guint width, height;
817
818   metafile = gdip_buffer_to_image ((gchar *)image_buffer->data, image_buffer->len, error);
819   if (!metafile) {
820     destroy_gdipcontext (context);
821     g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't load metafile"));
822     return FALSE;
823   }
824
825   GdipGetImageWidth (metafile, &width);
826   GdipGetImageHeight (metafile, &height);
827
828   status = GdipCreateBitmapFromScan0 (width, height, 0, PixelFormat32bppARGB, NULL, &bitmap);
829   if (Ok != status) {
830     gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
831     GdipDisposeImage (metafile);
832     
833     return FALSE;
834   }
835
836   GdipGetImageHorizontalResolution (metafile, &metafile_xres);
837   GdipGetImageVerticalResolution (metafile, &metafile_yres);
838   GdipBitmapSetResolution (bitmap, metafile_xres, metafile_yres);
839
840   status = GdipGetImageGraphicsContext ((GpImage *)bitmap, &graphics);
841   if (Ok != status) {
842     gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
843     GdipDisposeImage ((GpImage *)bitmap);
844     GdipDisposeImage (metafile);
845     
846     return FALSE;
847   }
848   
849   /* gotta clear the bitmap */
850   GdipGraphicsClear (graphics, 0xffffffff);
851   
852   status = GdipDrawImageI (graphics, metafile, 0, 0);
853   if (Ok != status) {
854     gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
855     GdipDeleteGraphics (graphics);
856     GdipDisposeImage ((GpImage *)bitmap);
857     GdipDisposeImage (metafile);
858     
859     return FALSE;
860   }
861   
862   GdipFlush (graphics, 1);
863   
864   GdipDeleteGraphics (graphics);
865   GdipDisposeImage (metafile);
866
867   return stop_load (bitmap, context, error);
868 }
869
870 static void 
871 gdip_animation_prepare (GdkPixbuf *pixbuf,
872                         GdkPixbufAnimation *animation,
873                         gpointer user_data)
874 {
875   GdkPixbufAnimation **anim;
876
877   anim = (GdkPixbufAnimation **)user_data;
878
879   /* save a reference to the animation */
880   g_object_ref (animation);
881   *anim = animation;
882 }
883
884 static GdkPixbufAnimation *
885 gdk_pixbuf__gdip_image_load_animation (FILE *file,
886                                        GError **error)
887 {
888   GdkPixbufAnimation *animation = NULL;
889
890   gpointer context;
891   char buffer[LOAD_BUFFER_SIZE];
892   size_t length;
893
894   context = gdk_pixbuf__gdip_image_begin_load (NULL, gdip_animation_prepare, NULL, &animation, error);
895
896   while (!feof (file) && !ferror (file)) {
897     length = fread (buffer, 1, sizeof (buffer), file);
898     if (length > 0) {
899       if (!gdk_pixbuf__gdip_image_load_increment (context, buffer, length, error)) {
900         gdk_pixbuf__gdip_image_stop_load (context, NULL);
901
902         if (animation)
903           g_object_unref (animation);
904
905         return NULL;
906       }
907     }
908   }
909
910   if (!gdk_pixbuf__gdip_image_stop_load(context, error)) {
911     if (animation)
912       g_object_unref (animation);
913     
914     return NULL;
915   }
916
917   return animation;
918 }
919
920 gboolean
921 gdip_save_to_file_callback (const gchar *buf,
922                             gsize        count,
923                             GError     **error,
924                             gpointer     data)
925 {
926   FILE *filehandle = data;
927   gsize n;
928   
929   n = fwrite (buf, 1, count, filehandle);
930   if (n != count) {
931     gint save_errno = errno;
932     g_set_error (error,
933                  G_FILE_ERROR,
934                  g_file_error_from_errno (save_errno),
935                  _("Error writing to image file: %s"),
936                  g_strerror (save_errno));
937     return FALSE;
938   }
939   
940   return TRUE;
941 }
942
943 void
944 gdip_fill_vtable (GdkPixbufModule *module)
945 {
946   if (gdip_init ()) {
947     module->begin_load     = gdk_pixbuf__gdip_image_begin_load;
948     module->stop_load      = gdk_pixbuf__gdip_image_stop_load;
949     module->load_increment = gdk_pixbuf__gdip_image_load_increment;
950     
951     /* this is the only way to get gtk_image_new_from_file() to load animations. it regrettably
952        does not use the GdkPixbufLoader interface. */
953     module->load_animation = gdk_pixbuf__gdip_image_load_animation;
954   }
955 }
956
957 void
958 gdip_fill_vector_vtable (GdkPixbufModule *module)
959 {
960   if (gdip_init ()) {
961     module->begin_load     = gdk_pixbuf__gdip_image_begin_load;
962     module->stop_load      = gdk_pixbuf__gdip_image_stop_vector_load;
963     module->load_increment = gdk_pixbuf__gdip_image_load_increment;
964   }
965 }
966
967 gboolean
968 gdip_save_pixbuf (GdkPixbuf *pixbuf,
969                   const WCHAR *format,
970                   const EncoderParameters *encoder_params,
971                   GdkPixbufSaveFunc save_func,
972                   gpointer user_data,
973                   GError **error)
974 {
975   GpBitmap *image;
976   CLSID clsid;
977   gboolean success;
978
979   if (!GetEncoderClsid (format, &clsid)) {
980     g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Unsupported image format for GDI+"));
981     return FALSE;
982   }
983   
984   image = gdip_pixbuf_to_bitmap (pixbuf);
985
986   if (image == NULL) {
987     g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Couldn't save"));
988     return FALSE;
989   }
990   
991   success = gdip_save_bitmap_to_callback (image, &clsid, encoder_params, save_func, user_data, error);
992
993   GdipDisposeImage ((GpImage *)image);
994
995   return success;
996 }