|  | /* | 
|  | * 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 "SkTemplates.h" | 
|  | #include "SkTFitsIn.h" | 
|  | #include "SkTScopedComPtr.h" | 
|  |  | 
|  | #include <dwrite.h> | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  | //  SkIDWriteFontFileStream | 
|  |  | 
|  | SkDWriteFontFileStream::SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream) | 
|  | : fFontFileStream(SkRefComPtr(fontFileStream)) | 
|  | , fPos(0) | 
|  | , fLockedMemory(NULL) | 
|  | , fFragmentLock(NULL) { | 
|  | } | 
|  |  | 
|  | SkDWriteFontFileStream::~SkDWriteFontFileStream() { | 
|  | if (fFragmentLock) { | 
|  | fFontFileStream->ReleaseFileFragment(fFragmentLock); | 
|  | } | 
|  | } | 
|  |  | 
|  | size_t SkDWriteFontFileStream::read(void* buffer, size_t size) { | 
|  | HRESULT hr = S_OK; | 
|  |  | 
|  | if (NULL == buffer) { | 
|  | size_t fileSize = this->getLength(); | 
|  |  | 
|  | 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. | 
|  | size_t fileSize = this->getLength(); | 
|  | if (fPos + size <= fileSize) { | 
|  | //This means we were within bounds, but failed for some other reason. | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | bool SkDWriteFontFileStream::isAtEnd() const { | 
|  | return fPos == this->getLength(); | 
|  | } | 
|  |  | 
|  | bool SkDWriteFontFileStream::rewind() { | 
|  | fPos = 0; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | SkDWriteFontFileStream* SkDWriteFontFileStream::duplicate() const { | 
|  | return SkNEW_ARGS(SkDWriteFontFileStream, (fFontFileStream.get())); | 
|  | } | 
|  |  | 
|  | size_t SkDWriteFontFileStream::getPosition() const { | 
|  | return fPos; | 
|  | } | 
|  |  | 
|  | bool SkDWriteFontFileStream::seek(size_t position) { | 
|  | size_t length = this->getLength(); | 
|  | fPos = (position > length) ? length : position; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool SkDWriteFontFileStream::move(long offset) { | 
|  | return seek(fPos + offset); | 
|  | } | 
|  |  | 
|  | SkDWriteFontFileStream* SkDWriteFontFileStream::fork() const { | 
|  | SkAutoTUnref<SkDWriteFontFileStream> that(this->duplicate()); | 
|  | that->seek(fPos); | 
|  | return that.detach(); | 
|  | } | 
|  |  | 
|  | size_t SkDWriteFontFileStream::getLength() const { | 
|  | HRESULT hr = S_OK; | 
|  | UINT64 realFileSize = 0; | 
|  | hr = fFontFileStream->GetFileSize(&realFileSize); | 
|  | if (!SkTFitsIn<size_t>(realFileSize)) { | 
|  | return 0; | 
|  | } | 
|  | return static_cast<size_t>(realFileSize); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  | //  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(SkRef(stream)) { | 
|  | } | 
|  |  | 
|  | 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 (!SkTFitsIn<size_t>(fileOffset + fragmentSize)) { | 
|  | 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; | 
|  | } | 
|  | SkAutoTMalloc<uint8_t> streamData(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) { | 
|  | sk_free(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; | 
|  | } |