diff options
Diffstat (limited to 'gfx/2d/2D.h')
-rw-r--r-- | gfx/2d/2D.h | 2059 |
1 files changed, 2059 insertions, 0 deletions
diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h new file mode 100644 index 0000000000..b12ff5b842 --- /dev/null +++ b/gfx/2d/2D.h @@ -0,0 +1,2059 @@ +/* -*- 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. */ +}; + +/** + * 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. +}; + +/** + * 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. */ +}; + +/** + * 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 external::AtomicRefCounted<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; + + protected: + Pattern() = default; +}; + +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; } + + 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. + */ +class LinearGradientPattern : public Pattern { + public: + /// For constructor parameter description, see member data documentation. + LinearGradientPattern(const Point& aBegin, const Point& aEnd, + GradientStops* aStops, const Matrix& aMatrix = Matrix()) + : mBegin(aBegin), mEnd(aEnd), mStops(aStops), mMatrix(aMatrix) {} + + PatternType GetType() const override { return PatternType::LINEAR_GRADIENT; } + + 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. */ + RefPtr<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 */ +}; + +/** + * 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. + */ +class RadialGradientPattern : public Pattern { + public: + /// For constructor parameter description, see member data documentation. + RadialGradientPattern(const Point& aCenter1, const Point& aCenter2, + Float aRadius1, Float aRadius2, GradientStops* aStops, + const Matrix& aMatrix = Matrix()) + : mCenter1(aCenter1), + mCenter2(aCenter2), + mRadius1(aRadius1), + mRadius2(aRadius2), + mStops(aStops), + mMatrix(aMatrix) {} + + PatternType GetType() const override { return PatternType::RADIAL_GRADIENT; } + + 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. + RefPtr<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 +}; + +/** + * 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. + */ +class ConicGradientPattern : public Pattern { + public: + /// For constructor parameter description, see member data documentation. + ConicGradientPattern(const Point& aCenter, Float aAngle, Float aStartOffset, + Float aEndOffset, GradientStops* aStops, + const Matrix& aMatrix = Matrix()) + : mCenter(aCenter), + mAngle(aAngle), + mStartOffset(aStartOffset), + mEndOffset(aEndOffset), + mStops(aStops), + mMatrix(aMatrix) {} + + PatternType GetType() const override { return PatternType::CONIC_GRADIENT; } + + Point mCenter; //!< Center of the gradient + Float mAngle; //!< Start angle of gradient + Float mStartOffset; // Offset of first stop + Float mEndOffset; // Offset of last stop + RefPtr<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 +}; + +/** + * 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. + */ +class SurfacePattern : public Pattern { + public: + /// For constructor parameter description, see member data documentation. + SurfacePattern(SourceSurface* aSourceSurface, ExtendMode aExtendMode, + const Matrix& aMatrix = Matrix(), + SamplingFilter aSamplingFilter = SamplingFilter::GOOD, + const IntRect& aSamplingRect = IntRect()) + : mSurface(aSourceSurface), + mExtendMode(aExtendMode), + mSamplingFilter(aSamplingFilter), + mMatrix(aMatrix), + mSamplingRect(aSamplingRect) {} + + PatternType GetType() const override { return PatternType::SURFACE; } + + RefPtr<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. */ +}; + +class StoredPattern; +class DrawTargetCaptureImpl; + +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 external::AtomicRefCounted<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: + 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); } + + protected: + friend class DrawTargetCaptureImpl; + friend class StoredPattern; + + // This is for internal use, it ensures the SourceSurface's data remains + // valid during the lifetime of the SourceSurface. + // @todo XXX - We need something better here :(. But we may be able to get rid + // of CreateWrappingDataSourceSurface in the future. + virtual void GuaranteePersistance() {} + + UserData 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; + + /** 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 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(); } +}; + +/** 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 = nullptr); + 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(void* aOwner = nullptr) { + mLock.Lock(); + return !aOwner || mLastLockOwner.exchange(aOwner) == aOwner; + } + void Unlock() { 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(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<void*> mLastLockOwner; +}; +#endif + +class UnscaledFont : public SupportsThreadSafeWeakPtr<UnscaledFont> { + public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(UnscaledFont) + MOZ_DECLARE_THREADSAFEWEAKREFERENCE_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) + MOZ_DECLARE_THREADSAFEWEAKREFERENCE_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; } + + 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) {} + + UserData 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; +}; + +class DrawTargetCapture; + +/** 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; } + virtual bool IsCaptureDT() const { return false; } + + /** + * 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; + + /** + * Realize a DrawTargetCapture onto the draw target. + * + * @param aSource Capture DrawTarget to draw + * @param aTransform Transform to apply when replaying commands + */ + virtual void DrawCapturedDT(DrawTargetCapture* aCaptureDT, + const Matrix& aTransform); + + /** + * 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, + const DrawSurfaceOptions& aSurfOptions = DrawSurfaceOptions(), + const DrawOptions& aOptions = DrawOptions()) { + 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 aColor Color of the drawn shadow + * @param aOffset Offset of the shadow + * @param aSigma Sigma used for the guassian filter kernel + * @param aOperator Composition operator used + */ + virtual void DrawSurfaceWithShadow(SourceSurface* aSurface, + const Point& aDest, + const DeviceColor& aColor, + const Point& aOffset, Float aSigma, + CompositionOp aOperator) = 0; + + /** + * 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). + */ + 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 IsDualDrawTarget() const { return false; } + 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; + + protected: + UserData mUserData; + Matrix mTransform; + IntRect mOpaqueRect; + bool mTransformDirty : 1; + bool mPermitSubpixelAA : 1; + + SurfaceFormat mFormat; +}; + +class DrawTargetCapture : public DrawTarget { + public: + bool IsCaptureDT() const override { return true; } + + virtual bool IsEmpty() const = 0; + virtual void Dump() = 0; +}; + +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 that the given buffer size doesn't exceed the allocation limit. + */ + static bool CheckBufferSize(int32_t bufSize); + + /** 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); + + /** + * Create a simple PathBuilder, which uses SKIA backend. If USE_SKIA is not + * defined, this returns nullptr; + */ + static already_AddRefed<PathBuilder> CreateSimplePathBuilder(); + + /** + * Create a DrawTarget that captures the drawing commands to eventually be + * replayed onto the DrawTarget provided. An optional byte size can be + * provided as a limit for the CaptureCommandList. When the limit is reached, + * the CaptureCommandList will be replayed to the target and then cleared. + * + * @param aSize Size of the area this DT will capture. + * @param aFlushBytes The byte limit at which to flush the CaptureCommandList + */ + static already_AddRefed<DrawTargetCapture> CreateCaptureDrawTargetForTarget( + gfx::DrawTarget* aTarget, size_t aFlushBytes = 0); + + /** + * Create a DrawTarget that captures the drawing commands and can be replayed + * onto a compatible DrawTarget afterwards. + * + * @param aSize Size of the area this DT will capture. + */ + static already_AddRefed<DrawTargetCapture> CreateCaptureDrawTarget( + BackendType aBackend, const IntSize& aSize, SurfaceFormat aFormat); + + static already_AddRefed<DrawTargetCapture> CreateCaptureDrawTargetForData( + BackendType aBackend, const IntSize& aSize, SurfaceFormat aFormat, + int32_t aStride, size_t aSurfaceAllocationSize); + + 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); +#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 already_AddRefed<DrawEventRecorder> CreateEventRecorderForFile( + const char_type* aFilename); + + static void SetGlobalEventRecorder(DrawEventRecorder* aRecorder); + + 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> CreateDualDrawTarget(DrawTarget* targetA, + DrawTarget* targetB); + + static already_AddRefed<SourceSurface> CreateDualSourceSurface( + SourceSurface* sourceA, SourceSurface* sourceB); + + /* + * This creates a new tiled DrawTarget. When a tiled drawtarget is used the + * drawing is distributed over number of tiles which may each hold an + * individual offset. The tiles in the set must each have the same backend + * and format. + */ + static already_AddRefed<DrawTarget> CreateTiledDrawTarget( + const TileSet& aTileSet); + 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: +#ifdef USE_SKIA + static already_AddRefed<DrawTarget> CreateDrawTargetWithSkCanvas( + SkCanvas* aCanvas); +#endif + +#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, int aRenderingMode, + IDWriteRenderingParams* aParams, Float aGamma, Float aContrast, + Float aClearTypeLevel); + + 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); + + static bool ReadbackTexture(layers::TextureData* aDestCpuTexture, + ID3D11Texture2D* aSrcTexture); + + static bool ReadbackTexture(DataSourceSurface* aDestCpuTexture, + ID3D11Texture2D* aSrcTexture); + + 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); + + 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 + + private: + static DrawEventRecorder* mRecorder; +}; + +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 |