/* 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 CanvasRenderingContext2D_h #define CanvasRenderingContext2D_h #include #include "mozilla/dom/CanvasRenderingContext2DBinding.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/intl/Bidi.h" #include "mozilla/gfx/Rect.h" #include "mozilla/gfx/2D.h" #include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/Maybe.h" #include "mozilla/MruCache.h" #include "mozilla/RefPtr.h" #include "mozilla/SurfaceFromElementResult.h" #include "mozilla/ThreadLocal.h" #include "mozilla/UniquePtr.h" #include "FilterDescription.h" #include "gfx2DGlue.h" #include "gfxFontConstants.h" #include "gfxTextRun.h" #include "gfxUtils.h" #include "nsICanvasRenderingContextInternal.h" #include "nsColor.h" #include "nsRFPService.h" #include "nsIFrame.h" class gfxFontGroup; class nsGlobalWindowInner; class nsXULElement; namespace mozilla { class ErrorResult; class PresShell; namespace gl { class SourceSurface; } // namespace gl namespace layers { class PersistentBufferProvider; enum class LayersBackend : int8_t; } // namespace layers namespace dom { class HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrVideoFrame; using CanvasImageSource = HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrVideoFrame; class ImageBitmap; class ImageData; class UTF8StringOrCanvasGradientOrCanvasPattern; class OwningUTF8StringOrCanvasGradientOrCanvasPattern; class TextMetrics; class CanvasGradient; class CanvasPath; class CanvasPattern; extern const mozilla::gfx::Float SIGMA_MAX; template class Optional; struct CanvasBidiProcessor; class CanvasDrawObserver; class CanvasShutdownObserver; class DOMMatrix; class DOMMatrixReadOnly; struct DOMMatrix2DInit; /** ** CanvasRenderingContext2D **/ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal, public nsWrapperCache { protected: virtual ~CanvasRenderingContext2D(); public: explicit CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; HTMLCanvasElement* GetCanvas() const { if (!mCanvasElement || mCanvasElement->IsInNativeAnonymousSubtree()) { return nullptr; } // corresponds to changes to the old bindings made in bug 745025 return mCanvasElement->GetOriginalCanvas(); } void GetContextAttributes(CanvasRenderingContext2DSettings& aSettings) const; void OnMemoryPressure() override; void OnBeforePaintTransaction() override; void OnDidPaintTransaction() override; layers::PersistentBufferProvider* GetBufferProvider() override; Maybe GetFrontBuffer( WebGLFramebufferJS*, const bool webvr = false) override; already_AddRefed UseCompositableForwarder( layers::CompositableForwarder* aForwarder) override; void Save(); void Restore(); void Reset() { // reset the rendering context to its default state // Userland polyfill is `c2d.width = c2d.width;` SetDimensions(GetWidth(), GetHeight()); } void Scale(double aX, double aY, mozilla::ErrorResult& aError); void Rotate(double aAngle, mozilla::ErrorResult& aError); void Translate(double aX, double aY, mozilla::ErrorResult& aError); void Transform(double aM11, double aM12, double aM21, double aM22, double aDx, double aDy, mozilla::ErrorResult& aError); already_AddRefed GetTransform(mozilla::ErrorResult& aError); void SetTransform(double aM11, double aM12, double aM21, double aM22, double aDx, double aDy, mozilla::ErrorResult& aError); void SetTransform(const DOMMatrix2DInit& aInit, mozilla::ErrorResult& aError); void ResetTransform(mozilla::ErrorResult& aError); double GlobalAlpha() { return CurrentState().globalAlpha; } // Useful for silencing cast warnings static mozilla::gfx::Float ToFloat(double aValue) { return mozilla::gfx::Float(aValue); } void SetGlobalAlpha(double aGlobalAlpha) { if (aGlobalAlpha >= 0.0 && aGlobalAlpha <= 1.0) { CurrentState().globalAlpha = ToFloat(aGlobalAlpha); } } enum class ResolveCurrentColor : bool { No, Yes }; Maybe ParseColor(const nsACString&, ResolveCurrentColor = ResolveCurrentColor::Yes); void GetGlobalCompositeOperation(nsAString& aOp, mozilla::ErrorResult& aError); void SetGlobalCompositeOperation(const nsAString& aOp, mozilla::ErrorResult& aError); void GetStrokeStyle(OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue) { GetStyleAsUnion(aValue, Style::STROKE); } void SetStrokeStyle(const UTF8StringOrCanvasGradientOrCanvasPattern& aValue) { SetStyleFromUnion(aValue, Style::STROKE); } void GetFillStyle(OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue) { GetStyleAsUnion(aValue, Style::FILL); } void SetFillStyle(const UTF8StringOrCanvasGradientOrCanvasPattern& aValue) { SetStyleFromUnion(aValue, Style::FILL); } already_AddRefed CreateLinearGradient(double aX0, double aY0, double aX1, double aY1); already_AddRefed CreateRadialGradient(double aX0, double aY0, double aR0, double aX1, double aY1, double aR1, ErrorResult& aError); already_AddRefed CreateConicGradient(double aAngle, double aCx, double aCy); already_AddRefed CreatePattern( const CanvasImageSource& aElement, const nsAString& aRepeat, ErrorResult& aError); double ShadowOffsetX() { return CurrentState().shadowOffset.x; } void SetShadowOffsetX(double aShadowOffsetX) { CurrentState().shadowOffset.x = ToFloat(aShadowOffsetX); } double ShadowOffsetY() { return CurrentState().shadowOffset.y; } void SetShadowOffsetY(double aShadowOffsetY) { CurrentState().shadowOffset.y = ToFloat(aShadowOffsetY); } double ShadowBlur() { return CurrentState().shadowBlur; } void SetShadowBlur(double aShadowBlur) { if (aShadowBlur >= 0.0) { CurrentState().shadowBlur = ToFloat(aShadowBlur); } } void GetShadowColor(nsACString& aShadowColor) { StyleColorToString(CurrentState().shadowColor, aShadowColor); } void GetFilter(nsACString& aFilter) { aFilter = CurrentState().filterString; } void SetShadowColor(const nsACString& aShadowColor); void SetFilter(const nsACString& aFilter, mozilla::ErrorResult& aError); void ClearRect(double aX, double aY, double aW, double aH); void FillRect(double aX, double aY, double aW, double aH); void StrokeRect(double aX, double aY, double aW, double aH); void BeginPath(); void Fill(const CanvasWindingRule& aWinding); void Fill(const CanvasPath& aPath, const CanvasWindingRule& aWinding); void Stroke(); void Stroke(const CanvasPath& aPath); void DrawFocusIfNeeded(mozilla::dom::Element& aElement, ErrorResult& aRv); bool DrawCustomFocusRing(mozilla::dom::Element& aElement); void Clip(const CanvasWindingRule& aWinding); void Clip(const CanvasPath& aPath, const CanvasWindingRule& aWinding); bool IsPointInPath(JSContext* aCx, double aX, double aY, const CanvasWindingRule& aWinding, nsIPrincipal& aSubjectPrincipal); bool IsPointInPath(JSContext* aCx, const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding, nsIPrincipal&); bool IsPointInStroke(JSContext* aCx, double aX, double aY, nsIPrincipal& aSubjectPrincipal); bool IsPointInStroke(JSContext* aCx, const CanvasPath& aPath, double aX, double aY, nsIPrincipal&); void FillText(const nsAString& aText, double aX, double aY, const Optional& aMaxWidth, mozilla::ErrorResult& aError); void StrokeText(const nsAString& aText, double aX, double aY, const Optional& aMaxWidth, mozilla::ErrorResult& aError); UniquePtr MeasureText(const nsAString& aRawText, mozilla::ErrorResult& aError); void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy, mozilla::ErrorResult& aError) { DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, 0.0, 0.0, 0, aError); } void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy, double aDw, double aDh, mozilla::ErrorResult& aError) { DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, aDw, aDh, 2, aError); } void DrawImage(const CanvasImageSource& aImage, double aSx, double aSy, double aSw, double aSh, double aDx, double aDy, double aDw, double aDh, mozilla::ErrorResult& aError) { DrawImage(aImage, aSx, aSy, aSw, aSh, aDx, aDy, aDw, aDh, 6, aError); } already_AddRefed CreateImageData(JSContext*, int32_t aSw, int32_t aSh, ErrorResult&); already_AddRefed CreateImageData(JSContext*, ImageData&, ErrorResult&); already_AddRefed GetImageData(JSContext*, int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh, nsIPrincipal& aSubjectPrincipal, ErrorResult&); void PutImageData(ImageData&, int32_t aDx, int32_t aDy, ErrorResult&); void PutImageData(ImageData&, int32_t aDx, int32_t aDy, int32_t aDirtyX, int32_t aDirtyY, int32_t aDirtyWidth, int32_t aDirtyHeight, ErrorResult&); double LineWidth() { return CurrentState().lineWidth; } void SetLineWidth(double aWidth) { if (aWidth > 0.0) { CurrentState().lineWidth = ToFloat(aWidth); } } CanvasLineCap LineCap() { return CurrentState().lineCap; } void SetLineCap(const CanvasLineCap& aLinecapStyle) { CurrentState().lineCap = aLinecapStyle; } CanvasLineJoin LineJoin() { return CurrentState().lineJoin; } void SetLineJoin(const CanvasLineJoin& aLinejoinStyle) { CurrentState().lineJoin = aLinejoinStyle; } double MiterLimit() { return CurrentState().miterLimit; } void SetMiterLimit(double aMiter) { if (aMiter > 0.0) { CurrentState().miterLimit = ToFloat(aMiter); } } void GetFont(nsACString& aFont) { aFont = GetFont(); } void SetFont(const nsACString& aFont, mozilla::ErrorResult& aError); CanvasTextAlign TextAlign() { return CurrentState().textAlign; } void SetTextAlign(const CanvasTextAlign& aTextAlign) { CurrentState().textAlign = aTextAlign; } CanvasTextBaseline TextBaseline() { return CurrentState().textBaseline; } void SetTextBaseline(const CanvasTextBaseline& aTextBaseline) { CurrentState().textBaseline = aTextBaseline; } CanvasDirection Direction() { return CurrentState().textDirection; } void SetDirection(const CanvasDirection& aDirection) { CurrentState().textDirection = aDirection; } CanvasFontKerning FontKerning() { return CurrentState().fontKerning; } void SetFontKerning(const CanvasFontKerning& aFontKerning) { if (CurrentState().fontKerning != aFontKerning) { CurrentState().fontKerning = aFontKerning; CurrentState().fontGroup = nullptr; } } CanvasFontStretch FontStretch() { return CurrentState().fontStretch; } void SetFontStretch(const CanvasFontStretch& aFontStretch) { if (CurrentState().fontStretch != aFontStretch) { CurrentState().fontStretch = aFontStretch; CurrentState().fontGroup = nullptr; } } CanvasFontVariantCaps FontVariantCaps() { return CurrentState().fontVariantCaps; } void SetFontVariantCaps(const CanvasFontVariantCaps& aFontVariantCaps) { if (CurrentState().fontVariantCaps != aFontVariantCaps) { CurrentState().fontVariantCaps = aFontVariantCaps; CurrentState().fontGroup = nullptr; } } CanvasTextRendering TextRendering() { return CurrentState().textRendering; } void SetTextRendering(const CanvasTextRendering& aTextRendering) { CurrentState().textRendering = aTextRendering; } void GetLetterSpacing(nsACString& aLetterSpacing); void SetLetterSpacing(const nsACString& aLetterSpacing); void GetWordSpacing(nsACString& aWordSpacing); void SetWordSpacing(const nsACString& aWordSpacing); void EnsureCapped() { if (mPathPruned) { mPathBuilder->LineTo(mPathBuilder->CurrentPoint()); mPathPruned = false; } } void EnsureActivePath() { if (mPathPruned && !mPathBuilder->IsActive()) { mPathBuilder->MoveTo(mPathBuilder->CurrentPoint()); mPathPruned = false; } } void ClosePath() { if (!EnsureWritablePath()) { return; } mPathBuilder->Close(); mPathPruned = false; } void MoveTo(double aX, double aY) { if (!EnsureWritablePath()) { return; } mozilla::gfx::Point pos(ToFloat(aX), ToFloat(aY)); if (!pos.IsFinite()) { return; } EnsureCapped(); mPathBuilder->MoveTo(pos); } void LineTo(double aX, double aY) { if (!EnsureWritablePath()) { return; } LineTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))); } void QuadraticCurveTo(double aCpx, double aCpy, double aX, double aY) { if (!EnsureWritablePath()) { return; } mozilla::gfx::Point cp1(ToFloat(aCpx), ToFloat(aCpy)); mozilla::gfx::Point cp2(ToFloat(aX), ToFloat(aY)); if (!cp1.IsFinite() || !cp2.IsFinite()) { return; } if (cp1 == mPathBuilder->CurrentPoint() && cp1 == cp2) { mPathPruned = true; return; } EnsureActivePath(); mPathBuilder->QuadraticBezierTo(cp1, cp2); mPathPruned = false; } void BezierCurveTo(double aCp1x, double aCp1y, double aCp2x, double aCp2y, double aX, double aY) { if (!EnsureWritablePath()) { return; } BezierTo(mozilla::gfx::Point(ToFloat(aCp1x), ToFloat(aCp1y)), mozilla::gfx::Point(ToFloat(aCp2x), ToFloat(aCp2y)), mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))); } void ArcTo(double aX1, double aY1, double aX2, double aY2, double aRadius, mozilla::ErrorResult& aError); void Rect(double aX, double aY, double aW, double aH); void RoundRect( double aX, double aY, double aW, double aH, const UnrestrictedDoubleOrDOMPointInitOrUnrestrictedDoubleOrDOMPointInitSequence& aRadii, ErrorResult& aError); void Arc(double aX, double aY, double aRadius, double aStartAngle, double aEndAngle, bool aAnticlockwise, mozilla::ErrorResult& aError); void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY, double aRotation, double aStartAngle, double aEndAngle, bool aAnticlockwise, ErrorResult& aError); void GetFillRule(nsAString& aFillRule); void SetFillRule(const nsAString& aFillRule); void SetLineDash(const Sequence& aSegments, mozilla::ErrorResult& aRv); void GetLineDash(nsTArray& aSegments) const; void SetLineDashOffset(double aOffset); double LineDashOffset() const; bool ImageSmoothingEnabled() { return CurrentState().imageSmoothingEnabled; } void SetImageSmoothingEnabled(bool aImageSmoothingEnabled) { if (aImageSmoothingEnabled != CurrentState().imageSmoothingEnabled) { CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled; } } void DrawWindow(nsGlobalWindowInner& aWindow, double aX, double aY, double aW, double aH, const nsACString& aBgColor, uint32_t aFlags, nsIPrincipal& aSubjectPrincipal, mozilla::ErrorResult& aError); // Eventually this should be deprecated. Keeping for now to keep the binding // functional. void Demote(); nsresult Redraw(); gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); } int32_t GetWidth() override { return GetSize().width; } int32_t GetHeight() override { return GetSize().height; } // nsICanvasRenderingContextInternal /** * Gets the pres shell from either the canvas element or the doc shell */ PresShell* GetPresShell() final; nsresult Initialize() override; NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override; NS_IMETHOD InitializeWithDrawTarget( nsIDocShell* aShell, NotNull aTarget) override; NS_IMETHOD GetInputStream(const char* aMimeType, const nsAString& aEncoderOptions, nsIInputStream** aStream) override; already_AddRefed GetOptimizedSnapshot( mozilla::gfx::DrawTarget* aTarget, gfxAlphaType* aOutAlphaType) override; already_AddRefed GetSurfaceSnapshot( gfxAlphaType* aOutAlphaType = nullptr) override { return GetOptimizedSnapshot(nullptr, aOutAlphaType); } virtual void SetOpaqueValueFromOpaqueAttr(bool aOpaqueAttrValue) override; bool GetIsOpaque() override { return mOpaque; } void ResetBitmap(bool aFreeBuffer); void ResetBitmap() override { ResetBitmap(true); } bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) override; bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder, CanvasRenderer* aRenderer) override; void MarkContextClean() override; void MarkContextCleanForFrameCapture() override { mFrameCaptureState = FrameCaptureState::CLEAN; } Watchable* GetFrameCaptureState() override { return &mFrameCaptureState; } // this rect is in canvas device space void Redraw(const mozilla::gfx::Rect& aR); NS_IMETHOD Redraw(const gfxRect& aR) override { Redraw(ToRect(aR)); return NS_OK; } NS_IMETHOD SetContextOptions(JSContext* aCx, JS::Handle aOptions, ErrorResult& aRvForDictionaryInit) override; /** * An abstract base class to be implemented by callers wanting to be notified * that a refresh has occurred. Callers must ensure an observer is removed * before it is destroyed. */ virtual void DidRefresh() override; // this rect is in mTarget's current user space void RedrawUser(const gfxRect& aR); // nsISupports interface + CC NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SKIPPABLE_WRAPPERCACHE_CLASS( CanvasRenderingContext2D) enum class CanvasMultiGetterType : uint8_t { STRING = 0, PATTERN = 1, GRADIENT = 2 }; enum class Style : uint8_t { STROKE = 0, FILL, MAX }; void LineTo(const mozilla::gfx::Point& aPoint) { mFeatureUsage |= CanvasFeatureUsage::LineTo; if (!aPoint.IsFinite()) { return; } if (mPathBuilder->CurrentPoint() == aPoint) { mPathPruned = true; return; } EnsureActivePath(); mPathBuilder->LineTo(aPoint); mPathPruned = false; } void BezierTo(const mozilla::gfx::Point& aCP1, const mozilla::gfx::Point& aCP2, const mozilla::gfx::Point& aCP3) { if (!aCP1.IsFinite() || !aCP2.IsFinite() || !aCP3.IsFinite()) { return; } if (aCP1 == mPathBuilder->CurrentPoint() && aCP1 == aCP2 && aCP1 == aCP3) { mPathPruned = true; return; } EnsureActivePath(); mPathBuilder->BezierTo(aCP1, aCP2, aCP3); mPathPruned = false; } virtual UniquePtr GetImageBuffer( int32_t* out_format, gfx::IntSize* out_imageSize) override; void OnShutdown(); bool IsContextLost() const { return mIsContextLost; } void OnRemoteCanvasLost(); void OnRemoteCanvasRestored(); /** * Update CurrentState().filter with the filter description for * CurrentState().filterChain. * Flushes the PresShell if aFlushIsNeeded is true, so the world can change * if you call this function. */ MOZ_CAN_RUN_SCRIPT_BOUNDARY void UpdateFilter(bool aFlushIfNeeded); CanvasFeatureUsage FeatureUsage() const { return mFeatureUsage; } protected: /** * Helper to parse a value for the letterSpacing or wordSpacing attribute. * If successful, returns the result in aValue, and the whitespace-normalized * value string in aNormalized; if unsuccessful these are left untouched. */ void ParseSpacing(const nsACString& aSpacing, float* aValue, nsACString& aNormalized); already_AddRefed ResolveStyleForProperty( nsCSSPropertyID aProperty, const nsACString& aValue); nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY, uint32_t aWidth, uint32_t aHeight, nsIPrincipal& aSubjectPrincipal, JSObject** aRetval); void PutImageData_explicit(int32_t aX, int32_t aY, ImageData&, bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY, int32_t aDirtyWidth, int32_t aDirtyHeight, ErrorResult&); bool CopyBufferProvider(layers::PersistentBufferProvider& aOld, gfx::DrawTarget& aTarget, gfx::IntRect aCopyRect); /** * Internal method to complete initialisation, expects mTarget to have been * set */ nsresult Initialize(int32_t aWidth, int32_t aHeight); nsresult InitializeWithTarget(mozilla::gfx::DrawTarget* aSurface, int32_t aWidth, int32_t aHeight); /** * The number of living nsCanvasRenderingContexts. When this goes down to * 0, we free the premultiply and unpremultiply tables, if they exist. */ static MOZ_THREAD_LOCAL(uintptr_t) sNumLivingContexts; static MOZ_THREAD_LOCAL(mozilla::gfx::DrawTarget*) sErrorTarget; void SetTransformInternal(const mozilla::gfx::Matrix& aTransform); // Some helpers. Doesn't modify a color on failure. void SetStyleFromUnion( const UTF8StringOrCanvasGradientOrCanvasPattern& aValue, Style aWhichStyle); void SetStyleFromString(const nsACString& aStr, Style aWhichStyle); void SetStyleFromGradient(CanvasGradient& aGradient, Style aWhichStyle) { CurrentState().SetGradientStyle(aWhichStyle, &aGradient); } void SetStyleFromPattern(CanvasPattern& aPattern, Style aWhichStyle) { CurrentState().SetPatternStyle(aWhichStyle, &aPattern); } void GetStyleAsUnion(OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue, Style aWhichStyle); static void StyleColorToString(const nscolor& aColor, nsACString& aStr); // Returns whether a filter was successfully parsed. bool ParseFilter(const nsACString& aString, StyleOwnedSlice& aFilterChain, ErrorResult& aError); // Returns whether the font was successfully updated. bool SetFontInternal(const nsACString& aFont, mozilla::ErrorResult& aError); // Helper for SetFontInternal in the case where we have no PresShell. bool SetFontInternalDisconnected(const nsACString& aFont, mozilla::ErrorResult& aError); // Update the resolved values for letterSpacing and wordSpacing, if present, // following a potential change to font-relative dimensions. void UpdateSpacing(); // Clears the target and updates mOpaque based on mOpaqueAttrValue and // mContextAttributesHasAlpha. void UpdateIsOpaque(); // Shared implementation for Stroke() and Stroke(CanvasPath) methods. void StrokeImpl(const mozilla::gfx::Path& aPath); // Shared implementation for Fill() methods. void FillImpl(const mozilla::gfx::Path& aPath); /** * Creates the error target, if it doesn't exist */ static void EnsureErrorTarget(); /* This function ensures there is a writable pathbuilder available */ bool EnsureWritablePath(); // Ensures a path in UserSpace is available. void EnsureUserSpacePath( const CanvasWindingRule& aWinding = CanvasWindingRule::Nonzero); /** * Needs to be called before updating the transform. This makes a call to * EnsureTarget() so you don't have to. */ void TransformCurrentPath(const mozilla::gfx::Matrix& aTransform); // Report the fillRule has changed. void FillRuleChanged(); /** * Create the backing surfacing, if it doesn't exist. If there is an error * in creating the target then it will put sErrorTarget in place. If there * is in turn an error in creating the sErrorTarget then they would both * be null so IsTargetValid() would still return null. * * Returns true on success. */ bool EnsureTarget(ErrorResult& aError, const gfx::Rect* aCoveredRect = nullptr, bool aWillClear = false); bool EnsureTarget(const gfx::Rect* aCoveredRect = nullptr, bool aWillClear = false) { IgnoredErrorResult error; return EnsureTarget(error, aCoveredRect, aWillClear); } // Attempt to borrow a new target from an existing buffer provider. bool BorrowTarget(const gfx::IntRect& aPersistedRect, bool aNeedsClear); void RestoreClipsAndTransformToTarget(); bool TryAcceleratedTarget( RefPtr& aOutDT, RefPtr& aOutProvider); bool TrySharedTarget(RefPtr& aOutDT, RefPtr& aOutProvider); bool TryBasicTarget(RefPtr& aOutDT, RefPtr& aOutProvider, ErrorResult& aError); void RegisterAllocation(); void SetInitialState(); void SetErrorState(); /** * This method is run at the end of the event-loop spin where * ScheduleStableStateCallback was called. * * We use it to unlock resources that need to be locked while drawing. */ void OnStableState(); /** * Cf. OnStableState. */ void ScheduleStableStateCallback(); /** * Disposes an old target and prepares to lazily create a new target. * * Parameters are the new dimensions to be used, or if either is negative, * existing dimensions will be left unchanged. */ void ClearTarget(int32_t aWidth = -1, int32_t aHeight = -1); /* * Returns the target to the buffer provider. i.e. this will queue a frame for * rendering. */ void ReturnTarget(bool aForceReset = false); /** * Check if the target is valid after calling EnsureTarget. */ bool IsTargetValid() const { return !!mTarget && mTarget != sErrorTarget.get() && !mIsContextLost; } /** * Returns the surface format this canvas should be allocated using. Takes * into account mOpaque, platform requirements, etc. */ mozilla::gfx::SurfaceFormat GetSurfaceFormat() const; /** * Returns true if we know for sure that the pattern for a given style is * opaque. Usefull to know if we can discard the content below in certain * situations. Optionally checks if the pattern is a color pattern. */ bool PatternIsOpaque(Style aStyle, bool* aIsColor = nullptr) const; SurfaceFromElementResult CachedSurfaceFromElement(Element* aElement); void DrawImage(const CanvasImageSource& aImgElt, double aSx, double aSy, double aSw, double aSh, double aDx, double aDy, double aDw, double aDh, uint8_t aOptional_argc, mozilla::ErrorResult& aError); void DrawDirectlyToCanvas(const DirectDrawInfo& aImage, mozilla::gfx::Rect* aBounds, mozilla::gfx::Rect aDest, mozilla::gfx::Rect aSrc, gfx::IntSize aImgSize); nsCString& GetFont() { /* will initilize the value if not set, else does nothing */ GetCurrentFontStyle(); return CurrentState().font; } bool GetEffectiveWillReadFrequently() const; // Member vars int32_t mWidth, mHeight; // This is true when the canvas is valid, but of zero size, this requires // specific behavior on some operations. bool mZero; // The two ways to set the opaqueness of the canvas. // mOpaqueAttrValue: Whether the element has the moz-opaque attribute // set. Can change during the lifetime of the context. Non-standard, should // hopefully go away soon. // mContextAttributesHasAlpha: The standard way of setting canvas opaqueness. // Set at context initialization time and never changes. bool mOpaqueAttrValue; bool mContextAttributesHasAlpha; // Determines the context's opaqueness. Is computed from mOpaqueAttrValue and // mContextAttributesHasAlpha in UpdateIsOpaque(). bool mOpaque; // This is true when the next time our layer is retrieved we need to // recreate it (i.e. our backing surface changed) bool mResetLayer; // This is needed for drawing in drawAsyncXULElement bool mIPC; bool mHasPendingStableStateCallback; // If mCanvasElement is not provided, then a docshell is nsCOMPtr mDocShell; // This is created lazily so it is necessary to call EnsureTarget before // accessing it. In the event of an error it will be equal to // sErrorTarget. RefPtr mTarget; RefPtr mBufferProvider; bool mBufferNeedsClear = false; // Whether we should try to create an accelerated buffer provider. bool mAllowAcceleration = true; // Whether the application expects to use operations that perform poorly with // acceleration. bool mWillReadFrequently = false; // Whether or not we have already shutdown. bool mHasShutdown = false; // Whether or not remote canvas is currently unavailable. bool mIsContextLost = false; // Whether or not we can restore the context after restoration. bool mAllowContextRestore = true; bool AddShutdownObserver(); void RemoveShutdownObserver(); bool AlreadyShutDown() const { return mHasShutdown; } /** * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever * Redraw is called, reset to false when Render is called. */ bool mIsEntireFrameInvalid; /** * When this is set, the first call to Redraw(gfxRect) should set * mIsEntireFrameInvalid since we expect it will be followed by * many more Redraw calls. */ bool mPredictManyRedrawCalls; /** * Flag to avoid unnecessary surface copies to FrameCaptureListeners in the * case when the canvas is not currently being drawn into and not rendered * but canvas capturing is still ongoing. */ Watchable mFrameCaptureState; /** * We also have a device space pathbuilder. The reason for this is as * follows, when a path is being built, but the transform changes, we * can no longer keep a single path in userspace, considering there's * several 'user spaces' now. We therefore transform the current path * into device space, and add all operations to this path in device * space. * * When then finally executing a render, the Azure drawing API expects * the path to be in userspace. We could then set an identity transform * on the DrawTarget and do all drawing in device space. This is * undesirable because it requires transforming patterns, gradients, * clips, etc. into device space and it would not work for stroking. * What we do instead is convert the path back to user space when it is * drawn, and draw it with the current transform. This makes all drawing * occur correctly. * * There's never both a device space path builder and a user space path * builder present at the same time. There is also never a path and a * path builder present at the same time. When writing proceeds on an * existing path the Path is cleared and a new builder is created. * * mPath is always in user-space. */ RefPtr mPath; RefPtr mPathBuilder; bool mPathPruned = false; mozilla::gfx::Matrix mPathTransform; bool mPathTransformDirty = false; void FlushPathTransform(); /** * Number of times we've invalidated before calling redraw */ uint32_t mInvalidateCount; static const uint32_t kCanvasMaxInvalidateCount = 100; mozilla::intl::Bidi mBidiEngine; /** * Returns true if a shadow should be drawn along with a * drawing operation. */ bool NeedToDrawShadow() { const ContextState& state = CurrentState(); // The spec says we should not draw shadows if the operator is OVER. // If it's over and the alpha value is zero, nothing needs to be drawn. return NS_GET_A(state.shadowColor) != 0 && (state.shadowBlur != 0.f || state.shadowOffset.x != 0.f || state.shadowOffset.y != 0.f); } /** * Returns true if the result of a drawing operation should be * drawn with a filter. */ bool NeedToApplyFilter() { return EnsureUpdatedFilter().mPrimitives.Length() > 0; } /** * Calls UpdateFilter if the canvas's WriteOnly state has changed between the * last call to UpdateFilter and now. */ const gfx::FilterDescription& EnsureUpdatedFilter() { bool isWriteOnly = mCanvasElement && mCanvasElement->IsWriteOnly(); if (CurrentState().filterSourceGraphicTainted != isWriteOnly) { UpdateFilter(/* aFlushIfNeeded = */ true); EnsureTarget(); } MOZ_ASSERT(CurrentState().filterSourceGraphicTainted == isWriteOnly); return CurrentState().filter; } bool NeedToCalculateBounds() { return NeedToDrawShadow() || NeedToApplyFilter(); } // text public: gfxFontGroup* GetCurrentFontStyle(); protected: enum class TextDrawOperation : uint8_t { FILL, STROKE, MEASURE }; /** * Implementation of the fillText, strokeText, and measure functions with * the operation abstracted to a flag. * Returns a TextMetrics object _only_ if the operation is measure; * drawing operations (fill or stroke) always return nullptr. */ UniquePtr DrawOrMeasureText(const nsAString& aText, float aX, float aY, const Optional& aMaxWidth, TextDrawOperation aOp, ErrorResult& aError); // A clip or a transform, recorded and restored in order. struct ClipState { explicit ClipState(mozilla::gfx::Path* aClip) : clip(aClip) {} explicit ClipState(const mozilla::gfx::Matrix& aTransform) : transform(aTransform) {} bool IsClip() const { return !!clip; } RefPtr clip; mozilla::gfx::Matrix transform; }; // state stack handling class ContextState { public: ContextState(); ContextState(const ContextState& aOther); ~ContextState(); void SetColorStyle(Style aWhichStyle, nscolor aColor); void SetPatternStyle(Style aWhichStyle, CanvasPattern* aPat); void SetGradientStyle(Style aWhichStyle, CanvasGradient* aGrad); /** * returns true iff the given style is a solid color. */ bool StyleIsColor(Style aWhichStyle) const { return !(patternStyles[aWhichStyle] || gradientStyles[aWhichStyle]); } int32_t ShadowBlurRadius() const { static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5; return (int32_t)floor(ShadowBlurSigma() * GAUSSIAN_SCALE_FACTOR + 0.5); } mozilla::gfx::Float ShadowBlurSigma() const { return std::min(SIGMA_MAX, shadowBlur / 2.0f); } ElementOrArray clipsAndTransforms; RefPtr fontGroup; RefPtr fontLanguage; nsFont fontFont; EnumeratedArray, size_t(Style::MAX)> gradientStyles; EnumeratedArray, size_t(Style::MAX)> patternStyles; EnumeratedArray colorStyles; nsCString font; CanvasTextAlign textAlign = CanvasTextAlign::Start; CanvasTextBaseline textBaseline = CanvasTextBaseline::Alphabetic; CanvasDirection textDirection = CanvasDirection::Inherit; CanvasFontKerning fontKerning = CanvasFontKerning::Auto; CanvasFontStretch fontStretch = CanvasFontStretch::Normal; CanvasFontVariantCaps fontVariantCaps = CanvasFontVariantCaps::Normal; CanvasTextRendering textRendering = CanvasTextRendering::Auto; gfx::Float letterSpacing = 0.0f; gfx::Float wordSpacing = 0.0f; nsCString letterSpacingStr; nsCString wordSpacingStr; nscolor shadowColor = 0; mozilla::gfx::Matrix transform; mozilla::gfx::Point shadowOffset; mozilla::gfx::Float lineWidth = 1.0f; mozilla::gfx::Float miterLimit = 10.0f; mozilla::gfx::Float globalAlpha = 1.0f; mozilla::gfx::Float shadowBlur = 0.0f; nsTArray dash; mozilla::gfx::Float dashOffset = 0.0f; mozilla::gfx::CompositionOp op = mozilla::gfx::CompositionOp::OP_OVER; mozilla::gfx::FillRule fillRule = mozilla::gfx::FillRule::FILL_WINDING; CanvasLineCap lineCap = CanvasLineCap::Butt; CanvasLineJoin lineJoin = CanvasLineJoin::Miter; nsCString filterString{"none"}; StyleOwnedSlice filterChain; // RAII object that we obtain when we start to observer SVG filter elements // for rendering changes. When released we stop observing the SVG elements. nsCOMPtr autoSVGFiltersObserver; mozilla::gfx::FilterDescription filter; nsTArray> filterAdditionalImages; // This keeps track of whether the canvas was "tainted" or not when // we last used a filter. This is a security measure, whereby the // canvas is flipped to write-only if a cross-origin image is drawn to it. // This is to stop bad actors from reading back data they shouldn't have // access to. // // This also limits what filters we can apply to the context; in particular // feDisplacementMap is restricted. // // We keep track of this to ensure that if this gets out of sync with the // tainted state of the canvas itself, we update our filters accordingly. bool filterSourceGraphicTainted = false; bool imageSmoothingEnabled = true; bool fontExplicitLanguage = false; }; AutoTArray mStyleStack; inline ContextState& CurrentState() { return mStyleStack[mStyleStack.Length() - 1]; } inline const ContextState& CurrentState() const { return mStyleStack[mStyleStack.Length() - 1]; } struct FontStyleCacheKey { FontStyleCacheKey() = default; FontStyleCacheKey(const nsACString& aFont, uint64_t aGeneration) : mFont(aFont), mGeneration(aGeneration) {} nsCString mFont; uint64_t mGeneration = 0; }; struct FontStyleData { FontStyleCacheKey mKey; nsCString mUsedFont; RefPtr mStyle; }; class FontStyleCache : public MruCache { public: static HashNumber Hash(const FontStyleCacheKey& aKey) { HashNumber hash = HashString(aKey.mFont); return AddToHash(hash, aKey.mGeneration); } static bool Match(const FontStyleCacheKey& aKey, const FontStyleData& aVal) { return aVal.mKey.mGeneration == aKey.mGeneration && aVal.mKey.mFont == aKey.mFont; } }; FontStyleCache mFontStyleCache; struct ColorStyleCacheEntry { nsCString mKey; Maybe mColor; bool mWasCurrentColor = false; }; class ColorStyleCache : public MruCache { public: static HashNumber Hash(const nsACString& aKey) { return HashString(aKey); } static bool Match(const nsACString& aKey, const ColorStyleCacheEntry& aVal) { return aVal.mKey == aKey; } }; ColorStyleCache mColorStyleCache; ColorStyleCacheEntry ParseColorSlow(const nsACString&); mozilla::gfx::PaletteCache mPaletteCache; friend class CanvasGeneralPattern; friend class AdjustedTarget; friend class AdjustedTargetForShadow; friend class AdjustedTargetForFilter; // other helpers void GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel); friend struct CanvasBidiProcessor; friend class CanvasDrawObserver; friend class ImageBitmap; void SetWriteOnly(); bool IsWriteOnly() const { return mWriteOnly; } bool mWriteOnly; bool mClipsNeedConverting = false; uint8_t mFillTextCalls = 0; // Flags used by the fingerprinting detection heuristic CanvasFeatureUsage mFeatureUsage = CanvasFeatureUsage::None; virtual void AddZoneWaitingForGC(); virtual void AddAssociatedMemory(); virtual void RemoveAssociatedMemory(); }; size_t BindingJSObjectMallocBytes(CanvasRenderingContext2D* aContext); } // namespace dom } // namespace mozilla #endif /* CanvasRenderingContext2D_h */