gfx/2d/2D.h
author Norisz Fay <nfay@mozilla.com>
Fri, 24 Mar 2023 01:51:51 +0200
changeset 657787 72b81149fa79431a048f90b0f67a1e6f31a01478
parent 657129 038ecfe701fb188e0e055f6ff3bc0ca6fd158239
permissions -rw-r--r--
Backed out changeset c6b34f285903 (bug 1822712) for causing xpcshell failures on test_private_field_xrays.js CLOSED TREE

/* -*- 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/. */

#ifndef _MOZILLA_GFX_2D_H
#define _MOZILLA_GFX_2D_H

#include "Types.h"
#include "Point.h"
#include "Rect.h"
#include "Matrix.h"
#include "Quaternion.h"
#include "UserData.h"
#include "FontVariation.h"
#include <vector>

// GenericRefCountedBase allows us to hold on to refcounted objects of any type
// (contrary to RefCounted<T> which requires knowing the type T) and, in
// particular, without having a dependency on that type. This is used for
// DrawTargetSkia to be able to hold on to a GLContext.
#include "mozilla/GenericRefCounted.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Path.h"

// This RefPtr class isn't ideal for usage in Azure, as it doesn't allow T**
// outparams using the &-operator. But it will have to do as there's no easy
// solution.
#include "mozilla/RefPtr.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/ThreadSafeWeakPtr.h"
#include "mozilla/Atomics.h"

#include "mozilla/DebugOnly.h"

#include "nsRegionFwd.h"

#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GTK)
#  ifndef MOZ_ENABLE_FREETYPE
#    define MOZ_ENABLE_FREETYPE
#  endif
#endif

struct _cairo_surface;
typedef _cairo_surface cairo_surface_t;

struct _cairo_scaled_font;
typedef _cairo_scaled_font cairo_scaled_font_t;

struct FT_LibraryRec_;
typedef FT_LibraryRec_* FT_Library;

struct FT_FaceRec_;
typedef FT_FaceRec_* FT_Face;

typedef int FT_Error;

struct _FcPattern;
typedef _FcPattern FcPattern;

struct ID3D11Texture2D;
struct ID3D11Device;
struct ID2D1Device;
struct ID2D1DeviceContext;
struct ID2D1Multithread;
struct IDWriteFactory;
struct IDWriteRenderingParams;
struct IDWriteFontFace;
struct IDWriteFontCollection;

class SkCanvas;
struct gfxFontStyle;

struct CGContext;
typedef struct CGContext* CGContextRef;

struct CGFont;
typedef CGFont* CGFontRef;

namespace mozilla {

class Mutex;

namespace layers {
class TextureData;
}

namespace wr {
struct FontInstanceOptions;
struct FontInstancePlatformOptions;
}  // namespace wr

namespace gfx {
class UnscaledFont;
class ScaledFont;
}  // namespace gfx

namespace gfx {

class AlphaBoxBlur;
class ScaledFont;
class SourceSurface;
class DataSourceSurface;
class DrawTarget;
class DrawEventRecorder;
class FilterNode;
class LogForwarder;

struct NativeSurface {
  NativeSurfaceType mType;
  SurfaceFormat mFormat;
  gfx::IntSize mSize;
  void* mSurface;
};

/**
 * This structure is used to send draw options that are universal to all drawing
 * operations.
 */
struct DrawOptions {
  /// For constructor parameter description, see member data documentation.
  explicit DrawOptions(Float aAlpha = 1.0f,
                       CompositionOp aCompositionOp = CompositionOp::OP_OVER,
                       AntialiasMode aAntialiasMode = AntialiasMode::DEFAULT)
      : mAlpha(aAlpha),
        mCompositionOp(aCompositionOp),
        mAntialiasMode(aAntialiasMode) {}

  Float mAlpha; /**< Alpha value by which the mask generated by this
                     operation is multiplied. */
  CompositionOp mCompositionOp; /**< The operator that indicates how the source
                                   and destination patterns are blended. */
  AntialiasMode mAntialiasMode; /**< The AntiAlias mode used for this drawing
                                     operation. */
};

struct StoredStrokeOptions;

/**
 * This structure is used to send stroke options that are used in stroking
 * operations.
 */
struct StrokeOptions {
  /// For constructor parameter description, see member data documentation.
  explicit StrokeOptions(Float aLineWidth = 1.0f,
                         JoinStyle aLineJoin = JoinStyle::MITER_OR_BEVEL,
                         CapStyle aLineCap = CapStyle::BUTT,
                         Float aMiterLimit = 10.0f, size_t aDashLength = 0,
                         const Float* aDashPattern = 0, Float aDashOffset = 0.f)
      : mLineWidth(aLineWidth),
        mMiterLimit(aMiterLimit),
        mDashPattern(aDashLength > 0 ? aDashPattern : 0),
        mDashLength(aDashLength),
        mDashOffset(aDashOffset),
        mLineJoin(aLineJoin),
        mLineCap(aLineCap) {
    MOZ_ASSERT(aDashLength == 0 || aDashPattern);
  }

  Float mLineWidth;          //!< Width of the stroke in userspace.
  Float mMiterLimit;         //!< Miter limit in units of linewidth
  const Float* mDashPattern; /**< Series of on/off userspace lengths defining
                                dash. Owned by the caller; must live at least as
                                long as this StrokeOptions.
                                  mDashPattern != null <=> mDashLength > 0. */
  size_t mDashLength;        //!< Number of on/off lengths in mDashPattern.
  Float mDashOffset;         /**< Userspace offset within mDashPattern at which
                                  stroking begins. */
  JoinStyle mLineJoin;       //!< Join style used for joining lines.
  CapStyle mLineCap;         //!< Cap style used for capping lines.

  StoredStrokeOptions* Clone() const;

  bool operator==(const StrokeOptions& aOther) const {
    return mLineWidth == aOther.mLineWidth &&
           mMiterLimit == aOther.mMiterLimit &&
           mDashLength == aOther.mDashLength &&
           (!mDashLength || (mDashPattern && aOther.mDashPattern &&
                             !memcmp(mDashPattern, aOther.mDashPattern,
                                     mDashLength * sizeof(Float)))) &&
           mDashOffset == aOther.mDashOffset && mLineJoin == aOther.mLineJoin &&
           mLineCap == aOther.mLineCap;
  }
};

/**
 * Heap-allocated variation of StrokeOptions that ensures dash patterns are
 * properly allocated and destroyed even if the source was stack-allocated.
 */
struct StoredStrokeOptions : public StrokeOptions {
  explicit StoredStrokeOptions(const StrokeOptions& aOptions)
      : StrokeOptions(aOptions) {
    if (mDashLength) {
      Float* pattern = new Float[mDashLength];
      memcpy(pattern, mDashPattern, mDashLength * sizeof(Float));
      mDashPattern = pattern;
    }
  }

  ~StoredStrokeOptions() {
    if (mDashPattern) {
      delete[] mDashPattern;
    }
  }
};

inline StoredStrokeOptions* StrokeOptions::Clone() const {
  return new StoredStrokeOptions(*this);
}

/**
 * This structure supplies additional options for calls to DrawSurface.
 */
struct DrawSurfaceOptions {
  /// For constructor parameter description, see member data documentation.
  explicit DrawSurfaceOptions(
      SamplingFilter aSamplingFilter = SamplingFilter::LINEAR,
      SamplingBounds aSamplingBounds = SamplingBounds::UNBOUNDED)
      : mSamplingFilter(aSamplingFilter), mSamplingBounds(aSamplingBounds) {}

  SamplingFilter
      mSamplingFilter; /**< SamplingFilter used when resampling source surface
                            region to the destination region. */
  SamplingBounds mSamplingBounds; /**< This indicates whether the implementation
                                     is allowed to sample pixels outside the
                                     source rectangle as specified in
                                     DrawSurface on the surface. */
};

/**
 * ShadowOptions supplies options necessary for describing the appearance of a
 * a shadow in draw calls that use shadowing.
 */
struct ShadowOptions {
  explicit ShadowOptions(const DeviceColor& aColor = DeviceColor(0.0f, 0.0f,
                                                                 0.0f),
                         const Point& aOffset = Point(), Float aSigma = 0.0f)
      : mColor(aColor), mOffset(aOffset), mSigma(aSigma) {}

  DeviceColor mColor; /**< Color of the drawn shadow. */
  Point mOffset;      /**< Offset of the shadow. */
  Float mSigma;       /**< Sigma used for the Gaussian filter kernel. */

  int32_t BlurRadius() const;
};

/**
 * This class is used to store gradient stops, it can only be used with a
 * matching DrawTarget. Not adhering to this condition will make a draw call
 * fail.
 */
class GradientStops : public SupportsThreadSafeWeakPtr<GradientStops> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStops)
  virtual ~GradientStops() = default;

  virtual BackendType GetBackendType() const = 0;
  virtual bool IsValid() const { return true; }

 protected:
  GradientStops() = default;
};

/**
 * This is the base class for 'patterns'. Patterns describe the pixels used as
 * the source for a masked composition operation that is done by the different
 * drawing commands. These objects are not backend specific, however for
 * example the gradient stops on a gradient pattern can be backend specific.
 */
class Pattern {
 public:
  virtual ~Pattern() = default;

  virtual PatternType GetType() const = 0;

  /** Instantiate a new clone with the same pattern type and values. Any
   * internal strong references will be converted to weak references. */
  virtual Pattern* CloneWeak() const { return nullptr; }

  /** Whether the pattern holds an internal weak reference. */
  virtual bool IsWeak() const { return false; }

  /** Whether any internal weak references still point to a target. */
  virtual bool IsValid() const { return true; }

  /** Determine if the pattern type and values exactly match. */
  virtual bool operator==(const Pattern& aOther) const = 0;

  bool operator!=(const Pattern& aOther) const { return !(*this == aOther); }

 protected:
  Pattern() = default;

  // Utility functions to check if a weak reference is still valid.
  template <typename T>
  static inline bool IsRefValid(const RefPtr<T>& aPtr) {
    // RefPtrs are always valid.
    return true;
  }

  template <typename T>
  static inline bool IsRefValid(const ThreadSafeWeakPtr<T>& aPtr) {
    // Weak refs are only valid if they aren't dead.
    return !aPtr.IsDead();
  }
};

