[go: nahoru, domu]

cc: Add solid color analyzer to replace AnalysisCanvas

This patch adds a solid color analysis to run on paint op buffer.
This means that we don't raster the raster source anymore for the
analysis canvas and instead just iterate the paint op buffer.

R=khushalsagar@chromium.org, enne@chromium.org

Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I5b1f24ab7baaa767384050aea61164f6d030c178
Reviewed-on: https://chromium-review.googlesource.com/538974
Reviewed-by: enne <enne@chromium.org>
Commit-Queue: Vladimir Levin <vmpstr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#482305}
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 0ffd466..bb6312a 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -778,6 +778,7 @@
     "paint/discardable_image_map_unittest.cc",
     "paint/display_item_list_unittest.cc",
     "paint/paint_op_buffer_unittest.cc",
+    "paint/solid_color_analyzer_unittest.cc",
     "quads/draw_polygon_unittest.cc",
     "quads/draw_quad_unittest.cc",
     "quads/nine_patch_generator_unittest.cc",
diff --git a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
index 203f770..3052425 100644
--- a/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
+++ b/cc/benchmarks/rasterize_and_record_benchmark_impl.cc
@@ -45,8 +45,10 @@
                    base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
                    kTimeCheckInterval);
     SkColor color = SK_ColorTRANSPARENT;
-    *is_solid_color = raster_source->PerformSolidColorAnalysis(
-        content_rect, contents_scale, &color);
+    gfx::Rect layer_rect =
+        gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale);
+    *is_solid_color =
+        raster_source->PerformSolidColorAnalysis(layer_rect, &color);
 
     do {
       SkBitmap bitmap;
diff --git a/cc/layers/recording_source.cc b/cc/layers/recording_source.cc
index e48e0ce..06defb1 100644
--- a/cc/layers/recording_source.cc
+++ b/cc/layers/recording_source.cc
@@ -12,6 +12,7 @@
 #include "cc/base/region.h"
 #include "cc/layers/content_layer_client.h"
 #include "cc/paint/display_item_list.h"
+#include "cc/paint/solid_color_analyzer.h"
 #include "cc/raster/raster_source.h"
 #include "skia/ext/analysis_canvas.h"
 
@@ -142,15 +143,14 @@
   is_solid_color_ = false;
   solid_color_ = SK_ColorTRANSPARENT;
 
+  // TODO(vmpstr): We can probably remove this check.
   if (!display_list_->ShouldBeAnalyzedForSolidColor())
     return;
 
   TRACE_EVENT1("cc", "RecordingSource::DetermineIfSolidColor", "opcount",
                display_list_->op_count());
-  gfx::Size layer_size = GetSize();
-  skia::AnalysisCanvas canvas(layer_size.width(), layer_size.height());
-  display_list_->Raster(&canvas);
-  is_solid_color_ = canvas.GetColorIfSolid(&solid_color_);
+  is_solid_color_ =
+      display_list_->GetColorIfSolidInRect(gfx::Rect(GetSize()), &solid_color_);
 }
 
 }  // namespace cc
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn
index baa9458a..57de616 100644
--- a/cc/paint/BUILD.gn
+++ b/cc/paint/BUILD.gn
@@ -35,6 +35,8 @@
     "record_paint_canvas.h",
     "skia_paint_canvas.cc",
     "skia_paint_canvas.h",
+    "solid_color_analyzer.cc",
+    "solid_color_analyzer.h",
   ]
 
   defines = [ "CC_PAINT_IMPLEMENTATION=1" ]
diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc
index a79d886c..8945e5a 100644
--- a/cc/paint/display_item_list.cc
+++ b/cc/paint/display_item_list.cc
@@ -15,6 +15,7 @@
 #include "cc/base/render_surface_filters.h"
 #include "cc/debug/picture_debug_util.h"
 #include "cc/paint/discardable_image_store.h"
+#include "cc/paint/solid_color_analyzer.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
 #include "ui/gfx/geometry/rect.h"
@@ -200,4 +201,23 @@
   return record;
 }
 
