Wrap SkStreams in IStreams instead of copying data around.
http://codereview.appspot.com/4630062/


git-svn-id: http://skia.googlecode.com/svn/trunk@1694 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/images.gyp b/gyp/images.gyp
index 8f0a06b..dad4361 100644
--- a/gyp/images.gyp
+++ b/gyp/images.gyp
@@ -6,6 +6,9 @@
     {
       'target_name': 'images',
       'type': 'static_library',
+      'dependencies': [
+        'utils.gyp:utils',
+      ],
       'include_dirs': [
         '../include/config',
         '../include/core',
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 8845d9a..9a3683f 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -12,6 +12,7 @@
         '../include/utils',
         '../include/utils/mac',
         '../include/utils/unix',
+        '../include/utils/win',
         '../include/views',
         '../include/effects',
         '../include/xml',
@@ -59,22 +60,32 @@
         '../src/utils/SkSfntUtils.cpp',
         '../src/utils/SkUnitMappers.cpp',
 
+        #mac
         '../include/utils/mac/SkCGUtils.h',
         '../src/utils/mac/SkCreateCGImageRef.cpp',
         '../src/utils/mac/SkEGLContext_mac.cpp',
         '../src/utils/mac/skia_mac.cpp',
         '../src/utils/mac/SkOSWindow_Mac.cpp',
 
+        #mesa
         '../src/utils/mesa/SkEGLContext_Mesa.cpp',
 
+        #sdl
         '../src/utils/SDL/SkOSWindow_SDL.cpp',
 
+        #*nix
         '../src/utils/unix/keysym2ucs.c',
         '../src/utils/unix/SkEGLContext_Unix.cpp',
         '../src/utils/unix/SkOSWindow_Unix.cpp',
         
+        #windows
+        '../include/utils/win/SkAutoCoInitialize.h',
+        '../include/utils/win/SkIStream.h',
+        '../include/utils/win/SkTScopedComPtr.h',
+        '../src/utils/win/SkAutoCoInitialize.cpp',
         '../src/utils/win/skia_win.cpp',
         '../src/utils/win/SkEGLContext_Win.cpp',
+        '../src/utils/win/SkIStream.cpp',
         '../src/utils/win/SkOSWindow_Win.cpp',
       ],
       'sources!': [
@@ -82,18 +93,6 @@
           '../src/utils/SDL/SkOSWindow_SDL.cpp',
       ],
       'conditions': [
-        [ 'OS != "mac"', {
-          'sources!': [
-            '../include/utils/mac/SkCGUtils.h',
-            '../src/utils/mac/SkCreateCGImageRef.cpp',
-            '../src/utils/mac/SkEGLContext_mac.cpp',
-            '../src/utils/mac/skia_mac.cpp',
-            '../src/utils/mac/SkOSWindow_Mac.cpp',
-          ],
-          'include_dirs!': [
-            '../include/utils/mac',
-          ],
-        }],
         [ 'OS == "mac"', {
           'sources!': [
             '../src/utils/SkEGLContext_none.cpp',
@@ -103,18 +102,19 @@
               '$(SDKROOT)/System/Library/Frameworks/AGL.framework',
             ],
           },
-        }],
-        [ 'OS != "linux" and OS != "freebsd" and OS != "openbsd" and OS != "solaris"', {
-          'sources!': [
-            '../src/utils/unix/keysym2ucs.c',
-            '../src/utils/unix/SkEGLContext_Unix.cpp',
-            '../src/utils/unix/SkOSWindow_Unix.cpp',
-          ],
+        },{ #else if 'OS != "mac"'
           'include_dirs!': [
-            '../include/utils/unix',
+            '../include/utils/mac',
+          ],
+          'sources!': [
+            '../include/utils/mac/SkCGUtils.h',
+            '../src/utils/mac/SkCreateCGImageRef.cpp',
+            '../src/utils/mac/SkEGLContext_mac.cpp',
+            '../src/utils/mac/skia_mac.cpp',
+            '../src/utils/mac/SkOSWindow_Mac.cpp',
           ],
         }],
-        [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
+        [ 'OS in ["linux", "freebsd", "openbsd", "solaris"]', {
           'sources!': [
             '../src/utils/SkEGLContext_none.cpp',
           ],
@@ -124,18 +124,39 @@
               '-lGLU',
             ],
           },
-        }],
-        [ 'OS != "win"', {
+        },{ #else if 'OS not in ["linux", "freebsd", "openbsd", "solaris"]'
+          'include_dirs!': [
+            '../include/utils/unix',
+          ],
           'sources!': [
-            '../src/utils/win/skia_win.cpp',
-            '../src/utils/win/SkEGLContext_Win.cpp',
-            '../src/utils/win/SkOSWindow_Win.cpp',
+            '../src/utils/unix/keysym2ucs.c',
+            '../src/utils/unix/SkEGLContext_Unix.cpp',
+            '../src/utils/unix/SkOSWindow_Unix.cpp',
           ],
         }],
         [ 'OS == "win"', {
           'sources!': [
             '../src/utils/SkEGLContext_none.cpp',
           ],
+          'direct_dependent_settings': {
+            'include_dirs': [
+              '../include/utils/win',
+            ],
+          },
+        },{ #else if 'OS != "win"'
+          'include_dirs!': [
+            '../include/utils/win',
+          ],
+          'sources!': [
+            '../include/utils/win/SkAutoCoInitialize.h',
+            '../include/utils/win/SkIStream.h',
+            '../include/utils/win/SkTScopedComPtr.h',
+            '../src/utils/win/SkAutoCoInitialize.cpp',
+            '../src/utils/win/skia_win.cpp',
+            '../src/utils/win/SkEGLContext_Win.cpp',
+            '../src/utils/win/SkIStream.cpp',
+            '../src/utils/win/SkOSWindow_Win.cpp',
+          ],
         }],
       ],
       'direct_dependent_settings': {
diff --git a/include/utils/win/SkAutoCoInitialize.h b/include/utils/win/SkAutoCoInitialize.h
new file mode 100644
index 0000000..8819047
--- /dev/null
+++ b/include/utils/win/SkAutoCoInitialize.h
@@ -0,0 +1,37 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+#ifndef SkAutoCo_DEFINED
+#define SkAutoCo_DEFINED
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include "SkTemplates.h"
+
+/**
+ * An instance of this class initializes COM on creation
+ * and closes the COM library on destruction.
+ */
+class AutoCoInitialize : SkNoncopyable {
+private:
+    HRESULT fHR;
+public:
+    AutoCoInitialize();
+    ~AutoCoInitialize();
+    HRESULT getHR();
+};
+
+#endif
diff --git a/include/utils/win/SkIStream.h b/include/utils/win/SkIStream.h
new file mode 100644
index 0000000..bc8aff5
--- /dev/null
+++ b/include/utils/win/SkIStream.h
@@ -0,0 +1,135 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+#ifndef SkIStream_DEFINED
+#define SkIStream_DEFINED
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <ole2.h>
+
+class SkStream;
+class SkWStream;
+
+/**
+ * A bare IStream implementation which properly reference counts
+ * but returns E_NOTIMPL for all ISequentialStream and IStream methods.
+ */
+class SkBaseIStream : public IStream {
+private:
+    LONG _refcount;
+
+protected:
+    explicit SkBaseIStream();
+    virtual ~SkBaseIStream();
+
+public:
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid
+                                                   , void ** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef(void);
+    virtual ULONG STDMETHODCALLTYPE Release(void);
+
+    // ISequentialStream Interface
+public:
+    virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead);
+
+    virtual HRESULT STDMETHODCALLTYPE Write(void const* pv
+                                          , ULONG cb
+                                          , ULONG* pcbWritten);
+
+    // IStream Interface
+public:
+    virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER);
+    
+    virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*
+                                           , ULARGE_INTEGER
+                                           , ULARGE_INTEGER*
+                                           , ULARGE_INTEGER*);
+    
+    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD);
+    
+    virtual HRESULT STDMETHODCALLTYPE Revert(void);
+    
+    virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER
+                                               , ULARGE_INTEGER
+                                               , DWORD);
+    
+    virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER
+                                                 , ULARGE_INTEGER
+                                                 , DWORD);
+    
+    virtual HRESULT STDMETHODCALLTYPE Clone(IStream **);
+
+    virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove
+                                         , DWORD dwOrigin
+                                         , ULARGE_INTEGER* lpNewFilePointer);
+
+    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg
+                                         , DWORD grfStatFlag);
+};
+
+/**
+ * A minimal read-only IStream implementation which wraps an SkIStream.
+ */
+class SkIStream : public SkBaseIStream {
+private:
+    SkStream *fSkStream;
+    bool fUnrefOnRelease;
+
+    SkIStream(SkStream* stream, bool unrefOnRelease);
+    virtual ~SkIStream();
+
+public:
+    HRESULT static CreateFromSkStream(SkStream* stream
+                                    , bool unrefOnRelease
+                                    , IStream ** ppStream);
+
+    virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead);
+
+    virtual HRESULT STDMETHODCALLTYPE Write(void const* pv
+                                          , ULONG cb
+                                          , ULONG* pcbWritten);
+
+    virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove
+                                         , DWORD dwOrigin
+                                         , ULARGE_INTEGER* lpNewFilePointer);
+
+    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg
+                                         , DWORD grfStatFlag);
+};
+
+/**
+ * A minimal write-only IStream implementation which wraps an SkWIStream.
+ */
+class SkWIStream : public SkBaseIStream {
+private:
+    SkWStream *fSkWStream;
+
+    SkWIStream(SkWStream* stream);
+    virtual ~SkWIStream();
+
+public:
+    HRESULT static CreateFromSkWStream(SkWStream* stream, IStream ** ppStream);
+
+    virtual HRESULT STDMETHODCALLTYPE Write(void const* pv
+                                          , ULONG cb
+                                          , ULONG* pcbWritten);
+
+    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg
+                                         , DWORD grfStatFlag);
+};
+
+#endif
diff --git a/include/utils/win/SkTScopedComPtr.h b/include/utils/win/SkTScopedComPtr.h
new file mode 100644
index 0000000..202dc7e
--- /dev/null
+++ b/include/utils/win/SkTScopedComPtr.h
@@ -0,0 +1,47 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+#ifndef SkSkTScopedPtr_DEFINED
+#define SkSkTScopedPtr_DEFINED
+
+#include "SkTemplates.h"
+
+template<typename T>
+class SkTScopedComPtr : SkNoncopyable {
+private:
+    T *fPtr;
+
+public:
+    explicit SkTScopedComPtr(T *ptr = NULL) : fPtr(ptr) { }
+    ~SkTScopedComPtr() {
+        if (NULL != fPtr) {
+            fPtr->Release();
+            fPtr = NULL;
+        }
+    }
+    T &operator*() const { return *fPtr; }
+    T *operator->() const { return fPtr; }
+    /**
+     * Returns the address of the underlying pointer.
+     * This is dangerous -- it breaks encapsulation and the reference escapes.
+     * Must only be used on instances currently pointing to NULL,
+     * and only to initialize the instance.
+     */
+    T **operator&() { SkASSERT(fPtr == NULL); return &fPtr; }
+    T *get() const { return fPtr; }
+};
+
+#endif
diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp
index b53db3b..c4f1748 100644
--- a/src/ports/SkImageDecoder_WIC.cpp
+++ b/src/ports/SkImageDecoder_WIC.cpp
@@ -1,5 +1,5 @@
 /*
-    Copyright 2010 Google Inc.
+    Copyright 2011 Google Inc.
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -17,137 +17,26 @@
 #define WIN32_LEAN_AND_MEAN
 #include <Windows.h>
 #include <wincodec.h>
+#include "SkAutoCoInitialize.h"
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
+#include "SkIStream.h"
 #include "SkMovie.h"
 #include "SkStream.h"
-#include "SkTemplates.h"
-
-template<typename T>
-class scoped_com_ptr {
-private:
-    T *fPtr;
-    
-    scoped_com_ptr(scoped_com_ptr const &);
-    scoped_com_ptr & operator=(scoped_com_ptr const &);
-
-public:
-    explicit scoped_com_ptr(T *ptr = NULL) : fPtr(ptr) { }
-    ~scoped_com_ptr() {
-        if (NULL != fPtr) {
-            fPtr->Release();
-            fPtr = NULL;
-        }
-    }
-    T &operator*() const { return *fPtr; }
-    T *operator->() const { return fPtr; }
-    /**
-     * Returns the address of the underlying pointer.
-     * This is dangerous -- it breaks encapsulation and the reference escapes.
-     * Must only be used on instances currently pointing to NULL,
-     * and only to initialize the instance.
-     */
-    T **operator&() { SkASSERT(fPtr == NULL); return &fPtr; }
-    T *get() const { return fPtr; }
-};
-
-/**
- * An instance of this class initializes COM on creation
- * and closes the COM library on destruction.
- */
-class AutoCoInitialize : SkNoncopyable {
-private:
-    HRESULT hr;
-public:
-    AutoCoInitialize() :
-        hr(
-            CoInitializeEx(
-                NULL
-                , COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE
-            )
-        )
-    { }
-    ~AutoCoInitialize() {
-        if (SUCCEEDED(this->hr)) {
-            CoUninitialize();
-        }
-    }
-    HRESULT getHR() { return this->hr; }
-};
+#include "SkTScopedComPtr.h"
 
 class SkImageDecoder_WIC : public SkImageDecoder {
 protected:
-    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
 };
 
