diff options
author | vmpstr@chromium.org <vmpstr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-07 23:52:24 +0000 |
---|---|---|
committer | vmpstr@chromium.org <vmpstr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-07 23:52:24 +0000 |
commit | 785145baf07145f8dc412fc6aa3e48851181d353 (patch) | |
tree | 6cb02ca0f5cb0469eabc744d6c34b8b09ff772b2 /skia | |
parent | 876395c1e0a01c3889b0605d4779a772215e74de (diff) | |
download | chromium_src-785145baf07145f8dc412fc6aa3e48851181d353.zip chromium_src-785145baf07145f8dc412fc6aa3e48851181d353.tar.gz chromium_src-785145baf07145f8dc412fc6aa3e48851181d353.tar.bz2 |
cc: Consolidate the analysis_canvas operations
This patch combines predictions (color, transparency,
and cheapness) into one processing step. Also adds code
to use color and transparency information.
BUG=179548
Review URL: https://chromiumcodereview.appspot.com/12316084
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@186819 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia')
-rw-r--r-- | skia/ext/analysis_canvas.cc | 269 | ||||
-rw-r--r-- | skia/ext/analysis_canvas.h | 22 | ||||
-rw-r--r-- | skia/ext/analysis_canvas_unittest.cc | 313 |
3 files changed, 588 insertions, 16 deletions
diff --git a/skia/ext/analysis_canvas.cc b/skia/ext/analysis_canvas.cc index 72b9eeb..2a2c7f3 100644 --- a/skia/ext/analysis_canvas.cc +++ b/skia/ext/analysis_canvas.cc @@ -7,6 +7,7 @@ #include "third_party/skia/include/core/SkDevice.h" #include "third_party/skia/include/core/SkDraw.h" #include "third_party/skia/include/core/SkRRect.h" +#include "third_party/skia/src/core/SkRasterClip.h" #include "ui/gfx/rect_conversions.h" namespace { @@ -16,13 +17,67 @@ namespace { // 25x as long as Z620. const int gPictureCostThreshold = 1000; +static bool isSolidColorPaint(const SkPaint& paint) { + SkXfermode::Mode xferMode; + + // getXfermode can return a NULL, but that is handled + // gracefully by AsMode (NULL turns into kSrcOver mode). + SkXfermode::AsMode(paint.getXfermode(), &xferMode); + + // Paint is solid color if the following holds: + // - Alpha is 1.0, style is fill, and there are no special effects + // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent + // to kSrc if source alpha is 1.0, which is already checked). + return (paint.getAlpha() == 255 && + !paint.getShader() && + !paint.getLooper() && + !paint.getMaskFilter() && + !paint.getColorFilter() && + paint.getStyle() == SkPaint::kFill_Style && + (xferMode == SkXfermode::kSrc_Mode || + xferMode == SkXfermode::kSrcOver_Mode)); } +static bool isFullQuad(const SkDraw& draw, + const SkRect& canvasRect, + const SkRect& drawnRect) { + + // If the transform results in a non-axis aligned + // rect, then be conservative and return false. + if (!draw.fMatrix->rectStaysRect()) + return false; + + SkRect drawBitmapRect; + draw.fBitmap->getBounds(&drawBitmapRect); + SkRect clipRect = SkRect::Make(draw.fRC->getBounds()); + SkRect deviceRect; + draw.fMatrix->mapRect(&deviceRect, drawnRect); + + // The drawn rect covers the full canvas, if the following conditions hold: + // - Clip rect is an actual rectangle. + // - The rect we're drawing (post-transform) contains the clip rect. + // That is, all of clip rect will be colored by the rect. + // - Clip rect contains the canvas rect. + // That is, we're not clipping to a portion of this canvas. + // - The bitmap into which the draw call happens is at least as + // big as the canvas rect + return draw.fRC->isRect() && + deviceRect.contains(clipRect) && + clipRect.contains(canvasRect) && + drawBitmapRect.contains(canvasRect); +} + +} // namespace + namespace skia { AnalysisDevice::AnalysisDevice(const SkBitmap& bm) : INHERITED(bm) - , estimatedCost_(0) { + , estimatedCost_(0) + , isForcedNotSolid_(false) + , isForcedNotTransparent_(false) + , isSolidColor_(false) + , isTransparent_(false) { } @@ -34,32 +89,112 @@ int AnalysisDevice::getEstimatedCost() const { return estimatedCost_; } +bool AnalysisDevice::getColorIfSolid(SkColor* color) const { + if (isSolidColor_) + *color = color_; + return isSolidColor_; +} + +bool AnalysisDevice::isTransparent() const { + return isTransparent_; +} + +void AnalysisDevice::setForceNotSolid(bool flag) { + isForcedNotSolid_ = flag; + if (isForcedNotSolid_) + isSolidColor_ = false; +} + +void AnalysisDevice::setForceNotTransparent(bool flag) { + isForcedNotTransparent_ = flag; + if (isForcedNotTransparent_) + isTransparent_ = false; +} + void AnalysisDevice::clear(SkColor color) { - ++estimatedCost_; + ++estimatedCost_; + + isTransparent_ = (!isForcedNotTransparent_ && SkColorGetA(color) == 0); + + if (!isForcedNotSolid_ && SkColorGetA(color) == 255) { + isSolidColor_ = true; + color_ = color; + } + else { + isSolidColor_ = false; + } } void AnalysisDevice::drawPaint(const SkDraw&, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, const SkPoint[], const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } -void AnalysisDevice::drawRect(const SkDraw&, const SkRect& r, +void AnalysisDevice::drawRect(const SkDraw& draw, const SkRect& rect, const SkPaint& paint) { + // FIXME: if there's a pending image decode & resize, more expensive if (paint.getMaskFilter()) { estimatedCost_ += 300; } ++estimatedCost_; + + bool doesCoverCanvas = isFullQuad(draw, + SkRect::MakeWH(width(), height()), + rect); + + SkXfermode::Mode xferMode; + SkXfermode::AsMode(paint.getXfermode(), &xferMode); + + // This canvas will become transparent if the following holds: + // - The quad is a full tile quad + // - We're not in "forced not transparent" mode + // - Transfer mode is clear (0 color, 0 alpha) + // + // If the paint alpha is not 0, or if the transfrer mode is + // not src, then this canvas will not be transparent. + // + // In all other cases, we keep the current transparent value + if (doesCoverCanvas && + !isForcedNotTransparent_ && + xferMode == SkXfermode::kClear_Mode) { + isTransparent_ = true; + } + else if (paint.getAlpha() != 0 || + xferMode != SkXfermode::kSrc_Mode) { + isTransparent_ = false; + } + + // This bitmap is solid if and only if the following holds. + // Note that this might be overly conservative: + // - We're not in "forced not solid" mode + // - Paint is solid color + // - The quad is a full tile quad + if (!isForcedNotSolid_ && + isSolidColorPaint(paint) && + doesCoverCanvas) { + isSolidColor_ = true; + color_ = paint.getColor(); + } + else { + isSolidColor_ = false; + } } void AnalysisDevice::drawOval(const SkDraw&, const SkRect& oval, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawPath(const SkDraw&, const SkPath& path, @@ -73,31 +208,42 @@ void AnalysisDevice::drawPath(const SkDraw&, const SkPath& path, estimatedCost_ += 300; } ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap, const SkIRect* srcRectOrNull, - const SkMatrix& matrix, const SkPaint& paint) - { + const SkMatrix& matrix, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } -void AnalysisDevice::drawBitmapRect(const SkDraw&, const SkBitmap&, +void AnalysisDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap&, const SkRect* srcOrNull, const SkRect& dst, const SkPaint& paint) { ++estimatedCost_; + + // Call drawRect to determine transparency, + // but reset solid color to false. + drawRect(draw, dst, paint); + isSolidColor_ = false; } void AnalysisDevice::drawText(const SkDraw&, const void* text, size_t len, - SkScalar x, SkScalar y, const SkPaint& paint) - { + SkScalar x, SkScalar y, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, @@ -106,21 +252,26 @@ void AnalysisDevice::drawPosText(const SkDraw& draw, const void* text, size_t le // FIXME: On Z620, every glyph cache miss costs us about 10us. // We don't have a good mechanism for predicting glyph cache misses. ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } #ifdef SK_BUILD_FOR_ANDROID void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw, const void* text, size_t len, const SkPoint pos[], const SkPaint& paint, - const SkPath& path, const SkMatrix* matrix) - { + const SkPath& path, const SkMatrix* matrix) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } #endif @@ -131,19 +282,25 @@ void AnalysisDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, const uint16_t indices[], int indexCount, const SkPaint& paint) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } void AnalysisDevice::drawDevice(const SkDraw&, SkDevice*, int x, int y, const SkPaint&) { ++estimatedCost_; + isSolidColor_ = false; + isTransparent_ = false; } - +const int AnalysisCanvas::kNoLayer = -1; AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device) - : INHERITED(device) { - + : INHERITED(device) + , savedStackSize_(0) + , forceNotSolidStackLevel_(kNoLayer) + , forceNotTransparentStackLevel_(kNoLayer) { } AnalysisCanvas::~AnalysisCanvas() { @@ -154,11 +311,18 @@ bool AnalysisCanvas::isCheap() const { return getEstimatedCost() < gPictureCostThreshold; } +bool AnalysisCanvas::getColorIfSolid(SkColor* color) const { + return (static_cast<AnalysisDevice*>(getDevice()))->getColorIfSolid(color); +} + +bool AnalysisCanvas::isTransparent() const { + return (static_cast<AnalysisDevice*>(getDevice()))->isTransparent(); +} + int AnalysisCanvas::getEstimatedCost() const { return (static_cast<AnalysisDevice*>(getDevice()))->getEstimatedCost(); } - bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) { return INHERITED::clipRect(rect, op, doAA); @@ -166,25 +330,98 @@ bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) { + // clipPaths can make our calls to isFullQuad invalid (ie have false + // positives). As a precaution, force the setting to be non-solid + // and non-transparent until we pop this + if (forceNotSolidStackLevel_ == kNoLayer) { + forceNotSolidStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(true); + } + if (forceNotTransparentStackLevel_ == kNoLayer) { + forceNotTransparentStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(true); + } + return INHERITED::clipRect(path.getBounds(), op, doAA); } bool AnalysisCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { + // clipRRect can make our calls to isFullQuad invalid (ie have false + // positives). As a precaution, force the setting to be non-solid + // and non-transparent until we pop this + if (forceNotSolidStackLevel_ == kNoLayer) { + forceNotSolidStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(true); + } + if (forceNotTransparentStackLevel_ == kNoLayer) { + forceNotTransparentStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(true); + } + return INHERITED::clipRect(rrect.getBounds(), op, doAA); } -int AnalysisCanvas::saveLayer(const SkRect* bounds, const SkPaint*, +int AnalysisCanvas::save(SkCanvas::SaveFlags flags) { + ++savedStackSize_; + return INHERITED::save(flags); +} + +int AnalysisCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SkCanvas::SaveFlags flags) { + ++savedStackSize_; + + // If after we draw to the saved layer, we have to blend with the current + // layer, then we can conservatively say that the canvas will not be of + // solid color. + if ((paint && !isSolidColorPaint(*paint)) || + (bounds && !bounds->contains( + SkRect::MakeWH(getDevice()->width(), getDevice()->height())))) { + if (forceNotSolidStackLevel_ == kNoLayer) { + forceNotSolidStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(true); + } + } + + // If after we draw to the save layer, we have to blend with the current + // layer using any part of the current layer's alpha, then we can + // conservatively say that the canvas will not be transparent. + SkXfermode::Mode xferMode = SkXfermode::kSrc_Mode; + if (paint) + SkXfermode::AsMode(paint->getXfermode(), &xferMode); + if (xferMode != SkXfermode::kSrc_Mode) { + if (forceNotTransparentStackLevel_ == kNoLayer) { + forceNotTransparentStackLevel_ = savedStackSize_; + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(true); + } + } + // Actually saving a layer here could cause a new bitmap to be created // and real rendering to occur. - int count = SkCanvas::save(flags); + int count = INHERITED::save(flags); if (bounds) { INHERITED::clipRectBounds(bounds, flags, NULL); } return count; } +void AnalysisCanvas::restore() { + INHERITED::restore(); + + DCHECK(savedStackSize_); + if (savedStackSize_) { + --savedStackSize_; + if (savedStackSize_ < forceNotSolidStackLevel_) { + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotSolid(false); + forceNotSolidStackLevel_ = kNoLayer; + } + if (savedStackSize_ < forceNotTransparentStackLevel_) { + (static_cast<AnalysisDevice*>(getDevice()))->setForceNotTransparent(false); + forceNotTransparentStackLevel_ = kNoLayer; + } + } +} + } // namespace skia diff --git a/skia/ext/analysis_canvas.h b/skia/ext/analysis_canvas.h index e2eedd2..fd7ee25 100644 --- a/skia/ext/analysis_canvas.h +++ b/skia/ext/analysis_canvas.h @@ -26,6 +26,8 @@ class SK_API AnalysisCanvas : public SkCanvas { // Returns true if the estimated cost of drawing is below an // arbitrary threshold. bool isCheap() const; + bool getColorIfSolid(SkColor* color) const; + bool isTransparent() const; // Returns the estimated cost of drawing, in arbitrary units. int getEstimatedCost() const; @@ -42,8 +44,17 @@ class SK_API AnalysisCanvas : public SkCanvas { virtual int saveLayer(const SkRect* bounds, const SkPaint*, SkCanvas::SaveFlags flags) OVERRIDE; + virtual int save(SaveFlags flags = kMatrixClip_SaveFlag) OVERRIDE; + + virtual void restore() OVERRIDE; + private: typedef SkCanvas INHERITED; + static const int kNoLayer; + + int savedStackSize_; + int forceNotSolidStackLevel_; + int forceNotTransparentStackLevel_; }; class SK_API AnalysisDevice : public SkDevice { @@ -52,6 +63,11 @@ class SK_API AnalysisDevice : public SkDevice { virtual ~AnalysisDevice(); int getEstimatedCost() const; + bool getColorIfSolid(SkColor* color) const; + bool isTransparent() const; + + void setForceNotSolid(bool flag); + void setForceNotTransparent(bool flag); protected: virtual void clear(SkColor color) OVERRIDE; @@ -105,6 +121,12 @@ class SK_API AnalysisDevice : public SkDevice { private: typedef SkDevice INHERITED; + + bool isForcedNotSolid_; + bool isForcedNotTransparent_; + bool isSolidColor_; + SkColor color_; + bool isTransparent_; }; } // namespace skia diff --git a/skia/ext/analysis_canvas_unittest.cc b/skia/ext/analysis_canvas_unittest.cc new file mode 100644 index 0000000..17cfcb5 --- /dev/null +++ b/skia/ext/analysis_canvas_unittest.cc @@ -0,0 +1,313 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/compiler_specific.h" +#include "skia/ext/analysis_canvas.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void solidColorFill(skia::AnalysisCanvas& canvas) { + canvas.clear(SkColorSetARGB(255, 255, 255, 255)); +} + +void transparentFill(skia::AnalysisCanvas& canvas) { + canvas.clear(SkColorSetARGB(0, 0, 0, 0)); +} + +} // namespace +namespace skia { + +TEST(AnalysisCanvasTest, EmptyCanvas) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + SkColor color; + EXPECT_FALSE(canvas.getColorIfSolid(&color)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); +} + +TEST(AnalysisCanvasTest, ClearCanvas) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + // Transparent color + SkColor color = SkColorSetARGB(0, 12, 34, 56); + canvas.clear(color); + + SkColor outputColor; + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_TRUE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Solid color + color = SkColorSetARGB(255, 65, 43, 21); + canvas.clear(color); + + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(outputColor, color); + + // Translucent color + color = SkColorSetARGB(128, 11, 22, 33); + canvas.clear(color); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Test helper methods + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_TRUE(canvas.isTransparent()); +} + +TEST(AnalysisCanvasTest, ComplexActions) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + // Draw paint test. + SkColor color = SkColorSetARGB(255, 11, 22, 33); + SkPaint paint; + paint.setColor(color); + + canvas.drawPaint(paint); + + SkColor outputColor; + //TODO(vmpstr): This should return true. (crbug.com/180597) + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Draw points test. + SkPoint points[4] = { + SkPoint::Make(0, 0), + SkPoint::Make(255, 0), + SkPoint::Make(255, 255), + SkPoint::Make(0, 255) + }; + + solidColorFill(canvas); + canvas.drawPoints(SkCanvas::kLines_PointMode, 4, points, paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Draw oval test. + solidColorFill(canvas); + canvas.drawOval(SkRect::MakeWH(255, 255), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + // Draw bitmap test. + solidColorFill(canvas); + SkBitmap secondBitmap; + secondBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + canvas.drawBitmap(secondBitmap, 0, 0); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); +} + +TEST(AnalysisCanvasTest, SimpleDrawRect) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + SkColor color = SkColorSetARGB(255, 11, 22, 33); + SkPaint paint; + paint.setColor(color); + canvas.clipRect(SkRect::MakeWH(255, 255)); + canvas.drawRect(SkRect::MakeWH(255, 255), paint); + + SkColor outputColor; + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(color, outputColor); + + color = SkColorSetARGB(255, 22, 33, 44); + paint.setColor(color); + canvas.translate(-128, -128); + canvas.drawRect(SkRect::MakeWH(382, 382), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + color = SkColorSetARGB(255, 33, 44, 55); + paint.setColor(color); + canvas.drawRect(SkRect::MakeWH(383, 383), paint); + + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(color, outputColor); + + color = SkColorSetARGB(0, 0, 0, 0); + paint.setColor(color); + canvas.drawRect(SkRect::MakeWH(383, 383), paint); + + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(outputColor, SkColorSetARGB(255, 33, 44, 55)); + + color = SkColorSetARGB(128, 128, 128, 128); + paint.setColor(color); + canvas.drawRect(SkRect::MakeWH(383, 383), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + paint.setXfermodeMode(SkXfermode::kClear_Mode); + canvas.drawRect(SkRect::MakeWH(382, 382), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + canvas.drawRect(SkRect::MakeWH(383, 383), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_TRUE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + + canvas.translate(128, 128); + color = SkColorSetARGB(255, 11, 22, 33); + paint.setColor(color); + paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); + canvas.drawRect(SkRect::MakeWH(255, 255), paint); + + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); + EXPECT_EQ(color, outputColor); + + canvas.rotate(50); + canvas.drawRect(SkRect::MakeWH(255, 255), paint); + + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + EXPECT_TRUE(canvas.isCheap()); +} + +TEST(AnalysisCanvasTest, ClipPath) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + SkPath path; + path.moveTo(0, 0); + path.lineTo(255, 0); + path.lineTo(255, 255); + path.lineTo(0, 255); + + SkColor outputColor; + solidColorFill(canvas); + canvas.clipPath(path); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + + canvas.save(); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + + canvas.clipPath(path); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + + canvas.restore(); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + + solidColorFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); +} + +TEST(AnalysisCanvasTest, SaveLayerRestore) { + SkBitmap emptyBitmap; + emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255); + skia::AnalysisDevice device(emptyBitmap); + skia::AnalysisCanvas canvas(&device); + + SkColor outputColor; + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + + SkRect bounds = SkRect::MakeWH(255, 255); + SkPaint paint; + paint.setColor(SkColorSetARGB(255, 255, 255, 255)); + paint.setXfermodeMode(SkXfermode::kSrcOver_Mode); + + // This should force non-transparency + canvas.saveLayer(&bounds, &paint, SkCanvas::kMatrix_SaveFlag); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + paint.setXfermodeMode(SkXfermode::kDst_Mode); + + // This should force non-solid color + canvas.saveLayer(&bounds, &paint, SkCanvas::kMatrix_SaveFlag); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + solidColorFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + canvas.restore(); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + canvas.restore(); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); + + transparentFill(canvas); + EXPECT_FALSE(canvas.getColorIfSolid(&outputColor)); + EXPECT_TRUE(canvas.isTransparent()); + + solidColorFill(canvas); + EXPECT_TRUE(canvas.getColorIfSolid(&outputColor)); + EXPECT_FALSE(canvas.isTransparent()); +} + +} // namespace skia |