michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef CanvasRenderingContext2D_h michael@0: #define CanvasRenderingContext2D_h michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: #include michael@0: #include "nsIDOMCanvasRenderingContext2D.h" michael@0: #include "nsICanvasRenderingContextInternal.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "nsColor.h" michael@0: #include "mozilla/dom/HTMLCanvasElement.h" michael@0: #include "mozilla/dom/HTMLVideoElement.h" michael@0: #include "CanvasUtils.h" michael@0: #include "gfxFont.h" michael@0: #include "mozilla/ErrorResult.h" michael@0: #include "mozilla/dom/CanvasGradient.h" michael@0: #include "mozilla/dom/CanvasRenderingContext2DBinding.h" michael@0: #include "mozilla/dom/CanvasPattern.h" michael@0: #include "mozilla/gfx/Rect.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "gfx2DGlue.h" michael@0: #include "imgIEncoder.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "mozilla/EnumeratedArray.h" michael@0: michael@0: class nsGlobalWindow; michael@0: class nsXULElement; michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: class SourceSurface; michael@0: class SurfaceStream; michael@0: } michael@0: michael@0: namespace dom { michael@0: class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement; michael@0: class ImageData; michael@0: class StringOrCanvasGradientOrCanvasPattern; michael@0: class OwningStringOrCanvasGradientOrCanvasPattern; michael@0: class TextMetrics; michael@0: michael@0: extern const mozilla::gfx::Float SIGMA_MAX; michael@0: michael@0: template class Optional; michael@0: michael@0: class CanvasPath MOZ_FINAL : michael@0: public nsWrapperCache michael@0: { michael@0: public: michael@0: NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CanvasPath) michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CanvasPath) michael@0: michael@0: nsCOMPtr GetParentObject() { return mParent; } michael@0: michael@0: JSObject* WrapObject(JSContext* aCx); michael@0: michael@0: static already_AddRefed Constructor(const GlobalObject& aGlobal, michael@0: ErrorResult& rv); michael@0: static already_AddRefed Constructor(const GlobalObject& aGlobal, michael@0: CanvasPath& aCanvasPath, michael@0: ErrorResult& rv); michael@0: static already_AddRefed Constructor(const GlobalObject& aGlobal, michael@0: const nsAString& aPathString, michael@0: ErrorResult& rv); michael@0: michael@0: void ClosePath(); michael@0: void MoveTo(double x, double y); michael@0: void LineTo(double x, double y); michael@0: void QuadraticCurveTo(double cpx, double cpy, double x, double y); michael@0: void BezierCurveTo(double cp1x, double cp1y, michael@0: double cp2x, double cp2y, michael@0: double x, double y); michael@0: void ArcTo(double x1, double y1, double x2, double y2, double radius, michael@0: ErrorResult& error); michael@0: void Rect(double x, double y, double w, double h); michael@0: void Arc(double x, double y, double radius, michael@0: double startAngle, double endAngle, bool anticlockwise, michael@0: ErrorResult& error); michael@0: michael@0: void LineTo(const gfx::Point& aPoint); michael@0: void BezierTo(const gfx::Point& aCP1, michael@0: const gfx::Point& aCP2, michael@0: const gfx::Point& aCP3); michael@0: michael@0: mozilla::RefPtr GetPath(const CanvasWindingRule& winding, michael@0: const mozilla::RefPtr& mTarget) const; michael@0: michael@0: explicit CanvasPath(nsISupports* aParent); michael@0: CanvasPath(nsISupports* aParent, RefPtr mPathBuilder); michael@0: virtual ~CanvasPath() {} michael@0: michael@0: private: michael@0: michael@0: nsCOMPtr mParent; michael@0: static gfx::Float ToFloat(double aValue) { return gfx::Float(aValue); } michael@0: michael@0: mutable RefPtr mPath; michael@0: mutable RefPtr mPathBuilder; michael@0: michael@0: void EnsurePathBuilder() const; michael@0: }; michael@0: michael@0: struct CanvasBidiProcessor; michael@0: class CanvasRenderingContext2DUserData; michael@0: michael@0: /** michael@0: ** CanvasRenderingContext2D michael@0: **/ michael@0: class CanvasRenderingContext2D : michael@0: public nsICanvasRenderingContextInternal, michael@0: public nsWrapperCache michael@0: { michael@0: typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement michael@0: HTMLImageOrCanvasOrVideoElement; michael@0: michael@0: public: michael@0: CanvasRenderingContext2D(); michael@0: virtual ~CanvasRenderingContext2D(); michael@0: michael@0: virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE; michael@0: michael@0: HTMLCanvasElement* GetCanvas() const michael@0: { michael@0: // corresponds to changes to the old bindings made in bug 745025 michael@0: return mCanvasElement->GetOriginalCanvas(); michael@0: } michael@0: michael@0: void Save(); michael@0: void Restore(); michael@0: void Scale(double x, double y, mozilla::ErrorResult& error); michael@0: void Rotate(double angle, mozilla::ErrorResult& error); michael@0: void Translate(double x, double y, mozilla::ErrorResult& error); michael@0: void Transform(double m11, double m12, double m21, double m22, double dx, michael@0: double dy, mozilla::ErrorResult& error); michael@0: void SetTransform(double m11, double m12, double m21, double m22, double dx, michael@0: double dy, mozilla::ErrorResult& error); michael@0: michael@0: double GlobalAlpha() michael@0: { michael@0: return CurrentState().globalAlpha; michael@0: } michael@0: michael@0: // Useful for silencing cast warnings michael@0: static mozilla::gfx::Float ToFloat(double aValue) { return mozilla::gfx::Float(aValue); } michael@0: michael@0: void SetGlobalAlpha(double globalAlpha) michael@0: { michael@0: if (globalAlpha >= 0.0 && globalAlpha <= 1.0) { michael@0: CurrentState().globalAlpha = ToFloat(globalAlpha); michael@0: } michael@0: } michael@0: michael@0: void GetGlobalCompositeOperation(nsAString& op, mozilla::ErrorResult& error); michael@0: void SetGlobalCompositeOperation(const nsAString& op, michael@0: mozilla::ErrorResult& error); michael@0: michael@0: void GetStrokeStyle(OwningStringOrCanvasGradientOrCanvasPattern& value) michael@0: { michael@0: GetStyleAsUnion(value, Style::STROKE); michael@0: } michael@0: michael@0: void SetStrokeStyle(const StringOrCanvasGradientOrCanvasPattern& value) michael@0: { michael@0: SetStyleFromUnion(value, Style::STROKE); michael@0: } michael@0: michael@0: void GetFillStyle(OwningStringOrCanvasGradientOrCanvasPattern& value) michael@0: { michael@0: GetStyleAsUnion(value, Style::FILL); michael@0: } michael@0: michael@0: void SetFillStyle(const StringOrCanvasGradientOrCanvasPattern& value) michael@0: { michael@0: SetStyleFromUnion(value, Style::FILL); michael@0: } michael@0: michael@0: already_AddRefed michael@0: CreateLinearGradient(double x0, double y0, double x1, double y1); michael@0: already_AddRefed michael@0: CreateRadialGradient(double x0, double y0, double r0, double x1, double y1, michael@0: double r1, ErrorResult& aError); michael@0: already_AddRefed michael@0: CreatePattern(const HTMLImageOrCanvasOrVideoElement& element, michael@0: const nsAString& repeat, ErrorResult& error); michael@0: michael@0: double ShadowOffsetX() michael@0: { michael@0: return CurrentState().shadowOffset.x; michael@0: } michael@0: michael@0: void SetShadowOffsetX(double shadowOffsetX) michael@0: { michael@0: CurrentState().shadowOffset.x = ToFloat(shadowOffsetX); michael@0: } michael@0: michael@0: double ShadowOffsetY() michael@0: { michael@0: return CurrentState().shadowOffset.y; michael@0: } michael@0: michael@0: void SetShadowOffsetY(double shadowOffsetY) michael@0: { michael@0: CurrentState().shadowOffset.y = ToFloat(shadowOffsetY); michael@0: } michael@0: michael@0: double ShadowBlur() michael@0: { michael@0: return CurrentState().shadowBlur; michael@0: } michael@0: michael@0: void SetShadowBlur(double shadowBlur) michael@0: { michael@0: if (shadowBlur >= 0.0) { michael@0: CurrentState().shadowBlur = ToFloat(shadowBlur); michael@0: } michael@0: } michael@0: michael@0: void GetShadowColor(nsAString& shadowColor) michael@0: { michael@0: StyleColorToString(CurrentState().shadowColor, shadowColor); michael@0: } michael@0: michael@0: void SetShadowColor(const nsAString& shadowColor); michael@0: void ClearRect(double x, double y, double w, double h); michael@0: void FillRect(double x, double y, double w, double h); michael@0: void StrokeRect(double x, double y, double w, double h); michael@0: void BeginPath(); michael@0: void Fill(const CanvasWindingRule& winding); michael@0: void Fill(const CanvasPath& path, const CanvasWindingRule& winding); michael@0: void Stroke(); michael@0: void Stroke(const CanvasPath& path); michael@0: void DrawFocusIfNeeded(mozilla::dom::Element& element); michael@0: bool DrawCustomFocusRing(mozilla::dom::Element& element); michael@0: void Clip(const CanvasWindingRule& winding); michael@0: void Clip(const CanvasPath& path, const CanvasWindingRule& winding); michael@0: bool IsPointInPath(JSContext* cx, double x, double y, michael@0: const CanvasWindingRule& winding); michael@0: bool IsPointInPath(JSContext* cx, const CanvasPath& path, double x, double y, michael@0: const CanvasWindingRule& winding); michael@0: bool IsPointInStroke(JSContext* cx, double x, double y); michael@0: bool IsPointInStroke(JSContext* cx, const CanvasPath& path, michael@0: double x, double y); michael@0: void FillText(const nsAString& text, double x, double y, michael@0: const Optional& maxWidth, michael@0: mozilla::ErrorResult& error); michael@0: void StrokeText(const nsAString& text, double x, double y, michael@0: const Optional& maxWidth, michael@0: mozilla::ErrorResult& error); michael@0: TextMetrics* michael@0: MeasureText(const nsAString& rawText, mozilla::ErrorResult& error); michael@0: michael@0: void AddHitRegion(const HitRegionOptions& options, mozilla::ErrorResult& error); michael@0: void RemoveHitRegion(const nsAString& id); michael@0: michael@0: void DrawImage(const HTMLImageOrCanvasOrVideoElement& image, michael@0: double dx, double dy, mozilla::ErrorResult& error) michael@0: { michael@0: DrawImage(image, 0.0, 0.0, 0.0, 0.0, dx, dy, 0.0, 0.0, 0, error); michael@0: } michael@0: michael@0: void DrawImage(const HTMLImageOrCanvasOrVideoElement& image, michael@0: double dx, double dy, double dw, double dh, michael@0: mozilla::ErrorResult& error) michael@0: { michael@0: DrawImage(image, 0.0, 0.0, 0.0, 0.0, dx, dy, dw, dh, 2, error); michael@0: } michael@0: michael@0: void DrawImage(const HTMLImageOrCanvasOrVideoElement& image, michael@0: double sx, double sy, double sw, double sh, double dx, michael@0: double dy, double dw, double dh, mozilla::ErrorResult& error) michael@0: { michael@0: DrawImage(image, sx, sy, sw, sh, dx, dy, dw, dh, 6, error); michael@0: } michael@0: michael@0: already_AddRefed michael@0: CreateImageData(JSContext* cx, double sw, double sh, michael@0: mozilla::ErrorResult& error); michael@0: already_AddRefed michael@0: CreateImageData(JSContext* cx, ImageData& imagedata, michael@0: mozilla::ErrorResult& error); michael@0: already_AddRefed michael@0: GetImageData(JSContext* cx, double sx, double sy, double sw, double sh, michael@0: mozilla::ErrorResult& error); michael@0: void PutImageData(ImageData& imageData, michael@0: double dx, double dy, mozilla::ErrorResult& error); michael@0: void PutImageData(ImageData& imageData, michael@0: double dx, double dy, double dirtyX, double dirtyY, michael@0: double dirtyWidth, double dirtyHeight, michael@0: mozilla::ErrorResult& error); michael@0: michael@0: double LineWidth() michael@0: { michael@0: return CurrentState().lineWidth; michael@0: } michael@0: michael@0: void SetLineWidth(double width) michael@0: { michael@0: if (width > 0.0) { michael@0: CurrentState().lineWidth = ToFloat(width); michael@0: } michael@0: } michael@0: void GetLineCap(nsAString& linecap); michael@0: void SetLineCap(const nsAString& linecap); michael@0: void GetLineJoin(nsAString& linejoin, mozilla::ErrorResult& error); michael@0: void SetLineJoin(const nsAString& linejoin); michael@0: michael@0: double MiterLimit() michael@0: { michael@0: return CurrentState().miterLimit; michael@0: } michael@0: michael@0: void SetMiterLimit(double miter) michael@0: { michael@0: if (miter > 0.0) { michael@0: CurrentState().miterLimit = ToFloat(miter); michael@0: } michael@0: } michael@0: michael@0: void GetFont(nsAString& font) michael@0: { michael@0: font = GetFont(); michael@0: } michael@0: michael@0: void SetFont(const nsAString& font, mozilla::ErrorResult& error); michael@0: void GetTextAlign(nsAString& textAlign); michael@0: void SetTextAlign(const nsAString& textAlign); michael@0: void GetTextBaseline(nsAString& textBaseline); michael@0: void SetTextBaseline(const nsAString& textBaseline); michael@0: michael@0: void ClosePath() michael@0: { michael@0: EnsureWritablePath(); michael@0: michael@0: if (mPathBuilder) { michael@0: mPathBuilder->Close(); michael@0: } else { michael@0: mDSPathBuilder->Close(); michael@0: } michael@0: } michael@0: michael@0: void MoveTo(double x, double y) michael@0: { michael@0: EnsureWritablePath(); michael@0: michael@0: if (mPathBuilder) { michael@0: mPathBuilder->MoveTo(mozilla::gfx::Point(ToFloat(x), ToFloat(y))); michael@0: } else { michael@0: mDSPathBuilder->MoveTo(mTarget->GetTransform() * michael@0: mozilla::gfx::Point(ToFloat(x), ToFloat(y))); michael@0: } michael@0: } michael@0: michael@0: void LineTo(double x, double y) michael@0: { michael@0: EnsureWritablePath(); michael@0: michael@0: LineTo(mozilla::gfx::Point(ToFloat(x), ToFloat(y))); michael@0: } michael@0: michael@0: void QuadraticCurveTo(double cpx, double cpy, double x, double y) michael@0: { michael@0: EnsureWritablePath(); michael@0: michael@0: if (mPathBuilder) { michael@0: mPathBuilder->QuadraticBezierTo(mozilla::gfx::Point(ToFloat(cpx), ToFloat(cpy)), michael@0: mozilla::gfx::Point(ToFloat(x), ToFloat(y))); michael@0: } else { michael@0: mozilla::gfx::Matrix transform = mTarget->GetTransform(); michael@0: mDSPathBuilder->QuadraticBezierTo(transform * michael@0: mozilla::gfx::Point(ToFloat(cpx), ToFloat(cpy)), michael@0: transform * michael@0: mozilla::gfx::Point(ToFloat(x), ToFloat(y))); michael@0: } michael@0: } michael@0: michael@0: void BezierCurveTo(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y) michael@0: { michael@0: EnsureWritablePath(); michael@0: michael@0: BezierTo(mozilla::gfx::Point(ToFloat(cp1x), ToFloat(cp1y)), michael@0: mozilla::gfx::Point(ToFloat(cp2x), ToFloat(cp2y)), michael@0: mozilla::gfx::Point(ToFloat(x), ToFloat(y))); michael@0: } michael@0: michael@0: void ArcTo(double x1, double y1, double x2, double y2, double radius, michael@0: mozilla::ErrorResult& error); michael@0: void Rect(double x, double y, double w, double h); michael@0: void Arc(double x, double y, double radius, double startAngle, michael@0: double endAngle, bool anticlockwise, mozilla::ErrorResult& error); michael@0: michael@0: void GetMozCurrentTransform(JSContext* cx, michael@0: JS::MutableHandle result, michael@0: mozilla::ErrorResult& error) const; michael@0: void SetMozCurrentTransform(JSContext* cx, michael@0: JS::Handle currentTransform, michael@0: mozilla::ErrorResult& error); michael@0: void GetMozCurrentTransformInverse(JSContext* cx, michael@0: JS::MutableHandle result, michael@0: mozilla::ErrorResult& error) const; michael@0: void SetMozCurrentTransformInverse(JSContext* cx, michael@0: JS::Handle currentTransform, michael@0: mozilla::ErrorResult& error); michael@0: void GetFillRule(nsAString& fillRule); michael@0: void SetFillRule(const nsAString& fillRule); michael@0: void GetMozDash(JSContext* cx, JS::MutableHandle retval, michael@0: mozilla::ErrorResult& error); michael@0: void SetMozDash(JSContext* cx, const JS::Value& mozDash, michael@0: mozilla::ErrorResult& error); michael@0: michael@0: void SetLineDash(const Sequence& mSegments); michael@0: void GetLineDash(nsTArray& mSegments) const; michael@0: michael@0: void SetLineDashOffset(double mOffset); michael@0: double LineDashOffset() const; michael@0: michael@0: double MozDashOffset() michael@0: { michael@0: return CurrentState().dashOffset; michael@0: } michael@0: void SetMozDashOffset(double mozDashOffset); michael@0: michael@0: void GetMozTextStyle(nsAString& mozTextStyle) michael@0: { michael@0: GetFont(mozTextStyle); michael@0: } michael@0: michael@0: void SetMozTextStyle(const nsAString& mozTextStyle, michael@0: mozilla::ErrorResult& error) michael@0: { michael@0: SetFont(mozTextStyle, error); michael@0: } michael@0: michael@0: bool ImageSmoothingEnabled() michael@0: { michael@0: return CurrentState().imageSmoothingEnabled; michael@0: } michael@0: michael@0: void SetImageSmoothingEnabled(bool imageSmoothingEnabled) michael@0: { michael@0: if (imageSmoothingEnabled != CurrentState().imageSmoothingEnabled) { michael@0: CurrentState().imageSmoothingEnabled = imageSmoothingEnabled; michael@0: } michael@0: } michael@0: michael@0: void DrawWindow(nsGlobalWindow& window, double x, double y, double w, double h, michael@0: const nsAString& bgColor, uint32_t flags, michael@0: mozilla::ErrorResult& error); michael@0: void AsyncDrawXULElement(nsXULElement& elem, double x, double y, double w, michael@0: double h, const nsAString& bgColor, uint32_t flags, michael@0: mozilla::ErrorResult& error); michael@0: michael@0: void Demote(); michael@0: michael@0: nsresult Redraw(); michael@0: michael@0: #ifdef DEBUG michael@0: virtual int32_t GetWidth() const MOZ_OVERRIDE; michael@0: virtual int32_t GetHeight() const MOZ_OVERRIDE; michael@0: #endif michael@0: // nsICanvasRenderingContextInternal michael@0: NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE; michael@0: NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE; michael@0: michael@0: NS_IMETHOD GetInputStream(const char* aMimeType, michael@0: const char16_t* aEncoderOptions, michael@0: nsIInputStream **aStream) MOZ_OVERRIDE; michael@0: michael@0: mozilla::TemporaryRef GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) MOZ_OVERRIDE michael@0: { michael@0: EnsureTarget(); michael@0: if (aPremultAlpha) { michael@0: *aPremultAlpha = true; michael@0: } michael@0: return mTarget->Snapshot(); michael@0: } michael@0: michael@0: NS_IMETHOD SetIsOpaque(bool isOpaque) MOZ_OVERRIDE; michael@0: bool GetIsOpaque() MOZ_OVERRIDE { return mOpaque; } michael@0: NS_IMETHOD Reset() MOZ_OVERRIDE; michael@0: already_AddRefed GetCanvasLayer(nsDisplayListBuilder* aBuilder, michael@0: CanvasLayer *aOldLayer, michael@0: LayerManager *aManager) MOZ_OVERRIDE; michael@0: virtual bool ShouldForceInactiveLayer(LayerManager *aManager) MOZ_OVERRIDE; michael@0: void MarkContextClean() MOZ_OVERRIDE; michael@0: NS_IMETHOD SetIsIPC(bool isIPC) MOZ_OVERRIDE; michael@0: // this rect is in canvas device space michael@0: void Redraw(const mozilla::gfx::Rect &r); michael@0: NS_IMETHOD Redraw(const gfxRect &r) MOZ_OVERRIDE { Redraw(ToRect(r)); return NS_OK; } michael@0: NS_IMETHOD SetContextOptions(JSContext* aCx, JS::Handle aOptions) MOZ_OVERRIDE; michael@0: michael@0: // this rect is in mTarget's current user space michael@0: void RedrawUser(const gfxRect &r); michael@0: michael@0: // nsISupports interface + CC michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(CanvasRenderingContext2D) michael@0: michael@0: MOZ_BEGIN_NESTED_ENUM_CLASS(CanvasMultiGetterType, uint8_t) michael@0: STRING = 0, michael@0: PATTERN = 1, michael@0: GRADIENT = 2 michael@0: MOZ_END_NESTED_ENUM_CLASS(CanvasMultiGetterType) michael@0: michael@0: MOZ_BEGIN_NESTED_ENUM_CLASS(Style, uint8_t) michael@0: STROKE = 0, michael@0: FILL, michael@0: MAX michael@0: MOZ_END_NESTED_ENUM_CLASS(Style) michael@0: michael@0: nsINode* GetParentObject() michael@0: { michael@0: return mCanvasElement; michael@0: } michael@0: michael@0: void LineTo(const mozilla::gfx::Point& aPoint) michael@0: { michael@0: if (mPathBuilder) { michael@0: mPathBuilder->LineTo(aPoint); michael@0: } else { michael@0: mDSPathBuilder->LineTo(mTarget->GetTransform() * aPoint); michael@0: } michael@0: } michael@0: michael@0: void BezierTo(const mozilla::gfx::Point& aCP1, michael@0: const mozilla::gfx::Point& aCP2, michael@0: const mozilla::gfx::Point& aCP3) michael@0: { michael@0: if (mPathBuilder) { michael@0: mPathBuilder->BezierTo(aCP1, aCP2, aCP3); michael@0: } else { michael@0: mozilla::gfx::Matrix transform = mTarget->GetTransform(); michael@0: mDSPathBuilder->BezierTo(transform * aCP1, michael@0: transform * aCP2, michael@0: transform * aCP3); michael@0: } michael@0: } michael@0: michael@0: friend class CanvasRenderingContext2DUserData; michael@0: michael@0: virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat); michael@0: michael@0: protected: michael@0: nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY, michael@0: uint32_t aWidth, uint32_t aHeight, michael@0: JSObject** aRetval); michael@0: michael@0: nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h, michael@0: dom::Uint8ClampedArray* aArray, michael@0: bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY, michael@0: int32_t dirtyWidth, int32_t dirtyHeight); michael@0: michael@0: /** michael@0: * Internal method to complete initialisation, expects mTarget to have been set michael@0: */ michael@0: nsresult Initialize(int32_t width, int32_t height); michael@0: michael@0: nsresult InitializeWithTarget(mozilla::gfx::DrawTarget *surface, michael@0: int32_t width, int32_t height); michael@0: michael@0: /** michael@0: * The number of living nsCanvasRenderingContexts. When this goes down to michael@0: * 0, we free the premultiply and unpremultiply tables, if they exist. michael@0: */ michael@0: static uint32_t sNumLivingContexts; michael@0: michael@0: /** michael@0: * Lookup table used to speed up GetImageData(). michael@0: */ michael@0: static uint8_t (*sUnpremultiplyTable)[256]; michael@0: michael@0: /** michael@0: * Lookup table used to speed up PutImageData(). michael@0: */ michael@0: static uint8_t (*sPremultiplyTable)[256]; michael@0: michael@0: static mozilla::gfx::DrawTarget* sErrorTarget; michael@0: michael@0: // Some helpers. Doesn't modify a color on failure. michael@0: void SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& value, michael@0: Style whichStyle); michael@0: void SetStyleFromString(const nsAString& str, Style whichStyle); michael@0: michael@0: void SetStyleFromGradient(CanvasGradient& gradient, Style whichStyle) michael@0: { michael@0: CurrentState().SetGradientStyle(whichStyle, &gradient); michael@0: } michael@0: michael@0: void SetStyleFromPattern(CanvasPattern& pattern, Style whichStyle) michael@0: { michael@0: CurrentState().SetPatternStyle(whichStyle, &pattern); michael@0: } michael@0: michael@0: void GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue, michael@0: Style aWhichStyle); michael@0: michael@0: // Returns whether a color was successfully parsed. michael@0: bool ParseColor(const nsAString& aString, nscolor* aColor); michael@0: michael@0: static void StyleColorToString(const nscolor& aColor, nsAString& aStr); michael@0: michael@0: /** michael@0: * Creates the error target, if it doesn't exist michael@0: */ michael@0: static void EnsureErrorTarget(); michael@0: michael@0: /* This function ensures there is a writable pathbuilder available, this michael@0: * pathbuilder may be working in user space or in device space or michael@0: * device space. michael@0: * After calling this function mPathTransformWillUpdate will be false michael@0: */ michael@0: void EnsureWritablePath(); michael@0: michael@0: // Ensures a path in UserSpace is available. michael@0: void EnsureUserSpacePath(const CanvasWindingRule& winding = CanvasWindingRule::Nonzero); michael@0: michael@0: /** michael@0: * Needs to be called before updating the transform. This makes a call to michael@0: * EnsureTarget() so you don't have to. michael@0: */ michael@0: void TransformWillUpdate(); michael@0: michael@0: // Report the fillRule has changed. michael@0: void FillRuleChanged(); michael@0: michael@0: /** michael@0: * Create the backing surfacing, if it doesn't exist. If there is an error michael@0: * in creating the target then it will put sErrorTarget in place. If there michael@0: * is in turn an error in creating the sErrorTarget then they would both michael@0: * be null so IsTargetValid() would still return null. michael@0: */ michael@0: void EnsureTarget(); michael@0: michael@0: /* michael@0: * Disposes an old target and prepares to lazily create a new target. michael@0: */ michael@0: void ClearTarget(); michael@0: michael@0: /** michael@0: * Check if the target is valid after calling EnsureTarget. michael@0: */ michael@0: bool IsTargetValid() { return mTarget != sErrorTarget && mTarget != nullptr; } michael@0: michael@0: /** michael@0: * Returns the surface format this canvas should be allocated using. Takes michael@0: * into account mOpaque, platform requirements, etc. michael@0: */ michael@0: mozilla::gfx::SurfaceFormat GetSurfaceFormat() const; michael@0: michael@0: void DrawImage(const HTMLImageOrCanvasOrVideoElement &imgElt, michael@0: double sx, double sy, double sw, double sh, michael@0: double dx, double dy, double dw, double dh, michael@0: uint8_t optional_argc, mozilla::ErrorResult& error); michael@0: michael@0: void DrawDirectlyToCanvas(const nsLayoutUtils::DirectDrawInfo& image, michael@0: mozilla::gfx::Rect* bounds, double dx, double dy, michael@0: double dw, double dh, double sx, double sy, michael@0: double sw, double sh, gfxIntSize imgSize); michael@0: michael@0: nsString& GetFont() michael@0: { michael@0: /* will initilize the value if not set, else does nothing */ michael@0: GetCurrentFontStyle(); michael@0: michael@0: return CurrentState().font; michael@0: } michael@0: michael@0: static std::vector& DemotableContexts(); michael@0: static void DemoteOldestContextIfNecessary(); michael@0: michael@0: static void AddDemotableContext(CanvasRenderingContext2D* context); michael@0: static void RemoveDemotableContext(CanvasRenderingContext2D* context); michael@0: michael@0: // Do not use GL michael@0: bool mForceSoftware; michael@0: michael@0: // Member vars michael@0: int32_t mWidth, mHeight; michael@0: michael@0: // This is true when the canvas is valid, but of zero size, this requires michael@0: // specific behavior on some operations. michael@0: bool mZero; michael@0: michael@0: bool mOpaque; michael@0: michael@0: // This is true when the next time our layer is retrieved we need to michael@0: // recreate it (i.e. our backing surface changed) michael@0: bool mResetLayer; michael@0: // This is needed for drawing in drawAsyncXULElement michael@0: bool mIPC; michael@0: michael@0: nsTArray mUserDatas; michael@0: michael@0: // If mCanvasElement is not provided, then a docshell is michael@0: nsCOMPtr mDocShell; michael@0: michael@0: // This is created lazily so it is necessary to call EnsureTarget before michael@0: // accessing it. In the event of an error it will be equal to michael@0: // sErrorTarget. michael@0: mozilla::RefPtr mTarget; michael@0: michael@0: RefPtr mStream; michael@0: michael@0: /** michael@0: * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever michael@0: * Redraw is called, reset to false when Render is called. michael@0: */ michael@0: bool mIsEntireFrameInvalid; michael@0: /** michael@0: * When this is set, the first call to Redraw(gfxRect) should set michael@0: * mIsEntireFrameInvalid since we expect it will be followed by michael@0: * many more Redraw calls. michael@0: */ michael@0: bool mPredictManyRedrawCalls; michael@0: michael@0: // This is stored after GetThebesSurface has been called once to avoid michael@0: // excessive ThebesSurface initialization overhead. michael@0: nsRefPtr mThebesSurface; michael@0: michael@0: /** michael@0: * We also have a device space pathbuilder. The reason for this is as michael@0: * follows, when a path is being built, but the transform changes, we michael@0: * can no longer keep a single path in userspace, considering there's michael@0: * several 'user spaces' now. We therefore transform the current path michael@0: * into device space, and add all operations to this path in device michael@0: * space. michael@0: * michael@0: * When then finally executing a render, the Azure drawing API expects michael@0: * the path to be in userspace. We could then set an identity transform michael@0: * on the DrawTarget and do all drawing in device space. This is michael@0: * undesirable because it requires transforming patterns, gradients, michael@0: * clips, etc. into device space and it would not work for stroking. michael@0: * What we do instead is convert the path back to user space when it is michael@0: * drawn, and draw it with the current transform. This makes all drawing michael@0: * occur correctly. michael@0: * michael@0: * There's never both a device space path builder and a user space path michael@0: * builder present at the same time. There is also never a path and a michael@0: * path builder present at the same time. When writing proceeds on an michael@0: * existing path the Path is cleared and a new builder is created. michael@0: * michael@0: * mPath is always in user-space. michael@0: */ michael@0: mozilla::RefPtr mPath; michael@0: mozilla::RefPtr mDSPathBuilder; michael@0: mozilla::RefPtr mPathBuilder; michael@0: bool mPathTransformWillUpdate; michael@0: mozilla::gfx::Matrix mPathToDS; michael@0: michael@0: /** michael@0: * Number of times we've invalidated before calling redraw michael@0: */ michael@0: uint32_t mInvalidateCount; michael@0: static const uint32_t kCanvasMaxInvalidateCount = 100; michael@0: michael@0: /** michael@0: * State information for hit regions michael@0: */ michael@0: michael@0: struct RegionInfo : public nsStringHashKey michael@0: { michael@0: RegionInfo(const nsAString& aKey) : michael@0: nsStringHashKey(&aKey) michael@0: { michael@0: } michael@0: RegionInfo(const nsAString *aKey) : michael@0: nsStringHashKey(aKey) michael@0: { michael@0: } michael@0: michael@0: nsRefPtr mElement; michael@0: }; michael@0: michael@0: #ifdef ACCESSIBILITY michael@0: static PLDHashOperator RemoveHitRegionProperty(RegionInfo* aEntry, void* aData); michael@0: #endif michael@0: nsTHashtable mHitRegionsOptions; michael@0: michael@0: /** michael@0: * Returns true if a shadow should be drawn along with a michael@0: * drawing operation. michael@0: */ michael@0: bool NeedToDrawShadow() michael@0: { michael@0: const ContextState& state = CurrentState(); michael@0: michael@0: // The spec says we should not draw shadows if the operator is OVER. michael@0: // If it's over and the alpha value is zero, nothing needs to be drawn. michael@0: return NS_GET_A(state.shadowColor) != 0 && michael@0: (state.shadowBlur != 0 || state.shadowOffset.x != 0 || state.shadowOffset.y != 0); michael@0: } michael@0: michael@0: mozilla::gfx::CompositionOp UsedOperation() michael@0: { michael@0: if (NeedToDrawShadow()) { michael@0: // In this case the shadow rendering will use the operator. michael@0: return mozilla::gfx::CompositionOp::OP_OVER; michael@0: } michael@0: michael@0: return CurrentState().op; michael@0: } michael@0: michael@0: /** michael@0: * Gets the pres shell from either the canvas element or the doc shell michael@0: */ michael@0: nsIPresShell *GetPresShell() { michael@0: if (mCanvasElement) { michael@0: return mCanvasElement->OwnerDoc()->GetShell(); michael@0: } michael@0: if (mDocShell) { michael@0: return mDocShell->GetPresShell(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: // text michael@0: michael@0: public: // These enums are public only to accomodate non-C++11 legacy path of michael@0: // MOZ_FINISH_NESTED_ENUM_CLASS. Can move back to protected as soon michael@0: // as that legacy path is dropped. michael@0: MOZ_BEGIN_NESTED_ENUM_CLASS(TextAlign, uint8_t) michael@0: START, michael@0: END, michael@0: LEFT, michael@0: RIGHT, michael@0: CENTER michael@0: MOZ_END_NESTED_ENUM_CLASS(TextAlign) michael@0: michael@0: MOZ_BEGIN_NESTED_ENUM_CLASS(TextBaseline, uint8_t) michael@0: TOP, michael@0: HANGING, michael@0: MIDDLE, michael@0: ALPHABETIC, michael@0: IDEOGRAPHIC, michael@0: BOTTOM michael@0: MOZ_END_NESTED_ENUM_CLASS(TextBaseline) michael@0: michael@0: MOZ_BEGIN_NESTED_ENUM_CLASS(TextDrawOperation, uint8_t) michael@0: FILL, michael@0: STROKE, michael@0: MEASURE michael@0: MOZ_END_NESTED_ENUM_CLASS(TextDrawOperation) michael@0: michael@0: protected: michael@0: gfxFontGroup *GetCurrentFontStyle(); michael@0: michael@0: /* michael@0: * Implementation of the fillText, strokeText, and measure functions with michael@0: * the operation abstracted to a flag. michael@0: */ michael@0: nsresult DrawOrMeasureText(const nsAString& text, michael@0: float x, michael@0: float y, michael@0: const Optional& maxWidth, michael@0: TextDrawOperation op, michael@0: float* aWidth); michael@0: michael@0: // state stack handling michael@0: class ContextState { michael@0: public: michael@0: ContextState() : textAlign(TextAlign::START), michael@0: textBaseline(TextBaseline::ALPHABETIC), michael@0: lineWidth(1.0f), michael@0: miterLimit(10.0f), michael@0: globalAlpha(1.0f), michael@0: shadowBlur(0.0), michael@0: dashOffset(0.0f), michael@0: op(mozilla::gfx::CompositionOp::OP_OVER), michael@0: fillRule(mozilla::gfx::FillRule::FILL_WINDING), michael@0: lineCap(mozilla::gfx::CapStyle::BUTT), michael@0: lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL), michael@0: imageSmoothingEnabled(true) michael@0: { } michael@0: michael@0: ContextState(const ContextState& other) michael@0: : fontGroup(other.fontGroup), michael@0: gradientStyles(other.gradientStyles), michael@0: patternStyles(other.patternStyles), michael@0: colorStyles(other.colorStyles), michael@0: font(other.font), michael@0: textAlign(other.textAlign), michael@0: textBaseline(other.textBaseline), michael@0: shadowColor(other.shadowColor), michael@0: transform(other.transform), michael@0: shadowOffset(other.shadowOffset), michael@0: lineWidth(other.lineWidth), michael@0: miterLimit(other.miterLimit), michael@0: globalAlpha(other.globalAlpha), michael@0: shadowBlur(other.shadowBlur), michael@0: dash(other.dash), michael@0: dashOffset(other.dashOffset), michael@0: op(other.op), michael@0: fillRule(other.fillRule), michael@0: lineCap(other.lineCap), michael@0: lineJoin(other.lineJoin), michael@0: imageSmoothingEnabled(other.imageSmoothingEnabled) michael@0: { } michael@0: michael@0: void SetColorStyle(Style whichStyle, nscolor color) michael@0: { michael@0: colorStyles[whichStyle] = color; michael@0: gradientStyles[whichStyle] = nullptr; michael@0: patternStyles[whichStyle] = nullptr; michael@0: } michael@0: michael@0: void SetPatternStyle(Style whichStyle, CanvasPattern* pat) michael@0: { michael@0: gradientStyles[whichStyle] = nullptr; michael@0: patternStyles[whichStyle] = pat; michael@0: } michael@0: michael@0: void SetGradientStyle(Style whichStyle, CanvasGradient* grad) michael@0: { michael@0: gradientStyles[whichStyle] = grad; michael@0: patternStyles[whichStyle] = nullptr; michael@0: } michael@0: michael@0: /** michael@0: * returns true iff the given style is a solid color. michael@0: */ michael@0: bool StyleIsColor(Style whichStyle) const michael@0: { michael@0: return !(patternStyles[whichStyle] || gradientStyles[whichStyle]); michael@0: } michael@0: michael@0: michael@0: std::vector > clipsPushed; michael@0: michael@0: nsRefPtr fontGroup; michael@0: EnumeratedArray> gradientStyles; michael@0: EnumeratedArray> patternStyles; michael@0: EnumeratedArray colorStyles; michael@0: michael@0: nsString font; michael@0: TextAlign textAlign; michael@0: TextBaseline textBaseline; michael@0: michael@0: nscolor shadowColor; michael@0: michael@0: mozilla::gfx::Matrix transform; michael@0: mozilla::gfx::Point shadowOffset; michael@0: mozilla::gfx::Float lineWidth; michael@0: mozilla::gfx::Float miterLimit; michael@0: mozilla::gfx::Float globalAlpha; michael@0: mozilla::gfx::Float shadowBlur; michael@0: FallibleTArray dash; michael@0: mozilla::gfx::Float dashOffset; michael@0: michael@0: mozilla::gfx::CompositionOp op; michael@0: mozilla::gfx::FillRule fillRule; michael@0: mozilla::gfx::CapStyle lineCap; michael@0: mozilla::gfx::JoinStyle lineJoin; michael@0: michael@0: bool imageSmoothingEnabled; michael@0: }; michael@0: michael@0: nsAutoTArray mStyleStack; michael@0: michael@0: inline ContextState& CurrentState() { michael@0: return mStyleStack[mStyleStack.Length() - 1]; michael@0: } michael@0: michael@0: inline const ContextState& CurrentState() const { michael@0: return mStyleStack[mStyleStack.Length() - 1]; michael@0: } michael@0: michael@0: friend class CanvasGeneralPattern; michael@0: friend class AdjustedTarget; michael@0: michael@0: // other helpers michael@0: void GetAppUnitsValues(int32_t *perDevPixel, int32_t *perCSSPixel) michael@0: { michael@0: // If we don't have a canvas element, we just return something generic. michael@0: int32_t devPixel = 60; michael@0: int32_t cssPixel = 60; michael@0: michael@0: nsIPresShell *ps = GetPresShell(); michael@0: nsPresContext *pc; michael@0: michael@0: if (!ps) goto FINISH; michael@0: pc = ps->GetPresContext(); michael@0: if (!pc) goto FINISH; michael@0: devPixel = pc->AppUnitsPerDevPixel(); michael@0: cssPixel = pc->AppUnitsPerCSSPixel(); michael@0: michael@0: FINISH: michael@0: if (perDevPixel) michael@0: *perDevPixel = devPixel; michael@0: if (perCSSPixel) michael@0: *perCSSPixel = cssPixel; michael@0: } michael@0: michael@0: friend struct CanvasBidiProcessor; michael@0: }; michael@0: michael@0: MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::CanvasMultiGetterType) michael@0: MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::Style) michael@0: MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::TextAlign) michael@0: MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::TextBaseline) michael@0: MOZ_FINISH_NESTED_ENUM_CLASS(CanvasRenderingContext2D::TextDrawOperation) michael@0: michael@0: } michael@0: } michael@0: michael@0: #endif /* CanvasRenderingContext2D_h */