-/**
- * Converts a SkStream to an IStream.
- * The caller must call Release() on the returned IStream.
- */
-static HRESULT SkStreamToIStream(SkStream* stream, IStream** ppStream) {
-    //TODO(bungeman): use a real IStream wrapper
-    HRESULT hr = S_OK;
-    
-    size_t len = stream->getLength();
-    
-    //Reserve memory for content of IStream.
-    HGLOBAL hdata = NULL;
-    if (SUCCEEDED(hr)) {
-        hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, len);
-        if (NULL == hdata) {
-            hr = HRESULT_FROM_WIN32(GetLastError());
-        }
-    }
-    
-    //Lock memory.
-    void* data = NULL;
-    if (SUCCEEDED(hr)) {
-        data = GlobalLock(hdata);
-        if (NULL == data) {
-            hr = HRESULT_FROM_WIN32(GetLastError());
-        }
-    }
-    
-    //Write SkStream data to memory.
-    if (SUCCEEDED(hr)) {
-        size_t read = stream->read(data, len);
-        if (read != len) {
-            hr = E_FAIL;
-        }
-    }
-    
-    //Unlock memory.
-    if (NULL != data) {
-        data = NULL;
-        SetLastError(NO_ERROR);
-        GlobalUnlock(hdata);
-        DWORD lastError = GetLastError();
-        if (SUCCEEDED(hr) && NO_ERROR != lastError) {
-            hr = HRESULT_FROM_WIN32(lastError);
-        }
-    }
-    
-    //Create IStream from memory.
-    if (SUCCEEDED(hr)) {
-        hr = CreateStreamOnHGlobal(hdata, TRUE, ppStream);
-    }
-    //If we failed for any reason, free the memory.
-    if (FAILED(hr)) {
-        if (NULL != hdata) {
-            GlobalFree(hdata);
-        }
-    }
-    
-    return hr;
-}
-
 bool SkImageDecoder_WIC::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     //Initialize COM.
     AutoCoInitialize scopedCo;
     HRESULT hr = scopedCo.getHR();
     
     //Create Windows Imaging Component ImagingFactory.
