Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(838)

Unified Diff: third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp

Issue 1732923002: Revert of Pull up a subset of CanvasRenderingContext2D into BaseRenderingContext2D. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
diff --git a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
index 5e847445d44d2524d35180eb8c46827bb14edf91..fdcfd12a44b4b55eb6ac77b05aa4155e49ea5428 100644
--- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.cpp
@@ -37,19 +37,27 @@
#include "bindings/core/v8/ExceptionStatePlaceholder.h"
#include "core/CSSPropertyNames.h"
#include "core/css/StylePropertySet.h"
+#include "core/css/parser/CSSParser.h"
#include "core/css/resolver/StyleResolver.h"
#include "core/dom/AXObjectCache.h"
#include "core/dom/StyleEngine.h"
#include "core/events/Event.h"
+#include "core/frame/ImageBitmap.h"
#include "core/frame/Settings.h"
+#include "core/html/HTMLVideoElement.h"
+#include "core/html/ImageData.h"
#include "core/html/TextMetrics.h"
#include "core/html/canvas/CanvasFontCache.h"
#include "core/layout/LayoutBox.h"
#include "core/layout/LayoutTheme.h"
+#include "modules/canvas2d/CanvasGradient.h"
+#include "modules/canvas2d/CanvasPattern.h"
+#include "modules/canvas2d/CanvasRenderingContext2DState.h"
#include "modules/canvas2d/CanvasStyle.h"
#include "modules/canvas2d/HitRegion.h"
#include "modules/canvas2d/Path2D.h"
#include "platform/fonts/FontCache.h"
+#include "platform/geometry/FloatQuad.h"
#include "platform/graphics/DrawLooperBuilder.h"
#include "platform/graphics/ExpensiveCanvasHeuristicParameters.h"
#include "platform/graphics/ImageBuffer.h"
@@ -110,6 +118,7 @@
CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, const CanvasContextCreationAttributes& attrs, Document& document)
: CanvasRenderingContext(canvas)
+ , m_clipAntialiasing(NotAntiAliased)
, m_hasAlpha(attrs.alpha())
, m_contextLostMode(NotLostContext)
, m_contextRestorable(true)
@@ -121,6 +130,7 @@
{
if (document.settings() && document.settings()->antialiasedClips2dCanvasEnabled())
m_clipAntialiasing = AntiAliased;
+ m_stateStack.append(CanvasRenderingContext2DState::create());
setShouldAntialias(true);
#if ENABLE(OILPAN)
ThreadState::current()->registerPreFinalizer(this);
@@ -160,6 +170,12 @@
ASSERT(static_cast<size_t>(skCanvas->getSaveCount()) == m_stateStack.size());
}
#endif
+}
+
+CanvasRenderingContext2DState& CanvasRenderingContext2D::modifiableState()
+{
+ realizeSaves();
+ return *m_stateStack.last();
}
bool CanvasRenderingContext2D::isAccelerated() const
@@ -214,9 +230,9 @@
DEFINE_TRACE(CanvasRenderingContext2D)
{
+ visitor->trace(m_stateStack);
visitor->trace(m_hitRegionManager);
CanvasRenderingContext::trace(visitor);
- BaseRenderingContext2D::trace(visitor);
}
void CanvasRenderingContext2D::dispatchContextLostEvent(Timer<CanvasRenderingContext2D>*)
@@ -302,9 +318,832 @@
c->restore();
}
+void CanvasRenderingContext2D::realizeSaves()
+{
+ validateStateStack();
+ if (state().hasUnrealizedSaves()) {
+ ASSERT(m_stateStack.size() >= 1);
+ // Reduce the current state's unrealized count by one now,
+ // to reflect the fact we are saving one state.
+ m_stateStack.last()->restore();
+ m_stateStack.append(CanvasRenderingContext2DState::create(state(), CanvasRenderingContext2DState::DontCopyClipList));
+ // Set the new state's unrealized count to 0, because it has no outstanding saves.
+ // We need to do this explicitly because the copy constructor and operator= used
+ // by the Vector operations copy the unrealized count from the previous state (in
+ // turn necessary to support correct resizing and unwinding of the stack).
+ m_stateStack.last()->resetUnrealizedSaveCount();
+ SkCanvas* canvas = drawingCanvas();
+ if (canvas)
+ canvas->save();
+ validateStateStack();
+ }
+}
+
+void CanvasRenderingContext2D::save()
+{
+ m_stateStack.last()->save();
+}
+
+void CanvasRenderingContext2D::restore()
+{
+ validateStateStack();
+ if (state().hasUnrealizedSaves()) {
+ // We never realized the save, so just record that it was unnecessary.
+ m_stateStack.last()->restore();
+ return;
+ }
+ ASSERT(m_stateStack.size() >= 1);
+ if (m_stateStack.size() <= 1)
+ return;
+ m_path.transform(state().transform());
+ m_stateStack.removeLast();
+ m_stateStack.last()->clearResolvedFilter();
+ m_path.transform(state().transform().inverse());
+ SkCanvas* c = drawingCanvas();
+ if (c)
+ c->restore();
+
+ validateStateStack();
+}
+
+static inline void convertCanvasStyleToUnionType(CanvasStyle* style, StringOrCanvasGradientOrCanvasPattern& returnValue)
+{
+ if (CanvasGradient* gradient = style->canvasGradient()) {
+ returnValue.setCanvasGradient(gradient);
+ return;
+ }
+ if (CanvasPattern* pattern = style->canvasPattern()) {
+ returnValue.setCanvasPattern(pattern);
+ return;
+ }
+ returnValue.setString(style->color());
+}
+
+void CanvasRenderingContext2D::strokeStyle(StringOrCanvasGradientOrCanvasPattern& returnValue) const
+{
+ convertCanvasStyleToUnionType(state().strokeStyle(), returnValue);
+}
+
+void CanvasRenderingContext2D::setStrokeStyle(const StringOrCanvasGradientOrCanvasPattern& style)
+{
+ ASSERT(!style.isNull());
+
+ String colorString;
+ CanvasStyle* canvasStyle = nullptr;
+ if (style.isString()) {
+ colorString = style.getAsString();
+ if (colorString == state().unparsedStrokeColor())
+ return;
+ Color parsedColor = 0;
+ if (!parseColorOrCurrentColor(parsedColor, colorString, canvas()))
+ return;
+ if (state().strokeStyle()->isEquivalentRGBA(parsedColor.rgb())) {
+ modifiableState().setUnparsedStrokeColor(colorString);
+ return;
+ }
+ canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb());
+ } else if (style.isCanvasGradient()) {
+ canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient());
+ } else if (style.isCanvasPattern()) {
+ CanvasPattern* canvasPattern = style.getAsCanvasPattern();
+
+ if (canvas()->originClean() && !canvasPattern->originClean())
+ canvas()->setOriginTainted();
+
+ canvasStyle = CanvasStyle::createFromPattern(canvasPattern);
+ }
+
+ ASSERT(canvasStyle);
+
+ modifiableState().setStrokeStyle(canvasStyle);
+ modifiableState().setUnparsedStrokeColor(colorString);
+ modifiableState().clearResolvedFilter();
+}
+
+void CanvasRenderingContext2D::fillStyle(StringOrCanvasGradientOrCanvasPattern& returnValue) const
+{
+ convertCanvasStyleToUnionType(state().fillStyle(), returnValue);
+}
+
+void CanvasRenderingContext2D::setFillStyle(const StringOrCanvasGradientOrCanvasPattern& style)
+{
+ ASSERT(!style.isNull());
+ validateStateStack();
+ String colorString;
+ CanvasStyle* canvasStyle = nullptr;
+ if (style.isString()) {
+ colorString = style.getAsString();
+ if (colorString == state().unparsedFillColor())
+ return;
+ Color parsedColor = 0;
+ if (!parseColorOrCurrentColor(parsedColor, colorString, canvas()))
+ return;
+ if (state().fillStyle()->isEquivalentRGBA(parsedColor.rgb())) {
+ modifiableState().setUnparsedFillColor(colorString);
+ return;
+ }
+ canvasStyle = CanvasStyle::createFromRGBA(parsedColor.rgb());
+ } else if (style.isCanvasGradient()) {
+ canvasStyle = CanvasStyle::createFromGradient(style.getAsCanvasGradient());
+ } else if (style.isCanvasPattern()) {
+ CanvasPattern* canvasPattern = style.getAsCanvasPattern();
+
+ if (canvas()->originClean() && !canvasPattern->originClean())
+ canvas()->setOriginTainted();
+ if (canvasPattern->pattern()->isTextureBacked())
+ canvas()->disableDeferral(DisableDeferralReasonUsingTextureBackedPattern);
+ canvasStyle = CanvasStyle::createFromPattern(canvasPattern);
+ }
+
+ ASSERT(canvasStyle);
+ modifiableState().setFillStyle(canvasStyle);
+ modifiableState().setUnparsedFillColor(colorString);
+ modifiableState().clearResolvedFilter();
+}
+
+double CanvasRenderingContext2D::lineWidth() const
+{
+ return state().lineWidth();
+}
+
+void CanvasRenderingContext2D::setLineWidth(double width)
+{
+ if (!std::isfinite(width) || width <= 0)
+ return;
+ if (state().lineWidth() == width)
+ return;
+ modifiableState().setLineWidth(width);
+}
+
+String CanvasRenderingContext2D::lineCap() const
+{
+ return lineCapName(state().lineCap());
+}
+
+void CanvasRenderingContext2D::setLineCap(const String& s)
+{
+ LineCap cap;
+ if (!parseLineCap(s, cap))
+ return;
+ if (state().lineCap() == cap)
+ return;
+ modifiableState().setLineCap(cap);
+}
+
+String CanvasRenderingContext2D::lineJoin() const
+{
+ return lineJoinName(state().lineJoin());
+}
+
+void CanvasRenderingContext2D::setLineJoin(const String& s)
+{
+ LineJoin join;
+ if (!parseLineJoin(s, join))
+ return;
+ if (state().lineJoin() == join)
+ return;
+ modifiableState().setLineJoin(join);
+}
+
+double CanvasRenderingContext2D::miterLimit() const
+{
+ return state().miterLimit();
+}
+
+void CanvasRenderingContext2D::setMiterLimit(double limit)
+{
+ if (!std::isfinite(limit) || limit <= 0)
+ return;
+ if (state().miterLimit() == limit)
+ return;
+ modifiableState().setMiterLimit(limit);
+}
+
+double CanvasRenderingContext2D::shadowOffsetX() const
+{
+ return state().shadowOffset().width();
+}
+
+void CanvasRenderingContext2D::setShadowOffsetX(double x)
+{
+ if (!std::isfinite(x))
+ return;
+ if (state().shadowOffset().width() == x)
+ return;
+ modifiableState().setShadowOffsetX(x);
+}
+
+double CanvasRenderingContext2D::shadowOffsetY() const
+{
+ return state().shadowOffset().height();
+}
+
+void CanvasRenderingContext2D::setShadowOffsetY(double y)
+{
+ if (!std::isfinite(y))
+ return;
+ if (state().shadowOffset().height() == y)
+ return;
+ modifiableState().setShadowOffsetY(y);
+}
+
+double CanvasRenderingContext2D::shadowBlur() const
+{
+ return state().shadowBlur();
+}
+
+void CanvasRenderingContext2D::setShadowBlur(double blur)
+{
+ if (!std::isfinite(blur) || blur < 0)
+ return;
+ if (state().shadowBlur() == blur)
+ return;
+ modifiableState().setShadowBlur(blur);
+}
+
+String CanvasRenderingContext2D::shadowColor() const
+{
+ return Color(state().shadowColor()).serialized();
+}
+
+void CanvasRenderingContext2D::setShadowColor(const String& colorString)
+{
+ Color color;
+ if (!parseColorOrCurrentColor(color, colorString, canvas()))
+ return;
+ if (state().shadowColor() == color)
+ return;
+ modifiableState().setShadowColor(color.rgb());
+}
+
+const Vector<double>& CanvasRenderingContext2D::getLineDash() const
+{
+ return state().lineDash();
+}
+
+static bool lineDashSequenceIsValid(const Vector<double>& dash)
+{
+ for (size_t i = 0; i < dash.size(); i++) {
+ if (!std::isfinite(dash[i]) || dash[i] < 0)
+ return false;
+ }
+ return true;
+}
+
+void CanvasRenderingContext2D::setLineDash(const Vector<double>& dash)
+{
+ if (!lineDashSequenceIsValid(dash))
+ return;
+ modifiableState().setLineDash(dash);
+}
+
+double CanvasRenderingContext2D::lineDashOffset() const
+{
+ return state().lineDashOffset();
+}
+
+void CanvasRenderingContext2D::setLineDashOffset(double offset)
+{
+ if (!std::isfinite(offset) || state().lineDashOffset() == offset)
+ return;
+ modifiableState().setLineDashOffset(offset);
+}
+
+double CanvasRenderingContext2D::globalAlpha() const
+{
+ return state().globalAlpha();
+}
+
+void CanvasRenderingContext2D::setGlobalAlpha(double alpha)
+{
+ if (!(alpha >= 0 && alpha <= 1))
+ return;
+ if (state().globalAlpha() == alpha)
+ return;
+ modifiableState().setGlobalAlpha(alpha);
+}
+
+bool CanvasRenderingContext2D::shouldAntialias() const
+{
+ return state().shouldAntialias();
+}
+
void CanvasRenderingContext2D::setShouldAntialias(bool doAA)
{
modifiableState().setShouldAntialias(doAA);
+}
+
+String CanvasRenderingContext2D::globalCompositeOperation() const
+{
+ return compositeOperatorName(compositeOperatorFromSkia(state().globalComposite()), blendModeFromSkia(state().globalComposite()));
+}
+
+void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation)
+{
+ CompositeOperator op = CompositeSourceOver;
+ WebBlendMode blendMode = WebBlendModeNormal;
+ if (!parseCompositeAndBlendOperator(operation, op, blendMode))
+ return;
+ SkXfermode::Mode xfermode = WebCoreCompositeToSkiaComposite(op, blendMode);
+ if (state().globalComposite() == xfermode)
+ return;
+ modifiableState().setGlobalComposite(xfermode);
+}
+
+String CanvasRenderingContext2D::filter() const
+{
+ return state().unparsedFilter();
+}
+
+void CanvasRenderingContext2D::setFilter(const String& filterString)
+{
+ if (filterString == state().unparsedFilter())
+ return;
+
+ RefPtrWillBeRawPtr<CSSValue> filterValue = CSSParser::parseSingleValue(CSSPropertyWebkitFilter, filterString, CSSParserContext(HTMLStandardMode, 0));
+
+ if (!filterValue || filterValue->isInitialValue() || filterValue->isInheritedValue())
+ return;
+
+ modifiableState().setUnparsedFilter(filterString);
+ modifiableState().setFilter(filterValue.release());
+}
+
+PassRefPtrWillBeRawPtr<SVGMatrixTearOff> CanvasRenderingContext2D::currentTransform() const
+{
+ return SVGMatrixTearOff::create(state().transform());
+}
+
+void CanvasRenderingContext2D::setCurrentTransform(PassRefPtrWillBeRawPtr<SVGMatrixTearOff> passMatrixTearOff)
+{
+ RefPtrWillBeRawPtr<SVGMatrixTearOff> matrixTearOff = passMatrixTearOff;
+ const AffineTransform& transform = matrixTearOff->value();
+ setTransform(transform.a(), transform.b(), transform.c(), transform.d(), transform.e(), transform.f());
+}
+
+void CanvasRenderingContext2D::scale(double sx, double sy)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return;
+
+ if (!std::isfinite(sx) || !std::isfinite(sy))
+ return;
+
+ AffineTransform newTransform = state().transform();
+ newTransform.scaleNonUniform(sx, sy);
+ if (state().transform() == newTransform)
+ return;
+
+ modifiableState().setTransform(newTransform);
+ if (!state().isTransformInvertible())
+ return;
+
+ c->scale(sx, sy);
+ m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy));
+}
+
+void CanvasRenderingContext2D::rotate(double angleInRadians)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return;
+
+ if (!std::isfinite(angleInRadians))
+ return;
+
+ AffineTransform newTransform = state().transform();
+ newTransform.rotateRadians(angleInRadians);
+ if (state().transform() == newTransform)
+ return;
+
+ modifiableState().setTransform(newTransform);
+ if (!state().isTransformInvertible())
+ return;
+ c->rotate(angleInRadians * (180.0 / piFloat));
+ m_path.transform(AffineTransform().rotateRadians(-angleInRadians));
+}
+
+void CanvasRenderingContext2D::translate(double tx, double ty)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return;
+ if (!state().isTransformInvertible())
+ return;
+
+ if (!std::isfinite(tx) || !std::isfinite(ty))
+ return;
+
+ AffineTransform newTransform = state().transform();
+ newTransform.translate(tx, ty);
+ if (state().transform() == newTransform)
+ return;
+
+ modifiableState().setTransform(newTransform);
+ if (!state().isTransformInvertible())
+ return;
+ c->translate(tx, ty);
+ m_path.transform(AffineTransform().translate(-tx, -ty));
+}
+
+void CanvasRenderingContext2D::transform(double m11, double m12, double m21, double m22, double dx, double dy)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return;
+
+ if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy))
+ return;
+
+ AffineTransform transform(m11, m12, m21, m22, dx, dy);
+ AffineTransform newTransform = state().transform() * transform;
+ if (state().transform() == newTransform)
+ return;
+
+ modifiableState().setTransform(newTransform);
+ if (!state().isTransformInvertible())
+ return;
+
+ c->concat(affineTransformToSkMatrix(transform));
+ m_path.transform(transform.inverse());
+}
+
+void CanvasRenderingContext2D::resetTransform()
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return;
+
+ AffineTransform ctm = state().transform();
+ bool invertibleCTM = state().isTransformInvertible();
+ // It is possible that CTM is identity while CTM is not invertible.
+ // When CTM becomes non-invertible, realizeSaves() can make CTM identity.
+ if (ctm.isIdentity() && invertibleCTM)
+ return;
+
+ // resetTransform() resolves the non-invertible CTM state.
+ modifiableState().resetTransform();
+ c->setMatrix(affineTransformToSkMatrix(canvas()->baseTransform()));
+
+ if (invertibleCTM)
+ m_path.transform(ctm);
+ // When else, do nothing because all transform methods didn't update m_path when CTM became non-invertible.
+ // It means that resetTransform() restores m_path just before CTM became non-invertible.
+}
+
+void CanvasRenderingContext2D::setTransform(double m11, double m12, double m21, double m22, double dx, double dy)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return;
+
+ if (!std::isfinite(m11) || !std::isfinite(m21) || !std::isfinite(dx) || !std::isfinite(m12) || !std::isfinite(m22) || !std::isfinite(dy))
+ return;
+
+ resetTransform();
+ transform(m11, m12, m21, m22, dx, dy);
+}
+
+void CanvasRenderingContext2D::beginPath()
+{
+ m_path.clear();
+}
+
+static bool validateRectForCanvas(double& x, double& y, double& width, double& height)
+{
+ if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(width) || !std::isfinite(height))
+ return false;
+
+ if (!width && !height)
+ return false;
+
+ if (width < 0) {
+ width = -width;
+ x -= width;
+ }
+
+ if (height < 0) {
+ height = -height;
+ y -= height;
+ }
+
+ return true;
+}
+
+static bool isFullCanvasCompositeMode(SkXfermode::Mode op)
+{
+ // See 4.8.11.1.3 Compositing
+ // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already
+ // implement the specification's behavior.
+ return op == SkXfermode::kSrcIn_Mode || op == SkXfermode::kSrcOut_Mode || op == SkXfermode::kDstIn_Mode || op == SkXfermode::kDstATop_Mode;
+}
+
+template<typename DrawFunc>
+void CanvasRenderingContext2D::compositedDraw(const DrawFunc& drawFunc, SkCanvas* c, CanvasRenderingContext2DState::PaintType paintType, CanvasRenderingContext2DState::ImageType imageType)
+{
+ SkImageFilter* filter = state().getFilter(canvas(), accessFont(), canvas()->size(), this);
+ ASSERT(isFullCanvasCompositeMode(state().globalComposite()) || filter);
+ SkMatrix ctm = c->getTotalMatrix();
+ c->resetMatrix();
+ SkPaint compositePaint;
+ compositePaint.setXfermodeMode(state().globalComposite());
+ if (state().shouldDrawShadows()) {
+ // unroll into two independently composited passes if drawing shadows
+ SkPaint shadowPaint = *state().getPaint(paintType, DrawShadowOnly, imageType);
+ int saveCount = c->getSaveCount();
+ if (filter) {
+ SkPaint filterPaint;
+ filterPaint.setImageFilter(filter);
+ // TODO(junov): crbug.com/502921 We could use primitive bounds if we knew that the filter
+ // does not affect transparent black regions.
+ c->saveLayer(nullptr, &shadowPaint);
+ c->saveLayer(nullptr, &filterPaint);
+ SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroundOnly, imageType);
+ c->setMatrix(ctm);
+ drawFunc(c, &foregroundPaint);
+ } else {
+ ASSERT(isFullCanvasCompositeMode(state().globalComposite()));
+ c->saveLayer(nullptr, &compositePaint);
+ shadowPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ c->setMatrix(ctm);
+ drawFunc(c, &shadowPaint);
+ }
+ c->restoreToCount(saveCount);
+ }
+
+ compositePaint.setImageFilter(filter);
+ // TODO(junov): crbug.com/502921 We could use primitive bounds if we knew that the filter
+ // does not affect transparent black regions *and* !isFullCanvasCompositeMode
+ c->saveLayer(nullptr, &compositePaint);
+ SkPaint foregroundPaint = *state().getPaint(paintType, DrawForegroundOnly, imageType);
+ foregroundPaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ c->setMatrix(ctm);
+ drawFunc(c, &foregroundPaint);
+ c->restore();
+ c->setMatrix(ctm);
+}
+
+template<typename DrawFunc, typename ContainsFunc>
+bool CanvasRenderingContext2D::draw(const DrawFunc& drawFunc, const ContainsFunc& drawCoversClipBounds, const SkRect& bounds, CanvasRenderingContext2DState::PaintType paintType, CanvasRenderingContext2DState::ImageType imageType)
+{
+ if (!state().isTransformInvertible())
+ return false;
+
+ SkIRect clipBounds;
+ if (!drawingCanvas() || !drawingCanvas()->getClipDeviceBounds(&clipBounds))
+ return false;
+
+ // If gradient size is zero, then paint nothing.
+ CanvasStyle* style = state().style(paintType);
+ if (style) {
+ CanvasGradient* gradient = style->canvasGradient();
+ if (gradient && gradient->gradient()->isZeroSize())
+ return false;
+ }
+
+ if (isFullCanvasCompositeMode(state().globalComposite()) || state().hasFilter(canvas(), accessFont(), canvas()->size(), this)) {
+ compositedDraw(drawFunc, drawingCanvas(), paintType, imageType);
+ didDraw(clipBounds);
+ } else if (state().globalComposite() == SkXfermode::kSrc_Mode) {
+ clearCanvas(); // takes care of checkOverdraw()
+ const SkPaint* paint = state().getPaint(paintType, DrawForegroundOnly, imageType);
+ drawFunc(drawingCanvas(), paint);
+ didDraw(clipBounds);
+ } else {
+ SkIRect dirtyRect;
+ if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) {
+ const SkPaint* paint = state().getPaint(paintType, DrawShadowAndForeground, imageType);
+ if (paintType != CanvasRenderingContext2DState::StrokePaintType && drawCoversClipBounds(clipBounds))
+ checkOverdraw(bounds, paint, imageType, ClipFill);
+ drawFunc(drawingCanvas(), paint);
+ didDraw(dirtyRect);
+ }
+ }
+ return true;
+}
+
+static bool isPathExpensive(const Path& path)
+{
+ const SkPath& skPath = path.skPath();
+ if (ExpensiveCanvasHeuristicParameters::ConcavePathsAreExpensive && !skPath.isConvex())
+ return true;
+
+ if (skPath.countPoints() > ExpensiveCanvasHeuristicParameters::ExpensivePathPointCount)
+ return true;
+
+ return false;
+}
+
+void CanvasRenderingContext2D::drawPathInternal(const Path& path, CanvasRenderingContext2DState::PaintType paintType, SkPath::FillType fillType)
+{
+ if (path.isEmpty())
+ return;
+
+ SkPath skPath = path.skPath();
+ FloatRect bounds = path.boundingRect();
+ skPath.setFillType(fillType);
+
+ if (paintType == CanvasRenderingContext2DState::StrokePaintType)
+ inflateStrokeRect(bounds);
+
+ if (!drawingCanvas())
+ return;
+
+ if (draw(
+ [&skPath, this](SkCanvas* c, const SkPaint* paint) // draw lambda
+ {
+ c->drawPath(skPath, *paint);
+ },
+ [](const SkIRect& rect) // overdraw test lambda
+ {
+ return false;
+ }, bounds, paintType)) {
+ if (isPathExpensive(path)) {
+ ImageBuffer* buffer = canvas()->buffer();
+ if (buffer)
+ buffer->setHasExpensiveOp();
+ }
+ }
+}
+
+static SkPath::FillType parseWinding(const String& windingRuleString)
+{
+ if (windingRuleString == "nonzero")
+ return SkPath::kWinding_FillType;
+ if (windingRuleString == "evenodd")
+ return SkPath::kEvenOdd_FillType;
+
+ ASSERT_NOT_REACHED();
+ return SkPath::kEvenOdd_FillType;
+}
+
+void CanvasRenderingContext2D::fill(const String& windingRuleString)
+{
+ drawPathInternal(m_path, CanvasRenderingContext2DState::FillPaintType, parseWinding(windingRuleString));
+}
+
+void CanvasRenderingContext2D::fill(Path2D* domPath, const String& windingRuleString)
+{
+ drawPathInternal(domPath->path(), CanvasRenderingContext2DState::FillPaintType, parseWinding(windingRuleString));
+}
+
+void CanvasRenderingContext2D::stroke()
+{
+ drawPathInternal(m_path, CanvasRenderingContext2DState::StrokePaintType);
+}
+
+void CanvasRenderingContext2D::stroke(Path2D* domPath)
+{
+ drawPathInternal(domPath->path(), CanvasRenderingContext2DState::StrokePaintType);
+}
+
+void CanvasRenderingContext2D::fillRect(double x, double y, double width, double height)
+{
+ if (!validateRectForCanvas(x, y, width, height))
+ return;
+
+ if (!drawingCanvas())
+ return;
+
+ SkRect rect = SkRect::MakeXYWH(x, y, width, height);
+ draw(
+ [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda
+ {
+ c->drawRect(rect, *paint);
+ },
+ [&rect, this](const SkIRect& clipBounds) // overdraw test lambda
+ {
+ return rectContainsTransformedRect(rect, clipBounds);
+ }, rect, CanvasRenderingContext2DState::FillPaintType);
+}
+
+static void strokeRectOnCanvas(const FloatRect& rect, SkCanvas* canvas, const SkPaint* paint)
+{
+ ASSERT(paint->getStyle() == SkPaint::kStroke_Style);
+ if ((rect.width() > 0) != (rect.height() > 0)) {
+ // When stroking, we must skip the zero-dimension segments
+ SkPath path;
+ path.moveTo(rect.x(), rect.y());
+ path.lineTo(rect.maxX(), rect.maxY());
+ path.close();
+ canvas->drawPath(path, *paint);
+ return;
+ }
+ canvas->drawRect(rect, *paint);
+}
+
+void CanvasRenderingContext2D::strokeRect(double x, double y, double width, double height)
+{
+ if (!validateRectForCanvas(x, y, width, height))
+ return;
+
+ if (!drawingCanvas())
+ return;
+
+ SkRect rect = SkRect::MakeXYWH(x, y, width, height);
+ FloatRect bounds = rect;
+ inflateStrokeRect(bounds);
+ draw(
+ [&rect, this](SkCanvas* c, const SkPaint* paint) // draw lambda
+ {
+ strokeRectOnCanvas(rect, c, paint);
+ },
+ [](const SkIRect& clipBounds) // overdraw test lambda
+ {
+ return false;
+ }, bounds, CanvasRenderingContext2DState::StrokePaintType);
+}
+
+void CanvasRenderingContext2D::clipInternal(const Path& path, const String& windingRuleString)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c) {
+ return;
+ }
+ if (!state().isTransformInvertible()) {
+ return;
+ }
+
+ SkPath skPath = path.skPath();
+ skPath.setFillType(parseWinding(windingRuleString));
+ modifiableState().clipPath(skPath, m_clipAntialiasing);
+ c->clipPath(skPath, SkRegion::kIntersect_Op, m_clipAntialiasing == AntiAliased);
+ if (ExpensiveCanvasHeuristicParameters::ComplexClipsAreExpensive && !skPath.isRect(0) && canvas()->hasImageBuffer()) {
+ canvas()->buffer()->setHasExpensiveOp();
+ }
+}
+
+void CanvasRenderingContext2D::clip(const String& windingRuleString)
+{
+ clipInternal(m_path, windingRuleString);
+}
+
+void CanvasRenderingContext2D::clip(Path2D* domPath, const String& windingRuleString)
+{
+ clipInternal(domPath->path(), windingRuleString);
+}
+
+bool CanvasRenderingContext2D::isPointInPath(const double x, const double y, const String& windingRuleString)
+{
+ return isPointInPathInternal(m_path, x, y, windingRuleString);
+}
+
+bool CanvasRenderingContext2D::isPointInPath(Path2D* domPath, const double x, const double y, const String& windingRuleString)
+{
+ return isPointInPathInternal(domPath->path(), x, y, windingRuleString);
+}
+
+bool CanvasRenderingContext2D::isPointInPathInternal(const Path& path, const double x, const double y, const String& windingRuleString)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return false;
+ if (!state().isTransformInvertible())
+ return false;
+
+ FloatPoint point(x, y);
+ if (!std::isfinite(point.x()) || !std::isfinite(point.y()))
+ return false;
+ AffineTransform ctm = state().transform();
+ FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
+
+ return path.contains(transformedPoint, SkFillTypeToWindRule(parseWinding(windingRuleString)));
+}
+
+bool CanvasRenderingContext2D::isPointInStroke(const double x, const double y)
+{
+ return isPointInStrokeInternal(m_path, x, y);
+}
+
+bool CanvasRenderingContext2D::isPointInStroke(Path2D* domPath, const double x, const double y)
+{
+ return isPointInStrokeInternal(domPath->path(), x, y);
+}
+
+bool CanvasRenderingContext2D::isPointInStrokeInternal(const Path& path, const double x, const double y)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return false;
+ if (!state().isTransformInvertible())
+ return false;
+
+ FloatPoint point(x, y);
+ if (!std::isfinite(point.x()) || !std::isfinite(point.y()))
+ return false;
+ AffineTransform ctm = state().transform();
+ FloatPoint transformedPoint = ctm.inverse().mapPoint(point);
+
+ StrokeData strokeData;
+ strokeData.setThickness(state().lineWidth());
+ strokeData.setLineCap(state().lineCap());
+ strokeData.setLineJoin(state().lineJoin());
+ strokeData.setMiterLimit(state().miterLimit());
+ Vector<float> lineDash(state().lineDash().size());
+ std::copy(state().lineDash().begin(), state().lineDash().end(), lineDash.begin());
+ strokeData.setLineDash(lineDash, state().lineDashOffset());
+ return path.strokeContains(transformedPoint, strokeData);
}
void CanvasRenderingContext2D::scrollPathIntoView()
@@ -348,12 +1187,368 @@
void CanvasRenderingContext2D::clearRect(double x, double y, double width, double height)
{
- BaseRenderingContext2D::clearRect(x, y, width, height);
+ if (!validateRectForCanvas(x, y, width, height))
+ return;
+
+ SkCanvas* c = drawingCanvas();
+ if (!c)
+ return;
+ if (!state().isTransformInvertible())
+ return;
+
+ SkIRect clipBounds;
+ if (!c->getClipDeviceBounds(&clipBounds))
+ return;
+
+ SkPaint clearPaint;
+ clearPaint.setXfermodeMode(SkXfermode::kClear_Mode);
+ clearPaint.setStyle(SkPaint::kFill_Style);
FloatRect rect(x, y, width, height);
+
+ if (rectContainsTransformedRect(rect, clipBounds)) {
+ checkOverdraw(rect, &clearPaint, CanvasRenderingContext2DState::NoImage, ClipFill);
+ if (drawingCanvas())
+ drawingCanvas()->drawRect(rect, clearPaint);
+ didDraw(clipBounds);
+ } else {
+ SkIRect dirtyRect;
+ if (computeDirtyRect(rect, clipBounds, &dirtyRect)) {
+ c->drawRect(rect, clearPaint);
+ didDraw(dirtyRect);
+ }
+ }
if (m_hitRegionManager) {
m_hitRegionManager->removeHitRegionsInRect(rect, state().transform());
}
+}
+
+static inline FloatRect normalizeRect(const FloatRect& rect)
+{
+ return FloatRect(std::min(rect.x(), rect.maxX()),
+ std::min(rect.y(), rect.maxY()),
+ std::max(rect.width(), -rect.width()),
+ std::max(rect.height(), -rect.height()));
+}
+
+static inline void clipRectsToImageRect(const FloatRect& imageRect, FloatRect* srcRect, FloatRect* dstRect)
+{
+ if (imageRect.contains(*srcRect))
+ return;
+
+ // Compute the src to dst transform
+ FloatSize scale(dstRect->size().width() / srcRect->size().width(), dstRect->size().height() / srcRect->size().height());
+ FloatPoint scaledSrcLocation = srcRect->location();
+ scaledSrcLocation.scale(scale.width(), scale.height());
+ FloatSize offset = dstRect->location() - scaledSrcLocation;
+
+ srcRect->intersect(imageRect);
+
+ // To clip the destination rectangle in the same proportion, transform the clipped src rect
+ *dstRect = *srcRect;
+ dstRect->scale(scale.width(), scale.height());
+ dstRect->move(offset);
+}
+
+static inline CanvasImageSource* toImageSourceInternal(const CanvasImageSourceUnion& value)
+{
+ if (value.isHTMLImageElement())
+ return value.getAsHTMLImageElement().get();
+ if (value.isHTMLVideoElement())
+ return value.getAsHTMLVideoElement().get();
+ if (value.isHTMLCanvasElement())
+ return value.getAsHTMLCanvasElement().get();
+ if (value.isImageBitmap())
+ return value.getAsImageBitmap().get();
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource, double x, double y, ExceptionState& exceptionState)
+{
+ CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
+ FloatSize sourceRectSize = imageSourceInternal->elementSize();
+ FloatSize destRectSize = imageSourceInternal->defaultDestinationSize();
+ drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize.height(), x, y, destRectSize.width(), destRectSize.height(), exceptionState);
+}
+
+void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource,
+ double x, double y, double width, double height, ExceptionState& exceptionState)
+{
+ CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
+ FloatSize sourceRectSize = imageSourceInternal->elementSize();
+ drawImage(imageSourceInternal, 0, 0, sourceRectSize.width(), sourceRectSize.height(), x, y, width, height, exceptionState);
+}
+
+void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSource,
+ double sx, double sy, double sw, double sh,
+ double dx, double dy, double dw, double dh, ExceptionState& exceptionState)
+{
+ CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
+ drawImage(imageSourceInternal, sx, sy, sw, sh, dx, dy, dw, dh, exceptionState);
+}
+
+bool CanvasRenderingContext2D::shouldDrawImageAntialiased(const FloatRect& destRect) const
+{
+ if (!shouldAntialias())
+ return false;
+ SkCanvas* c = drawingCanvas();
+ ASSERT(c);
+
+ const SkMatrix &ctm = c->getTotalMatrix();
+ // Don't disable anti-aliasing if we're rotated or skewed.
+ if (!ctm.rectStaysRect())
+ return true;
+ // Check if the dimensions of the destination are "small" (less than one
+ // device pixel). To prevent sudden drop-outs. Since we know that
+ // kRectStaysRect_Mask is set, the matrix either has scale and no skew or
+ // vice versa. We can query the kAffine_Mask flag to determine which case
+ // it is.
+ // FIXME: This queries the CTM while drawing, which is generally
+ // discouraged. Always drawing with AA can negatively impact performance
+ // though - that's why it's not always on.
+ SkScalar widthExpansion, heightExpansion;
+ if (ctm.getType() & SkMatrix::kAffine_Mask)
+ widthExpansion = ctm[SkMatrix::kMSkewY], heightExpansion = ctm[SkMatrix::kMSkewX];
+ else
+ widthExpansion = ctm[SkMatrix::kMScaleX], heightExpansion = ctm[SkMatrix::kMScaleY];
+ return destRect.width() * fabs(widthExpansion) < 1 || destRect.height() * fabs(heightExpansion) < 1;
+}
+
+void CanvasRenderingContext2D::drawImageInternal(SkCanvas* c, CanvasImageSource* imageSource, Image* image, const FloatRect& srcRect, const FloatRect& dstRect, const SkPaint* paint)
+{
+ int initialSaveCount = c->getSaveCount();
+ SkPaint imagePaint = *paint;
+
+ if (paint->getImageFilter()) {
+ SkMatrix invCtm;
+ if (!c->getTotalMatrix().invert(&invCtm)) {
+ // There is an earlier check for invertibility, but the arithmetic
+ // in AffineTransform is not exactly identical, so it is possible
+ // for SkMatrix to find the transform to be non-invertible at this stage.
+ // crbug.com/504687
+ return;
+ }
+ SkRect bounds = dstRect;
+ SkPaint layerPaint;
+ layerPaint.setXfermode(paint->getXfermode());
+ SkAutoTUnref<SkImageFilter> localFilter(paint->getImageFilter()->newWithLocalMatrix(invCtm));
+ layerPaint.setImageFilter(localFilter);
+ c->saveLayer(&bounds, &layerPaint);
+ imagePaint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ imagePaint.setImageFilter(nullptr);
+ }
+
+ if (!imageSource->isVideoElement()) {
+ imagePaint.setAntiAlias(shouldDrawImageAntialiased(dstRect));
+ image->draw(c, imagePaint, dstRect, srcRect, DoNotRespectImageOrientation, Image::DoNotClampImageToSourceRect);
+ } else {
+ c->save();
+ c->clipRect(dstRect);
+ c->translate(dstRect.x(), dstRect.y());
+ c->scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height());
+ c->translate(-srcRect.x(), -srcRect.y());
+ HTMLVideoElement* video = static_cast<HTMLVideoElement*>(imageSource);
+ video->paintCurrentFrame(c, IntRect(IntPoint(), IntSize(video->videoWidth(), video->videoHeight())), &imagePaint);
+ }
+
+ c->restoreToCount(initialSaveCount);
+}
+
+bool shouldDisableDeferral(CanvasImageSource* imageSource, DisableDeferralReason* reason)
+{
+ ASSERT(reason);
+ ASSERT(*reason == DisableDeferralReasonUnknown);
+
+ if (imageSource->isVideoElement()) {
+ *reason = DisableDeferralReasonDrawImageOfVideo;
+ return true;
+ }
+ if (imageSource->isCanvasElement()) {
+ HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(imageSource);
+ if (canvas->isAnimated2D()) {
+ *reason = DisableDeferralReasonDrawImageOfAnimated2dCanvas;
+ return true;
+ }
+ }
+ return false;
+}
+
+void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource,
+ double sx, double sy, double sw, double sh,
+ double dx, double dy, double dw, double dh, ExceptionState& exceptionState)
+{
+ if (!drawingCanvas())
+ return;
+
+ RefPtr<Image> image;
+ SourceImageStatus sourceImageStatus = InvalidSourceImageStatus;
+ if (!imageSource->isVideoElement()) {
+ AccelerationHint hint = canvas()->buffer()->isAccelerated() ? PreferAcceleration : PreferNoAcceleration;
+ image = imageSource->getSourceImageForCanvas(&sourceImageStatus, hint, SnapshotReasonDrawImage);
+ if (sourceImageStatus == UndecodableSourceImageStatus)
+ exceptionState.throwDOMException(InvalidStateError, "The HTMLImageElement provided is in the 'broken' state.");
+ if (!image || !image->width() || !image->height())
+ return;
+ } else {
+ if (!static_cast<HTMLVideoElement*>(imageSource)->hasAvailableVideoFrame())
+ return;
+ }
+
+ if (!std::isfinite(dx) || !std::isfinite(dy) || !std::isfinite(dw) || !std::isfinite(dh)
+ || !std::isfinite(sx) || !std::isfinite(sy) || !std::isfinite(sw) || !std::isfinite(sh)
+ || !dw || !dh || !sw || !sh)
+ return;
+
+ FloatRect srcRect = normalizeRect(FloatRect(sx, sy, sw, sh));
+ FloatRect dstRect = normalizeRect(FloatRect(dx, dy, dw, dh));
+
+ clipRectsToImageRect(FloatRect(FloatPoint(), imageSource->elementSize()), &srcRect, &dstRect);
+
+ imageSource->adjustDrawRects(&srcRect, &dstRect);
+
+ if (srcRect.isEmpty())
+ return;
+
+ DisableDeferralReason reason = DisableDeferralReasonUnknown;
+ if (shouldDisableDeferral(imageSource, &reason) || image->isTextureBacked())
+ canvas()->disableDeferral(reason);
+
+ validateStateStack();
+
+ draw(
+ [this, &imageSource, &image, &srcRect, dstRect](SkCanvas* c, const SkPaint* paint) // draw lambda
+ {
+ drawImageInternal(c, imageSource, image.get(), srcRect, dstRect, paint);
+ },
+ [this, &dstRect](const SkIRect& clipBounds) // overdraw test lambda
+ {
+ return rectContainsTransformedRect(dstRect, clipBounds);
+ }, dstRect, CanvasRenderingContext2DState::ImagePaintType,
+ imageSource->isOpaque() ? CanvasRenderingContext2DState::OpaqueImage : CanvasRenderingContext2DState::NonOpaqueImage);
+
+ validateStateStack();
+
+ bool isExpensive = false;
+
+ if (ExpensiveCanvasHeuristicParameters::SVGImageSourcesAreExpensive && imageSource->isSVGSource())
+ isExpensive = true;
+
+ if (imageSource->elementSize().width() * imageSource->elementSize().height() > canvas()->width() * canvas()->height() * ExpensiveCanvasHeuristicParameters::ExpensiveImageSizeRatio)
+ isExpensive = true;
+
+ if (isExpensive) {
+ ImageBuffer* buffer = canvas()->buffer();
+ if (buffer)
+ buffer->setHasExpensiveOp();
+ }
+
+ if (imageSource->isCanvasElement() && static_cast<HTMLCanvasElement*>(imageSource)->is3D()) {
+ // WebGL to 2D canvas: must flush graphics context to prevent a race
+ // FIXME: crbug.com/516331 Fix the underlying synchronization issue so this flush can be eliminated.
+ canvas()->buffer()->flushGpu(FlushReasonDrawImageOfWebGL);
+ }
+
+ if (canvas()->originClean() && wouldTaintOrigin(imageSource))
+ canvas()->setOriginTainted();
+}
+
+void CanvasRenderingContext2D::clearCanvas()
+{
+ FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height());
+ checkOverdraw(canvasRect, 0, CanvasRenderingContext2DState::NoImage, ClipFill);
+ SkCanvas* c = drawingCanvas();
+ if (c)
+ c->clear(m_hasAlpha ? SK_ColorTRANSPARENT : SK_ColorBLACK);
+}
+
+bool CanvasRenderingContext2D::rectContainsTransformedRect(const FloatRect& rect, const SkIRect& transformedRect) const
+{
+ FloatQuad quad(rect);
+ FloatQuad transformedQuad(FloatRect(transformedRect.x(), transformedRect.y(), transformedRect.width(), transformedRect.height()));
+ return state().transform().mapQuad(quad).containsQuad(transformedQuad);
+}
+
+CanvasGradient* CanvasRenderingContext2D::createLinearGradient(double x0, double y0, double x1, double y1)
+{
+ CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1));
+ return gradient;
+}
+
+CanvasGradient* CanvasRenderingContext2D::createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1, ExceptionState& exceptionState)
+{
+ if (r0 < 0 || r1 < 0) {
+ exceptionState.throwDOMException(IndexSizeError, String::format("The %s provided is less than 0.", r0 < 0 ? "r0" : "r1"));
+ return nullptr;
+ }
+
+ CanvasGradient* gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1);
+ return gradient;
+}
+
+CanvasPattern* CanvasRenderingContext2D::createPattern(const CanvasImageSourceUnion& imageSource, const String& repetitionType, ExceptionState& exceptionState)
+{
+ Pattern::RepeatMode repeatMode = CanvasPattern::parseRepetitionType(repetitionType, exceptionState);
+ if (exceptionState.hadException())
+ return nullptr;
+
+ SourceImageStatus status;
+ CanvasImageSource* imageSourceInternal = toImageSourceInternal(imageSource);
+ RefPtr<Image> imageForRendering = imageSourceInternal->getSourceImageForCanvas(&status, PreferNoAcceleration, SnapshotReasonCreatePattern);
+
+ switch (status) {
+ case NormalSourceImageStatus:
+ break;
+ case ZeroSizeCanvasSourceImageStatus:
+ exceptionState.throwDOMException(InvalidStateError, String::format("The canvas %s is 0.", imageSourceInternal->elementSize().width() ? "height" : "width"));
+ return nullptr;
+ case UndecodableSourceImageStatus:
+ exceptionState.throwDOMException(InvalidStateError, "Source image is in the 'broken' state.");
+ return nullptr;
+ case InvalidSourceImageStatus:
+ imageForRendering = Image::nullImage();
+ break;
+ case IncompleteSourceImageStatus:
+ return nullptr;
+ default:
+ ASSERT_NOT_REACHED();
+ return nullptr;
+ }
+ ASSERT(imageForRendering);
+
+ bool originClean = !wouldTaintOrigin(imageSourceInternal);
+
+ return CanvasPattern::create(imageForRendering.release(), repeatMode, originClean);
+}
+
+bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, SkIRect* dirtyRect)
+{
+ SkIRect clipBounds;
+ if (!drawingCanvas()->getClipDeviceBounds(&clipBounds))
+ return false;
+ return computeDirtyRect(localRect, clipBounds, dirtyRect);
+}
+
+bool CanvasRenderingContext2D::computeDirtyRect(const FloatRect& localRect, const SkIRect& transformedClipBounds, SkIRect* dirtyRect)
+{
+ FloatRect canvasRect = state().transform().mapRect(localRect);
+
+ if (alphaChannel(state().shadowColor())) {
+ FloatRect shadowRect(canvasRect);
+ shadowRect.move(state().shadowOffset());
+ shadowRect.inflate(state().shadowBlur());
+ canvasRect.unite(shadowRect);
+ }
+
+ SkIRect canvasIRect;
+ static_cast<SkRect>(canvasRect).roundOut(&canvasIRect);
+ if (!canvasIRect.intersect(transformedClipBounds))
+ return false;
+
+ if (dirtyRect)
+ *dirtyRect = canvasIRect;
+
+ return true;
}
void CanvasRenderingContext2D::didDraw(const SkIRect& dirtyRect)
@@ -370,16 +1565,6 @@
canvas()->didDraw(SkRect::Make(dirtyRect));
}
-bool CanvasRenderingContext2D::stateHasFilter()
-{
- return state().hasFilter(canvas(), accessFont(), canvas()->size(), this);
-}
-
-SkImageFilter* CanvasRenderingContext2D::stateGetFilter()
-{
- return state().getFilter(canvas(), accessFont(), canvas()->size(), this);
-}
-
SkCanvas* CanvasRenderingContext2D::drawingCanvas() const
{
if (isContextLost())
@@ -387,19 +1572,114 @@
return canvas()->drawingCanvas();
}
-SkCanvas* CanvasRenderingContext2D::existingDrawingCanvas() const
-{
- return canvas()->existingDrawingCanvas();
-}
-
-void CanvasRenderingContext2D::disableDeferral(DisableDeferralReason reason)
-{
- canvas()->disableDeferral(reason);
-}
-
-AffineTransform CanvasRenderingContext2D::baseTransform() const
-{
- return canvas()->baseTransform();
+ImageData* CanvasRenderingContext2D::createImageData(ImageData* imageData) const
+{
+ return ImageData::create(imageData->size());
+}
+
+ImageData* CanvasRenderingContext2D::createImageData(double sw, double sh, ExceptionState& exceptionState) const
+{
+ if (!sw || !sh) {
+ exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
+ return nullptr;
+ }
+
+ FloatSize logicalSize(fabs(sw), fabs(sh));
+ if (!logicalSize.isExpressibleAsIntSize())
+ return nullptr;
+
+ IntSize size = expandedIntSize(logicalSize);
+ if (size.width() < 1)
+ size.setWidth(1);
+ if (size.height() < 1)
+ size.setHeight(1);
+
+ return ImageData::create(size);
+}
+
+ImageData* CanvasRenderingContext2D::getImageData(double sx, double sy, double sw, double sh, ExceptionState& exceptionState) const
+{
+ if (!canvas()->originClean())
+ exceptionState.throwSecurityError("The canvas has been tainted by cross-origin data.");
+ else if (!sw || !sh)
+ exceptionState.throwDOMException(IndexSizeError, String::format("The source %s is 0.", sw ? "height" : "width"));
+
+ if (exceptionState.hadException())
+ return nullptr;
+
+ if (sw < 0) {
+ sx += sw;
+ sw = -sw;
+ }
+ if (sh < 0) {
+ sy += sh;
+ sh = -sh;
+ }
+
+ FloatRect logicalRect(sx, sy, sw, sh);
+ if (logicalRect.width() < 1)
+ logicalRect.setWidth(1);
+ if (logicalRect.height() < 1)
+ logicalRect.setHeight(1);
+ if (!logicalRect.isExpressibleAsIntRect())
+ return nullptr;
+
+ IntRect imageDataRect = enclosingIntRect(logicalRect);
+ ImageBuffer* buffer = canvas()->buffer();
+ if (!buffer || isContextLost())
+ return ImageData::create(imageDataRect.size());
+
+ WTF::ArrayBufferContents contents;
+ if (!buffer->getImageData(Unmultiplied, imageDataRect, contents))
+ return nullptr;
+
+ RefPtr<DOMArrayBuffer> arrayBuffer = DOMArrayBuffer::create(contents);
+ return ImageData::create(
+ imageDataRect.size(),
+ DOMUint8ClampedArray::create(arrayBuffer, 0, arrayBuffer->byteLength()));
+}
+
+void CanvasRenderingContext2D::putImageData(ImageData* data, double dx, double dy, ExceptionState& exceptionState)
+{
+ putImageData(data, dx, dy, 0, 0, data->width(), data->height(), exceptionState);
+}
+
+void CanvasRenderingContext2D::putImageData(ImageData* data, double dx, double dy, double dirtyX, double dirtyY, double dirtyWidth, double dirtyHeight, ExceptionState& exceptionState)
+{
+ if (data->data()->bufferBase()->isNeutered()) {
+ exceptionState.throwDOMException(InvalidStateError, "The source data has been neutered.");
+ return;
+ }
+ ImageBuffer* buffer = canvas()->buffer();
+ if (!buffer)
+ return;
+
+ if (dirtyWidth < 0) {
+ dirtyX += dirtyWidth;
+ dirtyWidth = -dirtyWidth;
+ }
+
+ if (dirtyHeight < 0) {
+ dirtyY += dirtyHeight;
+ dirtyHeight = -dirtyHeight;
+ }
+
+ FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+ clipRect.intersect(IntRect(0, 0, data->width(), data->height()));
+ IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy));
+ IntRect destRect = enclosingIntRect(clipRect);
+ destRect.move(destOffset);
+ destRect.intersect(IntRect(IntPoint(), buffer->size()));
+ if (destRect.isEmpty())
+ return;
+ IntRect sourceRect(destRect);
+ sourceRect.move(-destOffset);
+
+ checkOverdraw(destRect, 0, CanvasRenderingContext2DState::NoImage, UntransformedUnclippedFill);
+
+ buffer->putByteArray(Unmultiplied, data->data()->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset));
+
+ didDraw(destRect);
}
String CanvasRenderingContext2D::font() const
@@ -543,41 +1823,6 @@
state().clearResolvedFilter();
}
-bool CanvasRenderingContext2D::originClean() const
-{
- return canvas()->originClean();
-}
-
-void CanvasRenderingContext2D::setOriginTainted()
-{
- return canvas()->setOriginTainted();
-}
-
-int CanvasRenderingContext2D::width() const
-{
- return canvas()->width();
-}
-
-int CanvasRenderingContext2D::height() const
-{
- return canvas()->height();
-}
-
-bool CanvasRenderingContext2D::hasImageBuffer() const
-{
- return canvas()->hasImageBuffer();
-}
-
-ImageBuffer* CanvasRenderingContext2D::imageBuffer() const
-{
- return canvas()->buffer();
-}
-
-bool CanvasRenderingContext2D::parseColorOrCurrentColor(Color& color, const String& colorString) const
-{
- return ::blink::parseColorOrCurrentColor(color, colorString, canvas());
-}
-
String CanvasRenderingContext2D::textAlign() const
{
return textAlignName(state().textAlign());
@@ -811,6 +2056,21 @@
return false;
},
textRunPaintInfo.bounds, paintType);
+}
+
+void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const
+{
+ // Fast approximation of the stroke's bounding rect.
+ // This yields a slightly oversized rect but is very fast
+ // compared to Path::strokeBoundingRect().
+ static const double root2 = sqrtf(2);
+ double delta = state().lineWidth() / 2;
+ if (state().lineJoin() == MiterJoin)
+ delta *= state().miterLimit();
+ else if (state().lineCap() == SquareCap)
+ delta *= root2;
+
+ rect.inflate(delta);
}
const Font& CanvasRenderingContext2D::accessFont()
@@ -862,6 +2122,32 @@
return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0;
}
+bool CanvasRenderingContext2D::imageSmoothingEnabled() const
+{
+ return state().imageSmoothingEnabled();
+}
+
+void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled)
+{
+ if (enabled == state().imageSmoothingEnabled())
+ return;
+
+ modifiableState().setImageSmoothingEnabled(enabled);
+}
+
+String CanvasRenderingContext2D::imageSmoothingQuality() const
+{
+ return state().imageSmoothingQuality();
+}
+
+void CanvasRenderingContext2D::setImageSmoothingQuality(const String& quality)
+{
+ if (quality == state().imageSmoothingQuality())
+ return;
+
+ modifiableState().setImageSmoothingQuality(quality);
+}
+
void CanvasRenderingContext2D::getContextAttributes(Canvas2DContextAttributes& attrs) const
{
attrs.setAlpha(m_hasAlpha);
@@ -1021,4 +2307,71 @@
return 0;
}
+void CanvasRenderingContext2D::checkOverdraw(const SkRect& rect, const SkPaint* paint, CanvasRenderingContext2DState::ImageType imageType, DrawType drawType)
+{
+ SkCanvas* c = drawingCanvas();
+ if (!c || !canvas()->buffer()->isRecording())
+ return;
+
+ SkRect deviceRect;
+ if (drawType == UntransformedUnclippedFill) {
+ deviceRect = rect;
+ } else {
+ ASSERT(drawType == ClipFill);
+ if (state().hasComplexClip())
+ return;
+
+ SkIRect skIBounds;
+ if (!c->getClipDeviceBounds(&skIBounds))
+ return;
+ deviceRect = SkRect::Make(skIBounds);
+ }
+
+ const SkImageInfo& imageInfo = c->imageInfo();
+ if (!deviceRect.contains(SkRect::MakeWH(imageInfo.width(), imageInfo.height())))
+ return;
+
+ bool isSourceOver = true;
+ unsigned alpha = 0xFF;
+ if (paint) {
+ if (paint->getLooper() || paint->getImageFilter() || paint->getMaskFilter())
+ return;
+
+ SkXfermode* xfermode = paint->getXfermode();
+ if (xfermode) {
+ SkXfermode::Mode mode;
+ if (xfermode->asMode(&mode)) {
+ isSourceOver = mode == SkXfermode::kSrcOver_Mode;
+ if (!isSourceOver && mode != SkXfermode::kSrc_Mode && mode != SkXfermode::kClear_Mode)
+ return; // The code below only knows how to handle Src, SrcOver, and Clear
+ } else {
+ // unknown xfermode
+ ASSERT_NOT_REACHED();
+ return;
+ }
+ }
+
+ alpha = paint->getAlpha();
+
+ if (isSourceOver && imageType == CanvasRenderingContext2DState::NoImage) {
+ SkShader* shader = paint->getShader();
+ if (shader) {
+ if (shader->isOpaque() && alpha == 0xFF)
+ canvas()->buffer()->willOverwriteCanvas();
+ return;
+ }
+ }
+ }
+
+ if (isSourceOver) {
+ // With source over, we need to certify that alpha == 0xFF for all pixels
+ if (imageType == CanvasRenderingContext2DState::NonOpaqueImage)
+ return;
+ if (alpha < 0xFF)
+ return;
+ }
+
+ canvas()->buffer()->willOverwriteCanvas();
+}
+
} // namespace blink
« no previous file with comments | « third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2D.h ('k') | third_party/WebKit/Source/modules/modules.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698