Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/canvas/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 233 kB image not shown  

Quelle  CanvasRenderingContext2D.cpp   Sprache: C

 
/* -*- 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 "CanvasRenderingContext2D.h"

#include "mozilla/gfx/Helpers.h"
#include "nsCSSValue.h"
#include "nsXULElement.h"

#include "nsMathUtils.h"

#include "nsContentUtils.h"

#include "mozilla/intl/BidiEmbeddingLevel.h"
#include "mozilla/GeckoBindings.h"
#include "mozilla/PresShell.h"
#include "mozilla/PresShellInlines.h"
#include "mozilla/SVGImageContext.h"
#include "mozilla/SVGObserverUtils.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/FontFaceSetImpl.h"
#include "mozilla/dom/FontFaceSet.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/GeneratePlaceholderCanvasData.h"
#include "mozilla/dom/VideoFrame.h"
#include "mozilla/gfx/CanvasShutdownManager.h"
#include "nsPresContext.h"

#include "nsIInterfaceRequestorUtils.h"
#include "nsIFrame.h"
#include "nsError.h"

#include "nsCSSPseudoElements.h"
#include "nsComputedDOMStyle.h"

#include "nsPrintfCString.h"

#include "nsRFPService.h"
#include "nsReadableUtils.h"

#include "nsColor.h"
#include "nsGfxCIID.h"
#include "nsIDocShell.h"
#include "nsPIDOMWindow.h"
#include "nsDisplayList.h"
#include "nsFocusManager.h"

#include "nsTArray.h"

#include "ImageEncoder.h"
#include "ImageRegion.h"

#include "gfxContext.h"
#include "gfxPlatform.h"
#include "gfxFont.h"
#include "gfxBlur.h"
#include "gfxTextRun.h"
#include "gfxUtils.h"

#include "nsFrameLoader.h"
#include "nsBidiPresUtils.h"
#include "LayerUserData.h"
#include "CanvasUtils.h"
#include "nsIMemoryReporter.h"
#include "nsStyleUtil.h"
#include "CanvasImageCache.h"

#include <algorithm>

#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/Array.h"  // JS::GetArrayLength
#include "js/Conversions.h"
#include "js/experimental/TypedData.h"  // JS_NewUint8ClampedArray, JS_GetUint8ClampedArrayData
#include "js/HeapAPI.h"
#include "js/PropertyAndElement.h"  // JS_GetElement
#include "js/Warnings.h"            // JS::WarnASCII

#include "mozilla/Alignment.h"
#include "mozilla/Assertions.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/CanvasGradient.h"
#include "mozilla/dom/CanvasPattern.h"
#include "mozilla/dom/DOMMatrix.h"
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/ImageData.h"
#include "mozilla/dom/PBrowserParent.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/TypedArray.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/FilterInstance.h"
#include "mozilla/gfx/2D.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/layers/ImageBridgeChild.h"
#include "mozilla/layers/PersistentBufferProvider.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Preferences.h"
#include "mozilla/RestyleManager.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "nsCCUncollectableMarker.h"
#include "nsWrapperCacheInlines.h"
#include "mozilla/dom/CanvasRenderingContext2DBinding.h"
#include "mozilla/dom/CanvasPath.h"
#include "mozilla/dom/HTMLImageElement.h"
#include "mozilla/dom/HTMLVideoElement.h"
#include "mozilla/dom/SVGImageElement.h"
#include "mozilla/dom/TextMetrics.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Logging.h"
#include "nsGlobalWindowInner.h"
#include "nsDeviceContext.h"
#include "nsFontMetrics.h"
#include "nsLayoutUtils.h"
#include "Units.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/ServoCSSParser.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/SVGContentUtils.h"
#include "mozilla/layers/CanvasClient.h"
#include "mozilla/layers/WebRenderUserData.h"
#include "mozilla/layers/WebRenderCanvasRenderer.h"
#include "WindowRenderer.h"
#include "GeckoBindings.h"

#undef free  // apparently defined by some windows header, clashing with a
             // free() method in SkTypes.h

#ifdef XP_WIN
#  include "gfxWindowsPlatform.h"
#endif

// windows.h (included by chromium code) defines this, in its infinite wisdom
#undef DrawText

using namespace mozilla;
using namespace mozilla::CanvasUtils;
using namespace mozilla::css;
using namespace mozilla::gfx;
using namespace mozilla::image;
using namespace mozilla::ipc;
using namespace mozilla::layers;

namespace mozilla::dom {

// Cap sigma to avoid overly large temp surfaces.
const Float SIGMA_MAX = 100;

const size_t MAX_STYLE_STACK_SIZE = 1024;

/* Memory reporter stuff */
static Atomic<int64_t> gCanvasAzureMemoryUsed(0);

// Adds Save() / Restore() calls to the scope.
class MOZ_RAII AutoSaveRestore {
 public:
  explicit AutoSaveRestore(CanvasRenderingContext2D* aCtx) : mCtx(aCtx) {
    mCtx->Save();
  }
  ~AutoSaveRestore() { mCtx->Restore(); }

 private:
  RefPtr<CanvasRenderingContext2D> mCtx;
};

// This is KIND_OTHER because it's not always clear where in memory the pixels
// of a canvas are stored.  Furthermore, this memory will be tracked by the
// underlying surface implementations.  See bug 655638 for details.
class Canvas2dPixelsReporter final : public nsIMemoryReporter {
  ~Canvas2dPixelsReporter() = default;

 public:
  NS_DECL_ISUPPORTS

  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                            nsISupports* aData, bool aAnonymize) override {
    MOZ_COLLECT_REPORT("canvas-2d-pixels", KIND_OTHER, UNITS_BYTES,
                       gCanvasAzureMemoryUsed,
                       "Memory used by 2D canvases. Each canvas requires "
                       "(width * height * 4) bytes.");

    return NS_OK;
  }
};

NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter)

class CanvasConicGradient : public CanvasGradient {
 public:
  CanvasConicGradient(CanvasRenderingContext2D* aContext, Float aAngle,
                      const Point& aCenter)
      : CanvasGradient(aContext, Type::CONIC),
        mAngle(aAngle),
        mCenter(aCenter) {}

  const Float mAngle;
  const Point mCenter;
};

class CanvasRadialGradient : public CanvasGradient {
 public:
  CanvasRadialGradient(CanvasRenderingContext2D* aContext,
                       const Point& aBeginOrigin, Float aBeginRadius,
                       const Point& aEndOrigin, Float aEndRadius)
      : CanvasGradient(aContext, Type::RADIAL),
        mCenter1(aBeginOrigin),
        mCenter2(aEndOrigin),
        mRadius1(aBeginRadius),
        mRadius2(aEndRadius) {}

  Point mCenter1;
  Point mCenter2;
  Float mRadius1;
  Float mRadius2;
};

class CanvasLinearGradient : public CanvasGradient {
 public:
  CanvasLinearGradient(CanvasRenderingContext2D* aContext, const Point& aBegin,
                       const Point& aEnd)
      : CanvasGradient(aContext, Type::LINEAR), mBegin(aBegin), mEnd(aEnd) {}

 protected:
  friend struct CanvasBidiProcessor;
  friend class CanvasGeneralPattern;

  // Beginning of linear gradient.
  Point mBegin;
  // End of linear gradient.
  Point mEnd;
};

