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