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 "include/core/SkTypes.h" |
8 | #if defined(SK_BUILD_FOR_WIN) |
9 | |
10 | #include "include/core/SkTypes.h" |
11 | #include "include/private/SkTFitsIn.h" |
12 | #include "include/private/SkTemplates.h" |
13 | #include "src/utils/win/SkDWriteFontFileStream.h" |
14 | #include "src/utils/win/SkHRESULT.h" |
15 | #include "src/utils/win/SkTScopedComPtr.h" |
16 | |
17 | #include <dwrite.h> |
18 | |
19 | /////////////////////////////////////////////////////////////////////////////// |
20 | // SkIDWriteFontFileStream |
21 | |
22 | SkDWriteFontFileStream::SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream) |
23 | : fFontFileStream(SkRefComPtr(fontFileStream)) |
24 | , fPos(0) |
25 | , fLockedMemory(nullptr) |
26 | , fFragmentLock(nullptr) { |
27 | } |
28 | |
29 | SkDWriteFontFileStream::~SkDWriteFontFileStream() { |
30 | if (fFragmentLock) { |
31 | fFontFileStream->ReleaseFileFragment(fFragmentLock); |
32 | } |
33 | } |
34 | |
35 | size_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 | |
80 | bool SkDWriteFontFileStream::isAtEnd() const { |
81 | return fPos == this->getLength(); |
82 | } |
83 | |
84 | bool SkDWriteFontFileStream::rewind() { |
85 | fPos = 0; |
86 | return true; |
87 | } |
88 | |
89 | SkDWriteFontFileStream* SkDWriteFontFileStream::onDuplicate() const { |
90 | return new SkDWriteFontFileStream(fFontFileStream.get()); |
91 | } |
92 | |
93 | size_t SkDWriteFontFileStream::getPosition() const { |
94 | return fPos; |
95 | } |
96 | |
97 | bool SkDWriteFontFileStream::seek(size_t position) { |
98 | size_t length = this->getLength(); |
99 | fPos = (position > length) ? length : position; |
100 | return true; |
101 | } |
102 | |
103 | bool SkDWriteFontFileStream::move(long offset) { |
104 | return seek(fPos + offset); |
105 | } |
106 | |
107 | SkDWriteFontFileStream* SkDWriteFontFileStream::onFork() const { |
108 | std::unique_ptr<SkDWriteFontFileStream> that(this->duplicate()); |
109 | that->seek(fPos); |
110 | return that.release(); |
111 | } |
112 | |
113 | size_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 | |
123 | const 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 | |
138 | HRESULT 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 | |
148 | SkDWriteFontFileStreamWrapper::SkDWriteFontFileStreamWrapper(SkStreamAsset* stream) |
149 | : fRefCount(1), fStream(stream) { |
150 | } |
151 | |
152 | SK_STDMETHODIMP 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 | |
163 | SK_STDMETHODIMP_(ULONG) SkDWriteFontFileStreamWrapper::AddRef() { |
164 | return InterlockedIncrement(&fRefCount); |
165 | } |
166 | |
167 | SK_STDMETHODIMP_(ULONG) SkDWriteFontFileStreamWrapper::Release() { |
168 | ULONG newCount = InterlockedDecrement(&fRefCount); |
169 | if (0 == newCount) { |
170 | delete this; |
171 | } |
172 | return newCount; |
173 | } |
174 | |
175 | SK_STDMETHODIMP 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 | SkAutoMutexExclusive 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.release(); |
216 | } |
217 | return S_OK; |
218 | } |
219 | |
220 | SK_STDMETHODIMP_(void) SkDWriteFontFileStreamWrapper::ReleaseFileFragment(void* fragmentContext) { |
221 | sk_free(fragmentContext); |
222 | } |
223 | |
224 | SK_STDMETHODIMP SkDWriteFontFileStreamWrapper::GetFileSize(UINT64* fileSize) { |
225 | *fileSize = fStream->getLength(); |
226 | return S_OK; |
227 | } |
228 | |
229 | SK_STDMETHODIMP 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_WIN) |
236 | |