bool CanvasRenderingContext2D::PatternIsOpaque(
    CanvasRenderingContext2D::Style aStyle, bool* aIsColor) const {
  const ContextState& state = CurrentState();
  bool opaque = false;
  bool color = false;
  if (state.globalAlpha >= 1.0) {
    if (state.patternStyles[aStyle] && state.patternStyles[aStyle]->mSurface) {
      opaque = IsOpaque(state.patternStyles[aStyle]->mSurface->GetFormat());
    } else if (!state.gradientStyles[aStyle]) {
      // TODO: for gradient patterns we could check that all stops are opaque
      // colors.
      // it's a color pattern.
      opaque = sRGBColor::FromABGR(state.colorStyles[aStyle]).a >= 1.0;
      color = true;
    }
  }
  if (aIsColor) {
    *aIsColor = color;
  }
  return opaque;
}

// 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:
  using Style = CanvasRenderingContext2D::Style;
  using ContextState = CanvasRenderingContext2D::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) {
      auto 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) {
      auto gradient = static_cast<CanvasRadialGradient*>(
          state.gradientStyles[aStyle].get());

      mPattern.InitRadialGradientPattern(
          gradient->mCenter1, gradient->mCenter2, gradient->mRadius1,
          gradient->mRadius2, gradient->GetGradientStopsForTarget(aRT));
    } else if (state.gradientStyles[aStyle] &&
               state.gradientStyles[aStyle]->GetType() ==
                   CanvasGradient::Type::CONIC) {
      auto gradient =
          static_cast<CanvasConicGradient*>(state.gradientStyles[aStyle].get());

      mPattern.InitConicGradientPattern(
          gradient->mCenter, gradient->mAngle, 0, 1,
          gradient->GetGradientStopsForTarget(aRT));
    } else if (state.patternStyles[aStyle]) {
      aCtx->DoSecurityCheck(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:
  using ContextState = CanvasRenderingContext2D::ContextState;

  AdjustedTargetForFilter(CanvasRenderingContext2D* aCtx,
                          DrawTarget* aFinalTarget,
                          const gfx::IntPoint& aFilterSpaceToTargetOffset,
                          const gfx::IntRect& aPreFilterBounds,
                          const gfx::IntRect& aPostFilterBounds,
                          gfx::CompositionOp aCompositionOp)
      : mFinalTarget(aFinalTarget),
        mCtx(aCtx),
        mPostFilterBounds(aPostFilterBounds),
        mOffset(aFilterSpaceToTargetOffset),
        mCompositionOp(aCompositionOp) {
    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);
    }

    if (!mFinalTarget->CanCreateSimilarDrawTarget(mSourceGraphicRect.Size(),
                                                  SurfaceFormat::B8G8R8A8)) {
      mTarget = mFinalTarget;
      mCtx = nullptr;
      mFinalTarget = nullptr;
      return;
    }

    mTarget = mFinalTarget->CreateSimilarDrawTarget(mSourceGraphicRect.Size(),
                                                    SurfaceFormat::B8G8R8A8);

    if (mTarget) {
      // See bug 1524554.
      mTarget->ClearRect(gfx::Rect());
    }

    if (!mTarget || !mTarget->IsValid()) {
      // 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) {
      // See bug 1524554.
      dt->ClearRect(gfx::Rect());
    }

    if (!dt || !dt->IsValid()) {
      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()) {
      if (mCtx->mCanvasElement) {
        mCtx->mCanvasElement->SetWriteOnly();
      } else if (mCtx->mOffscreenCanvas) {
        mCtx->mOffscreenCanvas->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:
  using ContextState = CanvasRenderingContext2D::ContextState;

  AdjustedTargetForShadow(CanvasRenderingContext2D* aCtx,
                          DrawTarget* aFinalTarget, const gfx::Rect& aBounds,
                          gfx::CompositionOp aCompositionOp)
      : mFinalTarget(aFinalTarget), mCtx(aCtx), mCompositionOp(aCompositionOp) {
    const ContextState& state = mCtx->CurrentState();
    mSigma = state.ShadowBlurSigma();

    // 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.
    gfx::Rect bounds = aBounds;
    int32_t blurRadius = state.ShadowBlurRadius();
    bounds.Inflate(blurRadius);
    bounds.RoundOut();
    if (!bounds.ToIntRect(&mTempRect) ||
        !mFinalTarget->CanCreateSimilarDrawTarget(mTempRect.Size(),
                                                  SurfaceFormat::B8G8R8A8)) {
      mTarget = mFinalTarget;
      mCtx = nullptr;
      mFinalTarget = nullptr;
      return;
    }

    mTarget = mFinalTarget->CreateShadowDrawTarget(
        mTempRect.Size(), SurfaceFormat::B8G8R8A8, mSigma);

    if (mTarget) {
      // See bug 1524554.
      mTarget->ClearRect(gfx::Rect());
    }

    if (!mTarget || !mTarget->IsValid()) {
      // 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(),
        ShadowOptions(ToDeviceColor(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:
  using ContextState = CanvasRenderingContext2D::ContextState;

  explicit AdjustedTarget(CanvasRenderingContext2D* aCtx,
                          const gfx::Rect* aBounds = nullptr,
                          bool aAllowOptimization = false)
      : mCtx(aCtx),
        mOptimizeShadow(false),
        mUsedOperation(aCtx->CurrentState().op) {
    // 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);
    if (!aCtx->IsTargetValid()) {
      return;
    }

    gfx::Rect bounds = maxSourceNeededBoundsForFilter;
    if (aBounds) {
      bounds = bounds.Intersect(*aBounds);
    }
    gfx::Rect boundsAfterFilter = BoundsAfterFilter(bounds, aCtx);
    if (!aCtx->IsTargetValid() || !boundsAfterFilter.IsFinite()) {
      return;
    }

    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.
    const bool applyFilter = aCtx->NeedToApplyFilter();
    if (aCtx->NeedToDrawShadow()) {
      if (aAllowOptimization && !applyFilter) {
        // If only drawing a shadow and no filter, then avoid buffering to an
        // intermediate target while drawing the shadow directly to the final
        // target. When doing so, we want to use the actual composition op
        // instead of OP_OVER.
        mTarget = aCtx->mTarget;
        if (mTarget && mTarget->IsValid()) {
          mOptimizeShadow = true;
          return;
        }
      }
      mShadowTarget = MakeUnique<AdjustedTargetForShadow>(
          aCtx, aCtx->mTarget, boundsAfterFilter, mUsedOperation);
      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.
      mUsedOperation = CompositionOp::OP_OVER;
    }

    // Now set up the filter draw target.
    if (!aCtx->IsTargetValid()) {
      return;
    }
    if (applyFilter) {
      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), mUsedOperation);
      mTarget = mFilterTarget->DT();
      mUsedOperation = CompositionOp::OP_OVER;
    }
    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; }

  CompositionOp UsedOperation() const { return mUsedOperation; }

  bool UseOptimizeShadow() const { return mOptimizeShadow; }

  ShadowOptions ShadowParams() const {
    const ContextState& state = mCtx->CurrentState();
    return ShadowOptions(ToDeviceColor(state.shadowColor), state.shadowOffset,
                         state.ShadowBlurSigma());
  }

  void Fill(const Path* aPath, const Pattern& aPattern,
            const DrawOptions& aOptions) {
    if (mOptimizeShadow) {
      mTarget->DrawShadow(aPath, aPattern, ShadowParams(), aOptions);
    }
    mTarget->Fill(aPath, aPattern, aOptions);
  }

  void FillRect(const Rect& aRect, const Pattern& aPattern,
                const DrawOptions& aOptions) {
    if (mOptimizeShadow) {
      RefPtr<Path> path = MakePathForRect(*mTarget, aRect);
      mTarget->DrawShadow(path, aPattern, ShadowParams(), aOptions);
    }
    mTarget->FillRect(aRect, aPattern, aOptions);
  }

  void Stroke(const Path* aPath, const Pattern& aPattern,
              const StrokeOptions& aStrokeOptions,
              const DrawOptions& aOptions) {
    if (mOptimizeShadow) {
      mTarget->DrawShadow(aPath, aPattern, ShadowParams(), aOptions,
                          &aStrokeOptions);
    }
    mTarget->Stroke(aPath, aPattern, aStrokeOptions, aOptions);
  }

  void StrokeRect(const Rect& aRect, const Pattern& aPattern,
                  const StrokeOptions& aStrokeOptions,
                  const DrawOptions& aOptions) {
    if (mOptimizeShadow) {
      RefPtr<Path> path = MakePathForRect(*mTarget, aRect);
      mTarget->DrawShadow(path, aPattern, ShadowParams(), aOptions,
                          &aStrokeOptions);
    }
    mTarget->StrokeRect(aRect, aPattern, aStrokeOptions, aOptions);
  }

  void StrokeLine(const Point& aStart, const Point& aEnd,
                  const Pattern& aPattern, const StrokeOptions& aStrokeOptions,
                  const DrawOptions& aOptions) {
    if (mOptimizeShadow) {
      RefPtr<PathBuilder> builder = mTarget->CreatePathBuilder();
      builder->MoveTo(aStart);
      builder->LineTo(aEnd);
      RefPtr<Path> path = builder->Finish();
      mTarget->DrawShadow(path, aPattern, ShadowParams(), aOptions,
                          &aStrokeOptions);
    }
    mTarget->StrokeLine(aStart, aEnd, aPattern, aStrokeOptions, aOptions);
  }

  void DrawSurface(SourceSurface* aSurface, const Rect& aDest,
                   const Rect& aSource, const DrawSurfaceOptions& aSurfOptions,
                   const DrawOptions& aOptions) {
    if (mOptimizeShadow) {
      RefPtr<Path> path = MakePathForRect(*mTarget, aSource);
      ShadowOptions shadowParams(ShadowParams());
      SurfacePattern pattern(aSurface, ExtendMode::CLAMP, Matrix(),
                             shadowParams.BlurRadius() > 1
                                 ? SamplingFilter::POINT
                                 : aSurfOptions.mSamplingFilter);
      Matrix matrix = Matrix::Scaling(aDest.width / aSource.width,
                                      aDest.height / aSource.height);
      matrix.PreTranslate(-aSource.x, -aSource.y);
      matrix.PostTranslate(aDest.x, aDest.y);
      AutoRestoreTransform autoRestoreTransform(mTarget);
      mTarget->ConcatTransform(matrix);
      mTarget->DrawShadow(path, pattern, shadowParams, aOptions);
    }
    mTarget->DrawSurface(aSurface, aDest, aSource, aSurfOptions, aOptions);
  }

 private:
  gfx::Rect MaxSourceNeededBoundsForFilter(const gfx::Rect& aDestBounds,
                                           CanvasRenderingContext2D* aCtx) {
    const bool applyFilter = aCtx->NeedToApplyFilter();
    if (!aCtx->IsTargetValid()) {
      return aDestBounds;
    }
    if (!applyFilter) {
      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) {
    const bool applyFilter = aCtx->NeedToApplyFilter();
    if (!aCtx->IsTargetValid()) {
      return aBounds;
    }
    if (!applyFilter) {
      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());
  }

  CanvasRenderingContext2D* mCtx;
  bool mOptimizeShadow;
  CompositionOp mUsedOperation;
  RefPtr<DrawTarget> mTarget;
  UniquePtr<AdjustedTargetForShadow> mShadowTarget;
  UniquePtr<AdjustedTargetForFilter> mFilterTarget;
};

void CanvasPattern::SetTransform(const DOMMatrix2DInit& aInit,
                                 ErrorResult& aError) {
  RefPtr<DOMMatrixReadOnly> matrix =
      DOMMatrixReadOnly::FromMatrix(GetParentObject(), aInit, aError);
  if (aError.Failed()) {
    return;
  }
  const auto* matrix2D = matrix->GetInternal2D();
  if (!matrix2D->IsFinite()) {
    return;
  }
  mTransform = Matrix(*matrix2D);
}

void CanvasGradient::AddColorStop(float aOffset, const nsACString& aColorstr,
                                  ErrorResult& aRv) {
  if (aOffset < 0.0 || aOffset > 1.0) {
    return aRv.ThrowIndexSizeError("Offset out of 0-1.0 range");
  }

  if (!mContext) {
    return aRv.ThrowSyntaxError("No canvas context");
  }

  auto color = mContext->ParseColor(
      aColorstr, CanvasRenderingContext2D::ResolveCurrentColor::No);
  if (!color) {
    return aRv.ThrowSyntaxError("Invalid color");
  }

  GradientStop newStop;

  newStop.offset = aOffset;
  newStop.color = ToDeviceColor(*color);

  mRawStops.AppendElement(newStop);
}

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasGradient, mContext)

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CanvasPattern, mContext)

NS_IMPL_CYCLE_COLLECTING_ADDREF(CanvasRenderingContext2D)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CanvasRenderingContext2D)

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(CanvasRenderingContext2D)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CanvasRenderingContext2D)
  // Make sure we remove ourselves from the list of demotable contexts (raw
  // pointers), since we're logically destructed at this point.
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCanvasElement)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOffscreenCanvas)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocShell)
  for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
    ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::STROKE]);
    ImplCycleCollectionUnlink(tmp->mStyleStack[i].patternStyles[Style::FILL]);
    ImplCycleCollectionUnlink(
        tmp->mStyleStack[i].gradientStyles[Style::STROKE]);
    ImplCycleCollectionUnlink(tmp->mStyleStack[i].gradientStyles[Style::FILL]);
    if (auto* autoSVGFiltersObserver =
            tmp->mStyleStack[i].autoSVGFiltersObserver.get()) {
      /*
       * XXXjwatt: I don't think this is doing anything useful.  All we do under
       * this function is clear a raw C-style (i.e. not strong) pointer.  That's
       * clearly not helping in breaking any cycles.  The fact that we MOZ_CRASH
       * in OnRenderingChange if that pointer is null indicates that this isn't
       * even doing anything useful in terms of preventing further invalidation
       * from any observed filters.
       */

      autoSVGFiltersObserver->Detach();
    }
    ImplCycleCollectionUnlink(tmp->mStyleStack[i].autoSVGFiltersObserver);
  }
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CanvasRenderingContext2D)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCanvasElement)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOffscreenCanvas)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocShell)
  for (uint32_t i = 0; i < tmp->mStyleStack.Length(); i++) {
    ImplCycleCollectionTraverse(
        cb, tmp->mStyleStack[i].patternStyles[Style::STROKE],
        "Stroke CanvasPattern");
    ImplCycleCollectionTraverse(cb,
                                tmp->mStyleStack[i].patternStyles[Style::FILL],
                                "Fill CanvasPattern");
    ImplCycleCollectionTraverse(
        cb, tmp->mStyleStack[i].gradientStyles[Style::STROKE],
        "Stroke CanvasGradient");
    ImplCycleCollectionTraverse(cb,
                                tmp->mStyleStack[i].gradientStyles[Style::FILL],
                                "Fill CanvasGradient");
    ImplCycleCollectionTraverse(cb, tmp->mStyleStack[i].autoSVGFiltersObserver,
                                "RAII SVG Filters Observer");
  }
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CanvasRenderingContext2D)
  if (nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper()) {
    dom::Element* canvasElement = tmp->mCanvasElement;
    if (canvasElement) {
      if (canvasElement->IsPurple()) {
        canvasElement->RemovePurple();
      }
      dom::Element::MarkNodeChildren(canvasElement);
    }
    return true;
  }
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END

NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CanvasRenderingContext2D)
  return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END

NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CanvasRenderingContext2D)
  return nsCCUncollectableMarker::sGeneration && tmp->HasKnownLiveWrapper();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanvasRenderingContext2D)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

CanvasRenderingContext2D::ContextState::ContextState() = default;

CanvasRenderingContext2D::ContextState::ContextState(const ContextState& aOther)
    : fontGroup(aOther.fontGroup),
      fontLanguage(aOther.fontLanguage),
      fontFont(aOther.fontFont),
      gradientStyles(aOther.gradientStyles),
      patternStyles(aOther.patternStyles),
      colorStyles(aOther.colorStyles),
      font(aOther.font),
      textAlign(aOther.textAlign),
      textBaseline(aOther.textBaseline),
      textDirection(aOther.textDirection),
      fontKerning(aOther.fontKerning),
      fontStretch(aOther.fontStretch),
      fontVariantCaps(aOther.fontVariantCaps),
      textRendering(aOther.textRendering),
      letterSpacing(aOther.letterSpacing),
      wordSpacing(aOther.wordSpacing),
      fontLineHeight(aOther.fontLineHeight),
      letterSpacingStr(aOther.letterSpacingStr),
      wordSpacingStr(aOther.wordSpacingStr),
      shadowColor(aOther.shadowColor),
      transform(aOther.transform),
      shadowOffset(aOther.shadowOffset),
      lineWidth(aOther.lineWidth),
      miterLimit(aOther.miterLimit),
      globalAlpha(aOther.globalAlpha),
      shadowBlur(aOther.shadowBlur),
      dash(aOther.dash.Clone()),
      dashOffset(aOther.dashOffset),
      op(aOther.op),
      fillRule(aOther.fillRule),
      lineCap(aOther.lineCap),
      lineJoin(aOther.lineJoin),
      filterString(aOther.filterString),
      filterChain(aOther.filterChain),
      autoSVGFiltersObserver(aOther.autoSVGFiltersObserver),
      filter(aOther.filter),
      filterAdditionalImages(aOther.filterAdditionalImages.Clone()),
      filterSourceGraphicTainted(aOther.filterSourceGraphicTainted),
      imageSmoothingEnabled(aOther.imageSmoothingEnabled),
      fontExplicitLanguage(aOther.fontExplicitLanguage) {}

