new file mode 100644
--- /dev/null
+++ b/dom/canvas/AdjustedTarget.cpp
@@ -0,0 +1,331 @@
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need to have a filter applied to their results.
+ * All coordinates passed to the constructor are in device space.
+ */
+#include "AdjustedTarget.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+
+using mozilla::gfx::DrawTarget;
+
+namespace mozilla {
+namespace dom {
+
+AdjustedTargetForFilter::AdjustedTargetForFilter(BasicRenderingContext2D* aCtx,
+ DrawTarget* aFinalTarget,
+ const gfx::IntPoint& aFilterSpaceToTargetOffset,
+ const gfx::IntRect& aPreFilterBounds,
+ const gfx::IntRect& aPostFilterBounds,
+ gfx::CompositionOp aCompositionOp)
+ : mCtx(nullptr)
+ , mCompositionOp(aCompositionOp)
+{
+ mCtx = aCtx;
+ mFinalTarget = aFinalTarget;
+ mPostFilterBounds = aPostFilterBounds;
+ mOffset = aFilterSpaceToTargetOffset;
+
+ nsIntRegion sourceGraphicNeededRegion;
+ nsIntRegion fillPaintNeededRegion;
+ nsIntRegion strokePaintNeededRegion;
+
+ FilterSupport::ComputeSourceNeededRegions(
+ aCtx->CurrentState().filter, mPostFilterBounds,
+ sourceGraphicNeededRegion, fillPaintNeededRegion,
+ strokePaintNeededRegion);
+
+ mSourceGraphicRect = sourceGraphicNeededRegion.GetBounds();
+ mFillPaintRect = fillPaintNeededRegion.GetBounds();
+ mStrokePaintRect = strokePaintNeededRegion.GetBounds();
+
+ mSourceGraphicRect = mSourceGraphicRect.Intersect(aPreFilterBounds);
+
+ if (mSourceGraphicRect.IsEmpty()) {
+ // The filter might not make any use of the source graphic. We need to
+ // create a DrawTarget that we can return from DT() anyway, so we'll
+ // just use a 1x1-sized one.
+ mSourceGraphicRect.SizeTo(1, 1);
+ }
+
+ mTarget =
+ mFinalTarget->CreateSimilarDrawTarget(mSourceGraphicRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ if (!mTarget) {
+ // XXX - Deal with the situation where our temp size is too big to
+ // fit in a texture (bug 1066622).
+ mTarget = mFinalTarget;
+ mCtx = nullptr;
+ mFinalTarget = nullptr;
+ return;
+ }
+
+ mTarget->SetTransform(
+ mFinalTarget->GetTransform().PostTranslate(-mSourceGraphicRect.TopLeft() + mOffset));
+}
+
+// Return a SourceSurface that contains the FillPaint or StrokePaint source.
+already_AddRefed<SourceSurface>
+AdjustedTargetForFilter::DoSourcePaint(gfx::IntRect& aRect, BasicRenderingContext2D::Style aStyle)
+{
+ if (aRect.IsEmpty()) {
+ return nullptr;
+ }
+
+ RefPtr<DrawTarget> dt =
+ mFinalTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
+ if (!dt) {
+ aRect.SetEmpty();
+ return nullptr;
+ }
+
+ Matrix transform =
+ mFinalTarget->GetTransform().PostTranslate(-aRect.TopLeft() + mOffset);
+
+ dt->SetTransform(transform);
+
+ if (transform.Invert()) {
+ gfx::Rect dtBounds(0, 0, aRect.width, aRect.height);
+ gfx::Rect fillRect = transform.TransformBounds(dtBounds);
+ dt->FillRect(fillRect, CanvasGeneralPattern().ForStyle(mCtx, aStyle, dt));
+ }
+ return dt->Snapshot();
+}
+
+AdjustedTargetForFilter::~AdjustedTargetForFilter()
+{
+ if (!mCtx) {
+ return;
+ }
+
+ RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+
+ RefPtr<SourceSurface> fillPaint =
+ DoSourcePaint(mFillPaintRect, BasicRenderingContext2D::Style::FILL);
+ RefPtr<SourceSurface> strokePaint =
+ DoSourcePaint(mStrokePaintRect, BasicRenderingContext2D::Style::STROKE);
+
+ AutoRestoreTransform autoRestoreTransform(mFinalTarget);
+ mFinalTarget->SetTransform(Matrix());
+
+ MOZ_RELEASE_ASSERT(!mCtx->CurrentState().filter.mPrimitives.IsEmpty());
+ gfx::FilterSupport::RenderFilterDescription(
+ mFinalTarget, mCtx->CurrentState().filter,
+ gfx::Rect(mPostFilterBounds),
+ snapshot, mSourceGraphicRect,
+ fillPaint, mFillPaintRect,
+ strokePaint, mStrokePaintRect,
+ mCtx->CurrentState().filterAdditionalImages,
+ mPostFilterBounds.TopLeft() - mOffset,
+ DrawOptions(1.0f, mCompositionOp));
+
+ const gfx::FilterDescription& filter = mCtx->CurrentState().filter;
+ MOZ_RELEASE_ASSERT(!filter.mPrimitives.IsEmpty());
+ if (filter.mPrimitives.LastElement().IsTainted() &&
+ mCtx->GetCanvasElement()) {
+ mCtx->GetCanvasElement()->SetWriteOnly();
+ }
+}
+
+DrawTarget* AdjustedTargetForFilter::DT()
+{
+ return mTarget;
+}
+
+AdjustedTargetForShadow::AdjustedTargetForShadow(BasicRenderingContext2D* aCtx,
+ DrawTarget* aFinalTarget,
+ const gfx::Rect& aBounds,
+ gfx::CompositionOp aCompositionOp)
+ : mCtx(nullptr)
+ , mCompositionOp(aCompositionOp)
+{
+ mCtx = aCtx;
+ mFinalTarget = aFinalTarget;
+
+ const ContextState &state = mCtx->CurrentState();
+
+ mSigma = state.ShadowBlurSigma();
+
+ gfx::Rect bounds = aBounds;
+
+ int32_t blurRadius = state.ShadowBlurRadius();
+
+ // We actually include the bounds of the shadow blur, this makes it
+ // easier to execute the actual blur on hardware, and shouldn't affect
+ // the amount of pixels that need to be touched.
+ bounds.Inflate(blurRadius);
+
+ bounds.RoundOut();
+ bounds.ToIntRect(&mTempRect);
+
+ mTarget =
+ mFinalTarget->CreateShadowDrawTarget(mTempRect.Size(),
+ SurfaceFormat::B8G8R8A8, mSigma);
+
+ if (!mTarget) {
+ // XXX - Deal with the situation where our temp size is too big to
+ // fit in a texture (bug 1066622).
+ mTarget = mFinalTarget;
+ mCtx = nullptr;
+ mFinalTarget = nullptr;
+ } else {
+ mTarget->SetTransform(
+ mFinalTarget->GetTransform().PostTranslate(-mTempRect.TopLeft()));
+ }
+}
+
+AdjustedTargetForShadow::~AdjustedTargetForShadow()
+{
+ if (!mCtx) {
+ return;
+ }
+
+ RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
+
+ mFinalTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
+ Color::FromABGR(mCtx->CurrentState().shadowColor),
+ mCtx->CurrentState().shadowOffset, mSigma,
+ mCompositionOp);
+}
+
+DrawTarget* AdjustedTargetForShadow::DT()
+{
+ return mTarget;
+}
+
+gfx::IntPoint AdjustedTargetForShadow::OffsetToFinalDT()
+{
+ return mTempRect.TopLeft();
+}
+
+AdjustedTarget::AdjustedTarget(BasicRenderingContext2D* aCtx, const gfx::Rect *aBounds)
+{
+ // There are operations that can invalidate aCtx->mTarget along the way,
+ // so don't cache the pointer to it too soon.
+ mTarget = nullptr;
+
+ // All rects in this function are in the device space of ctx->mTarget.
+
+ // In order to keep our temporary surfaces as small as possible, we first
+ // calculate what their maximum required bounds would need to be if we
+ // were to fill the whole canvas. Everything outside those bounds we don't
+ // need to render.
+ gfx::Rect r(0, 0, aCtx->mWidth, aCtx->mHeight);
+ gfx::Rect maxSourceNeededBoundsForShadow =
+ MaxSourceNeededBoundsForShadow(r, aCtx);
+ gfx::Rect maxSourceNeededBoundsForFilter =
+ MaxSourceNeededBoundsForFilter(maxSourceNeededBoundsForShadow, aCtx);
+
+ gfx::Rect bounds = maxSourceNeededBoundsForFilter;
+ if (aBounds) {
+ bounds = bounds.Intersect(*aBounds);
+ }
+ gfx::Rect boundsAfterFilter = BoundsAfterFilter(bounds, aCtx);
+
+ mozilla::gfx::CompositionOp op = aCtx->CurrentState().op;
+
+ gfx::IntPoint offsetToFinalDT;
+
+ // First set up the shadow draw target, because the shadow goes outside.
+ // It applies to the post-filter results, if both a filter and a shadow
+ // are used.
+ if (aCtx->NeedToDrawShadow()) {
+ mShadowTarget = MakeUnique<AdjustedTargetForShadow>(
+ aCtx, aCtx->mTarget, boundsAfterFilter, op);
+ mTarget = mShadowTarget->DT();
+ offsetToFinalDT = mShadowTarget->OffsetToFinalDT();
+
+ // If we also have a filter, the filter needs to be drawn with OP_OVER
+ // because shadow drawing already applies op on the result.
+ op = gfx::CompositionOp::OP_OVER;
+ }
+
+ // Now set up the filter draw target.
+ if (aCtx->NeedToApplyFilter()) {
+ bounds.RoundOut();
+
+ if (!mTarget) {
+ mTarget = aCtx->mTarget;
+ }
+ gfx::IntRect intBounds;
+ if (!bounds.ToIntRect(&intBounds)) {
+ return;
+ }
+ mFilterTarget = MakeUnique<AdjustedTargetForFilter>(
+ aCtx, mTarget, offsetToFinalDT, intBounds,
+ gfx::RoundedToInt(boundsAfterFilter), op);
+ mTarget = mFilterTarget->DT();
+ }
+ if (!mTarget) {
+ mTarget = aCtx->mTarget;
+ }
+}
+
+AdjustedTarget::~AdjustedTarget()
+{
+ // The order in which the targets are finalized is important.
+ // Filters are inside, any shadow applies to the post-filter results.
+ mFilterTarget.reset();
+ mShadowTarget.reset();
+}
+
+DrawTarget* AdjustedTarget::operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN
+{
+ return mTarget;
+}
+
+gfx::Rect
+AdjustedTarget::MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds, BasicRenderingContext2D* aCtx)
+{
+ if (!aCtx->NeedToApplyFilter()) {
+ return aDestBounds;
+ }
+
+ nsIntRegion sourceGraphicNeededRegion;
+ nsIntRegion fillPaintNeededRegion;
+ nsIntRegion strokePaintNeededRegion;
+
+ FilterSupport::ComputeSourceNeededRegions(
+ aCtx->CurrentState().filter, gfx::RoundedToInt(aDestBounds),
+ sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
+
+ return gfx::Rect(sourceGraphicNeededRegion.GetBounds());
+}
+
+gfx::Rect
+AdjustedTarget::MaxSourceNeededBoundsForShadow(const gfx::Rect& aDestBounds, BasicRenderingContext2D* aCtx)
+{
+ if (!aCtx->NeedToDrawShadow()) {
+ return aDestBounds;
+ }
+
+ const ContextState &state = aCtx->CurrentState();
+ gfx::Rect sourceBounds = aDestBounds - state.shadowOffset;
+ sourceBounds.Inflate(state.ShadowBlurRadius());
+
+ // Union the shadow source with the original rect because we're going to
+ // draw both.
+ return sourceBounds.Union(aDestBounds);
+}
+
+gfx::Rect
+AdjustedTarget::BoundsAfterFilter(const gfx::Rect& aBounds, BasicRenderingContext2D* aCtx)
+{
+ if (!aCtx->NeedToApplyFilter()) {
+ return aBounds;
+ }
+
+ gfx::Rect bounds(aBounds);
+ bounds.RoundOut();
+
+ gfx::IntRect intBounds;
+ if (!bounds.ToIntRect(&intBounds)) {
+ return gfx::Rect();
+ }
+
+ nsIntRegion extents =
+ gfx::FilterSupport::ComputePostFilterExtents(aCtx->CurrentState().filter,
+ intBounds);
+ return gfx::Rect(extents.GetBounds());
+}
+
+} // dom
+} // mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/canvas/AdjustedTarget.h
@@ -0,0 +1,136 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef AdjustedTarget_h
+#define AdjustedTarget_h
+
+#include "FilterSupport.h"
+#include "mozilla/dom/BasicRenderingContext2D.h"
+#include "mozilla/gfx/Helpers.h"
+
+using mozilla::gfx::AutoRestoreTransform;
+using mozilla::gfx::Color;
+using mozilla::gfx::DrawOptions;
+using mozilla::gfx::FilterSupport;
+using mozilla::gfx::SurfaceFormat;
+using mozilla::gfx::SourceSurface;
+
+namespace mozilla {
+namespace dom {
+
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need to have a filter applied to their results.
+ * All coordinates passed to the constructor are in device space.
+ */
+class AdjustedTargetForFilter
+{
+ //typedef BasicRenderingContext2D::RenderingClass RenderingClass;
+
+public:
+ typedef BasicRenderingContext2D::ContextState ContextState;
+
+ AdjustedTargetForFilter(BasicRenderingContext2D* aCtx,
+ DrawTarget* aFinalTarget,
+ const gfx::IntPoint& aFilterSpaceToTargetOffset,
+ const gfx::IntRect& aPreFilterBounds,
+ const gfx::IntRect& aPostFilterBounds,
+ gfx::CompositionOp aCompositionOp);
+
+ // Return a SourceSurface that contains the FillPaint or StrokePaint source.
+ already_AddRefed<SourceSurface>
+ DoSourcePaint(gfx::IntRect& aRect, BasicRenderingContext2D::Style aStyle);
+
+ ~AdjustedTargetForFilter();
+
+ DrawTarget* DT();
+
+private:
+ RefPtr<DrawTarget> mTarget;
+ RefPtr<DrawTarget> mFinalTarget;
+ BasicRenderingContext2D *mCtx;
+ gfx::IntRect mSourceGraphicRect;
+ gfx::IntRect mFillPaintRect;
+ gfx::IntRect mStrokePaintRect;
+ gfx::IntRect mPostFilterBounds;
+ gfx::IntPoint mOffset;
+ gfx::CompositionOp mCompositionOp;
+};
+
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need to have a shadow applied to their results.
+ * All coordinates passed to the constructor are in device space.
+ */
+class AdjustedTargetForShadow
+{
+public:
+ typedef BasicRenderingContext2D::ContextState ContextState;
+
+ AdjustedTargetForShadow(BasicRenderingContext2D* aCtx,
+ DrawTarget* aFinalTarget,
+ const gfx::Rect& aBounds,
+ gfx::CompositionOp aCompositionOp);
+
+ ~AdjustedTargetForShadow();
+
+ DrawTarget* DT();
+
+ gfx::IntPoint OffsetToFinalDT();
+
+private:
+ RefPtr<DrawTarget> mTarget;
+ RefPtr<DrawTarget> mFinalTarget;
+ BasicRenderingContext2D *mCtx;
+ Float mSigma;
+ gfx::IntRect mTempRect;
+ gfx::CompositionOp mCompositionOp;
+};
+
+/* This is an RAII based class that can be used as a drawtarget for
+ * operations that need a shadow or a filter drawn. It will automatically
+ * provide a temporary target when needed, and if so blend it back with a
+ * shadow, filter, or both.
+ * If both a shadow and a filter are needed, the filter is applied first,
+ * and the shadow is applied to the filtered results.
+ *
+ * aBounds specifies the bounds of the drawing operation that will be
+ * drawn to the target, it is given in device space! If this is nullptr the
+ * drawing operation will be assumed to cover the whole canvas.
+ */
+class AdjustedTarget
+{
+public:
+ typedef BasicRenderingContext2D::ContextState ContextState;
+
+ explicit AdjustedTarget(BasicRenderingContext2D* aCtx,
+ const gfx::Rect *aBounds = nullptr);
+
+ ~AdjustedTarget();
+
+ operator DrawTarget*()
+ {
+ return mTarget;
+ }
+
+ DrawTarget* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN;
+
+private:
+
+ gfx::Rect
+ MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds, BasicRenderingContext2D* aCtx);
+
+ gfx::Rect
+ MaxSourceNeededBoundsForShadow(const gfx::Rect& aDestBounds, BasicRenderingContext2D* aCtx);
+
+ gfx::Rect
+ BoundsAfterFilter(const gfx::Rect& aBounds, BasicRenderingContext2D* aCtx);
+
+ RefPtr<DrawTarget> mTarget;
+ UniquePtr<AdjustedTargetForShadow> mShadowTarget;
+ UniquePtr<AdjustedTargetForFilter> mFilterTarget;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // AdjustedTarget_h
\ No newline at end of file
--- a/dom/canvas/BasicRenderingContext2D.cpp
+++ b/dom/canvas/BasicRenderingContext2D.cpp
@@ -1,24 +1,37 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "AdjustedTarget.h"
+#include "CanvasUtils.h"
+#include "gfxUtils.h"
#include "mozilla/dom/BasicRenderingContext2D.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/ImageBitmap.h"
#include "nsContentUtils.h"
#include "nsICanvasRenderingContextInternal.h"
#include "nsPrintfCString.h"
#include "nsStyleUtil.h"
+using mozilla::gfx::AntialiasMode;
+using mozilla::gfx::CapStyle;
+using mozilla::gfx::Color;
+using mozilla::gfx::DrawOptions;
+using mozilla::gfx::ExtendMode;
+using mozilla::gfx::JoinStyle;
+using mozilla::gfx::IntSize;
+using mozilla::gfx::SamplingFilter;
using mozilla::gfx::SourceSurface;
+using mozilla::gfx::StrokeOptions;
+using mozilla::gfx::ToDeviceColor;
namespace mozilla {
namespace dom{
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CanvasGradient, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CanvasGradient, Release)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)
@@ -32,16 +45,39 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Ca
// Cap sigma to avoid overly large temp surfaces.
const mozilla::gfx::Float SIGMA_MAX = 100;
const size_t MAX_STYLE_STACK_SIZE = 1024;
/**
** BasicRenderingContext2D impl
**/
+bool
+BasicRenderingContext2D::PatternIsOpaque(BasicRenderingContext2D::Style aStyle) const
+{
+ const ContextState& state = CurrentState();
+ if (state.globalAlpha < 1.0) {
+ return false;
+ }
+
+ if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
+ return IsOpaqueFormat(state.patternStyles[aStyle]->mSurface->GetFormat());
+ }
+
+ // TODO: for gradient patterns we could check that all stops are opaque
+ // colors.
+
+ if (!state.gradientStyles[aStyle]) {
+ // it's a color pattern.
+ return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
+ }
+
+ return false;
+}
+
void
BasicRenderingContext2D::GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
Style aWhichStyle)
{
const ContextState &state = CurrentState();
if (state.patternStyles[aWhichStyle]) {
aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
@@ -498,16 +534,223 @@ BasicRenderingContext2D::SetShadowColor(
if (!ParseColor(aShadowColor, &color)) {
return;
}
CurrentState().shadowColor = color;
}
//
+// rects
+//
+
+static bool
+ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight, bool aIsZeroSizeValid)
+{
+ if (!aIsZeroSizeValid && (aWidth == 0.0 || aHeight == 0.0)) {
+ return false;
+ }
+
+ // bug 1018527
+ // The values of canvas API input are in double precision, but Moz2D APIs are
+ // using float precision. Bypass canvas API calls when the input is out of
+ // float precision to avoid precision problem
+ if (!std::isfinite((float)aX) | !std::isfinite((float)aY) |
+ !std::isfinite((float)aWidth) | !std::isfinite((float)aHeight)) {
+ return false;
+ }
+
+ // bug 1074733
+ // The canvas spec does not forbid rects with negative w or h, so given
+ // corners (x, y), (x+w, y), (x+w, y+h), and (x, y+h) we must generate
+ // the appropriate rect by flipping negative dimensions. This prevents
+ // draw targets from receiving "empty" rects later on.
+ if (aWidth < 0) {
+ aWidth = -aWidth;
+ aX -= aWidth;
+ }
+ if (aHeight < 0) {
+ aHeight = -aHeight;
+ aY -= aHeight;
+ }
+ return true;
+}
+
+void
+BasicRenderingContext2D::ClearRect(double aX, double aY, double aW,
+ double aH)
+{
+ // Do not allow zeros - it's a no-op at that point per spec.
+ if (!ValidateRect(aX, aY, aW, aH, false)) {
+ return;
+ }
+
+ gfx::Rect clearRect(aX, aY, aW, aH);
+
+ EnsureTarget(&clearRect);
+
+ mTarget->ClearRect(clearRect);
+
+ RedrawUser(gfxRect(aX, aY, aW, aH));
+}
+
+void
+BasicRenderingContext2D::FillRect(double aX, double aY, double aW,
+ double aH)
+{
+ const ContextState &state = CurrentState();
+
+ if (!ValidateRect(aX, aY, aW, aH, true)) {
+ return;
+ }
+
+ if (state.patternStyles[Style::FILL]) {
+ CanvasPattern::RepeatMode repeat =
+ state.patternStyles[Style::FILL]->mRepeat;
+ // In the FillRect case repeat modes are easy to deal with.
+ bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATY;
+ bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATX;
+
+ IntSize patternSize =
+ state.patternStyles[Style::FILL]->mSurface->GetSize();
+
+ // We always need to execute painting for non-over operators, even if
+ // we end up with w/h = 0.
+ if (limitx) {
+ if (aX < 0) {
+ aW += aX;
+ if (aW < 0) {
+ aW = 0;
+ }
+
+ aX = 0;
+ }
+ if (aX + aW > patternSize.width) {
+ aW = patternSize.width - aX;
+ if (aW < 0) {
+ aW = 0;
+ }
+ }
+ }
+ if (limity) {
+ if (aY < 0) {
+ aH += aY;
+ if (aH < 0) {
+ aH = 0;
+ }
+
+ aY = 0;
+ }
+ if (aY + aH > patternSize.height) {
+ aH = patternSize.height - aY;
+ if (aH < 0) {
+ aH = 0;
+ }
+ }
+ }
+ }
+
+ CompositionOp op = UsedOperation();
+ bool discardContent = PatternIsOpaque(Style::FILL)
+ && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
+
+ const gfx::Rect fillRect(aX, aY, aW, aH);
+ EnsureTarget(discardContent ? &fillRect : nullptr);
+
+ gfx::Rect bounds;
+ if (NeedToCalculateBounds()) {
+ bounds = mTarget->GetTransform().TransformBounds(fillRect);
+ }
+
+ AntialiasMode antialiasMode = CurrentState().imageSmoothingEnabled ?
+ AntialiasMode::DEFAULT : AntialiasMode::NONE;
+
+ AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+ FillRect(gfx::Rect(aX, aY, aW, aH),
+ CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
+ DrawOptions(state.globalAlpha, op, antialiasMode));
+
+ RedrawUser(gfxRect(aX, aY, aW, aH));
+}
+
+void
+BasicRenderingContext2D::StrokeRect(double aX, double aY, double aW,
+ double aH)
+{
+ const ContextState &state = CurrentState();
+
+ gfx::Rect bounds;
+
+ if (!aW && !aH) {
+ return;
+ }
+
+ if (!ValidateRect(aX, aY, aW, aH, true)) {
+ return;
+ }
+
+ EnsureTarget();
+ if (!IsTargetValid()) {
+ return;
+ }
+
+ if (NeedToCalculateBounds()) {
+ bounds = gfx::Rect(aX - state.lineWidth / 2.0f, aY - state.lineWidth / 2.0f,
+ aW + state.lineWidth, aH + state.lineWidth);
+ bounds = mTarget->GetTransform().TransformBounds(bounds);
+ }
+
+ if (!aH) {
+ CapStyle cap = CapStyle::BUTT;
+ if (state.lineJoin == JoinStyle::ROUND) {
+ cap = CapStyle::ROUND;
+ }
+ AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+ StrokeLine(Point(aX, aY), Point(aX + aW, aY),
+ CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
+ StrokeOptions(state.lineWidth, state.lineJoin,
+ cap, state.miterLimit,
+ state.dash.Length(),
+ state.dash.Elements(),
+ state.dashOffset),
+ DrawOptions(state.globalAlpha, UsedOperation()));
+ return;
+ }
+
+ if (!aW) {
+ CapStyle cap = CapStyle::BUTT;
+ if (state.lineJoin == JoinStyle::ROUND) {
+ cap = CapStyle::ROUND;
+ }
+ AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+ StrokeLine(Point(aX, aY), Point(aX, aY + aH),
+ CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
+ StrokeOptions(state.lineWidth, state.lineJoin,
+ cap, state.miterLimit,
+ state.dash.Length(),
+ state.dash.Elements(),
+ state.dashOffset),
+ DrawOptions(state.globalAlpha, UsedOperation()));
+ return;
+ }
+
+ AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
+ StrokeRect(gfx::Rect(aX, aY, aW, aH),
+ CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
+ StrokeOptions(state.lineWidth, state.lineJoin,
+ state.lineCap, state.miterLimit,
+ state.dash.Length(),
+ state.dash.Elements(),
+ state.dashOffset),
+ DrawOptions(state.globalAlpha, UsedOperation()));
+
+ Redraw();
+}
+
+//
// path bits
//
void
BasicRenderingContext2D::TransformWillUpdate()
{
EnsureTarget();
@@ -520,10 +763,68 @@ BasicRenderingContext2D::TransformWillUp
// transform the current mPath into device space.
// We should leave it alone.
mPathToDS = mTarget->GetTransform();
}
mPathTransformWillUpdate = true;
}
}
+Pattern&
+CanvasGeneralPattern::ForStyle(BasicRenderingContext2D* aCtx,
+ BasicRenderingContext2D::Style aStyle,
+ DrawTarget* aRT)
+{
+ // This should only be called once or the mPattern destructor will
+ // not be executed.
+ NS_ASSERTION(!mPattern.GetPattern(), "ForStyle() should only be called once on CanvasGeneralPattern!");
+
+ const BasicRenderingContext2D::ContextState &state = aCtx->CurrentState();
+
+ if (state.StyleIsColor(aStyle)) {
+ mPattern.InitColorPattern(ToDeviceColor(state.colorStyles[aStyle]));
+ } else if (state.gradientStyles[aStyle] &&
+ state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::LINEAR) {
+ CanvasLinearGradient *gradient =
+ static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
+
+ mPattern.InitLinearGradientPattern(gradient->mBegin, gradient->mEnd,
+ gradient->GetGradientStopsForTarget(aRT));
+ } else if (state.gradientStyles[aStyle] &&
+ state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::RADIAL) {
+ CanvasRadialGradient *gradient =
+ static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
+
+ mPattern.InitRadialGradientPattern(gradient->mCenter1, gradient->mCenter2,
+ gradient->mRadius1, gradient->mRadius2,
+ gradient->GetGradientStopsForTarget(aRT));
+ } else if (state.patternStyles[aStyle]) {
+ if (aCtx->GetCanvasElement()) {
+ CanvasUtils::DoDrawImageSecurityCheck(aCtx->GetCanvasElement(),
+ state.patternStyles[aStyle]->mPrincipal,
+ state.patternStyles[aStyle]->mForceWriteOnly,
+ state.patternStyles[aStyle]->mCORSUsed);
+ }
+
+ ExtendMode mode;
+ if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::RepeatMode::NOREPEAT) {
+ mode = ExtendMode::CLAMP;
+ } else {
+ mode = ExtendMode::REPEAT;
+ }
+
+ SamplingFilter samplingFilter;
+ if (state.imageSmoothingEnabled) {
+ samplingFilter = SamplingFilter::GOOD;
+ } else {
+ samplingFilter = SamplingFilter::POINT;
+ }
+
+ mPattern.InitSurfacePattern(state.patternStyles[aStyle]->mSurface, mode,
+ state.patternStyles[aStyle]->mTransform,
+ samplingFilter);
+ }
+
+ return *mPattern.GetPattern();
+}
+
} // namespace dom
} // namespace mozilla
--- a/dom/canvas/BasicRenderingContext2D.h
+++ b/dom/canvas/BasicRenderingContext2D.h
@@ -6,22 +6,26 @@
#define BasicRenderingContext2D_h
#include "FilterSupport.h"
#include "gfxTextRun.h"
#include "mozilla/dom/CanvasGradient.h"
#include "mozilla/dom/CanvasPattern.h"
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/PatternHelpers.h"
#include "nsStyleStruct.h"
#include "nsSVGEffects.h"
using mozilla::gfx::CompositionOp;
+using mozilla::gfx::DrawTarget;
using mozilla::gfx::FilterDescription;
using mozilla::gfx::Matrix;
+using mozilla::gfx::Pattern;
+using mozilla::gfx::GeneralPattern;
namespace mozilla {
namespace dom {
#define NS_BASICRENDERINGCONTEXT2D_IID \
{ 0xbd7f3f74, 0x5ed8, 0x4451, \
{ 0x89, 0x95, 0x1c, 0x6a, 0x82, 0xeb, 0xef, 0x9f} }
@@ -41,24 +45,32 @@ public:
SoftwareBackendMode,
OpenGLBackendMode,
DefaultBackendMode
};
// This is created lazily so it is necessary to call EnsureTarget before
// accessing it. In the event of an error it will be equal to
// sErrorTarget.
- RefPtr<mozilla::gfx::DrawTarget> mTarget;
+ RefPtr<DrawTarget> mTarget;
+
+ // this rect is in mTarget's current user space
+ virtual void RedrawUser(const gfxRect& aR) = 0;
+
+ virtual nsresult Redraw() = 0;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_BASICRENDERINGCONTEXT2D_IID)
protected:
- ~BasicRenderingContext2D(){}
+ virtual ~BasicRenderingContext2D() {}
public:
explicit BasicRenderingContext2D(layers::LayersBackend aCompositorBackend)
- : mPathTransformWillUpdate(false){};
+ // these are the default values from the Canvas spec
+ : mWidth(0), mHeight(0)
+ , mPathTransformWillUpdate(false) {}
+
//
// CanvasState
//
void Save();
void Restore();
//
// CanvasTransform
@@ -187,19 +199,19 @@ public:
StyleColorToString(CurrentState().shadowColor, aShadowColor);
}
void SetShadowColor(const nsAString& aShadowColor);
//
// CanvasRect
//
- virtual void ClearRect(double aX, double aY, double aW, double aH) = 0;
- virtual void FillRect(double aX, double aY, double aW, double aH) = 0;
- virtual void StrokeRect(double aX, double aY, double aW, double aH) = 0;
+ void ClearRect(double aX, double aY, double aW, double aH);
+ void FillRect(double aX, double aY, double aW, double aH);
+ void StrokeRect(double aX, double aY, double aW, double aH);
//
// CanvasDrawImage
//
virtual void DrawImage(const CanvasImageSource& aImage,
double aDx,
double aDy,
mozilla::ErrorResult& aError) = 0;
@@ -273,16 +285,21 @@ public:
double aRadiusX,
double aRadiusY,
double aRotation,
double aStartAngle,
double aEndAngle,
bool aAnticlockwise,
ErrorResult& aError) = 0;
protected:
+ friend class CanvasGeneralPattern;
+ friend class AdjustedTarget;
+ friend class AdjustedTargetForShadow;
+ friend class AdjustedTargetForFilter;
+
enum class Style : uint8_t {
STROKE = 0,
FILL,
MAX
};
enum class TextAlign : uint8_t {
START,
@@ -457,18 +474,21 @@ protected:
// We keep track of this to ensure that if this gets out of sync with the
// tainted state of the canvas itself, we update our filters accordingly.
bool filterSourceGraphicTainted;
bool imageSmoothingEnabled;
bool fontExplicitLanguage;
};
+ // Member vars
AutoTArray<ContextState, 3> mStyleStack;
+ int32_t mWidth, mHeight;
+
/**
* We also have a device space pathbuilder. The reason for this is as
* follows, when a path is being built, but the transform changes, we
* can no longer keep a single path in userspace, considering there's
* several 'user spaces' now. We therefore transform the current path
* into device space, and add all operations to this path in device
* space.
*
@@ -489,16 +509,18 @@ protected:
* mPath is always in user-space.
*/
RefPtr<mozilla::gfx::Path> mPath;
RefPtr<mozilla::gfx::PathBuilder> mPathBuilder;
bool mPathTransformWillUpdate;
Matrix mPathToDS;
protected:
+ virtual HTMLCanvasElement* GetCanvasElement() = 0;
+
virtual bool AlreadyShutDown() const = 0;
/**
* Create the backing surfacing, if it doesn't exist. If there is an error
* in creating the target then it will put sErrorTarget in place. If there
* is in turn an error in creating the sErrorTarget then they would both
* be null so IsTargetValid() would still return null.
*
@@ -546,17 +568,71 @@ protected:
// Returns whether a color was successfully parsed.
virtual bool ParseColor(const nsAString& aString, nscolor* aColor) = 0;
static void StyleColorToString(const nscolor& aColor, nsAString& aStr);
void GetStyleAsUnion(OwningStringOrCanvasGradientOrCanvasPattern& aValue,
Style aWhichStyle);
+
+
+ /**
+ * Returns true if we know for sure that the pattern for a given style is opaque.
+ * Usefull to know if we can discard the content below in certain situations.
+ */
+ bool PatternIsOpaque(Style aStyle) const;
+
+ mozilla::gfx::CompositionOp UsedOperation()
+ {
+ if (NeedToDrawShadow() || NeedToApplyFilter()) {
+ // In this case the shadow or filter rendering will use the operator.
+ return mozilla::gfx::CompositionOp::OP_OVER;
+ }
+
+ return CurrentState().op;
+ }
+
+ /**
+ * Returns true if a shadow should be drawn along with a
+ * drawing operation.
+ */
+ bool NeedToDrawShadow()
+ {
+ const ContextState& state = CurrentState();
+
+ // The spec says we should not draw shadows if the operator is OVER.
+ // If it's over and the alpha value is zero, nothing needs to be drawn.
+ return NS_GET_A(state.shadowColor) != 0 &&
+ (state.shadowBlur != 0.f || state.shadowOffset.x != 0.f || state.shadowOffset.y != 0.f);
+ }
+
+ /**
+ * Returns true if the result of a drawing operation should be
+ * drawn with a filter.
+ */
+ virtual bool NeedToApplyFilter() = 0;
+
+ bool NeedToCalculateBounds()
+ {
+ return NeedToDrawShadow() || NeedToApplyFilter();
+ }
};
NS_DEFINE_STATIC_IID_ACCESSOR(BasicRenderingContext2D,
NS_BASICRENDERINGCONTEXT2D_IID)
+// This class is named 'GeneralCanvasPattern' instead of just
+// 'GeneralPattern' to keep Windows PGO builds from confusing the
+// GeneralPattern class in gfxContext.cpp with this one.
+class CanvasGeneralPattern
+{
+public:
+ Pattern& ForStyle(BasicRenderingContext2D* aCtx,
+ BasicRenderingContext2D::Style aStyle, DrawTarget* aRT);
+
+ GeneralPattern mPattern;
+};
+
} // namespace dom
} // namespace mozilla
#endif /* BasicRenderingContext2D_h */
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -47,17 +47,16 @@
#include "ImageEncoder.h"
#include "ImageRegion.h"
#include "gfxContext.h"
#include "gfxPlatform.h"
#include "gfxFont.h"
#include "gfxBlur.h"
#include "gfxPrefs.h"
-#include "gfxUtils.h"
#include "nsFrameLoader.h"
#include "nsBidi.h"
#include "nsBidiPresUtils.h"
#include "Layers.h"
#include "LayerUserData.h"
#include "CanvasUtils.h"
#include "nsIMemoryReporter.h"
@@ -81,17 +80,16 @@
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Helpers.h"
#include "mozilla/gfx/Tools.h"
#include "mozilla/gfx/PathHelpers.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
-#include "mozilla/gfx/PatternHelpers.h"
#include "mozilla/gfx/Swizzle.h"
#include "mozilla/ipc/DocumentRendererParent.h"
#include "mozilla/ipc/PDocumentRendererParent.h"
#include "mozilla/layers/PersistentBufferProvider.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
@@ -178,492 +176,16 @@ public:
"(width * height * 4) bytes.");
return NS_OK;
}
};
NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)
-bool
-CanvasRenderingContext2D::PatternIsOpaque(CanvasRenderingContext2D::Style aStyle) const
-{
- const ContextState& state = CurrentState();
- if (state.globalAlpha < 1.0) {
- return false;
- }
-
- if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
- return IsOpaqueFormat(state.patternStyles[aStyle]->mSurface->GetFormat());
- }
-
- // TODO: for gradient patterns we could check that all stops are opaque
- // colors.
-
- if (!state.gradientStyles[aStyle]) {
- // it's a color pattern.
- return Color::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
- }
-
- return false;
-}
-
-// This class is named 'GeneralCanvasPattern' instead of just
-// 'GeneralPattern' to keep Windows PGO builds from confusing the
-// GeneralPattern class in gfxContext.cpp with this one.
-class CanvasGeneralPattern
-{
-public:
- typedef CanvasRenderingContext2D::Style Style;
- typedef CanvasRenderingContext2D::ContextState ContextState;
-
- Pattern& ForStyle(CanvasRenderingContext2D* aCtx,
- Style aStyle,
- DrawTarget* aRT)
- {
- // This should only be called once or the mPattern destructor will
- // not be executed.
- NS_ASSERTION(!mPattern.GetPattern(), "ForStyle() should only be called once on CanvasGeneralPattern!");
-
- const ContextState &state = aCtx->CurrentState();
-
- if (state.StyleIsColor(aStyle)) {
- mPattern.InitColorPattern(ToDeviceColor(state.colorStyles[aStyle]));
- } else if (state.gradientStyles[aStyle] &&
- state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::LINEAR) {
- CanvasLinearGradient *gradient =
- static_cast<CanvasLinearGradient*>(state.gradientStyles[aStyle].get());
-
- mPattern.InitLinearGradientPattern(gradient->mBegin, gradient->mEnd,
- gradient->GetGradientStopsForTarget(aRT));
- } else if (state.gradientStyles[aStyle] &&
- state.gradientStyles[aStyle]->GetType() == CanvasGradient::Type::RADIAL) {
- CanvasRadialGradient *gradient =
- static_cast<CanvasRadialGradient*>(state.gradientStyles[aStyle].get());
-
- mPattern.InitRadialGradientPattern(gradient->mCenter1, gradient->mCenter2,
- gradient->mRadius1, gradient->mRadius2,
- gradient->GetGradientStopsForTarget(aRT));
- } else if (state.patternStyles[aStyle]) {
- if (aCtx->mCanvasElement) {
- CanvasUtils::DoDrawImageSecurityCheck(aCtx->mCanvasElement,
- state.patternStyles[aStyle]->mPrincipal,
- state.patternStyles[aStyle]->mForceWriteOnly,
- state.patternStyles[aStyle]->mCORSUsed);
- }
-
- ExtendMode mode;
- if (state.patternStyles[aStyle]->mRepeat == CanvasPattern::RepeatMode::NOREPEAT) {
- mode = ExtendMode::CLAMP;
- } else {
- mode = ExtendMode::REPEAT;
- }
-
- SamplingFilter samplingFilter;
- if (state.imageSmoothingEnabled) {
- samplingFilter = SamplingFilter::GOOD;
- } else {
- samplingFilter = SamplingFilter::POINT;
- }
-
- mPattern.InitSurfacePattern(state.patternStyles[aStyle]->mSurface, mode,
- state.patternStyles[aStyle]->mTransform,
- samplingFilter);
- }
-
- return *mPattern.GetPattern();
- }
-
- GeneralPattern mPattern;
-};
-
-/* This is an RAII based class that can be used as a drawtarget for
- * operations that need to have a filter applied to their results.
- * All coordinates passed to the constructor are in device space.
- */
-class AdjustedTargetForFilter
-{
-public:
- typedef CanvasRenderingContext2D::ContextState ContextState;
-
- AdjustedTargetForFilter(CanvasRenderingContext2D* aCtx,
- DrawTarget* aFinalTarget,
- const gfx::IntPoint& aFilterSpaceToTargetOffset,
- const gfx::IntRect& aPreFilterBounds,
- const gfx::IntRect& aPostFilterBounds,
- gfx::CompositionOp aCompositionOp)
- : mCtx(nullptr)
- , mCompositionOp(aCompositionOp)
- {
- mCtx = aCtx;
- mFinalTarget = aFinalTarget;
- mPostFilterBounds = aPostFilterBounds;
- mOffset = aFilterSpaceToTargetOffset;
-
- nsIntRegion sourceGraphicNeededRegion;
- nsIntRegion fillPaintNeededRegion;
- nsIntRegion strokePaintNeededRegion;
-
- FilterSupport::ComputeSourceNeededRegions(
- aCtx->CurrentState().filter, mPostFilterBounds,
- sourceGraphicNeededRegion, fillPaintNeededRegion,
- strokePaintNeededRegion);
-
- mSourceGraphicRect = sourceGraphicNeededRegion.GetBounds();
- mFillPaintRect = fillPaintNeededRegion.GetBounds();
- mStrokePaintRect = strokePaintNeededRegion.GetBounds();
-
- mSourceGraphicRect = mSourceGraphicRect.Intersect(aPreFilterBounds);
-
- if (mSourceGraphicRect.IsEmpty()) {
- // The filter might not make any use of the source graphic. We need to
- // create a DrawTarget that we can return from DT() anyway, so we'll
- // just use a 1x1-sized one.
- mSourceGraphicRect.SizeTo(1, 1);
- }
-
- mTarget =
- mFinalTarget->CreateSimilarDrawTarget(mSourceGraphicRect.Size(), SurfaceFormat::B8G8R8A8);
-
- if (!mTarget) {
- // XXX - Deal with the situation where our temp size is too big to
- // fit in a texture (bug 1066622).
- mTarget = mFinalTarget;
- mCtx = nullptr;
- mFinalTarget = nullptr;
- return;
- }
-
- mTarget->SetTransform(
- mFinalTarget->GetTransform().PostTranslate(-mSourceGraphicRect.TopLeft() + mOffset));
- }
-
- // Return a SourceSurface that contains the FillPaint or StrokePaint source.
- already_AddRefed<SourceSurface>
- DoSourcePaint(gfx::IntRect& aRect, CanvasRenderingContext2D::Style aStyle)
- {
- if (aRect.IsEmpty()) {
- return nullptr;
- }
-
- RefPtr<DrawTarget> dt =
- mFinalTarget->CreateSimilarDrawTarget(aRect.Size(), SurfaceFormat::B8G8R8A8);
- if (!dt) {
- aRect.SetEmpty();
- return nullptr;
- }
-
- Matrix transform =
- mFinalTarget->GetTransform().PostTranslate(-aRect.TopLeft() + mOffset);
-
- dt->SetTransform(transform);
-
- if (transform.Invert()) {
- gfx::Rect dtBounds(0, 0, aRect.width, aRect.height);
- gfx::Rect fillRect = transform.TransformBounds(dtBounds);
- dt->FillRect(fillRect, CanvasGeneralPattern().ForStyle(mCtx, aStyle, dt));
- }
- return dt->Snapshot();
- }
-
- ~AdjustedTargetForFilter()
- {
- if (!mCtx) {
- return;
- }
-
- RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
-
- RefPtr<SourceSurface> fillPaint =
- DoSourcePaint(mFillPaintRect, CanvasRenderingContext2D::Style::FILL);
- RefPtr<SourceSurface> strokePaint =
- DoSourcePaint(mStrokePaintRect, CanvasRenderingContext2D::Style::STROKE);
-
- AutoRestoreTransform autoRestoreTransform(mFinalTarget);
- mFinalTarget->SetTransform(Matrix());
-
- MOZ_RELEASE_ASSERT(!mCtx->CurrentState().filter.mPrimitives.IsEmpty());
- gfx::FilterSupport::RenderFilterDescription(
- mFinalTarget, mCtx->CurrentState().filter,
- gfx::Rect(mPostFilterBounds),
- snapshot, mSourceGraphicRect,
- fillPaint, mFillPaintRect,
- strokePaint, mStrokePaintRect,
- mCtx->CurrentState().filterAdditionalImages,
- mPostFilterBounds.TopLeft() - mOffset,
- DrawOptions(1.0f, mCompositionOp));
-
- const gfx::FilterDescription& filter = mCtx->CurrentState().filter;
- MOZ_RELEASE_ASSERT(!filter.mPrimitives.IsEmpty());
- if (filter.mPrimitives.LastElement().IsTainted() && mCtx->mCanvasElement) {
- mCtx->mCanvasElement->SetWriteOnly();
- }
- }
-
- DrawTarget* DT()
- {
- return mTarget;
- }
-
-private:
- RefPtr<DrawTarget> mTarget;
- RefPtr<DrawTarget> mFinalTarget;
- CanvasRenderingContext2D *mCtx;
- gfx::IntRect mSourceGraphicRect;
- gfx::IntRect mFillPaintRect;
- gfx::IntRect mStrokePaintRect;
- gfx::IntRect mPostFilterBounds;
- gfx::IntPoint mOffset;
- gfx::CompositionOp mCompositionOp;
-};
-
-/* This is an RAII based class that can be used as a drawtarget for
- * operations that need to have a shadow applied to their results.
- * All coordinates passed to the constructor are in device space.
- */
-class AdjustedTargetForShadow
-{
-public:
- typedef CanvasRenderingContext2D::ContextState ContextState;
-
- AdjustedTargetForShadow(CanvasRenderingContext2D* aCtx,
- DrawTarget* aFinalTarget,
- const gfx::Rect& aBounds,
- gfx::CompositionOp aCompositionOp)
- : mCtx(nullptr)
- , mCompositionOp(aCompositionOp)
- {
- mCtx = aCtx;
- mFinalTarget = aFinalTarget;
-
- const ContextState &state = mCtx->CurrentState();
-
- mSigma = state.ShadowBlurSigma();
-
- gfx::Rect bounds = aBounds;
-
- int32_t blurRadius = state.ShadowBlurRadius();
-
- // We actually include the bounds of the shadow blur, this makes it
- // easier to execute the actual blur on hardware, and shouldn't affect
- // the amount of pixels that need to be touched.
- bounds.Inflate(blurRadius);
-
- bounds.RoundOut();
- bounds.ToIntRect(&mTempRect);
-
- mTarget =
- mFinalTarget->CreateShadowDrawTarget(mTempRect.Size(),
- SurfaceFormat::B8G8R8A8, mSigma);
-
- if (!mTarget) {
- // XXX - Deal with the situation where our temp size is too big to
- // fit in a texture (bug 1066622).
- mTarget = mFinalTarget;
- mCtx = nullptr;
- mFinalTarget = nullptr;
- } else {
- mTarget->SetTransform(
- mFinalTarget->GetTransform().PostTranslate(-mTempRect.TopLeft()));
- }
- }
-
- ~AdjustedTargetForShadow()
- {
- if (!mCtx) {
- return;
- }
-
- RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
-
- mFinalTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
- Color::FromABGR(mCtx->CurrentState().shadowColor),
- mCtx->CurrentState().shadowOffset, mSigma,
- mCompositionOp);
- }
-
- DrawTarget* DT()
- {
- return mTarget;
- }
-
- gfx::IntPoint OffsetToFinalDT()
- {
- return mTempRect.TopLeft();
- }
-
-private:
- RefPtr<DrawTarget> mTarget;
- RefPtr<DrawTarget> mFinalTarget;
- CanvasRenderingContext2D *mCtx;
- Float mSigma;
- gfx::IntRect mTempRect;
- gfx::CompositionOp mCompositionOp;
-};
-
-/* This is an RAII based class that can be used as a drawtarget for
- * operations that need a shadow or a filter drawn. It will automatically
- * provide a temporary target when needed, and if so blend it back with a
- * shadow, filter, or both.
- * If both a shadow and a filter are needed, the filter is applied first,
- * and the shadow is applied to the filtered results.
- *
- * aBounds specifies the bounds of the drawing operation that will be
- * drawn to the target, it is given in device space! If this is nullptr the
- * drawing operation will be assumed to cover the whole canvas.
- */
-class AdjustedTarget
-{
-public:
- typedef CanvasRenderingContext2D::ContextState ContextState;
-
- explicit AdjustedTarget(CanvasRenderingContext2D* aCtx,
- const gfx::Rect *aBounds = nullptr)
- {
- // There are operations that can invalidate aCtx->mTarget along the way,
- // so don't cache the pointer to it too soon.
- mTarget = nullptr;
-
- // All rects in this function are in the device space of ctx->mTarget.
-
- // In order to keep our temporary surfaces as small as possible, we first
- // calculate what their maximum required bounds would need to be if we
- // were to fill the whole canvas. Everything outside those bounds we don't
- // need to render.
- gfx::Rect r(0, 0, aCtx->mWidth, aCtx->mHeight);
- gfx::Rect maxSourceNeededBoundsForShadow =
- MaxSourceNeededBoundsForShadow(r, aCtx);
- gfx::Rect maxSourceNeededBoundsForFilter =
- MaxSourceNeededBoundsForFilter(maxSourceNeededBoundsForShadow, aCtx);
-
- gfx::Rect bounds = maxSourceNeededBoundsForFilter;
- if (aBounds) {
- bounds = bounds.Intersect(*aBounds);
- }
- gfx::Rect boundsAfterFilter = BoundsAfterFilter(bounds, aCtx);
-
- mozilla::gfx::CompositionOp op = aCtx->CurrentState().op;
-
- gfx::IntPoint offsetToFinalDT;
-
- // First set up the shadow draw target, because the shadow goes outside.
- // It applies to the post-filter results, if both a filter and a shadow
- // are used.
- if (aCtx->NeedToDrawShadow()) {
- mShadowTarget = MakeUnique<AdjustedTargetForShadow>(
- aCtx, aCtx->mTarget, boundsAfterFilter, op);
- mTarget = mShadowTarget->DT();
- offsetToFinalDT = mShadowTarget->OffsetToFinalDT();
-
- // If we also have a filter, the filter needs to be drawn with OP_OVER
- // because shadow drawing already applies op on the result.
- op = gfx::CompositionOp::OP_OVER;
- }
-
- // Now set up the filter draw target.
- if (aCtx->NeedToApplyFilter()) {
- bounds.RoundOut();
-
- if (!mTarget) {
- mTarget = aCtx->mTarget;
- }
- gfx::IntRect intBounds;
- if (!bounds.ToIntRect(&intBounds)) {
- return;
- }
- mFilterTarget = MakeUnique<AdjustedTargetForFilter>(
- aCtx, mTarget, offsetToFinalDT, intBounds,
- gfx::RoundedToInt(boundsAfterFilter), op);
- mTarget = mFilterTarget->DT();
- }
- if (!mTarget) {
- mTarget = aCtx->mTarget;
- }
- }
-
- ~AdjustedTarget()
- {
- // The order in which the targets are finalized is important.
- // Filters are inside, any shadow applies to the post-filter results.
- mFilterTarget.reset();
- mShadowTarget.reset();
- }
-
- operator DrawTarget*()
- {
- return mTarget;
- }
-
- DrawTarget* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN
- {
- return mTarget;
- }
-
-private:
-
- gfx::Rect
- MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds, CanvasRenderingContext2D* aCtx)
- {
- if (!aCtx->NeedToApplyFilter()) {
- return aDestBounds;
- }
-
- nsIntRegion sourceGraphicNeededRegion;
- nsIntRegion fillPaintNeededRegion;
- nsIntRegion strokePaintNeededRegion;
-
- FilterSupport::ComputeSourceNeededRegions(
- aCtx->CurrentState().filter, gfx::RoundedToInt(aDestBounds),
- sourceGraphicNeededRegion, fillPaintNeededRegion, strokePaintNeededRegion);
-
- return gfx::Rect(sourceGraphicNeededRegion.GetBounds());
- }
-
- gfx::Rect
- MaxSourceNeededBoundsForShadow(const gfx::Rect& aDestBounds, CanvasRenderingContext2D* aCtx)
- {
- if (!aCtx->NeedToDrawShadow()) {
- return aDestBounds;
- }
-
- const ContextState &state = aCtx->CurrentState();
- gfx::Rect sourceBounds = aDestBounds - state.shadowOffset;
- sourceBounds.Inflate(state.ShadowBlurRadius());
-
- // Union the shadow source with the original rect because we're going to
- // draw both.
- return sourceBounds.Union(aDestBounds);
- }
-
- gfx::Rect
- BoundsAfterFilter(const gfx::Rect& aBounds, CanvasRenderingContext2D* aCtx)
- {
- if (!aCtx->NeedToApplyFilter()) {
- return aBounds;
- }
-
- gfx::Rect bounds(aBounds);
- bounds.RoundOut();
-
- gfx::IntRect intBounds;
- if (!bounds.ToIntRect(&intBounds)) {
- return gfx::Rect();
- }
-
- nsIntRegion extents =
- gfx::FilterSupport::ComputePostFilterExtents(aCtx->CurrentState().filter,
- intBounds);
- return gfx::Rect(extents.GetBounds());
- }
-
- RefPtr<DrawTarget> mTarget;
- UniquePtr<AdjustedTargetForShadow> mShadowTarget;
- UniquePtr<AdjustedTargetForFilter> mFilterTarget;
-};
-
class CanvasShutdownObserver final : public nsIObserver
{
public:
explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas)
: mCanvas(aCanvas)
{}
NS_DECL_ISUPPORTS
@@ -966,18 +488,16 @@ static bool sMaxContextsInitialized = fa
static int32_t sMaxContexts = 0;
CanvasRenderingContext2D::CanvasRenderingContext2D(layers::LayersBackend aCompositorBackend)
: BasicRenderingContext2D(aCompositorBackend)
, mRenderingMode(RenderingMode::OpenGLBackendMode)
, mCompositorBackend(aCompositorBackend)
- // these are the default values from the Canvas spec
- , mWidth(0), mHeight(0)
, mZero(false), mOpaque(false)
, mResetLayer(true)
, mIPC(false)
, mIsSkiaGL(false)
, mHasPendingStableStateCallback(false)
, mDrawObserver(nullptr)
, mIsEntireFrameInvalid(false)
, mPredictManyRedrawCalls(false)
@@ -2481,223 +2001,16 @@ CanvasRenderingContext2D::UpdateFilter()
CurrentState().fontExplicitLanguage,
presShell->GetPresContext()),
gfxRect(0, 0, mWidth, mHeight),
CurrentState().filterAdditionalImages);
CurrentState().filterSourceGraphicTainted = sourceGraphicIsTainted;
}
//
-// rects
-//
-
-static bool
-ValidateRect(double& aX, double& aY, double& aWidth, double& aHeight, bool aIsZeroSizeValid)
-{
- if (!aIsZeroSizeValid && (aWidth == 0.0 || aHeight == 0.0)) {
- return false;
- }
-
- // bug 1018527
- // The values of canvas API input are in double precision, but Moz2D APIs are
- // using float precision. Bypass canvas API calls when the input is out of
- // float precision to avoid precision problem
- if (!std::isfinite((float)aX) | !std::isfinite((float)aY) |
- !std::isfinite((float)aWidth) | !std::isfinite((float)aHeight)) {
- return false;
- }
-
- // bug 1074733
- // The canvas spec does not forbid rects with negative w or h, so given
- // corners (x, y), (x+w, y), (x+w, y+h), and (x, y+h) we must generate
- // the appropriate rect by flipping negative dimensions. This prevents
- // draw targets from receiving "empty" rects later on.
- if (aWidth < 0) {
- aWidth = -aWidth;
- aX -= aWidth;
- }
- if (aHeight < 0) {
- aHeight = -aHeight;
- aY -= aHeight;
- }
- return true;
-}
-
-void
-CanvasRenderingContext2D::ClearRect(double aX, double aY, double aW,
- double aH)
-{
- // Do not allow zeros - it's a no-op at that point per spec.
- if (!ValidateRect(aX, aY, aW, aH, false)) {
- return;
- }
-
- gfx::Rect clearRect(aX, aY, aW, aH);
-
- EnsureTarget(&clearRect);
-
- mTarget->ClearRect(clearRect);
-
- RedrawUser(gfxRect(aX, aY, aW, aH));
-}
-
-void
-CanvasRenderingContext2D::FillRect(double aX, double aY, double aW,
- double aH)
-{
- const ContextState &state = CurrentState();
-
- if (!ValidateRect(aX, aY, aW, aH, true)) {
- return;
- }
-
- if (state.patternStyles[Style::FILL]) {
- CanvasPattern::RepeatMode repeat =
- state.patternStyles[Style::FILL]->mRepeat;
- // In the FillRect case repeat modes are easy to deal with.
- bool limitx = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATY;
- bool limity = repeat == CanvasPattern::RepeatMode::NOREPEAT || repeat == CanvasPattern::RepeatMode::REPEATX;
-
- IntSize patternSize =
- state.patternStyles[Style::FILL]->mSurface->GetSize();
-
- // We always need to execute painting for non-over operators, even if
- // we end up with w/h = 0.
- if (limitx) {
- if (aX < 0) {
- aW += aX;
- if (aW < 0) {
- aW = 0;
- }
-
- aX = 0;
- }
- if (aX + aW > patternSize.width) {
- aW = patternSize.width - aX;
- if (aW < 0) {
- aW = 0;
- }
- }
- }
- if (limity) {
- if (aY < 0) {
- aH += aY;
- if (aH < 0) {
- aH = 0;
- }
-
- aY = 0;
- }
- if (aY + aH > patternSize.height) {
- aH = patternSize.height - aY;
- if (aH < 0) {
- aH = 0;
- }
- }
- }
- }
-
- CompositionOp op = UsedOperation();
- bool discardContent = PatternIsOpaque(Style::FILL)
- && (op == CompositionOp::OP_OVER || op == CompositionOp::OP_SOURCE);
-
- const gfx::Rect fillRect(aX, aY, aW, aH);
- EnsureTarget(discardContent ? &fillRect : nullptr);
-
- gfx::Rect bounds;
- if (NeedToCalculateBounds()) {
- bounds = mTarget->GetTransform().TransformBounds(fillRect);
- }
-
- AntialiasMode antialiasMode = CurrentState().imageSmoothingEnabled ?
- AntialiasMode::DEFAULT : AntialiasMode::NONE;
-
- AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
- FillRect(gfx::Rect(aX, aY, aW, aH),
- CanvasGeneralPattern().ForStyle(this, Style::FILL, mTarget),
- DrawOptions(state.globalAlpha, op, antialiasMode));
-
- RedrawUser(gfxRect(aX, aY, aW, aH));
-}
-
-void
-CanvasRenderingContext2D::StrokeRect(double aX, double aY, double aW,
- double aH)
-{
- const ContextState &state = CurrentState();
-
- gfx::Rect bounds;
-
- if (!aW && !aH) {
- return;
- }
-
- if (!ValidateRect(aX, aY, aW, aH, true)) {
- return;
- }
-
- EnsureTarget();
- if (!IsTargetValid()) {
- return;
- }
-
- if (NeedToCalculateBounds()) {
- bounds = gfx::Rect(aX - state.lineWidth / 2.0f, aY - state.lineWidth / 2.0f,
- aW + state.lineWidth, aH + state.lineWidth);
- bounds = mTarget->GetTransform().TransformBounds(bounds);
- }
-
- if (!aH) {
- CapStyle cap = CapStyle::BUTT;
- if (state.lineJoin == JoinStyle::ROUND) {
- cap = CapStyle::ROUND;
- }
- AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
- StrokeLine(Point(aX, aY), Point(aX + aW, aY),
- CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
- StrokeOptions(state.lineWidth, state.lineJoin,
- cap, state.miterLimit,
- state.dash.Length(),
- state.dash.Elements(),
- state.dashOffset),
- DrawOptions(state.globalAlpha, UsedOperation()));
- return;
- }
-
- if (!aW) {
- CapStyle cap = CapStyle::BUTT;
- if (state.lineJoin == JoinStyle::ROUND) {
- cap = CapStyle::ROUND;
- }
- AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
- StrokeLine(Point(aX, aY), Point(aX, aY + aH),
- CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
- StrokeOptions(state.lineWidth, state.lineJoin,
- cap, state.miterLimit,
- state.dash.Length(),
- state.dash.Elements(),
- state.dashOffset),
- DrawOptions(state.globalAlpha, UsedOperation()));
- return;
- }
-
- AdjustedTarget(this, bounds.IsEmpty() ? nullptr : &bounds)->
- StrokeRect(gfx::Rect(aX, aY, aW, aH),
- CanvasGeneralPattern().ForStyle(this, Style::STROKE, mTarget),
- StrokeOptions(state.lineWidth, state.lineJoin,
- state.lineCap, state.miterLimit,
- state.dash.Length(),
- state.dash.Elements(),
- state.dashOffset),
- DrawOptions(state.globalAlpha, UsedOperation()));
-
- Redraw();
-}
-
-//
// path bits
//
void
CanvasRenderingContext2D::BeginPath()
{
mPath = nullptr;
mPathBuilder = nullptr;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -75,19 +75,16 @@ public:
}
void GetFilter(nsAString& aFilter)
{
aFilter = CurrentState().filterString;
}
void SetFilter(const nsAString& aFilter, mozilla::ErrorResult& aError);
- void ClearRect(double aX, double aY, double aW, double aH) override;
- void FillRect(double aX, double aY, double aW, double aH) override;
- void StrokeRect(double aX, double aY, double aW, double aH) override;
void BeginPath();
void Fill(const CanvasWindingRule& aWinding);
void Fill(const CanvasPath& aPath, const CanvasWindingRule& aWinding);
void Stroke();
void Stroke(const CanvasPath& aPath);
void DrawFocusIfNeeded(mozilla::dom::Element& aElement, ErrorResult& aRv);
bool DrawCustomFocusRing(mozilla::dom::Element& aElement);
void Clip(const CanvasWindingRule& aWinding);
@@ -289,17 +286,17 @@ public:
const nsAString& aBgColor, uint32_t aFlags,
mozilla::ErrorResult& aError);
bool SwitchRenderingMode(RenderingMode aRenderingMode);
// Eventually this should be deprecated. Keeping for now to keep the binding functional.
void Demote();
- nsresult Redraw();
+ virtual nsresult Redraw() override;
virtual int32_t GetWidth() const override;
virtual int32_t GetHeight() const override;
gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
// nsICanvasRenderingContextInternal
/**
* Gets the pres shell from either the canvas element or the doc shell
@@ -352,17 +349,17 @@ public:
/**
* An abstract base class to be implemented by callers wanting to be notified
* that a refresh has occurred. Callers must ensure an observer is removed
* before it is destroyed.
*/
virtual void DidRefresh() override;
// this rect is in mTarget's current user space
- void RedrawUser(const gfxRect& aR);
+ virtual void RedrawUser(const gfxRect& aR) override;
// nsISupports interface + CC
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(CanvasRenderingContext2D,
nsICanvasRenderingContextInternal)
enum class CanvasMultiGetterType : uint8_t {
STRING = 0,
@@ -411,16 +408,17 @@ public:
bool GetHitRegionRect(Element* aElement, nsRect& aRect) override;
void OnShutdown();
// Check the global setup, as well as the compositor type:
bool AllowOpenGLCanvas() const;
protected:
+ HTMLCanvasElement* GetCanvasElement() override { return mCanvasElement; }
nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
uint32_t aWidth, uint32_t aHeight,
JSObject** aRetval);
nsresult PutImageData_explicit(int32_t aX, int32_t aY, uint32_t aW, uint32_t aH,
dom::Uint8ClampedArray* aArray,
bool aHasDirtyRect, int32_t aDirtyX, int32_t aDirtyY,
int32_t aDirtyWidth, int32_t aDirtyHeight);
@@ -541,22 +539,16 @@ protected:
/**
* Returns the surface format this canvas should be allocated using. Takes
* into account mOpaque, platform requirements, etc.
*/
mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
/**
- * Returns true if we know for sure that the pattern for a given style is opaque.
- * Usefull to know if we can discard the content below in certain situations.
- */
- bool PatternIsOpaque(Style aStyle) const;
-
- /**
* Update CurrentState().filter with the filter description for
* CurrentState().filterChain.
* Flushes the PresShell, so the world can change if you call this function.
*/
void UpdateFilter();
nsLayoutUtils::SurfaceFromElementResult
CachedSurfaceFromElement(Element* aElement);
@@ -589,17 +581,16 @@ protected:
static void AddDemotableContext(CanvasRenderingContext2D* aContext);
static void RemoveDemotableContext(CanvasRenderingContext2D* aContext);
RenderingMode mRenderingMode;
layers::LayersBackend mCompositorBackend;
// Member vars
- int32_t mWidth, mHeight;
// This is true when the canvas is valid, but of zero size, this requires
// specific behavior on some operations.
bool mZero;
bool mOpaque;
// This is true when the next time our layer is retrieved we need to
@@ -693,34 +684,20 @@ protected:
RefPtr<Element> mElement;
// Path of the hit region in the 2d context coordinate space (not user space)
RefPtr<gfx::Path> mPath;
};
nsTArray<RegionInfo> mHitRegionsOptions;
/**
- * Returns true if a shadow should be drawn along with a
- * drawing operation.
- */
- bool NeedToDrawShadow()
- {
- const ContextState& state = CurrentState();
-
- // The spec says we should not draw shadows if the operator is OVER.
- // If it's over and the alpha value is zero, nothing needs to be drawn.
- return NS_GET_A(state.shadowColor) != 0 &&
- (state.shadowBlur != 0.f || state.shadowOffset.x != 0.f || state.shadowOffset.y != 0.f);
- }
-
- /**
* Returns true if the result of a drawing operation should be
* drawn with a filter.
*/
- bool NeedToApplyFilter()
+ virtual bool NeedToApplyFilter() override
{
return EnsureUpdatedFilter().mPrimitives.Length() > 0;
}
/**
* Calls UpdateFilter if the canvas's WriteOnly state has changed between the
* last call to UpdateFilter and now.
*/
@@ -729,33 +706,16 @@ protected:
if (CurrentState().filterSourceGraphicTainted != isWriteOnly) {
UpdateFilter();
EnsureTarget();
}
MOZ_ASSERT(CurrentState().filterSourceGraphicTainted == isWriteOnly);
return CurrentState().filter;
}
- bool NeedToCalculateBounds()
- {
- return NeedToDrawShadow() || NeedToApplyFilter();
- }
-
- mozilla::gfx::CompositionOp UsedOperation()
- {
- if (NeedToDrawShadow() || NeedToApplyFilter()) {
- // In this case the shadow or filter rendering will use the operator.
- return mozilla::gfx::CompositionOp::OP_OVER;
- }
-
- return CurrentState().op;
- }
-
- // text
-
protected:
enum class TextDrawOperation : uint8_t {
FILL,
STROKE,
MEASURE
};
protected:
@@ -769,17 +729,16 @@ protected:
float aX,
float aY,
const Optional<double>& aMaxWidth,
TextDrawOperation aOp,
float* aWidth);
bool CheckSizeForSkiaGL(mozilla::gfx::IntSize aSize);
- friend class CanvasGeneralPattern;
friend class CanvasFilterChainObserver;
friend class AdjustedTarget;
friend class AdjustedTargetForShadow;
friend class AdjustedTargetForFilter;
// other helpers
void GetAppUnitsValues(int32_t* aPerDevPixel, int32_t* aPerCSSPixel)
{
--- a/dom/canvas/WebGLFramebuffer.cpp
+++ b/dom/canvas/WebGLFramebuffer.cpp
@@ -1942,16 +1942,36 @@ template<typename C>
inline void
ImplCycleCollectionUnlink(C& field)
{
for (auto& cur : field) {
cur.Unlink();
}
}
+void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
+ std::vector<IndexedBufferBinding>& field,
+ const char* name, uint32_t flags)
+{
+ for (const auto& cur : field) {
+ ImplCycleCollectionTraverse(callback, cur.mBufferBinding, name, flags);
+ }
+}
+
+template <typename T>
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
+ WebGLRefPtr<T>& field,
+ const char* name,
+ uint32_t flags = 0)
+{
+ CycleCollectionNoteChild(callback, field.get(), name, flags);
+}
+
template<typename C>
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
const C& field,
const char* name,
uint32_t flags = 0)
{
for (auto& cur : field) {
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -68,16 +68,17 @@ EXPORTS.mozilla.dom += [
'ImageData.h',
'OffscreenCanvas.h',
'TextMetrics.h',
'WebGLVertexArrayObject.h',
]
# Canvas 2D and common sources
UNIFIED_SOURCES += [
+ 'AdjustedTarget.cpp',
'BasicRenderingContext2D.cpp',
'CanvasGradient.cpp',
'CanvasImageCache.cpp',
'CanvasPattern.cpp',
'CanvasRenderingContext2D.cpp',
'CanvasRenderingContextHelper.cpp',
'CanvasUtils.cpp',
'DocumentRendererChild.cpp',