[libgxps] Add support to wdp images on Windows using WIC API



commit 6bf9be28f1d3c3340d95588bfa54465d5be151a9
Author: Vittorio Vaselli <vitvas amazon com>
Date:   Wed Jul 29 11:09:26 2020 +0200

    Add support to wdp images on Windows using WIC API

 libgxps/gxps-images.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 287 insertions(+)
---
diff --git a/libgxps/gxps-images.c b/libgxps/gxps-images.c
index 50f899f..464d848 100644
--- a/libgxps/gxps-images.c
+++ b/libgxps/gxps-images.c
@@ -44,6 +44,13 @@
 #define METERS_PER_INCH 0.0254
 #define CENTIMETERS_PER_INCH 2.54
 
+#ifdef G_OS_WIN32
+#define COBJMACROS
+#include <wincodec.h>
+#include <wincodecsdk.h>
+#include <combaseapi.h>
+#endif
+
 /* PNG */
 #ifdef HAVE_LIBPNG
 
@@ -870,6 +877,282 @@ gxps_images_create_from_tiff (GXPSArchive *zip,
 #endif /* #ifdef HAVE_LIBTIFF */
 }
 
+#ifdef G_OS_WIN32
+static GXPSImage *
+image_create_from_byte_array (BYTE    *bytes,
+                              int      width,
+                              int      height,
+                              UINT     buffer_size,
+                              GError **error)
+{
+       int stride;
+       guchar *data;
+       GXPSImage  *image;
+       cairo_status_t status;
+
+       data = g_try_malloc (buffer_size);
+
+       if (data == NULL) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error allocating data buffer for cairo surface");
+               return NULL;
+       }
+
+       memcpy (data, bytes, buffer_size);
+
+       stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width);
+
+       image = g_slice_new0 (GXPSImage);
+       image->res_x = 96;
+       image->res_y = 96;
+
+       image->surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, width, height, 
stride);
+       if (cairo_surface_status (image->surface) != CAIRO_STATUS_SUCCESS) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error creating cairo surface");
+               gxps_image_free (image);
+               g_free (data);
+               return NULL;
+       }
+
+       status = cairo_surface_set_user_data (image->surface,
+                                             &image_data_cairo_key,
+                                             data,
+                                             (cairo_destroy_func_t) g_free);
+       if (status) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error setting surface user data");
+               gxps_image_free (image);
+               g_free (data);
+               return NULL;
+       }
+
+       return image;
+}
+
+static GXPSImage *
+gxps_images_create_from_wdp (GXPSArchive *zip,
+                             const gchar *image_uri,
+                             GError     **error)
+{
+#define buffer_size 1024
+       GInputStream  *stream;
+       GXPSImage *image;
+       IID iid_imaging_factory;
+       HRESULT hr;
+       IWICImagingFactory *image_factory;
+       IWICBitmapDecoder *decoder;
+       IWICBitmapFrameDecode *decoder_frame;
+       IWICBitmap *bitmap;
+       IWICBitmapLock *bitmap_lock;
+       IStream *win_stream;
+       UINT width;
+       UINT height;
+       guchar buffer[buffer_size];
+       gsize read_bytes;
+       gsize nwritten;
+       UINT written_bytes;
+       UINT bytes_size = 0;
+       BYTE *bytes = NULL;
+       WICRect rc_lock;
+
+       stream = gxps_archive_open (zip, image_uri);
+       if (!stream) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_SOURCE_NOT_FOUND,
+                            "Image source %s not found in archive",
+                            image_uri);
+               return NULL;
+       }
+
+       /* Initialize COM. */
+       hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
+       if (!SUCCEEDED (hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error initializing COM, hr code: %d",
+                            HRESULT_CODE (hr));
+               g_object_unref (stream);
+               return NULL;
+       } else if (hr == S_FALSE) {
+               g_warning ("COM was already initialized");
+       }
+
+       /* Initialize IID IWICImagingFactory */
+       IIDFromString (L"{ec5ec8a9-c395-4314-9c77-54d7a935ff70}",
+                      &iid_imaging_factory);
+
+       /* Create COM imaging factory. */
+       hr = CoCreateInstance (&CLSID_WICImagingFactory,
+                              NULL,
+                              CLSCTX_INPROC_SERVER,
+                              &iid_imaging_factory,
+                              (LPVOID)&image_factory);
+
+       if (!SUCCEEDED (hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error creating an instance of IWICImagingFactory, hr code: %d",
+                            HRESULT_CODE (hr));
+               g_object_unref (stream);
+               CoUninitialize ();
+               return NULL;
+       }
+
+       hr = CreateStreamOnHGlobal (NULL, TRUE, &win_stream);
+
+       if (!SUCCEEDED (hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error allocating IStream, hr code: %d",
+                            HRESULT_CODE (hr));
+               IWICImagingFactory_Release (image_factory);
+               g_object_unref (stream);
+               CoUninitialize ();
+               return NULL;
+       }
+
+       /* Write GInputStream data into IStream */
+       do {
+               read_bytes = g_input_stream_read (stream,
+                                                 buffer,
+                                                 sizeof (buffer),
+                                                 NULL,
+                                                 error);
+               if (read_bytes < 0) {
+                       IWICImagingFactory_Release (image_factory);
+                       g_object_unref (stream);
+                       CoUninitialize ();
+                       return NULL;
+               }
+
+               nwritten = 0;
+
+               while (nwritten < read_bytes) {
+                       IStream_Write (win_stream,
+                                      buffer + nwritten,
+                                      read_bytes - nwritten,
+                                      &written_bytes);
+                       nwritten += written_bytes;
+               }
+
+       } while (read_bytes > 0);
+
+       g_object_unref (stream);
+
+       hr = IWICImagingFactory_CreateDecoderFromStream (image_factory,
+                                                        win_stream,
+                                                        NULL,
+                                                        WICDecodeMetadataCacheOnDemand,
+                                                        &decoder);
+       IStream_Release  (win_stream);
+
+       if (!SUCCEEDED (hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error creating decoder from stream, hr code: %d",
+                            HRESULT_CODE (hr));
+               IWICImagingFactory_Release (image_factory);
+               CoUninitialize ();
+               return NULL;
+       }
+
+       hr = IWICBitmapDecoder_GetFrame (decoder, 0, &decoder_frame);
+       IWICBitmapDecoder_Release (decoder);
+
+       if (!SUCCEEDED(hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error getting frame, hr code: %d",
+                            HRESULT_CODE (hr));
+               IWICImagingFactory_Release (image_factory);
+               CoUninitialize ();
+               return NULL;
+       }
+
+       hr = IWICBitmapFrameDecode_GetSize (decoder_frame, &width, &height);
+
+       if (!SUCCEEDED (hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error getting image size, hr code: %d",
+                            HRESULT_CODE (hr));
+               IWICImagingFactory_Release (image_factory);
+               IWICBitmapFrameDecode_Release (decoder_frame);
+               CoUninitialize ();
+               return NULL;
+       }
+
+       hr = IWICImagingFactory_CreateBitmapFromSource (image_factory,
+                                                       (IWICBitmapSource *)decoder_frame,
+                                                       WICBitmapCacheOnDemand,
+                                                       &bitmap);
+       IWICImagingFactory_Release (image_factory);
+       IWICBitmapFrameDecode_Release (decoder_frame);
+
+       if (!SUCCEEDED (hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error creating bitmap, hr code: %d",
+                            HRESULT_CODE (hr));
+               CoUninitialize ();
+               return NULL;
+       }
+
+       rc_lock.X = 0;
+       rc_lock.Y = 0;
+       rc_lock.Width = width;
+       rc_lock.Height = height;
+
+       hr = IWICBitmap_Lock (bitmap, &rc_lock, WICBitmapLockWrite, &bitmap_lock);
+       IWICBitmap_Release (bitmap);
+
+       if (!SUCCEEDED (hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error locking bitmap, hr code: %d",
+                            HRESULT_CODE (hr));
+               CoUninitialize ();
+               return NULL;
+       }
+
+       hr = IWICBitmapLock_GetDataPointer (bitmap_lock, &bytes_size, &bytes);
+
+       if (!SUCCEEDED (hr)) {
+               g_set_error (error,
+                            GXPS_ERROR,
+                            GXPS_ERROR_IMAGE,
+                            "Error getting data pointer, hr code: %d",
+                            HRESULT_CODE(hr));
+               IWICBitmapLock_Release (bitmap_lock);
+               CoUninitialize ();
+               return NULL;
+       }
+
+       image = image_create_from_byte_array (bytes, width, height, bytes_size, error);
+
+       IWICBitmapLock_Release (bitmap_lock);
+       CoUninitialize ();
+
+       return image;
+}
+#endif /* #ifdef G_OS_WIN32 */
+
 static gchar *
 gxps_images_guess_content_type (GXPSArchive *zip,
                                const gchar *image_uri)
@@ -910,9 +1193,13 @@ gxps_images_get_image (GXPSArchive *zip,
        } else if (g_str_has_suffix (image_uri_lower, ".tif")) {
                image = gxps_images_create_from_tiff (zip, image_uri, error);
        } else if (g_str_has_suffix (image_uri_lower, "wdp")) {
+#ifdef G_OS_WIN32
+               image = gxps_images_create_from_wdp (zip, image_uri, error);
+#else
                GXPS_DEBUG (g_message ("Unsupported image format windows media photo"));
                g_free (image_uri_lower);
                return NULL;
+#endif
        }
 
        g_free (image_uri_lower);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]