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 9f3346a862e6e8964aa072a165c91ade2dd36a4f..aebebe6b318d1c7eeb3121069b61758d5b2d09ec 100644 |
| --- a/Source/core/html/canvas/CanvasRenderingContext2D.cpp |
| +++ b/Source/core/html/canvas/CanvasRenderingContext2D.cpp |
| @@ -283,6 +283,7 @@ CanvasRenderingContext2D::State::State() |
| , m_unparsedFont(defaultFont) |
| , m_realizedFont(false) |
| , m_hasClip(false) |
| + , m_hasComplexClip(false) |
| { |
| } |
| @@ -313,6 +314,7 @@ CanvasRenderingContext2D::State::State(const State& other, ClipListCopyMode mode |
| , m_font(other.m_font) |
| , m_realizedFont(other.m_realizedFont) |
| , m_hasClip(other.m_hasClip) |
| + , m_hasComplexClip(other.m_hasComplexClip) |
| { |
| if (mode == CopyClipList) { |
| m_clipList = other.m_clipList; |
| @@ -355,6 +357,7 @@ CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(cons |
| m_font = other.m_font; |
| m_realizedFont = other.m_realizedFont; |
| m_hasClip = other.m_hasClip; |
| + m_hasComplexClip = other.m_hasComplexClip; |
| m_clipList = other.m_clipList; |
| if (m_realizedFont) |
| @@ -1122,7 +1125,7 @@ void CanvasRenderingContext2D::clipInternal(const Path& path, const String& wind |
| c->clipPath(skPath, SkRegion::kIntersect_Op, m_clipAntialiasing == AntiAliased); |
| if (!skPath.isRect(0)) |
| - drawingContext()->setHasComplexClip(); |
| + modifiableState().m_hasComplexClip = true; |
| modifiableState().m_hasClip = true; |
| } |
| @@ -1297,6 +1300,7 @@ void CanvasRenderingContext2D::fillRect(float x, float y, float width, float hei |
| FloatRect rect(x, y, width, height); |
| if (rectContainsTransformedRect(rect, clipBounds)) { |
| + checkOverdraw(rect, &c->fillPaint(), NoImage, ClipFill); |
| c->fillRect(rect); |
| didDraw(clipBounds); |
| } else if (isFullCanvasCompositeMode(state().m_globalComposite)) { |
| @@ -1304,7 +1308,7 @@ void CanvasRenderingContext2D::fillRect(float x, float y, float width, float hei |
| didDraw(clipBounds); |
| } else if (state().m_globalComposite == SkXfermode::kSrc_Mode) { |
| clearCanvas(); |
| - c->clearShadow(); |
| + c->clearShadow(); // Takes care of signaling the overdraw |
| c->fillRect(rect); |
| applyShadow(DrawShadowAndForeground); |
| didDraw(clipBounds); |
| @@ -1518,13 +1522,14 @@ void CanvasRenderingContext2D::drawImageInternal(CanvasImageSource* imageSource, |
| canvas()->buffer()->willAccessPixels(); |
| if (rectContainsTransformedRect(dstRect, clipBounds)) { |
| + checkOverdraw(dstRect, &c->fillPaint(), imageSource->isOpaque() ? OpaqueImage : NonOpaqueImage, NormalFill); |
|
dshwang
2015/02/06 19:10:37
Why drawImage uses NormalFill while fillRect use C
Justin Novosad
2015/02/06 19:37:31
You are right. And this was the only place NormalF
|
| drawImageOnContext(drawingCanvas(), c, imageSource, image.get(), srcRect, dstRect); |
| didDraw(clipBounds); |
| } else if (isFullCanvasCompositeMode(state().m_globalComposite)) { |
| fullCanvasCompositedDraw(bind(&drawImageOnContext, drawingCanvas(), c, imageSource, image.get(), srcRect, dstRect)); |
| didDraw(clipBounds); |
| } else if (state().m_globalComposite == SkXfermode::kSrc_Mode) { |
| - clearCanvas(); |
| + clearCanvas(); // takes care of signaling an overdraw |
| drawImageOnContext(drawingCanvas(), c, imageSource, image.get(), srcRect, dstRect); |
| didDraw(clipBounds); |
| } else { |
| @@ -1551,6 +1556,7 @@ void CanvasRenderingContext2D::clearCanvas() |
| if (!c) |
| return; |
| + checkOverdraw(canvasRect, 0, NoImage, ClipFill); |
| c->clear(m_hasAlpha ? SK_ColorTRANSPARENT : SK_ColorBLACK); |
| } |
| @@ -1791,6 +1797,8 @@ void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, |
| IntRect sourceRect(destRect); |
| sourceRect.move(-destOffset); |
| + checkOverdraw(destRect, 0, NoImage, UntransformedUnclippedFill); |
| + |
| buffer->putByteArray(Unmultiplied, data->data()->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset)); |
| didDraw(destRect); |
| @@ -2382,4 +2390,92 @@ unsigned CanvasRenderingContext2D::hitRegionsCount() const |
| return 0; |
| } |
| +void CanvasRenderingContext2D::checkOverdraw(const SkRect& rect, const SkPaint* paint, ImageType imageType, DrawType drawType) |
| +{ |
| + SkCanvas* c = drawingCanvas(); |
| + if (!c || !canvas()->buffer()->isRecording()) |
| + return; |
| + |
| + SkRect deviceRect; |
| + if (drawType == UntransformedUnclippedFill) { |
| + deviceRect = rect; |
| + } else if (drawType == ClipFill) { |
| + if (state().m_hasComplexClip) |
| + return; |
| + |
| + SkIRect skIBounds; |
| + if (!c->getClipDeviceBounds(&skIBounds)) |
| + return; |
| + deviceRect = SkRect::Make(skIBounds); |
| + } else { |
| + // FIXME: Early out if rotate/skew. To be thorough, we could compute |
| + // an enclsed rect, but that would probably be overkill. |
|
dshwang
2015/02/06 19:10:37
s/enclsed/enclosed/
Justin Novosad
2015/02/06 19:37:31
Acknowledged.
|
| + const SkMatrix& ctm = c->getTotalMatrix(); |
| + if (!ctm.rectStaysRect()) |
| + return; |
| + |
| + if (state().m_hasComplexClip) |
| + return; |
| + |
| + ctm.mapRect(&deviceRect, rect); |
| + SkIRect skIBounds; |
| + if (!c->getClipDeviceBounds(&skIBounds)) |
| + return; |
| + SkRect skBounds = SkRect::Make(skIBounds); |
| + if (!deviceRect.intersect(skBounds)) |
| + return; |
| + } |
| + |
| + 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 |
|
dshwang
2015/02/06 19:10:37
ASSERT_NOT_REACHED?
Justin Novosad
2015/02/06 19:37:31
Acknowledged.
|
| + return; |
| + } |
| + } |
| + |
| + if (isSourceOver) { |
| + // With source over, we need to certify that alpha == 0xFF for all pixels |
| + SkColorFilter* colorFilter = paint->getColorFilter(); |
| + if (colorFilter && !(colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag)) |
| + return; |
| + |
| + SkShader* shader = paint->getShader(); |
| + if (shader) { |
| + // Shader overrides bitmap and paint color, so we can end here |
| + if (shader->isOpaque()) |
|
dshwang
2015/02/06 19:10:37
don't we need to consider alpha even if shader is
Justin Novosad
2015/02/06 19:37:31
Did some digging in the code. In skia-land, the sh
|
| + canvas()->buffer()->willOverwriteCanvas(); |
| + return; |
| + } |
| + } |
| + |
| + alpha = paint->getAlpha(); |
| + } |
| + |
| + if (isSourceOver) { |
| + // With source over, we need to certify that alpha == 0xFF for all pixels |
| + if (imageType == NonOpaqueImage) |
| + return; |
| + if (imageType == NoImage && alpha < 0xFF) |
| + return; |
|
dshwang
2015/02/06 19:10:37
Can we overdraw when imageType == OpaqueImage && a
Justin Novosad
2015/02/06 19:37:31
Yep, Made the same mistake here as with shaders ab
|
| + } |
| + |
| + canvas()->buffer()->willOverwriteCanvas(); |
| +} |
| + |
| } // namespace blink |