Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #ifndef CanvasRenderingContext2D_h
6 : #define CanvasRenderingContext2D_h
7 :
8 : #include "mozilla/Attributes.h"
9 : #include <vector>
10 : #include "nsIDOMCanvasRenderingContext2D.h"
11 : #include "nsICanvasRenderingContextInternal.h"
12 : #include "mozilla/RefPtr.h"
13 : #include "nsColor.h"
14 : #include "mozilla/dom/HTMLCanvasElement.h"
15 : #include "mozilla/dom/HTMLVideoElement.h"
16 : #include "gfxTextRun.h"
17 : #include "mozilla/ErrorResult.h"
18 : #include "mozilla/dom/BasicRenderingContext2D.h"
19 : #include "mozilla/dom/CanvasGradient.h"
20 : #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
21 : #include "mozilla/dom/CanvasPattern.h"
22 : #include "mozilla/gfx/Rect.h"
23 : #include "mozilla/gfx/2D.h"
24 : #include "mozilla/UniquePtr.h"
25 : #include "gfx2DGlue.h"
26 : #include "imgIEncoder.h"
27 : #include "nsLayoutUtils.h"
28 : #include "mozilla/EnumeratedArray.h"
29 : #include "FilterSupport.h"
30 : #include "nsSVGEffects.h"
31 : #include "Layers.h"
32 : #include "nsBidi.h"
33 :
34 : class nsGlobalWindow;
35 : class nsXULElement;
36 :
37 : namespace mozilla {
38 : namespace gl {
39 : class SourceSurface;
40 : } // namespace gl
41 :
42 : namespace dom {
43 : class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap;
44 : typedef HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElementOrImageBitmap CanvasImageSource;
45 : class ImageData;
46 : class StringOrCanvasGradientOrCanvasPattern;
47 : class OwningStringOrCanvasGradientOrCanvasPattern;
48 : class TextMetrics;
49 : class CanvasFilterChainObserver;
50 : class CanvasPath;
51 :
52 : extern const mozilla::gfx::Float SIGMA_MAX;
53 :
54 : template<typename T> class Optional;
55 :
56 : struct CanvasBidiProcessor;
57 : class CanvasRenderingContext2DUserData;
58 : class CanvasDrawObserver;
59 : class CanvasShutdownObserver;
60 :
61 : /**
62 : ** CanvasRenderingContext2D
63 : **/
64 : class CanvasRenderingContext2D final :
65 : public nsICanvasRenderingContextInternal,
66 : public nsWrapperCache,
67 : public BasicRenderingContext2D
68 : {
69 : virtual ~CanvasRenderingContext2D();
70 :
71 : public:
72 : explicit CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend);
73 :
74 : virtual JSObject* WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
75 :
76 0 : HTMLCanvasElement* GetCanvas() const
77 : {
78 0 : if (!mCanvasElement || mCanvasElement->IsInNativeAnonymousSubtree()) {
79 0 : return nullptr;
80 : }
81 :
82 : // corresponds to changes to the old bindings made in bug 745025
83 0 : return mCanvasElement->GetOriginalCanvas();
84 : }
85 :
86 : void Save() override;
87 : void Restore() override;
88 : void Scale(double aX, double aY, mozilla::ErrorResult& aError) override;
89 : void Rotate(double aAngle, mozilla::ErrorResult& aError) override;
90 : void Translate(double aX, double aY, mozilla::ErrorResult& aError) override;
91 : void Transform(double aM11, double aM12, double aM21, double aM22,
92 : double aDx, double aDy, mozilla::ErrorResult& aError) override;
93 : void SetTransform(double aM11, double aM12, double aM21, double aM22,
94 : double aDx, double aDy, mozilla::ErrorResult& aError) override;
95 : void ResetTransform(mozilla::ErrorResult& aError) override;
96 :
97 0 : double GlobalAlpha() override
98 : {
99 0 : return CurrentState().globalAlpha;
100 : }
101 :
102 : // Useful for silencing cast warnings
103 0 : static mozilla::gfx::Float ToFloat(double aValue) { return mozilla::gfx::Float(aValue); }
104 :
105 0 : void SetGlobalAlpha(double aGlobalAlpha) override
106 : {
107 0 : if (aGlobalAlpha >= 0.0 && aGlobalAlpha <= 1.0) {
108 0 : CurrentState().globalAlpha = ToFloat(aGlobalAlpha);
109 : }
110 0 : }
111 :
112 : void GetGlobalCompositeOperation(nsAString& aOp,
113 : mozilla::ErrorResult& aError) override;
114 : void SetGlobalCompositeOperation(const nsAString& aOp,
115 : mozilla::ErrorResult& aError) override;
116 :
117 : void
118 0 : GetStrokeStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue) override
119 : {
120 0 : GetStyleAsUnion(aValue, Style::STROKE);
121 0 : }
122 :
123 : void
124 0 : SetStrokeStyle(const StringOrCanvasGradientOrCanvasPattern& aValue) override
125 : {
126 0 : SetStyleFromUnion(aValue, Style::STROKE);
127 0 : }
128 :
129 : void
130 0 : GetFillStyle(OwningStringOrCanvasGradientOrCanvasPattern& aValue) override
131 : {
132 0 : GetStyleAsUnion(aValue, Style::FILL);
133 0 : }
134 :
135 : void
136 0 : SetFillStyle(const StringOrCanvasGradientOrCanvasPattern& aValue) override
137 : {
138 0 : SetStyleFromUnion(aValue, Style::FILL);
139 0 : }
140 :
141 : already_AddRefed<CanvasGradient>
142 : CreateLinearGradient(double aX0, double aY0, double aX1, double aY1) override;
143 : already_AddRefed<CanvasGradient>
144 : CreateRadialGradient(double aX0, double aY0, double aR0,
145 : double aX1, double aY1, double aR1,
146 : ErrorResult& aError) override;
147 : already_AddRefed<CanvasPattern>
148 : CreatePattern(const CanvasImageSource& aElement,
149 : const nsAString& aRepeat, ErrorResult& aError) override;
150 :
151 0 : double ShadowOffsetX() override
152 : {
153 0 : return CurrentState().shadowOffset.x;
154 : }
155 :
156 0 : void SetShadowOffsetX(double aShadowOffsetX) override
157 : {
158 0 : CurrentState().shadowOffset.x = ToFloat(aShadowOffsetX);
159 0 : }
160 :
161 0 : double ShadowOffsetY() override
162 : {
163 0 : return CurrentState().shadowOffset.y;
164 : }
165 :
166 0 : void SetShadowOffsetY(double aShadowOffsetY) override
167 : {
168 0 : CurrentState().shadowOffset.y = ToFloat(aShadowOffsetY);
169 0 : }
170 :
171 0 : double ShadowBlur() override
172 : {
173 0 : return CurrentState().shadowBlur;
174 : }
175 :
176 0 : void SetShadowBlur(double aShadowBlur) override
177 : {
178 0 : if (aShadowBlur >= 0.0) {
179 0 : CurrentState().shadowBlur = ToFloat(aShadowBlur);
180 : }
181 0 : }
182 :
183 0 : void GetShadowColor(nsAString& aShadowColor) override
184 : {
185 0 : StyleColorToString(CurrentState().shadowColor, aShadowColor);
186 0 : }
187 :
188 0 : void GetFilter(nsAString& aFilter)
189 : {
190 0 : aFilter = CurrentState().filterString;
191 0 : }
192 :
193 : void SetShadowColor(const nsAString& aShadowColor) override;
194 : void SetFilter(const nsAString& aFilter, mozilla::ErrorResult& aError);
195 : void ClearRect(double aX, double aY, double aW, double aH) override;
196 : void FillRect(double aX, double aY, double aW, double aH) override;
197 : void StrokeRect(double aX, double aY, double aW, double aH) override;
198 : void BeginPath();
199 : void Fill(const CanvasWindingRule& aWinding);
200 : void Fill(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
201 : void Stroke();
202 : void Stroke(const CanvasPath& aPath);
203 : void DrawFocusIfNeeded(mozilla::dom::Element& aElement, ErrorResult& aRv);
204 : bool DrawCustomFocusRing(mozilla::dom::Element& aElement);
205 : void Clip(const CanvasWindingRule& aWinding);
206 : void Clip(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
207 : bool IsPointInPath(double aX, double aY, const CanvasWindingRule& aWinding);
208 : bool IsPointInPath(const CanvasPath& aPath, double aX, double aY, const CanvasWindingRule& aWinding);
209 : bool IsPointInStroke(double aX, double aY);
210 : bool IsPointInStroke(const CanvasPath& aPath, double aX, double aY);
211 : void FillText(const nsAString& aText, double aX, double aY,
212 : const Optional<double>& aMaxWidth,
213 : mozilla::ErrorResult& aError);
214 : void StrokeText(const nsAString& aText, double aX, double aY,
215 : const Optional<double>& aMaxWidth,
216 : mozilla::ErrorResult& aError);
217 : TextMetrics*
218 : MeasureText(const nsAString& aRawText, mozilla::ErrorResult& aError);
219 :
220 : void AddHitRegion(const HitRegionOptions& aOptions, mozilla::ErrorResult& aError);
221 : void RemoveHitRegion(const nsAString& aId);
222 : void ClearHitRegions();
223 :
224 0 : void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy,
225 : mozilla::ErrorResult& aError) override
226 : {
227 0 : DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, 0.0, 0.0, 0, aError);
228 0 : }
229 :
230 0 : void DrawImage(const CanvasImageSource& aImage, double aDx, double aDy,
231 : double aDw, double aDh, mozilla::ErrorResult& aError) override
232 : {
233 0 : DrawImage(aImage, 0.0, 0.0, 0.0, 0.0, aDx, aDy, aDw, aDh, 2, aError);
234 0 : }
235 :
236 0 : void DrawImage(const CanvasImageSource& aImage,
237 : double aSx, double aSy, double aSw, double aSh,
238 : double aDx, double aDy, double aDw, double aDh,
239 : mozilla::ErrorResult& aError) override
240 : {
241 0 : DrawImage(aImage, aSx, aSy, aSw, aSh, aDx, aDy, aDw, aDh, 6, aError);
242 0 : }
243 :
244 : already_AddRefed<ImageData>
245 : CreateImageData(JSContext* aCx, double aSw, double aSh,
246 : mozilla::ErrorResult& aError);
247 : already_AddRefed<ImageData>
248 : CreateImageData(JSContext* aCx, ImageData& aImagedata,
249 : mozilla::ErrorResult& aError);
250 : already_AddRefed<ImageData>
251 : GetImageData(JSContext* aCx, double aSx, double aSy, double aSw, double aSh,
252 : mozilla::ErrorResult& aError);
253 : void PutImageData(ImageData& aImageData,
254 : double aDx, double aDy, mozilla::ErrorResult& aError);
255 : void PutImageData(ImageData& aImageData,
256 : double aDx, double aDy, double aDirtyX, double aDirtyY,
257 : double aDirtyWidth, double aDirtyHeight,
258 : mozilla::ErrorResult& aError);
259 :
260 0 : double LineWidth() override
261 : {
262 0 : return CurrentState().lineWidth;
263 : }
264 :
265 0 : void SetLineWidth(double aWidth) override
266 : {
267 0 : if (aWidth > 0.0) {
268 0 : CurrentState().lineWidth = ToFloat(aWidth);
269 : }
270 0 : }
271 : void GetLineCap(nsAString& aLinecapStyle) override;
272 : void SetLineCap(const nsAString& aLinecapStyle) override;
273 : void GetLineJoin(nsAString& aLinejoinStyle,
274 : mozilla::ErrorResult& aError) override;
275 : void SetLineJoin(const nsAString& aLinejoinStyle) override;
276 :
277 0 : double MiterLimit() override
278 : {
279 0 : return CurrentState().miterLimit;
280 : }
281 :
282 0 : void SetMiterLimit(double aMiter) override
283 : {
284 0 : if (aMiter > 0.0) {
285 0 : CurrentState().miterLimit = ToFloat(aMiter);
286 : }
287 0 : }
288 :
289 0 : void GetFont(nsAString& aFont)
290 : {
291 0 : aFont = GetFont();
292 0 : }
293 :
294 : void SetFont(const nsAString& aFont, mozilla::ErrorResult& aError);
295 : void GetTextAlign(nsAString& aTextAlign);
296 : void SetTextAlign(const nsAString& aTextAlign);
297 : void GetTextBaseline(nsAString& aTextBaseline);
298 : void SetTextBaseline(const nsAString& aTextBaseline);
299 :
300 0 : void ClosePath() override
301 : {
302 0 : EnsureWritablePath();
303 :
304 0 : if (mPathBuilder) {
305 0 : mPathBuilder->Close();
306 : } else {
307 0 : mDSPathBuilder->Close();
308 : }
309 0 : }
310 :
311 0 : void MoveTo(double aX, double aY) override
312 : {
313 0 : EnsureWritablePath();
314 :
315 0 : if (mPathBuilder) {
316 0 : mPathBuilder->MoveTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
317 : } else {
318 0 : mDSPathBuilder->MoveTo(mTarget->GetTransform().TransformPoint(
319 0 : mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))));
320 : }
321 0 : }
322 :
323 0 : void LineTo(double aX, double aY) override
324 : {
325 0 : EnsureWritablePath();
326 :
327 0 : LineTo(mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
328 0 : }
329 :
330 0 : void QuadraticCurveTo(double aCpx, double aCpy, double aX, double aY) override
331 : {
332 0 : EnsureWritablePath();
333 :
334 0 : if (mPathBuilder) {
335 0 : mPathBuilder->QuadraticBezierTo(mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy)),
336 0 : mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
337 : } else {
338 0 : mozilla::gfx::Matrix transform = mTarget->GetTransform();
339 0 : mDSPathBuilder->QuadraticBezierTo(transform.TransformPoint(
340 0 : mozilla::gfx::Point(ToFloat(aCpx), ToFloat(aCpy))),
341 0 : transform.TransformPoint(
342 0 : mozilla::gfx::Point(ToFloat(aX), ToFloat(aY))));
343 : }
344 0 : }
345 :
346 0 : void BezierCurveTo(double aCp1x, double aCp1y, double aCp2x, double aCp2y,
347 : double aX, double aY) override
348 : {
349 0 : EnsureWritablePath();
350 :
351 0 : BezierTo(mozilla::gfx::Point(ToFloat(aCp1x), ToFloat(aCp1y)),
352 0 : mozilla::gfx::Point(ToFloat(aCp2x), ToFloat(aCp2y)),
353 0 : mozilla::gfx::Point(ToFloat(aX), ToFloat(aY)));
354 0 : }
355 :
356 : void ArcTo(double aX1, double aY1, double aX2, double aY2,
357 : double aRadius, mozilla::ErrorResult& aError) override;
358 : void Rect(double aX, double aY, double aW, double aH) override;
359 : void Arc(double aX, double aY, double aRadius, double aStartAngle,
360 : double aEndAngle, bool aAnticlockwise,
361 : mozilla::ErrorResult& aError) override;
362 : void Ellipse(double aX, double aY, double aRadiusX, double aRadiusY,
363 : double aRotation, double aStartAngle, double aEndAngle,
364 : bool aAnticlockwise, ErrorResult& aError) override;
365 :
366 : void GetMozCurrentTransform(JSContext* aCx,
367 : JS::MutableHandle<JSObject*> aResult,
368 : mozilla::ErrorResult& aError);
369 : void SetMozCurrentTransform(JSContext* aCx,
370 : JS::Handle<JSObject*> aCurrentTransform,
371 : mozilla::ErrorResult& aError);
372 : void GetMozCurrentTransformInverse(JSContext* aCx,
373 : JS::MutableHandle<JSObject*> aResult,
374 : mozilla::ErrorResult& aError);
375 : void SetMozCurrentTransformInverse(JSContext* aCx,
376 : JS::Handle<JSObject*> aCurrentTransform,
377 : mozilla::ErrorResult& aError);
378 : void GetFillRule(nsAString& aFillRule);
379 : void SetFillRule(const nsAString& aFillRule);
380 :
381 : void SetLineDash(const Sequence<double>& aSegments,
382 : mozilla::ErrorResult& aRv) override;
383 : void GetLineDash(nsTArray<double>& aSegments) const override;
384 :
385 : void SetLineDashOffset(double aOffset) override;
386 : double LineDashOffset() const override;
387 :
388 0 : void GetMozTextStyle(nsAString& aMozTextStyle)
389 : {
390 0 : GetFont(aMozTextStyle);
391 0 : }
392 :
393 0 : void SetMozTextStyle(const nsAString& aMozTextStyle,
394 : mozilla::ErrorResult& aError)
395 : {
396 0 : SetFont(aMozTextStyle, aError);
397 0 : }
398 :
399 0 : bool ImageSmoothingEnabled() override
400 : {
401 0 : return CurrentState().imageSmoothingEnabled;
402 : }
403 :
404 0 : void SetImageSmoothingEnabled(bool aImageSmoothingEnabled) override
405 : {
406 0 : if (aImageSmoothingEnabled != CurrentState().imageSmoothingEnabled) {
407 0 : CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled;
408 : }
409 0 : }
410 :
411 : void DrawWindow(nsGlobalWindow& aWindow, double aX, double aY,
412 : double aW, double aH,
413 : const nsAString& aBgColor, uint32_t aFlags,
414 : mozilla::ErrorResult& aError);
415 :
416 : enum RenderingMode {
417 : SoftwareBackendMode,
418 : OpenGLBackendMode,
419 : DefaultBackendMode
420 : };
421 :
422 : bool SwitchRenderingMode(RenderingMode aRenderingMode);
423 :
424 : // Eventually this should be deprecated. Keeping for now to keep the binding functional.
425 : void Demote();
426 :
427 : nsresult Redraw();
428 :
429 : virtual int32_t GetWidth() const override;
430 : virtual int32_t GetHeight() const override;
431 0 : gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
432 :
433 : // nsICanvasRenderingContextInternal
434 : /**
435 : * Gets the pres shell from either the canvas element or the doc shell
436 : */
437 0 : virtual nsIPresShell *GetPresShell() override {
438 0 : if (mCanvasElement) {
439 0 : return mCanvasElement->OwnerDoc()->GetShell();
440 : }
441 0 : if (mDocShell) {
442 0 : return mDocShell->GetPresShell();
443 : }
444 0 : return nullptr;
445 : }
446 : NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
447 : NS_IMETHOD InitializeWithDrawTarget(nsIDocShell* aShell,
448 : NotNull<gfx::DrawTarget*> aTarget) override;
449 :
450 : NS_IMETHOD GetInputStream(const char* aMimeType,
451 : const char16_t* aEncoderOptions,
452 : nsIInputStream** aStream) override;
453 :
454 : already_AddRefed<mozilla::gfx::SourceSurface>
455 0 : GetSurfaceSnapshot(gfxAlphaType* aOutAlphaType = nullptr) override
456 : {
457 0 : EnsureTarget();
458 0 : if (aOutAlphaType) {
459 0 : *aOutAlphaType = (mOpaque ? gfxAlphaType::Opaque : gfxAlphaType::Premult);
460 : }
461 0 : return mTarget->Snapshot();
462 : }
463 :
464 : virtual void SetIsOpaque(bool aIsOpaque) override;
465 0 : bool GetIsOpaque() override { return mOpaque; }
466 : NS_IMETHOD Reset() override;
467 : already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
468 : Layer* aOldLayer,
469 : LayerManager* aManager,
470 : bool aMirror = false) override;
471 : virtual bool ShouldForceInactiveLayer(LayerManager* aManager) override;
472 : void MarkContextClean() override;
473 : void MarkContextCleanForFrameCapture() override;
474 : bool IsContextCleanForFrameCapture() override;
475 : NS_IMETHOD SetIsIPC(bool aIsIPC) override;
476 : // this rect is in canvas device space
477 : void Redraw(const mozilla::gfx::Rect& aR);
478 0 : NS_IMETHOD Redraw(const gfxRect& aR) override { Redraw(ToRect(aR)); return NS_OK; }
479 : NS_IMETHOD SetContextOptions(JSContext* aCx,
480 : JS::Handle<JS::Value> aOptions,
481 : ErrorResult& aRvForDictionaryInit) override;
482 :
483 : /**
484 : * An abstract base class to be implemented by callers wanting to be notified
485 : * that a refresh has occurred. Callers must ensure an observer is removed
486 : * before it is destroyed.
487 : */
488 : virtual void DidRefresh() override;
489 :
490 : // this rect is in mTarget's current user space
491 : void RedrawUser(const gfxRect& aR);
492 :
493 : // nsISupports interface + CC
494 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
495 :
496 0 : NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS(CanvasRenderingContext2D)
497 :
498 : enum class CanvasMultiGetterType : uint8_t {
499 : STRING = 0,
500 : PATTERN = 1,
501 : GRADIENT = 2
502 : };
503 :
504 : enum class Style : uint8_t {
505 : STROKE = 0,
506 : FILL,
507 : MAX
508 : };
509 :
510 0 : nsINode* GetParentObject()
511 : {
512 0 : return mCanvasElement;
513 : }
514 :
515 0 : void LineTo(const mozilla::gfx::Point& aPoint)
516 : {
517 0 : if (mPathBuilder) {
518 0 : mPathBuilder->LineTo(aPoint);
519 : } else {
520 0 : mDSPathBuilder->LineTo(mTarget->GetTransform().TransformPoint(aPoint));
521 : }
522 0 : }
523 :
524 0 : void BezierTo(const mozilla::gfx::Point& aCP1,
525 : const mozilla::gfx::Point& aCP2,
526 : const mozilla::gfx::Point& aCP3)
527 : {
528 0 : if (mPathBuilder) {
529 0 : mPathBuilder->BezierTo(aCP1, aCP2, aCP3);
530 : } else {
531 0 : mozilla::gfx::Matrix transform = mTarget->GetTransform();
532 0 : mDSPathBuilder->BezierTo(transform.TransformPoint(aCP1),
533 0 : transform.TransformPoint(aCP2),
534 0 : transform.TransformPoint(aCP3));
535 : }
536 0 : }
537 :
538 : friend class CanvasRenderingContext2DUserData;
539 :
540 : virtual UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
541 :
542 :
543 : // Given a point, return hit region ID if it exists
544 : nsString GetHitRegion(const mozilla::gfx::Point& aPoint) override;
545 :
546 :
547 : // return true and fills in the bound rect if element has a hit region.
548 : bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
549 :
550 : void OnShutdown();
551 :
552 : // Check the global setup, as well as the compositor type:
553 : bool AllowOpenGLCanvas() const;
554 :
555 : protected:
556 : nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
557 : uint32_t aWidth, uint32_t aHeight,
558 : JSObject** aRetval);
559 :
560 : nsresult PutImageData_explicit(int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
561 : dom::Uint8ClampedArray* aArray,
562 : bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY,
563 : int32_t aDirtyWidth, int32_t aDirtyHeight);
564 :
565 : bool CopyBufferProvider(layers::PersistentBufferProvider& aOld,
566 : gfx::DrawTarget& aTarget,
567 : gfx::IntRect aCopyRect);
568 :
569 : /**
570 : * Internal method to complete initialisation, expects mTarget to have been set
571 : */
572 : nsresult Initialize(int32_t aWidth, int32_t aHeight);
573 :
574 : nsresult InitializeWithTarget(mozilla::gfx::DrawTarget* aSurface,
575 : int32_t aWidth, int32_t aHeight);
576 :
577 : /**
578 : * The number of living nsCanvasRenderingContexts. When this goes down to
579 : * 0, we free the premultiply and unpremultiply tables, if they exist.
580 : */
581 : static uintptr_t sNumLivingContexts;
582 :
583 : static mozilla::gfx::DrawTarget* sErrorTarget;
584 :
585 : void SetTransformInternal(const mozilla::gfx::Matrix& aTransform);
586 :
587 : // Some helpers. Doesn't modify a color on failure.
588 : void SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& aValue,
589 : Style aWhichStyle);
590 : void SetStyleFromString(const nsAString& aStr, Style aWhichStyle);
591 :
592 0 : void SetStyleFromGradient(CanvasGradient& aGradient, Style aWhichStyle)
593 : {
594 0 : CurrentState().SetGradientStyle(aWhichStyle, &aGradient);
595 0 : }
596 :
597 0 : void SetStyleFromPattern(CanvasPattern& aPattern, Style aWhichStyle)
598 : {
599 0 : CurrentState().SetPatternStyle(aWhichStyle, &aPattern);
600 0 : }
601 :
602 : void GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
603 : Style aWhichStyle);
604 :
605 : // Returns whether a color was successfully parsed.
606 : bool ParseColor(const nsAString& aString, nscolor* aColor);
607 :
608 : static void StyleColorToString(const nscolor& aColor, nsAString& aStr);
609 :
610 : // Returns whether a filter was successfully parsed.
611 : bool ParseFilter(const nsAString& aString,
612 : nsTArray<nsStyleFilter>& aFilterChain,
613 : ErrorResult& aError);
614 :
615 : // Returns whether the font was successfully updated.
616 : bool SetFontInternal(const nsAString& aFont, mozilla::ErrorResult& aError);
617 :
618 :
619 : /**
620 : * Creates the error target, if it doesn't exist
621 : */
622 : static void EnsureErrorTarget();
623 :
624 : /* This function ensures there is a writable pathbuilder available, this
625 : * pathbuilder may be working in user space or in device space or
626 : * device space.
627 : * After calling this function mPathTransformWillUpdate will be false
628 : */
629 : void EnsureWritablePath();
630 :
631 : // Ensures a path in UserSpace is available.
632 : void EnsureUserSpacePath(const CanvasWindingRule& aWinding = CanvasWindingRule::Nonzero);
633 :
634 : /**
635 : * Needs to be called before updating the transform. This makes a call to
636 : * EnsureTarget() so you don't have to.
637 : */
638 : void TransformWillUpdate();
639 :
640 : // Report the fillRule has changed.
641 : void FillRuleChanged();
642 :
643 : /**
644 : * Create the backing surfacing, if it doesn't exist. If there is an error
645 : * in creating the target then it will put sErrorTarget in place. If there
646 : * is in turn an error in creating the sErrorTarget then they would both
647 : * be null so IsTargetValid() would still return null.
648 : *
649 : * Returns the actual rendering mode being used by the created target.
650 : */
651 : RenderingMode EnsureTarget(const gfx::Rect* aCoveredRect = nullptr,
652 : RenderingMode aRenderMode = RenderingMode::DefaultBackendMode);
653 :
654 : void RestoreClipsAndTransformToTarget();
655 :
656 : bool TrySkiaGLTarget(RefPtr<gfx::DrawTarget>& aOutDT,
657 : RefPtr<layers::PersistentBufferProvider>& aOutProvider);
658 :
659 : bool TrySharedTarget(RefPtr<gfx::DrawTarget>& aOutDT,
660 : RefPtr<layers::PersistentBufferProvider>& aOutProvider);
661 :
662 : bool TryBasicTarget(RefPtr<gfx::DrawTarget>& aOutDT,
663 : RefPtr<layers::PersistentBufferProvider>& aOutProvider);
664 :
665 : void RegisterAllocation();
666 :
667 : void SetInitialState();
668 :
669 : void SetErrorState();
670 :
671 : /**
672 : * This method is run at the end of the event-loop spin where
673 : * ScheduleStableStateCallback was called.
674 : *
675 : * We use it to unlock resources that need to be locked while drawing.
676 : */
677 : void OnStableState();
678 :
679 : /**
680 : * Cf. OnStableState.
681 : */
682 : void ScheduleStableStateCallback();
683 :
684 : /**
685 : * Disposes an old target and prepares to lazily create a new target.
686 : */
687 : void ClearTarget();
688 :
689 : /*
690 : * Returns the target to the buffer provider. i.e. this will queue a frame for
691 : * rendering.
692 : */
693 : void ReturnTarget(bool aForceReset = false);
694 :
695 : /**
696 : * Check if the target is valid after calling EnsureTarget.
697 : */
698 0 : bool IsTargetValid() const {
699 0 : return !!mTarget && mTarget != sErrorTarget;
700 : }
701 :
702 : /**
703 : * Returns the surface format this canvas should be allocated using. Takes
704 : * into account mOpaque, platform requirements, etc.
705 : */
706 : mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
707 :
708 : /**
709 : * Returns true if we know for sure that the pattern for a given style is opaque.
710 : * Usefull to know if we can discard the content below in certain situations.
711 : */
712 : bool PatternIsOpaque(Style aStyle) const;
713 :
714 : /**
715 : * Update CurrentState().filter with the filter description for
716 : * CurrentState().filterChain.
717 : * Flushes the PresShell, so the world can change if you call this function.
718 : */
719 : void UpdateFilter();
720 :
721 : nsLayoutUtils::SurfaceFromElementResult
722 : CachedSurfaceFromElement(Element* aElement);
723 :
724 : void DrawImage(const CanvasImageSource& aImgElt,
725 : double aSx, double aSy, double aSw, double aSh,
726 : double aDx, double aDy, double aDw, double aDh,
727 : uint8_t aOptional_argc, mozilla::ErrorResult& aError);
728 :
729 : void DrawDirectlyToCanvas(const nsLayoutUtils::DirectDrawInfo& aImage,
730 : mozilla::gfx::Rect* aBounds,
731 : mozilla::gfx::Rect aDest,
732 : mozilla::gfx::Rect aSrc,
733 : gfx::IntSize aImgSize);
734 :
735 0 : nsString& GetFont()
736 : {
737 : /* will initilize the value if not set, else does nothing */
738 0 : GetCurrentFontStyle();
739 :
740 0 : return CurrentState().font;
741 : }
742 :
743 : // This function maintains a list of raw pointers to cycle-collected
744 : // objects. We need to ensure that no entries persist beyond unlink,
745 : // since the objects are logically destructed at that point.
746 : static std::vector<CanvasRenderingContext2D*>& DemotableContexts();
747 : static void DemoteOldestContextIfNecessary();
748 :
749 : static void AddDemotableContext(CanvasRenderingContext2D* aContext);
750 : static void RemoveDemotableContext(CanvasRenderingContext2D* aContext);
751 :
752 : RenderingMode mRenderingMode;
753 :
754 : layers::LayersBackend mCompositorBackend;
755 :
756 : // Member vars
757 : int32_t mWidth, mHeight;
758 :
759 : // This is true when the canvas is valid, but of zero size, this requires
760 : // specific behavior on some operations.
761 : bool mZero;
762 :
763 : bool mOpaque;
764 :
765 : // This is true when the next time our layer is retrieved we need to
766 : // recreate it (i.e. our backing surface changed)
767 : bool mResetLayer;
768 : // This is needed for drawing in drawAsyncXULElement
769 : bool mIPC;
770 : // True if the current DrawTarget is using skia-gl, used so we can avoid
771 : // requesting the DT from mBufferProvider to check.
772 : bool mIsSkiaGL;
773 :
774 : bool mHasPendingStableStateCallback;
775 :
776 : nsTArray<CanvasRenderingContext2DUserData*> mUserDatas;
777 :
778 : // If mCanvasElement is not provided, then a docshell is
779 : nsCOMPtr<nsIDocShell> mDocShell;
780 :
781 : // This is created lazily so it is necessary to call EnsureTarget before
782 : // accessing it. In the event of an error it will be equal to
783 : // sErrorTarget.
784 : RefPtr<mozilla::gfx::DrawTarget> mTarget;
785 :
786 : RefPtr<mozilla::layers::PersistentBufferProvider> mBufferProvider;
787 :
788 : uint32_t SkiaGLTex() const;
789 :
790 : // This observes our draw calls at the beginning of the canvas
791 : // lifetime and switches to software or GPU mode depending on
792 : // what it thinks is best
793 : CanvasDrawObserver* mDrawObserver;
794 : void RemoveDrawObserver();
795 :
796 : RefPtr<CanvasShutdownObserver> mShutdownObserver;
797 : void RemoveShutdownObserver();
798 0 : bool AlreadyShutDown() const { return !mShutdownObserver; }
799 :
800 : /**
801 : * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
802 : * Redraw is called, reset to false when Render is called.
803 : */
804 : bool mIsEntireFrameInvalid;
805 : /**
806 : * When this is set, the first call to Redraw(gfxRect) should set
807 : * mIsEntireFrameInvalid since we expect it will be followed by
808 : * many more Redraw calls.
809 : */
810 : bool mPredictManyRedrawCalls;
811 :
812 : /**
813 : * Flag to avoid unnecessary surface copies to FrameCaptureListeners in the
814 : * case when the canvas is not currently being drawn into and not rendered
815 : * but canvas capturing is still ongoing.
816 : */
817 : bool mIsCapturedFrameInvalid;
818 :
819 : /**
820 : * We also have a device space pathbuilder. The reason for this is as
821 : * follows, when a path is being built, but the transform changes, we
822 : * can no longer keep a single path in userspace, considering there's
823 : * several 'user spaces' now. We therefore transform the current path
824 : * into device space, and add all operations to this path in device
825 : * space.
826 : *
827 : * When then finally executing a render, the Azure drawing API expects
828 : * the path to be in userspace. We could then set an identity transform
829 : * on the DrawTarget and do all drawing in device space. This is
830 : * undesirable because it requires transforming patterns, gradients,
831 : * clips, etc. into device space and it would not work for stroking.
832 : * What we do instead is convert the path back to user space when it is
833 : * drawn, and draw it with the current transform. This makes all drawing
834 : * occur correctly.
835 : *
836 : * There's never both a device space path builder and a user space path
837 : * builder present at the same time. There is also never a path and a
838 : * path builder present at the same time. When writing proceeds on an
839 : * existing path the Path is cleared and a new builder is created.
840 : *
841 : * mPath is always in user-space.
842 : */
843 : RefPtr<mozilla::gfx::Path> mPath;
844 : RefPtr<mozilla::gfx::PathBuilder> mDSPathBuilder;
845 : RefPtr<mozilla::gfx::PathBuilder> mPathBuilder;
846 : bool mPathTransformWillUpdate;
847 : mozilla::gfx::Matrix mPathToDS;
848 :
849 : /**
850 : * Number of times we've invalidated before calling redraw
851 : */
852 : uint32_t mInvalidateCount;
853 : static const uint32_t kCanvasMaxInvalidateCount = 100;
854 :
855 : /**
856 : * State information for hit regions
857 : */
858 0 : struct RegionInfo
859 : {
860 : nsString mId;
861 : // fallback element for a11y
862 : RefPtr<Element> mElement;
863 : // Path of the hit region in the 2d context coordinate space (not user space)
864 : RefPtr<gfx::Path> mPath;
865 : };
866 :
867 : nsTArray<RegionInfo> mHitRegionsOptions;
868 :
869 : nsBidi mBidiEngine;
870 :
871 : /**
872 : * Returns true if a shadow should be drawn along with a
873 : * drawing operation.
874 : */
875 0 : bool NeedToDrawShadow()
876 : {
877 0 : const ContextState& state = CurrentState();
878 :
879 : // The spec says we should not draw shadows if the operator is OVER.
880 : // If it's over and the alpha value is zero, nothing needs to be drawn.
881 0 : return NS_GET_A(state.shadowColor) != 0 &&
882 0 : (state.shadowBlur != 0.f || state.shadowOffset.x != 0.f || state.shadowOffset.y != 0.f);
883 : }
884 :
885 : /**
886 : * Returns true if the result of a drawing operation should be
887 : * drawn with a filter.
888 : */
889 0 : bool NeedToApplyFilter()
890 : {
891 0 : return EnsureUpdatedFilter().mPrimitives.Length() > 0;
892 : }
893 :
894 : /**
895 : * Calls UpdateFilter if the canvas's WriteOnly state has changed between the
896 : * last call to UpdateFilter and now.
897 : */
898 0 : const gfx::FilterDescription& EnsureUpdatedFilter() {
899 0 : bool isWriteOnly = mCanvasElement && mCanvasElement->IsWriteOnly();
900 0 : if (CurrentState().filterSourceGraphicTainted != isWriteOnly) {
901 0 : UpdateFilter();
902 0 : EnsureTarget();
903 : }
904 0 : MOZ_ASSERT(CurrentState().filterSourceGraphicTainted == isWriteOnly);
905 0 : return CurrentState().filter;
906 : }
907 :
908 0 : bool NeedToCalculateBounds()
909 : {
910 0 : return NeedToDrawShadow() || NeedToApplyFilter();
911 : }
912 :
913 0 : mozilla::gfx::CompositionOp UsedOperation()
914 : {
915 0 : if (NeedToDrawShadow() || NeedToApplyFilter()) {
916 : // In this case the shadow or filter rendering will use the operator.
917 0 : return mozilla::gfx::CompositionOp::OP_OVER;
918 : }
919 :
920 0 : return CurrentState().op;
921 : }
922 :
923 : // text
924 :
925 : protected:
926 : enum class TextAlign : uint8_t {
927 : START,
928 : END,
929 : LEFT,
930 : RIGHT,
931 : CENTER
932 : };
933 :
934 : enum class TextBaseline : uint8_t {
935 : TOP,
936 : HANGING,
937 : MIDDLE,
938 : ALPHABETIC,
939 : IDEOGRAPHIC,
940 : BOTTOM
941 : };
942 :
943 : enum class TextDrawOperation : uint8_t {
944 : FILL,
945 : STROKE,
946 : MEASURE
947 : };
948 :
949 : protected:
950 : gfxFontGroup *GetCurrentFontStyle();
951 :
952 : /**
953 : * Implementation of the fillText, strokeText, and measure functions with
954 : * the operation abstracted to a flag.
955 : */
956 : nsresult DrawOrMeasureText(const nsAString& aText,
957 : float aX,
958 : float aY,
959 : const Optional<double>& aMaxWidth,
960 : TextDrawOperation aOp,
961 : float* aWidth);
962 :
963 : bool CheckSizeForSkiaGL(mozilla::gfx::IntSize aSize);
964 :
965 : // A clip or a transform, recorded and restored in order.
966 0 : struct ClipState {
967 0 : explicit ClipState(mozilla::gfx::Path* aClip)
968 0 : : clip(aClip)
969 0 : {}
970 :
971 0 : explicit ClipState(const mozilla::gfx::Matrix& aTransform)
972 0 : : transform(aTransform)
973 0 : {}
974 :
975 0 : bool IsClip() const { return !!clip; }
976 :
977 : RefPtr<mozilla::gfx::Path> clip;
978 : mozilla::gfx::Matrix transform;
979 : };
980 :
981 : // state stack handling
982 0 : class ContextState {
983 : public:
984 0 : ContextState() : textAlign(TextAlign::START),
985 : textBaseline(TextBaseline::ALPHABETIC),
986 : shadowColor(0),
987 : lineWidth(1.0f),
988 : miterLimit(10.0f),
989 : globalAlpha(1.0f),
990 : shadowBlur(0.0),
991 : dashOffset(0.0f),
992 : op(mozilla::gfx::CompositionOp::OP_OVER),
993 : fillRule(mozilla::gfx::FillRule::FILL_WINDING),
994 : lineCap(mozilla::gfx::CapStyle::BUTT),
995 : lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
996 : filterString(u"none"),
997 : filterSourceGraphicTainted(false),
998 : imageSmoothingEnabled(true),
999 0 : fontExplicitLanguage(false)
1000 0 : { }
1001 :
1002 0 : ContextState(const ContextState& aOther)
1003 0 : : fontGroup(aOther.fontGroup),
1004 : fontLanguage(aOther.fontLanguage),
1005 : fontFont(aOther.fontFont),
1006 : gradientStyles(aOther.gradientStyles),
1007 : patternStyles(aOther.patternStyles),
1008 : colorStyles(aOther.colorStyles),
1009 : font(aOther.font),
1010 0 : textAlign(aOther.textAlign),
1011 0 : textBaseline(aOther.textBaseline),
1012 0 : shadowColor(aOther.shadowColor),
1013 : transform(aOther.transform),
1014 : shadowOffset(aOther.shadowOffset),
1015 0 : lineWidth(aOther.lineWidth),
1016 0 : miterLimit(aOther.miterLimit),
1017 0 : globalAlpha(aOther.globalAlpha),
1018 0 : shadowBlur(aOther.shadowBlur),
1019 : dash(aOther.dash),
1020 0 : dashOffset(aOther.dashOffset),
1021 0 : op(aOther.op),
1022 0 : fillRule(aOther.fillRule),
1023 0 : lineCap(aOther.lineCap),
1024 0 : lineJoin(aOther.lineJoin),
1025 : filterString(aOther.filterString),
1026 : filterChain(aOther.filterChain),
1027 : filterChainObserver(aOther.filterChainObserver),
1028 : filter(aOther.filter),
1029 : filterAdditionalImages(aOther.filterAdditionalImages),
1030 0 : filterSourceGraphicTainted(aOther.filterSourceGraphicTainted),
1031 0 : imageSmoothingEnabled(aOther.imageSmoothingEnabled),
1032 0 : fontExplicitLanguage(aOther.fontExplicitLanguage)
1033 0 : { }
1034 :
1035 0 : void SetColorStyle(Style aWhichStyle, nscolor aColor)
1036 : {
1037 0 : colorStyles[aWhichStyle] = aColor;
1038 0 : gradientStyles[aWhichStyle] = nullptr;
1039 0 : patternStyles[aWhichStyle] = nullptr;
1040 0 : }
1041 :
1042 0 : void SetPatternStyle(Style aWhichStyle, CanvasPattern* aPat)
1043 : {
1044 0 : gradientStyles[aWhichStyle] = nullptr;
1045 0 : patternStyles[aWhichStyle] = aPat;
1046 0 : }
1047 :
1048 0 : void SetGradientStyle(Style aWhichStyle, CanvasGradient* aGrad)
1049 : {
1050 0 : gradientStyles[aWhichStyle] = aGrad;
1051 0 : patternStyles[aWhichStyle] = nullptr;
1052 0 : }
1053 :
1054 : /**
1055 : * returns true iff the given style is a solid color.
1056 : */
1057 0 : bool StyleIsColor(Style aWhichStyle) const
1058 : {
1059 0 : return !(patternStyles[aWhichStyle] || gradientStyles[aWhichStyle]);
1060 : }
1061 :
1062 0 : int32_t ShadowBlurRadius() const
1063 : {
1064 : static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
1065 0 : return (int32_t)floor(ShadowBlurSigma() * GAUSSIAN_SCALE_FACTOR + 0.5);
1066 : }
1067 :
1068 0 : mozilla::gfx::Float ShadowBlurSigma() const
1069 : {
1070 0 : return std::min(SIGMA_MAX, shadowBlur / 2.0f);
1071 : }
1072 :
1073 : nsTArray<ClipState> clipsAndTransforms;
1074 :
1075 : RefPtr<gfxFontGroup> fontGroup;
1076 : nsCOMPtr<nsIAtom> fontLanguage;
1077 : nsFont fontFont;
1078 :
1079 : EnumeratedArray<Style, Style::MAX, RefPtr<CanvasGradient>> gradientStyles;
1080 : EnumeratedArray<Style, Style::MAX, RefPtr<CanvasPattern>> patternStyles;
1081 : EnumeratedArray<Style, Style::MAX, nscolor> colorStyles;
1082 :
1083 : nsString font;
1084 : TextAlign textAlign;
1085 : TextBaseline textBaseline;
1086 :
1087 : nscolor shadowColor;
1088 :
1089 : mozilla::gfx::Matrix transform;
1090 : mozilla::gfx::Point shadowOffset;
1091 : mozilla::gfx::Float lineWidth;
1092 : mozilla::gfx::Float miterLimit;
1093 : mozilla::gfx::Float globalAlpha;
1094 : mozilla::gfx::Float shadowBlur;
1095 : nsTArray<mozilla::gfx::Float> dash;
1096 : mozilla::gfx::Float dashOffset;
1097 :
1098 : mozilla::gfx::CompositionOp op;
1099 : mozilla::gfx::FillRule fillRule;
1100 : mozilla::gfx::CapStyle lineCap;
1101 : mozilla::gfx::JoinStyle lineJoin;
1102 :
1103 : nsString filterString;
1104 : nsTArray<nsStyleFilter> filterChain;
1105 : RefPtr<nsSVGFilterChainObserver> filterChainObserver;
1106 : mozilla::gfx::FilterDescription filter;
1107 : nsTArray<RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
1108 :
1109 : // This keeps track of whether the canvas was "tainted" or not when
1110 : // we last used a filter. This is a security measure, whereby the
1111 : // canvas is flipped to write-only if a cross-origin image is drawn to it.
1112 : // This is to stop bad actors from reading back data they shouldn't have
1113 : // access to.
1114 : //
1115 : // This also limits what filters we can apply to the context; in particular
1116 : // feDisplacementMap is restricted.
1117 : //
1118 : // We keep track of this to ensure that if this gets out of sync with the
1119 : // tainted state of the canvas itself, we update our filters accordingly.
1120 : bool filterSourceGraphicTainted;
1121 :
1122 : bool imageSmoothingEnabled;
1123 : bool fontExplicitLanguage;
1124 : };
1125 :
1126 : AutoTArray<ContextState, 3> mStyleStack;
1127 :
1128 0 : inline ContextState& CurrentState() {
1129 0 : return mStyleStack[mStyleStack.Length() - 1];
1130 : }
1131 :
1132 0 : inline const ContextState& CurrentState() const {
1133 0 : return mStyleStack[mStyleStack.Length() - 1];
1134 : }
1135 :
1136 : friend class CanvasGeneralPattern;
1137 : friend class CanvasFilterChainObserver;
1138 : friend class AdjustedTarget;
1139 : friend class AdjustedTargetForShadow;
1140 : friend class AdjustedTargetForFilter;
1141 :
1142 : // other helpers
1143 0 : void GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel)
1144 : {
1145 : // If we don't have a canvas element, we just return something generic.
1146 0 : int32_t devPixel = 60;
1147 0 : int32_t cssPixel = 60;
1148 :
1149 0 : nsIPresShell *ps = GetPresShell();
1150 : nsPresContext *pc;
1151 :
1152 0 : if (!ps) goto FINISH;
1153 0 : pc = ps->GetPresContext();
1154 0 : if (!pc) goto FINISH;
1155 0 : devPixel = pc->AppUnitsPerDevPixel();
1156 0 : cssPixel = pc->AppUnitsPerCSSPixel();
1157 :
1158 : FINISH:
1159 0 : if (aPerDevPixel)
1160 0 : *aPerDevPixel = devPixel;
1161 0 : if (aPerCSSPixel)
1162 0 : *aPerCSSPixel = cssPixel;
1163 0 : }
1164 :
1165 : friend struct CanvasBidiProcessor;
1166 : friend class CanvasDrawObserver;
1167 : };
1168 :
1169 : } // namespace dom
1170 : } // namespace mozilla
1171 :
1172 : #endif /* CanvasRenderingContext2D_h */
|