CanvasRenderingContext2D::ContextState::~ContextState() = default;

void CanvasRenderingContext2D::ContextState::SetColorStyle(Style aWhichStyle,
                                                           nscolor aColor) {
  colorStyles[aWhichStyle] = aColor;
  gradientStyles[aWhichStyle] = nullptr;
  patternStyles[aWhichStyle] = nullptr;
}

void CanvasRenderingContext2D::ContextState::SetPatternStyle(
    Style aWhichStyle, CanvasPattern* aPat) {
  gradientStyles[aWhichStyle] = nullptr;
  patternStyles[aWhichStyle] = aPat;
}

void CanvasRenderingContext2D::ContextState::SetGradientStyle(
    Style aWhichStyle, CanvasGradient* aGrad) {
  gradientStyles[aWhichStyle] = aGrad;
  patternStyles[aWhichStyle] = nullptr;
}

/**
 ** CanvasRenderingContext2D impl
 **/


// Initialize our static variables.
MOZ_THREAD_LOCAL(uintptr_t) CanvasRenderingContext2D::sNumLivingContexts;
MOZ_THREAD_LOCAL(DrawTarget*) CanvasRenderingContext2D::sErrorTarget;

// Helpers to map Canvas2D WebIDL enum values to gfx constants for rendering.
static JoinStyle CanvasToGfx(CanvasLineJoin aJoin) {
  switch (aJoin) {
    case CanvasLineJoin::Round:
      return JoinStyle::ROUND;
    case CanvasLineJoin::Bevel:
      return JoinStyle::BEVEL;
    case CanvasLineJoin::Miter:
      return JoinStyle::MITER_OR_BEVEL;
    default:
      MOZ_CRASH("unknown lineJoin!");
  }
}

static CapStyle CanvasToGfx(CanvasLineCap aCap) {
  switch (aCap) {
    case CanvasLineCap::Butt:
      return CapStyle::BUTT;
    case CanvasLineCap::Round:
      return CapStyle::ROUND;
    case CanvasLineCap::Square:
      return CapStyle::SQUARE;
    default:
      MOZ_CRASH("unknown lineCap!");
  }
}

static uint8_t CanvasToGfx(CanvasFontKerning aKerning) {
  switch (aKerning) {
    case CanvasFontKerning::Auto:
      return NS_FONT_KERNING_AUTO;
    case CanvasFontKerning::Normal:
      return NS_FONT_KERNING_NORMAL;
    case CanvasFontKerning::None:
      return NS_FONT_KERNING_NONE;
    default:
      MOZ_CRASH("unknown kerning!");
  }
}

CanvasRenderingContext2D::CanvasRenderingContext2D(
    layers::LayersBackend aCompositorBackend)
    :  // these are the default values from the Canvas spec
      mWidth(0),
      mHeight(0),
      mZero(false),
      mOpaqueAttrValue(false),
      mContextAttributesHasAlpha(true),
      mOpaque(false),
      mResetLayer(true),
      mIPC(false),
      mHasPendingStableStateCallback(false),
      mIsEntireFrameInvalid(false),
      mPredictManyRedrawCalls(false),
      mFrameCaptureState(FrameCaptureState::CLEAN,
                         "CanvasRenderingContext2D::mFrameCaptureState"),
      mInvalidateCount(0),
      mWriteOnly(false) {
  sNumLivingContexts.infallibleInit();
  sErrorTarget.infallibleInit();
  sNumLivingContexts.set(sNumLivingContexts.get() + 1);
}

CanvasRenderingContext2D::~CanvasRenderingContext2D() {
  CanvasImageCache::NotifyCanvasDestroyed(this);
  RemovePostRefreshObserver();
  RemoveShutdownObserver();
  ResetBitmap();

  sNumLivingContexts.set(sNumLivingContexts.get() - 1);
  if (sNumLivingContexts.get() == 0 && sErrorTarget.get()) {
    RefPtr<DrawTarget> target = dont_AddRef(sErrorTarget.get());
    sErrorTarget.set(nullptr);
  }
}

