DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (2658e3d7a2b2)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "NativeFontResourceDWrite.h"
#include "UnscaledFontDWrite.h"

#include <unordered_map>

#include "Logging.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticMutex.h"
#include "nsTArray.h"

namespace mozilla {
namespace gfx {

static StaticMutex sFontFileStreamsMutex;
static uint64_t sNextFontFileKey = 0;
static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;

class DWriteFontFileLoader : public IDWriteFontFileLoader {
 public:
  DWriteFontFileLoader() {}

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

  IFACEMETHOD_(ULONG, AddRef)() { return 1; }

  IFACEMETHOD_(ULONG, Release)() { return 1; }

  // IDWriteFontFileLoader methods
  /**
   * Important! Note the key here has to be a uint64_t that will have been
   * generated by incrementing sNextFontFileKey.
   */
  virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey(
      void const* fontFileReferenceKey, UINT32 fontFileReferenceKeySize,
      OUT IDWriteFontFileStream** fontFileStream);

  /**
   * Gets the singleton loader instance. Note that when using this font
   * loader, the key must be a uint64_t that has been generated by incrementing
   * sNextFontFileKey.
   * Also note that this is _not_ threadsafe.
   */
  static IDWriteFontFileLoader* Instance() {
    if (!mInstance) {
      mInstance = new DWriteFontFileLoader();
      Factory::GetDWriteFactory()->RegisterFontFileLoader(mInstance);
    }
    return mInstance;
  }

 private:
  static IDWriteFontFileLoader* mInstance;
};

class DWriteFontFileStream final : public IDWriteFontFileStream {
 public:
  explicit DWriteFontFileStream(uint64_t aFontFileKey);

  /**
   * 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
   */
  bool Initialize(uint8_t* aData, uint32_t aSize);

  // 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)() { return ++mRefCnt; }

  IFACEMETHOD_(ULONG, Release)() {
    uint32_t count = --mRefCnt;
    if (count == 0) {
      // Avoid locking unless necessary. Verify the refcount hasn't changed
      // while locked. Delete within the scope of the lock when zero.
      StaticMutexAutoLock lock(sFontFileStreamsMutex);
      if (0 != mRefCnt) {
        return mRefCnt;
      }
      delete this;
    }
    return count;
  }

  // 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:
  nsTArray<uint8_t> mData;
  Atomic<uint32_t> mRefCnt;
  uint64_t mFontFileKey;

  ~DWriteFontFileStream();
};

IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr;

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

  StaticMutexAutoLock lock(sFontFileStreamsMutex);
  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;
}

DWriteFontFileStream::DWriteFontFileStream(uint64_t aFontFileKey)
    : mRefCnt(0), mFontFileKey(aFontFileKey) {}

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

bool DWriteFontFileStream::Initialize(uint8_t* aData, uint32_t aSize) {
  if (!mData.SetLength(aSize, fallible)) {
    return false;
  }
  memcpy(mData.Elements(), aData, aSize);
  return true;
}

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

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

HRESULT STDMETHODCALLTYPE DWriteFontFileStream::ReadFileFragment(
    const void** fragmentStart, UINT64 fileOffset, UINT64 fragmentSize,
    void** fragmentContext) {
  // We are required to do bounds checking.
  if (fileOffset + fragmentSize > mData.Length()) {
    return E_FAIL;
  }

  // truncate the 64 bit fileOffset to size_t sized index into mData
  size_t index = static_cast<size_t>(fileOffset);

  // We should be alive for the duration of this.
  *fragmentStart = &mData[index];
  *fragmentContext = nullptr;
  return S_OK;
}

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

/* static */
already_AddRefed<NativeFontResourceDWrite> NativeFontResourceDWrite::Create(
    uint8_t* aFontData, uint32_t aDataLength, bool aNeedsCairo) {
  RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
  if (!factory) {
    gfxWarning() << "Failed to get DWrite Factory.";
    return nullptr;
  }

  sFontFileStreamsMutex.Lock();
  uint64_t fontFileKey = sNextFontFileKey++;
  RefPtr<DWriteFontFileStream> ffsRef = new DWriteFontFileStream(fontFileKey);
  if (!ffsRef->Initialize(aFontData, aDataLength)) {
    sFontFileStreamsMutex.Unlock();
    gfxWarning() << "Failed to create DWriteFontFileStream.";
    return nullptr;
  }
  sFontFileStreams[fontFileKey] = ffsRef;
  sFontFileStreamsMutex.Unlock();

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

  BOOL isSupported;
  DWRITE_FONT_FILE_TYPE fileType;
  DWRITE_FONT_FACE_TYPE faceType;
  UINT32 numberOfFaces;
  hr = fontFile->Analyze(&isSupported, &fileType, &faceType, &numberOfFaces);
  if (FAILED(hr) || !isSupported) {
    gfxWarning() << "Font file is not supported.";
    return nullptr;
  }

  RefPtr<NativeFontResourceDWrite> fontResource =
      new NativeFontResourceDWrite(factory, fontFile.forget(), ffsRef.forget(),
                                   faceType, numberOfFaces, aNeedsCairo);
  return fontResource.forget();
}

already_AddRefed<UnscaledFont> NativeFontResourceDWrite::CreateUnscaledFont(
    uint32_t aIndex, const uint8_t* aInstanceData,
    uint32_t aInstanceDataLength) {
  if (aIndex >= mNumberOfFaces) {
    gfxWarning() << "Font face index is too high for font resource.";
    return nullptr;
  }

  IDWriteFontFile* fontFile = mFontFile;
  RefPtr<IDWriteFontFace> fontFace;
  if (FAILED(mFactory->CreateFontFace(mFaceType, 1, &fontFile, aIndex,
                                      DWRITE_FONT_SIMULATIONS_NONE,
                                      getter_AddRefs(fontFace)))) {
    gfxWarning() << "Failed to create font face from font file data.";
    return nullptr;
  }

  RefPtr<UnscaledFont> unscaledFont = new UnscaledFontDWrite(
      fontFace, nullptr, DWRITE_FONT_SIMULATIONS_NONE, mNeedsCairo);

  return unscaledFont.forget();
}

}  // namespace gfx
}  // namespace mozilla