+bool DisplayItemList::GetColorIfSolidInRect(const gfx::Rect& rect,
+                                            SkColor* color) {
+  std::vector<size_t>* indices_to_use = nullptr;
+  std::vector<size_t> indices;
+  if (!rect.Contains(rtree_.GetBounds())) {
+    indices = rtree_.Search(rect);
+    indices_to_use = &indices;
+  }
+
+  base::Optional<SkColor> solid_color =
+      SolidColorAnalyzer::DetermineIfSolidColor(&paint_op_buffer_, rect,
+                                                indices_to_use);
+  if (solid_color) {
+    *color = *solid_color;
+    return true;
+  }
+  return false;
+}
+
 }  // namespace cc
diff --git a/cc/paint/display_item_list.h b/cc/paint/display_item_list.h
index fe3bdc7..e419e04 100644
--- a/cc/paint/display_item_list.h
+++ b/cc/paint/display_item_list.h
@@ -138,6 +138,8 @@
   // an empty state.
   sk_sp<PaintRecord> ReleaseAsRecord();
 
+  bool GetColorIfSolidInRect(const gfx::Rect& rect, SkColor* color);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, AsValueWithNoOps);
   FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, AsValueWithOps);
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h
index 07e579a..6954892d 100644
--- a/cc/paint/paint_flags.h
+++ b/cc/paint/paint_flags.h
@@ -27,6 +27,7 @@
     kStroke_Style = SkPaint::kStroke_Style,
     kStrokeAndFill_Style = SkPaint::kStrokeAndFill_Style,
   };
+  ALWAYS_INLINE bool nothingToDraw() const { return paint_.nothingToDraw(); }
   ALWAYS_INLINE Style getStyle() const {
     return static_cast<Style>(paint_.getStyle());
   }
