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