1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7#include "SkTypes.h"
8#if defined(SK_BUILD_FOR_WIN32)
9
10#include "SkTypes.h"
11#include "SkDWriteFontFileStream.h"
12#include "SkHRESULT.h"
13#include "SkTemplates.h"
14#include "SkTFitsIn.h"
15#include "SkTScopedComPtr.h"
16
17#include <dwrite.h>
18
19///////////////////////////////////////////////////////////////////////////////
20//  SkIDWriteFontFileStream
21
22SkDWriteFontFileStream::SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream)
23    : fFontFileStream(SkRefComPtr(fontFileStream))
24    , fPos(0)
25    , fLockedMemory(nullptr)
26    , fFragmentLock(nullptr) {
27}
28
29SkDWriteFontFileStream::~SkDWriteFontFileStream() {
30    if (fFragmentLock) {
31        fFontFileStream->ReleaseFileFragment(fFragmentLock);
32    }
33}
34
35size_t SkDWriteFontFileStream::read(void* buffer, size_t size) {
36    HRESULT hr = S_OK;
37
38    if (nullptr == buffer) {
39        size_t fileSize = this->getLength();
40
41        if (fPos + size > fileSize) {
42            size_t skipped = fileSize - fPos;
43            fPos = fileSize;
44            return skipped;
45        } else {
46            fPos += size;
47            return size;
48        }
49    }
50
51    const void* start;
52    void* fragmentLock;
53    hr = fFontFileStream->ReadFileFragment(&start, fPos, size, &fragmentLock);
54    if (SUCCEEDED(hr)) {
55        memcpy(buffer, start, size);
56        fFontFileStream->ReleaseFileFragment(fragmentLock);
57        fPos += size;
58        return size;
59    }
60
61    //The read may have failed because we asked for too much data.
62    size_t fileSize = this->getLength();
63    if (fPos + size <= fileSize) {
64        //This means we were within bounds, but failed for some other reason.
65        return 0;
66    }
67
68    size_t read = fileSize - fPos;
69    hr = fFontFileStream->ReadFileFragment(&start, fPos, read, &fragmentLock);
70    if (SUCCEEDED(hr)) {
71        memcpy(buffer, start, read);
72        fFontFileStream->ReleaseFileFragment(fragmentLock);
73        fPos = fileSize;
74        return read;
75    }
76
77    return 0;
78}
79
80bool SkDWriteFontFileStream::isAtEnd() const {
81    return fPos == this->getLength();
82}
83
84bool SkDWriteFontFileStream::rewind() {
85    fPos = 0;
86    return true;
87}
88
89SkDWriteFontFileStream* SkDWriteFontFileStream::duplicate() const {
90    return new SkDWriteFontFileStream(fFontFileStream.get());
91}
92
93size_t SkDWriteFontFileStream::getPosition() const {
94    return fPos;
95}
96
97bool SkDWriteFontFileStream::seek(size_t position) {
98    size_t length = this->getLength();
99    fPos = (position > length) ? length : position;
100    return true;
101}
102
103bool SkDWriteFontFileStream::move(long offset) {
104    return seek(fPos + offset);
105}
106
107SkDWriteFontFileStream* SkDWriteFontFileStream::fork() const {
108    SkAutoTDelete<SkDWriteFontFileStream> that(this->duplicate());
109    that->seek(fPos);
110    return that.detach();
111}
112
113size_t SkDWriteFontFileStream::getLength() const {
114    HRESULT hr = S_OK;
115    UINT64 realFileSize = 0;
116    hr = fFontFileStream->GetFileSize(&realFileSize);
117    if (!SkTFitsIn<size_t>(realFileSize)) {
118        return 0;
119    }
120    return static_cast<size_t>(realFileSize);
121}
122
123const void* SkDWriteFontFileStream::getMemoryBase() {
124    if (fLockedMemory) {
125        return fLockedMemory;
126    }
127
128    UINT64 fileSize;
129    HRNM(fFontFileStream->GetFileSize(&fileSize), "Could not get file size");
130    HRNM(fFontFileStream->ReadFileFragment(&fLockedMemory, 0, fileSize, &fFragmentLock),
131         "Could not lock file fragment.");
132    return fLockedMemory;
133}
134
135///////////////////////////////////////////////////////////////////////////////
136//  SkIDWriteFontFileStreamWrapper
137
138HRESULT SkDWriteFontFileStreamWrapper::Create(SkStreamAsset* stream,
139                                              SkDWriteFontFileStreamWrapper** streamFontFileStream)
140{
141    *streamFontFileStream = new SkDWriteFontFileStreamWrapper(stream);
142    if (nullptr == streamFontFileStream) {
143        return E_OUTOFMEMORY;
144    }
145    return S_OK;
146}
147
148SkDWriteFontFileStreamWrapper::SkDWriteFontFileStreamWrapper(SkStreamAsset* stream)
149    : fRefCount(1), fStream(stream) {
150}
151
152HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::QueryInterface(REFIID iid, void** ppvObject) {
153    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
154        *ppvObject = this;
155        AddRef();
156        return S_OK;
157    } else {
158        *ppvObject = nullptr;
159        return E_NOINTERFACE;
160    }
161}
162
163ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::AddRef() {
164    return InterlockedIncrement(&fRefCount);
165}
166
167ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::Release() {
168    ULONG newCount = InterlockedDecrement(&fRefCount);
169    if (0 == newCount) {
170        delete this;
171    }
172    return newCount;
173}
174
175HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReadFileFragment(
176    void const** fragmentStart,
177    UINT64 fileOffset,
178    UINT64 fragmentSize,
179    void** fragmentContext)
180{
181    // The loader is responsible for doing a bounds check.
182    UINT64 fileSize;
183    this->GetFileSize(&fileSize);
184    if (fileOffset > fileSize || fragmentSize > fileSize - fileOffset) {
185        *fragmentStart = nullptr;
186        *fragmentContext = nullptr;
187        return E_FAIL;
188    }
189
190    if (!SkTFitsIn<size_t>(fileOffset + fragmentSize)) {
191        return E_FAIL;
192    }
193
194    const void* data = fStream->getMemoryBase();
195    if (data) {
196        *fragmentStart = static_cast<BYTE const*>(data) + static_cast<size_t>(fileOffset);
197        *fragmentContext = nullptr;
198
199    } else {
200        // May be called from multiple threads.
201        SkAutoMutexAcquire ama(fStreamMutex);
202
203        *fragmentStart = nullptr;
204        *fragmentContext = nullptr;
205
206        if (!fStream->seek(static_cast<size_t>(fileOffset))) {
207            return E_FAIL;
208        }
209        SkAutoTMalloc<uint8_t> streamData(static_cast<size_t>(fragmentSize));
210        if (fStream->read(streamData.get(), static_cast<size_t>(fragmentSize)) != fragmentSize) {
211            return E_FAIL;
212        }
213
214        *fragmentStart = streamData.get();
215        *fragmentContext = streamData.detach();
216    }
217    return S_OK;
218}
219
220void STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReleaseFileFragment(void* fragmentContext) {
221    sk_free(fragmentContext);
222}
223
224HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetFileSize(UINT64* fileSize) {
225    *fileSize = fStream->getLength();
226    return S_OK;
227}
228
229HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetLastWriteTime(UINT64* lastWriteTime) {
230    // The concept of last write time does not apply to this loader.
231    *lastWriteTime = 0;
232    return E_NOTIMPL;
233}
234
235#endif//defined(SK_BUILD_FOR_WIN32)
236