[PATCH 4/6] OLE: Implement OleSavePictureFile.

Nathan Beckmann nathan.beckmann at gmail.com
Sun Feb 24 09:57:17 CST 2008


Implemented OleSavePictureFile. Created helper functions as well as a
function hold common functionality between OleSavePictureFile and
IPersistStream::Save.

Added basic tests based on a sample image.

TODO: Implement serialize{JPG,PNG,GIF,etc} to allow for saving in more
formats than just BMP.
---
 dlls/oleaut32/Makefile.in        |    2 +-
 dlls/oleaut32/oleaut32.spec      |    2 +-
 dlls/oleaut32/olepicture.c       |  138 +++++++++++++++++++++++++++++++++++--
 dlls/oleaut32/tests/olepicture.c |   52 ++++++++++++++
 4 files changed, 184 insertions(+), 10 deletions(-)

diff --git a/dlls/oleaut32/Makefile.in b/dlls/oleaut32/Makefile.in
index 18390a6..54107e7 100644
--- a/dlls/oleaut32/Makefile.in
+++ b/dlls/oleaut32/Makefile.in
@@ -5,7 +5,7 @@ SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = oleaut32.dll
 IMPORTLIB = liboleaut32.$(IMPLIBEXT)
-IMPORTS   = ole32 rpcrt4 user32 gdi32 advapi32 kernel32 ntdll
+IMPORTS   = shlwapi ole32 rpcrt4 user32 gdi32 advapi32 kernel32 ntdll
 DELAYIMPORTS = comctl32 urlmon
 EXTRALIBS = -luuid
 
diff --git a/dlls/oleaut32/oleaut32.spec b/dlls/oleaut32/oleaut32.spec
index 7b764ef..a60926c 100644
--- a/dlls/oleaut32/oleaut32.spec
+++ b/dlls/oleaut32/oleaut32.spec
@@ -391,7 +391,7 @@
 420 stdcall OleCreateFontIndirect(ptr ptr ptr)
 421 stdcall OleTranslateColor(long long long)
 422 stub OleLoadPictureFile
-423 stub OleSavePictureFile
+423 stdcall OleSavePictureFile(ptr wstr)
 424 stdcall OleLoadPicturePath(wstr ptr long long ptr ptr)
 425 stdcall VarUI4FromI8(double ptr)
 426 stdcall VarUI4FromUI8(double ptr)
diff --git a/dlls/oleaut32/olepicture.c b/dlls/oleaut32/olepicture.c
index 42385a2..bf9213b 100644
--- a/dlls/oleaut32/olepicture.c
+++ b/dlls/oleaut32/olepicture.c
@@ -57,6 +57,8 @@
 #include "winbase.h"
 #include "wingdi.h"
 #include "winuser.h"
+#include "winreg.h"
+#include "shlwapi.h"
 #include "ole2.h"
 #include "olectl.h"
 #include "oleauto.h"
@@ -2150,8 +2152,69 @@ static int serializeIcon(HICON hIcon, void ** ppBuffer, unsigned int * pLength)
 	return iSuccess;
 }
 
