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 
SkDWriteFontFileStream(IDWriteFontFileStream * fontFileStream)20 SkDWriteFontFileStream::SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream)
21     : fFontFileStream(SkRefComPtr(fontFileStream))
22     , fPos(0)
23     , fLockedMemory(NULL)
24     , fFragmentLock(NULL) {
25 }
26 
~SkDWriteFontFileStream()27 SkDWriteFontFileStream::~SkDWriteFontFileStream() {
28     if (fFragmentLock) {
29         fFontFileStream->ReleaseFileFragment(fFragmentLock);
30     }
31 }
32 
read(void * buffer,size_t size)33 size_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 
isAtEnd() const78 bool SkDWriteFontFileStream::isAtEnd() const {
79     return fPos == this->getLength();
80 }
81 
rewind()82 bool SkDWriteFontFileStream::rewind() {
83     fPos = 0;
84     return true;
85 }
86 
duplicate() const87 SkDWriteFontFileStream* SkDWriteFontFileStream::duplicate() const {
88     return SkNEW_ARGS(SkDWriteFontFileStream, (fFontFileStream.get()));
89 }
90 
getPosition() const91 size_t SkDWriteFontFileStream::getPosition() const {
92     return fPos;
93 }
94 
seek(size_t position)95 bool SkDWriteFontFileStream::seek(size_t position) {
96     size_t length = this->getLength();
97     fPos = (position > length) ? length : position;
98     return true;
99 }
100 
move(long offset)101 bool SkDWriteFontFileStream::move(long offset) {
102     return seek(fPos + offset);
103 }
104 
fork() const105 SkDWriteFontFileStream* SkDWriteFontFileStream::fork() const {
106     SkAutoTDelete<SkDWriteFontFileStream> that(this->duplicate());
107     that->seek(fPos);
108     return that.detach();
109 }
110 
getLength() const111 size_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 
getMemoryBase()121 const 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 
Create(SkStreamAsset * stream,SkDWriteFontFileStreamWrapper ** streamFontFileStream)136 HRESULT SkDWriteFontFileStreamWrapper::Create(SkStreamAsset* stream,
137                                               SkDWriteFontFileStreamWrapper** streamFontFileStream)
138 {
139     *streamFontFileStream = new SkDWriteFontFileStreamWrapper(stream);
140     if (NULL == streamFontFileStream) {
141         return E_OUTOFMEMORY;
142     }
143     return S_OK;
144 }
145 
SkDWriteFontFileStreamWrapper(SkStreamAsset * stream)146 SkDWriteFontFileStreamWrapper::SkDWriteFontFileStreamWrapper(SkStreamAsset* stream)
147     : fRefCount(1), fStream(stream) {
148 }
149 
QueryInterface(REFIID iid,void ** ppvObject)150 HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::QueryInterface(REFIID iid, void** ppvObject) {
151     if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
152         *ppvObject = this;
153         AddRef();
154         return S_OK;
155     } else {
156         *ppvObject = NULL;
157         return E_NOINTERFACE;
158     }
159 }
160 
AddRef()161 ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::AddRef() {
162     return InterlockedIncrement(&fRefCount);
163 }
164 
Release()165 ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::Release() {
166     ULONG newCount = InterlockedDecrement(&fRefCount);
167     if (0 == newCount) {
168         delete this;
169     }
170     return newCount;
171 }
172 
ReadFileFragment(void const ** fragmentStart,UINT64 fileOffset,UINT64 fragmentSize,void ** fragmentContext)173 HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReadFileFragment(
174     void const** fragmentStart,
175     UINT64 fileOffset,
176     UINT64 fragmentSize,
177     void** fragmentContext)
178 {
179     // The loader is responsible for doing a bounds check.
180     UINT64 fileSize;
181     this->GetFileSize(&fileSize);
182     if (fileOffset > fileSize || fragmentSize > fileSize - fileOffset) {
183         *fragmentStart = NULL;
184         *fragmentContext = NULL;
185         return E_FAIL;
186     }
187 
188     if (!SkTFitsIn<size_t>(fileOffset + fragmentSize)) {
189         return E_FAIL;
190     }
191 
192     const void* data = fStream->getMemoryBase();
193     if (data) {
194         *fragmentStart = static_cast<BYTE const*>(data) + static_cast<size_t>(fileOffset);
195         *fragmentContext = NULL;
196 
197     } else {
198         // May be called from multiple threads.
199         SkAutoMutexAcquire ama(fStreamMutex);
200 
201         *fragmentStart = NULL;
202         *fragmentContext = NULL;
203 
204         if (!fStream->seek(static_cast<size_t>(fileOffset))) {
205             return E_FAIL;
206         }
207         SkAutoTMalloc<uint8_t> streamData(static_cast<size_t>(fragmentSize));
208         if (fStream->read(streamData.get(), static_cast<size_t>(fragmentSize)) != fragmentSize) {
209             return E_FAIL;
210         }
211 
212         *fragmentStart = streamData.get();
213         *fragmentContext = streamData.detach();
214     }
215     return S_OK;
216 }
217 
ReleaseFileFragment(void * fragmentContext)218 void STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReleaseFileFragment(void* fragmentContext) {
219     sk_free(fragmentContext);
220 }
221 
GetFileSize(UINT64 * fileSize)222 HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetFileSize(UINT64* fileSize) {
223     *fileSize = fStream->getLength();
224     return S_OK;
225 }
226 
GetLastWriteTime(UINT64 * lastWriteTime)227 HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetLastWriteTime(UINT64* lastWriteTime) {
228     // The concept of last write time does not apply to this loader.
229     *lastWriteTime = 0;
230     return E_NOTIMPL;
231 }
232