1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "CanvasRenderingContext2D.h"
8 #include "mozilla/gfx/Helpers.h"
9 #include "nsXULElement.h"
11 #include "nsIServiceManager.h"
12 #include "nsMathUtils.h"
13 #include "SVGImageContext.h"
15 #include "nsContentUtils.h"
17 #include "nsIDocument.h"
18 #include "mozilla/dom/HTMLCanvasElement.h"
19 #include "nsSVGEffects.h"
20 #include "nsPresContext.h"
21 #include "nsIPresShell.h"
23 #include "nsIInterfaceRequestorUtils.h"
27 #include "nsCSSParser.h"
28 #include "mozilla/css/StyleRule.h"
29 #include "mozilla/css/Declaration.h"
30 #include "nsComputedDOMStyle.h"
31 #include "nsStyleSet.h"
33 #include "nsPrintfCString.h"
35 #include "nsReadableUtils.h"
38 #include "nsGfxCIID.h"
39 #include "nsIDocShell.h"
40 #include "nsIDOMWindow.h"
41 #include "nsPIDOMWindow.h"
42 #include "nsDisplayList.h"
43 #include "nsFocusManager.h"
47 #include "ImageEncoder.h"
48 #include "ImageRegion.h"
50 #include "gfxContext.h"
51 #include "gfxASurface.h"
52 #include "gfxImageSurface.h"
53 #include "gfxPlatform.h"
58 #include "nsFrameLoader.h"
60 #include "nsBidiPresUtils.h"
62 #include "CanvasUtils.h"
63 #include "nsIMemoryReporter.h"
64 #include "nsStyleUtil.h"
65 #include "CanvasImageCache.h"
70 #include "jsfriendapi.h"
72 #include "mozilla/Alignment.h"
73 #include "mozilla/Assertions.h"
74 #include "mozilla/CheckedInt.h"
75 #include "mozilla/DebugOnly.h"
76 #include "mozilla/dom/ContentParent.h"
77 #include "mozilla/dom/ImageData.h"
78 #include "mozilla/dom/PBrowserParent.h"
79 #include "mozilla/dom/ToJSValue.h"
80 #include "mozilla/dom/TypedArray.h"
81 #include "mozilla/Endian.h"
82 #include "mozilla/gfx/2D.h"
83 #include "mozilla/gfx/PathHelpers.h"
84 #include "mozilla/gfx/DataSurfaceHelpers.h"
85 #include "mozilla/ipc/DocumentRendererParent.h"
86 #include "mozilla/ipc/PDocumentRendererParent.h"
87 #include "mozilla/MathAlgorithms.h"
88 #include "mozilla/Preferences.h"
89 #include "mozilla/Telemetry.h"
90 #include "mozilla/unused.h"
91 #include "nsCCUncollectableMarker.h"
92 #include "nsWrapperCacheInlines.h"
93 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
94 #include "mozilla/dom/HTMLImageElement.h"
95 #include "mozilla/dom/HTMLVideoElement.h"
96 #include "mozilla/dom/SVGMatrix.h"
97 #include "mozilla/dom/TextMetrics.h"
98 #include "mozilla/dom/UnionTypes.h"
99 #include "mozilla/dom/SVGMatrix.h"
100 #include "nsGlobalWindow.h"
101 #include "GLContext.h"
102 #include "GLContextProvider.h"
103 #include "SVGContentUtils.h"
104 #include "SVGImageContext.h"
105 #include "nsIScreenManager.h"
107 #undef free // apparently defined by some windows header, clashing with a free()
108 // method in SkTypes.h
110 #include "SkiaGLGlue.h"
111 #include "SurfaceStream.h"
112 #include "SurfaceTypes.h"
115 using mozilla::gl::GLContext
;
116 using mozilla::gl::SkiaGLGlue
;
117 using mozilla::gl::GLContextProvider
;
120 #include "gfxWindowsPlatform.h"
123 #ifdef MOZ_WIDGET_GONK
124 #include "mozilla/layers/ShadowLayers.h"
127 // windows.h (included by chromium code) defines this, in its infinite wisdom
130 using namespace mozilla
;
131 using namespace mozilla::CanvasUtils
;
132 using namespace mozilla::css
;
133 using namespace mozilla::gfx
;
134 using namespace mozilla::image
;
135 using namespace mozilla::ipc
;
136 using namespace mozilla::layers
;
138 namespace mgfx
= mozilla::gfx
;
143 // Cap sigma to avoid overly large temp surfaces.
144 const Float SIGMA_MAX
= 100;
146 /* Memory reporter stuff */
147 static int64_t gCanvasAzureMemoryUsed
= 0;
149 // This is KIND_OTHER because it's not always clear where in memory the pixels
150 // of a canvas are stored. Furthermore, this memory will be tracked by the
151 // underlying surface implementations. See bug 655638 for details.
152 class Canvas2dPixelsReporter MOZ_FINAL
: public nsIMemoryReporter
154 ~Canvas2dPixelsReporter() {}
158 NS_IMETHOD
CollectReports(nsIHandleReportCallback
* aHandleReport
,
159 nsISupports
* aData
, bool aAnonymize
)
161 return MOZ_COLLECT_REPORT(
162 "canvas-2d-pixels", KIND_OTHER
, UNITS_BYTES
,
163 gCanvasAzureMemoryUsed
,
164 "Memory used by 2D canvases. Each canvas requires "
165 "(width * height * 4) bytes.");
169 NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter
, nsIMemoryReporter
)
171 class CanvasRadialGradient
: public CanvasGradient
174 CanvasRadialGradient(CanvasRenderingContext2D
* aContext
,
175 const Point
&aBeginOrigin
, Float aBeginRadius
,
176 const Point
&aEndOrigin
, Float aEndRadius
)
177 : CanvasGradient(aContext
, Type::RADIAL
)
178 , mCenter1(aBeginOrigin
)
179 , mCenter2(aEndOrigin
)
180 , mRadius1(aBeginRadius
)
181 , mRadius2(aEndRadius
)
191 class CanvasLinearGradient
: public CanvasGradient
194 CanvasLinearGradient(CanvasRenderingContext2D
* aContext
,
195 const Point
&aBegin
, const Point
&aEnd
)
196 : CanvasGradient(aContext
, Type::LINEAR
)
203 friend class CanvasGeneralPattern
;
205 // Beginning of linear gradient.
207 // End of linear gradient.
211 // This class is named 'GeneralCanvasPattern' instead of just
212 // 'GeneralPattern' to keep Windows PGO builds from confusing the
213 // GeneralPattern class in gfxContext.cpp with this one.
215 class CanvasGeneralPattern
218 typedef CanvasRenderingContext2D::Style Style
;
219 typedef CanvasRenderingContext2D::ContextState ContextState
;
221 CanvasGeneralPattern() : mPattern(nullptr) {}
222 ~CanvasGeneralPattern()
225 mPattern
->~Pattern();
229 Pattern
& ForStyle(CanvasRenderingContext2D
*aCtx
,
233 // This should only be called once or the mPattern destructor will
235 NS_ASSERTION(!mPattern
, "ForStyle() should only be called once on CanvasGeneralPattern!");
237 const ContextState
&state
= aCtx
->CurrentState();
239 if (state
.StyleIsColor(aStyle
)) {
240 mPattern
= new (mColorPattern
.addr()) ColorPattern(Color::FromABGR(state
.colorStyles
[aStyle
]));
241 } else if (state
.gradientStyles
[aStyle
] &&
242 state
.gradientStyles
[aStyle
]->GetType() == CanvasGradient::Type::LINEAR
) {
243 CanvasLinearGradient
*gradient
=
244 static_cast<CanvasLinearGradient
*>(state
.gradientStyles
[aStyle
].get());
246 mPattern
= new (mLinearGradientPattern
.addr())
247 LinearGradientPattern(gradient
->mBegin
, gradient
->mEnd
,
248 gradient
->GetGradientStopsForTarget(aRT
));
249 } else if (state
.gradientStyles
[aStyle
] &&
250 state
.gradientStyles
[aStyle
]->GetType() == CanvasGradient::Type::RADIAL
) {
251 CanvasRadialGradient
*gradient
=
252 static_cast<CanvasRadialGradient
*>(state
.gradientStyles
[aStyle
].get());
254 mPattern
= new (mRadialGradientPattern
.addr())
255 RadialGradientPattern(gradient
->mCenter1
, gradient
->mCenter2
, gradient
->mRadius1
,
256 gradient
->mRadius2
, gradient
->GetGradientStopsForTarget(aRT
));
257 } else if (state
.patternStyles
[aStyle
]) {
258 if (aCtx
->mCanvasElement
) {
259 CanvasUtils::DoDrawImageSecurityCheck(aCtx
->mCanvasElement
,
260 state
.patternStyles
[aStyle
]->mPrincipal
,
261 state
.patternStyles
[aStyle
]->mForceWriteOnly
,
262 state
.patternStyles
[aStyle
]->mCORSUsed
);
266 if (state
.patternStyles
[aStyle
]->mRepeat
== CanvasPattern::RepeatMode::NOREPEAT
) {
267 mode
= ExtendMode::CLAMP
;
269 mode
= ExtendMode::REPEAT
;
271 mPattern
= new (mSurfacePattern
.addr())
272 SurfacePattern(state
.patternStyles
[aStyle
]->mSurface
, mode
,
273 state
.patternStyles
[aStyle
]->mTransform
);
280 AlignedStorage2
<ColorPattern
> mColorPattern
;
281 AlignedStorage2
<LinearGradientPattern
> mLinearGradientPattern
;
282 AlignedStorage2
<RadialGradientPattern
> mRadialGradientPattern
;
283 AlignedStorage2
<SurfacePattern
> mSurfacePattern
;
288 /* This is an RAII based class that can be used as a drawtarget for
289 * operations that need a shadow drawn. It will automatically provide a
290 * temporary target when needed, and if so blend it back with a shadow.
292 * aBounds specifies the bounds of the drawing operation that will be
293 * drawn to the target, it is given in device space! This function will
294 * change aBounds to incorporate shadow bounds. If this is nullptr the drawing
295 * operation will be assumed to cover an infinite rect.
300 typedef CanvasRenderingContext2D::ContextState ContextState
;
302 AdjustedTarget(CanvasRenderingContext2D
*ctx
,
303 mgfx::Rect
*aBounds
= nullptr)
306 if (!ctx
->NeedToDrawShadow()) {
307 mTarget
= ctx
->mTarget
;
312 const ContextState
&state
= mCtx
->CurrentState();
314 mSigma
= state
.shadowBlur
/ 2.0f
;
316 if (mSigma
> SIGMA_MAX
) {
320 Matrix transform
= mCtx
->mTarget
->GetTransform();
322 mTempRect
= mgfx::Rect(0, 0, ctx
->mWidth
, ctx
->mHeight
);
324 static const gfxFloat GAUSSIAN_SCALE_FACTOR
= (3 * sqrt(2 * M_PI
) / 4) * 1.5;
325 int32_t blurRadius
= (int32_t) floor(mSigma
* GAUSSIAN_SCALE_FACTOR
+ 0.5);
327 // We need to enlarge and possibly offset our temporary surface
328 // so that things outside of the canvas may cast shadows.
329 mTempRect
.Inflate(Margin(blurRadius
+ std::max
<Float
>(state
.shadowOffset
.y
, 0),
330 blurRadius
+ std::max
<Float
>(-state
.shadowOffset
.x
, 0),
331 blurRadius
+ std::max
<Float
>(-state
.shadowOffset
.y
, 0),
332 blurRadius
+ std::max
<Float
>(state
.shadowOffset
.x
, 0)));
335 // We actually include the bounds of the shadow blur, this makes it
336 // easier to execute the actual blur on hardware, and shouldn't affect
337 // the amount of pixels that need to be touched.
338 aBounds
->Inflate(Margin(blurRadius
, blurRadius
,
339 blurRadius
, blurRadius
));
340 mTempRect
= mTempRect
.Intersect(*aBounds
);
343 mTempRect
.ScaleRoundOut(1.0f
);
345 transform
._31
-= mTempRect
.x
;
346 transform
._32
-= mTempRect
.y
;
349 mCtx
->mTarget
->CreateShadowDrawTarget(IntSize(int32_t(mTempRect
.width
), int32_t(mTempRect
.height
)),
350 SurfaceFormat::B8G8R8A8
, mSigma
);
353 // XXX - Deal with the situation where our temp size is too big to
355 mTarget
= ctx
->mTarget
;
358 mTarget
->SetTransform(transform
);
368 RefPtr
<SourceSurface
> snapshot
= mTarget
->Snapshot();
370 mCtx
->mTarget
->DrawSurfaceWithShadow(snapshot
, mTempRect
.TopLeft(),
371 Color::FromABGR(mCtx
->CurrentState().shadowColor
),
372 mCtx
->CurrentState().shadowOffset
, mSigma
,
373 mCtx
->CurrentState().op
);
376 operator DrawTarget
*()
381 DrawTarget
* operator->()
387 RefPtr
<DrawTarget
> mTarget
;
388 CanvasRenderingContext2D
*mCtx
;
390 mgfx::Rect mTempRect
;
394 CanvasPattern::SetTransform(SVGMatrix
& aMatrix
)
396 mTransform
= ToMatrix(aMatrix
.GetMatrix());
400 CanvasGradient::AddColorStop(float offset
, const nsAString
& colorstr
, ErrorResult
& rv
)
402 if (offset
< 0.0 || offset
> 1.0) {
403 rv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
409 if (!parser
.ParseColorString(colorstr
, nullptr, 0, value
)) {
410 rv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
415 if (!nsRuleNode::ComputeColor(value
, nullptr, nullptr, color
)) {
416 rv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
422 GradientStop newStop
;
424 newStop
.offset
= offset
;
425 newStop
.color
= Color::FromABGR(color
);
427 mRawStops
.AppendElement(newStop
);
430 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient
, AddRef
)
431 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient
, Release
)
433 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient
, mContext
)
435 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPattern
, AddRef
)
436 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPattern
, Release
)
438 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern
, mContext
)
440 class CanvasRenderingContext2DUserData
: public LayerUserData
{
442 CanvasRenderingContext2DUserData(CanvasRenderingContext2D
*aContext
)
445 aContext
->mUserDatas
.AppendElement(this);
447 ~CanvasRenderingContext2DUserData()
450 mContext
->mUserDatas
.RemoveElement(this);
454 static void PreTransactionCallback(void* aData
)
456 CanvasRenderingContext2DUserData
* self
=
457 static_cast<CanvasRenderingContext2DUserData
*>(aData
);
458 CanvasRenderingContext2D
* context
= self
->mContext
;
459 if (!context
|| !context
->mStream
|| !context
->mTarget
)
462 // Since SkiaGL default to store drawing command until flush
463 // We will have to flush it before present.
464 context
->mTarget
->Flush();
467 static void DidTransactionCallback(void* aData
)
469 CanvasRenderingContext2DUserData
* self
=
470 static_cast<CanvasRenderingContext2DUserData
*>(aData
);
471 if (self
->mContext
) {
472 self
->mContext
->MarkContextClean();
475 bool IsForContext(CanvasRenderingContext2D
*aContext
)
477 return mContext
== aContext
;
485 CanvasRenderingContext2D
*mContext
;
488 NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D
)
489 NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D
)
491 NS_IMPL_CYCLE_COLLECTION_CLASS(CanvasRenderingContext2D
)
493 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D
)
494 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement
)
495 for (uint32_t i
= 0; i
< tmp
->mStyleStack
.Length(); i
++) {
496 ImplCycleCollectionUnlink(tmp
->mStyleStack
[i
].patternStyles
[Style::STROKE
]);
497 ImplCycleCollectionUnlink(tmp
->mStyleStack
[i
].patternStyles
[Style::FILL
]);
498 ImplCycleCollectionUnlink(tmp
->mStyleStack
[i
].gradientStyles
[Style::STROKE
]);
499 ImplCycleCollectionUnlink(tmp
->mStyleStack
[i
].gradientStyles
[Style::FILL
]);
501 for (size_t x
= 0 ; x
< tmp
->mHitRegionsOptions
.Length(); x
++) {
502 RegionInfo
& info
= tmp
->mHitRegionsOptions
[x
];
504 ImplCycleCollectionUnlink(info
.mElement
);
507 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
508 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
510 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D
)
511 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCanvasElement
)
512 for (uint32_t i
= 0; i
< tmp
->mStyleStack
.Length(); i
++) {
513 ImplCycleCollectionTraverse(cb
, tmp
->mStyleStack
[i
].patternStyles
[Style::STROKE
], "Stroke CanvasPattern");
514 ImplCycleCollectionTraverse(cb
, tmp
->mStyleStack
[i
].patternStyles
[Style::FILL
], "Fill CanvasPattern");
515 ImplCycleCollectionTraverse(cb
, tmp
->mStyleStack
[i
].gradientStyles
[Style::STROKE
], "Stroke CanvasGradient");
516 ImplCycleCollectionTraverse(cb
, tmp
->mStyleStack
[i
].gradientStyles
[Style::FILL
], "Fill CanvasGradient");
518 for (size_t x
= 0 ; x
< tmp
->mHitRegionsOptions
.Length(); x
++) {
519 RegionInfo
& info
= tmp
->mHitRegionsOptions
[x
];
521 ImplCycleCollectionTraverse(cb
, info
.mElement
, "Hit region fallback element");
524 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
525 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
527 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(CanvasRenderingContext2D
)
529 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CanvasRenderingContext2D
)
530 if (nsCCUncollectableMarker::sGeneration
&& tmp
->IsBlack()) {
531 dom::Element
* canvasElement
= tmp
->mCanvasElement
;
533 if (canvasElement
->IsPurple()) {
534 canvasElement
->RemovePurple();
536 dom::Element::MarkNodeChildren(canvasElement
);
540 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
542 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CanvasRenderingContext2D
)
543 return nsCCUncollectableMarker::sGeneration
&& tmp
->IsBlack();
544 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
546 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D
)
547 return nsCCUncollectableMarker::sGeneration
&& tmp
->IsBlack();
548 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
550 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D
)
551 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
552 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal
)
553 NS_INTERFACE_MAP_ENTRY(nsISupports
)
557 ** CanvasRenderingContext2D impl
561 // Initialize our static variables.
562 uint32_t CanvasRenderingContext2D::sNumLivingContexts
= 0;
563 DrawTarget
* CanvasRenderingContext2D::sErrorTarget
= nullptr;
567 CanvasRenderingContext2D::CanvasRenderingContext2D()
568 : mForceSoftware(false)
569 // these are the default values from the Canvas spec
570 , mWidth(0), mHeight(0)
571 , mZero(false), mOpaque(false)
575 , mIsEntireFrameInvalid(false)
576 , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false)
577 , mInvalidateCount(0)
579 sNumLivingContexts
++;
583 CanvasRenderingContext2D::~CanvasRenderingContext2D()
586 // Drop references from all CanvasRenderingContext2DUserData to this context
587 for (uint32_t i
= 0; i
< mUserDatas
.Length(); ++i
) {
588 mUserDatas
[i
]->Forget();
590 sNumLivingContexts
--;
591 if (!sNumLivingContexts
) {
592 NS_IF_RELEASE(sErrorTarget
);
595 RemoveDemotableContext(this);
599 CanvasRenderingContext2D::WrapObject(JSContext
*cx
)
601 return CanvasRenderingContext2DBinding::Wrap(cx
, this);
605 CanvasRenderingContext2D::ParseColor(const nsAString
& aString
,
608 nsIDocument
* document
= mCanvasElement
609 ? mCanvasElement
->OwnerDoc()
612 // Pass the CSS Loader object to the parser, to allow parser error
613 // reports to include the outer window ID.
614 nsCSSParser
parser(document
? document
->CSSLoader() : nullptr);
616 if (!parser
.ParseColorString(aString
, nullptr, 0, value
)) {
620 if (value
.IsNumericColorUnit()) {
621 // if we already have a color we can just use it directly
622 *aColor
= value
.GetColorValue();
624 // otherwise resolve it
625 nsIPresShell
* presShell
= GetPresShell();
626 nsRefPtr
<nsStyleContext
> parentContext
;
627 if (mCanvasElement
&& mCanvasElement
->IsInDoc()) {
628 // Inherit from the canvas element.
629 parentContext
= nsComputedDOMStyle::GetStyleContextForElement(
630 mCanvasElement
, nullptr, presShell
);
633 unused
<< nsRuleNode::ComputeColor(
634 value
, presShell
? presShell
->GetPresContext() : nullptr, parentContext
,
641 CanvasRenderingContext2D::Reset()
643 if (mCanvasElement
) {
644 mCanvasElement
->InvalidateCanvas();
647 // only do this for non-docshell created contexts,
648 // since those are the ones that we created a surface for
649 if (mTarget
&& IsTargetValid() && !mDocShell
) {
650 gCanvasAzureMemoryUsed
-= mWidth
* mHeight
* 4;
657 mHitRegionsOptions
.ClearAndRetainStorage();
659 // Since the target changes the backing texture will change, and this will
660 // no longer be valid.
661 mIsEntireFrameInvalid
= false;
662 mPredictManyRedrawCalls
= false;
668 CanvasRenderingContext2D::SetStyleFromString(const nsAString
& str
,
671 MOZ_ASSERT(!str
.IsVoid());
674 if (!ParseColor(str
, &color
)) {
678 CurrentState().SetColorStyle(whichStyle
, color
);
682 CanvasRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern
& aValue
,
685 const ContextState
&state
= CurrentState();
686 if (state
.patternStyles
[aWhichStyle
]) {
687 aValue
.SetAsCanvasPattern() = state
.patternStyles
[aWhichStyle
];
688 } else if (state
.gradientStyles
[aWhichStyle
]) {
689 aValue
.SetAsCanvasGradient() = state
.gradientStyles
[aWhichStyle
];
691 StyleColorToString(state
.colorStyles
[aWhichStyle
], aValue
.SetAsString());
697 CanvasRenderingContext2D::StyleColorToString(const nscolor
& aColor
, nsAString
& aStr
)
699 // We can't reuse the normal CSS color stringification code,
700 // because the spec calls for a different algorithm for canvas.
701 if (NS_GET_A(aColor
) == 255) {
702 CopyUTF8toUTF16(nsPrintfCString("#%02x%02x%02x",
708 CopyUTF8toUTF16(nsPrintfCString("rgba(%d, %d, %d, ",
713 aStr
.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor
)));
719 CanvasRenderingContext2D::Redraw()
721 if (mIsEntireFrameInvalid
) {
725 mIsEntireFrameInvalid
= true;
727 if (!mCanvasElement
) {
728 NS_ASSERTION(mDocShell
, "Redraw with no canvas element or docshell!");
732 nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement
);
734 mCanvasElement
->InvalidateCanvasContent(nullptr);
740 CanvasRenderingContext2D::Redraw(const mgfx::Rect
&r
)
744 if (mIsEntireFrameInvalid
) {
748 if (mPredictManyRedrawCalls
||
749 mInvalidateCount
> kCanvasMaxInvalidateCount
) {
754 if (!mCanvasElement
) {
755 NS_ASSERTION(mDocShell
, "Redraw with no canvas element or docshell!");
759 nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement
);
761 mCanvasElement
->InvalidateCanvasContent(&r
);
765 CanvasRenderingContext2D::RedrawUser(const gfxRect
& r
)
767 if (mIsEntireFrameInvalid
) {
773 mTarget
->GetTransform().TransformBounds(ToRect(r
));
777 void CanvasRenderingContext2D::Demote()
779 if (!IsTargetValid() || mForceSoftware
|| !mStream
)
782 RemoveDemotableContext(this);
784 RefPtr
<SourceSurface
> snapshot
= mTarget
->Snapshot();
785 RefPtr
<DrawTarget
> oldTarget
= mTarget
;
789 mForceSoftware
= true;
791 // Recreate target, now demoted to software only
793 if (!IsTargetValid())
796 // Restore the content from the old DrawTarget
797 mgfx::Rect
r(0, 0, mWidth
, mHeight
);
798 mTarget
->DrawSurface(snapshot
, r
, r
);
800 // Restore the clips and transform
801 for (uint32_t i
= 0; i
< CurrentState().clipsPushed
.size(); i
++) {
802 mTarget
->PushClip(CurrentState().clipsPushed
[i
]);
805 mTarget
->SetTransform(oldTarget
->GetTransform());
808 std::vector
<CanvasRenderingContext2D
*>&
809 CanvasRenderingContext2D::DemotableContexts()
811 static std::vector
<CanvasRenderingContext2D
*> contexts
;
816 CanvasRenderingContext2D::DemoteOldestContextIfNecessary()
818 const size_t kMaxContexts
= 64;
820 std::vector
<CanvasRenderingContext2D
*>& contexts
= DemotableContexts();
821 if (contexts
.size() < kMaxContexts
)
824 CanvasRenderingContext2D
* oldest
= contexts
.front();
829 CanvasRenderingContext2D::AddDemotableContext(CanvasRenderingContext2D
* context
)
831 std::vector
<CanvasRenderingContext2D
*>::iterator iter
= std::find(DemotableContexts().begin(), DemotableContexts().end(), context
);
832 if (iter
!= DemotableContexts().end())
835 DemotableContexts().push_back(context
);
839 CanvasRenderingContext2D::RemoveDemotableContext(CanvasRenderingContext2D
* context
)
841 std::vector
<CanvasRenderingContext2D
*>::iterator iter
= std::find(DemotableContexts().begin(), DemotableContexts().end(), context
);
842 if (iter
!= DemotableContexts().end())
843 DemotableContexts().erase(iter
);
847 CanvasRenderingContext2D::CheckSizeForSkiaGL(IntSize size
) {
848 MOZ_ASSERT(NS_IsMainThread());
850 int minsize
= Preferences::GetInt("gfx.canvas.min-size-for-skia-gl", 128);
851 if (size
.width
< minsize
|| size
.height
< minsize
) {
855 // Maximum pref allows 3 different options:
856 // 0 means unlimited size
857 // > 0 means use value as an absolute threshold
858 // < 0 means use the number of screen pixels as a threshold
859 int maxsize
= Preferences::GetInt("gfx.canvas.max-size-for-skia-gl", 0);
861 // unlimited max size
866 // absolute max size threshold
868 return size
.width
<= maxsize
&& size
.height
<= maxsize
;
871 // Cache the number of pixels on the primary screen
872 static int32_t gScreenPixels
= -1;
873 if (gScreenPixels
< 0) {
874 // Default to historical mobile screen size of 980x480, like FishIEtank.
875 // In addition, allow skia use up to this size even if the screen is smaller.
876 // A lot content expects this size to work well.
878 if (gfxPlatform::GetPlatform()->HasEnoughTotalSystemMemoryForSkiaGL()) {
879 gScreenPixels
= 980 * 480;
882 nsCOMPtr
<nsIScreenManager
> screenManager
=
883 do_GetService("@mozilla.org/gfx/screenmanager;1");
885 nsCOMPtr
<nsIScreen
> primaryScreen
;
886 screenManager
->GetPrimaryScreen(getter_AddRefs(primaryScreen
));
888 int32_t x
, y
, width
, height
;
889 primaryScreen
->GetRect(&x
, &y
, &width
, &height
);
891 gScreenPixels
= std::max(gScreenPixels
, width
* height
);
896 // Just always use a scale of 1.0. It can be changed if a lot of contents need it.
897 static double gDefaultScale
= 1.0;
899 double scale
= gDefaultScale
> 0 ? gDefaultScale
: 1.0;
900 int32_t threshold
= ceil(scale
* scale
* gScreenPixels
);
902 // screen size acts as max threshold
903 return threshold
< 0 || (size
.width
* size
.height
) <= threshold
;
907 CanvasRenderingContext2D::EnsureTarget()
913 // Check that the dimensions are sane
914 IntSize
size(mWidth
, mHeight
);
915 if (size
.width
<= 0xFFFF && size
.height
<= 0xFFFF &&
916 size
.width
>= 0 && size
.height
>= 0) {
917 SurfaceFormat format
= GetSurfaceFormat();
918 nsIDocument
* ownerDoc
= nullptr;
919 if (mCanvasElement
) {
920 ownerDoc
= mCanvasElement
->OwnerDoc();
923 nsRefPtr
<LayerManager
> layerManager
= nullptr;
927 nsContentUtils::PersistentLayerManagerForDocument(ownerDoc
);
931 if (gfxPlatform::GetPlatform()->UseAcceleratedSkiaCanvas() &&
933 CheckSizeForSkiaGL(size
)) {
934 DemoteOldestContextIfNecessary();
936 SkiaGLGlue
* glue
= gfxPlatform::GetPlatform()->GetSkiaGLGlue();
939 if (glue
&& glue
->GetGrContext() && glue
->GetGLContext()) {
940 mTarget
= Factory::CreateDrawTargetSkiaWithGrContext(glue
->GetGrContext(), size
, format
);
942 mStream
= gl::SurfaceStream::CreateForType(gl::SurfaceStreamType::TripleBuffer
,
943 glue
->GetGLContext());
944 AddDemotableContext(this);
946 printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
951 mTarget
= layerManager
->CreateDrawTarget(size
, format
);
954 mTarget
= layerManager
->CreateDrawTarget(size
, format
);
956 mTarget
= gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size
, format
);
961 static bool registered
= false;
964 RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
967 gCanvasAzureMemoryUsed
+= mWidth
* mHeight
* 4;
968 JSContext
* context
= nsContentUtils::GetCurrentJSContext();
970 JS_updateMallocCounter(context
, mWidth
* mHeight
* 4);
973 mTarget
->ClearRect(mgfx::Rect(Point(0, 0), Size(mWidth
, mHeight
)));
974 if (mTarget
->GetBackendType() == mgfx::BackendType::CAIRO
) {
975 // Cairo doesn't play well with huge clips. When given a very big clip it
976 // will try to allocate big mask surface without taking the target
977 // size into account which can cause OOM. See bug 1034593.
978 // This limits the clip extents to the size of the canvas.
979 // A fix in Cairo would probably be preferable, but requires somewhat
981 mTarget
->PushClipRect(mgfx::Rect(Point(0, 0), Size(mWidth
, mHeight
)));
983 // Force a full layer transaction since we didn't have a layer before
984 // and now we might need one.
985 if (mCanvasElement
) {
986 mCanvasElement
->InvalidateCanvas();
988 // Calling Redraw() tells our invalidation machinery that the entire
989 // canvas is already invalid, which can speed up future drawing.
993 mTarget
= sErrorTarget
;
999 CanvasRenderingContext2D::GetWidth() const
1005 CanvasRenderingContext2D::GetHeight() const
1012 CanvasRenderingContext2D::SetDimensions(int32_t width
, int32_t height
)
1016 // Zero sized surfaces can cause problems.
1033 CanvasRenderingContext2D::ClearTarget()
1039 // set up the initial canvas defaults
1040 mStyleStack
.Clear();
1041 mPathBuilder
= nullptr;
1043 mDSPathBuilder
= nullptr;
1045 ContextState
*state
= mStyleStack
.AppendElement();
1046 state
->globalAlpha
= 1.0;
1048 state
->colorStyles
[Style::FILL
] = NS_RGB(0,0,0);
1049 state
->colorStyles
[Style::STROKE
] = NS_RGB(0,0,0);
1050 state
->shadowColor
= NS_RGBA(0,0,0,0);
1054 CanvasRenderingContext2D::InitializeWithSurface(nsIDocShell
*shell
,
1055 gfxASurface
*surface
,
1061 SetDimensions(width
, height
);
1062 mTarget
= gfxPlatform::GetPlatform()->
1063 CreateDrawTargetForSurface(surface
, IntSize(width
, height
));
1066 EnsureErrorTarget();
1067 mTarget
= sErrorTarget
;
1070 if (mTarget
->GetBackendType() == mgfx::BackendType::CAIRO
) {
1071 // Cf comment in EnsureTarget
1072 mTarget
->PushClipRect(mgfx::Rect(Point(0, 0), Size(mWidth
, mHeight
)));
1079 CanvasRenderingContext2D::SetIsOpaque(bool isOpaque
)
1081 if (isOpaque
!= mOpaque
) {
1090 CanvasRenderingContext2D::SetIsIPC(bool isIPC
)
1092 if (isIPC
!= mIPC
) {
1101 CanvasRenderingContext2D::SetContextOptions(JSContext
* aCx
, JS::Handle
<JS::Value
> aOptions
)
1103 if (aOptions
.isNullOrUndefined()) {
1107 ContextAttributes2D attributes
;
1108 NS_ENSURE_TRUE(attributes
.Init(aCx
, aOptions
), NS_ERROR_UNEXPECTED
);
1110 if (Preferences::GetBool("gfx.canvas.willReadFrequently.enable", false)) {
1111 // Use software when there is going to be a lot of readback
1112 mForceSoftware
= attributes
.mWillReadFrequently
;
1115 if (!attributes
.mAlpha
) {
1123 CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer
,
1126 *aImageBuffer
= nullptr;
1130 RefPtr
<SourceSurface
> snapshot
= mTarget
->Snapshot();
1135 RefPtr
<DataSourceSurface
> data
= snapshot
->GetDataSurface();
1136 if (!data
|| data
->GetSize() != IntSize(mWidth
, mHeight
)) {
1140 *aImageBuffer
= SurfaceToPackedBGRA(data
);
1141 *aFormat
= imgIEncoder::INPUT_FORMAT_HOSTARGB
;
1144 nsString
CanvasRenderingContext2D::GetHitRegion(const mozilla::gfx::Point
& aPoint
)
1146 for (size_t x
= 0 ; x
< mHitRegionsOptions
.Length(); x
++) {
1147 RegionInfo
& info
= mHitRegionsOptions
[x
];
1148 if (info
.mPath
->ContainsPoint(aPoint
, Matrix())) {
1156 CanvasRenderingContext2D::GetInputStream(const char *aMimeType
,
1157 const char16_t
*aEncoderOptions
,
1158 nsIInputStream
**aStream
)
1160 nsCString
enccid("@mozilla.org/image/encoder;2?type=");
1161 enccid
+= aMimeType
;
1162 nsCOMPtr
<imgIEncoder
> encoder
= do_CreateInstance(enccid
.get());
1164 return NS_ERROR_FAILURE
;
1167 nsAutoArrayPtr
<uint8_t> imageBuffer
;
1169 GetImageBuffer(getter_Transfers(imageBuffer
), &format
);
1171 return NS_ERROR_FAILURE
;
1174 return ImageEncoder::GetInputStream(mWidth
, mHeight
, imageBuffer
, format
,
1175 encoder
, aEncoderOptions
, aStream
);
1179 CanvasRenderingContext2D::GetSurfaceFormat() const
1181 return mOpaque
? SurfaceFormat::B8G8R8X8
: SurfaceFormat::B8G8R8A8
;
1189 CanvasRenderingContext2D::Save()
1192 mStyleStack
[mStyleStack
.Length() - 1].transform
= mTarget
->GetTransform();
1193 mStyleStack
.SetCapacity(mStyleStack
.Length() + 1);
1194 mStyleStack
.AppendElement(CurrentState());
1198 CanvasRenderingContext2D::Restore()
1200 if (mStyleStack
.Length() - 1 == 0)
1203 TransformWillUpdate();
1205 for (uint32_t i
= 0; i
< CurrentState().clipsPushed
.size(); i
++) {
1209 mStyleStack
.RemoveElementAt(mStyleStack
.Length() - 1);
1211 mTarget
->SetTransform(CurrentState().transform
);
1219 CanvasRenderingContext2D::Scale(double x
, double y
, ErrorResult
& error
)
1221 TransformWillUpdate();
1222 if (!IsTargetValid()) {
1223 error
.Throw(NS_ERROR_FAILURE
);
1227 Matrix newMatrix
= mTarget
->GetTransform();
1228 mTarget
->SetTransform(newMatrix
.Scale(x
, y
));
1232 CanvasRenderingContext2D::Rotate(double angle
, ErrorResult
& error
)
1234 TransformWillUpdate();
1235 if (!IsTargetValid()) {
1236 error
.Throw(NS_ERROR_FAILURE
);
1240 Matrix rotation
= Matrix::Rotation(angle
);
1241 mTarget
->SetTransform(rotation
* mTarget
->GetTransform());
1245 CanvasRenderingContext2D::Translate(double x
, double y
, ErrorResult
& error
)
1247 TransformWillUpdate();
1248 if (!IsTargetValid()) {
1249 error
.Throw(NS_ERROR_FAILURE
);
1253 Matrix newMatrix
= mTarget
->GetTransform();
1254 mTarget
->SetTransform(newMatrix
.Translate(x
, y
));
1258 CanvasRenderingContext2D::Transform(double m11
, double m12
, double m21
,
1259 double m22
, double dx
, double dy
,
1262 TransformWillUpdate();
1263 if (!IsTargetValid()) {
1264 error
.Throw(NS_ERROR_FAILURE
);
1268 Matrix
matrix(m11
, m12
, m21
, m22
, dx
, dy
);
1269 mTarget
->SetTransform(matrix
* mTarget
->GetTransform());
1273 CanvasRenderingContext2D::SetTransform(double m11
, double m12
,
1274 double m21
, double m22
,
1275 double dx
, double dy
,
1278 TransformWillUpdate();
1279 if (!IsTargetValid()) {
1280 error
.Throw(NS_ERROR_FAILURE
);
1284 Matrix
matrix(m11
, m12
, m21
, m22
, dx
, dy
);
1285 mTarget
->SetTransform(matrix
);
1289 MatrixToJSObject(JSContext
* cx
, const Matrix
& matrix
,
1290 JS::MutableHandle
<JSObject
*> result
, ErrorResult
& error
)
1292 double elts
[6] = { matrix
._11
, matrix
._12
,
1293 matrix
._21
, matrix
._22
,
1294 matrix
._31
, matrix
._32
};
1296 // XXX Should we enter GetWrapper()'s compartment?
1297 JS::Rooted
<JS::Value
> val(cx
);
1298 if (!ToJSValue(cx
, elts
, &val
)) {
1299 error
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1301 result
.set(&val
.toObject());
1306 ObjectToMatrix(JSContext
* cx
, JS::Handle
<JSObject
*> obj
, Matrix
& matrix
,
1310 if (!JS_GetArrayLength(cx
, obj
, &length
) || length
!= 6) {
1311 // Not an array-like thing or wrong size
1312 error
.Throw(NS_ERROR_INVALID_ARG
);
1316 Float
* elts
[] = { &matrix
._11
, &matrix
._12
, &matrix
._21
, &matrix
._22
,
1317 &matrix
._31
, &matrix
._32
};
1318 for (uint32_t i
= 0; i
< 6; ++i
) {
1319 JS::Rooted
<JS::Value
> elt(cx
);
1321 if (!JS_GetElement(cx
, obj
, i
, &elt
)) {
1322 error
.Throw(NS_ERROR_FAILURE
);
1325 if (!CoerceDouble(elt
, &d
)) {
1326 error
.Throw(NS_ERROR_INVALID_ARG
);
1329 if (!FloatValidate(d
)) {
1330 // This is weird, but it's the behavior of SetTransform()
1333 *elts
[i
] = Float(d
);
1339 CanvasRenderingContext2D::SetMozCurrentTransform(JSContext
* cx
,
1340 JS::Handle
<JSObject
*> currentTransform
,
1344 if (!IsTargetValid()) {
1345 error
.Throw(NS_ERROR_FAILURE
);
1350 if (ObjectToMatrix(cx
, currentTransform
, newCTM
, error
)) {
1351 mTarget
->SetTransform(newCTM
);
1356 CanvasRenderingContext2D::GetMozCurrentTransform(JSContext
* cx
,
1357 JS::MutableHandle
<JSObject
*> result
,
1358 ErrorResult
& error
) const
1360 MatrixToJSObject(cx
, mTarget
? mTarget
->GetTransform() : Matrix(),
1365 CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext
* cx
,
1366 JS::Handle
<JSObject
*> currentTransform
,
1370 if (!IsTargetValid()) {
1371 error
.Throw(NS_ERROR_FAILURE
);
1375 Matrix newCTMInverse
;
1376 if (ObjectToMatrix(cx
, currentTransform
, newCTMInverse
, error
)) {
1377 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
1378 if (newCTMInverse
.Invert()) {
1379 mTarget
->SetTransform(newCTMInverse
);
1385 CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext
* cx
,
1386 JS::MutableHandle
<JSObject
*> result
,
1387 ErrorResult
& error
) const
1390 MatrixToJSObject(cx
, Matrix(), result
, error
);
1394 Matrix ctm
= mTarget
->GetTransform();
1396 if (!ctm
.Invert()) {
1397 double NaN
= JS_GetNaNValue(cx
).toDouble();
1398 ctm
= Matrix(NaN
, NaN
, NaN
, NaN
, NaN
, NaN
);
1401 MatrixToJSObject(cx
, ctm
, result
, error
);
1409 CanvasRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern
& value
,
1412 if (value
.IsString()) {
1413 SetStyleFromString(value
.GetAsString(), whichStyle
);
1417 if (value
.IsCanvasGradient()) {
1418 SetStyleFromGradient(value
.GetAsCanvasGradient(), whichStyle
);
1422 if (value
.IsCanvasPattern()) {
1423 SetStyleFromPattern(value
.GetAsCanvasPattern(), whichStyle
);
1427 MOZ_ASSERT_UNREACHABLE("Invalid union value");
1431 CanvasRenderingContext2D::SetFillRule(const nsAString
& aString
)
1435 if (aString
.EqualsLiteral("evenodd"))
1436 rule
= FillRule::FILL_EVEN_ODD
;
1437 else if (aString
.EqualsLiteral("nonzero"))
1438 rule
= FillRule::FILL_WINDING
;
1442 CurrentState().fillRule
= rule
;
1446 CanvasRenderingContext2D::GetFillRule(nsAString
& aString
)
1448 switch (CurrentState().fillRule
) {
1449 case FillRule::FILL_WINDING
:
1450 aString
.AssignLiteral("nonzero"); break;
1451 case FillRule::FILL_EVEN_ODD
:
1452 aString
.AssignLiteral("evenodd"); break;
1456 // gradients and patterns
1458 already_AddRefed
<CanvasGradient
>
1459 CanvasRenderingContext2D::CreateLinearGradient(double x0
, double y0
, double x1
, double y1
)
1461 nsRefPtr
<CanvasGradient
> grad
=
1462 new CanvasLinearGradient(this, Point(x0
, y0
), Point(x1
, y1
));
1464 return grad
.forget();
1467 already_AddRefed
<CanvasGradient
>
1468 CanvasRenderingContext2D::CreateRadialGradient(double x0
, double y0
, double r0
,
1469 double x1
, double y1
, double r1
,
1470 ErrorResult
& aError
)
1472 if (r0
< 0.0 || r1
< 0.0) {
1473 aError
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
1477 nsRefPtr
<CanvasGradient
> grad
=
1478 new CanvasRadialGradient(this, Point(x0
, y0
), r0
, Point(x1
, y1
), r1
);
1480 return grad
.forget();
1483 already_AddRefed
<CanvasPattern
>
1484 CanvasRenderingContext2D::CreatePattern(const HTMLImageOrCanvasOrVideoElement
& element
,
1485 const nsAString
& repeat
,
1488 CanvasPattern::RepeatMode repeatMode
=
1489 CanvasPattern::RepeatMode::NOREPEAT
;
1491 if (repeat
.IsEmpty() || repeat
.EqualsLiteral("repeat")) {
1492 repeatMode
= CanvasPattern::RepeatMode::REPEAT
;
1493 } else if (repeat
.EqualsLiteral("repeat-x")) {
1494 repeatMode
= CanvasPattern::RepeatMode::REPEATX
;
1495 } else if (repeat
.EqualsLiteral("repeat-y")) {
1496 repeatMode
= CanvasPattern::RepeatMode::REPEATY
;
1497 } else if (repeat
.EqualsLiteral("no-repeat")) {
1498 repeatMode
= CanvasPattern::RepeatMode::NOREPEAT
;
1500 error
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1504 Element
* htmlElement
;
1505 if (element
.IsHTMLCanvasElement()) {
1506 HTMLCanvasElement
* canvas
= &element
.GetAsHTMLCanvasElement();
1507 htmlElement
= canvas
;
1509 nsIntSize size
= canvas
->GetSize();
1510 if (size
.width
== 0 || size
.height
== 0) {
1511 error
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1515 // Special case for Canvas, which could be an Azure canvas!
1516 nsICanvasRenderingContextInternal
*srcCanvas
= canvas
->GetContextAtIndex(0);
1518 // This might not be an Azure canvas!
1519 RefPtr
<SourceSurface
> srcSurf
= srcCanvas
->GetSurfaceSnapshot();
1521 nsRefPtr
<CanvasPattern
> pat
=
1522 new CanvasPattern(this, srcSurf
, repeatMode
, htmlElement
->NodePrincipal(), canvas
->IsWriteOnly(), false);
1524 return pat
.forget();
1526 } else if (element
.IsHTMLImageElement()) {
1527 HTMLImageElement
* img
= &element
.GetAsHTMLImageElement();
1528 if (img
->IntrinsicState().HasState(NS_EVENT_STATE_BROKEN
)) {
1529 error
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1535 htmlElement
= &element
.GetAsHTMLVideoElement();
1540 // The canvas spec says that createPattern should use the first frame
1541 // of animated images
1542 nsLayoutUtils::SurfaceFromElementResult res
=
1543 nsLayoutUtils::SurfaceFromElement(htmlElement
,
1544 nsLayoutUtils::SFE_WANT_FIRST_FRAME
, mTarget
);
1546 if (!res
.mSourceSurface
) {
1547 error
.Throw(NS_ERROR_NOT_AVAILABLE
);
1551 nsRefPtr
<CanvasPattern
> pat
=
1552 new CanvasPattern(this, res
.mSourceSurface
, repeatMode
, res
.mPrincipal
,
1553 res
.mIsWriteOnly
, res
.mCORSUsed
);
1555 return pat
.forget();
1562 CanvasRenderingContext2D::SetShadowColor(const nsAString
& shadowColor
)
1565 if (!ParseColor(shadowColor
, &color
)) {
1569 CurrentState().shadowColor
= color
;
1577 CanvasRenderingContext2D::ClearRect(double x
, double y
, double w
,
1584 mTarget
->ClearRect(mgfx::Rect(x
, y
, w
, h
));
1586 RedrawUser(gfxRect(x
, y
, w
, h
));
1590 CanvasRenderingContext2D::FillRect(double x
, double y
, double w
,
1593 const ContextState
&state
= CurrentState();
1595 if (state
.patternStyles
[Style::FILL
]) {
1596 CanvasPattern::RepeatMode repeat
=
1597 state
.patternStyles
[Style::FILL
]->mRepeat
;
1598 // In the FillRect case repeat modes are easy to deal with.
1599 bool limitx
= repeat
== CanvasPattern::RepeatMode::NOREPEAT
|| repeat
== CanvasPattern::RepeatMode::REPEATY
;
1600 bool limity
= repeat
== CanvasPattern::RepeatMode::NOREPEAT
|| repeat
== CanvasPattern::RepeatMode::REPEATX
;
1602 IntSize patternSize
=
1603 state
.patternStyles
[Style::FILL
]->mSurface
->GetSize();
1605 // We always need to execute painting for non-over operators, even if
1606 // we end up with w/h = 0.
1616 if (x
+ w
> patternSize
.width
) {
1617 w
= patternSize
.width
- x
;
1632 if (y
+ h
> patternSize
.height
) {
1633 h
= patternSize
.height
- y
;
1644 if (NeedToDrawShadow()) {
1645 bounds
= mgfx::Rect(x
, y
, w
, h
);
1646 bounds
= mTarget
->GetTransform().TransformBounds(bounds
);
1649 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
1650 FillRect(mgfx::Rect(x
, y
, w
, h
),
1651 CanvasGeneralPattern().ForStyle(this, Style::FILL
, mTarget
),
1652 DrawOptions(state
.globalAlpha
, UsedOperation()));
1654 RedrawUser(gfxRect(x
, y
, w
, h
));
1658 CanvasRenderingContext2D::StrokeRect(double x
, double y
, double w
,
1661 const ContextState
&state
= CurrentState();
1670 if (!IsTargetValid()) {
1674 if (NeedToDrawShadow()) {
1675 bounds
= mgfx::Rect(x
- state
.lineWidth
/ 2.0f
, y
- state
.lineWidth
/ 2.0f
,
1676 w
+ state
.lineWidth
, h
+ state
.lineWidth
);
1677 bounds
= mTarget
->GetTransform().TransformBounds(bounds
);
1681 CapStyle cap
= CapStyle::BUTT
;
1682 if (state
.lineJoin
== JoinStyle::ROUND
) {
1683 cap
= CapStyle::ROUND
;
1685 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
1686 StrokeLine(Point(x
, y
), Point(x
+ w
, y
),
1687 CanvasGeneralPattern().ForStyle(this, Style::STROKE
, mTarget
),
1688 StrokeOptions(state
.lineWidth
, state
.lineJoin
,
1689 cap
, state
.miterLimit
,
1690 state
.dash
.Length(),
1691 state
.dash
.Elements(),
1693 DrawOptions(state
.globalAlpha
, UsedOperation()));
1698 CapStyle cap
= CapStyle::BUTT
;
1699 if (state
.lineJoin
== JoinStyle::ROUND
) {
1700 cap
= CapStyle::ROUND
;
1702 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
1703 StrokeLine(Point(x
, y
), Point(x
, y
+ h
),
1704 CanvasGeneralPattern().ForStyle(this, Style::STROKE
, mTarget
),
1705 StrokeOptions(state
.lineWidth
, state
.lineJoin
,
1706 cap
, state
.miterLimit
,
1707 state
.dash
.Length(),
1708 state
.dash
.Elements(),
1710 DrawOptions(state
.globalAlpha
, UsedOperation()));
1714 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
1715 StrokeRect(mgfx::Rect(x
, y
, w
, h
),
1716 CanvasGeneralPattern().ForStyle(this, Style::STROKE
, mTarget
),
1717 StrokeOptions(state
.lineWidth
, state
.lineJoin
,
1718 state
.lineCap
, state
.miterLimit
,
1719 state
.dash
.Length(),
1720 state
.dash
.Elements(),
1722 DrawOptions(state
.globalAlpha
, UsedOperation()));
1732 CanvasRenderingContext2D::BeginPath()
1735 mPathBuilder
= nullptr;
1736 mDSPathBuilder
= nullptr;
1737 mPathTransformWillUpdate
= false;
1741 CanvasRenderingContext2D::Fill(const CanvasWindingRule
& winding
)
1743 EnsureUserSpacePath(winding
);
1751 if (NeedToDrawShadow()) {
1752 bounds
= mPath
->GetBounds(mTarget
->GetTransform());
1755 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
1756 Fill(mPath
, CanvasGeneralPattern().ForStyle(this, Style::FILL
, mTarget
),
1757 DrawOptions(CurrentState().globalAlpha
, UsedOperation()));
1762 void CanvasRenderingContext2D::Fill(const CanvasPath
& path
, const CanvasWindingRule
& winding
)
1766 RefPtr
<gfx::Path
> gfxpath
= path
.GetPath(winding
, mTarget
);
1774 if (NeedToDrawShadow()) {
1775 bounds
= gfxpath
->GetBounds(mTarget
->GetTransform());
1778 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
1779 Fill(gfxpath
, CanvasGeneralPattern().ForStyle(this, Style::FILL
, mTarget
),
1780 DrawOptions(CurrentState().globalAlpha
, UsedOperation()));
1786 CanvasRenderingContext2D::Stroke()
1788 EnsureUserSpacePath();
1794 const ContextState
&state
= CurrentState();
1796 StrokeOptions
strokeOptions(state
.lineWidth
, state
.lineJoin
,
1797 state
.lineCap
, state
.miterLimit
,
1798 state
.dash
.Length(), state
.dash
.Elements(),
1802 if (NeedToDrawShadow()) {
1804 mPath
->GetStrokedBounds(strokeOptions
, mTarget
->GetTransform());
1807 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
1808 Stroke(mPath
, CanvasGeneralPattern().ForStyle(this, Style::STROKE
, mTarget
),
1809 strokeOptions
, DrawOptions(state
.globalAlpha
, UsedOperation()));
1815 CanvasRenderingContext2D::Stroke(const CanvasPath
& path
)
1819 RefPtr
<gfx::Path
> gfxpath
= path
.GetPath(CanvasWindingRule::Nonzero
, mTarget
);
1825 const ContextState
&state
= CurrentState();
1827 StrokeOptions
strokeOptions(state
.lineWidth
, state
.lineJoin
,
1828 state
.lineCap
, state
.miterLimit
,
1829 state
.dash
.Length(), state
.dash
.Elements(),
1833 if (NeedToDrawShadow()) {
1835 gfxpath
->GetStrokedBounds(strokeOptions
, mTarget
->GetTransform());
1838 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
1839 Stroke(gfxpath
, CanvasGeneralPattern().ForStyle(this, Style::STROKE
, mTarget
),
1840 strokeOptions
, DrawOptions(state
.globalAlpha
, UsedOperation()));
1845 void CanvasRenderingContext2D::DrawFocusIfNeeded(mozilla::dom::Element
& aElement
)
1847 EnsureUserSpacePath();
1853 if(DrawCustomFocusRing(aElement
)) {
1856 // set state to conforming focus state
1857 ContextState
& state
= CurrentState();
1858 state
.globalAlpha
= 1.0;
1859 state
.shadowBlur
= 0;
1860 state
.shadowOffset
.x
= 0;
1861 state
.shadowOffset
.y
= 0;
1862 state
.op
= mozilla::gfx::CompositionOp::OP_OVER
;
1864 state
.lineCap
= CapStyle::BUTT
;
1865 state
.lineJoin
= mozilla::gfx::JoinStyle::MITER_OR_BEVEL
;
1866 state
.lineWidth
= 1;
1867 CurrentState().dash
.Clear();
1869 // color and style of the rings is the same as for image maps
1870 // set the background focus color
1871 CurrentState().SetColorStyle(Style::STROKE
, NS_RGBA(255, 255, 255, 255));
1872 // draw the focus ring
1875 // set dashing for foreground
1876 FallibleTArray
<mozilla::gfx::Float
>& dash
= CurrentState().dash
;
1877 dash
.AppendElement(1);
1878 dash
.AppendElement(1);
1880 // set the foreground focus color
1881 CurrentState().SetColorStyle(Style::STROKE
, NS_RGBA(0,0,0, 255));
1882 // draw the focus ring
1889 bool CanvasRenderingContext2D::DrawCustomFocusRing(mozilla::dom::Element
& aElement
)
1891 EnsureUserSpacePath();
1893 HTMLCanvasElement
* canvas
= GetCanvas();
1895 if (!canvas
|| !nsContentUtils::ContentIsDescendantOf(&aElement
, canvas
)) {
1899 nsIFocusManager
* fm
= nsFocusManager::GetFocusManager();
1901 // check that the element i focused
1902 nsCOMPtr
<nsIDOMElement
> focusedElement
;
1903 fm
->GetFocusedElement(getter_AddRefs(focusedElement
));
1904 if (SameCOMIdentity(aElement
.AsDOMNode(), focusedElement
)) {
1905 nsPIDOMWindow
*window
= aElement
.OwnerDoc()->GetWindow();
1907 return window
->ShouldShowFocusRing();
1916 CanvasRenderingContext2D::Clip(const CanvasWindingRule
& winding
)
1918 EnsureUserSpacePath(winding
);
1924 mTarget
->PushClip(mPath
);
1925 CurrentState().clipsPushed
.push_back(mPath
);
1929 CanvasRenderingContext2D::Clip(const CanvasPath
& path
, const CanvasWindingRule
& winding
)
1933 RefPtr
<gfx::Path
> gfxpath
= path
.GetPath(winding
, mTarget
);
1939 mTarget
->PushClip(gfxpath
);
1940 CurrentState().clipsPushed
.push_back(gfxpath
);
1944 CanvasRenderingContext2D::ArcTo(double x1
, double y1
, double x2
,
1945 double y2
, double radius
,
1949 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
1953 EnsureWritablePath();
1955 // Current point in user space!
1958 p0
= mPathBuilder
->CurrentPoint();
1960 Matrix invTransform
= mTarget
->GetTransform();
1961 if (!invTransform
.Invert()) {
1965 p0
= invTransform
* mDSPathBuilder
->CurrentPoint();
1971 // Execute these calculations in double precision to avoid cumulative
1973 double dir
, a2
, b2
, c2
, cosx
, sinx
, d
, anx
, any
,
1974 bnx
, bny
, x3
, y3
, x4
, y4
, cx
, cy
, angle0
, angle1
;
1977 if (p0
== p1
|| p1
== p2
|| radius
== 0) {
1982 // Check for colinearity
1983 dir
= (p2
.x
- p1
.x
) * (p0
.y
- p1
.y
) + (p2
.y
- p1
.y
) * (p1
.x
- p0
.x
);
1990 // XXX - Math for this code was already available from the non-azure code
1991 // and would be well tested. Perhaps converting to bezier directly might
1992 // be more efficient longer run.
1993 a2
= (p0
.x
-x1
)*(p0
.x
-x1
) + (p0
.y
-y1
)*(p0
.y
-y1
);
1994 b2
= (x1
-x2
)*(x1
-x2
) + (y1
-y2
)*(y1
-y2
);
1995 c2
= (p0
.x
-x2
)*(p0
.x
-x2
) + (p0
.y
-y2
)*(p0
.y
-y2
);
1996 cosx
= (a2
+b2
-c2
)/(2*sqrt(a2
*b2
));
1998 sinx
= sqrt(1 - cosx
*cosx
);
1999 d
= radius
/ ((1 - cosx
) / sinx
);
2001 anx
= (x1
-p0
.x
) / sqrt(a2
);
2002 any
= (y1
-p0
.y
) / sqrt(a2
);
2003 bnx
= (x1
-x2
) / sqrt(b2
);
2004 bny
= (y1
-y2
) / sqrt(b2
);
2009 anticlockwise
= (dir
< 0);
2010 cx
= x3
+ any
*radius
*(anticlockwise
? 1 : -1);
2011 cy
= y3
- anx
*radius
*(anticlockwise
? 1 : -1);
2012 angle0
= atan2((y3
-cy
), (x3
-cx
));
2013 angle1
= atan2((y4
-cy
), (x4
-cx
));
2018 Arc(cx
, cy
, radius
, angle0
, angle1
, anticlockwise
, error
);
2022 CanvasRenderingContext2D::Arc(double x
, double y
, double r
,
2023 double startAngle
, double endAngle
,
2024 bool anticlockwise
, ErrorResult
& error
)
2027 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
2031 EnsureWritablePath();
2033 ArcToBezier(this, Point(x
, y
), Size(r
, r
), startAngle
, endAngle
, anticlockwise
);
2037 CanvasRenderingContext2D::Rect(double x
, double y
, double w
, double h
)
2039 EnsureWritablePath();
2042 mPathBuilder
->MoveTo(Point(x
, y
));
2043 mPathBuilder
->LineTo(Point(x
+ w
, y
));
2044 mPathBuilder
->LineTo(Point(x
+ w
, y
+ h
));
2045 mPathBuilder
->LineTo(Point(x
, y
+ h
));
2046 mPathBuilder
->Close();
2048 mDSPathBuilder
->MoveTo(mTarget
->GetTransform() * Point(x
, y
));
2049 mDSPathBuilder
->LineTo(mTarget
->GetTransform() * Point(x
+ w
, y
));
2050 mDSPathBuilder
->LineTo(mTarget
->GetTransform() * Point(x
+ w
, y
+ h
));
2051 mDSPathBuilder
->LineTo(mTarget
->GetTransform() * Point(x
, y
+ h
));
2052 mDSPathBuilder
->Close();
2057 CanvasRenderingContext2D::EnsureWritablePath()
2059 if (mDSPathBuilder
) {
2063 FillRule fillRule
= CurrentState().fillRule
;
2066 if (mPathTransformWillUpdate
) {
2067 mPath
= mPathBuilder
->Finish();
2069 mPath
->TransformedCopyToBuilder(mPathToDS
, fillRule
);
2071 mPathBuilder
= nullptr;
2072 mPathTransformWillUpdate
= false;
2079 NS_ASSERTION(!mPathTransformWillUpdate
, "mPathTransformWillUpdate should be false, if all paths are null");
2080 mPathBuilder
= mTarget
->CreatePathBuilder(fillRule
);
2081 } else if (!mPathTransformWillUpdate
) {
2082 mPathBuilder
= mPath
->CopyToBuilder(fillRule
);
2085 mPath
->TransformedCopyToBuilder(mPathToDS
, fillRule
);
2086 mPathTransformWillUpdate
= false;
2092 CanvasRenderingContext2D::EnsureUserSpacePath(const CanvasWindingRule
& winding
)
2094 FillRule fillRule
= CurrentState().fillRule
;
2095 if(winding
== CanvasWindingRule::Evenodd
)
2096 fillRule
= FillRule::FILL_EVEN_ODD
;
2098 if (!mPath
&& !mPathBuilder
&& !mDSPathBuilder
) {
2100 mPathBuilder
= mTarget
->CreatePathBuilder(fillRule
);
2104 mPath
= mPathBuilder
->Finish();
2105 mPathBuilder
= nullptr;
2109 mPathTransformWillUpdate
) {
2111 mPath
->TransformedCopyToBuilder(mPathToDS
, fillRule
);
2113 mPathTransformWillUpdate
= false;
2116 if (mDSPathBuilder
) {
2117 RefPtr
<Path
> dsPath
;
2118 dsPath
= mDSPathBuilder
->Finish();
2119 mDSPathBuilder
= nullptr;
2121 Matrix inverse
= mTarget
->GetTransform();
2122 if (!inverse
.Invert()) {
2123 NS_WARNING("Could not invert transform");
2128 dsPath
->TransformedCopyToBuilder(inverse
, fillRule
);
2129 mPath
= mPathBuilder
->Finish();
2130 mPathBuilder
= nullptr;
2133 if (mPath
&& mPath
->GetFillRule() != fillRule
) {
2134 mPathBuilder
= mPath
->CopyToBuilder(fillRule
);
2135 mPath
= mPathBuilder
->Finish();
2136 mPathBuilder
= nullptr;
2139 NS_ASSERTION(mPath
, "mPath should exist");
2143 CanvasRenderingContext2D::TransformWillUpdate()
2147 // Store the matrix that would transform the current path to device
2149 if (mPath
|| mPathBuilder
) {
2150 if (!mPathTransformWillUpdate
) {
2151 // If the transform has already been updated, but a device space builder
2152 // has not been created yet mPathToDS contains the right transform to
2153 // transform the current mPath into device space.
2154 // We should leave it alone.
2155 mPathToDS
= mTarget
->GetTransform();
2157 mPathTransformWillUpdate
= true;
2166 * Helper function for SetFont that creates a style rule for the given font.
2167 * @param aFont The CSS font string
2168 * @param aNode The canvas element
2169 * @param aResult Pointer in which to place the new style rule.
2170 * @remark Assumes all pointer arguments are non-null.
2173 CreateFontStyleRule(const nsAString
& aFont
,
2175 StyleRule
** aResult
)
2177 nsRefPtr
<StyleRule
> rule
;
2180 nsIPrincipal
* principal
= aNode
->NodePrincipal();
2181 nsIDocument
* document
= aNode
->OwnerDoc();
2183 nsIURI
* docURL
= document
->GetDocumentURI();
2184 nsIURI
* baseURL
= document
->GetDocBaseURI();
2186 // Pass the CSS Loader object to the parser, to allow parser error reports
2187 // to include the outer window ID.
2188 nsCSSParser
parser(document
->CSSLoader());
2190 nsresult rv
= parser
.ParseStyleAttribute(EmptyString(), docURL
, baseURL
,
2191 principal
, getter_AddRefs(rule
));
2192 if (NS_FAILED(rv
)) {
2196 rv
= parser
.ParseProperty(eCSSProperty_font
, aFont
, docURL
, baseURL
,
2197 principal
, rule
->GetDeclaration(), &changed
,
2202 rv
= parser
.ParseProperty(eCSSProperty_line_height
,
2203 NS_LITERAL_STRING("normal"), docURL
, baseURL
,
2204 principal
, rule
->GetDeclaration(), &changed
,
2206 if (NS_FAILED(rv
)) {
2210 rule
->RuleMatched();
2212 rule
.forget(aResult
);
2217 CanvasRenderingContext2D::SetFont(const nsAString
& font
,
2221 * If font is defined with relative units (e.g. ems) and the parent
2222 * style context changes in between calls, setting the font to the
2223 * same value as previous could result in a different computed value,
2224 * so we cannot have the optimization where we check if the new font
2225 * string is equal to the old one.
2228 if (!mCanvasElement
&& !mDocShell
) {
2229 NS_WARNING("Canvas element must be non-null or a docshell must be provided");
2230 error
.Throw(NS_ERROR_FAILURE
);
2234 nsIPresShell
* presShell
= GetPresShell();
2236 error
.Throw(NS_ERROR_FAILURE
);
2239 nsIDocument
* document
= presShell
->GetDocument();
2241 nsRefPtr
<css::StyleRule
> rule
;
2242 error
= CreateFontStyleRule(font
, document
, getter_AddRefs(rule
));
2244 if (error
.Failed()) {
2248 css::Declaration
*declaration
= rule
->GetDeclaration();
2249 // The easiest way to see whether we got a syntax error or whether
2250 // we got 'inherit' or 'initial' is to look at font-size-adjust,
2251 // which the shorthand resets to either 'none' or
2252 // '-moz-system-font'.
2253 // We know the declaration is not !important, so we can use
2254 // GetNormalBlock().
2255 const nsCSSValue
*fsaVal
=
2256 declaration
->GetNormalBlock()->ValueFor(eCSSProperty_font_size_adjust
);
2257 if (!fsaVal
|| (fsaVal
->GetUnit() != eCSSUnit_None
&&
2258 fsaVal
->GetUnit() != eCSSUnit_System_Font
)) {
2259 // We got an all-property value or a syntax error. The spec says
2260 // this value must be ignored.
2264 nsTArray
< nsCOMPtr
<nsIStyleRule
> > rules
;
2265 rules
.AppendElement(rule
);
2267 nsStyleSet
* styleSet
= presShell
->StyleSet();
2269 // have to get a parent style context for inherit-like relative
2270 // values (2em, bolder, etc.)
2271 nsRefPtr
<nsStyleContext
> parentContext
;
2273 if (mCanvasElement
&& mCanvasElement
->IsInDoc()) {
2274 // inherit from the canvas element
2275 parentContext
= nsComputedDOMStyle::GetStyleContextForElement(
2280 // otherwise inherit from default (10px sans-serif)
2281 nsRefPtr
<css::StyleRule
> parentRule
;
2282 error
= CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"),
2284 getter_AddRefs(parentRule
));
2286 if (error
.Failed()) {
2290 nsTArray
< nsCOMPtr
<nsIStyleRule
> > parentRules
;
2291 parentRules
.AppendElement(parentRule
);
2292 parentContext
= styleSet
->ResolveStyleForRules(nullptr, parentRules
);
2295 if (!parentContext
) {
2296 error
.Throw(NS_ERROR_FAILURE
);
2300 // add a rule to prevent text zoom from affecting the style
2301 rules
.AppendElement(new nsDisableTextZoomStyleRule
);
2303 nsRefPtr
<nsStyleContext
> sc
=
2304 styleSet
->ResolveStyleForRules(parentContext
, rules
);
2306 error
.Throw(NS_ERROR_FAILURE
);
2310 const nsStyleFont
* fontStyle
= sc
->StyleFont();
2312 NS_ASSERTION(fontStyle
, "Could not obtain font style");
2314 nsIAtom
* language
= sc
->StyleFont()->mLanguage
;
2316 language
= presShell
->GetPresContext()->GetLanguageFromCharset();
2319 // use CSS pixels instead of dev pixels to avoid being affected by page zoom
2320 const uint32_t aupcp
= nsPresContext::AppUnitsPerCSSPixel();
2322 bool printerFont
= (presShell
->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview
||
2323 presShell
->GetPresContext()->Type() == nsPresContext::eContext_Print
);
2325 // Purposely ignore the font size that respects the user's minimum
2326 // font preference (fontStyle->mFont.size) in favor of the computed
2327 // size (fontStyle->mSize). See
2328 // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
2329 MOZ_ASSERT(!fontStyle
->mAllowZoom
,
2330 "expected text zoom to be disabled on this nsStyleFont");
2331 gfxFontStyle
style(fontStyle
->mFont
.style
,
2332 fontStyle
->mFont
.weight
,
2333 fontStyle
->mFont
.stretch
,
2334 NSAppUnitsToFloatPixels(fontStyle
->mSize
, float(aupcp
)),
2336 fontStyle
->mFont
.sizeAdjust
,
2337 fontStyle
->mFont
.systemFont
,
2339 fontStyle
->mFont
.synthesis
& NS_FONT_SYNTHESIS_WEIGHT
,
2340 fontStyle
->mFont
.synthesis
& NS_FONT_SYNTHESIS_STYLE
,
2341 fontStyle
->mFont
.languageOverride
);
2343 fontStyle
->mFont
.AddFontFeaturesToStyle(&style
);
2345 nsPresContext
*c
= presShell
->GetPresContext();
2346 CurrentState().fontGroup
=
2347 gfxPlatform::GetPlatform()->CreateFontGroup(fontStyle
->mFont
.fontlist
,
2349 c
->GetUserFontSet());
2350 NS_ASSERTION(CurrentState().fontGroup
, "Could not get font group");
2351 CurrentState().fontGroup
->SetTextPerfMetrics(c
->GetTextPerfMetrics());
2353 // The font getter is required to be reserialized based on what we
2354 // parsed (including having line-height removed). (Older drafts of
2355 // the spec required font sizes be converted to pixels, but that no
2356 // longer seems to be required.)
2357 declaration
->GetValue(eCSSProperty_font
, CurrentState().font
);
2361 CanvasRenderingContext2D::SetTextAlign(const nsAString
& ta
)
2363 if (ta
.EqualsLiteral("start"))
2364 CurrentState().textAlign
= TextAlign::START
;
2365 else if (ta
.EqualsLiteral("end"))
2366 CurrentState().textAlign
= TextAlign::END
;
2367 else if (ta
.EqualsLiteral("left"))
2368 CurrentState().textAlign
= TextAlign::LEFT
;
2369 else if (ta
.EqualsLiteral("right"))
2370 CurrentState().textAlign
= TextAlign::RIGHT
;
2371 else if (ta
.EqualsLiteral("center"))
2372 CurrentState().textAlign
= TextAlign::CENTER
;
2376 CanvasRenderingContext2D::GetTextAlign(nsAString
& ta
)
2378 switch (CurrentState().textAlign
)
2380 case TextAlign::START
:
2381 ta
.AssignLiteral("start");
2383 case TextAlign::END
:
2384 ta
.AssignLiteral("end");
2386 case TextAlign::LEFT
:
2387 ta
.AssignLiteral("left");
2389 case TextAlign::RIGHT
:
2390 ta
.AssignLiteral("right");
2392 case TextAlign::CENTER
:
2393 ta
.AssignLiteral("center");
2399 CanvasRenderingContext2D::SetTextBaseline(const nsAString
& tb
)
2401 if (tb
.EqualsLiteral("top"))
2402 CurrentState().textBaseline
= TextBaseline::TOP
;
2403 else if (tb
.EqualsLiteral("hanging"))
2404 CurrentState().textBaseline
= TextBaseline::HANGING
;
2405 else if (tb
.EqualsLiteral("middle"))
2406 CurrentState().textBaseline
= TextBaseline::MIDDLE
;
2407 else if (tb
.EqualsLiteral("alphabetic"))
2408 CurrentState().textBaseline
= TextBaseline::ALPHABETIC
;
2409 else if (tb
.EqualsLiteral("ideographic"))
2410 CurrentState().textBaseline
= TextBaseline::IDEOGRAPHIC
;
2411 else if (tb
.EqualsLiteral("bottom"))
2412 CurrentState().textBaseline
= TextBaseline::BOTTOM
;
2416 CanvasRenderingContext2D::GetTextBaseline(nsAString
& tb
)
2418 switch (CurrentState().textBaseline
)
2420 case TextBaseline::TOP
:
2421 tb
.AssignLiteral("top");
2423 case TextBaseline::HANGING
:
2424 tb
.AssignLiteral("hanging");
2426 case TextBaseline::MIDDLE
:
2427 tb
.AssignLiteral("middle");
2429 case TextBaseline::ALPHABETIC
:
2430 tb
.AssignLiteral("alphabetic");
2432 case TextBaseline::IDEOGRAPHIC
:
2433 tb
.AssignLiteral("ideographic");
2435 case TextBaseline::BOTTOM
:
2436 tb
.AssignLiteral("bottom");
2442 * Helper function that replaces the whitespace characters in a string
2443 * with U+0020 SPACE. The whitespace characters are defined as U+0020 SPACE,
2444 * U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000B LINE
2445 * TABULATION, U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).
2446 * @param str The string whose whitespace characters to replace.
2449 TextReplaceWhitespaceCharacters(nsAutoString
& str
)
2451 str
.ReplaceChar("\x09\x0A\x0B\x0C\x0D", char16_t(' '));
2455 CanvasRenderingContext2D::FillText(const nsAString
& text
, double x
,
2457 const Optional
<double>& maxWidth
,
2460 error
= DrawOrMeasureText(text
, x
, y
, maxWidth
, TextDrawOperation::FILL
, nullptr);
2464 CanvasRenderingContext2D::StrokeText(const nsAString
& text
, double x
,
2466 const Optional
<double>& maxWidth
,
2469 error
= DrawOrMeasureText(text
, x
, y
, maxWidth
, TextDrawOperation::STROKE
, nullptr);
2473 CanvasRenderingContext2D::MeasureText(const nsAString
& rawText
,
2477 Optional
<double> maxWidth
;
2478 error
= DrawOrMeasureText(rawText
, 0, 0, maxWidth
, TextDrawOperation::MEASURE
, &width
);
2479 if (error
.Failed()) {
2483 return new TextMetrics(width
);
2487 CanvasRenderingContext2D::AddHitRegion(const HitRegionOptions
& options
, ErrorResult
& error
)
2489 // check if the path is valid
2490 EnsureUserSpacePath(CanvasWindingRule::Nonzero
);
2492 error
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
2496 // get the bounds of the current path. They are relative to the canvas
2497 mgfx::Rect
bounds(mPath
->GetBounds(mTarget
->GetTransform()));
2498 if ((bounds
.width
== 0) || (bounds
.height
== 0) || !bounds
.IsFinite()) {
2499 // The specified region has no pixels.
2500 error
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
2504 // remove old hit region first
2505 RemoveHitRegion(options
.mId
);
2507 if (options
.mControl
) {
2508 // also remove regions with this control
2509 for (size_t x
= 0; x
< mHitRegionsOptions
.Length(); x
++) {
2510 RegionInfo
& info
= mHitRegionsOptions
[x
];
2511 if (info
.mElement
== options
.mControl
) {
2512 mHitRegionsOptions
.RemoveElementAt(x
);
2516 #ifdef ACCESSIBILITY
2517 options
.mControl
->SetProperty(nsGkAtoms::hitregion
, new bool(true),
2518 nsINode::DeleteProperty
<bool>);
2522 // finally, add the region to the list
2524 info
.mId
= options
.mId
;
2525 info
.mElement
= options
.mControl
;
2526 RefPtr
<PathBuilder
> pathBuilder
= mPath
->TransformedCopyToBuilder(mTarget
->GetTransform());
2527 info
.mPath
= pathBuilder
->Finish();
2529 mHitRegionsOptions
.InsertElementAt(0, info
);
2533 CanvasRenderingContext2D::RemoveHitRegion(const nsAString
& id
)
2535 if (id
.Length() == 0) {
2539 for (size_t x
= 0; x
< mHitRegionsOptions
.Length(); x
++) {
2540 RegionInfo
& info
= mHitRegionsOptions
[x
];
2541 if (info
.mId
== id
) {
2542 mHitRegionsOptions
.RemoveElementAt(x
);
2550 CanvasRenderingContext2D::GetHitRegionRect(Element
* aElement
, nsRect
& aRect
)
2552 for (unsigned int x
= 0; x
< mHitRegionsOptions
.Length(); x
++) {
2553 RegionInfo
& info
= mHitRegionsOptions
[x
];
2554 if (info
.mElement
== aElement
) {
2555 mgfx::Rect
bounds(info
.mPath
->GetBounds());
2556 gfxRect
rect(bounds
.x
, bounds
.y
, bounds
.width
, bounds
.height
);
2557 aRect
= nsLayoutUtils::RoundGfxRectToAppRect(rect
, AppUnitsPerCSSPixel());
2567 * Used for nsBidiPresUtils::ProcessText
2569 struct MOZ_STACK_CLASS CanvasBidiProcessor
: public nsBidiPresUtils::BidiProcessor
2571 typedef CanvasRenderingContext2D::ContextState ContextState
;
2573 virtual void SetText(const char16_t
* text
, int32_t length
, nsBidiDirection direction
)
2575 mFontgrp
->UpdateFontList(); // ensure user font generation is current
2576 mTextRun
= mFontgrp
->MakeTextRun(text
,
2579 mAppUnitsPerDevPixel
,
2580 direction
==NSBIDI_RTL
? gfxTextRunFactory::TEXT_IS_RTL
: 0);
2583 virtual nscoord
GetWidth()
2585 gfxTextRun::Metrics textRunMetrics
= mTextRun
->MeasureText(0,
2586 mTextRun
->GetLength(),
2587 mDoMeasureBoundingBox
?
2588 gfxFont::TIGHT_INK_EXTENTS
:
2589 gfxFont::LOOSE_INK_EXTENTS
,
2593 // this only measures the height; the total width is gotten from the
2594 // the return value of ProcessText.
2595 if (mDoMeasureBoundingBox
) {
2596 textRunMetrics
.mBoundingBox
.Scale(1.0 / mAppUnitsPerDevPixel
);
2597 mBoundingBox
= mBoundingBox
.Union(textRunMetrics
.mBoundingBox
);
2600 return NSToCoordRound(textRunMetrics
.mAdvanceWidth
);
2603 virtual void DrawText(nscoord xOffset
, nscoord width
)
2605 gfxPoint point
= mPt
;
2608 // offset is given in terms of left side of string
2609 if (mTextRun
->IsRightToLeft()) {
2610 // Bug 581092 - don't use rounded pixel width to advance to
2611 // right-hand end of run, because this will cause different
2612 // glyph positioning for LTR vs RTL drawing of the same
2613 // glyph string on OS X and DWrite where textrun widths may
2614 // involve fractional pixels.
2615 gfxTextRun::Metrics textRunMetrics
=
2616 mTextRun
->MeasureText(0,
2617 mTextRun
->GetLength(),
2618 mDoMeasureBoundingBox
?
2619 gfxFont::TIGHT_INK_EXTENTS
:
2620 gfxFont::LOOSE_INK_EXTENTS
,
2623 point
.x
+= textRunMetrics
.mAdvanceWidth
;
2625 // point.x += width * mAppUnitsPerDevPixel;
2626 // TODO: restore this if/when we move to fractional coords
2627 // throughout the text layout process
2631 const gfxTextRun::GlyphRun
*runs
= mTextRun
->GetGlyphRuns(&numRuns
);
2632 const int32_t appUnitsPerDevUnit
= mAppUnitsPerDevPixel
;
2633 const double devUnitsPerAppUnit
= 1.0/double(appUnitsPerDevUnit
);
2634 Point baselineOrigin
=
2635 Point(point
.x
* devUnitsPerAppUnit
, point
.y
* devUnitsPerAppUnit
);
2637 float advanceSum
= 0;
2639 mCtx
->EnsureTarget();
2640 for (uint32_t c
= 0; c
< numRuns
; c
++) {
2641 gfxFont
*font
= runs
[c
].mFont
;
2642 uint32_t endRun
= 0;
2643 if (c
+ 1 < numRuns
) {
2644 endRun
= runs
[c
+ 1].mCharacterOffset
;
2646 endRun
= mTextRun
->GetLength();
2649 const gfxTextRun::CompressedGlyph
*glyphs
= mTextRun
->GetCharacterGlyphs();
2651 RefPtr
<ScaledFont
> scaledFont
=
2652 gfxPlatform::GetPlatform()->GetScaledFontForFont(mCtx
->mTarget
, font
);
2655 // This can occur when something switched DirectWrite off.
2659 RefPtr
<GlyphRenderingOptions
> renderingOptions
= font
->GetGlyphRenderingOptions();
2663 std::vector
<Glyph
> glyphBuf
;
2665 for (uint32_t i
= runs
[c
].mCharacterOffset
; i
< endRun
; i
++) {
2667 if (glyphs
[i
].IsSimpleGlyph()) {
2668 newGlyph
.mIndex
= glyphs
[i
].GetSimpleGlyph();
2669 if (mTextRun
->IsRightToLeft()) {
2670 newGlyph
.mPosition
.x
= baselineOrigin
.x
- advanceSum
-
2671 glyphs
[i
].GetSimpleAdvance() * devUnitsPerAppUnit
;
2673 newGlyph
.mPosition
.x
= baselineOrigin
.x
+ advanceSum
;
2675 newGlyph
.mPosition
.y
= baselineOrigin
.y
;
2676 advanceSum
+= glyphs
[i
].GetSimpleAdvance() * devUnitsPerAppUnit
;
2677 glyphBuf
.push_back(newGlyph
);
2681 if (!glyphs
[i
].GetGlyphCount()) {
2685 gfxTextRun::DetailedGlyph
*detailedGlyphs
=
2686 mTextRun
->GetDetailedGlyphs(i
);
2688 if (glyphs
[i
].IsMissing()) {
2689 newGlyph
.mIndex
= 0;
2690 if (mTextRun
->IsRightToLeft()) {
2691 newGlyph
.mPosition
.x
= baselineOrigin
.x
- advanceSum
-
2692 detailedGlyphs
[0].mAdvance
* devUnitsPerAppUnit
;
2694 newGlyph
.mPosition
.x
= baselineOrigin
.x
+ advanceSum
;
2696 newGlyph
.mPosition
.y
= baselineOrigin
.y
;
2697 advanceSum
+= detailedGlyphs
[0].mAdvance
* devUnitsPerAppUnit
;
2698 glyphBuf
.push_back(newGlyph
);
2702 for (uint32_t c
= 0; c
< glyphs
[i
].GetGlyphCount(); c
++) {
2703 newGlyph
.mIndex
= detailedGlyphs
[c
].mGlyphID
;
2704 if (mTextRun
->IsRightToLeft()) {
2705 newGlyph
.mPosition
.x
= baselineOrigin
.x
+ detailedGlyphs
[c
].mXOffset
* devUnitsPerAppUnit
-
2706 advanceSum
- detailedGlyphs
[c
].mAdvance
* devUnitsPerAppUnit
;
2708 newGlyph
.mPosition
.x
= baselineOrigin
.x
+ detailedGlyphs
[c
].mXOffset
* devUnitsPerAppUnit
+ advanceSum
;
2710 newGlyph
.mPosition
.y
= baselineOrigin
.y
+ detailedGlyphs
[c
].mYOffset
* devUnitsPerAppUnit
;
2711 glyphBuf
.push_back(newGlyph
);
2712 advanceSum
+= detailedGlyphs
[c
].mAdvance
* devUnitsPerAppUnit
;
2716 if (!glyphBuf
.size()) {
2717 // This may happen for glyph runs for a 0 size font.
2721 buffer
.mGlyphs
= &glyphBuf
.front();
2722 buffer
.mNumGlyphs
= glyphBuf
.size();
2724 Rect bounds
= mCtx
->mTarget
->GetTransform().
2725 TransformBounds(Rect(mBoundingBox
.x
, mBoundingBox
.y
,
2726 mBoundingBox
.width
, mBoundingBox
.height
));
2727 if (mOp
== CanvasRenderingContext2D::TextDrawOperation::FILL
) {
2728 AdjustedTarget(mCtx
, &bounds
)->
2729 FillGlyphs(scaledFont
, buffer
,
2730 CanvasGeneralPattern().
2731 ForStyle(mCtx
, CanvasRenderingContext2D::Style::FILL
, mCtx
->mTarget
),
2732 DrawOptions(mState
->globalAlpha
, mCtx
->UsedOperation()),
2734 } else if (mOp
== CanvasRenderingContext2D::TextDrawOperation::STROKE
) {
2735 // stroke glyphs one at a time to avoid poor CoreGraphics performance
2736 // when stroking a path with a very large number of points
2737 buffer
.mGlyphs
= &glyphBuf
.front();
2738 buffer
.mNumGlyphs
= 1;
2739 const ContextState
& state
= *mState
;
2740 AdjustedTarget
target(mCtx
, &bounds
);
2741 const StrokeOptions
strokeOpts(state
.lineWidth
, state
.lineJoin
,
2742 state
.lineCap
, state
.miterLimit
,
2743 state
.dash
.Length(),
2744 state
.dash
.Elements(),
2746 CanvasGeneralPattern cgp
;
2747 const Pattern
& patForStyle
2748 (cgp
.ForStyle(mCtx
, CanvasRenderingContext2D::Style::STROKE
, mCtx
->mTarget
));
2749 const DrawOptions
drawOpts(state
.globalAlpha
, mCtx
->UsedOperation());
2751 for (unsigned i
= glyphBuf
.size(); i
> 0; --i
) {
2752 RefPtr
<Path
> path
= scaledFont
->GetPathForGlyphs(buffer
, mCtx
->mTarget
);
2753 target
->Stroke(path
, patForStyle
, strokeOpts
, drawOpts
);
2761 nsAutoPtr
<gfxTextRun
> mTextRun
;
2763 // pointer to a screen reference context used to measure text and such
2764 nsRefPtr
<gfxContext
> mThebes
;
2766 // Pointer to the draw target we should fill our text to
2767 CanvasRenderingContext2D
*mCtx
;
2769 // position of the left side of the string, alphabetic baseline
2773 gfxFontGroup
* mFontgrp
;
2775 // dev pixel conversion factor
2776 int32_t mAppUnitsPerDevPixel
;
2778 // operation (fill or stroke)
2779 CanvasRenderingContext2D::TextDrawOperation mOp
;
2782 ContextState
*mState
;
2784 // union of bounding boxes of all runs, needed for shadows
2785 gfxRect mBoundingBox
;
2787 // true iff the bounding box should be measured
2788 bool mDoMeasureBoundingBox
;
2792 CanvasRenderingContext2D::DrawOrMeasureText(const nsAString
& aRawText
,
2795 const Optional
<double>& aMaxWidth
,
2796 TextDrawOperation aOp
,
2801 // spec isn't clear on what should happen if aMaxWidth <= 0, so
2802 // treat it as an invalid argument
2803 // technically, 0 should be an invalid value as well, but 0 is the default
2804 // arg, and there is no way to tell if the default was used
2805 if (aMaxWidth
.WasPassed() && aMaxWidth
.Value() < 0)
2806 return NS_ERROR_INVALID_ARG
;
2808 if (!mCanvasElement
&& !mDocShell
) {
2809 NS_WARNING("Canvas element must be non-null or a docshell must be provided");
2810 return NS_ERROR_FAILURE
;
2813 nsCOMPtr
<nsIPresShell
> presShell
= GetPresShell();
2815 return NS_ERROR_FAILURE
;
2817 nsIDocument
* document
= presShell
->GetDocument();
2819 // replace all the whitespace characters with U+0020 SPACE
2820 nsAutoString
textToDraw(aRawText
);
2821 TextReplaceWhitespaceCharacters(textToDraw
);
2823 // for now, default to ltr if not in doc
2826 if (mCanvasElement
&& mCanvasElement
->IsInDoc()) {
2827 // try to find the closest context
2828 nsRefPtr
<nsStyleContext
> canvasStyle
=
2829 nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement
,
2833 return NS_ERROR_FAILURE
;
2836 isRTL
= canvasStyle
->StyleVisibility()->mDirection
==
2837 NS_STYLE_DIRECTION_RTL
;
2839 isRTL
= GET_BIDI_OPTION_DIRECTION(document
->GetBidiOptions()) == IBMBIDI_TEXTDIRECTION_RTL
;
2842 gfxFontGroup
* currentFontStyle
= GetCurrentFontStyle();
2843 NS_ASSERTION(currentFontStyle
, "font group is null");
2845 // ensure user font set is up to date
2847 SetUserFontSet(presShell
->GetPresContext()->GetUserFontSet());
2849 if (currentFontStyle
->GetStyle()->size
== 0.0F
) {
2856 const ContextState
&state
= CurrentState();
2858 // This is only needed to know if we can know the drawing bounding box easily.
2859 bool doDrawShadow
= NeedToDrawShadow();
2861 CanvasBidiProcessor processor
;
2863 GetAppUnitsValues(&processor
.mAppUnitsPerDevPixel
, nullptr);
2864 processor
.mPt
= gfxPoint(aX
, aY
);
2866 new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
2868 // If we don't have a target then we don't have a transform. A target won't
2869 // be needed in the case where we're measuring the text size. This allows
2870 // to avoid creating a target if it's only being used to measure text sizes.
2872 Matrix matrix
= mTarget
->GetTransform();
2873 processor
.mThebes
->SetMatrix(gfxMatrix(matrix
._11
, matrix
._12
, matrix
._21
, matrix
._22
, matrix
._31
, matrix
._32
));
2875 processor
.mCtx
= this;
2876 processor
.mOp
= aOp
;
2877 processor
.mBoundingBox
= gfxRect(0, 0, 0, 0);
2878 processor
.mDoMeasureBoundingBox
= doDrawShadow
|| !mIsEntireFrameInvalid
;
2879 processor
.mState
= &CurrentState();
2880 processor
.mFontgrp
= currentFontStyle
;
2882 nscoord totalWidthCoord
;
2884 // calls bidi algo twice since it needs the full text width and the
2885 // bounding boxes before rendering anything
2887 rv
= nsBidiPresUtils::ProcessText(textToDraw
.get(),
2888 textToDraw
.Length(),
2889 isRTL
? NSBIDI_RTL
: NSBIDI_LTR
,
2890 presShell
->GetPresContext(),
2892 nsBidiPresUtils::MODE_MEASURE
,
2897 if (NS_FAILED(rv
)) {
2901 float totalWidth
= float(totalWidthCoord
) / processor
.mAppUnitsPerDevPixel
;
2903 *aWidth
= totalWidth
;
2906 // if only measuring, don't need to do any more work
2907 if (aOp
==TextDrawOperation::MEASURE
) {
2911 // offset pt.x based on text align
2914 if (state
.textAlign
== TextAlign::CENTER
) {
2916 } else if (state
.textAlign
== TextAlign::LEFT
||
2917 (!isRTL
&& state
.textAlign
== TextAlign::START
) ||
2918 (isRTL
&& state
.textAlign
== TextAlign::END
)) {
2924 processor
.mPt
.x
-= anchorX
* totalWidth
;
2926 // offset pt.y based on text baseline
2927 processor
.mFontgrp
->UpdateFontList(); // ensure user font generation is current
2928 NS_ASSERTION(processor
.mFontgrp
->FontListLength()>0, "font group contains no fonts");
2929 const gfxFont::Metrics
& fontMetrics
= processor
.mFontgrp
->GetFontAt(0)->GetMetrics();
2933 switch (state
.textBaseline
)
2935 case TextBaseline::HANGING
:
2936 // fall through; best we can do with the information available
2937 case TextBaseline::TOP
:
2938 anchorY
= fontMetrics
.emAscent
;
2940 case TextBaseline::MIDDLE
:
2941 anchorY
= (fontMetrics
.emAscent
- fontMetrics
.emDescent
) * .5f
;
2943 case TextBaseline::IDEOGRAPHIC
:
2944 // fall through; best we can do with the information available
2945 case TextBaseline::ALPHABETIC
:
2948 case TextBaseline::BOTTOM
:
2949 anchorY
= -fontMetrics
.emDescent
;
2952 MOZ_CRASH("unexpected TextBaseline");
2955 processor
.mPt
.y
+= anchorY
;
2957 // correct bounding box to get it to be the correct size/position
2958 processor
.mBoundingBox
.width
= totalWidth
;
2959 processor
.mBoundingBox
.MoveBy(processor
.mPt
);
2961 processor
.mPt
.x
*= processor
.mAppUnitsPerDevPixel
;
2962 processor
.mPt
.y
*= processor
.mAppUnitsPerDevPixel
;
2965 Matrix oldTransform
= mTarget
->GetTransform();
2966 // if text is over aMaxWidth, then scale the text horizontally such that its
2967 // width is precisely aMaxWidth
2968 if (aMaxWidth
.WasPassed() && aMaxWidth
.Value() > 0 &&
2969 totalWidth
> aMaxWidth
.Value()) {
2970 Matrix newTransform
= oldTransform
;
2972 // Translate so that the anchor point is at 0,0, then scale and then
2974 newTransform
.Translate(aX
, 0);
2975 newTransform
.Scale(aMaxWidth
.Value() / totalWidth
, 1);
2976 newTransform
.Translate(-aX
, 0);
2977 /* we do this to avoid an ICE in the android compiler */
2978 Matrix androidCompilerBug
= newTransform
;
2979 mTarget
->SetTransform(androidCompilerBug
);
2982 // save the previous bounding box
2983 gfxRect boundingBox
= processor
.mBoundingBox
;
2985 // don't ever need to measure the bounding box twice
2986 processor
.mDoMeasureBoundingBox
= false;
2988 rv
= nsBidiPresUtils::ProcessText(textToDraw
.get(),
2989 textToDraw
.Length(),
2990 isRTL
? NSBIDI_RTL
: NSBIDI_LTR
,
2991 presShell
->GetPresContext(),
2993 nsBidiPresUtils::MODE_DRAW
,
3000 mTarget
->SetTransform(oldTransform
);
3002 if (aOp
== CanvasRenderingContext2D::TextDrawOperation::FILL
&&
3004 RedrawUser(boundingBox
);
3012 gfxFontGroup
*CanvasRenderingContext2D::GetCurrentFontStyle()
3014 // use lazy initilization for the font group since it's rather expensive
3015 if (!CurrentState().fontGroup
) {
3017 NS_NAMED_LITERAL_STRING(kDefaultFontStyle
, "10px sans-serif");
3018 static float kDefaultFontSize
= 10.0;
3019 SetFont(kDefaultFontStyle
, err
);
3022 style
.size
= kDefaultFontSize
;
3023 CurrentState().fontGroup
=
3024 gfxPlatform::GetPlatform()->CreateFontGroup(FontFamilyList(eFamily_sans_serif
),
3027 if (CurrentState().fontGroup
) {
3028 CurrentState().font
= kDefaultFontStyle
;
3030 nsIPresShell
* presShell
= GetPresShell();
3032 CurrentState().fontGroup
->SetTextPerfMetrics(
3033 presShell
->GetPresContext()->GetTextPerfMetrics());
3036 NS_ERROR("Default canvas font is invalid");
3042 return CurrentState().fontGroup
;
3050 CanvasRenderingContext2D::SetLineCap(const nsAString
& capstyle
)
3054 if (capstyle
.EqualsLiteral("butt")) {
3055 cap
= CapStyle::BUTT
;
3056 } else if (capstyle
.EqualsLiteral("round")) {
3057 cap
= CapStyle::ROUND
;
3058 } else if (capstyle
.EqualsLiteral("square")) {
3059 cap
= CapStyle::SQUARE
;
3061 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3065 CurrentState().lineCap
= cap
;
3069 CanvasRenderingContext2D::GetLineCap(nsAString
& capstyle
)
3071 switch (CurrentState().lineCap
) {
3072 case CapStyle::BUTT
:
3073 capstyle
.AssignLiteral("butt");
3075 case CapStyle::ROUND
:
3076 capstyle
.AssignLiteral("round");
3078 case CapStyle::SQUARE
:
3079 capstyle
.AssignLiteral("square");
3085 CanvasRenderingContext2D::SetLineJoin(const nsAString
& joinstyle
)
3089 if (joinstyle
.EqualsLiteral("round")) {
3090 j
= JoinStyle::ROUND
;
3091 } else if (joinstyle
.EqualsLiteral("bevel")) {
3092 j
= JoinStyle::BEVEL
;
3093 } else if (joinstyle
.EqualsLiteral("miter")) {
3094 j
= JoinStyle::MITER_OR_BEVEL
;
3096 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3100 CurrentState().lineJoin
= j
;
3104 CanvasRenderingContext2D::GetLineJoin(nsAString
& joinstyle
, ErrorResult
& error
)
3106 switch (CurrentState().lineJoin
) {
3107 case JoinStyle::ROUND
:
3108 joinstyle
.AssignLiteral("round");
3110 case JoinStyle::BEVEL
:
3111 joinstyle
.AssignLiteral("bevel");
3113 case JoinStyle::MITER_OR_BEVEL
:
3114 joinstyle
.AssignLiteral("miter");
3117 error
.Throw(NS_ERROR_FAILURE
);
3122 CanvasRenderingContext2D::SetMozDash(JSContext
* cx
,
3123 const JS::Value
& mozDash
,
3126 FallibleTArray
<Float
> dash
;
3127 error
= JSValToDashArray(cx
, mozDash
, dash
);
3128 if (!error
.Failed()) {
3129 ContextState
& state
= CurrentState();
3131 if (state
.dash
.IsEmpty()) {
3132 state
.dashOffset
= 0;
3138 CanvasRenderingContext2D::GetMozDash(JSContext
* cx
,
3139 JS::MutableHandle
<JS::Value
> retval
,
3142 DashArrayToJSVal(CurrentState().dash
, cx
, retval
, error
);
3146 CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset
)
3148 ContextState
& state
= CurrentState();
3149 if (!state
.dash
.IsEmpty()) {
3150 state
.dashOffset
= mozDashOffset
;
3155 CanvasRenderingContext2D::SetLineDash(const Sequence
<double>& aSegments
)
3157 FallibleTArray
<mozilla::gfx::Float
> dash
;
3159 for (uint32_t x
= 0; x
< aSegments
.Length(); x
++) {
3160 if (aSegments
[x
] < 0.0) {
3161 // Pattern elements must be finite "numbers" >= 0, with "finite"
3162 // taken care of by WebIDL
3165 dash
.AppendElement(aSegments
[x
]);
3167 if (aSegments
.Length() % 2) { // If the number of elements is odd, concatenate again
3168 for (uint32_t x
= 0; x
< aSegments
.Length(); x
++) {
3169 dash
.AppendElement(aSegments
[x
]);
3173 CurrentState().dash
= dash
;
3177 CanvasRenderingContext2D::GetLineDash(nsTArray
<double>& aSegments
) const {
3178 const FallibleTArray
<mozilla::gfx::Float
>& dash
= CurrentState().dash
;
3181 for (uint32_t x
= 0; x
< dash
.Length(); x
++) {
3182 aSegments
.AppendElement(dash
[x
]);
3187 CanvasRenderingContext2D::SetLineDashOffset(double mOffset
) {
3188 CurrentState().dashOffset
= mOffset
;
3192 CanvasRenderingContext2D::LineDashOffset() const {
3193 return CurrentState().dashOffset
;
3197 CanvasRenderingContext2D::IsPointInPath(double x
, double y
, const CanvasWindingRule
& winding
)
3199 if (!FloatValidate(x
,y
)) {
3203 EnsureUserSpacePath(winding
);
3208 if (mPathTransformWillUpdate
) {
3209 return mPath
->ContainsPoint(Point(x
, y
), mPathToDS
);
3212 return mPath
->ContainsPoint(Point(x
, y
), mTarget
->GetTransform());
3215 bool CanvasRenderingContext2D::IsPointInPath(const CanvasPath
& mPath
, double x
, double y
, const CanvasWindingRule
& mWinding
)
3217 if (!FloatValidate(x
,y
)) {
3222 RefPtr
<gfx::Path
> tempPath
= mPath
.GetPath(mWinding
, mTarget
);
3224 return tempPath
->ContainsPoint(Point(x
, y
), mTarget
->GetTransform());
3228 CanvasRenderingContext2D::IsPointInStroke(double x
, double y
)
3230 if (!FloatValidate(x
,y
)) {
3234 EnsureUserSpacePath();
3239 const ContextState
&state
= CurrentState();
3241 StrokeOptions
strokeOptions(state
.lineWidth
,
3245 state
.dash
.Length(),
3246 state
.dash
.Elements(),
3249 if (mPathTransformWillUpdate
) {
3250 return mPath
->StrokeContainsPoint(strokeOptions
, Point(x
, y
), mPathToDS
);
3252 return mPath
->StrokeContainsPoint(strokeOptions
, Point(x
, y
), mTarget
->GetTransform());
3255 bool CanvasRenderingContext2D::IsPointInStroke(const CanvasPath
& mPath
, double x
, double y
)
3257 if (!FloatValidate(x
,y
)) {
3262 RefPtr
<gfx::Path
> tempPath
= mPath
.GetPath(CanvasWindingRule::Nonzero
, mTarget
);
3264 const ContextState
&state
= CurrentState();
3266 StrokeOptions
strokeOptions(state
.lineWidth
,
3270 state
.dash
.Length(),
3271 state
.dash
.Elements(),
3274 return tempPath
->StrokeContainsPoint(strokeOptions
, Point(x
, y
), mTarget
->GetTransform());
3277 // Acts like nsLayoutUtils::SurfaceFromElement, but it'll attempt
3278 // to pull a SourceSurface from our cache. This allows us to avoid
3279 // reoptimizing surfaces if content and canvas backends are different.
3280 nsLayoutUtils::SurfaceFromElementResult
3281 CanvasRenderingContext2D::CachedSurfaceFromElement(Element
* aElement
)
3283 nsLayoutUtils::SurfaceFromElementResult res
;
3285 nsCOMPtr
<nsIImageLoadingContent
> imageLoader
= do_QueryInterface(aElement
);
3290 nsCOMPtr
<imgIRequest
> imgRequest
;
3291 imageLoader
->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST
,
3292 getter_AddRefs(imgRequest
));
3298 if (NS_FAILED(imgRequest
->GetImageStatus(&status
)) ||
3299 !(status
& imgIRequest::STATUS_LOAD_COMPLETE
)) {
3303 nsCOMPtr
<nsIPrincipal
> principal
;
3304 if (NS_FAILED(imgRequest
->GetImagePrincipal(getter_AddRefs(principal
))) ||
3309 res
.mSourceSurface
= CanvasImageCache::SimpleLookup(aElement
);
3310 if (!res
.mSourceSurface
) {
3314 int32_t corsmode
= imgIRequest::CORS_NONE
;
3315 if (NS_SUCCEEDED(imgRequest
->GetCORSMode(&corsmode
))) {
3316 res
.mCORSUsed
= corsmode
!= imgIRequest::CORS_NONE
;
3319 res
.mSize
= ThebesIntSize(res
.mSourceSurface
->GetSize());
3320 res
.mPrincipal
= principal
.forget();
3321 res
.mIsWriteOnly
= false;
3322 res
.mImageRequest
= imgRequest
.forget();
3331 // drawImage(in HTMLImageElement image, in float dx, in float dy);
3332 // -- render image from 0,0 at dx,dy top-left coords
3333 // drawImage(in HTMLImageElement image, in float dx, in float dy, in float sw, in float sh);
3334 // -- render image from 0,0 at dx,dy top-left coords clipping it to sw,sh
3335 // drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh);
3336 // -- render the region defined by (sx,sy,sw,wh) in image-local space into the region (dx,dy,dw,dh) on the canvas
3338 // If only dx and dy are passed in then optional_argc should be 0. If only
3339 // dx, dy, dw and dh are passed in then optional_argc should be 2. The only
3340 // other valid value for optional_argc is 6 if sx, sy, sw, sh, dx, dy, dw and dh
3341 // are all passed in.
3344 CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement
& image
,
3345 double sx
, double sy
, double sw
,
3346 double sh
, double dx
, double dy
,
3347 double dw
, double dh
,
3348 uint8_t optional_argc
,
3351 MOZ_ASSERT(optional_argc
== 0 || optional_argc
== 2 || optional_argc
== 6);
3353 RefPtr
<SourceSurface
> srcSurf
;
3359 if (image
.IsHTMLCanvasElement()) {
3360 HTMLCanvasElement
* canvas
= &image
.GetAsHTMLCanvasElement();
3362 nsIntSize size
= canvas
->GetSize();
3363 if (size
.width
== 0 || size
.height
== 0) {
3364 error
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
3368 if (image
.IsHTMLImageElement()) {
3369 HTMLImageElement
* img
= &image
.GetAsHTMLImageElement();
3372 HTMLVideoElement
* video
= &image
.GetAsHTMLVideoElement();
3377 CanvasImageCache::Lookup(element
, mCanvasElement
, &imgSize
);
3380 nsLayoutUtils::DirectDrawInfo drawInfo
;
3383 // The canvas spec says that drawImage should draw the first frame
3384 // of animated images. We also don't want to rasterize vector images.
3385 uint32_t sfeFlags
= nsLayoutUtils::SFE_WANT_FIRST_FRAME
|
3386 nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS
;
3387 // The cache lookup can miss even if the image is already in the cache
3388 // if the image is coming from a different element or cached for a
3389 // different canvas. This covers the case when we miss due to caching
3390 // for a different canvas, but CanvasImageCache should be fixed if we
3391 // see misses due to different elements drawing the same image.
3392 nsLayoutUtils::SurfaceFromElementResult res
=
3393 CachedSurfaceFromElement(element
);
3394 if (!res
.mSourceSurface
)
3395 res
= nsLayoutUtils::SurfaceFromElement(element
, sfeFlags
, mTarget
);
3397 if (!res
.mSourceSurface
&& !res
.mDrawInfo
.mImgContainer
) {
3398 // Spec says to silently do nothing if the element is still loading.
3399 if (!res
.mIsStillLoading
) {
3400 error
.Throw(NS_ERROR_NOT_AVAILABLE
);
3405 imgSize
= res
.mSize
;
3407 // Scale sw/sh based on aspect ratio
3408 if (image
.IsHTMLVideoElement()) {
3409 HTMLVideoElement
* video
= &image
.GetAsHTMLVideoElement();
3410 int32_t displayWidth
= video
->VideoWidth();
3411 int32_t displayHeight
= video
->VideoHeight();
3412 sw
*= (double)imgSize
.width
/ (double)displayWidth
;
3413 sh
*= (double)imgSize
.height
/ (double)displayHeight
;
3416 if (mCanvasElement
) {
3417 CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement
,
3418 res
.mPrincipal
, res
.mIsWriteOnly
,
3422 if (res
.mSourceSurface
) {
3423 if (res
.mImageRequest
) {
3424 CanvasImageCache::NotifyDrawImage(element
, mCanvasElement
, res
.mImageRequest
,
3425 res
.mSourceSurface
, imgSize
);
3428 srcSurf
= res
.mSourceSurface
;
3430 drawInfo
= res
.mDrawInfo
;
3434 if (optional_argc
== 0) {
3436 dw
= sw
= (double) imgSize
.width
;
3437 dh
= sh
= (double) imgSize
.height
;
3438 } else if (optional_argc
== 2) {
3440 sw
= (double) imgSize
.width
;
3441 sh
= (double) imgSize
.height
;
3444 if (sw
== 0.0 || sh
== 0.0) {
3445 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
3449 if (dw
== 0.0 || dh
== 0.0) {
3450 // not really failure, but nothing to do --
3451 // and noone likes a divide-by-zero
3455 if (sx
< 0.0 || sy
< 0.0 ||
3456 sw
< 0.0 || sw
> (double) imgSize
.width
||
3457 sh
< 0.0 || sh
> (double) imgSize
.height
||
3458 dw
< 0.0 || dh
< 0.0) {
3459 // XXX - Unresolved spec issues here, for now return error.
3460 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
3466 if (CurrentState().imageSmoothingEnabled
)
3467 filter
= mgfx::Filter::LINEAR
;
3469 filter
= mgfx::Filter::POINT
;
3473 if (NeedToDrawShadow()) {
3474 bounds
= mgfx::Rect(dx
, dy
, dw
, dh
);
3475 bounds
= mTarget
->GetTransform().TransformBounds(bounds
);
3479 AdjustedTarget(this, bounds
.IsEmpty() ? nullptr : &bounds
)->
3480 DrawSurface(srcSurf
,
3481 mgfx::Rect(dx
, dy
, dw
, dh
),
3482 mgfx::Rect(sx
, sy
, sw
, sh
),
3483 DrawSurfaceOptions(filter
),
3484 DrawOptions(CurrentState().globalAlpha
, UsedOperation()));
3486 DrawDirectlyToCanvas(drawInfo
, &bounds
,
3487 mgfx::Rect(dx
, dy
, dw
, dh
),
3488 mgfx::Rect(sx
, sy
, sw
, sh
),
3492 RedrawUser(gfxRect(dx
, dy
, dw
, dh
));
3496 CanvasRenderingContext2D::DrawDirectlyToCanvas(
3497 const nsLayoutUtils::DirectDrawInfo
& image
,
3503 MOZ_ASSERT(src
.width
> 0 && src
.height
> 0,
3504 "Need positive source width and height");
3506 gfxMatrix contextMatrix
;
3507 AdjustedTarget
tempTarget(this, bounds
->IsEmpty() ? nullptr: bounds
);
3509 // Get any existing transforms on the context, including transformations used
3510 // for context shadow.
3512 Matrix matrix
= tempTarget
->GetTransform();
3513 contextMatrix
= gfxMatrix(matrix
._11
, matrix
._12
, matrix
._21
,
3514 matrix
._22
, matrix
._31
, matrix
._32
);
3516 gfxSize
contextScale(contextMatrix
.ScaleFactors(true));
3518 // Scale the dest rect to include the context scale.
3519 dest
.Scale(contextScale
.width
, contextScale
.height
);
3521 // Scale the image size to the dest rect, and adjust the source rect to match.
3522 gfxSize
scale(dest
.width
/ src
.width
, dest
.height
/ src
.height
);
3523 nsIntSize
scaledImageSize(std::ceil(imgSize
.width
* scale
.width
),
3524 std::ceil(imgSize
.height
* scale
.height
));
3525 src
.Scale(scale
.width
, scale
.height
);
3527 // We're wrapping tempTarget's (our) DrawTarget here, so we need to restore
3528 // the matrix even though this is a temp gfxContext.
3529 AutoSaveTransform
autoSR(mTarget
);
3531 nsRefPtr
<gfxContext
> context
= new gfxContext(tempTarget
);
3532 context
->SetMatrix(contextMatrix
);
3533 context
->Scale(1.0 / contextScale
.width
, 1.0 / contextScale
.height
);
3534 context
->Translate(gfxPoint(dest
.x
- src
.x
, dest
.y
- src
.y
));
3536 // FLAG_CLAMP is added for increased performance, since we never tile here.
3537 uint32_t modifiedFlags
= image
.mDrawingFlags
| imgIContainer::FLAG_CLAMP
;
3539 SVGImageContext
svgContext(scaledImageSize
, Nothing(), CurrentState().globalAlpha
);
3541 nsresult rv
= image
.mImgContainer
->
3542 Draw(context
, scaledImageSize
,
3543 ImageRegion::Create(gfxRect(src
.x
, src
.y
, src
.width
, src
.height
)),
3544 image
.mWhichFrame
, GraphicsFilter::FILTER_GOOD
,
3545 Some(svgContext
), modifiedFlags
);
3547 NS_ENSURE_SUCCESS_VOID(rv
);
3551 IsStandardCompositeOp(CompositionOp op
)
3553 return (op
== CompositionOp::OP_SOURCE
||
3554 op
== CompositionOp::OP_ATOP
||
3555 op
== CompositionOp::OP_IN
||
3556 op
== CompositionOp::OP_OUT
||
3557 op
== CompositionOp::OP_OVER
||
3558 op
== CompositionOp::OP_DEST_IN
||
3559 op
== CompositionOp::OP_DEST_OUT
||
3560 op
== CompositionOp::OP_DEST_OVER
||
3561 op
== CompositionOp::OP_DEST_ATOP
||
3562 op
== CompositionOp::OP_ADD
||
3563 op
== CompositionOp::OP_XOR
);
3567 CanvasRenderingContext2D::SetGlobalCompositeOperation(const nsAString
& op
,
3570 CompositionOp comp_op
;
3572 #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
3573 if (op.EqualsLiteral(cvsop)) \
3574 comp_op = CompositionOp::OP_##op2d;
3576 CANVAS_OP_TO_GFX_OP("copy", SOURCE
)
3577 else CANVAS_OP_TO_GFX_OP("source-atop", ATOP
)
3578 else CANVAS_OP_TO_GFX_OP("source-in", IN
)
3579 else CANVAS_OP_TO_GFX_OP("source-out", OUT
)
3580 else CANVAS_OP_TO_GFX_OP("source-over", OVER
)
3581 else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN
)
3582 else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT
)
3583 else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER
)
3584 else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP
)
3585 else CANVAS_OP_TO_GFX_OP("lighter", ADD
)
3586 else CANVAS_OP_TO_GFX_OP("xor", XOR
)
3587 else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY
)
3588 else CANVAS_OP_TO_GFX_OP("screen", SCREEN
)
3589 else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY
)
3590 else CANVAS_OP_TO_GFX_OP("darken", DARKEN
)
3591 else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN
)
3592 else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE
)
3593 else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN
)
3594 else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT
)
3595 else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT
)
3596 else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE
)
3597 else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION
)
3598 else CANVAS_OP_TO_GFX_OP("hue", HUE
)
3599 else CANVAS_OP_TO_GFX_OP("saturation", SATURATION
)
3600 else CANVAS_OP_TO_GFX_OP("color", COLOR
)
3601 else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY
)
3602 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3605 if (!IsStandardCompositeOp(comp_op
)) {
3609 #undef CANVAS_OP_TO_GFX_OP
3610 CurrentState().op
= comp_op
;
3614 CanvasRenderingContext2D::GetGlobalCompositeOperation(nsAString
& op
,
3617 CompositionOp comp_op
= CurrentState().op
;
3619 #define CANVAS_OP_TO_GFX_OP(cvsop, op2d) \
3620 if (comp_op == CompositionOp::OP_##op2d) \
3621 op.AssignLiteral(cvsop);
3623 CANVAS_OP_TO_GFX_OP("copy", SOURCE
)
3624 else CANVAS_OP_TO_GFX_OP("destination-atop", DEST_ATOP
)
3625 else CANVAS_OP_TO_GFX_OP("destination-in", DEST_IN
)
3626 else CANVAS_OP_TO_GFX_OP("destination-out", DEST_OUT
)
3627 else CANVAS_OP_TO_GFX_OP("destination-over", DEST_OVER
)
3628 else CANVAS_OP_TO_GFX_OP("lighter", ADD
)
3629 else CANVAS_OP_TO_GFX_OP("source-atop", ATOP
)
3630 else CANVAS_OP_TO_GFX_OP("source-in", IN
)
3631 else CANVAS_OP_TO_GFX_OP("source-out", OUT
)
3632 else CANVAS_OP_TO_GFX_OP("source-over", OVER
)
3633 else CANVAS_OP_TO_GFX_OP("xor", XOR
)
3634 else CANVAS_OP_TO_GFX_OP("multiply", MULTIPLY
)
3635 else CANVAS_OP_TO_GFX_OP("screen", SCREEN
)
3636 else CANVAS_OP_TO_GFX_OP("overlay", OVERLAY
)
3637 else CANVAS_OP_TO_GFX_OP("darken", DARKEN
)
3638 else CANVAS_OP_TO_GFX_OP("lighten", LIGHTEN
)
3639 else CANVAS_OP_TO_GFX_OP("color-dodge", COLOR_DODGE
)
3640 else CANVAS_OP_TO_GFX_OP("color-burn", COLOR_BURN
)
3641 else CANVAS_OP_TO_GFX_OP("hard-light", HARD_LIGHT
)
3642 else CANVAS_OP_TO_GFX_OP("soft-light", SOFT_LIGHT
)
3643 else CANVAS_OP_TO_GFX_OP("difference", DIFFERENCE
)
3644 else CANVAS_OP_TO_GFX_OP("exclusion", EXCLUSION
)
3645 else CANVAS_OP_TO_GFX_OP("hue", HUE
)
3646 else CANVAS_OP_TO_GFX_OP("saturation", SATURATION
)
3647 else CANVAS_OP_TO_GFX_OP("color", COLOR
)
3648 else CANVAS_OP_TO_GFX_OP("luminosity", LUMINOSITY
)
3650 error
.Throw(NS_ERROR_FAILURE
);
3653 if (!IsStandardCompositeOp(comp_op
)) {
3657 #undef CANVAS_OP_TO_GFX_OP
3661 CanvasRenderingContext2D::DrawWindow(nsGlobalWindow
& window
, double x
,
3662 double y
, double w
, double h
,
3663 const nsAString
& bgColor
,
3664 uint32_t flags
, ErrorResult
& error
)
3666 // protect against too-large surfaces that will cause allocation
3667 // or overflow issues
3668 if (!gfxASurface::CheckSurfaceSize(gfxIntSize(int32_t(w
), int32_t(h
)),
3670 error
.Throw(NS_ERROR_FAILURE
);
3675 // We can't allow web apps to call this until we fix at least the
3676 // following potential security issues:
3677 // -- rendering cross-domain IFRAMEs and then extracting the results
3678 // -- rendering the user's theme and then extracting the results
3679 // -- rendering native anonymous content (e.g., file input paths;
3680 // scrollbars should be allowed)
3681 if (!nsContentUtils::IsCallerChrome()) {
3682 // not permitted to use DrawWindow
3683 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3684 error
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
3688 // Flush layout updates
3689 if (!(flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH
)) {
3690 nsContentUtils::FlushLayoutForTree(&window
);
3693 nsRefPtr
<nsPresContext
> presContext
;
3694 nsIDocShell
* docshell
= window
.GetDocShell();
3696 docshell
->GetPresContext(getter_AddRefs(presContext
));
3699 error
.Throw(NS_ERROR_FAILURE
);
3703 nscolor backgroundColor
;
3704 if (!ParseColor(bgColor
, &backgroundColor
)) {
3705 error
.Throw(NS_ERROR_FAILURE
);
3709 nsRect
r(nsPresContext::CSSPixelsToAppUnits((float)x
),
3710 nsPresContext::CSSPixelsToAppUnits((float)y
),
3711 nsPresContext::CSSPixelsToAppUnits((float)w
),
3712 nsPresContext::CSSPixelsToAppUnits((float)h
));
3713 uint32_t renderDocFlags
= (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING
|
3714 nsIPresShell::RENDER_DOCUMENT_RELATIVE
);
3715 if (flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET
) {
3716 renderDocFlags
|= nsIPresShell::RENDER_CARET
;
3718 if (flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW
) {
3719 renderDocFlags
&= ~(nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING
|
3720 nsIPresShell::RENDER_DOCUMENT_RELATIVE
);
3722 if (flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_USE_WIDGET_LAYERS
) {
3723 renderDocFlags
|= nsIPresShell::RENDER_USE_WIDGET_LAYERS
;
3725 if (flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_ASYNC_DECODE_IMAGES
) {
3726 renderDocFlags
|= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES
;
3728 if (flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH
) {
3729 renderDocFlags
|= nsIPresShell::RENDER_DRAWWINDOW_NOT_FLUSHING
;
3732 // gfxContext-over-Azure may modify the DrawTarget's transform, so
3733 // save and restore it
3734 Matrix matrix
= mTarget
->GetTransform();
3735 double sw
= matrix
._11
* w
;
3736 double sh
= matrix
._22
* h
;
3740 nsRefPtr
<gfxContext
> thebes
;
3741 RefPtr
<DrawTarget
> drawDT
;
3742 // Rendering directly is faster and can be done if mTarget supports Azure
3743 // and does not need alpha blending.
3744 if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget
) &&
3745 GlobalAlpha() == 1.0f
)
3747 thebes
= new gfxContext(mTarget
);
3748 thebes
->SetMatrix(gfxMatrix(matrix
._11
, matrix
._12
, matrix
._21
,
3749 matrix
._22
, matrix
._31
, matrix
._32
));
3752 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw
), ceil(sh
)),
3753 SurfaceFormat::B8G8R8A8
);
3755 error
.Throw(NS_ERROR_FAILURE
);
3759 thebes
= new gfxContext(drawDT
);
3760 thebes
->Scale(matrix
._11
, matrix
._22
);
3763 nsCOMPtr
<nsIPresShell
> shell
= presContext
->PresShell();
3764 unused
<< shell
->RenderDocument(r
, renderDocFlags
, backgroundColor
, thebes
);
3766 RefPtr
<SourceSurface
> snapshot
= drawDT
->Snapshot();
3767 RefPtr
<DataSourceSurface
> data
= snapshot
->GetDataSurface();
3769 RefPtr
<SourceSurface
> source
=
3770 mTarget
->CreateSourceSurfaceFromData(data
->GetData(),
3776 error
.Throw(NS_ERROR_FAILURE
);
3780 mgfx::Rect
destRect(0, 0, w
, h
);
3781 mgfx::Rect
sourceRect(0, 0, sw
, sh
);
3782 mTarget
->DrawSurface(source
, destRect
, sourceRect
,
3783 DrawSurfaceOptions(mgfx::Filter::POINT
),
3784 DrawOptions(GlobalAlpha(), CompositionOp::OP_OVER
,
3785 AntialiasMode::NONE
));
3788 mTarget
->SetTransform(matrix
);
3791 // note that x and y are coordinates in the document that
3792 // we're drawing; x and y are drawn to 0,0 in current user
3794 RedrawUser(gfxRect(0, 0, w
, h
));
3798 CanvasRenderingContext2D::AsyncDrawXULElement(nsXULElement
& elem
,
3801 const nsAString
& bgColor
,
3805 // We can't allow web apps to call this until we fix at least the
3806 // following potential security issues:
3807 // -- rendering cross-domain IFRAMEs and then extracting the results
3808 // -- rendering the user's theme and then extracting the results
3809 // -- rendering native anonymous content (e.g., file input paths;
3810 // scrollbars should be allowed)
3811 if (!nsContentUtils::IsCallerChrome()) {
3812 // not permitted to use DrawWindow
3813 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3814 error
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
3819 nsCOMPtr
<nsIFrameLoaderOwner
> loaderOwner
= do_QueryInterface(&elem
);
3821 error
.Throw(NS_ERROR_FAILURE
);
3825 nsRefPtr
<nsFrameLoader
> frameloader
= loaderOwner
->GetFrameLoader();
3827 error
.Throw(NS_ERROR_FAILURE
);
3831 PBrowserParent
*child
= frameloader
->GetRemoteBrowser();
3833 nsIDocShell
* docShell
= frameLoader
->GetExistingDocShell();
3835 error
.Throw(NS_ERROR_FAILURE
);
3839 nsCOMPtr
<nsIDOMWindow
> window
= docShell
->GetWindow();
3841 error
.Throw(NS_ERROR_FAILURE
);
3845 return DrawWindow(window
, x
, y
, w
, h
, bgColor
, flags
);
3848 // protect against too-large surfaces that will cause allocation
3849 // or overflow issues
3850 if (!gfxASurface::CheckSurfaceSize(gfxIntSize(w
, h
), 0xffff)) {
3851 error
.Throw(NS_ERROR_FAILURE
);
3856 (flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DO_NOT_FLUSH
) == 0;
3858 uint32_t renderDocFlags
= nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING
;
3859 if (flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_CARET
) {
3860 renderDocFlags
|= nsIPresShell::RENDER_CARET
;
3862 if (flags
& nsIDOMCanvasRenderingContext2D::DRAWWINDOW_DRAW_VIEW
) {
3863 renderDocFlags
&= ~nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING
;
3866 nsRect
rect(nsPresContext::CSSPixelsToAppUnits(x
),
3867 nsPresContext::CSSPixelsToAppUnits(y
),
3868 nsPresContext::CSSPixelsToAppUnits(w
),
3869 nsPresContext::CSSPixelsToAppUnits(h
));
3871 PDocumentRendererParent
*pdocrender
=
3872 child
->SendPDocumentRendererConstructor(rect
,
3873 mThebes
->CurrentMatrix(),
3875 renderDocFlags
, flush
,
3876 nsIntSize(mWidth
, mHeight
));
3878 return NS_ERROR_FAILURE
;
3880 DocumentRendererParent
*docrender
=
3881 static_cast<DocumentRendererParent
*>(pdocrender
);
3883 docrender
->SetCanvasContext(this, mThebes
);
3889 // device pixel getting/setting
3892 already_AddRefed
<ImageData
>
3893 CanvasRenderingContext2D::GetImageData(JSContext
* aCx
, double aSx
,
3894 double aSy
, double aSw
,
3895 double aSh
, ErrorResult
& error
)
3898 if (!IsTargetValid()) {
3899 error
.Throw(NS_ERROR_FAILURE
);
3903 if (!mCanvasElement
&& !mDocShell
) {
3904 NS_ERROR("No canvas element and no docshell in GetImageData!!!");
3905 error
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
3909 // Check only if we have a canvas element; if we were created with a docshell,
3910 // then it's special internal use.
3911 if (mCanvasElement
&& mCanvasElement
->IsWriteOnly() &&
3912 !nsContentUtils::IsCallerChrome())
3914 // XXX ERRMSG we need to report an error to developers here! (bug 329026)
3915 error
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
3919 if (!NS_finite(aSx
) || !NS_finite(aSy
) ||
3920 !NS_finite(aSw
) || !NS_finite(aSh
)) {
3921 error
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
3926 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
3930 int32_t x
= JS_DoubleToInt32(aSx
);
3931 int32_t y
= JS_DoubleToInt32(aSy
);
3932 int32_t wi
= JS_DoubleToInt32(aSw
);
3933 int32_t hi
= JS_DoubleToInt32(aSh
);
3935 // Handle negative width and height by flipping the rectangle over in the
3936 // relevant direction.
3958 JS::Rooted
<JSObject
*> array(aCx
);
3959 error
= GetImageDataArray(aCx
, x
, y
, w
, h
, array
.address());
3960 if (error
.Failed()) {
3965 nsRefPtr
<ImageData
> imageData
= new ImageData(w
, h
, *array
);
3966 return imageData
.forget();
3970 CanvasRenderingContext2D::GetImageDataArray(JSContext
* aCx
,
3977 MOZ_ASSERT(aWidth
&& aHeight
);
3979 CheckedInt
<uint32_t> len
= CheckedInt
<uint32_t>(aWidth
) * aHeight
* 4;
3980 if (!len
.isValid()) {
3981 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
3984 CheckedInt
<int32_t> rightMost
= CheckedInt
<int32_t>(aX
) + aWidth
;
3985 CheckedInt
<int32_t> bottomMost
= CheckedInt
<int32_t>(aY
) + aHeight
;
3987 if (!rightMost
.isValid() || !bottomMost
.isValid()) {
3988 return NS_ERROR_DOM_SYNTAX_ERR
;
3991 IntRect
srcRect(0, 0, mWidth
, mHeight
);
3992 IntRect
destRect(aX
, aY
, aWidth
, aHeight
);
3993 IntRect srcReadRect
= srcRect
.Intersect(destRect
);
3994 RefPtr
<DataSourceSurface
> readback
;
3995 if (!srcReadRect
.IsEmpty() && !mZero
) {
3996 RefPtr
<SourceSurface
> snapshot
= mTarget
->Snapshot();
3998 readback
= snapshot
->GetDataSurface();
4000 if (!readback
|| !readback
->GetData()) {
4001 return NS_ERROR_OUT_OF_MEMORY
;
4005 JS::Rooted
<JSObject
*> darray(aCx
, JS_NewUint8ClampedArray(aCx
, len
.value()));
4007 return NS_ERROR_OUT_OF_MEMORY
;
4015 uint8_t* data
= JS_GetUint8ClampedArrayData(darray
);
4017 IntRect dstWriteRect
= srcReadRect
;
4018 dstWriteRect
.MoveBy(-aX
, -aY
);
4020 uint8_t* src
= data
;
4021 uint32_t srcStride
= aWidth
* 4;
4023 srcStride
= readback
->Stride();
4024 src
= readback
->GetData() + srcReadRect
.y
* srcStride
+ srcReadRect
.x
* 4;
4027 // NOTE! dst is the same as src, and this relies on reading
4028 // from src and advancing that ptr before writing to dst.
4029 // NOTE! I'm not sure that it is, I think this comment might have been
4030 // inherited from Thebes canvas and is no longer true
4031 uint8_t* dst
= data
+ dstWriteRect
.y
* (aWidth
* 4) + dstWriteRect
.x
* 4;
4034 for (int32_t j
= 0; j
< dstWriteRect
.height
; ++j
) {
4035 for (int32_t i
= 0; i
< dstWriteRect
.width
; ++i
) {
4036 // XXX Is there some useful swizzle MMX we can use here?
4037 #if MOZ_LITTLE_ENDIAN
4053 src
+= srcStride
- (dstWriteRect
.width
* 4);
4054 dst
+= (aWidth
* 4) - (dstWriteRect
.width
* 4);
4057 for (int32_t j
= 0; j
< dstWriteRect
.height
; ++j
) {
4058 for (int32_t i
= 0; i
< dstWriteRect
.width
; ++i
) {
4059 // XXX Is there some useful swizzle MMX we can use here?
4060 #if MOZ_LITTLE_ENDIAN
4071 // Convert to non-premultiplied color
4072 *dst
++ = gfxUtils::sUnpremultiplyTable
[a
* 256 + r
];
4073 *dst
++ = gfxUtils::sUnpremultiplyTable
[a
* 256 + g
];
4074 *dst
++ = gfxUtils::sUnpremultiplyTable
[a
* 256 + b
];
4077 src
+= srcStride
- (dstWriteRect
.width
* 4);
4078 dst
+= (aWidth
* 4) - (dstWriteRect
.width
* 4);
4086 CanvasRenderingContext2D::EnsureErrorTarget()
4092 RefPtr
<DrawTarget
> errorTarget
= gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8
);
4093 MOZ_ASSERT(errorTarget
, "Failed to allocate the error target!");
4095 sErrorTarget
= errorTarget
;
4096 NS_ADDREF(sErrorTarget
);
4100 CanvasRenderingContext2D::FillRuleChanged()
4103 mPathBuilder
= mPath
->CopyToBuilder(CurrentState().fillRule
);
4109 CanvasRenderingContext2D::PutImageData(ImageData
& imageData
, double dx
,
4110 double dy
, ErrorResult
& error
)
4112 dom::Uint8ClampedArray arr
;
4113 DebugOnly
<bool> inited
= arr
.Init(imageData
.GetDataObject());
4116 error
= PutImageData_explicit(JS_DoubleToInt32(dx
), JS_DoubleToInt32(dy
),
4117 imageData
.Width(), imageData
.Height(),
4118 &arr
, false, 0, 0, 0, 0);
4122 CanvasRenderingContext2D::PutImageData(ImageData
& imageData
, double dx
,
4123 double dy
, double dirtyX
,
4124 double dirtyY
, double dirtyWidth
,
4128 dom::Uint8ClampedArray arr
;
4129 DebugOnly
<bool> inited
= arr
.Init(imageData
.GetDataObject());
4132 error
= PutImageData_explicit(JS_DoubleToInt32(dx
), JS_DoubleToInt32(dy
),
4133 imageData
.Width(), imageData
.Height(),
4135 JS_DoubleToInt32(dirtyX
),
4136 JS_DoubleToInt32(dirtyY
),
4137 JS_DoubleToInt32(dirtyWidth
),
4138 JS_DoubleToInt32(dirtyHeight
));
4141 // void putImageData (in ImageData d, in float x, in float y);
4142 // void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
4145 CanvasRenderingContext2D::PutImageData_explicit(int32_t x
, int32_t y
, uint32_t w
, uint32_t h
,
4146 dom::Uint8ClampedArray
* aArray
,
4147 bool hasDirtyRect
, int32_t dirtyX
, int32_t dirtyY
,
4148 int32_t dirtyWidth
, int32_t dirtyHeight
)
4150 if (w
== 0 || h
== 0) {
4151 return NS_ERROR_DOM_INVALID_STATE_ERR
;
4155 IntRect
imageDataRect(0, 0, w
, h
);
4158 // fix up negative dimensions
4159 if (dirtyWidth
< 0) {
4160 NS_ENSURE_TRUE(dirtyWidth
!= INT_MIN
, NS_ERROR_DOM_INDEX_SIZE_ERR
);
4162 CheckedInt32 checkedDirtyX
= CheckedInt32(dirtyX
) + dirtyWidth
;
4164 if (!checkedDirtyX
.isValid())
4165 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
4167 dirtyX
= checkedDirtyX
.value();
4168 dirtyWidth
= -dirtyWidth
;
4171 if (dirtyHeight
< 0) {
4172 NS_ENSURE_TRUE(dirtyHeight
!= INT_MIN
, NS_ERROR_DOM_INDEX_SIZE_ERR
);
4174 CheckedInt32 checkedDirtyY
= CheckedInt32(dirtyY
) + dirtyHeight
;
4176 if (!checkedDirtyY
.isValid())
4177 return NS_ERROR_DOM_INDEX_SIZE_ERR
;
4179 dirtyY
= checkedDirtyY
.value();
4180 dirtyHeight
= -dirtyHeight
;
4183 // bound the dirty rect within the imageData rectangle
4184 dirtyRect
= imageDataRect
.Intersect(IntRect(dirtyX
, dirtyY
, dirtyWidth
, dirtyHeight
));
4186 if (dirtyRect
.Width() <= 0 || dirtyRect
.Height() <= 0)
4189 dirtyRect
= imageDataRect
;
4192 dirtyRect
.MoveBy(IntPoint(x
, y
));
4193 dirtyRect
= IntRect(0, 0, mWidth
, mHeight
).Intersect(dirtyRect
);
4195 if (dirtyRect
.Width() <= 0 || dirtyRect
.Height() <= 0) {
4199 aArray
->ComputeLengthAndData();
4201 uint32_t dataLen
= aArray
->Length();
4203 uint32_t len
= w
* h
* 4;
4204 if (dataLen
!= len
) {
4205 return NS_ERROR_DOM_INVALID_STATE_ERR
;
4208 nsRefPtr
<gfxImageSurface
> imgsurf
= new gfxImageSurface(gfxIntSize(w
, h
),
4209 gfxImageFormat::ARGB32
,
4211 if (!imgsurf
|| imgsurf
->CairoStatus()) {
4212 return NS_ERROR_FAILURE
;
4215 uint8_t *src
= aArray
->Data();
4216 uint8_t *dst
= imgsurf
->Data();
4218 for (uint32_t j
= 0; j
< h
; j
++) {
4219 for (uint32_t i
= 0; i
< w
; i
++) {
4225 // Convert to premultiplied color (losslessly if the input came from getImageData)
4226 #if MOZ_LITTLE_ENDIAN
4227 *dst
++ = gfxUtils::sPremultiplyTable
[a
* 256 + b
];
4228 *dst
++ = gfxUtils::sPremultiplyTable
[a
* 256 + g
];
4229 *dst
++ = gfxUtils::sPremultiplyTable
[a
* 256 + r
];
4233 *dst
++ = gfxUtils::sPremultiplyTable
[a
* 256 + r
];
4234 *dst
++ = gfxUtils::sPremultiplyTable
[a
* 256 + g
];
4235 *dst
++ = gfxUtils::sPremultiplyTable
[a
* 256 + b
];
4241 if (!IsTargetValid()) {
4242 return NS_ERROR_FAILURE
;
4245 RefPtr
<SourceSurface
> sourceSurface
=
4246 mTarget
->CreateSourceSurfaceFromData(imgsurf
->Data(), IntSize(w
, h
), imgsurf
->Stride(), SurfaceFormat::B8G8R8A8
);
4248 // In certain scenarios, requesting larger than 8k image fails. Bug 803568
4249 // covers the details of how to run into it, but the full detailed
4250 // investigation hasn't been done to determine the underlying cause. We
4251 // will just handle the failure to allocate the surface to avoid a crash.
4252 if (!sourceSurface
) {
4253 return NS_ERROR_FAILURE
;
4256 mTarget
->CopySurface(sourceSurface
,
4257 IntRect(dirtyRect
.x
- x
, dirtyRect
.y
- y
,
4258 dirtyRect
.width
, dirtyRect
.height
),
4259 IntPoint(dirtyRect
.x
, dirtyRect
.y
));
4261 Redraw(mgfx::Rect(dirtyRect
.x
, dirtyRect
.y
, dirtyRect
.width
, dirtyRect
.height
));
4266 static already_AddRefed
<ImageData
>
4267 CreateImageData(JSContext
* cx
, CanvasRenderingContext2D
* context
,
4268 uint32_t w
, uint32_t h
, ErrorResult
& error
)
4275 CheckedInt
<uint32_t> len
= CheckedInt
<uint32_t>(w
) * h
* 4;
4276 if (!len
.isValid()) {
4277 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
4281 // Create the fast typed array; it's initialized to 0 by default.
4282 JSObject
* darray
= Uint8ClampedArray::Create(cx
, context
, len
.value());
4284 error
.Throw(NS_ERROR_OUT_OF_MEMORY
);
4288 nsRefPtr
<mozilla::dom::ImageData
> imageData
=
4289 new mozilla::dom::ImageData(w
, h
, *darray
);
4290 return imageData
.forget();
4293 already_AddRefed
<ImageData
>
4294 CanvasRenderingContext2D::CreateImageData(JSContext
* cx
, double sw
,
4295 double sh
, ErrorResult
& error
)
4298 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
4302 int32_t wi
= JS_DoubleToInt32(sw
);
4303 int32_t hi
= JS_DoubleToInt32(sh
);
4305 uint32_t w
= Abs(wi
);
4306 uint32_t h
= Abs(hi
);
4307 return mozilla::dom::CreateImageData(cx
, this, w
, h
, error
);
4310 already_AddRefed
<ImageData
>
4311 CanvasRenderingContext2D::CreateImageData(JSContext
* cx
,
4312 ImageData
& imagedata
,
4315 return mozilla::dom::CreateImageData(cx
, this, imagedata
.Width(),
4316 imagedata
.Height(), error
);
4319 static uint8_t g2DContextLayerUserData
;
4321 already_AddRefed
<CanvasLayer
>
4322 CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder
* aBuilder
,
4323 CanvasLayer
*aOldLayer
,
4324 LayerManager
*aManager
)
4327 // If we're opaque then make sure we have a surface so we paint black
4328 // instead of transparent.
4332 // Don't call EnsureTarget() ... if there isn't already a surface, then
4333 // we have nothing to paint and there is no need to create a surface just
4334 // to paint nothing. Also, EnsureTarget() can cause creation of a persistent
4335 // layer manager which must NOT happen during a paint.
4336 if (!mTarget
|| !IsTargetValid()) {
4337 // No DidTransactionCallback will be received, so mark the context clean
4338 // now so future invalidations will be dispatched.
4345 if (!mResetLayer
&& aOldLayer
) {
4346 CanvasRenderingContext2DUserData
* userData
=
4347 static_cast<CanvasRenderingContext2DUserData
*>(
4348 aOldLayer
->GetUserData(&g2DContextLayerUserData
));
4350 CanvasLayer::Data data
;
4353 SkiaGLGlue
* glue
= gfxPlatform::GetPlatform()->GetSkiaGLGlue();
4356 data
.mGLContext
= glue
->GetGLContext();
4357 data
.mStream
= mStream
.get();
4361 data
.mDrawTarget
= mTarget
;
4364 if (userData
&& userData
->IsForContext(this) && aOldLayer
->IsDataValid(data
)) {
4365 nsRefPtr
<CanvasLayer
> ret
= aOldLayer
;
4366 return ret
.forget();
4370 nsRefPtr
<CanvasLayer
> canvasLayer
= aManager
->CreateCanvasLayer();
4372 NS_WARNING("CreateCanvasLayer returned null!");
4373 // No DidTransactionCallback will be received, so mark the context clean
4374 // now so future invalidations will be dispatched.
4378 CanvasRenderingContext2DUserData
*userData
= nullptr;
4379 // Make the layer tell us whenever a transaction finishes (including
4380 // the current transaction), so we can clear our invalidation state and
4381 // start invalidating again. We need to do this for all layers since
4382 // callers of DrawWindow may be expecting to receive normal invalidation
4383 // notifications after this paint.
4385 // The layer will be destroyed when we tear down the presentation
4386 // (at the latest), at which time this userData will be destroyed,
4387 // releasing the reference to the element.
4388 // The userData will receive DidTransactionCallbacks, which flush the
4389 // the invalidation state to indicate that the canvas is up to date.
4390 userData
= new CanvasRenderingContext2DUserData(this);
4391 canvasLayer
->SetDidTransactionCallback(
4392 CanvasRenderingContext2DUserData::DidTransactionCallback
, userData
);
4393 canvasLayer
->SetUserData(&g2DContextLayerUserData
, userData
);
4395 CanvasLayer::Data data
;
4397 SkiaGLGlue
* glue
= gfxPlatform::GetPlatform()->GetSkiaGLGlue();
4400 canvasLayer
->SetPreTransactionCallback(
4401 CanvasRenderingContext2DUserData::PreTransactionCallback
, userData
);
4403 data
.mGLContext
= glue
->GetGLContext();
4405 data
.mStream
= mStream
.get();
4406 data
.mTexID
= (uint32_t)((uintptr_t)mTarget
->GetNativeSurface(NativeSurfaceType::OPENGL_TEXTURE
));
4409 data
.mDrawTarget
= mTarget
;
4412 data
.mSize
= nsIntSize(mWidth
, mHeight
);
4413 data
.mHasAlpha
= !mOpaque
;
4415 canvasLayer
->Initialize(data
);
4416 uint32_t flags
= mOpaque
? Layer::CONTENT_OPAQUE
: 0;
4417 canvasLayer
->SetContentFlags(flags
);
4418 canvasLayer
->Updated();
4420 mResetLayer
= false;
4422 return canvasLayer
.forget();
4426 CanvasRenderingContext2D::MarkContextClean()
4428 if (mInvalidateCount
> 0) {
4429 mPredictManyRedrawCalls
= mInvalidateCount
> kCanvasMaxInvalidateCount
;
4431 mIsEntireFrameInvalid
= false;
4432 mInvalidateCount
= 0;
4437 CanvasRenderingContext2D::ShouldForceInactiveLayer(LayerManager
*aManager
)
4439 return !aManager
->CanUseCanvasLayerForSize(IntSize(mWidth
, mHeight
));
4442 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasPath
, AddRef
)
4443 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasPath
, Release
)
4445 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPath
, mParent
)
4447 CanvasPath::CanvasPath(nsISupports
* aParent
)
4452 mPathBuilder
= gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
4455 CanvasPath::CanvasPath(nsISupports
* aParent
, TemporaryRef
<PathBuilder
> aPathBuilder
)
4456 : mParent(aParent
), mPathBuilder(aPathBuilder
)
4460 if (!mPathBuilder
) {
4461 mPathBuilder
= gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget()->CreatePathBuilder();
4466 CanvasPath::WrapObject(JSContext
* aCx
)
4468 return Path2DBinding::Wrap(aCx
, this);
4471 already_AddRefed
<CanvasPath
>
4472 CanvasPath::Constructor(const GlobalObject
& aGlobal
, ErrorResult
& aRv
)
4474 nsRefPtr
<CanvasPath
> path
= new CanvasPath(aGlobal
.GetAsSupports());
4475 return path
.forget();
4478 already_AddRefed
<CanvasPath
>
4479 CanvasPath::Constructor(const GlobalObject
& aGlobal
, CanvasPath
& aCanvasPath
, ErrorResult
& aRv
)
4481 RefPtr
<gfx::Path
> tempPath
= aCanvasPath
.GetPath(CanvasWindingRule::Nonzero
,
4482 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
4484 nsRefPtr
<CanvasPath
> path
= new CanvasPath(aGlobal
.GetAsSupports(), tempPath
->CopyToBuilder());
4485 return path
.forget();
4488 already_AddRefed
<CanvasPath
>
4489 CanvasPath::Constructor(const GlobalObject
& aGlobal
, const nsAString
& aPathString
, ErrorResult
& aRv
)
4491 RefPtr
<gfx::Path
> tempPath
= SVGContentUtils::GetPath(aPathString
);
4493 return Constructor(aGlobal
, aRv
);
4496 nsRefPtr
<CanvasPath
> path
= new CanvasPath(aGlobal
.GetAsSupports(), tempPath
->CopyToBuilder());
4497 return path
.forget();
4501 CanvasPath::ClosePath()
4503 EnsurePathBuilder();
4505 mPathBuilder
->Close();
4509 CanvasPath::MoveTo(double x
, double y
)
4511 EnsurePathBuilder();
4513 mPathBuilder
->MoveTo(Point(ToFloat(x
), ToFloat(y
)));
4517 CanvasPath::LineTo(double x
, double y
)
4519 EnsurePathBuilder();
4521 mPathBuilder
->LineTo(Point(ToFloat(x
), ToFloat(y
)));
4525 CanvasPath::QuadraticCurveTo(double cpx
, double cpy
, double x
, double y
)
4527 EnsurePathBuilder();
4529 mPathBuilder
->QuadraticBezierTo(gfx::Point(ToFloat(cpx
), ToFloat(cpy
)),
4530 gfx::Point(ToFloat(x
), ToFloat(y
)));
4534 CanvasPath::BezierCurveTo(double cp1x
, double cp1y
,
4535 double cp2x
, double cp2y
,
4538 BezierTo(gfx::Point(ToFloat(cp1x
), ToFloat(cp1y
)),
4539 gfx::Point(ToFloat(cp2x
), ToFloat(cp2y
)),
4540 gfx::Point(ToFloat(x
), ToFloat(y
)));
4544 CanvasPath::ArcTo(double x1
, double y1
, double x2
, double y2
, double radius
,
4548 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
4552 EnsurePathBuilder();
4554 // Current point in user space!
4555 Point p0
= mPathBuilder
->CurrentPoint();
4559 // Execute these calculations in double precision to avoid cumulative
4561 double dir
, a2
, b2
, c2
, cosx
, sinx
, d
, anx
, any
,
4562 bnx
, bny
, x3
, y3
, x4
, y4
, cx
, cy
, angle0
, angle1
;
4565 if (p0
== p1
|| p1
== p2
|| radius
== 0) {
4570 // Check for colinearity
4571 dir
= (p2
.x
- p1
.x
) * (p0
.y
- p1
.y
) + (p2
.y
- p1
.y
) * (p1
.x
- p0
.x
);
4578 // XXX - Math for this code was already available from the non-azure code
4579 // and would be well tested. Perhaps converting to bezier directly might
4580 // be more efficient longer run.
4581 a2
= (p0
.x
-x1
)*(p0
.x
-x1
) + (p0
.y
-y1
)*(p0
.y
-y1
);
4582 b2
= (x1
-x2
)*(x1
-x2
) + (y1
-y2
)*(y1
-y2
);
4583 c2
= (p0
.x
-x2
)*(p0
.x
-x2
) + (p0
.y
-y2
)*(p0
.y
-y2
);
4584 cosx
= (a2
+b2
-c2
)/(2*sqrt(a2
*b2
));
4586 sinx
= sqrt(1 - cosx
*cosx
);
4587 d
= radius
/ ((1 - cosx
) / sinx
);
4589 anx
= (x1
-p0
.x
) / sqrt(a2
);
4590 any
= (y1
-p0
.y
) / sqrt(a2
);
4591 bnx
= (x1
-x2
) / sqrt(b2
);
4592 bny
= (y1
-y2
) / sqrt(b2
);
4597 anticlockwise
= (dir
< 0);
4598 cx
= x3
+ any
*radius
*(anticlockwise
? 1 : -1);
4599 cy
= y3
- anx
*radius
*(anticlockwise
? 1 : -1);
4600 angle0
= atan2((y3
-cy
), (x3
-cx
));
4601 angle1
= atan2((y4
-cy
), (x4
-cx
));
4606 Arc(cx
, cy
, radius
, angle0
, angle1
, anticlockwise
, error
);
4610 CanvasPath::Rect(double x
, double y
, double w
, double h
)
4614 LineTo(x
+ w
, y
+ h
);
4620 CanvasPath::Arc(double x
, double y
, double radius
,
4621 double startAngle
, double endAngle
, bool anticlockwise
,
4625 error
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
4629 EnsurePathBuilder();
4631 ArcToBezier(this, Point(x
, y
), Size(radius
, radius
), startAngle
, endAngle
, anticlockwise
);
4635 CanvasPath::LineTo(const gfx::Point
& aPoint
)
4637 EnsurePathBuilder();
4639 mPathBuilder
->LineTo(aPoint
);
4643 CanvasPath::BezierTo(const gfx::Point
& aCP1
,
4644 const gfx::Point
& aCP2
,
4645 const gfx::Point
& aCP3
)
4647 EnsurePathBuilder();
4649 mPathBuilder
->BezierTo(aCP1
, aCP2
, aCP3
);
4653 CanvasPath::AddPath(CanvasPath
& aCanvasPath
, const Optional
<NonNull
<SVGMatrix
>>& aMatrix
)
4655 RefPtr
<gfx::Path
> tempPath
= aCanvasPath
.GetPath(CanvasWindingRule::Nonzero
,
4656 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
4658 if (aMatrix
.WasPassed()) {
4659 const SVGMatrix
& m
= aMatrix
.Value();
4660 Matrix
transform(m
.A(), m
.B(), m
.C(), m
.D(), m
.E(), m
.F());
4662 if (!transform
.IsIdentity()) {
4663 RefPtr
<PathBuilder
> tempBuilder
= tempPath
->TransformedCopyToBuilder(transform
, FillRule::FILL_WINDING
);
4664 tempPath
= tempBuilder
->Finish();
4668 tempPath
->StreamToSink(mPathBuilder
);
4671 TemporaryRef
<gfx::Path
>
4672 CanvasPath::GetPath(const CanvasWindingRule
& winding
, const DrawTarget
* aTarget
) const
4674 FillRule fillRule
= FillRule::FILL_WINDING
;
4675 if (winding
== CanvasWindingRule::Evenodd
) {
4676 fillRule
= FillRule::FILL_EVEN_ODD
;
4680 (mPath
->GetBackendType() == aTarget
->GetBackendType()) &&
4681 (mPath
->GetFillRule() == fillRule
)) {
4686 // if there is no path, there must be a pathbuilder
4687 MOZ_ASSERT(mPathBuilder
);
4688 mPath
= mPathBuilder
->Finish();
4692 mPathBuilder
= nullptr;
4695 // retarget our backend if we're used with a different backend
4696 if (mPath
->GetBackendType() != aTarget
->GetBackendType()) {
4697 RefPtr
<PathBuilder
> tmpPathBuilder
= aTarget
->CreatePathBuilder(fillRule
);
4698 mPath
->StreamToSink(tmpPathBuilder
);
4699 mPath
= tmpPathBuilder
->Finish();
4700 } else if (mPath
->GetFillRule() != fillRule
) {
4701 RefPtr
<PathBuilder
> tmpPathBuilder
= mPath
->CopyToBuilder(fillRule
);
4702 mPath
= tmpPathBuilder
->Finish();
4709 CanvasPath::EnsurePathBuilder() const
4715 // if there is not pathbuilder, there must be a path
4717 mPathBuilder
= mPath
->CopyToBuilder();