class ColorPattern : public Pattern {
 public:
  // Explicit because consumers should generally use ToDeviceColor when
  // creating a ColorPattern.
  explicit ColorPattern(const DeviceColor& aColor) : mColor(aColor) {}

  PatternType GetType() const override { return PatternType::COLOR; }

  Pattern* CloneWeak() const override { return new ColorPattern(mColor); }

  bool operator==(const Pattern& aOther) const override {
    if (aOther.GetType() != PatternType::COLOR) {
      return false;
    }
    const ColorPattern& other = static_cast<const ColorPattern&>(aOther);
    return mColor == other.mColor;
  }

  DeviceColor mColor;
};

/**
 * This class is used for Linear Gradient Patterns, the gradient stops are
 * stored in a separate object and are backend dependent. This class itself
 * may be used on the stack.
 */
template <template <typename> typename REF = RefPtr>
class LinearGradientPatternT : public Pattern {
  typedef LinearGradientPatternT<ThreadSafeWeakPtr> Weak;

 public:
  /// For constructor parameter description, see member data documentation.
  LinearGradientPatternT(const Point& aBegin, const Point& aEnd,
                         RefPtr<GradientStops> aStops,
                         const Matrix& aMatrix = Matrix())
      : mBegin(aBegin),
        mEnd(aEnd),
        mStops(std::move(aStops)),
        mMatrix(aMatrix) {}

  PatternType GetType() const override { return PatternType::LINEAR_GRADIENT; }

  Pattern* CloneWeak() const override {
    return new Weak(mBegin, mEnd, do_AddRef(mStops), mMatrix);
  }

  bool IsWeak() const override {
    return std::is_same<decltype(*this), Weak>::value;
  }

  bool IsValid() const override { return IsRefValid(mStops); }

  template <template <typename> typename T>
  bool operator==(const LinearGradientPatternT<T>& aOther) const {
    return mBegin == aOther.mBegin && mEnd == aOther.mEnd &&
           mStops == aOther.mStops && mMatrix.ExactlyEquals(aOther.mMatrix);
  }

  bool operator==(const Pattern& aOther) const override {
    if (aOther.GetType() != PatternType::LINEAR_GRADIENT) {
      return false;
    }
    return aOther.IsWeak()
               ? *this == static_cast<const Weak&>(aOther)
               : *this == static_cast<const LinearGradientPatternT<>&>(aOther);
  }

  Point mBegin;              //!< Start of the linear gradient
  Point mEnd;                /**< End of the linear gradient - NOTE: In the case
                                  of a zero length gradient it will act as the
                                  color of the last stop. */
  REF<GradientStops> mStops; /**< GradientStops object for this gradient, this
                                  should match the backend type of the draw
                                  target this pattern will be used with. */
  Matrix mMatrix;            /**< A matrix that transforms the pattern into
                                  user space */
};

typedef LinearGradientPatternT<> LinearGradientPattern;

/**
 * This class is used for Radial Gradient Patterns, the gradient stops are
 * stored in a separate object and are backend dependent. This class itself
 * may be used on the stack.
 */
template <template <typename> typename REF = RefPtr>
class RadialGradientPatternT : public Pattern {
  typedef RadialGradientPatternT<ThreadSafeWeakPtr> Weak;

 public:
  /// For constructor parameter description, see member data documentation.
  RadialGradientPatternT(const Point& aCenter1, const Point& aCenter2,
                         Float aRadius1, Float aRadius2,
                         RefPtr<GradientStops> aStops,
                         const Matrix& aMatrix = Matrix())
      : mCenter1(aCenter1),
        mCenter2(aCenter2),
        mRadius1(aRadius1),
        mRadius2(aRadius2),
        mStops(std::move(aStops)),
        mMatrix(aMatrix) {}

  PatternType GetType() const override { return PatternType::RADIAL_GRADIENT; }

  Pattern* CloneWeak() const override {
    return new Weak(mCenter1, mCenter2, mRadius1, mRadius2, do_AddRef(mStops),
                    mMatrix);
  }

  bool IsWeak() const override {
    return std::is_same<decltype(*this), Weak>::value;
  }

  bool IsValid() const override { return IsRefValid(mStops); }

  template <template <typename> typename T>
  bool operator==(const RadialGradientPatternT<T>& aOther) const {
    return mCenter1 == aOther.mCenter1 && mCenter2 == aOther.mCenter2 &&
           mRadius1 == aOther.mRadius1 && mRadius2 == aOther.mRadius2 &&
           mStops == aOther.mStops && mMatrix.ExactlyEquals(aOther.mMatrix);
  }

  bool operator==(const Pattern& aOther) const override {
    if (aOther.GetType() != PatternType::RADIAL_GRADIENT) {
      return false;
    }
    return aOther.IsWeak()
               ? *this == static_cast<const Weak&>(aOther)
               : *this == static_cast<const RadialGradientPatternT<>&>(aOther);
  }

  Point mCenter1;            //!< Center of the inner (focal) circle.
  Point mCenter2;            //!< Center of the outer circle.
  Float mRadius1;            //!< Radius of the inner (focal) circle.
  Float mRadius2;            //!< Radius of the outer circle.
  REF<GradientStops> mStops; /**< GradientStops object for this gradient, this
                                  should match the backend type of the draw
                                target this pattern will be used with. */
  Matrix mMatrix;  //!< A matrix that transforms the pattern into user space
};

typedef RadialGradientPatternT<> RadialGradientPattern;

/**
 * This class is used for Conic Gradient Patterns, the gradient stops are
 * stored in a separate object and are backend dependent. This class itself
 * may be used on the stack.
 */
template <template <typename> typename REF = RefPtr>
class ConicGradientPatternT : public Pattern {
  typedef ConicGradientPatternT<ThreadSafeWeakPtr> Weak;

 public:
  /// For constructor parameter description, see member data documentation.
  ConicGradientPatternT(const Point& aCenter, Float aAngle, Float aStartOffset,
                        Float aEndOffset, RefPtr<GradientStops> aStops,
                        const Matrix& aMatrix = Matrix())
      : mCenter(aCenter),
        mAngle(aAngle),
        mStartOffset(aStartOffset),
        mEndOffset(aEndOffset),
        mStops(std::move(aStops)),
        mMatrix(aMatrix) {}

  PatternType GetType() const override { return PatternType::CONIC_GRADIENT; }

  Pattern* CloneWeak() const override {
    return new Weak(mCenter, mAngle, mStartOffset, mEndOffset,
                    do_AddRef(mStops), mMatrix);
  }

  bool IsWeak() const override {
    return std::is_same<decltype(*this), Weak>::value;
  }

  bool IsValid() const override { return IsRefValid(mStops); }

  template <template <typename> typename T>
  bool operator==(const ConicGradientPatternT<T>& aOther) const {
    return mCenter == aOther.mCenter && mAngle == aOther.mAngle &&
           mStartOffset == aOther.mStartOffset &&
           mEndOffset == aOther.mEndOffset && mStops == aOther.mStops &&
           mMatrix.ExactlyEquals(aOther.mMatrix);
  }

  bool operator==(const Pattern& aOther) const override {
    if (aOther.GetType() != PatternType::CONIC_GRADIENT) {
      return false;
    }
    return aOther.IsWeak()
               ? *this == static_cast<const Weak&>(aOther)
               : *this == static_cast<const ConicGradientPatternT<>&>(aOther);
  }

  Point mCenter;             //!< Center of the gradient
  Float mAngle;              //!< Start angle of gradient
  Float mStartOffset;        // Offset of first stop
  Float mEndOffset;          // Offset of last stop
  REF<GradientStops> mStops; /**< GradientStops object for this gradient, this
                                  should match the backend type of the draw
                                target this pattern will be used with. */
  Matrix mMatrix;  //!< A matrix that transforms the pattern into user space
};

typedef ConicGradientPatternT<> ConicGradientPattern;

/**
 * This class is used for Surface Patterns, they wrap a surface and a
 * repetition mode for the surface. This may be used on the stack.
 */
template <template <typename> typename REF = RefPtr>
class SurfacePatternT : public Pattern {
  typedef SurfacePatternT<ThreadSafeWeakPtr> Weak;

 public:
  /// For constructor parameter description, see member data documentation.
  SurfacePatternT(RefPtr<SourceSurface> aSourceSurface, ExtendMode aExtendMode,
                  const Matrix& aMatrix = Matrix(),
                  SamplingFilter aSamplingFilter = SamplingFilter::GOOD,
                  const IntRect& aSamplingRect = IntRect())
      : mSurface(std::move(aSourceSurface)),
        mExtendMode(aExtendMode),
        mSamplingFilter(aSamplingFilter),
        mMatrix(aMatrix),
        mSamplingRect(aSamplingRect) {}

  PatternType GetType() const override { return PatternType::SURFACE; }

  Pattern* CloneWeak() const override {
    return new Weak(do_AddRef(mSurface), mExtendMode, mMatrix, mSamplingFilter,
                    mSamplingRect);
  }

  bool IsWeak() const override {
    return std::is_same<decltype(*this), Weak>::value;
  }

  bool IsValid() const override { return IsRefValid(mSurface); }

  template <template <typename> typename T>
  bool operator==(const SurfacePatternT<T>& aOther) const {
    return mSurface == aOther.mSurface && mExtendMode == aOther.mExtendMode &&
           mSamplingFilter == aOther.mSamplingFilter &&
           mMatrix.ExactlyEquals(aOther.mMatrix) &&
           mSamplingRect.IsEqualEdges(aOther.mSamplingRect);
  }

  bool operator==(const Pattern& aOther) const override {
    if (aOther.GetType() != PatternType::SURFACE) {
      return false;
    }
    return aOther.IsWeak()
               ? *this == static_cast<const Weak&>(aOther)
               : *this == static_cast<const SurfacePatternT<>&>(aOther);
  }

  REF<SourceSurface> mSurface;  //!< Surface to use for drawing
  ExtendMode mExtendMode;       /**< This determines how the image is extended
                                     outside the bounds of the image */
  SamplingFilter
      mSamplingFilter;  //!< Resampling filter for resampling the image.
  Matrix mMatrix;       //!< Transforms the pattern into user space

  IntRect mSamplingRect; /**< Rect that must not be sampled outside of,
                              or an empty rect if none has been
                              specified. */
};

typedef SurfacePatternT<> SurfacePattern;