diff --git a/cc/paint/solid_color_analyzer.cc b/cc/paint/solid_color_analyzer.cc
new file mode 100644
index 0000000..9aac466a
--- /dev/null
+++ b/cc/paint/solid_color_analyzer.cc
@@ -0,0 +1,255 @@
+// Copyright 2017 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 "cc/paint/solid_color_analyzer.h"
+
+#include "base/trace_event/trace_event.h"
+#include "cc/paint/paint_op_buffer.h"
+#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
+
+namespace cc {
+namespace {
+const int kMaxOpsToAnalyze = 1;
+
+bool ActsLikeClear(SkBlendMode mode, unsigned src_alpha) {
+  switch (mode) {
+    case SkBlendMode::kClear:
+      return true;
+    case SkBlendMode::kSrc:
+    case SkBlendMode::kSrcIn:
+    case SkBlendMode::kDstIn:
+    case SkBlendMode::kSrcOut:
+    case SkBlendMode::kDstATop:
+      return src_alpha == 0;
+    case SkBlendMode::kDstOut:
+      return src_alpha == 0xFF;
+    default:
+      return false;
+  }
+}
+
+bool IsSolidColor(SkColor color, SkBlendMode blendmode) {
+  return SkColorGetA(color) == 255 &&
+         (blendmode == SkBlendMode::kSrc || blendmode == SkBlendMode::kSrcOver);
+}
+
+bool IsSolidColorPaint(const PaintFlags& flags) {
+  SkBlendMode blendmode = flags.getBlendMode();
+
+  // 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 IsSolidColor(flags.getColor(), blendmode) && !flags.HasShader() &&
+         !flags.getLooper() && !flags.getMaskFilter() &&
+         !flags.getColorFilter() && !flags.getImageFilter() &&
+         flags.getStyle() == PaintFlags::kFill_Style;
+}
+
+// Returns true if the specified drawn_rect will cover the entire canvas, and
+// that the canvas is not clipped (i.e. it covers ALL of the canvas).
+bool IsFullQuad(const SkCanvas& canvas, const SkRect& drawn_rect) {
+  if (!canvas.isClipRect())
+    return false;
+
+  SkIRect clip_irect;
+  if (!canvas.getDeviceClipBounds(&clip_irect))
+    return false;
+
+  // if the clip is smaller than the canvas, we're partly clipped, so abort.
+  if (!clip_irect.contains(SkIRect::MakeSize(canvas.getBaseLayerSize())))
+    return false;
+
+  const SkMatrix& matrix = canvas.getTotalMatrix();
+  // If the transform results in a non-axis aligned
+  // rect, then be conservative and return false.
+  if (!matrix.rectStaysRect())
+    return false;
+
+  SkRect device_rect;
+  matrix.mapRect(&device_rect, drawn_rect);
+  SkRect clip_rect;
+  clip_rect.set(clip_irect);
+  return device_rect.contains(clip_rect);
+}
+
+void CheckIfSolidColor(const SkCanvas& canvas,
+                       SkColor color,
+                       SkBlendMode blendmode,
+                       bool* is_solid_color,
+                       bool* is_transparent,
+                       SkColor* out_color) {
+  SkRect rect;
+  if (!canvas.getLocalClipBounds(&rect)) {
+    *is_transparent = false;
+    *is_solid_color = false;
+    return;
+  }
+
+  bool does_cover_canvas = IsFullQuad(canvas, rect);
+  uint8_t alpha = SkColorGetA(color);
+  if (does_cover_canvas && ActsLikeClear(blendmode, alpha))
+    *is_transparent = true;
+  else if (alpha != 0 || blendmode != SkBlendMode::kSrc)
+    *is_transparent = false;
+
+  if (does_cover_canvas && IsSolidColor(color, blendmode)) {
+    *is_solid_color = true;
+    *out_color = color;
+  } else {
+    *is_solid_color = false;
+  }
+}
+
+void CheckIfSolidRect(const SkCanvas& canvas,
+                      const SkRect& rect,
+                      const PaintFlags& flags,
+                      bool* is_solid_color,
+                      bool* is_transparent,
+                      SkColor* color) {
+  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
+               "SolidColorAnalyzer::HandleDrawRect");
+  if (flags.nothingToDraw())
+    return;
+
+  bool does_cover_canvas = IsFullQuad(canvas, rect);
+  SkBlendMode blendmode = flags.getBlendMode();
+  if (does_cover_canvas && ActsLikeClear(blendmode, flags.getAlpha()))
+    *is_transparent = true;
+  else if (flags.getAlpha() != 0 || blendmode != SkBlendMode::kSrc)
+    *is_transparent = false;
+
+  if (does_cover_canvas && IsSolidColorPaint(flags)) {
+    *is_solid_color = true;
+    *color = flags.getColor();
+  } else {
+    *is_solid_color = false;
+  }
+}
+
+}  // namespace
+
+base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
+    const PaintOpBuffer* buffer,
+    const gfx::Rect& rect,
+    const std::vector<size_t>* indices) {
+  if (buffer->size() == 0 || (indices && indices->empty()))
+    return SK_ColorTRANSPARENT;
+
+  bool is_solid = false;
+  bool is_transparent = true;
+  SkColor color = SK_ColorTRANSPARENT;
+
+  struct Frame {
+    Frame() = default;
+    Frame(PaintOpBuffer::Iterator iter,
+          const SkMatrix& original_ctm,
+          int save_count)
+        : iter(iter), original_ctm(original_ctm), save_count(save_count) {}
+
+    PaintOpBuffer::Iterator iter;
+    const SkMatrix original_ctm;
+    int save_count = 0;
+  };
+
+  SkNoDrawCanvas canvas(rect.width(), rect.height());
+  canvas.translate(-rect.x(), -rect.y());
+  canvas.clipRect(gfx::RectToSkRect(rect), SkClipOp::kIntersect, false);
+
+  std::vector<Frame> stack;
+  // We expect to see at least one DrawRecordOp because of the way items are
+  // constructed. Reserve this to 2, and go from there.
+  stack.reserve(2);
+  stack.emplace_back(PaintOpBuffer::Iterator(buffer, indices),
+                     canvas.getTotalMatrix(), canvas.getSaveCount());
+
+  int num_ops = 0;
+  while (!stack.empty()) {
+    auto& frame = stack.back();
+    if (!frame.iter) {
+      canvas.restoreToCount(frame.save_count);
+      stack.pop_back();
+      if (!stack.empty())
+        ++stack.back().iter;
+      continue;
+    }
+
+    const PaintOp* op = *frame.iter;
+    const SkMatrix& original_ctm = frame.original_ctm;
+    switch (op->GetType()) {
+      case PaintOpType::DrawRecord: {
+        const DrawRecordOp* record_op = static_cast<const DrawRecordOp*>(op);
+        stack.emplace_back(PaintOpBuffer::Iterator(record_op->record.get()),
+                           canvas.getTotalMatrix(), canvas.getSaveCount());
+        continue;
+      }
+
+      // Any of the following ops result in non solid content.
+      case PaintOpType::DrawArc:
+      case PaintOpType::DrawCircle:
+      case PaintOpType::DrawDRRect:
+      case PaintOpType::DrawImage:
+      case PaintOpType::DrawImageRect:
+      case PaintOpType::DrawIRect:
+      case PaintOpType::DrawLine:
+      case PaintOpType::DrawOval:
+      case PaintOpType::DrawPath:
+      case PaintOpType::DrawPosText:
+      case PaintOpType::DrawRRect:
+      case PaintOpType::DrawText:
+      case PaintOpType::DrawTextBlob:
+      // Anything that has to do a save layer is probably not solid. As it will
+      // likely need more than one draw op.
+      // TODO(vmpstr): We could investigate handling these.
+      case PaintOpType::SaveLayer:
+      case PaintOpType::SaveLayerAlpha:
+      // Complex clips will probably result in non solid color as it might not
+      // cover the canvas.
+      // TODO(vmpstr): We could investigate handling these.
+      case PaintOpType::ClipPath:
+      case PaintOpType::ClipRRect:
+        return base::nullopt;
+
+      case PaintOpType::DrawRect: {
+        if (++num_ops > kMaxOpsToAnalyze)
+          return base::nullopt;
+        const DrawRectOp* rect_op = static_cast<const DrawRectOp*>(op);
+        CheckIfSolidRect(canvas, rect_op->rect, rect_op->flags, &is_solid,
+                         &is_transparent, &color);
+        break;
+      }
+      case PaintOpType::DrawColor: {
+        if (++num_ops > kMaxOpsToAnalyze)
+          return base::nullopt;
+        const DrawColorOp* color_op = static_cast<const DrawColorOp*>(op);
+        CheckIfSolidColor(canvas, color_op->color, color_op->mode, &is_solid,
+                          &is_transparent, &color);
+        break;
+      }
+
+      // The rest of the ops should only affect our state canvas.
+      case PaintOpType::Annotate:
+      case PaintOpType::ClipRect:
+      case PaintOpType::Concat:
+      case PaintOpType::Scale:
+      case PaintOpType::SetMatrix:
+      case PaintOpType::Restore:
+      case PaintOpType::Rotate:
+      case PaintOpType::Save:
+      case PaintOpType::Translate:
+      case PaintOpType::Noop:
+        op->Raster(&canvas, original_ctm);
+        break;
+    }
+    ++frame.iter;
+  }
+
+  if (is_transparent)
+    return SK_ColorTRANSPARENT;
+  if (is_solid)
+    return color;
+  return base::nullopt;
+}
+
+}  // namespace cc
diff --git a/cc/paint/solid_color_analyzer.h b/cc/paint/solid_color_analyzer.h
new file mode 100644
index 0000000..f0d9f26
--- /dev/null
+++ b/cc/paint/solid_color_analyzer.h
@@ -0,0 +1,30 @@
+// Copyright 2017 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.
+
+#ifndef CC_PAINT_SOLID_COLOR_ANALYZER_H_
+#define CC_PAINT_SOLID_COLOR_ANALYZER_H_
+
+#include <vector>
+
+#include "base/optional.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_flags.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT SolidColorAnalyzer {
+ public:
+  SolidColorAnalyzer() = delete;
+
+  static base::Optional<SkColor> DetermineIfSolidColor(
+      const PaintOpBuffer* buffer,
+      const gfx::Rect& rect,
+      const std::vector<size_t>* indices = nullptr);
+};
+
+}  // namespace cc
+
+#endif  // CC_PAINT_SOLID_COLOR_ANALYZER_H_
diff --git a/cc/paint/solid_color_analyzer_unittest.cc b/cc/paint/solid_color_analyzer_unittest.cc
new file mode 100644
index 0000000..a56d107
--- /dev/null
+++ b/cc/paint/solid_color_analyzer_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright 2017 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 "cc/paint/solid_color_analyzer.h"
+#include "base/optional.h"
+#include "cc/paint/record_paint_canvas.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/effects/SkOffsetImageFilter.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+namespace {
+
+class SolidColorAnalyzerTest : public testing::Test {
+ public:
+  void SetUp() override {}
+
+  void TearDown() override {
+    canvas_.reset();
+    buffer_.Reset();
+  }
+
+  void Initialize(const gfx::Rect& rect = gfx::Rect(0, 0, 100, 100)) {
+    canvas_.emplace(&buffer_, gfx::RectToSkRect(rect));
+    rect_ = rect;
+  }
+  RecordPaintCanvas* canvas() { return &*canvas_; }
+  PaintOpBuffer* paint_op_buffer() { return &buffer_; }
+
+  bool IsSolidColor() {
+    auto color =
+        SolidColorAnalyzer::DetermineIfSolidColor(&buffer_, rect_, nullptr);
+    return !!color;
+  }
+
+  SkColor GetColor() const {
+    auto color =
+        SolidColorAnalyzer::DetermineIfSolidColor(&buffer_, rect_, nullptr);
+    EXPECT_TRUE(color);
+    return color ? *color : SK_ColorTRANSPARENT;
+  }
+
+ private:
+  gfx::Rect rect_;
+  PaintOpBuffer buffer_;
+  base::Optional<RecordPaintCanvas> canvas_;
+  base::Optional<SolidColorAnalyzer> analyzer_;
+};
+
+TEST_F(SolidColorAnalyzerTest, Empty) {
+  Initialize();
+  EXPECT_EQ(SK_ColorTRANSPARENT, GetColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, ClearTransparent) {
+  Initialize();
+  SkColor color = SkColorSetARGB(0, 12, 34, 56);
+  canvas()->clear(color);
+  EXPECT_EQ(SK_ColorTRANSPARENT, GetColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, ClearSolid) {
+  Initialize();
+  SkColor color = SkColorSetARGB(255, 65, 43, 21);
+  canvas()->clear(color);
+  EXPECT_EQ(color, GetColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, ClearTranslucent) {
+  Initialize();
+  SkColor color = SkColorSetARGB(128, 11, 22, 33);
+  canvas()->clear(color);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawColor) {
+  Initialize();
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  canvas()->drawColor(color);
+  EXPECT_EQ(color, GetColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawOval) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  canvas()->drawOval(SkRect::MakeWH(100, 100), flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawBitmap) {
+  Initialize();
+  SkBitmap bitmap;
+  bitmap.allocN32Pixels(16, 16);
+  canvas()->drawBitmap(bitmap, 0, 0, nullptr);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRect) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->clipRect(rect, SkClipOp::kIntersect, false);
+  canvas()->drawRect(rect, flags);
+  EXPECT_EQ(color, GetColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectClipped) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->clipRect(SkRect::MakeWH(50, 50), SkClipOp::kIntersect, false);
+  canvas()->drawRect(rect, flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectWithTranslateNotSolid) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  SkRect rect = SkRect::MakeWH(100, 100);
+  canvas()->translate(1, 1);
+  canvas()->drawRect(rect, flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectWithTranslateSolid) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  SkRect rect = SkRect::MakeWH(101, 101);
+  canvas()->translate(1, 1);
+  canvas()->drawRect(rect, flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, TwoOpsNotSolid) {
+  Initialize();
+  SkColor color = SkColorSetARGB(255, 65, 43, 21);
+  canvas()->clear(color);
+  canvas()->clear(color);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectBlendModeClear) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  flags.setBlendMode(SkBlendMode::kClear);
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->drawRect(rect, flags);
+  EXPECT_EQ(SK_ColorTRANSPARENT, GetColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectBlendModeSrcOver) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  flags.setBlendMode(SkBlendMode::kSrcOver);
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->drawRect(rect, flags);
+  EXPECT_EQ(color, GetColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectRotated) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->rotate(50);
+  canvas()->drawRect(rect, flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectScaledNotSolid) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->scale(0.1f, 0.1f);
+  canvas()->drawRect(rect, flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectScaledSolid) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  SkRect rect = SkRect::MakeWH(10, 10);
+  canvas()->scale(10, 10);
+  canvas()->drawRect(rect, flags);
+  EXPECT_EQ(color, GetColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectFilterPaint) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+  flags.setImageFilter(SkOffsetImageFilter::Make(10, 10, nullptr));
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->drawRect(rect, flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, DrawRectClipPath) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+
+  SkPath path;
+  path.moveTo(0, 0);
+  path.lineTo(128, 50);
+  path.lineTo(255, 0);
+  path.lineTo(255, 255);
+  path.lineTo(0, 255);
+
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->clipPath(path, SkClipOp::kIntersect);
+  canvas()->drawRect(rect, flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+TEST_F(SolidColorAnalyzerTest, SaveLayer) {
+  Initialize();
+  PaintFlags flags;
+  SkColor color = SkColorSetARGB(255, 11, 22, 33);
+  flags.setColor(color);
+
+  SkRect rect = SkRect::MakeWH(200, 200);
+  canvas()->saveLayer(&rect, &flags);
+  EXPECT_FALSE(IsSolidColor());
+}
+
+}  // namespace
+}  // namespace cc
diff --git a/cc/raster/raster_source.cc b/cc/raster/raster_source.cc
index 9294cd6..124d5c5 100644
--- a/cc/raster/raster_source.cc
+++ b/cc/raster/raster_source.cc
@@ -237,21 +237,12 @@
   return display_list_->BytesUsed() + painter_reported_memory_usage_;
 }
 
-bool RasterSource::PerformSolidColorAnalysis(const gfx::Rect& content_rect,
-                                             float contents_scale,
+bool RasterSource::PerformSolidColorAnalysis(gfx::Rect layer_rect,
                                              SkColor* color) const {
   TRACE_EVENT0("cc", "RasterSource::PerformSolidColorAnalysis");
 
-  gfx::Rect layer_rect =
-      gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale);
-
   layer_rect.Intersect(gfx::Rect(size_));
-  skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height());
-  canvas.translate(-layer_rect.x(), -layer_rect.y());
-  // Note that because no color conversion is applied to solid color analysis,
-  // the resulting solid color will be known to be sRGB.
-  RasterCommon(&canvas, &canvas);
-  return canvas.GetColorIfSolid(color);
+  return display_list_->GetColorIfSolidInRect(layer_rect, color);
 }
 
 void RasterSource::GetDiscardableImagesInRect(
diff --git a/cc/raster/raster_source.h b/cc/raster/raster_source.h
index 92e429b..8479de1a 100644
--- a/cc/raster/raster_source.h
+++ b/cc/raster/raster_source.h
@@ -89,9 +89,7 @@
 
   // Returns whether the given rect at given scale is of solid color in
   // this raster source, as well as the solid color value.
-  bool PerformSolidColorAnalysis(const gfx::Rect& content_rect,
-                                 float contents_scale,
-                                 SkColor* color) const;
+  bool PerformSolidColorAnalysis(gfx::Rect content_rect, SkColor* color) const;
 
   // Returns true iff the whole raster source is of solid color.
   bool IsSolidColor() const;
diff --git a/cc/raster/raster_source_unittest.cc b/cc/raster/raster_source_unittest.cc
index 5cb06aaf..c75fcb8 100644
--- a/cc/raster/raster_source_unittest.cc
+++ b/cc/raster/raster_source_unittest.cc
@@ -53,7 +53,7 @@
   for (int y = 0; y <= 300; y += 100) {
     for (int x = 0; x <= 300; x += 100) {
       gfx::Rect rect(x, y, 100, 100);
-      is_solid_color = raster->PerformSolidColorAnalysis(rect, 1.f, &color);
+      is_solid_color = raster->PerformSolidColorAnalysis(rect, &color);
       EXPECT_TRUE(is_solid_color) << rect.ToString();
       EXPECT_EQ(solid_color, color) << rect.ToString();
     }
@@ -68,124 +68,35 @@
 
   color = SK_ColorTRANSPARENT;
   is_solid_color =
-      raster->PerformSolidColorAnalysis(gfx::Rect(0, 0, 100, 100), 1.f, &color);
+      raster->PerformSolidColorAnalysis(gfx::Rect(0, 0, 100, 100), &color);
   EXPECT_FALSE(is_solid_color);
 
   color = SK_ColorTRANSPARENT;
-  is_solid_color = raster->PerformSolidColorAnalysis(
-      gfx::Rect(100, 0, 100, 100), 1.f, &color);
+  is_solid_color =
+      raster->PerformSolidColorAnalysis(gfx::Rect(100, 0, 100, 100), &color);
   EXPECT_TRUE(is_solid_color);
   EXPECT_EQ(solid_color, color);
 
   // Boundaries should be clipped.
   color = SK_ColorTRANSPARENT;
-  is_solid_color = raster->PerformSolidColorAnalysis(
-      gfx::Rect(350, 0, 100, 100), 1.f, &color);
+  is_solid_color =
+      raster->PerformSolidColorAnalysis(gfx::Rect(350, 0, 100, 100), &color);
   EXPECT_TRUE(is_solid_color);
   EXPECT_EQ(solid_color, color);
 
   color = SK_ColorTRANSPARENT;
-  is_solid_color = raster->PerformSolidColorAnalysis(
-      gfx::Rect(0, 350, 100, 100), 1.f, &color);
+  is_solid_color =
+      raster->PerformSolidColorAnalysis(gfx::Rect(0, 350, 100, 100), &color);
   EXPECT_TRUE(is_solid_color);
   EXPECT_EQ(solid_color, color);
 
   color = SK_ColorTRANSPARENT;
-  is_solid_color = raster->PerformSolidColorAnalysis(
-      gfx::Rect(350, 350, 100, 100), 1.f, &color);
+  is_solid_color =
+      raster->PerformSolidColorAnalysis(gfx::Rect(350, 350, 100, 100), &color);
   EXPECT_TRUE(is_solid_color);
   EXPECT_EQ(solid_color, color);
 }
 
-TEST(RasterSourceTest, AnalyzeIsSolidScaled) {
-  gfx::Size layer_bounds(400, 400);
-
-  std::unique_ptr<FakeRecordingSource> recording_source =
-      FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
-
-  SkColor solid_color = SkColorSetARGB(255, 12, 23, 34);
-  SkColor color = SK_ColorTRANSPARENT;
-  PaintFlags solid_flags;
-  bool is_solid_color = false;
-  solid_flags.setColor(solid_color);
-
-  SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67);
-  PaintFlags non_solid_flags;
-  non_solid_flags.setColor(non_solid_color);
-
-  recording_source->add_draw_rect_with_flags(gfx::Rect(0, 0, 400, 400),
-                                             solid_flags);
-  recording_source->Rerecord();
-
-  scoped_refptr<RasterSource> raster =
-      RasterSource::CreateFromRecordingSource(recording_source.get(), false);
-
-  // Ensure everything is solid.
-  for (int y = 0; y <= 30; y += 10) {
-    for (int x = 0; x <= 30; x += 10) {
-      gfx::Rect rect(x, y, 10, 10);
-      is_solid_color = raster->PerformSolidColorAnalysis(rect, 0.1f, &color);
-      EXPECT_TRUE(is_solid_color) << rect.ToString();
-      EXPECT_EQ(color, solid_color) << rect.ToString();
-    }
-  }
-
-  // Add one non-solid pixel and recreate the raster source.
-  recording_source->add_draw_rect_with_flags(gfx::Rect(50, 50, 1, 1),
-                                             non_solid_flags);
-  recording_source->Rerecord();
-  raster =
-      RasterSource::CreateFromRecordingSource(recording_source.get(), false);
-
-  color = SK_ColorTRANSPARENT;
-  is_solid_color =
-      raster->PerformSolidColorAnalysis(gfx::Rect(0, 0, 10, 10), 0.1f, &color);
-  EXPECT_FALSE(is_solid_color);
-
-  color = SK_ColorTRANSPARENT;
-  is_solid_color =
-      raster->PerformSolidColorAnalysis(gfx::Rect(10, 0, 10, 10), 0.1f, &color);
-  EXPECT_TRUE(is_solid_color);
-  EXPECT_EQ(color, solid_color);
-
-  // Boundaries should be clipped.
-  color = SK_ColorTRANSPARENT;
-  is_solid_color =
-      raster->PerformSolidColorAnalysis(gfx::Rect(35, 0, 10, 10), 0.1f, &color);
-  EXPECT_TRUE(is_solid_color);
-  EXPECT_EQ(color, solid_color);
-
-  color = SK_ColorTRANSPARENT;
-  is_solid_color =
-      raster->PerformSolidColorAnalysis(gfx::Rect(0, 35, 10, 10), 0.1f, &color);
-  EXPECT_TRUE(is_solid_color);
-  EXPECT_EQ(color, solid_color);
-
-  color = SK_ColorTRANSPARENT;
-  is_solid_color = raster->PerformSolidColorAnalysis(gfx::Rect(35, 35, 10, 10),
-                                                     0.1f, &color);
-  EXPECT_TRUE(is_solid_color);
-  EXPECT_EQ(color, solid_color);
-}
-
-TEST(RasterSourceTest, AnalyzeIsSolidEmpty) {
-  gfx::Size layer_bounds(400, 400);
-
-  std::unique_ptr<FakeRecordingSource> recording_source =
-      FakeRecordingSource::CreateFilledRecordingSource(layer_bounds);
-  recording_source->Rerecord();
-
-  scoped_refptr<RasterSource> raster =
-      RasterSource::CreateFromRecordingSource(recording_source.get(), false);
-
-  SkColor color = SK_ColorTRANSPARENT;
-  bool is_solid_color =
-      raster->PerformSolidColorAnalysis(gfx::Rect(0, 0, 400, 400), 1.f, &color);
-
-  EXPECT_TRUE(is_solid_color);
-  EXPECT_EQ(color, SkColorSetARGB(0, 0, 0, 0));
-}
-
 TEST(RasterSourceTest, PixelRefIteratorDiscardableRefsOneTile) {
   gfx::Size layer_bounds(512, 512);
 
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 2e78129..d9a680d6 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -685,11 +685,9 @@
       // canvas which is reset between tiles.
       tile->set_solid_color_analysis_performed(true);
       SkColor color = SK_ColorTRANSPARENT;
-      gfx::RectF layer_rect = tile->raster_transform().InverseMapRect(
-          gfx::RectF(tile->content_rect()));
       bool is_solid_color =
           prioritized_tile.raster_source()->PerformSolidColorAnalysis(
-              gfx::ToEnclosingRect(layer_rect), 1.f, &color);
+              tile->enclosing_layer_rect(), &color);
       if (is_solid_color) {
         tile->draw_info().set_solid_color(color);
         client_->NotifyTileStateChanged(tile);