-    scoped_com_ptr<IWICImagingFactory> piImagingFactory;
+    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
     if (SUCCEEDED(hr)) {
         hr = CoCreateInstance(
             CLSID_WICImagingFactory
@@ -158,9 +47,9 @@
     }
     
     //Convert SkStream to IStream.
-    scoped_com_ptr<IStream> piStream;
+    SkTScopedComPtr<IStream> piStream;
     if (SUCCEEDED(hr)) {
-        hr = SkStreamToIStream(stream, &piStream);
+        hr = SkIStream::CreateFromSkStream(stream, false, &piStream);
     }
     
     //Make sure we're at the beginning of the stream.
@@ -170,7 +59,7 @@
     }
     
     //Create the decoder from the stream content.
-    scoped_com_ptr<IWICBitmapDecoder> piBitmapDecoder;
+    SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder;
     if (SUCCEEDED(hr)) {
         hr = piImagingFactory->CreateDecoderFromStream(
             piStream.get()                    //Image to be decoded
@@ -181,13 +70,13 @@
     }
     
     //Get the first frame from the decoder.
-    scoped_com_ptr<IWICBitmapFrameDecode> piBitmapFrameDecode;
+    SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode;
     if (SUCCEEDED(hr)) {
         hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode);
     }
     
     //Get the BitmapSource interface of the frame.