nsresult CanvasRenderingContext2D::Initialize() {
  if (NS_WARN_IF(!AddShutdownObserver())) {
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

JSObject* CanvasRenderingContext2D::WrapObject(
    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
  return CanvasRenderingContext2D_Binding::Wrap(aCx, this, aGivenProto);
}

void CanvasRenderingContext2D::GetContextAttributes(
    CanvasRenderingContext2DSettings& aSettings) const {
  aSettings = CanvasRenderingContext2DSettings();

  aSettings.mAlpha = mContextAttributesHasAlpha;
  aSettings.mWillReadFrequently = mWillReadFrequently;
  aSettings.mForceSoftwareRendering = mForceSoftwareRendering;

  // We don't support the 'desynchronized' and 'colorSpace' attributes, so
  // those just keep their default values.
}

void CanvasRenderingContext2D::GetDebugInfo(
    bool aEnsureTarget, CanvasRenderingContext2DDebugInfo& aDebugInfo,
    ErrorResult& aError) {
  if (aEnsureTarget && !EnsureTarget(aError)) {
    return;
  }

  if (!mBufferProvider) {
    aError.ThrowInvalidStateError("No buffer provider available");
    return;
  }

  if (!mTarget) {
    aError.ThrowInvalidStateError("No target available");
    return;
  }

  aDebugInfo.mIsAccelerated = mBufferProvider->IsAccelerated();
  aDebugInfo.mIsShared = mBufferProvider->IsShared();
  aDebugInfo.mBackendType = static_cast<int8_t>(mTarget->GetBackendType());
  aDebugInfo.mDrawTargetType = static_cast<int8_t>(mTarget->GetType());
}

CanvasRenderingContext2D::ColorStyleCacheEntry
CanvasRenderingContext2D::ParseColorSlow(const nsACString& aString) {
  ColorStyleCacheEntry result{nsCString(aString)};
  Document* document = mCanvasElement ? mCanvasElement->OwnerDoc() : nullptr;
  css::Loader* loader = document ? document->CSSLoader() : nullptr;

  PresShell* presShell = GetPresShell();
  ServoStyleSet* set = presShell ? presShell->StyleSet() : nullptr;
  bool wasCurrentColor = false;
  nscolor color;
  if (ServoCSSParser::ComputeColor(set, NS_RGB(0, 0, 0), aString, &color,
                                   &wasCurrentColor, loader)) {
    result.mWasCurrentColor = wasCurrentColor;
    result.mColor.emplace(color);
  }

  return result;
}

Maybe<nscolor> CanvasRenderingContext2D::ParseColor(
    const nsACString& aString, ResolveCurrentColor aResolveCurrentColor) {
  auto entry = mColorStyleCache.Lookup(aString);
  if (!entry) {
    entry.Set(ParseColorSlow(aString));
  }

  const auto& data = entry.Data();
  if (data.mWasCurrentColor && mCanvasElement &&
      aResolveCurrentColor == ResolveCurrentColor::Yes) {
    // If it was currentColor, get the value of the color property, flushing
    // style if necessary.
    RefPtr<const ComputedStyle> canvasStyle =
        nsComputedDOMStyle::GetComputedStyle(mCanvasElement);
    if (canvasStyle) {
      return Some(canvasStyle->StyleText()->mColor.ToColor());
    }
  }
  return data.mColor;
}

void CanvasRenderingContext2D::ResetBitmap(bool aFreeBuffer) {
  if (mCanvasElement) {
    mCanvasElement->InvalidateCanvas();
  }

  // only do this for non-docshell created contexts,
  // since those are the ones that we created a surface for
  if (mTarget && IsTargetValid() && !mDocShell) {
    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
  }

  bool forceReset = true;
  ReturnTarget(forceReset);
  mTarget = nullptr;
  if (aFreeBuffer) {
    mBufferProvider = nullptr;
  } else if (mBufferProvider) {
    // Try to keep the buffer around. However, we still need to clear the
    // contents as if it was recreated before next use.
    mBufferNeedsClear = true;
  }

  // Since the target changes the backing texture will change, and this will
  // no longer be valid.
  mIsEntireFrameInvalid = false;
  mPredictManyRedrawCalls = false;
  mFrameCaptureState = FrameCaptureState::CLEAN;
}

void CanvasRenderingContext2D::OnShutdown() {
  RefPtr<PersistentBufferProvider> provider = mBufferProvider;

  ResetBitmap();

  if (provider) {
    provider->OnShutdown();
  }

  if (mOffscreenCanvas) {
    mOffscreenCanvas->Destroy();
  }

  mHasShutdown = true;
}

bool CanvasRenderingContext2D::AddShutdownObserver() {
  autoconst canvasManager = CanvasShutdownManager::Get();
  if (NS_WARN_IF(!canvasManager)) {
    mHasShutdown = true;
    return false;
  }

  canvasManager->AddShutdownObserver(this);
  return true;
}

void CanvasRenderingContext2D::RemoveShutdownObserver() {
  autoconst canvasManager = CanvasShutdownManager::MaybeGet();
  if (!canvasManager) {
    return;
  }

  canvasManager->RemoveShutdownObserver(this);
}

void CanvasRenderingContext2D::OnRemoteCanvasLost() {
  // We only lose context / data if we are using remote canvas, which is only
  // for accelerated targets.
  if (!mBufferProvider || !mBufferProvider->IsAccelerated() || mIsContextLost) {
    return;
  }

  // 2. Set context's context lost to true.
  mIsContextLost = mAllowContextRestore = true;

  // 3. Reset the rendering context to its default state given context.
  ClearTarget();

  // We dispatch because it isn't safe to call into the script event handlers,
  // and we don't want to mutate our state in CanvasShutdownManager.
  NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction(
      "CanvasRenderingContext2D::OnRemoteCanvasLost", [self = RefPtr{this}] {
        // 4. Let shouldRestore be the result of firing an event named
        // contextlost at canvas, with the cancelable attribute initialized to
        // true.
        self->mAllowContextRestore = self->DispatchEvent(
            u"contextlost"_ns, CanBubble::eNo, Cancelable::eYes);
      }));
}

void CanvasRenderingContext2D::OnRemoteCanvasRestored() {
  // We never lost our context if it was not a remote canvas, nor can we restore
  // if we have already shutdown.
  if (mHasShutdown || !mIsContextLost || !mAllowContextRestore) {
    return;
  }

  // We dispatch because it isn't safe to call into the script event handlers,
  // and we don't want to mutate our state in CanvasShutdownManager.
  NS_DispatchToCurrentThread(NS_NewCancelableRunnableFunction(
      "CanvasRenderingContext2D::OnRemoteCanvasRestored",
      [self = RefPtr{this}] {
        // 5. If shouldRestore is false, then abort these steps.
        if (!self->mHasShutdown && self->mIsContextLost &&
            self->mAllowContextRestore) {
          // 7. Set context's context lost to false.
          self->mIsContextLost = false;

          // 6. Attempt to restore context by creating a backing storage using
          // context's attributes and associating them with context. If this
          // fails, then abort these steps.
          if (!self->EnsureTarget()) {
            self->mIsContextLost = true;
            return;
          }

          // 8. Fire an event named contextrestored at canvas.
          self->DispatchEvent(u"contextrestored"_ns, CanBubble::eNo,
                              Cancelable::eNo);
        }
      }));
}

void CanvasRenderingContext2D::SetStyleFromString(const nsACString& aStr,
                                                  Style aWhichStyle) {
  MOZ_ASSERT(!aStr.IsVoid());

  Maybe<nscolor> color = ParseColor(aStr);
  if (!color) {
    return;
  }

  CurrentState().SetColorStyle(aWhichStyle, *color);
}

void CanvasRenderingContext2D::GetStyleAsUnion(
    OwningUTF8StringOrCanvasGradientOrCanvasPattern& aValue,
    Style aWhichStyle) {
  const ContextState& state = CurrentState();
  if (state.patternStyles[aWhichStyle]) {
    aValue.SetAsCanvasPattern() = state.patternStyles[aWhichStyle];
  } else if (state.gradientStyles[aWhichStyle]) {
    aValue.SetAsCanvasGradient() = state.gradientStyles[aWhichStyle];
  } else {
    StyleColorToString(state.colorStyles[aWhichStyle],
                       aValue.SetAsUTF8String());
  }
}

// static
void CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor,
                                                  nsACString& aStr) {
  aStr.Truncate();
  // We can't reuse the normal CSS color stringification code,
  // because the spec calls for a different algorithm for canvas.
  if (NS_GET_A(aColor) == 255) {
    aStr.AppendPrintf("#%02x%02x%02x", NS_GET_R(aColor), NS_GET_G(aColor),
                      NS_GET_B(aColor));
  } else {
    aStr.AppendPrintf("rgba(%d, %d, %d, ", NS_GET_R(aColor), NS_GET_G(aColor),
                      NS_GET_B(aColor));
    aStr.AppendFloat(nsStyleUtil::ColorComponentToFloat(NS_GET_A(aColor)));
    aStr.Append(')');
  }
}

