Add powerbox hook
[gtk-with-powerbox.git] / gdk-pixbuf / io-gdip-utils.c
blob74a0f1758499dd86670140763e101aa540a649f4
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* GdkPixbuf library - Win32 GDI+ Pixbuf Loader
4 * Copyright (C) 2008 Dominic Lachowicz
5 * Copyright (C) 2008 Alberto Ruiz
7 * Authors: Dominic Lachowicz <domlachowicz@gmail.com>
8 * Alberto Ruiz <aruiz@gnome.org>
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.
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.
24 #define INITGUID
25 #include <ole2.h>
27 #include "io-gdip-utils.h"
28 #include "io-gdip-native.h"
29 #include "io-gdip-propertytags.h"
30 #include "io-gdip-animation.h"
32 #define LOAD_BUFFER_SIZE 65536
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;
62 DEFINE_GUID(FrameDimensionTime, 0x6aedbd6d,0x3fb5,0x418a,0x83,0xa6,0x7f,0x45,0x22,0x9d,0xc8,0x72);
63 DEFINE_GUID(FrameDimensionPage, 0x7462dc86,0x6180,0x4c7e,0x8e,0x3f,0xee,0x73,0x33,0xa7,0xa4,0x83);
65 static void
66 gdip_set_error_from_hresult (GError **error, gint code, HRESULT hr, const char *format)
68 gchar *msg;
70 msg = g_win32_error_message (hr);
72 if (msg) {
73 g_set_error (error, GDK_PIXBUF_ERROR, code, format, msg);
74 g_free (msg);
78 static void
79 gdip_set_error_from_gpstatus (GError **error, gint code, GpStatus status)
81 const char *msg;
83 switch (status)
85 #define CASE(x) case x: msg = #x; break
86 CASE (GenericError);
87 CASE (InvalidParameter);
88 CASE (OutOfMemory);
89 CASE (ObjectBusy);
90 CASE (InsufficientBuffer);
91 CASE (NotImplemented);
92 CASE (Win32Error);
93 CASE (WrongState);
94 CASE (Aborted);
95 CASE (FileNotFound);
96 CASE (ValueOverflow);
97 CASE (AccessDenied);
98 CASE (UnknownImageFormat);
99 CASE (FontFamilyNotFound);
100 CASE (FontStyleNotFound);
101 CASE (NotTrueTypeFont);
102 CASE (UnsupportedGdiplusVersion);
103 CASE (GdiplusNotInitialized);
104 CASE (PropertyNotFound);
105 CASE (PropertyNotSupported);
106 CASE (ProfileNotFound);
107 default:
108 msg = "Unknown error";
110 g_set_error_literal (error, GDK_PIXBUF_ERROR, code, msg);
113 static gboolean
114 gdip_init (void)
116 GdiplusStartupInput input;
117 ULONG_PTR gdiplusToken = 0;
118 static HINSTANCE gdipluslib = NULL;
120 if (!gdipluslib)
121 gdipluslib = LoadLibrary ("gdiplus.dll");
122 else
123 return TRUE; /* gdip_init() is idempotent */
125 if (!gdipluslib)
126 return FALSE;
128 #define LOOKUP(func) \
129 G_STMT_START { \
130 func = (func##Func) GetProcAddress (gdipluslib, #func); \
131 if (!func) {\
132 g_warning ("Couldn't find GDI+ function %s\n", #func); \
133 return FALSE; \
135 } G_STMT_END
137 LOOKUP (GdiplusStartup);
138 LOOKUP (GdipCreateBitmapFromStream);
139 LOOKUP (GdipBitmapGetPixel);
140 LOOKUP (GdipGetImageHeight);
141 LOOKUP (GdipDisposeImage);
142 LOOKUP (GdipGetImageFlags);
143 LOOKUP (GdipGetImageWidth);
144 LOOKUP (GdipImageGetFrameCount);
145 LOOKUP (GdipImageSelectActiveFrame);
146 LOOKUP (GdipGetPropertyItemSize);
147 LOOKUP (GdipGetPropertyItem);
148 LOOKUP (GdipGetPropertyCount);
149 LOOKUP (GdipGetPropertyIdList);
150 LOOKUP (GdipCreateBitmapFromScan0);
151 LOOKUP (GdipSaveImageToStream);
152 LOOKUP (GdipBitmapSetPixel);
153 LOOKUP (GdipDrawImageI);
154 LOOKUP (GdipGetImageGraphicsContext);
155 LOOKUP (GdipFlush);
156 LOOKUP (GdipGraphicsClear);
157 LOOKUP (GdipBitmapSetResolution);
158 LOOKUP (GdipGetImageHorizontalResolution);
159 LOOKUP (GdipGetImageVerticalResolution);
160 LOOKUP (GdipLoadImageFromStream);
161 LOOKUP (GdipDeleteGraphics);
162 LOOKUP (GdipGetImageEncoders);
163 LOOKUP (GdipGetImageEncodersSize);
165 #undef LOOKUP
167 input.GdiplusVersion = 1;
168 input.DebugEventCallback = NULL;
169 input.SuppressBackgroundThread = input.SuppressExternalCodecs = FALSE;
171 return (GdiplusStartup (&gdiplusToken, &input, NULL) == 0 ? TRUE : FALSE);
174 static gboolean
175 GetEncoderClsid (const WCHAR *format, CLSID *pClsid)
177 UINT num, size;
178 int j;
179 ImageCodecInfo *pImageCodecInfo;
181 if (Ok != GdipGetImageEncodersSize (&num, &size))
182 return FALSE;
184 pImageCodecInfo = (ImageCodecInfo *) g_malloc (size);
186 if (Ok != GdipGetImageEncoders (num, size, pImageCodecInfo)) {
187 g_free (pImageCodecInfo);
188 return FALSE;
191 for (j = 0; j < num; j++) {
192 if (wcscmp (pImageCodecInfo[j].MimeType, format) == 0) {
193 *pClsid = pImageCodecInfo[j].Clsid;
194 g_free (pImageCodecInfo);
195 return TRUE;
199 g_free (pImageCodecInfo);
201 return FALSE;
204 static HGLOBAL
205 gdip_buffer_to_hglobal (const gchar *buffer, size_t size, GError **error)
207 HGLOBAL hg = NULL;
209 hg = GlobalAlloc (GPTR, size);
211 if (!hg) {
212 gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, GetLastError (), _("Could not allocate memory: %s"));
213 return NULL;
216 CopyMemory (hg, buffer, size);
218 return hg;
221 static gboolean
222 gdip_save_bitmap_to_callback (GpBitmap *bitmap,
223 const CLSID *format,
224 const EncoderParameters *encoder_params,
225 GdkPixbufSaveFunc save_func,
226 gpointer user_data,
227 GError **error)
229 HRESULT hr;
230 IStream *streamOut = NULL;
231 gboolean success = FALSE;
232 guint64 zero = 0;
233 GpStatus status;
235 hr = CreateStreamOnHGlobal (NULL, TRUE, &streamOut);
236 if (!SUCCEEDED (hr)) {
237 gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s"));
238 return FALSE;
241 status = GdipSaveImageToStream ((GpImage *)bitmap, streamOut, format, encoder_params);
242 if (Ok != status) {
243 gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
244 IStream_Release (streamOut);
245 return FALSE;
248 /* seek back to the beginning of the stream */
249 hr = IStream_Seek (streamOut, *(LARGE_INTEGER *)&zero, STREAM_SEEK_SET, NULL);
250 if (!SUCCEEDED (hr)) {
251 gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not seek stream: %s"));
252 IStream_Release (streamOut);
253 return FALSE;
256 for (;;) {
257 char buffer[LOAD_BUFFER_SIZE];
258 ULONG nread;
260 hr = IStream_Read (streamOut, buffer, sizeof(buffer), &nread);
261 if (!SUCCEEDED (hr))
263 gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not read from stream: %s"));
264 break;
266 else if (0 == nread) {
267 success = TRUE; /* EOF */
268 break;
270 else if (!(*save_func) (buffer, nread, error, user_data))
271 break;
274 IStream_Release (streamOut);
276 return success;
279 static GpBitmap *
280 gdip_pixbuf_to_bitmap (GdkPixbuf *pixbuf)
282 GpBitmap *bitmap = NULL;
284 int width, height, stride, n_channels;
285 guint8 *pixels;
287 width = gdk_pixbuf_get_width (pixbuf);
288 height = gdk_pixbuf_get_height (pixbuf);
289 stride = gdk_pixbuf_get_rowstride (pixbuf);
290 n_channels = gdk_pixbuf_get_n_channels (pixbuf);
291 pixels = gdk_pixbuf_get_pixels (pixbuf);
293 if (n_channels == 3 || n_channels == 4) {
294 /* rgbX. need to convert to argb. pass a null data to get an empty bitmap */
295 GdipCreateBitmapFromScan0 (width, height, 0, PixelFormat32bppARGB, NULL, &bitmap);
297 if (bitmap) {
298 int x, y;
300 for (y = 0; y < height; y++) {
301 for (x = 0; x < width; x++) {
302 ARGB p;
303 guint8 alpha;
304 guchar *base = pixels + (y * stride + (x * n_channels));
306 if (n_channels == 4)
307 alpha = base[3];
308 else
309 alpha = 0xff;
311 if (alpha == 0)
312 p = 0;
313 else {
314 guint8 red = base[0];
315 guint8 green = base[1];
316 guint8 blue = base[2];
318 p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0);
321 GdipBitmapSetPixel (bitmap, x, y, p);
326 else {
327 g_warning ("Unsupported number of channels: %d\n", n_channels);
330 return bitmap;
333 static GpBitmap *
334 gdip_buffer_to_bitmap (const gchar *buffer, size_t size, GError **error)
336 HRESULT hr;
337 HGLOBAL hg = NULL;
338 GpBitmap *bitmap = NULL;
339 IStream *stream = NULL;
340 GpStatus status;
342 hg = gdip_buffer_to_hglobal (buffer, size, error);
344 if (!hg)
345 return NULL;
347 hr = CreateStreamOnHGlobal (hg, FALSE, (LPSTREAM *)&stream);
349 if (!SUCCEEDED (hr)) {
350 gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s"));
351 GlobalFree (hg);
352 return NULL;
355 status = GdipCreateBitmapFromStream (stream, &bitmap);
357 if (Ok != status)
358 gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
360 IStream_Release (stream);
361 GlobalFree (hg);
363 return bitmap;
366 static GpImage *
367 gdip_buffer_to_image (const gchar *buffer, size_t size, GError **error)
369 HRESULT hr;
370 HGLOBAL hg = NULL;
371 GpImage *image = NULL;
372 IStream *stream = NULL;
373 GpStatus status;
375 hg = gdip_buffer_to_hglobal (buffer, size, error);
377 if (!hg)
378 return NULL;
380 hr = CreateStreamOnHGlobal (hg, FALSE, (LPSTREAM *)&stream);
382 if (!SUCCEEDED (hr)) {
383 gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s"));
384 GlobalFree (hg);
385 return NULL;
388 status = GdipLoadImageFromStream (stream, &image);
390 if (Ok != status)
391 gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
393 IStream_Release (stream);
394 GlobalFree (hg);
396 return image;
399 static void
400 gdip_bitmap_get_size (GpBitmap *bitmap, guint *width, guint *height)
402 if (bitmap == NULL || width == NULL || height == NULL)
403 return;
405 *width = *height = 0;
407 GdipGetImageWidth ((GpImage *) bitmap, width);
408 GdipGetImageHeight ((GpImage *) bitmap, height);
411 static void
412 gdip_bitmap_get_has_alpha (GpBitmap *bitmap, gboolean *has_alpha)
414 guint flags = 0;
416 if (bitmap == NULL || has_alpha == NULL)
417 return;
419 GdipGetImageFlags ((GpImage *) bitmap, &flags);
420 *has_alpha = (flags & ImageFlagsHasAlpha);
423 static gboolean
424 gdip_bitmap_get_n_frames (GpBitmap *bitmap, guint *n_frames, gboolean timeDimension)
426 if (bitmap == NULL || n_frames == NULL)
427 return FALSE;
429 *n_frames = 1;
431 return (Ok == GdipImageGetFrameCount ((GpImage *) bitmap, (timeDimension ? &FrameDimensionTime : &FrameDimensionPage), n_frames));
434 static gboolean
435 gdip_bitmap_select_frame (GpBitmap *bitmap, guint frame, gboolean timeDimension)
437 if (bitmap == NULL)
438 return FALSE;
440 return (Ok == GdipImageSelectActiveFrame ((GpImage *)bitmap, (timeDimension ? &FrameDimensionTime : &FrameDimensionPage), frame));
443 static gboolean
444 gdip_bitmap_get_property_as_string (GpBitmap *bitmap, guint propertyId, gchar **str)
446 guint item_size;
447 gboolean success = FALSE;
449 if (bitmap == NULL || str == NULL)
450 return FALSE;
452 *str = 0;
454 if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, propertyId, &item_size)) {
455 PropertyItem *item;
457 item = (PropertyItem *)g_try_malloc (item_size);
458 if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, propertyId, item_size, item)) {
459 GString *gstr;
460 int i;
462 gstr = g_string_new (NULL);
464 success = TRUE;
465 switch (item->type) {
466 case PropertyTagTypeByte:
467 for (i = 0; i < item->length / sizeof(guint8); i++) {
468 guint8 *bytes = (guint8 *)item->value;
470 if (gstr->len != 0)
471 g_string_append_c(gstr, ',');
472 g_string_append_printf (gstr, "%u", (guint32)bytes[i]);
474 break;
476 case PropertyTagTypeASCII:
477 g_string_append_len (gstr, (const char *)item->value, item->length);
478 break;
480 case PropertyTagTypeShort:
481 for (i = 0; i < item->length / sizeof(guint16); i++) {
482 guint16 *shorts = (guint16 *)item->value;
484 if (gstr->len != 0)
485 g_string_append_c (gstr, ',');
486 g_string_append_printf (gstr, "%u", (guint32)shorts[i]);
488 break;
490 case PropertyTagTypeLong:
491 for (i = 0; i < item->length / sizeof(guint32); i++) {
492 guint32 *longs = (guint32 *)item->value;
494 if (gstr->len != 0)
495 g_string_append_c (gstr, ',');
496 g_string_append_printf (gstr, "%u", longs[i]);
498 break;
500 case PropertyTagTypeSLONG:
501 for (i = 0; i < item->length / sizeof(guint32); i++) {
502 gint32 *longs = (gint32 *)item->value;
504 if (gstr->len != 0)
505 g_string_append_c (gstr, ',');
506 g_string_append_printf (gstr, "%d", longs[i]);
508 break;
510 default:
511 success = FALSE;
512 break;
515 if (gstr->len > 0)
516 *str = g_string_free (gstr, FALSE);
517 else
518 g_string_free (gstr, TRUE);
521 g_free (item);
524 return success;
527 static gboolean
528 gdip_bitmap_get_frame_delay (GpBitmap *bitmap, guint *delay)
530 guint item_size;
531 gboolean success = FALSE;
533 if (bitmap == NULL || delay == NULL)
534 return FALSE;
536 *delay = 0;
538 if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, PropertyTagFrameDelay, &item_size)) {
539 PropertyItem *item;
541 item = (PropertyItem *)g_try_malloc (item_size);
542 if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, PropertyTagFrameDelay, item_size, item)) {
543 /* PropertyTagFrameDelay. Time delay, in hundredths of a second, between two frames in an animated GIF image. */
544 *delay = *((long *)item->value);
545 success = TRUE;
548 g_free (item);
551 return success;
554 static gboolean
555 gdip_bitmap_get_n_loops (GpBitmap *bitmap, guint *loops)
557 guint item_size;
558 gboolean success = FALSE;
560 if (bitmap == NULL || loops == NULL)
561 return FALSE;
563 *loops = 1;
565 /* PropertyTagLoopCount. 0 == infinitely */
566 if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, PropertyTagLoopCount, &item_size)) {
567 PropertyItem *item;
569 item = (PropertyItem *)g_try_malloc (item_size);
570 if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, PropertyTagLoopCount, item_size, item)) {
571 *loops = *((short *)item->value);
572 success = TRUE;
575 g_free (item);
578 return success;
581 /*************************************************************************/
582 /*************************************************************************/
584 struct _GdipContext {
585 GdkPixbufModuleUpdatedFunc updated_func;
586 GdkPixbufModulePreparedFunc prepared_func;
587 GdkPixbufModuleSizeFunc size_func;
589 gpointer user_data;
591 GByteArray *buffer;
593 typedef struct _GdipContext GdipContext;
595 static void
596 destroy_gdipcontext (GdipContext *context)
598 if (context != NULL) {
599 g_byte_array_free (context->buffer, TRUE);
600 g_free (context);
604 static void
605 emit_updated (GdipContext *context, GdkPixbuf *pixbuf)
607 if (context->updated_func)
608 (*context->updated_func) (pixbuf,
609 0, 0,
610 gdk_pixbuf_get_width (pixbuf),
611 gdk_pixbuf_get_height (pixbuf),
612 context->user_data);
615 static void
616 emit_prepared (GdipContext *context, GdkPixbuf *pixbuf, GdkPixbufAnimation *anim)
618 if (context->prepared_func)
619 (*context->prepared_func) (pixbuf, anim, context->user_data);
622 static gpointer
623 gdk_pixbuf__gdip_image_begin_load (GdkPixbufModuleSizeFunc size_func,
624 GdkPixbufModulePreparedFunc prepared_func,
625 GdkPixbufModuleUpdatedFunc updated_func,
626 gpointer user_data,
627 GError **error)
629 GdipContext *context = g_new0 (GdipContext, 1);
631 context->size_func = size_func;
632 context->prepared_func = prepared_func;
633 context->updated_func = updated_func;
634 context->user_data = user_data;
635 context->buffer = g_byte_array_new ();
637 return context;
640 static gboolean
641 gdk_pixbuf__gdip_image_load_increment (gpointer data,
642 const guchar *buf, guint size,
643 GError **error)
645 GdipContext *context = (GdipContext *)data;
646 GByteArray *image_buffer = context->buffer;
648 g_byte_array_append (image_buffer, (guint8 *)buf, size);
650 return TRUE;
653 static GdkPixbuf *
654 gdip_bitmap_to_pixbuf (GpBitmap *bitmap)
656 GdkPixbuf *pixbuf = NULL;
657 guchar *cursor = NULL;
658 gint rowstride;
659 gboolean has_alpha = FALSE;
660 gint n_channels = 0;
661 gchar *option;
663 guint width = 0, height = 0, x, y;
665 gdip_bitmap_get_size (bitmap, &width, &height);
666 gdip_bitmap_get_has_alpha (bitmap, &has_alpha);
668 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, width, height);
670 if (!pixbuf)
671 return NULL;
673 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
674 cursor = gdk_pixbuf_get_pixels (pixbuf);
675 n_channels = gdk_pixbuf_get_n_channels (pixbuf);
677 for (y = 0; y < height; y++) {
678 for (x = 0; x < width; x++) {
679 ARGB pixel;
680 guchar *b = cursor + (y * rowstride + (x * n_channels));
682 if (Ok != GdipBitmapGetPixel (bitmap, x, y, &pixel)) {
683 g_object_unref (pixbuf);
684 return NULL;
687 b[0] = (pixel & 0xff0000) >> 16;
688 b[1] = (pixel & 0x00ff00) >> 8;
689 b[2] = (pixel & 0x0000ff) >> 0;
691 if (has_alpha)
692 b[3] = (pixel & 0xff000000) >> 24;
696 if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagOrientation, &option)) {
697 gdk_pixbuf_set_option (pixbuf, "orientation", option);
698 g_free (option);
701 if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagArtist, &option)) {
702 gdk_pixbuf_set_option (pixbuf, "Author", option);
703 g_free (option);
706 if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagImageTitle, &option)) {
707 gdk_pixbuf_set_option (pixbuf, "Title", option);
708 g_free (option);
711 return pixbuf;
714 static gboolean
715 stop_load (GpBitmap *bitmap, GdipContext *context, GError **error)
717 guint n_frames = 1, i;
718 GdkPixbufGdipAnim *animation = NULL;
720 gdip_bitmap_get_n_frames (bitmap, &n_frames, TRUE);
722 for (i = 0; i < n_frames; i++) {
723 GdkPixbuf *pixbuf = NULL;
724 GdkPixbufFrame *frame;
725 guint frame_delay = 0;
727 gdip_bitmap_select_frame (bitmap, i, TRUE);
729 pixbuf = gdip_bitmap_to_pixbuf (bitmap);
731 if (!pixbuf) {
732 if (animation != NULL)
733 g_object_unref (G_OBJECT (animation));
735 destroy_gdipcontext (context);
736 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't create pixbuf"));
737 return FALSE;
740 if (animation == NULL) {
741 guint n_loops = 1;
743 animation = g_object_new (GDK_TYPE_PIXBUF_GDIP_ANIM, NULL);
744 gdip_bitmap_get_n_loops (bitmap, &n_loops);
745 animation->loop = n_loops;
748 frame = g_new (GdkPixbufFrame, 1);
749 frame->pixbuf = pixbuf;
751 gdip_bitmap_get_frame_delay (bitmap, &frame_delay);
753 animation->n_frames++;
754 animation->frames = g_list_append (animation->frames, frame);
756 animation->width = gdk_pixbuf_get_width (pixbuf);
757 animation->height = gdk_pixbuf_get_height (pixbuf);
759 /* GIF delay is in hundredths, we want thousandths */
760 frame->delay_time = frame_delay * 10;
761 frame->elapsed = animation->total_time;
763 /* Some GIFs apparently have delay time of 0,
764 * that crashes everything so set it to "fast".
765 * Also, timeouts less than 20 or so just lock up
766 * the app or make the animation choppy, so fix them.
768 if (frame->delay_time < 20)
769 frame->delay_time = 20; /* 20 = "fast" */
771 animation->total_time += frame->delay_time;
773 if (i == 0)
774 emit_prepared (context, pixbuf, GDK_PIXBUF_ANIMATION (animation));
776 emit_updated (context, pixbuf);
779 if (animation != NULL)
780 g_object_unref (G_OBJECT (animation));
782 destroy_gdipcontext (context);
784 return TRUE;
787 static gboolean
788 gdk_pixbuf__gdip_image_stop_load (gpointer data, GError **error)
790 GdipContext *context = (GdipContext *)data;
791 GpBitmap *bitmap = NULL;
792 GByteArray *image_buffer = context->buffer;
794 bitmap = gdip_buffer_to_bitmap ((gchar *)image_buffer->data, image_buffer->len, error);
796 if (!bitmap) {
797 destroy_gdipcontext (context);
798 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't load bitmap"));
799 return FALSE;
802 return stop_load (bitmap, context, error);
805 static gboolean
806 gdk_pixbuf__gdip_image_stop_vector_load (gpointer data, GError **error)
808 GdipContext *context = (GdipContext *)data;
809 GByteArray *image_buffer = context->buffer;
811 GpImage *metafile;
812 GpGraphics *graphics;
813 GpBitmap *bitmap;
814 GpStatus status;
815 float metafile_xres, metafile_yres;
816 guint width, height;
818 metafile = gdip_buffer_to_image ((gchar *)image_buffer->data, image_buffer->len, error);
819 if (!metafile) {
820 destroy_gdipcontext (context);
821 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't load metafile"));
822 return FALSE;
825 GdipGetImageWidth (metafile, &width);
826 GdipGetImageHeight (metafile, &height);
828 status = GdipCreateBitmapFromScan0 (width, height, 0, PixelFormat32bppARGB, NULL, &bitmap);
829 if (Ok != status) {
830 gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
831 GdipDisposeImage (metafile);
833 return FALSE;
836 GdipGetImageHorizontalResolution (metafile, &metafile_xres);
837 GdipGetImageVerticalResolution (metafile, &metafile_yres);
838 GdipBitmapSetResolution (bitmap, metafile_xres, metafile_yres);
840 status = GdipGetImageGraphicsContext ((GpImage *)bitmap, &graphics);
841 if (Ok != status) {
842 gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
843 GdipDisposeImage ((GpImage *)bitmap);
844 GdipDisposeImage (metafile);
846 return FALSE;
849 /* gotta clear the bitmap */
850 GdipGraphicsClear (graphics, 0xffffffff);
852 status = GdipDrawImageI (graphics, metafile, 0, 0);
853 if (Ok != status) {
854 gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status);
855 GdipDeleteGraphics (graphics);
856 GdipDisposeImage ((GpImage *)bitmap);
857 GdipDisposeImage (metafile);
859 return FALSE;
862 GdipFlush (graphics, 1);
864 GdipDeleteGraphics (graphics);
865 GdipDisposeImage (metafile);
867 return stop_load (bitmap, context, error);
870 static void
871 gdip_animation_prepare (GdkPixbuf *pixbuf,
872 GdkPixbufAnimation *animation,
873 gpointer user_data)
875 GdkPixbufAnimation **anim;
877 anim = (GdkPixbufAnimation **)user_data;
879 /* save a reference to the animation */
880 g_object_ref (animation);
881 *anim = animation;
884 static GdkPixbufAnimation *
885 gdk_pixbuf__gdip_image_load_animation (FILE *file,
886 GError **error)
888 GdkPixbufAnimation *animation = NULL;
890 gpointer context;
891 char buffer[LOAD_BUFFER_SIZE];
892 size_t length;
894 context = gdk_pixbuf__gdip_image_begin_load (NULL, gdip_animation_prepare, NULL, &animation, error);
896 while (!feof (file) && !ferror (file)) {
897 length = fread (buffer, 1, sizeof (buffer), file);
898 if (length > 0) {
899 if (!gdk_pixbuf__gdip_image_load_increment (context, buffer, length, error)) {
900 gdk_pixbuf__gdip_image_stop_load (context, NULL);
902 if (animation)
903 g_object_unref (animation);
905 return NULL;
910 if (!gdk_pixbuf__gdip_image_stop_load(context, error)) {
911 if (animation)
912 g_object_unref (animation);
914 return NULL;
917 return animation;
920 gboolean
921 gdip_save_to_file_callback (const gchar *buf,
922 gsize count,
923 GError **error,
924 gpointer data)
926 FILE *filehandle = data;
927 gsize n;
929 n = fwrite (buf, 1, count, filehandle);
930 if (n != count) {
931 gint save_errno = errno;
932 g_set_error (error,
933 G_FILE_ERROR,
934 g_file_error_from_errno (save_errno),
935 _("Error writing to image file: %s"),
936 g_strerror (save_errno));
937 return FALSE;
940 return TRUE;
943 void
944 gdip_fill_vtable (GdkPixbufModule *module)
946 if (gdip_init ()) {
947 module->begin_load = gdk_pixbuf__gdip_image_begin_load;
948 module->stop_load = gdk_pixbuf__gdip_image_stop_load;
949 module->load_increment = gdk_pixbuf__gdip_image_load_increment;
951 /* this is the only way to get gtk_image_new_from_file() to load animations. it regrettably
952 does not use the GdkPixbufLoader interface. */
953 module->load_animation = gdk_pixbuf__gdip_image_load_animation;
957 void
958 gdip_fill_vector_vtable (GdkPixbufModule *module)
960 if (gdip_init ()) {
961 module->begin_load = gdk_pixbuf__gdip_image_begin_load;
962 module->stop_load = gdk_pixbuf__gdip_image_stop_vector_load;
963 module->load_increment = gdk_pixbuf__gdip_image_load_increment;
967 gboolean
968 gdip_save_pixbuf (GdkPixbuf *pixbuf,
969 const WCHAR *format,
970 const EncoderParameters *encoder_params,
971 GdkPixbufSaveFunc save_func,
972 gpointer user_data,
973 GError **error)
975 GpBitmap *image;
976 CLSID clsid;
977 gboolean success;
979 if (!GetEncoderClsid (format, &clsid)) {
980 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Unsupported image format for GDI+"));
981 return FALSE;
984 image = gdip_pixbuf_to_bitmap (pixbuf);
986 if (image == NULL) {
987 g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Couldn't save"));
988 return FALSE;
991 success = gdip_save_bitmap_to_callback (image, &clsid, encoder_params, save_func, user_data, error);
993 GdipDisposeImage ((GpImage *)image);
995 return success;