-    scoped_com_ptr<IWICBitmapSource> piBitmapSourceOriginal;
+    SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal;
     if (SUCCEEDED(hr)) {
         hr = piBitmapFrameDecode->QueryInterface(
             IID_PPV_ARGS(&piBitmapSourceOriginal)
@@ -213,7 +102,7 @@
     }
     
     //Create a format converter.
-    scoped_com_ptr<IWICFormatConverter> piFormatConverter;
+    SkTScopedComPtr<IWICFormatConverter> piFormatConverter;
     if (SUCCEEDED(hr)) {
         hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
     }
@@ -230,7 +119,7 @@
     }
     
     //Get the BitmapSource interface of the format converter.
-    scoped_com_ptr<IWICBitmapSource> piBitmapSourceConverted;
+    SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted;
     if (SUCCEEDED(hr)) {
         hr = piFormatConverter->QueryInterface(
             IID_PPV_ARGS(&piBitmapSourceConverted)
@@ -300,7 +189,7 @@
     HRESULT hr = scopedCo.getHR();
     
     //Create Windows Imaging Component ImagingFactory.
-    scoped_com_ptr<IWICImagingFactory> piImagingFactory;
+    SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
     if (SUCCEEDED(hr)) {
         hr = CoCreateInstance(
             CLSID_WICImagingFactory
@@ -310,14 +199,14 @@
         );
     }
     
-    //Create the stream to hold the output of the encoder.
-    scoped_com_ptr<IStream> piStream;
+    //Convert the SkWStream to an IStream.
+    SkTScopedComPtr<IStream> piStream;
     if (SUCCEEDED(hr)) {
-        hr = CreateStreamOnHGlobal(NULL, TRUE, &piStream);
+        hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
     }
     
     //Create an encode of the appropriate type.
-    scoped_com_ptr<IWICBitmapEncoder> piEncoder;
+    SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
     if (SUCCEEDED(hr)) {
         hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder);
     }
@@ -327,8 +216,8 @@
     }
     
     //Create a the frame.