class StoredPattern;

static const int32_t kReasonableSurfaceSize = 8192;

/**
 * This is the base class for source surfaces. These objects are surfaces
 * which may be used as a source in a SurfacePattern or a DrawSurface call.
 * They cannot be drawn to directly.
 *
 * Although SourceSurface has thread-safe refcount, some SourceSurface cannot
 * be used on random threads at the same time. Only DataSourceSurface can be
 * used on random threads now. This will be fixed in the future. Eventually
 * all SourceSurface should be thread-safe.
 */
class SourceSurface : public SupportsThreadSafeWeakPtr<SourceSurface> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurface)
  virtual ~SourceSurface() = default;

  virtual SurfaceType GetType() const = 0;
  virtual IntSize GetSize() const = 0;
  /* GetRect is useful for when the underlying surface doesn't actually
   * have a backing store starting at 0, 0. e.g. SourceSurfaceOffset */
  virtual IntRect GetRect() const { return IntRect(IntPoint(0, 0), GetSize()); }
  virtual SurfaceFormat GetFormat() const = 0;

  /**
   * Structure containing memory size information for the surface.
   */
  struct SizeOfInfo {
    SizeOfInfo()
        : mHeapBytes(0),
          mNonHeapBytes(0),
          mUnknownBytes(0),
          mExternalHandles(0),
          mExternalId(0),
          mTypes(0) {}

    void Accumulate(const SizeOfInfo& aOther) {
      mHeapBytes += aOther.mHeapBytes;
      mNonHeapBytes += aOther.mNonHeapBytes;
      mUnknownBytes += aOther.mUnknownBytes;
      mExternalHandles += aOther.mExternalHandles;
      if (aOther.mExternalId) {
        mExternalId = aOther.mExternalId;
      }
      mTypes |= aOther.mTypes;
    }

    void AddType(SurfaceType aType) { mTypes |= 1 << uint32_t(aType); }

    size_t mHeapBytes;        // Bytes allocated on the heap.
    size_t mNonHeapBytes;     // Bytes allocated off the heap.
    size_t mUnknownBytes;     // Bytes allocated to either, but unknown.
    size_t mExternalHandles;  // Open handles for the surface.
    uint64_t mExternalId;     // External ID for WebRender, if available.
    uint32_t mTypes;          // Bit shifted values representing SurfaceType.
  };

  /**
   * Get the size information of the underlying data buffer.
   */
  virtual void SizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
                                   SizeOfInfo& aInfo) const {
    // Default is to estimate the footprint based on its size/format.
    auto size = GetSize();
    auto format = GetFormat();
    aInfo.AddType(GetType());
    aInfo.mUnknownBytes = size.width * size.height * BytesPerPixel(format);
  }

  /** This returns false if some event has made this source surface invalid for
   * usage with current DrawTargets. For example in the case of Direct2D this
   * could return false if we have switched devices since this surface was
   * created.
   */
  virtual bool IsValid() const { return true; }

  /**
   * This returns true if it is the same underlying surface data, even if
   * the objects are different (e.g. indirection due to
   * DataSourceSurfaceWrapper).
   */
  virtual bool Equals(SourceSurface* aOther, bool aSymmetric = true) {
    return this == aOther ||
           (aSymmetric && aOther && aOther->Equals(this, false));
  }

  /**
   * This function will return true if the surface type matches that of a
   * DataSourceSurface and if GetDataSurface will return the same object.
   */
  bool IsDataSourceSurface() const {
    switch (GetType()) {
      case SurfaceType::DATA:
      case SurfaceType::DATA_SHARED:
      case SurfaceType::DATA_RECYCLING_SHARED:
      case SurfaceType::DATA_ALIGNED:
      case SurfaceType::DATA_SHARED_WRAPPER:
      case SurfaceType::DATA_MAPPED:
      case SurfaceType::SKIA:
      case SurfaceType::WEBGL:
        return true;
      default:
        return false;
    }
  }

  /**
   * This function will get a DataSourceSurface for this surface, a
   * DataSourceSurface's data can be accessed directly.
   */
  virtual already_AddRefed<DataSourceSurface> GetDataSurface() = 0;

  /** This function will return a SourceSurface without any offset. */
  virtual already_AddRefed<SourceSurface> GetUnderlyingSurface() {
    RefPtr<SourceSurface> surface = this;
    return surface.forget();
  }

  /** Tries to get this SourceSurface's native surface.  This will fail if aType
   * is not the type of this SourceSurface's native surface.
   */
  virtual void* GetNativeSurface(NativeSurfaceType aType) { return nullptr; }

  void AddUserData(UserDataKey* key, void* userData, void (*destroy)(void*)) {
    mUserData.Add(key, userData, destroy);
  }
  void* GetUserData(UserDataKey* key) const { return mUserData.Get(key); }
  void RemoveUserData(UserDataKey* key) { mUserData.RemoveAndDestroy(key); }

  /** Tries to extract an optimal subrect for the surface. This may fail if the
   * request can't be satisfied.
   */
  virtual already_AddRefed<SourceSurface> ExtractSubrect(const IntRect& aRect) {
    return nullptr;
  }

 protected:
  friend class StoredPattern;

  ThreadSafeUserData mUserData;
};

class DataSourceSurface : public SourceSurface {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DataSourceSurface, override)
  DataSourceSurface() : mMapCount(0) {}

#ifdef DEBUG
  virtual ~DataSourceSurface() { MOZ_ASSERT(mMapCount == 0); }
#endif

  struct MappedSurface {
    uint8_t* mData = nullptr;
    int32_t mStride = 0;
  };

  enum MapType { READ, WRITE, READ_WRITE };

  /**
   * This is a scoped version of Map(). Map() is called in the constructor and
   * Unmap() in the destructor. Use this for automatic unmapping of your data
   * surfaces.
   *
   * Use IsMapped() to verify whether Map() succeeded or not.
   */
  class ScopedMap final {
   public:
    ScopedMap(DataSourceSurface* aSurface, MapType aType)
        : mSurface(aSurface), mIsMapped(aSurface->Map(aType, &mMap)) {}

    ScopedMap(ScopedMap&& aOther)
        : mSurface(std::move(aOther.mSurface)),
          mMap(aOther.mMap),
          mIsMapped(aOther.mIsMapped) {
      aOther.mMap.mData = nullptr;
      aOther.mIsMapped = false;
    }

    ScopedMap& operator=(ScopedMap&& aOther) {
      if (mIsMapped) {
        mSurface->Unmap();
      }
      mSurface = std::move(aOther.mSurface);
      mMap = aOther.mMap;
      mIsMapped = aOther.mIsMapped;
      aOther.mMap.mData = nullptr;
      aOther.mIsMapped = false;
      return *this;
    }

    ~ScopedMap() {
      if (mIsMapped) {
        mSurface->Unmap();
      }
    }

    uint8_t* GetData() const {
      MOZ_ASSERT(mIsMapped);
      return mMap.mData;
    }

    int32_t GetStride() const {
      MOZ_ASSERT(mIsMapped);
      return mMap.mStride;
    }

    const MappedSurface* GetMappedSurface() const {
      MOZ_ASSERT(mIsMapped);
      return &mMap;
    }

    const DataSourceSurface* GetSurface() const {
      MOZ_ASSERT(mIsMapped);
      return mSurface;
    }

    bool IsMapped() const { return mIsMapped; }

   private:
    ScopedMap(const ScopedMap& aOther) = delete;
    ScopedMap& operator=(const ScopedMap& aOther) = delete;

    RefPtr<DataSourceSurface> mSurface;
    MappedSurface mMap;
    bool mIsMapped;
  };

  SurfaceType GetType() const override { return SurfaceType::DATA; }
  /** @deprecated
   * Get the raw bitmap data of the surface.
   * Can return null if there was OOM allocating surface data.
   *
   * Deprecated means you shouldn't be using this!! Use Map instead.
   * Please deny any reviews which add calls to this!
   */
  virtual uint8_t* GetData() = 0;

  /** @deprecated
   * Stride of the surface, distance in bytes between the start of the image
   * data belonging to row y and row y+1. This may be negative.
   * Can return 0 if there was OOM allocating surface data.
   */
  virtual int32_t Stride() = 0;

  /**
   * The caller is responsible for ensuring aMappedSurface is not null.
  // Althought Map (and Moz2D in general) isn't normally threadsafe,
  // we want to allow it for SourceSurfaceRawData since it should
  // always be fine (for reading at least).
  //
  // This is the same as the base class implementation except using
  // mMapCount instead of mIsMapped since that breaks for multithread.
  //
  // Once mfbt supports Monitors we should implement proper read/write
  // locking to prevent write races.
   */
  virtual bool Map(MapType, MappedSurface* aMappedSurface) {
    aMappedSurface->mData = GetData();
    aMappedSurface->mStride = Stride();
    bool success = !!aMappedSurface->mData;
    if (success) {
      mMapCount++;
    }
    return success;
  }

  virtual void Unmap() {
    mMapCount--;
    MOZ_ASSERT(mMapCount >= 0);
  }

  /**
   * Returns a DataSourceSurface with the same data as this one, but
   * guaranteed to have surface->GetType() == SurfaceType::DATA.
   *
   * The returning surface might be null, because of OOM or gfx device reset.
   * The caller needs to do null-check before using it.
   */
  already_AddRefed<DataSourceSurface> GetDataSurface() override;

  /**
   * Returns whether or not the data was allocated on the heap. This should
   * be used to determine if the memory needs to be cleared to 0.
   */
  virtual bool OnHeap() const { return true; }

  /**
   * Yields a dirty rect of what has changed since it was last called.
   */
  virtual Maybe<IntRect> TakeDirtyRect() { return Nothing(); }

  /**
   * Indicate a region which has changed in the surface.
   */
  virtual void Invalidate(const IntRect& aDirtyRect) {}

 protected:
  Atomic<int32_t> mMapCount;
};

