| /* |
| * Copyright 2012 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkTypes.h" |
| #include "SkDWriteFontFileStream.h" |
| #include "SkHRESULT.h" |
| |
| #include <dwrite.h> |
| #include <limits> |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // SkIDWriteFontFileStream |
| |
| SkDWriteFontFileStream::SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream) |
| : fFontFileStream(fontFileStream) |
| , fPos(0) |
| , fLockedMemory(NULL) |
| , fFragmentLock(NULL) { |
| fontFileStream->AddRef(); |
| } |
| |
| SkDWriteFontFileStream::~SkDWriteFontFileStream() { |
| if (fFragmentLock) { |
| fFontFileStream->ReleaseFileFragment(fFragmentLock); |
| } |
| } |
| |
| const void* SkDWriteFontFileStream::getMemoryBase() { |
| if (fLockedMemory) { |
| return fLockedMemory; |
| } |
| |
| UINT64 fileSize; |
| HRNM(fFontFileStream->GetFileSize(&fileSize), "Could not get file size"); |
| HRNM(fFontFileStream->ReadFileFragment(&fLockedMemory, 0, fileSize, &fFragmentLock), |
| "Could not lock file fragment."); |
| return fLockedMemory; |
| } |
| |
| bool SkDWriteFontFileStream::rewind() { |
| fPos = 0; |
| return true; |
| } |
| |
| size_t SkDWriteFontFileStream::read(void* buffer, size_t size) { |
| HRESULT hr = S_OK; |
| |
| if (NULL == buffer) { |
| UINT64 realFileSize = 0; |
| hr = fFontFileStream->GetFileSize(&realFileSize); |
| if (realFileSize > (std::numeric_limits<size_t>::max)()) { |
| return 0; |
| } |
| size_t fileSize = static_cast<size_t>(realFileSize); |
| if (size == 0) { |
| return fileSize; |
| } else { |
| if (fPos + size > fileSize) { |
| size_t skipped = fileSize - fPos; |
| fPos = fileSize; |
| return skipped; |
| } else { |
| fPos += size; |
| return size; |
| } |
| } |
| } |
| |
| const void* start; |
| void* fragmentLock; |
| hr = fFontFileStream->ReadFileFragment(&start, fPos, size, &fragmentLock); |
| if (SUCCEEDED(hr)) { |
| memcpy(buffer, start, size); |
| fFontFileStream->ReleaseFileFragment(fragmentLock); |
| fPos += size; |
| return size; |
| } |
| |
| //The read may have failed because we asked for too much data. |
| UINT64 realFileSize = 0; |
| hr = fFontFileStream->GetFileSize(&realFileSize); |
| if (realFileSize > (std::numeric_limits<size_t>::max)()) { |
| return 0; |
| } |
| size_t fileSize = static_cast<size_t>(realFileSize); |
| if (fPos + size > fileSize) { |
| size_t read = fileSize - fPos; |
| hr = fFontFileStream->ReadFileFragment(&start, fPos, read, &fragmentLock); |
| if (SUCCEEDED(hr)) { |
| memcpy(buffer, start, read); |
| fFontFileStream->ReleaseFileFragment(fragmentLock); |
| fPos = fileSize; |
| return read; |
| } |
| return 0; |
| } else { |
| //This means we were within bounds, but failed for some other reason. |
| return 0; |
| } |
| } |
| |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // SkIDWriteFontFileStreamWrapper |
| |
| HRESULT SkDWriteFontFileStreamWrapper::Create(SkStream* stream, SkDWriteFontFileStreamWrapper** streamFontFileStream) { |
| *streamFontFileStream = new SkDWriteFontFileStreamWrapper(stream); |
| if (NULL == streamFontFileStream) { |
| return E_OUTOFMEMORY; |
| } |
| return S_OK; |
| } |
| |
| SkDWriteFontFileStreamWrapper::SkDWriteFontFileStreamWrapper(SkStream* stream) |
| : fRefCount(1), fStream(stream) { |
| stream->ref(); |
| } |
| |
| HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::QueryInterface(REFIID iid, void** ppvObject) { |
| if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) { |
| *ppvObject = this; |
| AddRef(); |
| return S_OK; |
| } else { |
| *ppvObject = NULL; |
| return E_NOINTERFACE; |
| } |
| } |
| |
| ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::AddRef() { |
| return InterlockedIncrement(&fRefCount); |
| } |
| |
| ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::Release() { |
| ULONG newCount = InterlockedDecrement(&fRefCount); |
| if (0 == newCount) { |
| delete this; |
| } |
| return newCount; |
| } |
| |
| HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReadFileFragment( |
| void const** fragmentStart, |
| UINT64 fileOffset, |
| UINT64 fragmentSize, |
| void** fragmentContext) |
| { |
| // The loader is responsible for doing a bounds check. |
| UINT64 fileSize; |
| this->GetFileSize(&fileSize); |
| if (fileOffset > fileSize || fragmentSize > fileSize - fileOffset) { |
| *fragmentStart = NULL; |
| *fragmentContext = NULL; |
| return E_FAIL; |
| } |
| |
| if (fileOffset + fragmentSize > (std::numeric_limits<size_t>::max)()) { |
| return E_FAIL; |
| } |
| |
| const void* data = fStream->getMemoryBase(); |
| if (NULL != data) { |
| *fragmentStart = static_cast<BYTE const*>(data) + static_cast<size_t>(fileOffset); |
| *fragmentContext = NULL; |
| |
| } else { |
| //May be called from multiple threads. |
| SkAutoMutexAcquire ama(fStreamMutex); |
| |
| *fragmentStart = NULL; |
| *fragmentContext = NULL; |
| |
| if (!fStream->rewind()) { |
| return E_FAIL; |
| } |
| if (fStream->skip(static_cast<size_t>(fileOffset)) != fileOffset) { |
| return E_FAIL; |
| } |
| SkAutoTDeleteArray<uint8_t> streamData(new uint8_t[static_cast<size_t>(fragmentSize)]); |
| if (fStream->read(streamData.get(), static_cast<size_t>(fragmentSize)) != fragmentSize) { |
| return E_FAIL; |
| } |
| |
| *fragmentStart = streamData.get(); |
| *fragmentContext = streamData.detach(); |
| } |
| return S_OK; |
| } |
| |
| void STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReleaseFileFragment(void* fragmentContext) { |
| if (NULL == fragmentContext) { |
| return; |
| } |
| delete [] fragmentContext; |
| } |
| |
| HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetFileSize(UINT64* fileSize) { |
| *fileSize = fStream->getLength(); |
| return S_OK; |
| } |
| |
| HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetLastWriteTime(UINT64* lastWriteTime) { |
| // The concept of last write time does not apply to this loader. |
| *lastWriteTime = 0; |
| return E_NOTIMPL; |
| } |