-static HRESULT WINAPI OLEPictureImpl_Save(
-  IPersistStream* iface,IStream*pStm,BOOL fClearDirty)
+/***********************************************************************
+ * picture_format_from_filename -
+ *   Detect which picture format to use based on a file's extension.
+ */
+
+#define BITMAP_FORMAT_BMP   0x4d42 /* "BM */
+#define BITMAP_FORMAT_JPEG  0xd8ff /* ?? */
+#define BITMAP_FORMAT_GIF   0x4947 /* ?? */
+#define BITMAP_FORMAT_PNG   0x5089 /* ?? */
+
+static unsigned int picture_format_from_filename(WCHAR* filename)
+{
+    WCHAR* end_of_filename;
+    WCHAR* extension;
+    unsigned int fmt;
+
+    static const WCHAR bmp_ext[]  = { 'b','m','p','\0' };
+    static const WCHAR jpg_ext[]  = { 'j','p','g','\0' };
+    static const WCHAR jpeg_ext[] = { 'j','p','e','g','\0' };
+    static const WCHAR gif_ext[]  = { 'g','i','f','\0' };
+    static const WCHAR png_ext[]  = { 'p','n','g','\0' };
+
+    end_of_filename = filename;
+    while (*end_of_filename != '\0') {
+        ++end_of_filename;
+    }
+
+    extension = end_of_filename;
+    while (extension > filename && *extension != ((WCHAR)'.')) {
+        --extension;
+    }
+
+    if (extension <= filename)
+        return 0;
+
+    ++extension;
+
+    fmt = 0;
+
+    if (CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, extension, -1, bmp_ext, -1) == CSTR_EQUAL)
+        fmt = BITMAP_FORMAT_BMP;
+    else if (CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, extension, -1, jpeg_ext, -1) == CSTR_EQUAL ||
+             CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, extension, -1, jpg_ext, -1) == CSTR_EQUAL)
+        fmt = BITMAP_FORMAT_JPEG;
+    else if (CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, extension, -1, gif_ext, -1) == CSTR_EQUAL)
+        fmt = BITMAP_FORMAT_GIF;
+    else if (CompareStringW(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, extension, -1, png_ext, -1) == CSTR_EQUAL)
+        fmt = BITMAP_FORMAT_PNG;
+
+    return fmt;
+}
+
+/***********************************************************************
+ * save_picture_to_stream -
+ *   This function combines OLEPictureImpl_Save, which implements
+ *   IPersistStream::Save, with the functionality needed for
+ *   OleSavePictureFile.
+ */
+
+static HRESULT save_picture_to_stream(IPersistStream *iface,
+                                      IStream *pStm,
+                                      BOOL fClearDirty,
+                                      unsigned int dest_format)
 {
     HRESULT hResult = E_NOTIMPL;
     void * pIconData;
@@ -2159,6 +2222,7 @@ static HRESULT WINAPI OLEPictureImpl_Save(
     ULONG dummy;
     int iSerializeResult = 0;
     OLEPictureImpl *This = impl_from_IPersistStream(iface);
+    unsigned int format;
 
     TRACE("%p %p %d\n", This, pStm, fClearDirty);
 
@@ -2186,18 +2250,29 @@ static HRESULT WINAPI OLEPictureImpl_Save(
         hResult = S_OK;
         break;
     case PICTYPE_BITMAP:
-        if (This->bIsDirty) {
-            switch (This->keepOrigFormat ? This->loadtime_format : 0x4d42) {
-            case 0x4d42:
+        if (This->bIsDirty
+            ||
+            (dest_format && (dest_format != This->loadtime_format))
+            ) {
+
+            if (dest_format)
+                format = dest_format;
+            else if (This->keepOrigFormat)
+                format = This->loadtime_format;
+            else
+                format = BITMAP_FORMAT_BMP;
+
+            switch (format) {
+            case BITMAP_FORMAT_BMP:
                 iSerializeResult = serializeBMP(This->desc.u.bmp.hbitmap, &pIconData, &iDataSize);
                 break;
-            case 0xd8ff:
+            case BITMAP_FORMAT_JPEG:
                 FIXME("(%p,%p,%d), PICTYPE_BITMAP (format JPEG) not implemented!\n",This,pStm,fClearDirty);
                 break;
-            case 0x4947:
+            case BITMAP_FORMAT_GIF:
                 FIXME("(%p,%p,%d), PICTYPE_BITMAP (format GIF) not implemented!\n",This,pStm,fClearDirty);
                 break;
-            case 0x5089:
+            case BITMAP_FORMAT_PNG:
                 FIXME("(%p,%p,%d), PICTYPE_BITMAP (format PNG) not implemented!\n",This,pStm,fClearDirty);
                 break;
             default:
@@ -2231,6 +2306,12 @@ static HRESULT WINAPI OLEPictureImpl_Save(
     return hResult;
 }
 
+static HRESULT WINAPI OLEPictureImpl_Save(
+  IPersistStream* iface,IStream*pStm,BOOL fClearDirty)
+{
+    return save_picture_to_stream(iface, pStm, fClearDirty, 0);
+}
+
 static HRESULT WINAPI OLEPictureImpl_GetSizeMax(
   IPersistStream* iface,ULARGE_INTEGER*pcbSize)
 {
@@ -2590,6 +2671,47 @@ HRESULT WINAPI OleCreatePictureIndirect(LPPICTDESC lpPictDesc, REFIID riid,
   return hr;
 }
 
+/***********************************************************************
+ * OleSavePictureFile (OLEAUT32.418)
+ */
+
+HRESULT WINAPI OleSavePictureFile( LPDISPATCH lpdispPicture, BSTR bstrFileName )
+{
+    LPPERSISTSTREAM ps;
+    LPSTREAM stream;
+    unsigned int dest_format;
+    HRESULT hr;
+
+    if (lpdispPicture == NULL || bstrFileName == NULL)
+        return CTL_E_INVALIDPROPERTYVALUE;
+
+    dest_format = picture_format_from_filename(bstrFileName);
+    if (!dest_format)
+        return CTL_E_INVALIDPROPERTYVALUE; /* FIXME: Is this a generic invalid parameter error code? */
+
+    hr = SHCreateStreamOnFileW(bstrFileName, STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE, &stream);
+    if (FAILED(hr)) {
+        return CTL_E_FILENOTFOUND;
+    }
+
+    hr = IDispatch_QueryInterface(lpdispPicture, &IID_IPersistStream, (LPVOID*)&ps);
+    if (FAILED(hr)) {
+        FIXME("Could not get IPersistStream iface from IPictureDisp?\n");
+        IStream_Release(stream);
+        return hr; /* FIXME: what is the right error code? */
+    }
+
+    hr = save_picture_to_stream(ps, stream, FALSE, dest_format);
+
+    if (FAILED(hr)) {
+        FIXME("If save_picture_to_stream fails, we still create an empty file. Is this correct behavior?\n");
+    }
+
+    IPersistStream_Release(ps);
+    IStream_Release(stream);
+    return hr;
+}
+
 
 /***********************************************************************
  * OleLoadPicture (OLEAUT32.418)
diff --git a/dlls/oleaut32/tests/olepicture.c b/dlls/oleaut32/tests/olepicture.c
index 22e2321..cd7c2b0 100644
--- a/dlls/oleaut32/tests/olepicture.c
+++ b/dlls/oleaut32/tests/olepicture.c
@@ -489,6 +489,7 @@ static void test_save_picture()
     LPSTREAM in_stream;
     LPSTREAM out_stream;
     IPicture *pic;
+    IDispatch *dispatch;
     IPersistStream *persist_stream;
     ULONG dummy;
     int i;
@@ -497,10 +498,13 @@ static void test_save_picture()
 
     static const char oleloadpicture_file[] = { 't','e','s','t','.','b','m','p','\0' };
     static const char ipersiststream_save_file[] = { 't','e','s','t','_','i','p','e','r','s','i','s','t','s','t','r','e','a','m','.','b','m','p','\0' };
+    static const WCHAR olesavepicturefile_file[] = { 't','e','s','t','_','o','l','e','s','a','v','e','p','i','c','t','u','r','e','f','i','l','e','.','b','m','p','\0' };
+    static const WCHAR olesavepicturefile_jpg_file[] = { 't','e','s','t','_','o','l','e','s','a','v','e','p','i','c','t','u','r','e','f','i','l','e','.','j','p','g','\0' };
 
     in_stream = NULL;
     out_stream = NULL;
     pic = NULL;
+    dispatch = NULL;
     persist_stream = NULL;
 
     /* Create file */
@@ -534,6 +538,22 @@ static void test_save_picture()
     IStream_Release(out_stream);
     IPersistStream_Release(persist_stream);
 
+    /* OleSavePictureFile */
+    ret = IPicture_QueryInterface(pic, &IID_IDispatch, (LPVOID*) &dispatch);
+    ok(ret == S_OK, "Couldn't query IDispatch from pic.\n");
+
+    ret = OleSavePictureFile(dispatch, (BSTR)olesavepicturefile_file);
+    ok(ret == S_OK, "Failed OleSavePictureFile [bmp]\n");
+    ret = OleSavePictureFile(NULL, (BSTR)olesavepicturefile_file);
+    ok(ret != S_OK, "OleSavePictureFile succeeded with NULL pointer\n");
+    ret = OleSavePictureFile(dispatch, NULL);
+    ok(ret != S_OK, "OleSavePictureFile succeeded with NULL pointer\n");
+    todo_wine {
+        ret = OleSavePictureFile(dispatch, (BSTR)olesavepicturefile_jpg_file);
+        ok(ret == S_OK, "Failed OleSavePictureFile [jpeg]\n");
+    }
+
+    IDispatch_Release(dispatch);
     IPicture_Release(pic);
 
     /* Test output */
@@ -551,6 +571,38 @@ static void test_save_picture()
 
         IStream_Release(in_stream);
     }
+    
+    ret = SHCreateStreamOnFileW(olesavepicturefile_file, STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE, &in_stream);
+    ok(ret == S_OK, "Failed to read OleSavePictureFile [bmp] test image.\n");
+    if (SUCCEEDED(ret)) {
+        IStream_Read(in_stream, output_file, sizeof(output_file), &dummy);
+        ok(dummy == sizeof(bmpimage), "Output from OleSavePictureFile [bmp] is different size than test image.\n");
+        image_differ = FALSE;
+        for (i = 0; i < sizeof(bmpimage); i++) {
+            if (output_file[i] != bmpimage[i])
+                image_differ = TRUE;
+        }
+        ok(!image_differ, "Output from OleSavePictureFile [bmp] differs from test image.\n");
+
+        IStream_Release(in_stream);
+    }
+
+    ret = SHCreateStreamOnFileW(olesavepicturefile_jpg_file, STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE, &in_stream);
+    ok(ret == S_OK, "Failed to read OleSavePictureFile [jpg] test image.\n");
+    if (SUCCEEDED(ret)) {
+        IStream_Read(in_stream, output_file, sizeof(output_file), &dummy);
+        ok(dummy != sizeof(bmpimage), "Output from OleSavePictureFile [jpg] is same size as test image.\n");
+        if (dummy == sizeof(bmpimage)) {
+            image_differ = FALSE;
+            for (i = 0; i < sizeof(bmpimage); i++) {
+                if (output_file[i] != bmpimage[i])
+                    image_differ = TRUE;
+            }
+            ok(image_differ, "Output from OleSavePictureFile [jpg] is same as test image.\n");
+        }
+
+        IStream_Release(in_stream);
+    }
 }
 
 START_TEST(olepicture)
-- 
1.5.4.2




More information about the wine-patches mailing list