/** This is an abstract object that accepts path segments. */
class PathSink : public RefCounted<PathSink> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathSink)
  virtual ~PathSink() = default;

  /** Move the current point in the path, any figure currently being drawn will
   * be considered closed during fill operations, however when stroking the
   * closing line segment will not be drawn.
   */
  virtual void MoveTo(const Point& aPoint) = 0;
  /** Add a linesegment to the current figure */
  virtual void LineTo(const Point& aPoint) = 0;
  /** Add a cubic bezier curve to the current figure */
  virtual void BezierTo(const Point& aCP1, const Point& aCP2,
                        const Point& aCP3) = 0;
  /** Add a quadratic bezier curve to the current figure */
  virtual void QuadraticBezierTo(const Point& aCP1, const Point& aCP2) = 0;
  /** Close the current figure, this will essentially generate a line segment
   * from the current point to the starting point for the current figure
   */
  virtual void Close() = 0;
  /** Add an arc to the current figure */
  virtual void Arc(const Point& aOrigin, float aRadius, float aStartAngle,
                   float aEndAngle, bool aAntiClockwise = false) = 0;

  virtual Point CurrentPoint() const { return mCurrentPoint; }

  virtual Point BeginPoint() const { return mBeginPoint; }

  virtual void SetCurrentPoint(const Point& aPoint) { mCurrentPoint = aPoint; }

  virtual void SetBeginPoint(const Point& aPoint) { mBeginPoint = aPoint; }

 protected:
  /** Point the current subpath is at - or where the next subpath will start
   * if there is no active subpath.
   */
  Point mCurrentPoint;

  /** Position of the previous MoveTo operation. */
  Point mBeginPoint;
};

class PathBuilder;
class FlattenedPath;

/** The path class is used to create (sets of) figures of any shape that can be
 * filled or stroked to a DrawTarget
 */
class Path : public external::AtomicRefCounted<Path> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(Path)
  virtual ~Path();

  virtual BackendType GetBackendType() const = 0;

  /** This returns a PathBuilder object that contains a copy of the contents of
   * this path and is still writable.
   */
  inline already_AddRefed<PathBuilder> CopyToBuilder() const {
    return CopyToBuilder(GetFillRule());
  }
  inline already_AddRefed<PathBuilder> TransformedCopyToBuilder(
      const Matrix& aTransform) const {
    return TransformedCopyToBuilder(aTransform, GetFillRule());
  }
  /** This returns a PathBuilder object that contains a copy of the contents of
   * this path, converted to use the specified FillRule, and still writable.
   */
  virtual already_AddRefed<PathBuilder> CopyToBuilder(
      FillRule aFillRule) const = 0;
  virtual already_AddRefed<PathBuilder> TransformedCopyToBuilder(
      const Matrix& aTransform, FillRule aFillRule) const = 0;

  /** This function checks if a point lies within a path. It allows passing a
   * transform that will transform the path to the coordinate space in which
   * aPoint is given.
   */
  virtual bool ContainsPoint(const Point& aPoint,
                             const Matrix& aTransform) const = 0;

  /** This function checks if a point lies within the stroke of a path using the
   * specified strokeoptions. It allows passing a transform that will transform
   * the path to the coordinate space in which aPoint is given.
   */
  virtual bool StrokeContainsPoint(const StrokeOptions& aStrokeOptions,
                                   const Point& aPoint,
                                   const Matrix& aTransform) const = 0;

  /** This functions gets the bounds of this path. These bounds are not
   * guaranteed to be tight. A transform may be specified that gives the bounds
   * after application of the transform.
   */
  virtual Rect GetBounds(const Matrix& aTransform = Matrix()) const = 0;

  /** This function gets the bounds of the stroke of this path using the
   * specified strokeoptions. These bounds are not guaranteed to be tight.
   * A transform may be specified that gives the bounds after application of
   * the transform.
   */
  virtual Rect GetStrokedBounds(const StrokeOptions& aStrokeOptions,
                                const Matrix& aTransform = Matrix()) const = 0;

  /** Gets conservative bounds for the path, optionally stroked or transformed.
   * This function will prioritize speed of computation over tightness of the
   * computed bounds if the backend supports the distinction.
   */
  virtual Rect GetFastBounds(
      const Matrix& aTransform = Matrix(),
      const StrokeOptions* aStrokeOptions = nullptr) const;

  /** Take the contents of this path and stream it to another sink, this works
   * regardless of the backend that might be used for the destination sink.
   */
  virtual void StreamToSink(PathSink* aSink) const = 0;

  /** This gets the fillrule this path's builder was created with. This is not
   * mutable.
   */
  virtual FillRule GetFillRule() const = 0;

  virtual Float ComputeLength();

  virtual Maybe<Rect> AsRect() const { return Nothing(); }

  virtual Point ComputePointAtLength(Float aLength, Point* aTangent = nullptr);

 protected:
  Path();
  void EnsureFlattenedPath();

  RefPtr<FlattenedPath> mFlattenedPath;
};

/** The PathBuilder class allows path creation. Once finish is called on the
 * pathbuilder it may no longer be written to.
 */
class PathBuilder : public PathSink {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(PathBuilder, override)
  /** Finish writing to the path and return a Path object that can be used for
   * drawing. Future use of the builder results in a crash!
   */
  virtual already_AddRefed<Path> Finish() = 0;

  virtual BackendType GetBackendType() const = 0;
};

struct Glyph {
  uint32_t mIndex;
  Point mPosition;
};

static inline bool operator==(const Glyph& aOne, const Glyph& aOther) {
  return aOne.mIndex == aOther.mIndex && aOne.mPosition == aOther.mPosition;
}

/** This class functions as a glyph buffer that can be drawn to a DrawTarget.
 * @todo XXX - This should probably contain the guts of gfxTextRun in the future
 * as roc suggested. But for now it's a simple container for a glyph vector.
 */
struct GlyphBuffer {
  const Glyph*
      mGlyphs;  //!< A pointer to a buffer of glyphs. Managed by the caller.
  uint32_t mNumGlyphs;  //!< Number of glyphs mGlyphs points to.
};

#ifdef MOZ_ENABLE_FREETYPE
class SharedFTFace;

/** SharedFTFaceData abstracts data that may be used to back a SharedFTFace.
 * Its main function is to manage the lifetime of the data and ensure that it
 * lasts as long as the face.
 */
class SharedFTFaceData {
 public:
  /** Utility for creating a new face from this data. */
  virtual already_AddRefed<SharedFTFace> CloneFace(int aFaceIndex = 0) {
    return nullptr;
  }
  /** Binds the data's lifetime to the face. */
  virtual void BindData() = 0;
  /** Signals that the data is no longer needed by a face. */
  virtual void ReleaseData() = 0;
};

/** Wrapper class for ref-counted SharedFTFaceData that handles calling the
 * appropriate ref-counting methods
 */
template <class T>
class SharedFTFaceRefCountedData : public SharedFTFaceData {
 public:
  void BindData() { static_cast<T*>(this)->AddRef(); }
  void ReleaseData() { static_cast<T*>(this)->Release(); }
};

// Helper class used for clearing out user font data when FT font
// face is destroyed. Since multiple faces may use the same data, be
// careful to assure that the data is only cleared out when all uses
// expire. The font entry object contains a refptr to FTUserFontData and
// each FT face created from that font entry contains a refptr to that
// same FTUserFontData object.
// This is also attached to FT faces for installed fonts (recording the
// filename, rather than storing the font data) if variations are present.
class FTUserFontData final
    : public mozilla::gfx::SharedFTFaceRefCountedData<FTUserFontData> {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FTUserFontData)

  FTUserFontData(const uint8_t* aData, uint32_t aLength)
      : mFontData(aData), mLength(aLength) {}
  explicit FTUserFontData(const char* aFilename) : mFilename(aFilename) {}

  const uint8_t* FontData() const { return mFontData; }

  already_AddRefed<mozilla::gfx::SharedFTFace> CloneFace(
      int aFaceIndex = 0) override;

 private:
  ~FTUserFontData() {
    if (mFontData) {
      free((void*)mFontData);
    }
  }

  std::string mFilename;
  const uint8_t* mFontData = nullptr;
  uint32_t mLength = 0;
};

/** SharedFTFace is a shared wrapper around an FT_Face. It is ref-counted,
 * unlike FT_Face itself, so that it may be shared among many users with
 * RefPtr. Users should take care to lock SharedFTFace before accessing any
 * FT_Face fields that may change to ensure exclusive access to it. It also
 * allows backing data's lifetime to be bound to it via SharedFTFaceData so
 * that the data will not disappear before the face does.
 */
class SharedFTFace : public external::AtomicRefCounted<SharedFTFace> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SharedFTFace)

  explicit SharedFTFace(FT_Face aFace, SharedFTFaceData* aData);
  virtual ~SharedFTFace();

  FT_Face GetFace() const { return mFace; }
  SharedFTFaceData* GetData() const { return mData; }

  /** Locks the face for exclusive access by a given owner. Returns false if
   * the given owner is acquiring the lock for the first time, and true if
   * the owner was the prior owner of the lock. Thus the return value can be
   * used to do owner-specific initialization of the FT face such as setting
   * a size or transform that may have been invalidated by a previous owner.
   * If no owner is given, then the user should avoid modifying any state on
   * the face so as not to invalidate the prior owner's modification.
   */
  bool Lock(const void* aOwner = nullptr) MOZ_CAPABILITY_ACQUIRE(mLock) {
    mLock.Lock();
    return !aOwner || mLastLockOwner.exchange(aOwner) == aOwner;
  }
  void Unlock() MOZ_CAPABILITY_RELEASE(mLock) { mLock.Unlock(); }

  /** Should be called when a lock owner is destroyed so that we don't have
   * a dangling pointer to a destroyed owner.
   */
  void ForgetLockOwner(const void* aOwner) {
    if (aOwner) {
      mLastLockOwner.compareExchange(aOwner, nullptr);
    }
  }

 private:
  FT_Face mFace;
  SharedFTFaceData* mData;
  Mutex mLock;
  // Remember the last owner of the lock, even after unlocking, to allow users
  // to avoid reinitializing state on the FT face if the last owner hasn't
  // changed by the next time it is locked with the same owner.
  Atomic<const void*> mLastLockOwner;
};
#endif

