gfx/thebes/gfxDWriteCommon.cpp
author Mark Banner <standard8@mozilla.com>
Wed, 07 Feb 2018 16:01:29 +0000
changeset 455301 098f795537450afd8e7a3040247613cf570a859b
parent 453518 0cd5065a76e9f351f47fec93a93c514ce4732b5b
child 465697 6429b0db7653411c3e3cd3f7164c8d64408067e5
permissions -rw-r--r--
Bug 1436389 - Upgrade ESLint to version 4.17.0. r=mossop MozReview-Commit-ID: I4KBELxhBlM

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "gfxDWriteCommon.h"

#include <unordered_map>

#include "mozilla/Atomics.h"
#include "mozilla/gfx/Logging.h"

static mozilla::Atomic<uint64_t> sNextFontFileKey;
static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;

IDWriteFontFileLoader* gfxDWriteFontFileLoader::mInstance = nullptr;

class gfxDWriteFontFileStream final : public IDWriteFontFileStream
{
public:
  /**
  * Used by the FontFileLoader to create a new font stream,
  * this font stream is created from data in memory. The memory
  * passed may be released after object creation, it will be
  * copied internally.
  *
  * @param aData Font data
  */
  gfxDWriteFontFileStream(const uint8_t* aData,
                          uint32_t aLength,
                          uint64_t aFontFileKey);
  ~gfxDWriteFontFileStream();

  // IUnknown interface
  IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
  {
    if (iid == __uuidof(IDWriteFontFileStream)) {
      *ppObject = static_cast<IDWriteFontFileStream*>(this);
      return S_OK;
    }
    else if (iid == __uuidof(IUnknown)) {
      *ppObject = static_cast<IUnknown*>(this);
      return S_OK;
    }
    else {
      return E_NOINTERFACE;
    }
  }

  IFACEMETHOD_(ULONG, AddRef)()
  {
    NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
    ++mRefCnt;
    return mRefCnt;
  }

  IFACEMETHOD_(ULONG, Release)()
  {
    NS_PRECONDITION(0 != mRefCnt, "dup release");
    --mRefCnt;
    if (mRefCnt == 0) {
      delete this;
      return 0;
    }
    return mRefCnt;
  }

  // IDWriteFontFileStream methods
  virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
                                                     UINT64 fileOffset,
                                                     UINT64 fragmentSize,
                                                     OUT void** fragmentContext);

  virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);

  virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);

  virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);

private:
  FallibleTArray<uint8_t> mData;
  nsAutoRefCnt mRefCnt;
  uint64_t mFontFileKey;
};

gfxDWriteFontFileStream::gfxDWriteFontFileStream(const uint8_t* aData,
                                                 uint32_t aLength,
                                                 uint64_t aFontFileKey)
  : mFontFileKey(aFontFileKey)
{
  // If this fails, mData will remain empty. That's OK: GetFileSize()
  // will then return 0, etc., and the font just won't load.
  if (!mData.AppendElements(aData, aLength, mozilla::fallible_t())) {
    NS_WARNING("Failed to store data in gfxDWriteFontFileStream");
  }
}

gfxDWriteFontFileStream::~gfxDWriteFontFileStream()
{
  sFontFileStreams.erase(mFontFileKey);
}

HRESULT STDMETHODCALLTYPE
gfxDWriteFontFileStream::GetFileSize(UINT64 *fileSize)
{
  *fileSize = mData.Length();
  return S_OK;
}

HRESULT STDMETHODCALLTYPE
gfxDWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
{
  return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE
gfxDWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
                                          UINT64 fileOffset,
                                          UINT64 fragmentSize,
                                          void **fragmentContext)
{
  // We are required to do bounds checking.
  if (fileOffset + fragmentSize > (UINT64)mData.Length()) {
    return E_FAIL;
  }
  // We should be alive for the duration of this.
  *fragmentStart = &mData[fileOffset];
  *fragmentContext = nullptr;
  return S_OK;
}

void STDMETHODCALLTYPE
gfxDWriteFontFileStream::ReleaseFileFragment(void *fragmentContext)
{
}

HRESULT STDMETHODCALLTYPE
gfxDWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey, 
                                             UINT32 fontFileReferenceKeySize, 
                                             IDWriteFontFileStream **fontFileStream)
{
    if (!fontFileReferenceKey || !fontFileStream) {
        return E_POINTER;
    }

    uint64_t fontFileKey = *static_cast<const uint64_t*>(fontFileReferenceKey);
    auto found = sFontFileStreams.find(fontFileKey);
    if (found == sFontFileStreams.end()) {
      *fontFileStream = nullptr;
      return E_FAIL;
    }

    found->second->AddRef();
    *fontFileStream = found->second;
    return S_OK;
}

/* static */
HRESULT
gfxDWriteFontFileLoader::CreateCustomFontFile(const uint8_t* aFontData,
                                              uint32_t aLength,
                                              IDWriteFontFile** aFontFile,
                                              IDWriteFontFileStream** aFontFileStream)
{
  MOZ_ASSERT(aFontFile);
  MOZ_ASSERT(aFontFileStream);

  RefPtr<IDWriteFactory> factory = mozilla::gfx::Factory::GetDWriteFactory();
  if (!factory) {
    gfxCriticalError() << "Failed to get DWrite Factory in CreateCustomFontFile.";
    return E_FAIL;
  }

  uint64_t fontFileKey = sNextFontFileKey++;
  RefPtr<IDWriteFontFileStream> ffsRef =
      new gfxDWriteFontFileStream(aFontData, aLength, fontFileKey);
  sFontFileStreams[fontFileKey] = ffsRef;

  RefPtr<IDWriteFontFile> fontFile;
  HRESULT hr = factory->CreateCustomFontFileReference(&fontFileKey, sizeof(fontFileKey), Instance(), getter_AddRefs(fontFile));
  if (FAILED(hr)) {
    NS_WARNING("Failed to load font file from data!");
    return hr;
  }

  fontFile.forget(aFontFile);
  ffsRef.forget(aFontFileStream);

  return S_OK;
}