Chromium Code Reviews| 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..7b0973e746258064f4acd5aa0e1a99edeae16a27 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" |
|
Justin Novosad
2015/04/16 18:15:31
I enjoyed deleting this line. Booyah! \o/
|
| #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); |
| @@ -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); |
| + SkPaint layerPaint; |
| + layerPaint.setXfermode(paint->getXfermode()); |
| + layerPaint.setImageFilter(paint->getImageFilter()); |
| + c->saveLayer(&filteredBounds, &layerPaint); |
| + 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) |
| + paint.setAlpha(64); |
| + float cornerRadius = (width - 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); |
| } |