nsresult CanvasRenderingContext2D::Redraw() {
  mFrameCaptureState = FrameCaptureState::DIRTY;

  if (mIsEntireFrameInvalid) {
    return NS_OK;
  }

  mIsEntireFrameInvalid = true;

  if (mCanvasElement) {
    SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
    mCanvasElement->InvalidateCanvasContent(nullptr);
  } else if (mOffscreenCanvas) {
    mOffscreenCanvas->QueueCommitToCompositor();
  } else {
    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
  }

  return NS_OK;
}

void CanvasRenderingContext2D::Redraw(const gfx::Rect& aR) {
  mFrameCaptureState = FrameCaptureState::DIRTY;

  ++mInvalidateCount;

  if (mIsEntireFrameInvalid) {
    return;
  }

  if (mPredictManyRedrawCalls || mInvalidateCount > kCanvasMaxInvalidateCount) {
    Redraw();
    return;
  }

  if (mCanvasElement) {
    SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
    mCanvasElement->InvalidateCanvasContent(&aR);
  } else if (mOffscreenCanvas) {
    mOffscreenCanvas->QueueCommitToCompositor();
  } else {
    NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
  }
}

void CanvasRenderingContext2D::DidRefresh() {}

void CanvasRenderingContext2D::RedrawUser(const gfxRect& aR) {
  mFrameCaptureState = FrameCaptureState::DIRTY;

  if (mIsEntireFrameInvalid) {
    ++mInvalidateCount;
    return;
  }

  gfx::Rect newr = mTarget->GetTransform().TransformBounds(ToRect(aR));
  Redraw(newr);
}

bool CanvasRenderingContext2D::CopyBufferProvider(
    PersistentBufferProvider& aOld, DrawTarget& aTarget, IntRect aCopyRect) {
  // Borrowing the snapshot must be done after ReturnTarget.
  RefPtr<SourceSurface> snapshot = aOld.BorrowSnapshot();

  if (!snapshot) {
    return false;
  }

  aTarget.CopySurface(snapshot, aCopyRect, IntPoint());

  aOld.ReturnSnapshot(snapshot.forget());
  return true;
}

void CanvasRenderingContext2D::Demote() {}

void CanvasRenderingContext2D::ScheduleStableStateCallback() {
  if (mHasPendingStableStateCallback) {
    return;
  }
  mHasPendingStableStateCallback = true;

  nsContentUtils::RunInStableState(
      NewRunnableMethod("dom::CanvasRenderingContext2D::OnStableState"this,
                        &CanvasRenderingContext2D::OnStableState));
}

void CanvasRenderingContext2D::OnStableState() {
  if (!mHasPendingStableStateCallback) {
    return;
  }

  ReturnTarget();

  mHasPendingStableStateCallback = false;
}

void CanvasRenderingContext2D::RestoreClipsAndTransformToTarget() {
  // Restore clips and transform.
  mTarget->SetTransform(Matrix());

  if (mTarget->GetBackendType() == gfx::BackendType::CAIRO) {
    // Cairo doesn't play well with huge clips. When given a very big clip it
    // will try to allocate big mask surface without taking the target
    // size into account which can cause OOM. See bug 1034593.
    // This limits the clip extents to the size of the canvas.
    // A fix in Cairo would probably be preferable, but requires somewhat
    // invasive changes.
    mTarget->PushClipRect(gfx::Rect(0, 0, mWidth, mHeight));
  }

  for (auto& style : mStyleStack) {
    for (auto& clipOrTransform : style.clipsAndTransforms) {
      if (clipOrTransform.IsClip()) {
        if (mClipsNeedConverting) {
          // We have possibly changed backends, so we need to convert the clips
          // in case they are no longer compatible with mTarget.
          RefPtr<PathBuilder> pathBuilder = mTarget->CreatePathBuilder();
          clipOrTransform.clip->StreamToSink(pathBuilder);
          clipOrTransform.clip = pathBuilder->Finish();
        }
        mTarget->PushClip(clipOrTransform.clip);
      } else {
        mTarget->SetTransform(clipOrTransform.transform);
      }
    }
  }

  mClipsNeedConverting = false;
}

bool CanvasRenderingContext2D::BorrowTarget(const IntRect& aPersistedRect,
                                            bool aNeedsClear) {
  // We are attempting to request a DrawTarget from the current
  // PersistentBufferProvider. However, if the provider needs to be refreshed,
  // or if it is accelerated and the application has requested that we disallow
  // acceleration, then we skip trying to use this provider so that it will be
  // recreated by EnsureTarget later.
  if (!mBufferProvider || mBufferProvider->RequiresRefresh() ||
      (mBufferProvider->IsAccelerated() && UseSoftwareRendering())) {
    return false;
  }
  mTarget = mBufferProvider->BorrowDrawTarget(aPersistedRect);
  if (!mTarget || !mTarget->IsValid()) {
    if (mTarget) {
      mBufferProvider->ReturnDrawTarget(mTarget.forget());
    }
    return false;
  }
  if (mBufferNeedsClear) {
    if (mBufferProvider->PreservesDrawingState()) {
      // If the buffer provider preserves the clip and transform state, then
      // we must ensure it is cleared before reusing the target.
      if (!mTarget->RemoveAllClips()) {
        mBufferProvider->ReturnDrawTarget(mTarget.forget());
        return false;
      }
      mTarget->SetTransform(Matrix());
    }
    // If the canvas was reset, then we need to clear the target in case its
    // contents was somehow preserved. We only need to clear the target if
    // the operation doesn't fill the entire canvas.
    if (aNeedsClear) {
      mTarget->ClearRect(gfx::Rect(mTarget->GetRect()));
    }
  }
  if (!mBufferProvider->PreservesDrawingState() || mBufferNeedsClear) {
    RestoreClipsAndTransformToTarget();
  }
  mBufferNeedsClear = false;
  return true;
}

