/* -*- 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 // GenericRefCountedBase allows us to hold on to refcounted objects of any type // (contrary to RefCounted 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 { 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 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 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 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 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 { 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 GetDataSurface() = 0; /** This function will return a SourceSurface without any offset. */ virtual already_AddRefed GetUnderlyingSurface() { RefPtr 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; int32_t mStride; }; 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 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 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 TakeDirtyRect() { return Nothing(); } /** * Indicate a region which has changed in the surface. */ virtual void Invalidate(const IntRect& aDirtyRect) {} protected: Atomic mMapCount; }; /** This is an abstract object that accepts path segments. */ class PathSink : public RefCounted { 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 { 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 CopyToBuilder() const { return CopyToBuilder(GetFillRule()); } inline already_AddRefed 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 CopyToBuilder( FillRule aFillRule) const = 0; virtual already_AddRefed 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 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 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 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 SharedFTFaceRefCountedData : public SharedFTFaceData { public: void BindData() { static_cast(this)->AddRef(); } void ReleaseData() { static_cast(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 { 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 mLastLockOwner; }; #endif class UnscaledFont : public SupportsThreadSafeWeakPtr { 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 CreateScaledFont( Float aGlyphSize, const uint8_t* aInstanceData, uint32_t aInstanceDataLength, const FontVariation* aVariations, uint32_t aNumVariations) { return nullptr; } virtual already_AddRefed 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 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 { 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 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* aOutOptions, Maybe* aOutPlatformOptions, std::vector* 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& 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& aUnscaledFont) : mUnscaledFont(aUnscaledFont), mSyntheticObliqueAngle(0.0f) {} UserData mUserData; RefPtr mUnscaledFont; Float mSyntheticObliqueAngle; private: static Atomic sDeletionCounter; }; /** * Derived classes hold a native font resource from which to create * ScaledFonts. */ class NativeFontResource : public external::AtomicRefCounted { 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 CreateUnscaledFont( uint32_t aIndex, const uint8_t* aInstanceData, uint32_t aInstanceDataLength) = 0; virtual ~NativeFontResource() = default; }; 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 { 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 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 GetBackingSurface() { return Snapshot(); } // Snapshots the contents and returns an alpha mask // based on the RGB values. virtual already_AddRefed 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; 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 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 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 OptimizeSourceSurface( SourceSurface* aSurface) const = 0; virtual already_AddRefed 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 CreateSourceSurfaceFromNativeSurface( const NativeSurface& aSurface) const = 0; /** * Create a DrawTarget whose snapshot is optimized for use with this * DrawTarget. */ virtual already_AddRefed CreateSimilarDrawTarget( const IntSize& aSize, SurfaceFormat aFormat) const = 0; /** * Create a DrawTarget whose backing surface is optimized for use with this * DrawTarget. */ virtual already_AddRefed 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 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 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 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 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 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 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 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 { 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 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 CreateDrawTargetForCairoSurface( cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat* aFormat = nullptr); static already_AddRefed CreateSourceSurfaceForCairoSurface( cairo_surface_t* aSurface, const IntSize& aSize, SurfaceFormat aFormat); static already_AddRefed 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 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 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 CreateCaptureDrawTarget( BackendType aBackend, const IntSize& aSize, SurfaceFormat aFormat); static already_AddRefed CreateCaptureDrawTargetForData( BackendType aBackend, const IntSize& aSize, SurfaceFormat aFormat, int32_t aStride, size_t aSurfaceAllocationSize); static already_AddRefed CreateWrapAndRecordDrawTarget( DrawEventRecorder* aRecorder, DrawTarget* aDT); static already_AddRefed CreateRecordingDrawTarget( DrawEventRecorder* aRecorder, DrawTarget* aDT, IntRect aRect); static already_AddRefed CreateDrawTargetForData( BackendType aBackend, unsigned char* aData, const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat, bool aUninitialized = false); #ifdef XP_DARWIN static already_AddRefed CreateScaledFontForMacFont( CGFontRef aCGFont, const RefPtr& aUnscaledFont, Float aSize, const DeviceColor& aFontSmoothingBackgroundColor, bool aUseFontSmoothing = true, bool aApplySyntheticBold = false); #endif #ifdef MOZ_WIDGET_GTK static already_AddRefed CreateScaledFontForFontconfigFont( const RefPtr& aUnscaledFont, Float aSize, RefPtr aFace, FcPattern* aPattern); #endif #ifdef MOZ_WIDGET_ANDROID static already_AddRefed CreateScaledFontForFreeTypeFont( const RefPtr& aUnscaledFont, Float aSize, RefPtr 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 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 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 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 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 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 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 CreateDualDrawTarget(DrawTarget* targetA, DrawTarget* targetB); static already_AddRefed 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 CreateTiledDrawTarget( const TileSet& aTileSet); static already_AddRefed 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 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 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 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 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 GetDirect3D11Device(); static RefPtr GetD2D1Device(uint32_t* aOutSeqNo = nullptr); static bool HasD2D1Device(); static RefPtr GetDWriteFactory(); static RefPtr EnsureDWriteFactory(); static bool SupportsD2D1(); static RefPtr GetDWriteSystemFonts( bool aUpdate = false); static RefPtr GetD2DDeviceContext(); static uint64_t GetD2DVRAMUsageDrawTarget(); static uint64_t GetD2DVRAMUsageSourceSurface(); static void D2DCleanup(); static already_AddRefed CreateScaledFontForDWriteFont( IDWriteFontFace* aFontFace, const gfxFontStyle* aStyle, const RefPtr& aUnscaledFont, Float aSize, bool aUseEmbeddedBitmap, int aRenderingMode, IDWriteRenderingParams* aParams, Float aGamma, Float aContrast, Float aClearTypeLevel); static already_AddRefed CreateScaledFontForGDIFont( const void* aLogFont, const RefPtr& aUnscaledFont, Float aSize); static void SetSystemTextQuality(uint8_t aQuality); static already_AddRefed CreateBGRA8DataSourceSurfaceForD3D11Texture(ID3D11Texture2D* aSrcTexture); static bool ReadbackTexture(layers::TextureData* aDestCpuTexture, ID3D11Texture2D* aSrcTexture); static bool ReadbackTexture(DataSourceSurface* aDestCpuTexture, ID3D11Texture2D* aSrcTexture); private: static StaticRefPtr mD2D1Device; static StaticRefPtr mD3D11Device; static StaticRefPtr mDWriteFactory; static bool mDWriteFactoryInitialized; static StaticRefPtr mDWriteSystemFonts; static StaticRefPtr mMTDC; static StaticRefPtr mOffMTDC; static bool ReadbackTexture(uint8_t* aDestData, int32_t aDestStride, ID3D11Texture2D* aSrcTexture); // DestTextureT can be TextureData or DataSourceSurface. template 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 mMT; #endif }; } // namespace gfx } // namespace mozilla #endif // _MOZILLA_GFX_2D_H