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