bool CanvasRenderingContext2D::EnsureTarget(ErrorResult& aError,
                                            const gfx::Rect* aCoveredRect,
                                            bool aWillClear,
                                            bool aSkipTransform) {
  if (AlreadyShutDown()) {
    gfxCriticalNoteOnce << "Attempt to render into a Canvas2d after shutdown.";
    SetErrorState();
    aError.ThrowInvalidStateError(
        "Cannot use canvas after shutdown initiated.");
    return false;
  }

  // The spec doesn't say what to do in this case, but Chrome silently fails
  // without throwing an error. We should at least throw if the canvas is
  // permanently disabled.
  if (NS_WARN_IF(mIsContextLost)) {
    if (!mAllowContextRestore) {
      aError.ThrowInvalidStateError(
          "Cannot use canvas as context is lost forever.");
    }
    return false;
  }

  if (mTarget) {
    if (mTarget == sErrorTarget.get()) {
      aError.ThrowInvalidStateError("Canvas is already in error state.");
      return false;
    }
    return true;
  }

  // Check that the dimensions are sane
  if (mWidth > StaticPrefs::gfx_canvas_max_size() ||
      mHeight > StaticPrefs::gfx_canvas_max_size()) {
    SetErrorState();
    aError.ThrowInvalidStateError("Canvas exceeds max size.");
    return false;
  }

  if (mWidth < 0 || mHeight < 0) {
    SetErrorState();
    aError.ThrowInvalidStateError("Canvas has invalid size.");
    return false;
  }

  // If the next drawing command covers the entire canvas, we can skip copying
  // from the previous frame and/or clearing the canvas.
  gfx::Rect canvasRect(0, 0, mWidth, mHeight);
  bool canDiscardContent =
      aCoveredRect &&
      (aSkipTransform ? *aCoveredRect
                      : CurrentState().transform.TransformBounds(*aCoveredRect))
          .Contains(canvasRect);

  // If a clip is active we don't know for sure that the next drawing command
  // will really cover the entire canvas.
  for (const auto& style : mStyleStack) {
    if (!canDiscardContent) {
      break;
    }
    for (const auto& clipOrTransform : style.clipsAndTransforms) {
      if (clipOrTransform.IsClip()) {
        canDiscardContent = false;
        break;
      }
    }
  }

  ScheduleStableStateCallback();

  IntRect persistedRect = canDiscardContent || mBufferNeedsClear
                              ? IntRect()
                              : IntRect(0, 0, mWidth, mHeight);

  // Attempt to reuse the existing buffer provider.
  if (BorrowTarget(persistedRect, !canDiscardContent)) {
    return true;
  }

  RefPtr<DrawTarget> newTarget;
  RefPtr<PersistentBufferProvider> newProvider;

  if (!TryAcceleratedTarget(newTarget, newProvider) &&
      !TrySharedTarget(newTarget, newProvider) &&
      !TryBasicTarget(newTarget, newProvider, aError)) {
    gfxCriticalError(
        CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(GetSize())))
        << "Failed borrow shared and basic targets.";

    SetErrorState();
    return false;
  }

  MOZ_ASSERT(newTarget);
  MOZ_ASSERT(newProvider);

  bool needsClear =
      !canDiscardContent || (mBufferProvider && mBufferNeedsClear);
  if (newTarget->GetBackendType() == gfx::BackendType::SKIA &&
      (needsClear || !aWillClear)) {
    // Skia expects the unused X channel to contains 0xFF even for opaque
    // operations so we can't skip clearing in that case, even if we are going
    // to cover the entire canvas in the next drawing operation.
    newTarget->ClearRect(canvasRect);
    needsClear = false;
  }

  // Try to copy data from the previous buffer provider if there is one.
  if (!canDiscardContent && mBufferProvider && !mBufferNeedsClear &&
      CopyBufferProvider(*mBufferProvider, *newTarget, persistedRect)) {
    needsClear = false;
  }

  if (needsClear) {
    newTarget->ClearRect(canvasRect);
  }

  // Ensure any Path state is compatible with the type of DrawTarget used. This
  // may require making a copy with the correct type if they (rarely) mismatch.
  if (mPathBuilder &&
      mPathBuilder->GetBackendType() != newTarget->GetBackendType()) {
    RefPtr<Path> path = mPathBuilder->Finish();
    mPathBuilder = newTarget->CreatePathBuilder(path->GetFillRule());
    path->StreamToSink(mPathBuilder);
  }
  if (mPath && mPath->GetBackendType() != newTarget->GetBackendType()) {
    RefPtr<PathBuilder> builder =
        newTarget->CreatePathBuilder(mPath->GetFillRule());
    mPath->StreamToSink(builder);
    mPath = builder->Finish();
  }

  mTarget = std::move(newTarget);
  mBufferProvider = std::move(newProvider);
  mBufferNeedsClear = false;

  RegisterAllocation();
  AddZoneWaitingForGC();

  RestoreClipsAndTransformToTarget();

  // Force a full layer transaction since we didn't have a layer before
  // and now we might need one.
  if (mCanvasElement) {
    mCanvasElement->InvalidateCanvas();
  }
  // EnsureTarget hasn't drawn anything. Preserve mFrameCaptureState.
  FrameCaptureState captureState = mFrameCaptureState;
  // Calling Redraw() tells our invalidation machinery that the entire
  // canvas is already invalid, which can speed up future drawing.
  Redraw();
  mFrameCaptureState = captureState;

  return true;
}

void CanvasRenderingContext2D::SetInitialState() {
  // Set up the initial canvas defaults
  mPathBuilder = nullptr;
  mPath = nullptr;
  mPathPruned = false;
  mPathTransform = Matrix();
  mPathTransformDirty = false;

  mStyleStack.Clear();
  ContextState* state = mStyleStack.AppendElement();
  state->globalAlpha = 1.0;

  state->colorStyles[Style::FILL] = NS_RGB(0, 0, 0);
  state->colorStyles[Style::STROKE] = NS_RGB(0, 0, 0);
  state->shadowColor = NS_RGBA(0, 0, 0, 0);
}

void CanvasRenderingContext2D::SetErrorState() {
  EnsureErrorTarget();

  if (mTarget && mTarget != sErrorTarget.get()) {
    gCanvasAzureMemoryUsed -= mWidth * mHeight * 4;
  }

  mTarget = sErrorTarget.get();
  mBufferProvider = nullptr;

  // clear transforms, clips, etc.
  SetInitialState();
}

void CanvasRenderingContext2D::RegisterAllocation() {
  // XXX - It would make more sense to track the allocation in
  // PeristentBufferProvider, rather than here.
  static bool registered = false;
  // FIXME: Disable the reporter for now, see bug 1241865
  if (!registered && false) {
    registered = true;
    RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
  }
}

void CanvasRenderingContext2D::AddZoneWaitingForGC() {
  JSObject* wrapper = GetWrapperPreserveColor();
  if (wrapper) {
    CycleCollectedJSRuntime::Get()->AddZoneWaitingForGC(
        JS::GetObjectZone(wrapper));
  }
}