-    scoped_com_ptr<IWICBitmapFrameEncode> piBitmapFrameEncode;
-    scoped_com_ptr<IPropertyBag2> piPropertybag;
+    SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
+    SkTScopedComPtr<IPropertyBag2> piPropertybag;
     if (SUCCEEDED(hr)) {
         hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
     }
@@ -389,36 +278,6 @@
         hr = piEncoder->Commit();
     }
     
-    //Rewind the IStream with the output of the encoder.
-    if (SUCCEEDED(hr)) {
-        LARGE_INTEGER liBeginning = { 0 };
-        hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL);
-    }
-    
-    //Write the content of the IStream to the SkWStream.
-    if (SUCCEEDED(hr)) {
-        //TODO(bungeman): use a real IStream(SkWStream) wrapper
-        const unsigned int BUFFER_SIZE = 1024;
-        void* buffer = new BYTE[BUFFER_SIZE];
-        ULONG bytesRead = 0;
-        while (true) {
-            hr = piStream->Read(buffer, BUFFER_SIZE, &bytesRead);
-            if (FAILED(hr)) {
-                break;
-            }
-            bool wrote = stream->write(buffer, bytesRead);
-            if (!wrote) {
-                hr = E_FAIL;
-                break;
-            }
-            if (BUFFER_SIZE != bytesRead) {
-                break;
-            }
-        }
-        stream->flush();
-        delete[] buffer;
-    }
-    
     return SUCCEEDED(hr);
 }
 
