/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* GdkPixbuf library - Win32 GDI+ Pixbuf Loader * * Copyright (C) 2008 Dominic Lachowicz * Copyright (C) 2008 Alberto Ruiz * * Authors: Dominic Lachowicz * Alberto Ruiz * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #define INITGUID #include #include "io-gdip-utils.h" #include "io-gdip-native.h" #include "io-gdip-propertytags.h" #include "io-gdip-animation.h" #define LOAD_BUFFER_SIZE 65536 static GdiplusStartupFunc GdiplusStartup; static GdipCreateBitmapFromStreamFunc GdipCreateBitmapFromStream; static GdipBitmapGetPixelFunc GdipBitmapGetPixel; static GdipGetImageHeightFunc GdipGetImageHeight; static GdipDisposeImageFunc GdipDisposeImage; static GdipGetImageFlagsFunc GdipGetImageFlags; static GdipGetImageWidthFunc GdipGetImageWidth; static GdipImageGetFrameCountFunc GdipImageGetFrameCount; static GdipImageSelectActiveFrameFunc GdipImageSelectActiveFrame; static GdipGetPropertyItemSizeFunc GdipGetPropertyItemSize; static GdipGetPropertyItemFunc GdipGetPropertyItem; static GdipGetPropertyCountFunc GdipGetPropertyCount; static GdipGetPropertyIdListFunc GdipGetPropertyIdList; static GdipCreateBitmapFromScan0Func GdipCreateBitmapFromScan0; static GdipSaveImageToStreamFunc GdipSaveImageToStream; static GdipBitmapSetPixelFunc GdipBitmapSetPixel; static GdipDrawImageIFunc GdipDrawImageI; static GdipGetImageGraphicsContextFunc GdipGetImageGraphicsContext; static GdipFlushFunc GdipFlush; static GdipGraphicsClearFunc GdipGraphicsClear; static GdipBitmapSetResolutionFunc GdipBitmapSetResolution; static GdipGetImageHorizontalResolutionFunc GdipGetImageHorizontalResolution; static GdipGetImageVerticalResolutionFunc GdipGetImageVerticalResolution; static GdipLoadImageFromStreamFunc GdipLoadImageFromStream; static GdipDeleteGraphicsFunc GdipDeleteGraphics; static GdipGetImageEncodersFunc GdipGetImageEncoders; static GdipGetImageEncodersSizeFunc GdipGetImageEncodersSize; DEFINE_GUID(FrameDimensionTime, 0x6aedbd6d,0x3fb5,0x418a,0x83,0xa6,0x7f,0x45,0x22,0x9d,0xc8,0x72); DEFINE_GUID(FrameDimensionPage, 0x7462dc86,0x6180,0x4c7e,0x8e,0x3f,0xee,0x73,0x33,0xa7,0xa4,0x83); static void gdip_set_error_from_hresult (GError **error, gint code, HRESULT hr, const char *format) { gchar *msg; msg = g_win32_error_message (hr); if (msg) { g_set_error (error, GDK_PIXBUF_ERROR, code, format, msg); g_free (msg); } } static void gdip_set_error_from_gpstatus (GError **error, gint code, GpStatus status) { const char *msg; switch (status) { #define CASE(x) case x: msg = #x; break CASE (GenericError); CASE (InvalidParameter); CASE (OutOfMemory); CASE (ObjectBusy); CASE (InsufficientBuffer); CASE (NotImplemented); CASE (Win32Error); CASE (WrongState); CASE (Aborted); CASE (FileNotFound); CASE (ValueOverflow); CASE (AccessDenied); CASE (UnknownImageFormat); CASE (FontFamilyNotFound); CASE (FontStyleNotFound); CASE (NotTrueTypeFont); CASE (UnsupportedGdiplusVersion); CASE (GdiplusNotInitialized); CASE (PropertyNotFound); CASE (PropertyNotSupported); CASE (ProfileNotFound); default: msg = "Unknown error"; } g_set_error (error, GDK_PIXBUF_ERROR, code, msg); } static gboolean gdip_init (void) { GdiplusStartupInput input; ULONG_PTR gdiplusToken = 0; static HINSTANCE gdipluslib = NULL; if (!gdipluslib) gdipluslib = LoadLibrary ("gdiplus.dll"); else return TRUE; /* gdip_init() is idempotent */ if (!gdipluslib) return FALSE; #define LOOKUP(func) \ G_STMT_START { \ func = (func##Func) GetProcAddress (gdipluslib, #func); \ if (!func) {\ g_warning ("Couldn't find GDI+ function %s\n", #func); \ return FALSE; \ } \ } G_STMT_END LOOKUP (GdiplusStartup); LOOKUP (GdipCreateBitmapFromStream); LOOKUP (GdipBitmapGetPixel); LOOKUP (GdipGetImageHeight); LOOKUP (GdipDisposeImage); LOOKUP (GdipGetImageFlags); LOOKUP (GdipGetImageWidth); LOOKUP (GdipImageGetFrameCount); LOOKUP (GdipImageSelectActiveFrame); LOOKUP (GdipGetPropertyItemSize); LOOKUP (GdipGetPropertyItem); LOOKUP (GdipGetPropertyCount); LOOKUP (GdipGetPropertyIdList); LOOKUP (GdipCreateBitmapFromScan0); LOOKUP (GdipSaveImageToStream); LOOKUP (GdipBitmapSetPixel); LOOKUP (GdipDrawImageI); LOOKUP (GdipGetImageGraphicsContext); LOOKUP (GdipFlush); LOOKUP (GdipGraphicsClear); LOOKUP (GdipBitmapSetResolution); LOOKUP (GdipGetImageHorizontalResolution); LOOKUP (GdipGetImageVerticalResolution); LOOKUP (GdipLoadImageFromStream); LOOKUP (GdipDeleteGraphics); LOOKUP (GdipGetImageEncoders); LOOKUP (GdipGetImageEncodersSize); #undef LOOKUP input.GdiplusVersion = 1; input.DebugEventCallback = NULL; input.SuppressBackgroundThread = input.SuppressExternalCodecs = FALSE; return (GdiplusStartup (&gdiplusToken, &input, NULL) == 0 ? TRUE : FALSE); } static gboolean GetEncoderClsid (const WCHAR *format, CLSID *pClsid) { UINT num, size; int j; ImageCodecInfo *pImageCodecInfo; if (Ok != GdipGetImageEncodersSize (&num, &size)) return FALSE; pImageCodecInfo = (ImageCodecInfo *) g_malloc (size); if (Ok != GdipGetImageEncoders (num, size, pImageCodecInfo)) { g_free (pImageCodecInfo); return FALSE; } for (j = 0; j < num; j++) { if (wcscmp (pImageCodecInfo[j].MimeType, format) == 0) { *pClsid = pImageCodecInfo[j].Clsid; g_free (pImageCodecInfo); return TRUE; } } g_free (pImageCodecInfo); return FALSE; } static HGLOBAL gdip_buffer_to_hglobal (const gchar *buffer, size_t size, GError **error) { HGLOBAL hg = NULL; hg = GlobalAlloc (GPTR, size); if (!hg) { gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, GetLastError (), _("Could not allocate memory: %s")); return NULL; } CopyMemory (hg, buffer, size); return hg; } static gboolean gdip_save_bitmap_to_callback (GpBitmap *bitmap, const CLSID *format, const EncoderParameters *encoder_params, GdkPixbufSaveFunc save_func, gpointer user_data, GError **error) { HRESULT hr; IStream *streamOut = NULL; gboolean success = FALSE; guint64 zero = 0; GpStatus status; hr = CreateStreamOnHGlobal (NULL, TRUE, &streamOut); if (!SUCCEEDED (hr)) { gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s")); return FALSE; } status = GdipSaveImageToStream ((GpImage *)bitmap, streamOut, format, encoder_params); if (Ok != status) { gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status); IStream_Release (streamOut); return FALSE; } /* seek back to the beginning of the stream */ hr = IStream_Seek (streamOut, *(LARGE_INTEGER *)&zero, STREAM_SEEK_SET, NULL); if (!SUCCEEDED (hr)) { gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not seek stream: %s")); IStream_Release (streamOut); return FALSE; } for (;;) { char buffer[LOAD_BUFFER_SIZE]; ULONG nread; hr = IStream_Read (streamOut, buffer, sizeof(buffer), &nread); if (!SUCCEEDED (hr)) { gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not read from stream: %s")); break; } else if (0 == nread) { success = TRUE; /* EOF */ break; } else if (!(*save_func) (buffer, nread, error, user_data)) break; } IStream_Release (streamOut); return success; } static GpBitmap * gdip_pixbuf_to_bitmap (GdkPixbuf *pixbuf) { GpBitmap *bitmap = NULL; int width, height, stride, n_channels; guint8 *pixels; width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); stride = gdk_pixbuf_get_rowstride (pixbuf); n_channels = gdk_pixbuf_get_n_channels (pixbuf); pixels = gdk_pixbuf_get_pixels (pixbuf); if (n_channels == 3 || n_channels == 4) { /* rgbX. need to convert to argb. pass a null data to get an empty bitmap */ GdipCreateBitmapFromScan0 (width, height, 0, PixelFormat32bppARGB, NULL, &bitmap); if (bitmap) { int x, y; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { ARGB p; guint8 alpha; guchar *base = pixels + (y * stride + (x * n_channels)); if (n_channels == 4) alpha = base[3]; else alpha = 0xff; if (alpha == 0) p = 0; else { guint8 red = base[0]; guint8 green = base[1]; guint8 blue = base[2]; p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); } GdipBitmapSetPixel (bitmap, x, y, p); } } } } else { g_warning ("Unsupported number of channels: %d\n", n_channels); } return bitmap; } static GpBitmap * gdip_buffer_to_bitmap (const gchar *buffer, size_t size, GError **error) { HRESULT hr; HGLOBAL hg = NULL; GpBitmap *bitmap = NULL; IStream *stream = NULL; GpStatus status; hg = gdip_buffer_to_hglobal (buffer, size, error); if (!hg) return NULL; hr = CreateStreamOnHGlobal (hg, FALSE, (LPSTREAM *)&stream); if (!SUCCEEDED (hr)) { gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s")); GlobalFree (hg); return NULL; } status = GdipCreateBitmapFromStream (stream, &bitmap); if (Ok != status) gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status); IStream_Release (stream); GlobalFree (hg); return bitmap; } static GpImage * gdip_buffer_to_image (const gchar *buffer, size_t size, GError **error) { HRESULT hr; HGLOBAL hg = NULL; GpImage *image = NULL; IStream *stream = NULL; GpStatus status; hg = gdip_buffer_to_hglobal (buffer, size, error); if (!hg) return NULL; hr = CreateStreamOnHGlobal (hg, FALSE, (LPSTREAM *)&stream); if (!SUCCEEDED (hr)) { gdip_set_error_from_hresult (error, GDK_PIXBUF_ERROR_FAILED, hr, _("Could not create stream: %s")); GlobalFree (hg); return NULL; } status = GdipLoadImageFromStream (stream, &image); if (Ok != status) gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status); IStream_Release (stream); GlobalFree (hg); return image; } static void gdip_bitmap_get_size (GpBitmap *bitmap, guint *width, guint *height) { if (bitmap == NULL || width == NULL || height == NULL) return; *width = *height = 0; GdipGetImageWidth ((GpImage *) bitmap, width); GdipGetImageHeight ((GpImage *) bitmap, height); } static void gdip_bitmap_get_has_alpha (GpBitmap *bitmap, gboolean *has_alpha) { guint flags = 0; if (bitmap == NULL || has_alpha == NULL) return; GdipGetImageFlags ((GpImage *) bitmap, &flags); *has_alpha = (flags & ImageFlagsHasAlpha); } static gboolean gdip_bitmap_get_n_frames (GpBitmap *bitmap, guint *n_frames, gboolean timeDimension) { if (bitmap == NULL || n_frames == NULL) return FALSE; *n_frames = 1; return (Ok == GdipImageGetFrameCount ((GpImage *) bitmap, (timeDimension ? &FrameDimensionTime : &FrameDimensionPage), n_frames)); } static gboolean gdip_bitmap_select_frame (GpBitmap *bitmap, guint frame, gboolean timeDimension) { if (bitmap == NULL) return FALSE; return (Ok == GdipImageSelectActiveFrame ((GpImage *)bitmap, (timeDimension ? &FrameDimensionTime : &FrameDimensionPage), frame)); } static gboolean gdip_bitmap_get_property_as_string (GpBitmap *bitmap, guint propertyId, gchar **str) { guint item_size; gboolean success = FALSE; if (bitmap == NULL || str == NULL) return FALSE; *str = 0; if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, propertyId, &item_size)) { PropertyItem *item; item = (PropertyItem *)g_try_malloc (item_size); if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, propertyId, item_size, item)) { GString *gstr; int i; gstr = g_string_new (NULL); success = TRUE; switch (item->type) { case PropertyTagTypeByte: for (i = 0; i < item->length / sizeof(guint8); i++) { guint8 *bytes = (guint8 *)item->value; if (gstr->len != 0) g_string_append_c(gstr, ','); g_string_append_printf (gstr, "%u", (guint32)bytes[i]); } break; case PropertyTagTypeASCII: g_string_append_len (gstr, (const char *)item->value, item->length); break; case PropertyTagTypeShort: for (i = 0; i < item->length / sizeof(guint16); i++) { guint16 *shorts = (guint16 *)item->value; if (gstr->len != 0) g_string_append_c (gstr, ','); g_string_append_printf (gstr, "%u", (guint32)shorts[i]); } break; case PropertyTagTypeLong: for (i = 0; i < item->length / sizeof(guint32); i++) { guint32 *longs = (guint32 *)item->value; if (gstr->len != 0) g_string_append_c (gstr, ','); g_string_append_printf (gstr, "%u", longs[i]); } break; case PropertyTagTypeSLONG: for (i = 0; i < item->length / sizeof(guint32); i++) { gint32 *longs = (gint32 *)item->value; if (gstr->len != 0) g_string_append_c (gstr, ','); g_string_append_printf (gstr, "%d", longs[i]); } break; default: success = FALSE; break; } if (gstr->len > 0) *str = g_string_free (gstr, FALSE); else g_string_free (gstr, TRUE); } g_free (item); } return success; } static gboolean gdip_bitmap_get_frame_delay (GpBitmap *bitmap, guint *delay) { guint item_size; gboolean success = FALSE; if (bitmap == NULL || delay == NULL) return FALSE; *delay = 0; if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, PropertyTagFrameDelay, &item_size)) { PropertyItem *item; item = (PropertyItem *)g_try_malloc (item_size); if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, PropertyTagFrameDelay, item_size, item)) { /* PropertyTagFrameDelay. Time delay, in hundredths of a second, between two frames in an animated GIF image. */ *delay = *((long *)item->value); success = TRUE; } g_free (item); } return success; } static gboolean gdip_bitmap_get_n_loops (GpBitmap *bitmap, guint *loops) { guint item_size; gboolean success = FALSE; if (bitmap == NULL || loops == NULL) return FALSE; *loops = 1; /* PropertyTagLoopCount. 0 == infinitely */ if (Ok == GdipGetPropertyItemSize ((GpImage *)bitmap, PropertyTagLoopCount, &item_size)) { PropertyItem *item; item = (PropertyItem *)g_try_malloc (item_size); if (Ok == GdipGetPropertyItem ((GpImage *)bitmap, PropertyTagLoopCount, item_size, item)) { *loops = *((short *)item->value); success = TRUE; } g_free (item); } return success; } /*************************************************************************/ /*************************************************************************/ struct _GdipContext { GdkPixbufModuleUpdatedFunc updated_func; GdkPixbufModulePreparedFunc prepared_func; GdkPixbufModuleSizeFunc size_func; gpointer user_data; GByteArray *buffer; }; typedef struct _GdipContext GdipContext; static void destroy_gdipcontext (GdipContext *context) { if (context != NULL) { g_byte_array_free (context->buffer, TRUE); g_free (context); } } static void emit_updated (GdipContext *context, GdkPixbuf *pixbuf) { if (context->updated_func) (*context->updated_func) (pixbuf, 0, 0, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), context->user_data); } static void emit_prepared (GdipContext *context, GdkPixbuf *pixbuf, GdkPixbufAnimation *anim) { if (context->prepared_func) (*context->prepared_func) (pixbuf, anim, context->user_data); } static gpointer gdk_pixbuf__gdip_image_begin_load (GdkPixbufModuleSizeFunc size_func, GdkPixbufModulePreparedFunc prepared_func, GdkPixbufModuleUpdatedFunc updated_func, gpointer user_data, GError **error) { GdipContext *context = g_new0 (GdipContext, 1); context->size_func = size_func; context->prepared_func = prepared_func; context->updated_func = updated_func; context->user_data = user_data; context->buffer = g_byte_array_new (); return context; } static gboolean gdk_pixbuf__gdip_image_load_increment (gpointer data, const guchar *buf, guint size, GError **error) { GdipContext *context = (GdipContext *)data; GByteArray *image_buffer = context->buffer; g_byte_array_append (image_buffer, (guint8 *)buf, size); return TRUE; } static GdkPixbuf * gdip_bitmap_to_pixbuf (GpBitmap *bitmap) { GdkPixbuf *pixbuf = NULL; guchar *cursor = NULL; gint rowstride; gboolean has_alpha = FALSE; gint n_channels = 0; gchar *option; guint width = 0, height = 0, x, y; gdip_bitmap_get_size (bitmap, &width, &height); gdip_bitmap_get_has_alpha (bitmap, &has_alpha); pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, width, height); if (!pixbuf) return NULL; rowstride = gdk_pixbuf_get_rowstride (pixbuf); cursor = gdk_pixbuf_get_pixels (pixbuf); n_channels = gdk_pixbuf_get_n_channels (pixbuf); for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { ARGB pixel; guchar *b = cursor + (y * rowstride + (x * n_channels)); if (Ok != GdipBitmapGetPixel (bitmap, x, y, &pixel)) { g_object_unref (pixbuf); return NULL; } b[0] = (pixel & 0xff0000) >> 16; b[1] = (pixel & 0x00ff00) >> 8; b[2] = (pixel & 0x0000ff) >> 0; if (has_alpha) b[3] = (pixel & 0xff000000) >> 24; } } if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagOrientation, &option)) { gdk_pixbuf_set_option (pixbuf, "orientation", option); g_free (option); } if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagArtist, &option)) { gdk_pixbuf_set_option (pixbuf, "Author", option); g_free (option); } if (gdip_bitmap_get_property_as_string (bitmap, PropertyTagImageTitle, &option)) { gdk_pixbuf_set_option (pixbuf, "Title", option); g_free (option); } return pixbuf; } static gboolean stop_load (GpBitmap *bitmap, GdipContext *context, GError **error) { guint n_frames = 1, i; GdkPixbufGdipAnim *animation = NULL; gdip_bitmap_get_n_frames (bitmap, &n_frames, TRUE); for (i = 0; i < n_frames; i++) { GdkPixbuf *pixbuf = NULL; GdkPixbufFrame *frame; guint frame_delay = 0; gdip_bitmap_select_frame (bitmap, i, TRUE); pixbuf = gdip_bitmap_to_pixbuf (bitmap); if (!pixbuf) { if (animation != NULL) g_object_unref (G_OBJECT (animation)); destroy_gdipcontext (context); g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Couldn't create pixbuf")); return FALSE; } if (animation == NULL) { guint n_loops = 1; animation = g_object_new (GDK_TYPE_PIXBUF_GDIP_ANIM, NULL); gdip_bitmap_get_n_loops (bitmap, &n_loops); animation->loop = n_loops; } frame = g_new (GdkPixbufFrame, 1); frame->pixbuf = pixbuf; gdip_bitmap_get_frame_delay (bitmap, &frame_delay); animation->n_frames++; animation->frames = g_list_append (animation->frames, frame); animation->width = gdk_pixbuf_get_width (pixbuf); animation->height = gdk_pixbuf_get_height (pixbuf); /* GIF delay is in hundredths, we want thousandths */ frame->delay_time = frame_delay * 10; frame->elapsed = animation->total_time; /* Some GIFs apparently have delay time of 0, * that crashes everything so set it to "fast". * Also, timeouts less than 20 or so just lock up * the app or make the animation choppy, so fix them. */ if (frame->delay_time < 20) frame->delay_time = 20; /* 20 = "fast" */ animation->total_time += frame->delay_time; if (i == 0) emit_prepared (context, pixbuf, GDK_PIXBUF_ANIMATION (animation)); emit_updated (context, pixbuf); } if (animation != NULL) g_object_unref (G_OBJECT (animation)); destroy_gdipcontext (context); return TRUE; } static gboolean gdk_pixbuf__gdip_image_stop_load (gpointer data, GError **error) { GdipContext *context = (GdipContext *)data; GpBitmap *bitmap = NULL; GByteArray *image_buffer = context->buffer; bitmap = gdip_buffer_to_bitmap ((gchar *)image_buffer->data, image_buffer->len, error); if (!bitmap) { destroy_gdipcontext (context); g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't load bitmap")); return FALSE; } return stop_load (bitmap, context, error); } static gboolean gdk_pixbuf__gdip_image_stop_vector_load (gpointer data, GError **error) { GdipContext *context = (GdipContext *)data; GByteArray *image_buffer = context->buffer; GpImage *metafile; GpGraphics *graphics; GpBitmap *bitmap; GpStatus status; float metafile_xres, metafile_yres; guint width, height; metafile = gdip_buffer_to_image ((gchar *)image_buffer->data, image_buffer->len, error); if (!metafile) { destroy_gdipcontext (context); g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, _("Couldn't load metafile")); return FALSE; } GdipGetImageWidth (metafile, &width); GdipGetImageHeight (metafile, &height); status = GdipCreateBitmapFromScan0 (width, height, 0, PixelFormat32bppARGB, NULL, &bitmap); if (Ok != status) { gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status); GdipDisposeImage (metafile); return FALSE; } GdipGetImageHorizontalResolution (metafile, &metafile_xres); GdipGetImageVerticalResolution (metafile, &metafile_yres); GdipBitmapSetResolution (bitmap, metafile_xres, metafile_yres); status = GdipGetImageGraphicsContext ((GpImage *)bitmap, &graphics); if (Ok != status) { gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status); GdipDisposeImage ((GpImage *)bitmap); GdipDisposeImage (metafile); return FALSE; } /* gotta clear the bitmap */ GdipGraphicsClear (graphics, 0xffffffff); status = GdipDrawImageI (graphics, metafile, 0, 0); if (Ok != status) { gdip_set_error_from_gpstatus (error, GDK_PIXBUF_ERROR_FAILED, status); GdipDeleteGraphics (graphics); GdipDisposeImage ((GpImage *)bitmap); GdipDisposeImage (metafile); return FALSE; } GdipFlush (graphics, 1); GdipDeleteGraphics (graphics); GdipDisposeImage (metafile); return stop_load (bitmap, context, error); } static void gdip_animation_prepare (GdkPixbuf *pixbuf, GdkPixbufAnimation *animation, gpointer user_data) { GdkPixbufAnimation **anim; anim = (GdkPixbufAnimation **)user_data; /* save a reference to the animation */ g_object_ref (animation); *anim = animation; } static GdkPixbufAnimation * gdk_pixbuf__gdip_image_load_animation (FILE *file, GError **error) { GdkPixbufAnimation *animation = NULL; gpointer context; char buffer[LOAD_BUFFER_SIZE]; size_t length; context = gdk_pixbuf__gdip_image_begin_load (NULL, gdip_animation_prepare, NULL, &animation, error); while (!feof (file) && !ferror (file)) { length = fread (buffer, 1, sizeof (buffer), file); if (length > 0) { if (!gdk_pixbuf__gdip_image_load_increment (context, buffer, length, error)) { gdk_pixbuf__gdip_image_stop_load (context, NULL); if (animation) g_object_unref (animation); return NULL; } } } if (!gdk_pixbuf__gdip_image_stop_load(context, error)) { if (animation) g_object_unref (animation); return NULL; } return animation; } gboolean gdip_save_to_file_callback (const gchar *buf, gsize count, GError **error, gpointer data) { FILE *filehandle = data; gsize n; n = fwrite (buf, 1, count, filehandle); if (n != count) { gint save_errno = errno; g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (save_errno), _("Error writing to image file: %s"), g_strerror (save_errno)); return FALSE; } return TRUE; } void gdip_fill_vtable (GdkPixbufModule *module) { if (gdip_init ()) { module->begin_load = gdk_pixbuf__gdip_image_begin_load; module->stop_load = gdk_pixbuf__gdip_image_stop_load; module->load_increment = gdk_pixbuf__gdip_image_load_increment; /* this is the only way to get gtk_image_new_from_file() to load animations. it regrettably does not use the GdkPixbufLoader interface. */ module->load_animation = gdk_pixbuf__gdip_image_load_animation; } } void gdip_fill_vector_vtable (GdkPixbufModule *module) { if (gdip_init ()) { module->begin_load = gdk_pixbuf__gdip_image_begin_load; module->stop_load = gdk_pixbuf__gdip_image_stop_vector_load; module->load_increment = gdk_pixbuf__gdip_image_load_increment; } } gboolean gdip_save_pixbuf (GdkPixbuf *pixbuf, const WCHAR *format, const EncoderParameters *encoder_params, GdkPixbufSaveFunc save_func, gpointer user_data, GError **error) { GpBitmap *image; CLSID clsid; gboolean success; if (!GetEncoderClsid (format, &clsid)) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Unsupported image format for GDI+")); return FALSE; } image = gdip_pixbuf_to_bitmap (pixbuf); if (image == NULL) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Couldn't save")); return FALSE; } success = gdip_save_bitmap_to_callback (image, &clsid, encoder_params, save_func, user_data, error); GdipDisposeImage ((GpImage *)image); return success; }