static WindowRenderer* WindowRendererFromCanvasElement(
    nsINode* aCanvasElement) {
  if (!aCanvasElement) {
    return nullptr;
  }

  return nsContentUtils::WindowRendererForDocument(aCanvasElement->OwnerDoc());
}

bool CanvasRenderingContext2D::TryAcceleratedTarget(
    RefPtr<gfx::DrawTarget>& aOutDT,
    RefPtr<layers::PersistentBufferProvider>& aOutProvider) {
  if (!XRE_IsContentProcess()) {
    // Only allow accelerated contexts to be created in a content process to
    // ensure it is remoted appropriately and run on the correct parent or
    // GPU process threads.
    return false;
  }
  if (mBufferProvider && mBufferProvider->IsAccelerated() &&
      mBufferProvider->RequiresRefresh()) {
    // If there is already a provider and we got here, then the provider needs
    // to be refreshed and we should avoid using acceleration in the future.
    mAllowAcceleration = false;
  }
  // Don't try creating an accelerate DrawTarget if either acceleration failed
  // previously or if the application expects acceleration to be slow.
  if (!mAllowAcceleration || UseSoftwareRendering()) {
    return false;
  }

  if (mCanvasElement) {
    MOZ_ASSERT(NS_IsMainThread());

    WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement);
    if (NS_WARN_IF(!renderer)) {
      return false;
    }

    aOutProvider = PersistentBufferProviderAccelerated::Create(
        GetSize(), GetSurfaceFormat(), renderer->AsKnowsCompositor());
  } else if (mOffscreenCanvas &&
             StaticPrefs::gfx_canvas_remote_allow_offscreen()) {
    RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
    if (NS_WARN_IF(!imageBridge)) {
      return false;
    }

    aOutProvider = PersistentBufferProviderAccelerated::Create(
        GetSize(), GetSurfaceFormat(), imageBridge);
  }

  if (!aOutProvider) {
    return false;
  }
  aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
  MOZ_ASSERT(aOutDT);
  return !!aOutDT;
}

bool CanvasRenderingContext2D::TrySharedTarget(
    RefPtr<gfx::DrawTarget>& aOutDT,
    RefPtr<layers::PersistentBufferProvider>& aOutProvider) {
  aOutDT = nullptr;
  aOutProvider = nullptr;

  if (mBufferProvider && mBufferProvider->IsShared()) {
    // we are already using a shared buffer provider, we are allocating a new
    // one because the current one failed so let's just fall back to the basic
    // provider.
    mClipsNeedConverting = true;
    return false;
  }

  if (mCanvasElement) {
    MOZ_ASSERT(NS_IsMainThread());

    WindowRenderer* renderer = WindowRendererFromCanvasElement(mCanvasElement);
    if (NS_WARN_IF(!renderer)) {
      return false;
    }

    aOutProvider = renderer->CreatePersistentBufferProvider(
        GetSize(), GetSurfaceFormat(),
        !mAllowAcceleration || UseSoftwareRendering());
  } else if (mOffscreenCanvas) {
    if (!StaticPrefs::gfx_offscreencanvas_shared_provider()) {
      return false;
    }

    RefPtr<layers::ImageBridgeChild> imageBridge =
        layers::ImageBridgeChild::GetSingleton();
    if (NS_WARN_IF(!imageBridge)) {
      return false;
    }

    aOutProvider = PersistentBufferProviderShared::Create(
        GetSize(), GetSurfaceFormat(), imageBridge,
        !mAllowAcceleration || UseSoftwareRendering(),
        mOffscreenCanvas->GetWindowID());
  }

  if (!aOutProvider) {
    return false;
  }

  // We can pass an empty persisted rect since we just created the buffer
  // provider (nothing to restore).
  aOutDT = aOutProvider->BorrowDrawTarget(IntRect());
  MOZ_ASSERT(aOutDT);

  return !!aOutDT;
}

bool CanvasRenderingContext2D::TryBasicTarget(
    RefPtr<gfx::DrawTarget>& aOutDT,
    RefPtr<layers::PersistentBufferProvider>& aOutProvider,
    ErrorResult& aError) {
  aOutDT = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(
      GetSize(), GetSurfaceFormat(), UseSoftwareRendering());
  if (!aOutDT) {
    aError.ThrowInvalidStateError("Canvas could not create basic draw target.");
    return false;
  }

  // See Bug 1524554 - this forces DT initialization.
  aOutDT->ClearRect(gfx::Rect());

  if (!aOutDT->IsValid()) {
    aOutDT = nullptr;
    aError.ThrowInvalidStateError("Canvas could not init basic draw target.");
    return false;
  }

  aOutProvider = new PersistentBufferProviderBasic(aOutDT);
  return true;
}

PersistentBufferProvider* CanvasRenderingContext2D::GetBufferProvider() {
  if (mBufferProvider && mBufferNeedsClear) {
    // Force the buffer to clear before it is used.
    EnsureTarget();
  }
  return mBufferProvider;
}

Maybe<SurfaceDescriptor> CanvasRenderingContext2D::GetFrontBuffer(
    WebGLFramebufferJS*, const bool webvr) {
  if (auto* provider = GetBufferProvider()) {
    return provider->GetFrontBuffer();
  }
  return Nothing();
}

already_AddRefed<layers::FwdTransactionTracker>
CanvasRenderingContext2D::UseCompositableForwarder(
    layers::CompositableForwarder* aForwarder) {
  if (mBufferProvider) {
    return mBufferProvider->UseCompositableForwarder(aForwarder);
  }
  return nullptr;
}

PresShell* CanvasRenderingContext2D::GetPresShell() {
  if (mCanvasElement) {
    return mCanvasElement->OwnerDoc()->GetPresShell();
  }
  if (mDocShell) {
    return mDocShell->GetPresShell();
  }
  return nullptr;
}

NS_IMETHODIMP
CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight) {
  // Zero sized surfaces can cause problems.
  mZero = false;
  if (aHeight == 0) {
    aHeight = 1;
    mZero = true;
  }
  if (aWidth == 0) {
    aWidth = 1;
    mZero = true;
  }

  ClearTarget(aWidth, aHeight);

  return NS_OK;
}

void CanvasRenderingContext2D::AddAssociatedMemory() {
  JSObject* wrapper = GetWrapperMaybeDead();
  if (wrapper) {
    JS::AddAssociatedMemory(wrapper, BindingJSObjectMallocBytes(this),
                            JS::MemoryUse::DOMBinding);
  }
}

void CanvasRenderingContext2D::RemoveAssociatedMemory() {
  JSObject* wrapper = GetWrapperMaybeDead();
  if (wrapper) {
    JS::RemoveAssociatedMemory(wrapper, BindingJSObjectMallocBytes(this),
                               JS::MemoryUse::DOMBinding);
  }
}

void CanvasRenderingContext2D::ClearTarget(int32_t aWidth, int32_t aHeight) {
  // Only free the buffer provider if the size no longer matches.
  bool freeBuffer = aWidth != mWidth || aHeight != mHeight;
  ResetBitmap(freeBuffer);

  mResetLayer = true;

  SetInitialState();

  // Update dimensions only if new (strictly positive) values were passed.
  if (aWidth > 0 && aHeight > 0) {
    // Update the memory size associated with the wrapper object when we change
    // the dimensions. Note that we need to keep updating dying wrappers before
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=92 H=99 G=95

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.