diff --git a/src/utils/win/SkAutoCoInitialize.cpp b/src/utils/win/SkAutoCoInitialize.cpp
new file mode 100644
index 0000000..7249341
--- /dev/null
+++ b/src/utils/win/SkAutoCoInitialize.cpp
@@ -0,0 +1,34 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <ole2.h>
+#include "SkAutoCoInitialize.h"
+
+AutoCoInitialize::AutoCoInitialize() :
+    fHR(
+        CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
+    )
+{ }
+
+AutoCoInitialize::~AutoCoInitialize() {
+    if (SUCCEEDED(this->fHR)) {
+        CoUninitialize();
+    }
+}
+
+HRESULT AutoCoInitialize::getHR() { return this->fHR; }
diff --git a/src/utils/win/SkIStream.cpp b/src/utils/win/SkIStream.cpp
new file mode 100644
index 0000000..29e1c87
--- /dev/null
+++ b/src/utils/win/SkIStream.cpp
@@ -0,0 +1,263 @@
+/*
+    Copyright 2011 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <ole2.h>
+#include "SkIStream.h"
+#include "SkStream.h"
+
+/**
+ * SkBaseIStream
+ */
+SkBaseIStream::SkBaseIStream() : _refcount(1) { }
+SkBaseIStream::~SkBaseIStream() { }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::QueryInterface(REFIID iid
+                                                      , void ** ppvObject)
+{
+    if (NULL == ppvObject) {
+        return E_INVALIDARG;
+    }
+    if (iid == __uuidof(IUnknown)
+        || iid == __uuidof(IStream)
+        || iid == __uuidof(ISequentialStream))
+    {
+        *ppvObject = static_cast<IStream*>(this);
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE; 
+    }
+}
+
+ULONG STDMETHODCALLTYPE SkBaseIStream::AddRef(void) {
+    return (ULONG)InterlockedIncrement(&_refcount);
+}
+
+ULONG STDMETHODCALLTYPE SkBaseIStream::Release(void) {
+    ULONG res = (ULONG) InterlockedDecrement(&_refcount);
+    if (0 == res) {
+        delete this;
+    }
+    return res;
+}
+
+// ISequentialStream Interface
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Read(void* pv
+                                            , ULONG cb
+                                            , ULONG* pcbRead)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Write(void const* pv
+                                             , ULONG cb
+                                             , ULONG* pcbWritten)
+{ return E_NOTIMPL; }
+
+// IStream Interface
+HRESULT STDMETHODCALLTYPE SkBaseIStream::SetSize(ULARGE_INTEGER) 
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::CopyTo(IStream*
+                                              , ULARGE_INTEGER
+                                              , ULARGE_INTEGER*
+                                              , ULARGE_INTEGER*)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Commit(DWORD)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Revert(void)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::LockRegion(ULARGE_INTEGER
+                                                  , ULARGE_INTEGER
+                                                  , DWORD)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::UnlockRegion(ULARGE_INTEGER
+                                                    , ULARGE_INTEGER
+                                                    , DWORD)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Clone(IStream **)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Seek(LARGE_INTEGER liDistanceToMove
+                                            , DWORD dwOrigin
+                                            , ULARGE_INTEGER* lpNewFilePointer)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Stat(STATSTG* pStatstg
+                                            , DWORD grfStatFlag)
+{ return E_NOTIMPL; }
+
+
+/**
+ * SkIStream
+ */
+SkIStream::SkIStream(SkStream* stream, bool unrefOnRelease)
+    : SkBaseIStream()
+    , fSkStream(stream)
+    , fUnrefOnRelease(unrefOnRelease)
+{ }
+
+SkIStream::~SkIStream() {
+    if (NULL != this->fSkStream && fUnrefOnRelease) {
+        this->fSkStream->unref();
+    }
+}
+
+HRESULT SkIStream::CreateFromSkStream(SkStream* stream
+                                    , bool unrefOnRelease
+                                    , IStream ** ppStream)
+{
+    *ppStream = new SkIStream(stream, unrefOnRelease);
+    return S_OK;
+}
+
+// ISequentialStream Interface
+HRESULT STDMETHODCALLTYPE SkIStream::Read(void* pv, ULONG cb, ULONG* pcbRead) {
+    *pcbRead = this->fSkStream->read(pv, cb);
+    return (*pcbRead == cb) ? S_OK : S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE SkIStream::Write(void const* pv
+                                         , ULONG cb
+                                         , ULONG* pcbWritten)
+{
+    return STG_E_CANTSAVE;
+}
+
+// IStream Interface
+HRESULT STDMETHODCALLTYPE SkIStream::Seek(LARGE_INTEGER liDistanceToMove
+                                        , DWORD dwOrigin
+                                        , ULARGE_INTEGER* lpNewFilePointer)
+{
+    if (lpNewFilePointer != NULL) {
+        (*lpNewFilePointer).QuadPart = NULL;
+    }
+        
+    HRESULT hr = S_OK;
+    switch(dwOrigin) {
+    case STREAM_SEEK_SET: {
+        if (!this->fSkStream->rewind()) {
+            hr = E_FAIL;
+        } else {
+            size_t skipped = this->fSkStream->skip(
+                liDistanceToMove.QuadPart
+            );
+            if (skipped != liDistanceToMove.QuadPart) {
+                hr = E_FAIL;
+            }
+        }
+        break;
+    }
+    case STREAM_SEEK_CUR: {
+        size_t skipped = this->fSkStream->skip(liDistanceToMove.QuadPart);
+        if (skipped != liDistanceToMove.QuadPart) {
+            hr = E_FAIL;
+        }
+        break;
+    }
+    case STREAM_SEEK_END: {
+        if (!this->fSkStream->rewind()) {
+            hr = E_FAIL;
+        } else {
+            size_t skipped = this->fSkStream->skip(
+                this->fSkStream->getLength() + liDistanceToMove.QuadPart
+            );
+            if (skipped != liDistanceToMove.QuadPart) {
+                hr = E_FAIL;
+            }
+        }
+        break;
+    }
+    default:
+        hr = STG_E_INVALIDFUNCTION;
+        break;
+    }
+    
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE SkIStream::Stat(STATSTG* pStatstg
+                                        , DWORD grfStatFlag)
+{
+    if (0 == grfStatFlag & STATFLAG_NONAME) {
+        return STG_E_INVALIDFLAG;
+    }
+    pStatstg->pwcsName = NULL;
+    pStatstg->cbSize.QuadPart = this->fSkStream->getLength();
+    pStatstg->clsid = CLSID_NULL;
+    pStatstg->type = STGTY_STREAM;
+    pStatstg->grfMode = STGM_READ;
+    return S_OK;
+}
+
+
+/**
+ * SkIWStream
+ */
+SkWIStream::SkWIStream(SkWStream* stream)
+    : SkBaseIStream()
+    , fSkWStream(stream)
+{ }
+
+SkWIStream::~SkWIStream() {
+    if (NULL != this->fSkWStream) {
+        this->fSkWStream->flush();
+    }
+}
+
+HRESULT SkWIStream::CreateFromSkWStream(SkWStream* stream
+                                      , IStream ** ppStream)
+{
+    *ppStream = new SkWIStream(stream);
+    return S_OK;
+}
+
+// ISequentialStream Interface
+HRESULT STDMETHODCALLTYPE SkWIStream::Write(void const* pv
+                                          , ULONG cb
+                                          , ULONG* pcbWritten)
+{
+    HRESULT hr = S_OK;
+    bool wrote = this->fSkWStream->write(pv, cb);
+    if (wrote) {
+        *pcbWritten = cb;
+    } else {
+        *pcbWritten = 0;
+        hr = S_FALSE;
+    }
+    return hr;
+}
+
+// IStream Interface
+HRESULT STDMETHODCALLTYPE SkWIStream::Stat(STATSTG* pStatstg
+                                         , DWORD grfStatFlag)
+{
+    if (0 == grfStatFlag & STATFLAG_NONAME) {
+        return STG_E_INVALIDFLAG;
+    }
+    pStatstg->pwcsName = NULL;
+    pStatstg->cbSize.QuadPart = 0;
+    pStatstg->clsid = CLSID_NULL;
+    pStatstg->type = STGTY_STREAM;
+    pStatstg->grfMode = STGM_WRITE;
+    return S_OK;
+}