1
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9#include "SkTypes.h"
10#if defined(SK_BUILD_FOR_WIN32)
11
12#define WIN32_LEAN_AND_MEAN
13#include <windows.h>
14#include <ole2.h>
15#include "SkIStream.h"
16#include "SkStream.h"
17
18/**
19 * SkBaseIStream
20 */
21SkBaseIStream::SkBaseIStream() : _refcount(1) { }
22SkBaseIStream::~SkBaseIStream() { }
23
24HRESULT STDMETHODCALLTYPE SkBaseIStream::QueryInterface(REFIID iid
25                                                      , void ** ppvObject)
26{
27    if (nullptr == ppvObject) {
28        return E_INVALIDARG;
29    }
30    if (iid == __uuidof(IUnknown)
31        || iid == __uuidof(IStream)
32        || iid == __uuidof(ISequentialStream))
33    {
34        *ppvObject = static_cast<IStream*>(this);
35        AddRef();
36        return S_OK;
37    } else {
38        *ppvObject = nullptr;
39        return E_NOINTERFACE;
40    }
41}
42
43ULONG STDMETHODCALLTYPE SkBaseIStream::AddRef(void) {
44    return (ULONG)InterlockedIncrement(&_refcount);
45}
46
47ULONG STDMETHODCALLTYPE SkBaseIStream::Release(void) {
48    ULONG res = (ULONG) InterlockedDecrement(&_refcount);
49    if (0 == res) {
50        delete this;
51    }
52    return res;
53}
54
55// ISequentialStream Interface
56HRESULT STDMETHODCALLTYPE SkBaseIStream::Read(void* pv
57                                            , ULONG cb
58                                            , ULONG* pcbRead)
59{ return E_NOTIMPL; }
60
61HRESULT STDMETHODCALLTYPE SkBaseIStream::Write(void const* pv
62                                             , ULONG cb
63                                             , ULONG* pcbWritten)
64{ return E_NOTIMPL; }
65
66// IStream Interface
67HRESULT STDMETHODCALLTYPE SkBaseIStream::SetSize(ULARGE_INTEGER)
68{ return E_NOTIMPL; }
69
70HRESULT STDMETHODCALLTYPE SkBaseIStream::CopyTo(IStream*
71                                              , ULARGE_INTEGER
72                                              , ULARGE_INTEGER*
73                                              , ULARGE_INTEGER*)
74{ return E_NOTIMPL; }
75
76HRESULT STDMETHODCALLTYPE SkBaseIStream::Commit(DWORD)
77{ return E_NOTIMPL; }
78
79HRESULT STDMETHODCALLTYPE SkBaseIStream::Revert(void)
80{ return E_NOTIMPL; }
81
82HRESULT STDMETHODCALLTYPE SkBaseIStream::LockRegion(ULARGE_INTEGER
83                                                  , ULARGE_INTEGER
84                                                  , DWORD)
85{ return E_NOTIMPL; }
86
87HRESULT STDMETHODCALLTYPE SkBaseIStream::UnlockRegion(ULARGE_INTEGER
88                                                    , ULARGE_INTEGER
89                                                    , DWORD)
90{ return E_NOTIMPL; }
91
92HRESULT STDMETHODCALLTYPE SkBaseIStream::Clone(IStream **)
93{ return E_NOTIMPL; }
94
95HRESULT STDMETHODCALLTYPE SkBaseIStream::Seek(LARGE_INTEGER liDistanceToMove
96                                            , DWORD dwOrigin
97                                            , ULARGE_INTEGER* lpNewFilePointer)
98{ return E_NOTIMPL; }
99
100HRESULT STDMETHODCALLTYPE SkBaseIStream::Stat(STATSTG* pStatstg
101                                            , DWORD grfStatFlag)
102{ return E_NOTIMPL; }
103
104
105/**
106 * SkIStream
107 */
108SkIStream::SkIStream(SkStream* stream, bool deleteOnRelease)
109    : SkBaseIStream()
110    , fSkStream(stream)
111    , fDeleteOnRelease(deleteOnRelease)
112    , fLocation()
113{
114    this->fSkStream->rewind();
115}
116
117SkIStream::~SkIStream() {
118    if (fDeleteOnRelease) {
119        delete this->fSkStream;
120    }
121}
122
123HRESULT SkIStream::CreateFromSkStream(SkStream* stream
124                                    , bool deleteOnRelease
125                                    , IStream ** ppStream)
126{
127    if (nullptr == stream) {
128        return E_INVALIDARG;
129    }
130    *ppStream = new SkIStream(stream, deleteOnRelease);
131    return S_OK;
132}
133
134// ISequentialStream Interface
135HRESULT STDMETHODCALLTYPE SkIStream::Read(void* pv, ULONG cb, ULONG* pcbRead) {
136    *pcbRead = static_cast<ULONG>(this->fSkStream->read(pv, cb));
137    this->fLocation.QuadPart += *pcbRead;
138    return (*pcbRead == cb) ? S_OK : S_FALSE;
139}
140
141HRESULT STDMETHODCALLTYPE SkIStream::Write(void const* pv
142                                         , ULONG cb
143                                         , ULONG* pcbWritten)
144{
145    return STG_E_CANTSAVE;
146}
147
148// IStream Interface
149HRESULT STDMETHODCALLTYPE SkIStream::Seek(LARGE_INTEGER liDistanceToMove
150                                        , DWORD dwOrigin
151                                        , ULARGE_INTEGER* lpNewFilePointer)
152{
153    HRESULT hr = S_OK;
154
155    switch(dwOrigin) {
156    case STREAM_SEEK_SET: {
157        if (!this->fSkStream->rewind()) {
158            hr = E_FAIL;
159        } else {
160            size_t skipped = this->fSkStream->skip(
161                static_cast<size_t>(liDistanceToMove.QuadPart)
162            );
163            this->fLocation.QuadPart = skipped;
164            if (skipped != liDistanceToMove.QuadPart) {
165                hr = E_FAIL;
166            }
167        }
168        break;
169    }
170    case STREAM_SEEK_CUR: {
171        size_t skipped = this->fSkStream->skip(
172            static_cast<size_t>(liDistanceToMove.QuadPart)
173        );
174        this->fLocation.QuadPart += skipped;
175        if (skipped != liDistanceToMove.QuadPart) {
176            hr = E_FAIL;
177        }
178        break;
179    }
180    case STREAM_SEEK_END: {
181        if (!this->fSkStream->rewind()) {
182            hr = E_FAIL;
183        } else {
184            // FIXME: Should not depend on getLength.
185            // See https://code.google.com/p/skia/issues/detail?id=1570
186            LONGLONG skip = this->fSkStream->getLength()
187                          + liDistanceToMove.QuadPart;
188            size_t skipped = this->fSkStream->skip(static_cast<size_t>(skip));
189            this->fLocation.QuadPart = skipped;
190            if (skipped != skip) {
191                hr = E_FAIL;
192            }
193        }
194        break;
195    }
196    default:
197        hr = STG_E_INVALIDFUNCTION;
198        break;
199    }
200
201    if (lpNewFilePointer) {
202        lpNewFilePointer->QuadPart = this->fLocation.QuadPart;
203    }
204    return hr;
205}
206
207HRESULT STDMETHODCALLTYPE SkIStream::Stat(STATSTG* pStatstg
208                                        , DWORD grfStatFlag)
209{
210    if (0 == (grfStatFlag & STATFLAG_NONAME)) {
211        return STG_E_INVALIDFLAG;
212    }
213    pStatstg->pwcsName = nullptr;
214    // FIXME: Should not depend on getLength
215    // See https://code.google.com/p/skia/issues/detail?id=1570
216    pStatstg->cbSize.QuadPart = this->fSkStream->getLength();
217    pStatstg->clsid = CLSID_NULL;
218    pStatstg->type = STGTY_STREAM;
219    pStatstg->grfMode = STGM_READ;
220    return S_OK;
221}
222
223
224/**
225 * SkIWStream
226 */
227SkWIStream::SkWIStream(SkWStream* stream)
228    : SkBaseIStream()
229    , fSkWStream(stream)
230{ }
231
232SkWIStream::~SkWIStream() {
233    if (this->fSkWStream) {
234        this->fSkWStream->flush();
235    }
236}
237
238HRESULT SkWIStream::CreateFromSkWStream(SkWStream* stream
239                                      , IStream ** ppStream)
240{
241    *ppStream = new SkWIStream(stream);
242    return S_OK;
243}
244
245// ISequentialStream Interface
246HRESULT STDMETHODCALLTYPE SkWIStream::Write(void const* pv
247                                          , ULONG cb
248                                          , ULONG* pcbWritten)
249{
250    HRESULT hr = S_OK;
251    bool wrote = this->fSkWStream->write(pv, cb);
252    if (wrote) {
253        *pcbWritten = cb;
254    } else {
255        *pcbWritten = 0;
256        hr = S_FALSE;
257    }
258    return hr;
259}
260
261// IStream Interface
262HRESULT STDMETHODCALLTYPE SkWIStream::Commit(DWORD) {
263    this->fSkWStream->flush();
264    return S_OK;
265}
266
267HRESULT STDMETHODCALLTYPE SkWIStream::Stat(STATSTG* pStatstg
268                                         , DWORD grfStatFlag)
269{
270    if (0 == (grfStatFlag & STATFLAG_NONAME)) {
271        return STG_E_INVALIDFLAG;
272    }
273    pStatstg->pwcsName = nullptr;
274    pStatstg->cbSize.QuadPart = 0;
275    pStatstg->clsid = CLSID_NULL;
276    pStatstg->type = STGTY_STREAM;
277    pStatstg->grfMode = STGM_WRITE;
278    return S_OK;
279}
280#endif//defined(SK_BUILD_FOR_WIN32)
281