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