class UnscaledFont : public SupportsThreadSafeWeakPtr<UnscaledFont> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFont)

  virtual ~UnscaledFont();

  virtual FontType GetType() const = 0;

  static uint32_t DeletionCounter() { return sDeletionCounter; }

  typedef void (*FontFileDataOutput)(const uint8_t* aData, uint32_t aLength,
                                     uint32_t aIndex, void* aBaton);
  typedef void (*FontInstanceDataOutput)(const uint8_t* aData, uint32_t aLength,
                                         void* aBaton);
  typedef void (*FontDescriptorOutput)(const uint8_t* aData, uint32_t aLength,
                                       uint32_t aIndex, void* aBaton);

  virtual bool GetFontFileData(FontFileDataOutput, void*) { return false; }

  virtual bool GetFontInstanceData(FontInstanceDataOutput, void*) {
    return false;
  }

  virtual bool GetFontDescriptor(FontDescriptorOutput, void*) { return false; }

  virtual already_AddRefed<ScaledFont> CreateScaledFont(
      Float aGlyphSize, const uint8_t* aInstanceData,
      uint32_t aInstanceDataLength, const FontVariation* aVariations,
      uint32_t aNumVariations) {
    return nullptr;
  }

  virtual already_AddRefed<ScaledFont> CreateScaledFontFromWRFont(
      Float aGlyphSize, const wr::FontInstanceOptions* aOptions,
      const wr::FontInstancePlatformOptions* aPlatformOptions,
      const FontVariation* aVariations, uint32_t aNumVariations) {
    return CreateScaledFont(aGlyphSize, nullptr, 0, aVariations,
                            aNumVariations);
  }

 protected:
  UnscaledFont() = default;

 private:
  static Atomic<uint32_t> sDeletionCounter;
};

/** This class is an abstraction of a backend/platform specific font object
 * at a particular size. It is passed into text drawing calls to describe
 * the font used for the drawing call.
 */
class ScaledFont : public SupportsThreadSafeWeakPtr<ScaledFont> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont)

  virtual ~ScaledFont();

  virtual FontType GetType() const = 0;
  virtual Float GetSize() const = 0;
  virtual AntialiasMode GetDefaultAAMode() { return AntialiasMode::DEFAULT; }

  static uint32_t DeletionCounter() { return sDeletionCounter; }

  /** This allows getting a path that describes the outline of a set of glyphs.
   * A target is passed in so that the guarantee is made the returned path
   * can be used with any DrawTarget that has the same backend as the one
   * passed in.
   */
  virtual already_AddRefed<Path> GetPathForGlyphs(
      const GlyphBuffer& aBuffer, const DrawTarget* aTarget) = 0;

  /** This copies the path describing the glyphs into a PathBuilder. We use this
   * API rather than a generic API to append paths because it allows easier
   * implementation in some backends, and more efficient implementation in
   * others.
   */
  virtual void CopyGlyphsToBuilder(const GlyphBuffer& aBuffer,
                                   PathBuilder* aBuilder,
                                   const Matrix* aTransformHint = nullptr) = 0;

  typedef void (*FontInstanceDataOutput)(const uint8_t* aData, uint32_t aLength,
                                         const FontVariation* aVariations,
                                         uint32_t aNumVariations, void* aBaton);

  virtual bool GetFontInstanceData(FontInstanceDataOutput, void*) {
    return false;
  }

  virtual bool GetWRFontInstanceOptions(
      Maybe<wr::FontInstanceOptions>* aOutOptions,
      Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
      std::vector<FontVariation>* aOutVariations) {
    return false;
  }

  virtual bool CanSerialize() { return false; }

  virtual bool HasVariationSettings() { return false; }

  virtual bool MayUseBitmaps() { return false; }

  virtual bool UseSubpixelPosition() const { return false; }

  void AddUserData(UserDataKey* key, void* userData, void (*destroy)(void*)) {
    mUserData.Add(key, userData, destroy);
  }
  void* GetUserData(UserDataKey* key) { return mUserData.Get(key); }

  void RemoveUserData(UserDataKey* key) { mUserData.RemoveAndDestroy(key); }

  const RefPtr<UnscaledFont>& GetUnscaledFont() const { return mUnscaledFont; }

  virtual cairo_scaled_font_t* GetCairoScaledFont() { return nullptr; }

  Float GetSyntheticObliqueAngle() const { return mSyntheticObliqueAngle; }
  void SetSyntheticObliqueAngle(Float aAngle) {
    mSyntheticObliqueAngle = aAngle;
  }

 protected:
  explicit ScaledFont(const RefPtr<UnscaledFont>& aUnscaledFont)
      : mUnscaledFont(aUnscaledFont), mSyntheticObliqueAngle(0.0f) {}

  ThreadSafeUserData mUserData;
  RefPtr<UnscaledFont> mUnscaledFont;
  Float mSyntheticObliqueAngle;

 private:
  static Atomic<uint32_t> sDeletionCounter;
};

/**
 * Derived classes hold a native font resource from which to create
 * ScaledFonts.
 */
class NativeFontResource
    : public external::AtomicRefCounted<NativeFontResource> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(NativeFontResource)

  /**
   * Creates a UnscaledFont using the font corresponding to the index.
   *
   * @param aIndex index for the font within the resource.
   * @param aInstanceData pointer to read-only buffer of any available instance
   *                      data.
   * @param aInstanceDataLength the size of the instance data.
   * @return an already_addrefed UnscaledFont, containing nullptr if failed.
   */
  virtual already_AddRefed<UnscaledFont> CreateUnscaledFont(
      uint32_t aIndex, const uint8_t* aInstanceData,
      uint32_t aInstanceDataLength) = 0;

  NativeFontResource(size_t aDataLength);
  virtual ~NativeFontResource();

  static void RegisterMemoryReporter();

 private:
  size_t mDataLength;
};

/** This is the main class used for all the drawing. It is created through the
 * factory and accepts drawing commands. The results of drawing to a target
 * may be used either through a Snapshot or by flushing the target and directly
 * accessing the backing store a DrawTarget was created with.
 */
