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