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