gfx/2d/ScaledFontDWrite.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/2d/ScaledFontDWrite.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,435 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "ScaledFontDWrite.h"
    1.10 +#include "PathD2D.h"
    1.11 +#include "DrawTargetD2D.h"
    1.12 +#include "Logging.h"
    1.13 +
    1.14 +#include <vector>
    1.15 +
    1.16 +namespace mozilla {
    1.17 +namespace gfx {
    1.18 +
    1.19 +struct ffReferenceKey
    1.20 +{
    1.21 +    uint8_t *mData;
    1.22 +    uint32_t mSize;
    1.23 +};
    1.24 +
    1.25 +class DWriteFontFileLoader : public IDWriteFontFileLoader
    1.26 +{
    1.27 +public:
    1.28 +  DWriteFontFileLoader()
    1.29 +  {
    1.30 +  }
    1.31 +
    1.32 +  // IUnknown interface
    1.33 +  IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
    1.34 +  {
    1.35 +    if (iid == __uuidof(IDWriteFontFileLoader)) {
    1.36 +      *ppObject = static_cast<IDWriteFontFileLoader*>(this);
    1.37 +      return S_OK;
    1.38 +    } else if (iid == __uuidof(IUnknown)) {
    1.39 +      *ppObject = static_cast<IUnknown*>(this);
    1.40 +      return S_OK;
    1.41 +    } else {
    1.42 +      return E_NOINTERFACE;
    1.43 +    }
    1.44 +  }
    1.45 +
    1.46 +  IFACEMETHOD_(ULONG, AddRef)()
    1.47 +  {
    1.48 +    return 1;
    1.49 +  }
    1.50 +
    1.51 +  IFACEMETHOD_(ULONG, Release)()
    1.52 +  {
    1.53 +    return 1;
    1.54 +  }
    1.55 +
    1.56 +  // IDWriteFontFileLoader methods
    1.57 +  /**
    1.58 +    * Important! Note the key here -has- to be a pointer to an
    1.59 +    * ffReferenceKey object.
    1.60 +    */
    1.61 +  virtual HRESULT STDMETHODCALLTYPE 
    1.62 +    CreateStreamFromKey(void const* fontFileReferenceKey,
    1.63 +                        UINT32 fontFileReferenceKeySize,
    1.64 +                        OUT IDWriteFontFileStream** fontFileStream);
    1.65 +
    1.66 +  /**
    1.67 +    * Gets the singleton loader instance. Note that when using this font
    1.68 +    * loader, the key must be a pointer to an FallibleTArray<uint8_t>. This
    1.69 +    * array will be empty when the function returns.
    1.70 +    */
    1.71 +  static IDWriteFontFileLoader* Instance()
    1.72 +  {
    1.73 +    if (!mInstance) {
    1.74 +      mInstance = new DWriteFontFileLoader();
    1.75 +      DrawTargetD2D::GetDWriteFactory()->
    1.76 +          RegisterFontFileLoader(mInstance);
    1.77 +    }
    1.78 +    return mInstance;
    1.79 +  }
    1.80 +
    1.81 +private:
    1.82 +  static IDWriteFontFileLoader* mInstance;
    1.83 +}; 
    1.84 +
    1.85 +class DWriteFontFileStream : public IDWriteFontFileStream
    1.86 +{
    1.87 +public:
    1.88 +  /**
    1.89 +    * Used by the FontFileLoader to create a new font stream,
    1.90 +    * this font stream is created from data in memory. The memory
    1.91 +    * passed may be released after object creation, it will be
    1.92 +    * copied internally.
    1.93 +    *
    1.94 +    * @param aData Font data
    1.95 +    */
    1.96 +  DWriteFontFileStream(uint8_t *aData, uint32_t aSize);
    1.97 +  ~DWriteFontFileStream();
    1.98 +
    1.99 +  // IUnknown interface
   1.100 +  IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
   1.101 +  {
   1.102 +    if (iid == __uuidof(IDWriteFontFileStream)) {
   1.103 +      *ppObject = static_cast<IDWriteFontFileStream*>(this);
   1.104 +      return S_OK;
   1.105 +    } else if (iid == __uuidof(IUnknown)) {
   1.106 +      *ppObject = static_cast<IUnknown*>(this);
   1.107 +      return S_OK;
   1.108 +    } else {
   1.109 +      return E_NOINTERFACE;
   1.110 +    }
   1.111 +  }
   1.112 +
   1.113 +  IFACEMETHOD_(ULONG, AddRef)()
   1.114 +  {
   1.115 +    ++mRefCnt;
   1.116 +    return mRefCnt;
   1.117 +  }
   1.118 +
   1.119 +  IFACEMETHOD_(ULONG, Release)()
   1.120 +  {
   1.121 +    --mRefCnt;
   1.122 +    if (mRefCnt == 0) {
   1.123 +      delete this;
   1.124 +      return 0;
   1.125 +    }
   1.126 +    return mRefCnt;
   1.127 +  }
   1.128 +
   1.129 +  // IDWriteFontFileStream methods
   1.130 +  virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
   1.131 +                                                     UINT64 fileOffset,
   1.132 +                                                     UINT64 fragmentSize,
   1.133 +                                                     OUT void** fragmentContext);
   1.134 +
   1.135 +  virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
   1.136 +
   1.137 +  virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);
   1.138 +
   1.139 +  virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);
   1.140 +
   1.141 +private:
   1.142 +  std::vector<uint8_t> mData;
   1.143 +  uint32_t mRefCnt;
   1.144 +};
   1.145 +
   1.146 +static BYTE
   1.147 +GetSystemTextQuality()
   1.148 +{
   1.149 +  BOOL font_smoothing;
   1.150 +  UINT smoothing_type;
   1.151 +
   1.152 +  if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
   1.153 +    return DEFAULT_QUALITY;
   1.154 +  }
   1.155 +
   1.156 +  if (font_smoothing) {
   1.157 +      if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE,
   1.158 +                                0, &smoothing_type, 0)) {
   1.159 +        return DEFAULT_QUALITY;
   1.160 +      }
   1.161 +
   1.162 +      if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) {
   1.163 +        return CLEARTYPE_QUALITY;
   1.164 +      }
   1.165 +
   1.166 +      return ANTIALIASED_QUALITY;
   1.167 +  }
   1.168 +
   1.169 +  return DEFAULT_QUALITY;
   1.170 +}
   1.171 +
   1.172 +#define GASP_TAG 0x70736167
   1.173 +#define GASP_DOGRAY 0x2
   1.174 +
   1.175 +static inline unsigned short
   1.176 +readShort(const char *aBuf)
   1.177 +{
   1.178 +  return (*aBuf << 8) | *(aBuf + 1);
   1.179 +}
   1.180 +
   1.181 +static bool
   1.182 +DoGrayscale(IDWriteFontFace *aDWFace, Float ppem)
   1.183 +{
   1.184 +  void *tableContext;
   1.185 +  char *tableData;
   1.186 +  UINT32 tableSize;
   1.187 +  BOOL exists;
   1.188 +  aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists);
   1.189 +
   1.190 +  if (exists) {
   1.191 +    if (tableSize < 4) {
   1.192 +      aDWFace->ReleaseFontTable(tableContext);
   1.193 +      return true;
   1.194 +    }
   1.195 +    struct gaspRange {
   1.196 +      unsigned short maxPPEM; // Stored big-endian
   1.197 +      unsigned short behavior; // Stored big-endian
   1.198 +    };
   1.199 +    unsigned short numRanges = readShort(tableData + 2);
   1.200 +    if (tableSize < (UINT)4 + numRanges * 4) {
   1.201 +      aDWFace->ReleaseFontTable(tableContext);
   1.202 +      return true;
   1.203 +    }
   1.204 +    gaspRange *ranges = (gaspRange *)(tableData + 4);
   1.205 +    for (int i = 0; i < numRanges; i++) {
   1.206 +      if (readShort((char*)&ranges[i].maxPPEM) > ppem) {
   1.207 +        if (!(readShort((char*)&ranges[i].behavior) & GASP_DOGRAY)) {
   1.208 +          aDWFace->ReleaseFontTable(tableContext);
   1.209 +          return false;
   1.210 +        }
   1.211 +        break;
   1.212 +      }
   1.213 +    }
   1.214 +    aDWFace->ReleaseFontTable(tableContext);
   1.215 +  }
   1.216 +  return true;
   1.217 +}
   1.218 +
   1.219 +IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr;
   1.220 +
   1.221 +HRESULT STDMETHODCALLTYPE
   1.222 +DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey, 
   1.223 +                                          UINT32 fontFileReferenceKeySize, 
   1.224 +                                          IDWriteFontFileStream **fontFileStream)
   1.225 +{
   1.226 +  if (!fontFileReferenceKey || !fontFileStream) {
   1.227 +    return E_POINTER;
   1.228 +  }
   1.229 +
   1.230 +  const ffReferenceKey *key = static_cast<const ffReferenceKey*>(fontFileReferenceKey);
   1.231 +  *fontFileStream = 
   1.232 +    new DWriteFontFileStream(key->mData, key->mSize);
   1.233 +
   1.234 +  if (!*fontFileStream) {
   1.235 +    return E_OUTOFMEMORY;
   1.236 +  }
   1.237 +  (*fontFileStream)->AddRef();
   1.238 +  return S_OK;
   1.239 +}
   1.240 +
   1.241 +DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize)
   1.242 +  : mRefCnt(0)
   1.243 +{
   1.244 +  mData.resize(aSize);
   1.245 +  memcpy(&mData.front(), aData, aSize);
   1.246 +}
   1.247 +
   1.248 +DWriteFontFileStream::~DWriteFontFileStream()
   1.249 +{
   1.250 +}
   1.251 +
   1.252 +HRESULT STDMETHODCALLTYPE
   1.253 +DWriteFontFileStream::GetFileSize(UINT64 *fileSize)
   1.254 +{
   1.255 +  *fileSize = mData.size();
   1.256 +  return S_OK;
   1.257 +}
   1.258 +
   1.259 +HRESULT STDMETHODCALLTYPE
   1.260 +DWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
   1.261 +{
   1.262 +  return E_NOTIMPL;
   1.263 +}
   1.264 +
   1.265 +HRESULT STDMETHODCALLTYPE
   1.266 +DWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
   1.267 +                                       UINT64 fileOffset,
   1.268 +                                       UINT64 fragmentSize,
   1.269 +                                       void **fragmentContext)
   1.270 +{
   1.271 +  // We are required to do bounds checking.
   1.272 +  if (fileOffset + fragmentSize > mData.size()) {
   1.273 +    return E_FAIL;
   1.274 +  }
   1.275 +
   1.276 +  // truncate the 64 bit fileOffset to size_t sized index into mData
   1.277 +  size_t index = static_cast<size_t>(fileOffset);
   1.278 +
   1.279 +  // We should be alive for the duration of this.
   1.280 +  *fragmentStart = &mData[index];
   1.281 +  *fragmentContext = nullptr;
   1.282 +  return S_OK;
   1.283 +}
   1.284 +
   1.285 +void STDMETHODCALLTYPE
   1.286 +DWriteFontFileStream::ReleaseFileFragment(void *fragmentContext)
   1.287 +{
   1.288 +}
   1.289 +
   1.290 +ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize,
   1.291 +                                   uint32_t aIndex, Float aGlyphSize)
   1.292 +  : ScaledFontBase(aGlyphSize)
   1.293 +{
   1.294 +  IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory();
   1.295 +
   1.296 +  ffReferenceKey key;
   1.297 +  key.mData = aData;
   1.298 +  key.mSize = aSize;
   1.299 +
   1.300 +  RefPtr<IDWriteFontFile> fontFile;
   1.301 +  if (FAILED(factory->CreateCustomFontFileReference(&key, sizeof(ffReferenceKey), DWriteFontFileLoader::Instance(), byRef(fontFile)))) {
   1.302 +    gfxWarning() << "Failed to load font file from data!";
   1.303 +    return;
   1.304 +  }
   1.305 +
   1.306 +  IDWriteFontFile *ff = fontFile;
   1.307 +  if (FAILED(factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &ff, aIndex, DWRITE_FONT_SIMULATIONS_NONE, byRef(mFontFace)))) {
   1.308 +    gfxWarning() << "Failed to create font face from font file data!";
   1.309 +  }
   1.310 +}
   1.311 +
   1.312 +TemporaryRef<Path>
   1.313 +ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
   1.314 +{
   1.315 +  if (aTarget->GetType() != BackendType::DIRECT2D) {
   1.316 +    return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
   1.317 +  }
   1.318 +
   1.319 +  RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
   1.320 +
   1.321 +  PathBuilderD2D *pathBuilderD2D =
   1.322 +    static_cast<PathBuilderD2D*>(pathBuilder.get());
   1.323 +
   1.324 +  CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
   1.325 +
   1.326 +  return pathBuilder->Finish();
   1.327 +}
   1.328 +
   1.329 +void
   1.330 +ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
   1.331 +{
   1.332 +  if (aBackendType != BackendType::DIRECT2D) {
   1.333 +    ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
   1.334 +    return;
   1.335 +  }
   1.336 +
   1.337 +  PathBuilderD2D *pathBuilderD2D =
   1.338 +    static_cast<PathBuilderD2D*>(aBuilder);
   1.339 +
   1.340 +  CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
   1.341 +}
   1.342 +
   1.343 +void
   1.344 +ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink)
   1.345 +{
   1.346 +  std::vector<UINT16> indices;
   1.347 +  std::vector<FLOAT> advances;
   1.348 +  std::vector<DWRITE_GLYPH_OFFSET> offsets;
   1.349 +  indices.resize(aBuffer.mNumGlyphs);
   1.350 +  advances.resize(aBuffer.mNumGlyphs);
   1.351 +  offsets.resize(aBuffer.mNumGlyphs);
   1.352 +
   1.353 +  memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs);
   1.354 +  for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
   1.355 +    indices[i] = aBuffer.mGlyphs[i].mIndex;
   1.356 +    offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x;
   1.357 +    offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y;
   1.358 +  }
   1.359 +
   1.360 +  mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(),
   1.361 +                                &offsets.front(), aBuffer.mNumGlyphs,
   1.362 +                                FALSE, FALSE, aSink);
   1.363 +}
   1.364 +
   1.365 +bool
   1.366 +ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
   1.367 +{
   1.368 +  UINT32 fileCount = 0;
   1.369 +  mFontFace->GetFiles(&fileCount, nullptr);
   1.370 +
   1.371 +  if (fileCount > 1) {
   1.372 +    MOZ_ASSERT(false);
   1.373 +    return false;
   1.374 +  }
   1.375 +
   1.376 +  RefPtr<IDWriteFontFile> file;
   1.377 +  mFontFace->GetFiles(&fileCount, byRef(file));
   1.378 +
   1.379 +  const void *referenceKey;
   1.380 +  UINT32 refKeySize;
   1.381 +  // XXX - This can currently crash for webfonts, as when we get the reference
   1.382 +  // key out of the file, that can be an invalid reference key for the loader
   1.383 +  // we use it with. The fix to this is not obvious but it will probably 
   1.384 +  // have to happen inside thebes.
   1.385 +  file->GetReferenceKey(&referenceKey, &refKeySize);
   1.386 +
   1.387 +  RefPtr<IDWriteFontFileLoader> loader;
   1.388 +  file->GetLoader(byRef(loader));
   1.389 +  
   1.390 +  RefPtr<IDWriteFontFileStream> stream;
   1.391 +  loader->CreateStreamFromKey(referenceKey, refKeySize, byRef(stream));
   1.392 +
   1.393 +  UINT64 fileSize64;
   1.394 +  stream->GetFileSize(&fileSize64);
   1.395 +  if (fileSize64 > UINT32_MAX) {
   1.396 +    MOZ_ASSERT(false);
   1.397 +    return false;
   1.398 +  }
   1.399 +  
   1.400 +  uint32_t fileSize = static_cast<uint32_t>(fileSize64);
   1.401 +  const void *fragmentStart;
   1.402 +  void *context;
   1.403 +  stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context);
   1.404 +
   1.405 +  aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton);
   1.406 +
   1.407 +  stream->ReleaseFileFragment(context);
   1.408 +
   1.409 +  return true;
   1.410 +}
   1.411 +
   1.412 +AntialiasMode
   1.413 +ScaledFontDWrite::GetDefaultAAMode()
   1.414 +{
   1.415 +  AntialiasMode defaultMode = AntialiasMode::SUBPIXEL;
   1.416 +
   1.417 +  switch (GetSystemTextQuality()) {
   1.418 +  case CLEARTYPE_QUALITY:
   1.419 +    defaultMode = AntialiasMode::SUBPIXEL;
   1.420 +    break;
   1.421 +  case ANTIALIASED_QUALITY:
   1.422 +    defaultMode = AntialiasMode::GRAY;
   1.423 +    break;
   1.424 +  case DEFAULT_QUALITY:
   1.425 +    defaultMode = AntialiasMode::NONE;
   1.426 +    break;
   1.427 +  }
   1.428 +
   1.429 +  if (defaultMode == AntialiasMode::GRAY) {
   1.430 +    if (!DoGrayscale(mFontFace, mSize)) {
   1.431 +      defaultMode = AntialiasMode::NONE;
   1.432 +    }
   1.433 +  }
   1.434 +  return defaultMode;
   1.435 +}
   1.436 +
   1.437 +}
   1.438 +}

mercurial