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