Index: Source/core/html/canvas/CanvasRenderingContext2D.cpp |
diff --git a/Source/core/html/canvas/CanvasRenderingContext2D.cpp b/Source/core/html/canvas/CanvasRenderingContext2D.cpp |
index c65edef46ed276cf94356eea8b31d70242ba9cfc..3081cbff583f2e3625bf34563040aab11dcb8cb4 100644 |
--- a/Source/core/html/canvas/CanvasRenderingContext2D.cpp |
+++ b/Source/core/html/canvas/CanvasRenderingContext2D.cpp |
@@ -61,10 +61,13 @@ |
#include "platform/geometry/FloatQuad.h" |
#include "platform/graphics/DrawLooperBuilder.h" |
#include "platform/graphics/ExpensiveCanvasHeuristicParameters.h" |
-#include "platform/graphics/GraphicsContext.h" |
#include "platform/graphics/ImageBuffer.h" |
+#include "platform/graphics/StrokeData.h" |
+#include "platform/graphics/skia/SkiaUtils.h" |
#include "platform/text/BidiTextRun.h" |
#include "third_party/skia/include/core/SkCanvas.h" |
+#include "third_party/skia/include/core/SkImageFilter.h" |
+#include "third_party/skia/include/effects/SkCornerPathEffect.h" |
#include "wtf/ArrayBufferContents.h" |
#include "wtf/CheckedArithmetic.h" |
#include "wtf/MathExtras.h" |
@@ -158,7 +161,7 @@ void CanvasRenderingContext2D::validateStateStack() |
CanvasRenderingContext2DState& CanvasRenderingContext2D::modifiableState() |
{ |
- ASSERT(!state().hasUnrealizedSaves()); |
+ realizeSaves(); |
return *m_stateStack.last(); |
} |
@@ -300,7 +303,7 @@ void CanvasRenderingContext2D::restoreCanvasMatrixClipStack() |
} |
} |
-void CanvasRenderingContext2D::realizeSaves(SkCanvas* canvas) |
+void CanvasRenderingContext2D::realizeSaves() |
{ |
validateStateStack(); |
if (state().hasUnrealizedSaves()) { |
@@ -314,8 +317,7 @@ void CanvasRenderingContext2D::realizeSaves(SkCanvas* canvas) |
// 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(); |
- if (!canvas) |
- canvas = drawingCanvas(); |
+ SkCanvas* canvas = drawingCanvas(); |
if (canvas) |
canvas->save(); |
validateStateStack(); |
@@ -345,16 +347,6 @@ void CanvasRenderingContext2D::restore() |
if (c) |
c->restore(); |
- // Temporary code while crbug.com/453113 is a WIP: GraphicsContext state stack |
- // is no longer exercised so state stored still stored in GC must be re-installed |
- // after a restore. |
- GraphicsContext* gc = drawingContext(); |
- if (gc) { |
- gc->setAlphaAsFloat(state().globalAlpha()); |
- gc->setCompositeOperation(state().globalComposite()); |
- gc->setImageInterpolationQuality(state().imageSmoothingEnabled() ? CanvasDefaultInterpolationQuality : InterpolationNone); |
- } |
- |
validateStateStack(); |
} |
@@ -390,7 +382,6 @@ void CanvasRenderingContext2D::setStrokeStyle(const StringOrCanvasGradientOrCanv |
if (!parseColorOrCurrentColor(parsedColor, colorString, canvas())) |
return; |
if (state().strokeStyle()->isEquivalentRGBA(parsedColor)) { |
- realizeSaves(nullptr); |
modifiableState().setUnparsedStrokeColor(colorString); |
return; |
} |
@@ -408,11 +399,7 @@ void CanvasRenderingContext2D::setStrokeStyle(const StringOrCanvasGradientOrCanv |
ASSERT(canvasStyle); |
- SkCanvas* c = drawingCanvas(); |
- realizeSaves(c); |
modifiableState().setStrokeStyle(canvasStyle.release()); |
- if (!c) |
- return; |
modifiableState().setUnparsedStrokeColor(colorString); |
} |
@@ -435,7 +422,6 @@ void CanvasRenderingContext2D::setFillStyle(const StringOrCanvasGradientOrCanvas |
if (!parseColorOrCurrentColor(parsedColor, colorString, canvas())) |
return; |
if (state().fillStyle()->isEquivalentRGBA(parsedColor)) { |
- realizeSaves(nullptr); |
modifiableState().setUnparsedFillColor(colorString); |
return; |
} |
@@ -452,11 +438,6 @@ void CanvasRenderingContext2D::setFillStyle(const StringOrCanvasGradientOrCanvas |
} |
ASSERT(canvasStyle); |
- SkCanvas* c = drawingCanvas(); |
- if (!c) |
- return; |
- realizeSaves(c); |
- |
modifiableState().setFillStyle(canvasStyle.release()); |
modifiableState().setUnparsedFillColor(colorString); |
} |
@@ -472,11 +453,7 @@ void CanvasRenderingContext2D::setLineWidth(float width) |
return; |
if (state().lineWidth() == width) |
return; |
- SkCanvas* c = drawingCanvas(); |
- realizeSaves(c); |
modifiableState().setLineWidth(width); |
- if (!c) |
- return; |
} |
String CanvasRenderingContext2D::lineCap() const |
@@ -491,11 +468,7 @@ void CanvasRenderingContext2D::setLineCap(const String& s) |
return; |
if (state().lineCap() == cap) |
return; |
- SkCanvas* c = drawingCanvas(); |
- realizeSaves(c); |
modifiableState().setLineCap(cap); |
- if (!c) |
- return; |
} |
String CanvasRenderingContext2D::lineJoin() const |
@@ -510,11 +483,7 @@ void CanvasRenderingContext2D::setLineJoin(const String& s) |
return; |
if (state().lineJoin() == join) |
return; |
- SkCanvas* c = drawingCanvas(); |
- realizeSaves(c); |
modifiableState().setLineJoin(join); |
- if (!c) |
- return; |
} |
float CanvasRenderingContext2D::miterLimit() const |
@@ -528,11 +497,7 @@ void CanvasRenderingContext2D::setMiterLimit(float limit) |
return; |
if (state().miterLimit() == limit) |
return; |
- SkCanvas* c = drawingCanvas(); |
- realizeSaves(c); |
modifiableState().setMiterLimit(limit); |
- if (!c) |
- return; |
} |
float CanvasRenderingContext2D::shadowOffsetX() const |
@@ -546,7 +511,6 @@ void CanvasRenderingContext2D::setShadowOffsetX(float x) |
return; |
if (state().shadowOffset().width() == x) |
return; |
- realizeSaves(nullptr); |
modifiableState().setShadowOffsetX(x); |
} |
@@ -561,7 +525,6 @@ void CanvasRenderingContext2D::setShadowOffsetY(float y) |
return; |
if (state().shadowOffset().height() == y) |
return; |
- realizeSaves(nullptr); |
modifiableState().setShadowOffsetY(y); |
} |
@@ -576,7 +539,6 @@ void CanvasRenderingContext2D::setShadowBlur(float blur) |
return; |
if (state().shadowBlur() == blur) |
return; |
- realizeSaves(nullptr); |
modifiableState().setShadowBlur(blur); |
} |
@@ -592,7 +554,6 @@ void CanvasRenderingContext2D::setShadowColor(const String& color) |
return; |
if (state().shadowColor() == rgba) |
return; |
- realizeSaves(nullptr); |
modifiableState().setShadowColor(rgba); |
} |
@@ -614,8 +575,6 @@ void CanvasRenderingContext2D::setLineDash(const Vector<float>& dash) |
{ |
if (!lineDashSequenceIsValid(dash)) |
return; |
- |
- realizeSaves(nullptr); |
modifiableState().setLineDash(dash); |
} |
@@ -628,8 +587,6 @@ void CanvasRenderingContext2D::setLineDashOffset(float offset) |
{ |
if (!std::isfinite(offset) || state().lineDashOffset() == offset) |
return; |
- |
- realizeSaves(nullptr); |
modifiableState().setLineDashOffset(offset); |
} |
@@ -644,12 +601,7 @@ void CanvasRenderingContext2D::setGlobalAlpha(float alpha) |
return; |
if (state().globalAlpha() == alpha) |
return; |
- SkCanvas* c = drawingCanvas(); |
- realizeSaves(c); |
modifiableState().setGlobalAlpha(alpha); |
- if (!c) |
- return; |
- drawingContext()->setAlphaAsFloat(alpha); |
} |
String CanvasRenderingContext2D::globalCompositeOperation() const |
@@ -673,12 +625,7 @@ void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operati |
SkXfermode::Mode xfermode = WebCoreCompositeToSkiaComposite(op, blendMode); |
if (state().globalComposite() == xfermode) |
return; |
- SkCanvas* c = drawingCanvas(); |
- realizeSaves(c); |
modifiableState().setGlobalComposite(xfermode); |
- if (!c) |
- return; |
- drawingContext()->setCompositeOperation(xfermode); |
} |
PassRefPtrWillBeRawPtr<SVGMatrixTearOff> CanvasRenderingContext2D::currentTransform() const |
@@ -707,8 +654,6 @@ void CanvasRenderingContext2D::scale(float sx, float sy) |
if (state().transform() == newTransform) |
return; |
- realizeSaves(c); |
- |
modifiableState().setTransform(newTransform); |
if (!state().isTransformInvertible()) |
return; |
@@ -731,8 +676,6 @@ void CanvasRenderingContext2D::rotate(float angleInRadians) |
if (state().transform() == newTransform) |
return; |
- realizeSaves(c); |
- |
modifiableState().setTransform(newTransform); |
if (!state().isTransformInvertible()) |
return; |
@@ -756,8 +699,6 @@ void CanvasRenderingContext2D::translate(float tx, float ty) |
if (state().transform() == newTransform) |
return; |
- realizeSaves(c); |
- |
modifiableState().setTransform(newTransform); |
if (!state().isTransformInvertible()) |
return; |
@@ -779,8 +720,6 @@ void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float |
if (state().transform() == newTransform) |
return; |
- realizeSaves(c); |
- |
modifiableState().setTransform(newTransform); |
if (!state().isTransformInvertible()) |
return; |
@@ -802,7 +741,6 @@ void CanvasRenderingContext2D::resetTransform() |
if (ctm.isIdentity() && invertibleCTM) |
return; |
- realizeSaves(c); |
// resetTransform() resolves the non-invertible CTM state. |
modifiableState().resetTransform(); |
c->setMatrix(affineTransformToSkMatrix(canvas()->baseTransform())); |
@@ -899,9 +837,12 @@ bool CanvasRenderingContext2D::draw(const DrawFunc& drawFunc, const ContainsFunc |
return false; |
// If gradient size is zero, then paint nothing. |
- CanvasGradient* gradient = state().style(paintType)->canvasGradient(); |
- if (gradient && gradient->gradient()->isZeroSize()) |
- return false; |
+ CanvasStyle* style = state().style(paintType); |
+ if (style) { |
+ CanvasGradient* gradient = style->canvasGradient(); |
+ if (gradient && gradient->gradient()->isZeroSize()) |
+ return false; |
+ } |
if (isFullCanvasCompositeMode(state().globalComposite())) { |
fullCanvasCompositedDraw(drawFunc, paintType, imageType); |
@@ -915,7 +856,7 @@ bool CanvasRenderingContext2D::draw(const DrawFunc& drawFunc, const ContainsFunc |
SkIRect dirtyRect; |
if (computeDirtyRect(bounds, clipBounds, &dirtyRect)) { |
const SkPaint* paint = state().getPaint(paintType, DrawShadowAndForeground, imageType); |
- if (paintType == CanvasRenderingContext2DState::FillPaintType && drawCoversClipBounds(clipBounds)) |
+ if (paintType != CanvasRenderingContext2DState::StrokePaintType && drawCoversClipBounds(clipBounds)) |
checkOverdraw(bounds, paint, imageType, ClipFill); |
drawFunc(paint); |
didDraw(dirtyRect); |
@@ -1064,8 +1005,6 @@ void CanvasRenderingContext2D::clipInternal(const Path& path, const String& wind |
return; |
} |
- realizeSaves(c); |
- |
SkPath skPath = path.skPath(); |
skPath.setFillType(parseWinding(windingRuleString)); |
modifiableState().clipPath(skPath, m_clipAntialiasing); |
@@ -1222,20 +1161,6 @@ void CanvasRenderingContext2D::clearRect(float x, float y, float width, float he |
} |
} |
-void CanvasRenderingContext2D::applyShadow(ShadowMode shadowMode) |
-{ |
- GraphicsContext* c = drawingContext(); |
- if (!c) |
- return; |
- |
- if (state().shouldDrawShadows()) { |
- c->setShadow(state().shadowOffset(), state().shadowBlur(), state().shadowColor(), |
- DrawLooperBuilder::ShadowIgnoresTransforms, DrawLooperBuilder::ShadowRespectsAlpha, shadowMode); |
- } else { |
- c->clearShadow(); |
- } |
-} |
- |
static inline FloatRect normalizeRect(const FloatRect& rect) |
{ |
return FloatRect(std::min(rect.x(), rect.maxX()), |
@@ -1301,45 +1226,49 @@ void CanvasRenderingContext2D::drawImage(const CanvasImageSourceUnion& imageSour |
drawImage(imageSourceInternal, sx, sy, sw, sh, dx, dy, dw, dh, exceptionState); |
} |
-void CanvasRenderingContext2D::drawVideo(CanvasImageSource* imageSource, const FloatRect& srcRect, const FloatRect& dstRect) |
+void CanvasRenderingContext2D::drawImageInternal(CanvasImageSource* imageSource, Image* image, const FloatRect& srcRect, const FloatRect& dstRect, const SkPaint* paint) |
{ |
- HTMLVideoElement* video = static_cast<HTMLVideoElement*>(imageSource); |
- ASSERT(video); |
SkCanvas* c = drawingCanvas(); |
- if (!c) |
- return; |
- c->save(); |
- c->clipRect(WebCoreFloatRectToSKRect(dstRect)); |
- c->translate(dstRect.x(), dstRect.y()); |
- c->scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()); |
- c->translate(-srcRect.x(), -srcRect.y()); |
- video->paintCurrentFrameInContext(drawingContext(), IntRect(IntPoint(), IntSize(video->videoWidth(), video->videoHeight()))); |
- // In case the paint propagated a queued context loss signal |
- if (drawingCanvas()) |
- drawingCanvas()->restore(); |
-} |
+ ASSERT(c); |
-void CanvasRenderingContext2D::drawImageOnContext(CanvasImageSource* imageSource, Image* image, const FloatRect& srcRect, const FloatRect& dstRect, const SkPaint* paint) |
-{ |
- SkXfermode::Mode mode; |
- if (!SkXfermode::AsMode(paint->getXfermode(), &mode)) |
- mode = SkXfermode::kSrcOver_Mode; |
+ int initialSaveCount = c->getSaveCount(); |
+ SkPaint imagePaint = *paint; |
- RefPtr<SkImageFilter> imageFilter(paint->getImageFilter()); |
- drawingContext()->setDropShadowImageFilter(imageFilter.release()); |
- RefPtr<SkDrawLooper> drawLooper(paint->getLooper()); |
- drawingContext()->setDrawLooper(drawLooper.release()); |
+ if (paint->getImageFilter()) { |
+ SkMatrix ctm = c->getTotalMatrix(); |
+ SkMatrix invCtm; |
+ if (!ctm.invert(&invCtm)) { |
+ ASSERT_NOT_REACHED(); // There is an earlier check for invertibility |
+ } |
+ c->save(); |
+ c->concat(invCtm); |
+ SkRect bounds = dstRect; |
+ ctm.mapRect(&bounds); |
+ SkRect filteredBounds; |
+ paint->getImageFilter()->computeFastBounds(bounds, &filteredBounds); |
Stephen White
2015/04/17 15:14:56
Nit: I realize this is just moving code from elsew
|
+ SkPaint layerPaint; |
+ layerPaint.setXfermode(paint->getXfermode()); |
+ layerPaint.setImageFilter(paint->getImageFilter()); |
+ c->saveLayer(&filteredBounds, &layerPaint); |
Stephen White
2015/04/17 15:14:56
It seems unfortunate to have to call saveLayer() y
|
+ c->concat(ctm); |
+ imagePaint.setXfermodeMode(SkXfermode::kSrcOver_Mode); |
+ imagePaint.setImageFilter(nullptr); |
+ } |
if (!imageSource->isVideoElement()) { |
- drawingContext()->drawImage(image, dstRect, srcRect, mode); |
+ // TODO: Find a way to pass SkCanvas::kBleed_DrawBitmapRectFlag |
+ image->draw(c, imagePaint, dstRect, srcRect, DoNotRespectImageOrientation); |
} else { |
- SkXfermode::Mode oldMode = drawingContext()->compositeOperation(); |
- drawingContext()->setCompositeOperation(mode); |
- drawVideo(imageSource, srcRect, dstRect); |
- // Must re-check drawingContext() in case drawVideo propagated a pending context loss signal |
- if (drawingContext()) |
- drawingContext()->setCompositeOperation(oldMode); |
+ c->save(); |
+ c->clipRect(WebCoreFloatRectToSKRect(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); |
} |
void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, |
@@ -1389,15 +1318,14 @@ void CanvasRenderingContext2D::drawImage(CanvasImageSource* imageSource, |
draw( |
[this, &imageSource, &image, &srcRect, dstRect](const SkPaint* paint) // draw lambda |
{ |
- if (drawingCanvas()) |
- drawImageOnContext(imageSource, image.get(), srcRect, dstRect, paint); |
+ drawImageInternal(imageSource, image.get(), srcRect, dstRect, paint); |
}, |
[this, &dstRect](const SkIRect& clipBounds) // overdraw test lambda |
{ |
return rectContainsTransformedRect(dstRect, clipBounds); |
}, |
dstRect, |
- CanvasRenderingContext2DState::FillPaintType, |
+ CanvasRenderingContext2DState::ImagePaintType, |
imageSource->isOpaque() ? CanvasRenderingContext2DState::OpaqueImage : CanvasRenderingContext2DState::NonOpaqueImage); |
validateStateStack(); |
@@ -1535,13 +1463,6 @@ SkCanvas* CanvasRenderingContext2D::drawingCanvas() const |
return canvas()->drawingCanvas(); |
} |
-GraphicsContext* CanvasRenderingContext2D::drawingContext() const |
-{ |
- if (isContextLost()) |
- return nullptr; |
- return canvas()->drawingContext(); |
-} |
- |
PassRefPtrWillBeRawPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtrWillBeRawPtr<ImageData> imageData) const |
{ |
return ImageData::create(imageData->size()); |
@@ -1719,7 +1640,6 @@ void CanvasRenderingContext2D::setFont(const String& newFont) |
// The parse succeeded. |
String newFontSafeCopy(newFont); // Create a string copy since newFont can be deleted inside realizeSaves. |
- realizeSaves(nullptr); |
modifiableState().setUnparsedFont(newFontSafeCopy); |
// Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work |
@@ -1762,7 +1682,6 @@ void CanvasRenderingContext2D::setTextAlign(const String& s) |
return; |
if (state().textAlign() == align) |
return; |
- realizeSaves(nullptr); |
modifiableState().setTextAlign(align); |
} |
@@ -1778,7 +1697,6 @@ void CanvasRenderingContext2D::setTextBaseline(const String& s) |
return; |
if (state().textBaseline() == baseline) |
return; |
- realizeSaves(nullptr); |
modifiableState().setTextBaseline(baseline); |
} |
@@ -1821,7 +1739,6 @@ void CanvasRenderingContext2D::setDirection(const String& directionString) |
if (state().direction() == direction) |
return; |
- realizeSaves(nullptr); |
modifiableState().setDirection(direction); |
} |
@@ -1995,8 +1912,6 @@ void CanvasRenderingContext2D::inflateStrokeRect(FloatRect& rect) const |
const Font& CanvasRenderingContext2D::accessFont() |
{ |
- // This needs style to be up to date, but can't assert so because drawTextInternal |
- // can invalidate style before this is called (e.g. drawingContext invalidates style). |
if (!state().hasRealizedFont()) |
setFont(state().unparsedFont()); |
return state().font(); |
@@ -2050,11 +1965,7 @@ void CanvasRenderingContext2D::setImageSmoothingEnabled(bool enabled) |
if (enabled == state().imageSmoothingEnabled()) |
return; |
- SkCanvas* c = drawingCanvas(); |
- realizeSaves(c); |
modifiableState().setImageSmoothingEnabled(enabled); |
- if (c) |
- drawingContext()->setImageInterpolationQuality(enabled ? CanvasDefaultInterpolationQuality : InterpolationNone); |
} |
void CanvasRenderingContext2D::getContextAttributes(Canvas2DContextAttributes& attrs) const |
@@ -2104,14 +2015,12 @@ bool CanvasRenderingContext2D::focusRingCallIsValid(const Path& path, Element* e |
void CanvasRenderingContext2D::drawFocusRing(const Path& path) |
{ |
- GraphicsContext* c = drawingContext(); |
- if (!c) |
+ if (!drawingCanvas()) |
return; |
// These should match the style defined in html.css. |
Color focusRingColor = LayoutTheme::theme().focusRingColor(); |
const int focusRingWidth = 5; |
- const int focusRingOutline = 0; |
// We need to add focusRingWidth to dirtyRect. |
StrokeData strokeData; |
@@ -2121,14 +2030,31 @@ void CanvasRenderingContext2D::drawFocusRing(const Path& path) |
if (!computeDirtyRect(path.strokeBoundingRect(strokeData), &dirtyRect)) |
return; |
- c->setAlphaAsFloat(1.0); |
- c->clearShadow(); |
- c->setCompositeOperation(SkXfermode::kSrcOver_Mode); |
- c->drawFocusRing(path, focusRingWidth, focusRingOutline, focusRingColor); |
- c->setAlphaAsFloat(state().globalAlpha()); |
- c->setCompositeOperation(state().globalComposite()); |
+ SkPaint paint; |
+ paint.setAntiAlias(true); |
+ paint.setStyle(SkPaint::kStroke_Style); |
+ paint.setColor(focusRingColor.rgb()); |
+ paint.setStrokeWidth(focusRingWidth); |
+ |
+#if OS(MACOSX) |
Stephen White
2015/04/17 15:14:56
Nit: I wonder if we should keep this stuff in plat
|
+ paint.setAlpha(64); |
+ float cornerRadius = (focusRingWidth - 1) * 0.5f; |
+#else |
+ const float cornerRadius = 1; |
+#endif |
+ |
+ paint.setPathEffect(SkCornerPathEffect::Create(SkFloatToScalar(cornerRadius)))->unref(); |
+ |
+ // Outer path |
+ drawingCanvas()->drawPath(path.skPath(), paint); |
+ |
+#if OS(MACOSX) |
+ // Inner path |
+ paint.setAlpha(128); |
+ paint.setStrokeWidth(paint.getStrokeWidth() * 0.5f); |
+ drawingCanvas()->drawPath(path.skPath(), paint); |
+#endif |
- validateStateStack(); |
didDraw(dirtyRect); |
} |