class DrawTarget : public external::AtomicRefCounted<DrawTarget> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTarget)
  DrawTarget()
      : mTransformDirty(false),
        mPermitSubpixelAA(false),
        mFormat(SurfaceFormat::UNKNOWN) {}
  virtual ~DrawTarget() = default;

  virtual bool IsValid() const { return true; };
  virtual DrawTargetType GetType() const = 0;

  virtual BackendType GetBackendType() const = 0;

  virtual bool IsRecording() const { return false; }

  /**
   * Method to generate hyperlink in PDF output (with appropriate backend).
   */
  virtual void Link(const char* aDestination, const Rect& aRect) {}
  virtual void Destination(const char* aDestination, const Point& aPoint) {}

  /**
   * Returns a SourceSurface which is a snapshot of the current contents of the
   * DrawTarget. Multiple calls to Snapshot() without any drawing operations in
   * between will normally return the same SourceSurface object.
   */
  virtual already_AddRefed<SourceSurface> Snapshot() = 0;

  /**
   * Returns a SourceSurface which wraps the buffer backing the DrawTarget. The
   * contents of the buffer may change if there are drawing operations after
   * calling but only guarantees that it reflects the state at the time it was
   * called.
   */
  virtual already_AddRefed<SourceSurface> GetBackingSurface() {
    return Snapshot();
  }

  // Snapshots the contents and returns an alpha mask
  // based on the RGB values.
  virtual already_AddRefed<SourceSurface> IntoLuminanceSource(
      LuminanceType aLuminanceType, float aOpacity);
  virtual IntSize GetSize() const = 0;
  virtual IntRect GetRect() const { return IntRect(IntPoint(0, 0), GetSize()); }

  /**
   * If possible returns the bits to this DrawTarget for direct manipulation.
   * While the bits is locked any modifications to this DrawTarget is forbidden.
   * Release takes the original data pointer for safety.
   */
  virtual bool LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride,
                        SurfaceFormat* aFormat, IntPoint* aOrigin = nullptr) {
    return false;
  }
  virtual void ReleaseBits(uint8_t* aData) {}

  /** Ensure that the DrawTarget backend has flushed all drawing operations to
   * this draw target. This must be called before using the backing surface of
   * this draw target outside of GFX 2D code.
   */
  virtual void Flush() = 0;

  /**
   * Draw a surface to the draw target. Possibly doing partial drawing or
   * applying scaling. No sampling happens outside the source.
   *
   * @param aSurface Source surface to draw
   * @param aDest Destination rectangle that this drawing operation should draw
   *              to
   * @param aSource Source rectangle in aSurface coordinates, this area of
   *                aSurface
   *                will be stretched to the size of aDest.
   * @param aOptions General draw options that are applied to the operation
   * @param aSurfOptions DrawSurface options that are applied
   */
  virtual void DrawSurface(
      SourceSurface* aSurface, const Rect& aDest, const Rect& aSource,
      const DrawSurfaceOptions& aSurfOptions = DrawSurfaceOptions(),
      const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Draw a surface to the draw target, when the surface will be available
   * at a later time. This is only valid for recording DrawTargets.
   *
   * This is considered fallible, and replaying this without making the surface
   * available to the replay will just skip the draw.
   */
  virtual void DrawDependentSurface(uint64_t aId, const Rect& aDest) {
    MOZ_CRASH("GFX: DrawDependentSurface");
  }

  /**
   * Draw the output of a FilterNode to the DrawTarget.
   *
   * @param aNode FilterNode to draw
   * @param aSourceRect Source rectangle in FilterNode space to draw
   * @param aDestPoint Destination point on the DrawTarget to draw the
   *                   SourceRectangle of the filter output to
   */
  virtual void DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
                          const Point& aDestPoint,
                          const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Blend a surface to the draw target with a shadow. The shadow is drawn as a
   * gaussian blur using a specified sigma. The shadow is clipped to the size
   * of the input surface, so the input surface should contain a transparent
   * border the size of the approximate coverage of the blur (3 * aSigma).
   * NOTE: This function works in device space!
   *
   * @param aSurface Source surface to draw.
   * @param aDest Destination point that this drawing operation should draw to.
   * @param aShadow Description of shadow to be drawn.
   * @param aOperator Composition operator used
   */
  virtual void DrawSurfaceWithShadow(SourceSurface* aSurface,
                                     const Point& aDest,
                                     const ShadowOptions& aShadow,
                                     CompositionOp aOperator) = 0;

  /**
   * Draws a shadow for the specified path, which may be optionally stroked.
   *
   * @param aPath The path to use for the shadow geometry.
   * @param aPattern The pattern to use for filling the path.
   * @param aShadow Description of shadow to be drawn.
   * @param aOptions General drawing options to apply to drawing the path.
   * @param aStrokeOptions Stroking parameters that control stroking of path
   * geometry, if supplied.
   */
  virtual void DrawShadow(const Path* aPath, const Pattern& aPattern,
                          const ShadowOptions& aShadow,
                          const DrawOptions& aOptions = DrawOptions(),
                          const StrokeOptions* aStrokeOptions = nullptr);

  /**
   * Clear a rectangle on the draw target to transparent black. This will
   * respect the clipping region and transform.
   *
   * @param aRect Rectangle to clear
   */
  virtual void ClearRect(const Rect& aRect) = 0;

  /**
   * This is essentially a 'memcpy' between two surfaces. It moves a pixel
   * aligned area from the source surface unscaled directly onto the
   * drawtarget. This ignores both transform and clip.
   *
   * @param aSurface Surface to copy from
   * @param aSourceRect Source rectangle to be copied
   * @param aDest Destination point to copy the surface to
   */
  virtual void CopySurface(SourceSurface* aSurface, const IntRect& aSourceRect,
                           const IntPoint& aDestination) = 0;

  /** @see CopySurface
   * Same as CopySurface, except uses itself as the source.
   *
   * Some backends may be able to optimize this better
   * than just taking a snapshot and using CopySurface.
   */
  virtual void CopyRect(const IntRect& aSourceRect,
                        const IntPoint& aDestination) {
    RefPtr<SourceSurface> source = Snapshot();
    CopySurface(source, aSourceRect, aDestination);
  }

  /**
   * Fill a rectangle on the DrawTarget with a certain source pattern.
   *
   * @param aRect Rectangle that forms the mask of this filling operation
   * @param aPattern Pattern that forms the source of this filling operation
   * @param aOptions Options that are applied to this operation
   */
  virtual void FillRect(const Rect& aRect, const Pattern& aPattern,
                        const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Fill a rounded rectangle on the DrawTarget with a certain source pattern.
   *
   * @param aRect Rounded rectangle that forms the mask of this filling
   * operation
   * @param aPattern Pattern that forms the source of this filling operation
   * @param aOptions Options that are applied to this operation
   */
  virtual void FillRoundedRect(const RoundedRect& aRect,
                               const Pattern& aPattern,
                               const DrawOptions& aOptions = DrawOptions());

  /**
   * Stroke a rectangle on the DrawTarget with a certain source pattern.
   *
   * @param aRect Rectangle that forms the mask of this stroking operation
   * @param aPattern Pattern that forms the source of this stroking operation
   * @param aOptions Options that are applied to this operation
   */
  virtual void StrokeRect(const Rect& aRect, const Pattern& aPattern,
                          const StrokeOptions& aStrokeOptions = StrokeOptions(),
                          const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Stroke a line on the DrawTarget with a certain source pattern.
   *
   * @param aStart Starting point of the line
   * @param aEnd End point of the line
   * @param aPattern Pattern that forms the source of this stroking operation
   * @param aOptions Options that are applied to this operation
   */
  virtual void StrokeLine(const Point& aStart, const Point& aEnd,
                          const Pattern& aPattern,
                          const StrokeOptions& aStrokeOptions = StrokeOptions(),
                          const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Stroke a path on the draw target with a certain source pattern.
   *
   * @param aPath Path that is to be stroked
   * @param aPattern Pattern that should be used for the stroke
   * @param aStrokeOptions Stroke options used for this operation
   * @param aOptions Draw options used for this operation
   */
  virtual void Stroke(const Path* aPath, const Pattern& aPattern,
                      const StrokeOptions& aStrokeOptions = StrokeOptions(),
                      const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Fill a path on the draw target with a certain source pattern.
   *
   * @param aPath Path that is to be filled
   * @param aPattern Pattern that should be used for the fill
   * @param aOptions Draw options used for this operation
   */
  virtual void Fill(const Path* aPath, const Pattern& aPattern,
                    const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Fill a series of glyphs on the draw target with a certain source pattern.
   */
  virtual void FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
                          const Pattern& aPattern,
                          const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Stroke a series of glyphs on the draw target with a certain source pattern.
   */
  virtual void StrokeGlyphs(
      ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern,
      const StrokeOptions& aStrokeOptions = StrokeOptions(),
      const DrawOptions& aOptions = DrawOptions());

  /**
   * This takes a source pattern and a mask, and composites the source pattern
   * onto the destination surface using the alpha channel of the mask pattern
   * as a mask for the operation.
   *
   * @param aSource Source pattern
   * @param aMask Mask pattern
   * @param aOptions Drawing options
   */
  virtual void Mask(const Pattern& aSource, const Pattern& aMask,
                    const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * This takes a source pattern and a mask, and composites the source pattern
   * onto the destination surface using the alpha channel of the mask source.
   * The operation is bound by the extents of the mask.
   *
   * @param aSource Source pattern
   * @param aMask Mask surface
   * @param aOffset a transformed offset that the surface is masked at
   * @param aOptions Drawing options
   */
  virtual void MaskSurface(const Pattern& aSource, SourceSurface* aMask,
                           Point aOffset,
                           const DrawOptions& aOptions = DrawOptions()) = 0;

  /**
   * Draw aSurface using the 3D transform aMatrix. The DrawTarget's transform
   * and clip are applied after the 3D transform.
   *
   * If the transform fails (i.e. because aMatrix is singular), false is
   * returned and nothing is drawn.
   */
  virtual bool Draw3DTransformedSurface(SourceSurface* aSurface,
                                        const Matrix4x4& aMatrix);

  /**
   * Push a clip to the DrawTarget.
   *
   * @param aPath The path to clip to
   */
  virtual void PushClip(const Path* aPath) = 0;

  /**
   * Push an axis-aligned rectangular clip to the DrawTarget. This rectangle
   * is specified in user space.
   *
   * @param aRect The rect to clip to
   */
  virtual void PushClipRect(const Rect& aRect) = 0;

  /**
   * Push a clip region specifed by the union of axis-aligned rectangular
   * clips to the DrawTarget. These rectangles are specified in device space.
   * This must be balanced by a corresponding call to PopClip within a layer.
   *
   * @param aRects The rects to clip to
   * @param aCount The number of rectangles
   */
  virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount);

  /** Pop a clip from the DrawTarget. A pop without a corresponding push will
   * be ignored.
   */
  virtual void PopClip() = 0;

  /**
   * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
   * drawing will be redirected to, this is used for example to support group
   * opacity or the masking of groups. Clips must be balanced within a layer,
   * i.e. between a matching PushLayer/PopLayer pair there must be as many
   * PushClip(Rect) calls as there are PopClip calls.
   *
   * @param aOpaque Whether the layer will be opaque
   * @param aOpacity Opacity of the layer
   * @param aMask Mask applied to the layer
   * @param aMaskTransform Transform applied to the layer mask
   * @param aBounds Optional bounds in device space to which the layer is
   *                limited in size.
   * @param aCopyBackground Whether to copy the background into the layer, this
   *                        is only supported when aOpaque is true.
   */
  virtual void PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask,
                         const Matrix& aMaskTransform,
                         const IntRect& aBounds = IntRect(),
                         bool aCopyBackground = false) {
    MOZ_CRASH("GFX: PushLayer");
  }

  /**
   * Push a 'layer' to the DrawTarget, a layer is a temporary surface that all
   * drawing will be redirected to, this is used for example to support group
   * opacity or the masking of groups. Clips must be balanced within a layer,
   * i.e. between a matching PushLayer/PopLayer pair there must be as many
   * PushClip(Rect) calls as there are PopClip calls.
   *
   * @param aOpaque Whether the layer will be opaque
   * @param aOpacity Opacity of the layer
   * @param aMask Mask applied to the layer
   * @param aMaskTransform Transform applied to the layer mask
   * @param aBounds Optional bounds in device space to which the layer is
   *                limited in size.
   * @param aCopyBackground Whether to copy the background into the layer, this
   *                        is only supported when aOpaque is true.
   */
  virtual void PushLayerWithBlend(bool aOpaque, Float aOpacity,
                                  SourceSurface* aMask,
                                  const Matrix& aMaskTransform,
                                  const IntRect& aBounds = IntRect(),
                                  bool aCopyBackground = false,
                                  CompositionOp = CompositionOp::OP_OVER) {
    MOZ_CRASH("GFX: PushLayerWithBlend");
  }

  /**
   * This balances a call to PushLayer and proceeds to blend the layer back
   * onto the background. This blend will blend the temporary surface back
   * onto the target in device space using POINT sampling and operator over.
   */
  virtual void PopLayer() { MOZ_CRASH("GFX: PopLayer"); }

  /**
   * Perform an in-place blur operation. This is only supported on data draw
   * targets.
   */
  virtual void Blur(const AlphaBoxBlur& aBlur);

  /**
   * Performs an in-place edge padding operation.
   * aRegion is specified in device space.
   */
  virtual void PadEdges(const IntRegion& aRegion);

  /**
   * Performs an in-place buffer unrotation operation.
   */
  virtual bool Unrotate(IntPoint aRotation);

  /**
   * Create a SourceSurface optimized for use with this DrawTarget from
   * existing bitmap data in memory.
   *
   * The SourceSurface does not take ownership of aData, and may be freed at any
   * time.
   */
  virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromData(
      unsigned char* aData, const IntSize& aSize, int32_t aStride,
      SurfaceFormat aFormat) const = 0;

  /**
   * Create a SourceSurface optimized for use with this DrawTarget from an
   * arbitrary SourceSurface type supported by this backend. This may return
   * aSourceSurface or some other existing surface.
   */
  virtual already_AddRefed<SourceSurface> OptimizeSourceSurface(
      SourceSurface* aSurface) const = 0;
  virtual already_AddRefed<SourceSurface> OptimizeSourceSurfaceForUnknownAlpha(
      SourceSurface* aSurface) const {
    return OptimizeSourceSurface(aSurface);
  }

  /**
   * Create a SourceSurface for a type of NativeSurface. This may fail if the
   * draw target does not know how to deal with the type of NativeSurface passed
   * in. If this succeeds, the SourceSurface takes the ownersip of the
   * NativeSurface.
   */
  virtual already_AddRefed<SourceSurface> CreateSourceSurfaceFromNativeSurface(
      const NativeSurface& aSurface) const = 0;

  /**
   * Create a DrawTarget whose snapshot is optimized for use with this
   * DrawTarget.
   */
  virtual already_AddRefed<DrawTarget> CreateSimilarDrawTarget(
      const IntSize& aSize, SurfaceFormat aFormat) const = 0;

  /**
   * Create a DrawTarget whose backing surface is optimized for use with this
   * DrawTarget.
   */
  virtual already_AddRefed<DrawTarget> CreateSimilarDrawTargetWithBacking(
      const IntSize& aSize, SurfaceFormat aFormat) const {
    return CreateSimilarDrawTarget(aSize, aFormat);
  }

  /**
   * Create a DrawTarget whose snapshot is optimized for use with this
   * DrawTarget and aFilter.
   * @param aSource is the FilterNode that that will be attached to this
   * surface.
   * @param aSourceRect is the source rect that will be passed to DrawFilter
   * @param aDestPoint is the dest point that will be passed to DrawFilter.
   */
  virtual already_AddRefed<DrawTarget> CreateSimilarDrawTargetForFilter(
      const IntSize& aSize, SurfaceFormat aFormat, FilterNode* aFilter,
      FilterNode* aSource, const Rect& aSourceRect, const Point& aDestPoint) {
    return CreateSimilarDrawTarget(aSize, aFormat);
  }

  /**
   * Returns false if CreateSimilarDrawTarget would return null with the same
   * parameters. May return true even in cases where CreateSimilarDrawTarget
   * return null (i.e. this function returning false has meaning, but returning
   * true doesn't guarantee anything).
   */
  virtual bool CanCreateSimilarDrawTarget(const IntSize& aSize,
                                          SurfaceFormat aFormat) const {
    return true;
  }

  /**
   * Create a draw target optimized for drawing a shadow.
   *
   * Note that aSigma is the blur radius that must be used when we draw the
   * shadow. Also note that this doesn't affect the size of the allocated
   * surface, the caller is still responsible for including the shadow area in
   * its size.
   */
  virtual already_AddRefed<DrawTarget> CreateShadowDrawTarget(
      const IntSize& aSize, SurfaceFormat aFormat, float aSigma) const {
    return CreateSimilarDrawTarget(aSize, aFormat);
  }

  /**
   * Create a similar DrawTarget in the same space as this DrawTarget whose
   * device size may be clipped based on the active clips intersected with
   * aBounds (if it is not empty).
   * aRect is a rectangle in user space.
   */
  virtual RefPtr<DrawTarget> CreateClippedDrawTarget(const Rect& aBounds,
                                                     SurfaceFormat aFormat) = 0;

  /**
   * Create a similar draw target, but if the draw target is not backed by a
   * raster backend (for example, it is capturing or recording), force it to
   * create a raster target instead. This is intended for code that wants to
   * cache pixels, and would have no effect if it were caching a recording.
   */
  virtual RefPtr<DrawTarget> CreateSimilarRasterTarget(
      const IntSize& aSize, SurfaceFormat aFormat) const {
    return CreateSimilarDrawTarget(aSize, aFormat);
  }

  /**
   * Create a path builder with the specified fillmode.
   *
   * We need the fill mode up front because of Direct2D.
   * ID2D1SimplifiedGeometrySink requires the fill mode
   * to be set before calling BeginFigure().
   */
  virtual already_AddRefed<PathBuilder> CreatePathBuilder(
      FillRule aFillRule = FillRule::FILL_WINDING) const = 0;

  /**
   * Create a GradientStops object that holds information about a set of
   * gradient stops, this object is required for linear or radial gradient
   * patterns to represent the color stops in the gradient.
   *
   * @param aStops An array of gradient stops
   * @param aNumStops Number of stops in the array aStops
   * @param aExtendNone This describes how to extend the stop color outside of
   * the gradient area.
   */
  virtual already_AddRefed<GradientStops> CreateGradientStops(
      GradientStop* aStops, uint32_t aNumStops,
      ExtendMode aExtendMode = ExtendMode::CLAMP) const = 0;

  /**
   * Create a FilterNode object that can be used to apply a filter to various
   * inputs.
   *
   * @param aType Type of filter node to be created.
   */
  virtual already_AddRefed<FilterNode> CreateFilter(FilterType aType) = 0;

  Matrix GetTransform() const { return mTransform; }

  /**
   * Set a transform on the surface, this transform is applied at drawing time
   * to both the mask and source of the operation.
   *
   * Performance note: For some backends it is expensive to change the current
   * transform (because transforms affect a lot of the parts of the pipeline,
   * so new transform change can result in a pipeline flush).  To get around
   * this, DrawTarget implementations buffer transform changes and try to only
   * set the current transform on the backend when required.  That tracking has
   * its own performance impact though, and ideally callers would be smart
   * enough not to require it.  At a future date this method may stop this
   * doing transform buffering so, if you're a consumer, please try to be smart
   * about calling this method as little as possible.  For example, instead of
   * concatenating a translation onto the current transform then calling
   * FillRect, try to integrate the translation into FillRect's aRect
   * argument's x/y offset.
   */
  virtual void SetTransform(const Matrix& aTransform) {
    mTransform = aTransform;
    mTransformDirty = true;
  }

  inline void ConcatTransform(const Matrix& aTransform) {
    SetTransform(aTransform * Matrix(GetTransform()));
  }

  SurfaceFormat GetFormat() const { return mFormat; }

  /** Tries to get a native surface for a DrawTarget, this may fail if the
   * draw target cannot convert to this surface type.
   */
  virtual void* GetNativeSurface(NativeSurfaceType aType) { return nullptr; }

  virtual bool IsTiledDrawTarget() const { return false; }
  virtual bool SupportsRegionClipping() const { return true; }

  void AddUserData(UserDataKey* key, void* userData, void (*destroy)(void*)) {
    mUserData.Add(key, userData, destroy);
  }
  void* GetUserData(UserDataKey* key) const { return mUserData.Get(key); }
  void* RemoveUserData(UserDataKey* key) { return mUserData.Remove(key); }

  /** Within this rectangle all pixels will be opaque by the time the result of
   * this DrawTarget is first used for drawing. Either by the underlying surface
   * being used as an input to external drawing, or Snapshot() being called.
   * This rectangle is specified in device space.
   */
  void SetOpaqueRect(const IntRect& aRect) { mOpaqueRect = aRect; }

  const IntRect& GetOpaqueRect() const { return mOpaqueRect; }

  virtual bool IsCurrentGroupOpaque() {
    return GetFormat() == SurfaceFormat::B8G8R8X8;
  }

  virtual void SetPermitSubpixelAA(bool aPermitSubpixelAA) {
    mPermitSubpixelAA = aPermitSubpixelAA;
  }

  bool GetPermitSubpixelAA() { return mPermitSubpixelAA; }

  /**
   * Mark the end of an Item in a DrawTargetRecording. These markers
   * are used for merging recordings together.
   *
   * This should only be called on the 'root' DrawTargetRecording.
   * Calling it on a child DrawTargetRecordings will cause confusion.
   *
   * Note: this is a bit of a hack. It might be better to just recreate
   * the DrawTargetRecording.
   */
  virtual void FlushItem(const IntRect& aBounds) {}

  /**
   * Ensures that no snapshot is still pointing to this DrawTarget's surface
   * data.
   *
   * This can be useful if the DrawTarget is wrapped around data that it does
   * not own, and for some reason the owner of the data has to make it
   * temporarily unavailable without the DrawTarget knowing about it. This can
   * cause costly surface copies, so it should not be used without a a good
   * reason.
   */
  virtual void DetachAllSnapshots() = 0;

  /**
   * Remove all clips in the DrawTarget.
   */
  virtual bool RemoveAllClips() { return false; }

 protected:
  UserData mUserData;
  Matrix mTransform;
  IntRect mOpaqueRect;
  bool mTransformDirty : 1;
  bool mPermitSubpixelAA : 1;

  SurfaceFormat mFormat;
};

class DrawEventRecorder : public RefCounted<DrawEventRecorder> {
 public:
  MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
  // returns true if there were any items in the recording
  virtual bool Finish() = 0;
  virtual ~DrawEventRecorder() = default;
};

struct Tile {
  RefPtr<DrawTarget> mDrawTarget;
  IntPoint mTileOrigin;
};

struct TileSet {
  Tile* mTiles;
  size_t mTileCount;
};

struct Config {
  LogForwarder* mLogForwarder;
  int32_t mMaxTextureSize;
  int32_t mMaxAllocSize;

  Config()
      : mLogForwarder(nullptr),
        mMaxTextureSize(kReasonableSurfaceSize),
        mMaxAllocSize(52000000) {}
};

class GFX2D_API Factory {
  using char_type = filesystem::Path::value_type;

 public:
  static void Init(const Config& aConfig);
  static void ShutDown();

  static bool HasSSE2();
  static bool HasSSE4();

  /**
   * Returns false if any of the following are true:
   *
   *   - the width/height of |sz| are less than or equal to zero
   *   - the width/height of |sz| are greater than |limit|
   *   - the number of bytes that need to be allocated for the surface is too
   *     big to fit in an int32_t, or bigger than |allocLimit|, if specifed
   *
   * To calculate the number of bytes that need to be allocated for the surface
   * this function makes the conservative assumption that there need to be
   * 4 bytes-per-pixel, and the stride alignment is 16 bytes.
   *
   * The reason for using int32_t rather than uint32_t is again to be
   * conservative; some code has in the past and may in the future use signed
   * integers to store buffer lengths etc.
   */
  static bool CheckSurfaceSize(const IntSize& sz, int32_t limit = 0,
                               int32_t allocLimit = 0);

  /** Make sure the given dimension satisfies the CheckSurfaceSize and is
   * within 8k limit.  The 8k value is chosen a bit randomly.
   */
  static bool ReasonableSurfaceSize(const IntSize& aSize);

  static bool AllowedSurfaceSize(const IntSize& aSize);

  static already_AddRefed<DrawTarget> CreateDrawTargetForCairoSurface(
      cairo_surface_t* aSurface, const IntSize& aSize,
      SurfaceFormat* aFormat = nullptr);

  static already_AddRefed<SourceSurface> CreateSourceSurfaceForCairoSurface(
      cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat);

  static already_AddRefed<DrawTarget> CreateDrawTarget(BackendType aBackend,
                                                       const IntSize& aSize,
                                                       SurfaceFormat aFormat);

  static already_AddRefed<PathBuilder> CreatePathBuilder(
      BackendType aBackend, FillRule aFillRule = FillRule::FILL_WINDING);

  /**
   * Create a simple PathBuilder, which uses SKIA backend.
   */
  static already_AddRefed<PathBuilder> CreateSimplePathBuilder();

  static already_AddRefed<DrawTarget> CreateRecordingDrawTarget(
      DrawEventRecorder* aRecorder, DrawTarget* aDT, IntRect aRect);

  static already_AddRefed<DrawTarget> CreateDrawTargetForData(
      BackendType aBackend, unsigned char* aData, const IntSize& aSize,
      int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false);

#ifdef XP_DARWIN
  static already_AddRefed<ScaledFont> CreateScaledFontForMacFont(
      CGFontRef aCGFont, const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
      const DeviceColor& aFontSmoothingBackgroundColor,
      bool aUseFontSmoothing = true, bool aApplySyntheticBold = false,
      bool aHasColorGlyphs = false);
#endif

#ifdef MOZ_WIDGET_GTK
  static already_AddRefed<ScaledFont> CreateScaledFontForFontconfigFont(
      const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
      RefPtr<SharedFTFace> aFace, FcPattern* aPattern);
#endif

#ifdef MOZ_WIDGET_ANDROID
  static already_AddRefed<ScaledFont> CreateScaledFontForFreeTypeFont(
      const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
      RefPtr<SharedFTFace> aFace, bool aApplySyntheticBold = false);
#endif

  /**
   * This creates a NativeFontResource from TrueType data.
   *
   * @param aData Pointer to the data
   * @param aSize Size of the TrueType data
   * @param aFontType Type of NativeFontResource that should be created.
   * @param aFontContext Optional native font context to be used to create the
   *                              NativeFontResource.
   * @return a NativeFontResource of nullptr if failed.
   */
  static already_AddRefed<NativeFontResource> CreateNativeFontResource(
      uint8_t* aData, uint32_t aSize, FontType aFontType,
      void* aFontContext = nullptr);

  /**
   * This creates an unscaled font of the given type based on font descriptor
   * data retrieved from ScaledFont::GetFontDescriptor.
   */
  static already_AddRefed<UnscaledFont> CreateUnscaledFontFromFontDescriptor(
      FontType aType, const uint8_t* aData, uint32_t aDataLength,
      uint32_t aIndex);

  /**
   * This creates a simple data source surface for a certain size. It allocates
   * new memory for the surface. This memory is freed when the surface is
   * destroyed.  The caller is responsible for handing the case where nullptr
   * is returned. The surface is not zeroed unless requested.
   */
  static already_AddRefed<DataSourceSurface> CreateDataSourceSurface(
      const IntSize& aSize, SurfaceFormat aFormat, bool aZero = false);

  /**
   * This creates a simple data source surface for a certain size with a
   * specific stride, which must be large enough to fit all pixels.
   * It allocates new memory for the surface. This memory is freed when
   * the surface is destroyed.  The caller is responsible for handling the case
   * where nullptr is returned. The surface is not zeroed unless requested.
   */
  static already_AddRefed<DataSourceSurface> CreateDataSourceSurfaceWithStride(
      const IntSize& aSize, SurfaceFormat aFormat, int32_t aStride,
      bool aZero = false);

  typedef void (*SourceSurfaceDeallocator)(void* aClosure);

  /**
   * This creates a simple data source surface for some existing data. It will
   * wrap this data and the data for this source surface.
   *
   * We can provide a custom destroying function for |aData|. This will be
   * called in the surface dtor using |aDeallocator| and the |aClosure|. If
   * there are errors during construction(return a nullptr surface), the caller
   * is responsible for the deallocation.
   *
   * If there is no destroying function, the caller is responsible for
   * deallocating the aData memory only after destruction of this
   * DataSourceSurface.
   */
  static already_AddRefed<DataSourceSurface> CreateWrappingDataSourceSurface(
      uint8_t* aData, int32_t aStride, const IntSize& aSize,
      SurfaceFormat aFormat, SourceSurfaceDeallocator aDeallocator = nullptr,
      void* aClosure = nullptr);

  static void CopyDataSourceSurface(DataSourceSurface* aSource,
                                    DataSourceSurface* aDest);

  static uint32_t GetMaxSurfaceSize(BackendType aType);

  static LogForwarder* GetLogForwarder() {
    return sConfig ? sConfig->mLogForwarder : nullptr;
  }

 private:
  static Config* sConfig;

 public:
  static void PurgeAllCaches();

  static already_AddRefed<DrawTarget> CreateOffsetDrawTarget(
      DrawTarget* aDrawTarget, IntPoint aTileOrigin);

  static bool DoesBackendSupportDataDrawtarget(BackendType aType);

  static void SetBGRSubpixelOrder(bool aBGR);
  static bool GetBGRSubpixelOrder();

 private:
  static bool mBGRSubpixelOrder;

 public:
  static already_AddRefed<DrawTarget> CreateDrawTargetWithSkCanvas(
      SkCanvas* aCanvas);

#ifdef MOZ_ENABLE_FREETYPE
  static void SetFTLibrary(FT_Library aFTLibrary);
  static FT_Library GetFTLibrary();

  static FT_Library NewFTLibrary();
  static void ReleaseFTLibrary(FT_Library aFTLibrary);
  static void LockFTLibrary(FT_Library aFTLibrary);
  static void UnlockFTLibrary(FT_Library aFTLibrary);

  static FT_Face NewFTFace(FT_Library aFTLibrary, const char* aFileName,
                           int aFaceIndex);
  static already_AddRefed<SharedFTFace> NewSharedFTFace(FT_Library aFTLibrary,
                                                        const char* aFilename,
                                                        int aFaceIndex);
  static FT_Face NewFTFaceFromData(FT_Library aFTLibrary, const uint8_t* aData,
                                   size_t aDataSize, int aFaceIndex);
  static already_AddRefed<SharedFTFace> NewSharedFTFaceFromData(
      FT_Library aFTLibrary, const uint8_t* aData, size_t aDataSize,
      int aFaceIndex, SharedFTFaceData* aSharedData = nullptr);
  static void ReleaseFTFace(FT_Face aFace);
  static FT_Error LoadFTGlyph(FT_Face aFace, uint32_t aGlyphIndex,
                              int32_t aFlags);

 private:
  static FT_Library mFTLibrary;
  static StaticMutex mFTLock;

 public:
#endif

#ifdef WIN32
  static already_AddRefed<DrawTarget> CreateDrawTargetForD3D11Texture(
      ID3D11Texture2D* aTexture, SurfaceFormat aFormat);

  /*
   * Attempts to create and install a D2D1 device from the supplied Direct3D11
   * device. Returns true on success, or false on failure and leaves the
   * D2D1/Direct3D11 devices unset.
   */
  static bool SetDirect3D11Device(ID3D11Device* aDevice);
  static RefPtr<ID3D11Device> GetDirect3D11Device();
  static RefPtr<ID2D1Device> GetD2D1Device(uint32_t* aOutSeqNo = nullptr);
  static bool HasD2D1Device();
  static RefPtr<IDWriteFactory> GetDWriteFactory();
  static RefPtr<IDWriteFactory> EnsureDWriteFactory();
  static bool SupportsD2D1();
  static RefPtr<IDWriteFontCollection> GetDWriteSystemFonts(
      bool aUpdate = false);
  static RefPtr<ID2D1DeviceContext> GetD2DDeviceContext();

  static uint64_t GetD2DVRAMUsageDrawTarget();
  static uint64_t GetD2DVRAMUsageSourceSurface();
  static void D2DCleanup();

  static already_AddRefed<ScaledFont> CreateScaledFontForDWriteFont(
      IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle,
      const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize,
      bool aUseEmbeddedBitmap, bool aUseMultistrikeBold, bool aGDIForced);

  static already_AddRefed<ScaledFont> CreateScaledFontForGDIFont(
      const void* aLogFont, const RefPtr<UnscaledFont>& aUnscaledFont,
      Float aSize);

  static void SetSystemTextQuality(uint8_t aQuality);

  static already_AddRefed<DataSourceSurface>
  CreateBGRA8DataSourceSurfaceForD3D11Texture(ID3D11Texture2D* aSrcTexture,
                                              uint32_t aArrayIndex = 0);

  static bool ReadbackTexture(layers::TextureData* aDestCpuTexture,
                              ID3D11Texture2D* aSrcTexture);

  static bool ReadbackTexture(DataSourceSurface* aDestCpuTexture,
                              ID3D11Texture2D* aSrcTexture,
                              uint32_t aArrayIndex = 0);

 private:
  static StaticRefPtr<ID2D1Device> mD2D1Device;
  static StaticRefPtr<ID3D11Device> mD3D11Device;
  static StaticRefPtr<IDWriteFactory> mDWriteFactory;
  static bool mDWriteFactoryInitialized;
  static StaticRefPtr<IDWriteFontCollection> mDWriteSystemFonts;
  static StaticRefPtr<ID2D1DeviceContext> mMTDC;
  static StaticRefPtr<ID2D1DeviceContext> mOffMTDC;

  static bool ReadbackTexture(uint8_t* aDestData, int32_t aDestStride,
                              ID3D11Texture2D* aSrcTexture);

  // DestTextureT can be TextureData or DataSourceSurface.
  template <typename DestTextureT>
  static bool ConvertSourceAndRetryReadback(DestTextureT* aDestCpuTexture,
                                            ID3D11Texture2D* aSrcTexture,
                                            uint32_t aArrayIndex = 0);

 protected:
  // This guards access to the singleton devices above, as well as the
  // singleton devices in DrawTargetD2D1.
  static StaticMutex mDeviceLock;
  // This synchronizes access between different D2D drawtargets and their
  // implied dependency graph.
  static StaticMutex mDTDependencyLock;

  friend class DrawTargetD2D1;
#endif  // WIN32
};

class MOZ_RAII AutoSerializeWithMoz2D final {
 public:
  explicit AutoSerializeWithMoz2D(BackendType aBackendType);
  ~AutoSerializeWithMoz2D();

 private:
#if defined(WIN32)
  RefPtr<ID2D1Multithread> mMT;
#endif
};

}  // namespace gfx
}  // namespace mozilla

#endif  // _MOZILLA_GFX_2D_H