summaryrefslogtreecommitdiffstats
path: root/cc/playback
diff options
context:
space:
mode:
authordanakj <danakj@chromium.org>2015-05-18 13:22:29 -0700
committerCommit bot <commit-bot@chromium.org>2015-05-18 20:22:35 +0000
commit92015685c3dc2ef005e58c975269f532468e68b8 (patch)
tree94d71b2784e5a7e640cba6883d8c954cbabbca3c /cc/playback
parente3417c1e06cb80a5d1493e66a7f872297bd94e8f (diff)
downloadchromium_src-92015685c3dc2ef005e58c975269f532468e68b8.zip
chromium_src-92015685c3dc2ef005e58c975269f532468e68b8.tar.gz
chromium_src-92015685c3dc2ef005e58c975269f532468e68b8.tar.bz2
cc: Move files out of cc/resources/.
This moves files into cc/playback/ cc/raster/ cc/tiles/ and cc/output/ and is based on the proposal found in https://docs.google.com/spreadsheets/d/1wmPOmV9uqd9zNJ5l2TGePH7_vlSvPeXbC-Y3SeGJ_rc/edit#gid=0 R=enne, vmpstr TBR=piman BUG=488755 Review URL: https://codereview.chromium.org/1144693002 Cr-Commit-Position: refs/heads/master@{#330396}
Diffstat (limited to 'cc/playback')
-rw-r--r--cc/playback/clip_display_item.cc95
-rw-r--r--cc/playback/clip_display_item.h48
-rw-r--r--cc/playback/clip_path_display_item.cc61
-rw-r--r--cc/playback/clip_path_display_item.h50
-rw-r--r--cc/playback/compositing_display_item.cc78
-rw-r--r--cc/playback/compositing_display_item.h58
-rw-r--r--cc/playback/display_item.cc12
-rw-r--r--cc/playback/display_item.h51
-rw-r--r--cc/playback/display_item_list.cc229
-rw-r--r--cc/playback/display_item_list.h100
-rw-r--r--cc/playback/display_item_list_unittest.cc340
-rw-r--r--cc/playback/display_list_raster_source.cc235
-rw-r--r--cc/playback/display_list_raster_source.h98
-rw-r--r--cc/playback/display_list_recording_source.cc192
-rw-r--r--cc/playback/display_list_recording_source.h70
-rw-r--r--cc/playback/display_list_recording_source_unittest.cc165
-rw-r--r--cc/playback/drawing_display_item.cc62
-rw-r--r--cc/playback/drawing_display_item.h38
-rw-r--r--cc/playback/filter_display_item.cc81
-rw-r--r--cc/playback/filter_display_item.h49
-rw-r--r--cc/playback/float_clip_display_item.cc58
-rw-r--r--cc/playback/float_clip_display_item.h49
-rw-r--r--cc/playback/largest_display_item.cc71
-rw-r--r--cc/playback/largest_display_item.h17
-rw-r--r--cc/playback/picture.cc340
-rw-r--r--cc/playback/picture.h119
-rw-r--r--cc/playback/picture_pile.cc684
-rw-r--r--cc/playback/picture_pile.h107
-rw-r--r--cc/playback/picture_pile_impl.cc463
-rw-r--r--cc/playback/picture_pile_impl.h159
-rw-r--r--cc/playback/picture_pile_impl_perftest.cc83
-rw-r--r--cc/playback/picture_pile_impl_unittest.cc549
-rw-r--r--cc/playback/picture_pile_unittest.cc1345
-rw-r--r--cc/playback/picture_unittest.cc181
-rw-r--r--cc/playback/pixel_ref_map.cc172
-rw-r--r--cc/playback/pixel_ref_map.h94
-rw-r--r--cc/playback/pixel_ref_map_unittest.cc292
-rw-r--r--cc/playback/raster_source.h116
-rw-r--r--cc/playback/raster_source_helper.cc81
-rw-r--r--cc/playback/raster_source_helper.h29
-rw-r--r--cc/playback/recording_source.h63
-rw-r--r--cc/playback/recording_source_unittest.cc470
-rw-r--r--cc/playback/transform_display_item.cc59
-rw-r--r--cc/playback/transform_display_item.h47
44 files changed, 7760 insertions, 0 deletions
diff --git a/cc/playback/clip_display_item.cc b/cc/playback/clip_display_item.cc
new file mode 100644
index 0000000..a297874
--- /dev/null
+++ b/cc/playback/clip_display_item.cc
@@ -0,0 +1,95 @@
+// Copyright 2014 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/playback/clip_display_item.h"
+
+#include <string>
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+ClipDisplayItem::ClipDisplayItem() {
+}
+
+ClipDisplayItem::~ClipDisplayItem() {
+}
+
+void ClipDisplayItem::SetNew(gfx::Rect clip_rect,
+ const std::vector<SkRRect>& rounded_clip_rects) {
+ clip_rect_ = clip_rect;
+ rounded_clip_rects_ = rounded_clip_rects;
+
+ size_t memory_usage = sizeof(gfx::Rect);
+ for (size_t i = 0; i < rounded_clip_rects_.size(); ++i) {
+ memory_usage += sizeof(rounded_clip_rects_[i]);
+ }
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 1 /* op_count */,
+ memory_usage);
+}
+
+void ClipDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->save();
+ canvas->clipRect(SkRect::MakeXYWH(clip_rect_.x(), clip_rect_.y(),
+ clip_rect_.width(), clip_rect_.height()));
+ for (size_t i = 0; i < rounded_clip_rects_.size(); ++i) {
+ if (rounded_clip_rects_[i].isRect()) {
+ canvas->clipRect(rounded_clip_rects_[i].rect());
+ } else {
+ bool antialiased = true;
+ canvas->clipRRect(rounded_clip_rects_[i], SkRegion::kIntersect_Op,
+ antialiased);
+ }
+ }
+}
+
+void ClipDisplayItem::AsValueInto(base::trace_event::TracedValue* array) const {
+ std::string value = base::StringPrintf("ClipDisplayItem rect: [%s]",
+ clip_rect_.ToString().c_str());
+ for (const SkRRect& rounded_rect : rounded_clip_rects_) {
+ base::StringAppendF(
+ &value, " rounded_rect: [rect: [%s]",
+ gfx::SkRectToRectF(rounded_rect.rect()).ToString().c_str());
+ base::StringAppendF(&value, " radii: [");
+ SkVector upper_left_radius = rounded_rect.radii(SkRRect::kUpperLeft_Corner);
+ base::StringAppendF(&value, "[%f,%f],", upper_left_radius.x(),
+ upper_left_radius.y());
+ SkVector upper_right_radius =
+ rounded_rect.radii(SkRRect::kUpperRight_Corner);
+ base::StringAppendF(&value, " [%f,%f],", upper_right_radius.x(),
+ upper_right_radius.y());
+ SkVector lower_right_radius =
+ rounded_rect.radii(SkRRect::kLowerRight_Corner);
+ base::StringAppendF(&value, " [%f,%f],", lower_right_radius.x(),
+ lower_right_radius.y());
+ SkVector lower_left_radius = rounded_rect.radii(SkRRect::kLowerLeft_Corner);
+ base::StringAppendF(&value, " [%f,%f]]", lower_left_radius.x(),
+ lower_left_radius.y());
+ }
+ array->AppendString(value);
+}
+
+EndClipDisplayItem::EndClipDisplayItem() {
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 0 /* op_count */,
+ 0 /* memory_usage */);
+}
+
+EndClipDisplayItem::~EndClipDisplayItem() {
+}
+
+void EndClipDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->restore();
+}
+
+void EndClipDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString("EndClipDisplayItem");
+}
+
+} // namespace cc
diff --git a/cc/playback/clip_display_item.h b/cc/playback/clip_display_item.h
new file mode 100644
index 0000000..a4acc9f
--- /dev/null
+++ b/cc/playback/clip_display_item.h
@@ -0,0 +1,48 @@
+// Copyright 2014 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_PLAYBACK_CLIP_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_CLIP_DISPLAY_ITEM_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/playback/display_item.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+
+namespace cc {
+
+class CC_EXPORT ClipDisplayItem : public DisplayItem {
+ public:
+ ClipDisplayItem();
+ ~ClipDisplayItem() override;
+
+ void SetNew(gfx::Rect clip_rect,
+ const std::vector<SkRRect>& rounded_clip_rects);
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+
+ private:
+ gfx::Rect clip_rect_;
+ std::vector<SkRRect> rounded_clip_rects_;
+};
+
+class CC_EXPORT EndClipDisplayItem : public DisplayItem {
+ public:
+ EndClipDisplayItem();
+ ~EndClipDisplayItem() override;
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_CLIP_DISPLAY_ITEM_H_
diff --git a/cc/playback/clip_path_display_item.cc b/cc/playback/clip_path_display_item.cc
new file mode 100644
index 0000000..7be8922
--- /dev/null
+++ b/cc/playback/clip_path_display_item.cc
@@ -0,0 +1,61 @@
+// Copyright 2015 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/playback/clip_path_display_item.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+
+namespace cc {
+
+ClipPathDisplayItem::ClipPathDisplayItem() {
+}
+
+ClipPathDisplayItem::~ClipPathDisplayItem() {
+}
+
+void ClipPathDisplayItem::SetNew(const SkPath& clip_path,
+ SkRegion::Op clip_op,
+ bool antialias) {
+ clip_path_ = clip_path;
+ clip_op_ = clip_op;
+ antialias_ = antialias;
+
+ size_t memory_usage = sizeof(SkPath) + sizeof(SkRegion::Op) + sizeof(bool);
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 1 /* op_count */,
+ memory_usage);
+}
+
+void ClipPathDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->save();
+ canvas->clipPath(clip_path_, clip_op_, antialias_);
+}
+
+void ClipPathDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString(base::StringPrintf("ClipPathDisplayItem length: %d",
+ clip_path_.countPoints()));
+}
+
+EndClipPathDisplayItem::EndClipPathDisplayItem() {
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 0 /* op_count */,
+ 0 /* memory_usage */);
+}
+
+EndClipPathDisplayItem::~EndClipPathDisplayItem() {
+}
+
+void EndClipPathDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->restore();
+}
+
+void EndClipPathDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString("EndClipPathDisplayItem");
+}
+
+} // namespace cc
diff --git a/cc/playback/clip_path_display_item.h b/cc/playback/clip_path_display_item.h
new file mode 100644
index 0000000..19d4a7c
--- /dev/null
+++ b/cc/playback/clip_path_display_item.h
@@ -0,0 +1,50 @@
+// Copyright 2015 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_PLAYBACK_CLIP_PATH_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_CLIP_PATH_DISPLAY_ITEM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/playback/display_item.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkRegion.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+
+namespace cc {
+
+class CC_EXPORT ClipPathDisplayItem : public DisplayItem {
+ public:
+ ClipPathDisplayItem();
+ ~ClipPathDisplayItem() override;
+
+ void SetNew(const SkPath& path, SkRegion::Op clip_op, bool antialias);
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+
+ private:
+ SkPath clip_path_;
+ SkRegion::Op clip_op_;
+ bool antialias_;
+};
+
+class CC_EXPORT EndClipPathDisplayItem : public DisplayItem {
+ public:
+ EndClipPathDisplayItem();
+ ~EndClipPathDisplayItem() override;
+
+ static scoped_ptr<EndClipPathDisplayItem> Create() {
+ return make_scoped_ptr(new EndClipPathDisplayItem());
+ }
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_CLIP_PATH_DISPLAY_ITEM_H_
diff --git a/cc/playback/compositing_display_item.cc b/cc/playback/compositing_display_item.cc
new file mode 100644
index 0000000..bee1fb1
--- /dev/null
+++ b/cc/playback/compositing_display_item.cc
@@ -0,0 +1,78 @@
+// Copyright 2015 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/playback/compositing_display_item.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+CompositingDisplayItem::CompositingDisplayItem() {
+}
+
+CompositingDisplayItem::~CompositingDisplayItem() {
+}
+
+void CompositingDisplayItem::SetNew(uint8_t alpha,
+ SkXfermode::Mode xfermode,
+ SkRect* bounds,
+ skia::RefPtr<SkColorFilter> cf) {
+ alpha_ = alpha;
+ xfermode_ = xfermode;
+ has_bounds_ = !!bounds;
+ if (bounds)
+ bounds_ = SkRect(*bounds);
+ color_filter_ = cf;
+
+ // TODO(pdr): Include color_filter's memory here.
+ size_t memory_usage =
+ sizeof(float) + sizeof(bool) + sizeof(SkRect) + sizeof(SkXfermode::Mode);
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 1 /* op_count */,
+ memory_usage);
+}
+
+void CompositingDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ SkPaint paint;
+ paint.setXfermodeMode(xfermode_);
+ paint.setAlpha(alpha_);
+ paint.setColorFilter(color_filter_.get());
+ canvas->saveLayer(has_bounds_ ? &bounds_ : nullptr, &paint);
+}
+
+void CompositingDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString(base::StringPrintf(
+ "CompositingDisplayItem alpha: %d, xfermode: %d", alpha_, xfermode_));
+ if (has_bounds_)
+ array->AppendString(base::StringPrintf(
+ ", bounds: [%f, %f, %f, %f]", static_cast<float>(bounds_.x()),
+ static_cast<float>(bounds_.y()), static_cast<float>(bounds_.width()),
+ static_cast<float>(bounds_.height())));
+}
+
+EndCompositingDisplayItem::EndCompositingDisplayItem() {
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 0 /* op_count */,
+ 0 /* memory_usage */);
+}
+
+EndCompositingDisplayItem::~EndCompositingDisplayItem() {
+}
+
+void EndCompositingDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->restore();
+}
+
+void EndCompositingDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString("EndCompositingDisplayItem");
+}
+
+} // namespace cc
diff --git a/cc/playback/compositing_display_item.h b/cc/playback/compositing_display_item.h
new file mode 100644
index 0000000..d2af8d5
--- /dev/null
+++ b/cc/playback/compositing_display_item.h
@@ -0,0 +1,58 @@
+// Copyright 2015 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_PLAYBACK_COMPOSITING_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_COMPOSITING_DISPLAY_ITEM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/playback/display_item.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+
+namespace cc {
+
+class CC_EXPORT CompositingDisplayItem : public DisplayItem {
+ public:
+ CompositingDisplayItem();
+ ~CompositingDisplayItem() override;
+
+ void SetNew(uint8_t alpha,
+ SkXfermode::Mode xfermode,
+ SkRect* bounds,
+ skia::RefPtr<SkColorFilter> color_filter);
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+
+ private:
+ uint8_t alpha_;
+ SkXfermode::Mode xfermode_;
+ bool has_bounds_;
+ SkRect bounds_;
+ skia::RefPtr<SkColorFilter> color_filter_;
+};
+
+class CC_EXPORT EndCompositingDisplayItem : public DisplayItem {
+ public:
+ EndCompositingDisplayItem();
+ ~EndCompositingDisplayItem() override;
+
+ static scoped_ptr<EndCompositingDisplayItem> Create() {
+ return make_scoped_ptr(new EndCompositingDisplayItem());
+ }
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_COMPOSITING_DISPLAY_ITEM_H_
diff --git a/cc/playback/display_item.cc b/cc/playback/display_item.cc
new file mode 100644
index 0000000..cbfee03
--- /dev/null
+++ b/cc/playback/display_item.cc
@@ -0,0 +1,12 @@
+// Copyright 2014 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/playback/display_item.h"
+
+namespace cc {
+
+DisplayItem::DisplayItem() {
+}
+
+} // namespace cc
diff --git a/cc/playback/display_item.h b/cc/playback/display_item.h
new file mode 100644
index 0000000..2eedb91
--- /dev/null
+++ b/cc/playback/display_item.h
@@ -0,0 +1,51 @@
+// Copyright 2014 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_PLAYBACK_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_DISPLAY_ITEM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/debug/traced_value.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+
+namespace cc {
+
+class CC_EXPORT DisplayItem {
+ public:
+ virtual ~DisplayItem() {}
+
+ void SetNew(bool is_suitable_for_gpu_rasterization,
+ int approximate_op_count,
+ size_t picture_memory_usage) {
+ is_suitable_for_gpu_rasterization_ = is_suitable_for_gpu_rasterization;
+ approximate_op_count_ = approximate_op_count;
+ picture_memory_usage_ =
+ picture_memory_usage + sizeof(bool) + sizeof(int) + sizeof(size_t);
+ }
+
+ virtual void Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const = 0;
+ virtual void AsValueInto(base::trace_event::TracedValue* array) const = 0;
+
+ bool is_suitable_for_gpu_rasterization() const {
+ return is_suitable_for_gpu_rasterization_;
+ }
+ int approximate_op_count() const { return approximate_op_count_; }
+ size_t picture_memory_usage() const { return picture_memory_usage_; }
+
+ protected:
+ DisplayItem();
+
+ bool is_suitable_for_gpu_rasterization_;
+ int approximate_op_count_;
+ size_t picture_memory_usage_;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_DISPLAY_ITEM_H_
diff --git a/cc/playback/display_item_list.cc b/cc/playback/display_item_list.cc
new file mode 100644
index 0000000..656ade1
--- /dev/null
+++ b/cc/playback/display_item_list.cc
@@ -0,0 +1,229 @@
+// Copyright 2014 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/playback/display_item_list.h"
+
+#include <string>
+
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "cc/base/math_util.h"
+#include "cc/debug/picture_debug_util.h"
+#include "cc/debug/traced_picture.h"
+#include "cc/debug/traced_value.h"
+#include "cc/playback/largest_display_item.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDrawPictureCallback.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/utils/SkPictureUtils.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+namespace {
+
+bool PictureTracingEnabled() {
+ bool tracing_enabled;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
+ TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
+ &tracing_enabled);
+ return tracing_enabled;
+}
+
+const int kDefaultNumDisplayItemsToReserve = 100;
+
+} // namespace
+
+DisplayItemList::DisplayItemList(gfx::Rect layer_rect,
+ bool use_cached_picture,
+ bool retain_individual_display_items)
+ : items_(LargestDisplayItemSize(), kDefaultNumDisplayItemsToReserve),
+ use_cached_picture_(use_cached_picture),
+ retain_individual_display_items_(retain_individual_display_items),
+ layer_rect_(layer_rect),
+ is_suitable_for_gpu_rasterization_(true),
+ approximate_op_count_(0),
+ picture_memory_usage_(0) {
+#if DCHECK_IS_ON()
+ needs_process_ = false;
+#endif
+ if (use_cached_picture_) {
+ SkRTreeFactory factory;
+ recorder_.reset(new SkPictureRecorder());
+ canvas_ = skia::SharePtr(recorder_->beginRecording(
+ layer_rect_.width(), layer_rect_.height(), &factory));
+ canvas_->translate(-layer_rect_.x(), -layer_rect_.y());
+ canvas_->clipRect(gfx::RectToSkRect(layer_rect_));
+ }
+}
+
+DisplayItemList::DisplayItemList(gfx::Rect layer_rect, bool use_cached_picture)
+ : DisplayItemList(layer_rect,
+ use_cached_picture,
+ !use_cached_picture || PictureTracingEnabled()) {
+}
+
+scoped_refptr<DisplayItemList> DisplayItemList::Create(
+ gfx::Rect layer_rect,
+ bool use_cached_picture) {
+ return make_scoped_refptr(
+ new DisplayItemList(layer_rect, use_cached_picture));
+}
+
+DisplayItemList::~DisplayItemList() {
+}
+
+void DisplayItemList::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback,
+ float contents_scale) const {
+ DCHECK(ProcessAppendedItemsCalled());
+ if (!use_cached_picture_) {
+ canvas->save();
+ canvas->scale(contents_scale, contents_scale);
+ for (auto* item : items_)
+ item->Raster(canvas, callback);
+ canvas->restore();
+ } else {
+ DCHECK(picture_);
+
+ canvas->save();
+ canvas->scale(contents_scale, contents_scale);
+ canvas->translate(layer_rect_.x(), layer_rect_.y());
+ if (callback) {
+ // If we have a callback, we need to call |draw()|, |drawPicture()|
+ // doesn't take a callback. This is used by |AnalysisCanvas| to early
+ // out.
+ picture_->playback(canvas, callback);
+ } else {
+ // Prefer to call |drawPicture()| on the canvas since it could place the
+ // entire picture on the canvas instead of parsing the skia operations.
+ canvas->drawPicture(picture_.get());
+ }
+ canvas->restore();
+ }
+}
+
+void DisplayItemList::ProcessAppendedItemsOnTheFly() {
+ if (retain_individual_display_items_)
+ return;
+ if (items_.size() >= kDefaultNumDisplayItemsToReserve) {
+ ProcessAppendedItems();
+ // This function exists to keep the |items_| from growing indefinitely if
+ // we're not going to store them anyway. So the items better be deleted
+ // after |items_| grows too large and we process it.
+ DCHECK(items_.empty());
+ }
+}
+
+void DisplayItemList::ProcessAppendedItems() {
+#if DCHECK_IS_ON()
+ needs_process_ = false;
+#endif
+ for (DisplayItem* item : items_) {
+ is_suitable_for_gpu_rasterization_ &=
+ item->is_suitable_for_gpu_rasterization();
+ approximate_op_count_ += item->approximate_op_count();
+
+ if (use_cached_picture_) {
+ DCHECK(canvas_);
+ item->Raster(canvas_.get(), NULL);
+ }
+
+ if (retain_individual_display_items_) {
+ // Warning: this double-counts SkPicture data if use_cached_picture_ is
+ // also true.
+ picture_memory_usage_ += item->picture_memory_usage();
+ }
+ }
+
+ if (!retain_individual_display_items_)
+ items_.clear();
+}
+
+void DisplayItemList::CreateAndCacheSkPicture() {
+ DCHECK(ProcessAppendedItemsCalled());
+ // Convert to an SkPicture for faster rasterization.
+ DCHECK(use_cached_picture_);
+ DCHECK(!picture_);
+ picture_ = skia::AdoptRef(recorder_->endRecordingAsPicture());
+ DCHECK(picture_);
+ picture_memory_usage_ += SkPictureUtils::ApproximateBytesUsed(picture_.get());
+ recorder_.reset();
+ canvas_.clear();
+}
+
+bool DisplayItemList::IsSuitableForGpuRasterization() const {
+ DCHECK(ProcessAppendedItemsCalled());
+ // This is more permissive than Picture's implementation, since none of the
+ // items might individually trigger a veto even though they collectively have
+ // enough "bad" operations that a corresponding Picture would get vetoed.
+ return is_suitable_for_gpu_rasterization_;
+}
+
+int DisplayItemList::ApproximateOpCount() const {
+ DCHECK(ProcessAppendedItemsCalled());
+ return approximate_op_count_;
+}
+
+size_t DisplayItemList::PictureMemoryUsage() const {
+ DCHECK(ProcessAppendedItemsCalled());
+ // We double-count in this case. Produce zero to avoid being misleading.
+ if (use_cached_picture_ && retain_individual_display_items_)
+ return 0;
+
+ DCHECK_IMPLIES(use_cached_picture_, picture_);
+ return picture_memory_usage_;
+}
+
+scoped_refptr<base::trace_event::ConvertableToTraceFormat>
+DisplayItemList::AsValue() const {
+ DCHECK(ProcessAppendedItemsCalled());
+ scoped_refptr<base::trace_event::TracedValue> state =
+ new base::trace_event::TracedValue();
+
+ state->SetInteger("length", items_.size());
+ state->BeginArray("params.items");
+ for (const DisplayItem* item : items_) {
+ item->AsValueInto(state.get());
+ }
+ state->EndArray();
+ state->SetValue("params.layer_rect", MathUtil::AsValue(layer_rect_));
+
+ SkPictureRecorder recorder;
+ SkCanvas* canvas =
+ recorder.beginRecording(layer_rect_.width(), layer_rect_.height());
+ canvas->translate(-layer_rect_.x(), -layer_rect_.y());
+ canvas->clipRect(gfx::RectToSkRect(layer_rect_));
+ Raster(canvas, NULL, 1.f);
+ skia::RefPtr<SkPicture> picture =
+ skia::AdoptRef(recorder.endRecordingAsPicture());
+
+ std::string b64_picture;
+ PictureDebugUtil::SerializeAsBase64(picture.get(), &b64_picture);
+ state->SetString("skp64", b64_picture);
+
+ return state;
+}
+
+void DisplayItemList::EmitTraceSnapshot() const {
+ DCHECK(ProcessAppendedItemsCalled());
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
+ TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
+ "cc::DisplayItemList", this, AsValue());
+}
+
+void DisplayItemList::GatherPixelRefs(const gfx::Size& grid_cell_size) {
+ DCHECK(ProcessAppendedItemsCalled());
+ // This should be only called once, and only after CreateAndCacheSkPicture.
+ DCHECK(picture_);
+ DCHECK(!pixel_refs_);
+ pixel_refs_ = make_scoped_ptr(new PixelRefMap(grid_cell_size));
+ if (!picture_->willPlayBackBitmaps())
+ return;
+
+ pixel_refs_->GatherPixelRefsFromPicture(picture_.get());
+}
+} // namespace cc
diff --git a/cc/playback/display_item_list.h b/cc/playback/display_item_list.h
new file mode 100644
index 0000000..84b4168
--- /dev/null
+++ b/cc/playback/display_item_list.h
@@ -0,0 +1,100 @@
+// Copyright 2014 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_PLAYBACK_DISPLAY_ITEM_LIST_H_
+#define CC_PLAYBACK_DISPLAY_ITEM_LIST_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/base/cc_export.h"
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/playback/display_item.h"
+#include "cc/playback/pixel_ref_map.h"
+// TODO(danakj): Move ListContainer out of cc/quads/
+#include "cc/quads/list_container.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+class SkPictureRecorder;
+
+namespace cc {
+
+class CC_EXPORT DisplayItemList
+ : public base::RefCountedThreadSafe<DisplayItemList> {
+ public:
+ static scoped_refptr<DisplayItemList> Create(gfx::Rect layer_rect,
+ bool use_cached_picture);
+
+ void Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback,
+ float contents_scale) const;
+
+ template <typename DisplayItemType>
+ DisplayItemType* CreateAndAppendItem() {
+#if DCHECK_IS_ON()
+ needs_process_ = true;
+#endif
+ ProcessAppendedItemsOnTheFly();
+ return items_.AllocateAndConstruct<DisplayItemType>();
+ }
+
+ void ProcessAppendedItems();
+ void CreateAndCacheSkPicture();
+
+ bool IsSuitableForGpuRasterization() const;
+ int ApproximateOpCount() const;
+ size_t PictureMemoryUsage() const;
+
+ scoped_refptr<base::trace_event::ConvertableToTraceFormat> AsValue() const;
+
+ void EmitTraceSnapshot() const;
+
+ void GatherPixelRefs(const gfx::Size& grid_cell_size);
+
+ private:
+ DisplayItemList(gfx::Rect layer_rect,
+ bool use_cached_picture,
+ bool retain_individual_display_items);
+ DisplayItemList(gfx::Rect layer_rect, bool use_cached_picture);
+ ~DisplayItemList();
+
+ // While appending new items, if they are not being retained, this can process
+ // periodically to avoid retaining all the items and processing at the end.
+ void ProcessAppendedItemsOnTheFly();
+#if DCHECK_IS_ON()
+ bool ProcessAppendedItemsCalled() const { return !needs_process_; }
+ bool needs_process_;
+#else
+ bool ProcessAppendedItemsCalled() const { return true; }
+#endif
+
+ ListContainer<DisplayItem> items_;
+ skia::RefPtr<SkPicture> picture_;
+
+ scoped_ptr<SkPictureRecorder> recorder_;
+ skia::RefPtr<SkCanvas> canvas_;
+ bool use_cached_picture_;
+ bool retain_individual_display_items_;
+
+ gfx::Rect layer_rect_;
+ bool is_suitable_for_gpu_rasterization_;
+ int approximate_op_count_;
+ size_t picture_memory_usage_;
+
+ scoped_ptr<PixelRefMap> pixel_refs_;
+
+ friend class base::RefCountedThreadSafe<DisplayItemList>;
+ friend class PixelRefMap::Iterator;
+ FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, PictureMemoryUsage);
+ DISALLOW_COPY_AND_ASSIGN(DisplayItemList);
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_DISPLAY_ITEM_LIST_H_
diff --git a/cc/playback/display_item_list_unittest.cc b/cc/playback/display_item_list_unittest.cc
new file mode 100644
index 0000000..6a5bbf6
--- /dev/null
+++ b/cc/playback/display_item_list_unittest.cc
@@ -0,0 +1,340 @@
+// Copyright 2014 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/playback/display_item_list.h"
+
+#include <vector>
+
+#include "cc/output/filter_operation.h"
+#include "cc/output/filter_operations.h"
+#include "cc/playback/clip_display_item.h"
+#include "cc/playback/drawing_display_item.h"
+#include "cc/playback/filter_display_item.h"
+#include "cc/playback/transform_display_item.h"
+#include "cc/test/skia_common.h"
+#include "skia/ext/refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/effects/SkBitmapSource.h"
+#include "third_party/skia/include/utils/SkPictureUtils.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+TEST(DisplayItemListTest, SingleDrawingItem) {
+ gfx::Rect layer_rect(100, 100);
+ SkPictureRecorder recorder;
+ skia::RefPtr<SkCanvas> canvas;
+ skia::RefPtr<SkPicture> picture;
+ SkPaint blue_paint;
+ blue_paint.setColor(SK_ColorBLUE);
+ SkPaint red_paint;
+ red_paint.setColor(SK_ColorRED);
+ unsigned char pixels[4 * 100 * 100] = {0};
+ const bool use_cached_picture = true;
+ scoped_refptr<DisplayItemList> list =
+ DisplayItemList::Create(layer_rect, use_cached_picture);
+
+ gfx::PointF offset(8.f, 9.f);
+ gfx::RectF recording_rect(offset, layer_rect.size());
+ canvas = skia::SharePtr(
+ recorder.beginRecording(gfx::RectFToSkRect(recording_rect)));
+ canvas->translate(offset.x(), offset.y());
+ canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
+ canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
+ picture = skia::AdoptRef(recorder.endRecordingAsPicture());
+ auto* item = list->CreateAndAppendItem<DrawingDisplayItem>();
+ item->SetNew(picture);
+ list->ProcessAppendedItems();
+ list->CreateAndCacheSkPicture();
+ DrawDisplayList(pixels, layer_rect, list);
+
+ SkBitmap expected_bitmap;
+ unsigned char expected_pixels[4 * 100 * 100] = {0};
+ SkImageInfo info =
+ SkImageInfo::MakeN32Premul(layer_rect.width(), layer_rect.height());
+ expected_bitmap.installPixels(info, expected_pixels, info.minRowBytes());
+ SkCanvas expected_canvas(expected_bitmap);
+ expected_canvas.clipRect(gfx::RectToSkRect(layer_rect));
+ expected_canvas.drawRectCoords(0.f + offset.x(), 0.f + offset.y(),
+ 60.f + offset.x(), 60.f + offset.y(),
+ red_paint);
+ expected_canvas.drawRectCoords(50.f + offset.x(), 50.f + offset.y(),
+ 75.f + offset.x(), 75.f + offset.y(),
+ blue_paint);
+
+ EXPECT_EQ(0, memcmp(pixels, expected_pixels, 4 * 100 * 100));
+}
+
+TEST(DisplayItemListTest, ClipItem) {
+ gfx::Rect layer_rect(100, 100);
+ SkPictureRecorder recorder;
+ skia::RefPtr<SkCanvas> canvas;
+ skia::RefPtr<SkPicture> picture;
+ SkPaint blue_paint;
+ blue_paint.setColor(SK_ColorBLUE);
+ SkPaint red_paint;
+ red_paint.setColor(SK_ColorRED);
+ unsigned char pixels[4 * 100 * 100] = {0};
+ const bool use_cached_picture = true;
+ scoped_refptr<DisplayItemList> list =
+ DisplayItemList::Create(layer_rect, use_cached_picture);
+
+ gfx::PointF first_offset(8.f, 9.f);
+ gfx::RectF first_recording_rect(first_offset, layer_rect.size());
+ canvas = skia::SharePtr(
+ recorder.beginRecording(gfx::RectFToSkRect(first_recording_rect)));
+ canvas->translate(first_offset.x(), first_offset.y());
+ canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
+ picture = skia::AdoptRef(recorder.endRecordingAsPicture());
+ auto* item1 = list->CreateAndAppendItem<DrawingDisplayItem>();
+ item1->SetNew(picture.Pass());
+
+ gfx::Rect clip_rect(60, 60, 10, 10);
+ auto* item2 = list->CreateAndAppendItem<ClipDisplayItem>();
+ item2->SetNew(clip_rect, std::vector<SkRRect>());
+
+ gfx::PointF second_offset(2.f, 3.f);
+ gfx::RectF second_recording_rect(second_offset, layer_rect.size());
+ canvas = skia::SharePtr(
+ recorder.beginRecording(gfx::RectFToSkRect(second_recording_rect)));
+ canvas->translate(second_offset.x(), second_offset.y());
+ canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
+ picture = skia::AdoptRef(recorder.endRecordingAsPicture());
+ auto* item3 = list->CreateAndAppendItem<DrawingDisplayItem>();
+ item3->SetNew(picture.Pass());
+
+ list->CreateAndAppendItem<EndClipDisplayItem>();
+ list->ProcessAppendedItems();
+ list->CreateAndCacheSkPicture();
+
+ DrawDisplayList(pixels, layer_rect, list);
+
+ SkBitmap expected_bitmap;
+ unsigned char expected_pixels[4 * 100 * 100] = {0};
+ SkImageInfo info =
+ SkImageInfo::MakeN32Premul(layer_rect.width(), layer_rect.height());
+ expected_bitmap.installPixels(info, expected_pixels, info.minRowBytes());
+ SkCanvas expected_canvas(expected_bitmap);
+ expected_canvas.clipRect(gfx::RectToSkRect(layer_rect));
+ expected_canvas.drawRectCoords(0.f + first_offset.x(), 0.f + first_offset.y(),
+ 60.f + first_offset.x(),
+ 60.f + first_offset.y(), red_paint);
+ expected_canvas.clipRect(gfx::RectToSkRect(clip_rect));
+ expected_canvas.drawRectCoords(
+ 50.f + second_offset.x(), 50.f + second_offset.y(),
+ 75.f + second_offset.x(), 75.f + second_offset.y(), blue_paint);
+
+ EXPECT_EQ(0, memcmp(pixels, expected_pixels, 4 * 100 * 100));
+}
+
+TEST(DisplayItemListTest, TransformItem) {
+ gfx::Rect layer_rect(100, 100);
+ SkPictureRecorder recorder;
+ skia::RefPtr<SkCanvas> canvas;
+ skia::RefPtr<SkPicture> picture;
+ SkPaint blue_paint;
+ blue_paint.setColor(SK_ColorBLUE);
+ SkPaint red_paint;
+ red_paint.setColor(SK_ColorRED);
+ unsigned char pixels[4 * 100 * 100] = {0};
+ const bool use_cached_picture = true;
+ scoped_refptr<DisplayItemList> list =
+ DisplayItemList::Create(layer_rect, use_cached_picture);
+
+ gfx::PointF first_offset(8.f, 9.f);
+ gfx::RectF first_recording_rect(first_offset, layer_rect.size());
+ canvas = skia::SharePtr(
+ recorder.beginRecording(gfx::RectFToSkRect(first_recording_rect)));
+ canvas->translate(first_offset.x(), first_offset.y());
+ canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
+ picture = skia::AdoptRef(recorder.endRecordingAsPicture());
+ auto* item1 = list->CreateAndAppendItem<DrawingDisplayItem>();
+ item1->SetNew(picture);
+
+ gfx::Transform transform;
+ transform.Rotate(45.0);
+ auto* item2 = list->CreateAndAppendItem<TransformDisplayItem>();
+ item2->SetNew(transform);
+
+ gfx::PointF second_offset(2.f, 3.f);
+ gfx::RectF second_recording_rect(second_offset, layer_rect.size());
+ canvas = skia::SharePtr(
+ recorder.beginRecording(gfx::RectFToSkRect(second_recording_rect)));
+ canvas->translate(second_offset.x(), second_offset.y());
+ canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
+ picture = skia::AdoptRef(recorder.endRecordingAsPicture());
+ auto* item3 = list->CreateAndAppendItem<DrawingDisplayItem>();
+ item3->SetNew(picture);
+
+ list->CreateAndAppendItem<EndTransformDisplayItem>();
+ list->ProcessAppendedItems();
+ list->CreateAndCacheSkPicture();
+
+ DrawDisplayList(pixels, layer_rect, list);
+
+ SkBitmap expected_bitmap;
+ unsigned char expected_pixels[4 * 100 * 100] = {0};
+ SkImageInfo info =
+ SkImageInfo::MakeN32Premul(layer_rect.width(), layer_rect.height());
+ expected_bitmap.installPixels(info, expected_pixels, info.minRowBytes());
+ SkCanvas expected_canvas(expected_bitmap);
+ expected_canvas.clipRect(gfx::RectToSkRect(layer_rect));
+ expected_canvas.drawRectCoords(0.f + first_offset.x(), 0.f + first_offset.y(),
+ 60.f + first_offset.x(),
+ 60.f + first_offset.y(), red_paint);
+ expected_canvas.setMatrix(transform.matrix());
+ expected_canvas.drawRectCoords(
+ 50.f + second_offset.x(), 50.f + second_offset.y(),
+ 75.f + second_offset.x(), 75.f + second_offset.y(), blue_paint);
+
+ EXPECT_EQ(0, memcmp(pixels, expected_pixels, 4 * 100 * 100));
+}
+
+TEST(DisplayItemListTest, FilterItem) {
+ gfx::Rect layer_rect(100, 100);
+ FilterOperations filters;
+ unsigned char pixels[4 * 100 * 100] = {0};
+ const bool use_cached_picture = true;
+ scoped_refptr<DisplayItemList> list =
+ DisplayItemList::Create(layer_rect, use_cached_picture);
+
+ SkBitmap source_bitmap;
+ source_bitmap.allocN32Pixels(50, 50);
+ SkCanvas source_canvas(source_bitmap);
+ source_canvas.clear(SkColorSetRGB(128, 128, 128));
+
+ // For most SkImageFilters, the |dst| bounds computed by computeFastBounds are
+ // dependent on the provided |src| bounds. This means, for example, that
+ // translating |src| results in a corresponding translation of |dst|. But this
+ // is not the case for all SkImageFilters; for some of them (e.g.
+ // SkBitmapSource), the computation of |dst| in computeFastBounds doesn't
+ // involve |src| at all. Incorrectly assuming such a relationship (e.g. by
+ // translating |dst| after it is computed by computeFastBounds, rather than
+ // translating |src| before it provided to computedFastBounds) can cause
+ // incorrect clipping of filter output. To test for this, we include an
+ // SkBitmapSource filter in |filters|. Here, |src| is |filter_bounds|, defined
+ // below.
+ skia::RefPtr<SkImageFilter> image_filter =
+ skia::AdoptRef(SkBitmapSource::Create(source_bitmap));
+ filters.Append(FilterOperation::CreateReferenceFilter(image_filter));
+ filters.Append(FilterOperation::CreateBrightnessFilter(0.5f));
+ gfx::RectF filter_bounds(10.f, 10.f, 50.f, 50.f);
+ auto* item = list->CreateAndAppendItem<FilterDisplayItem>();
+ item->SetNew(filters, filter_bounds);
+ list->CreateAndAppendItem<EndFilterDisplayItem>();
+ list->ProcessAppendedItems();
+ list->CreateAndCacheSkPicture();
+
+ DrawDisplayList(pixels, layer_rect, list);
+
+ SkBitmap expected_bitmap;
+ unsigned char expected_pixels[4 * 100 * 100] = {0};
+ SkPaint paint;
+ paint.setColor(SkColorSetRGB(64, 64, 64));
+ SkImageInfo info =
+ SkImageInfo::MakeN32Premul(layer_rect.width(), layer_rect.height());
+ expected_bitmap.installPixels(info, expected_pixels, info.minRowBytes());
+ SkCanvas expected_canvas(expected_bitmap);
+ expected_canvas.drawRect(RectFToSkRect(filter_bounds), paint);
+
+ EXPECT_EQ(0, memcmp(pixels, expected_pixels, 4 * 100 * 100));
+}
+
+TEST(DisplayItemListTest, CompactingItems) {
+ gfx::Rect layer_rect(100, 100);
+ SkPictureRecorder recorder;
+ skia::RefPtr<SkCanvas> canvas;
+ skia::RefPtr<SkPicture> picture;
+ SkPaint blue_paint;
+ blue_paint.setColor(SK_ColorBLUE);
+ SkPaint red_paint;
+ red_paint.setColor(SK_ColorRED);
+ unsigned char pixels[4 * 100 * 100] = {0};
+
+ gfx::PointF offset(8.f, 9.f);
+ gfx::RectF recording_rect(offset, layer_rect.size());
+
+ bool use_cached_picture = false;
+ scoped_refptr<DisplayItemList> list_without_caching =
+ DisplayItemList::Create(layer_rect, use_cached_picture);
+
+ canvas = skia::SharePtr(
+ recorder.beginRecording(gfx::RectFToSkRect(recording_rect)));
+ canvas->translate(offset.x(), offset.y());
+ canvas->drawRectCoords(0.f, 0.f, 60.f, 60.f, red_paint);
+ canvas->drawRectCoords(50.f, 50.f, 75.f, 75.f, blue_paint);
+ picture = skia::AdoptRef(recorder.endRecordingAsPicture());
+ auto* item1 = list_without_caching->CreateAndAppendItem<DrawingDisplayItem>();
+ item1->SetNew(picture);
+ list_without_caching->ProcessAppendedItems();
+ DrawDisplayList(pixels, layer_rect, list_without_caching);
+
+ unsigned char expected_pixels[4 * 100 * 100] = {0};
+ use_cached_picture = true;
+ scoped_refptr<DisplayItemList> list_with_caching =
+ DisplayItemList::Create(layer_rect, use_cached_picture);
+ auto* item2 = list_with_caching->CreateAndAppendItem<DrawingDisplayItem>();
+ item2->SetNew(picture);
+ list_with_caching->ProcessAppendedItems();
+ list_with_caching->CreateAndCacheSkPicture();
+ DrawDisplayList(expected_pixels, layer_rect, list_with_caching);
+
+ EXPECT_EQ(0, memcmp(pixels, expected_pixels, 4 * 100 * 100));
+}
+
+TEST(DisplayItemListTest, PictureMemoryUsage) {
+ scoped_refptr<DisplayItemList> list;
+ size_t memory_usage;
+
+ // Make an SkPicture whose size is known.
+ gfx::Rect layer_rect(100, 100);
+ SkPictureRecorder recorder;
+ SkPaint blue_paint;
+ blue_paint.setColor(SK_ColorBLUE);
+ SkCanvas* canvas = recorder.beginRecording(gfx::RectFToSkRect(layer_rect));
+ for (int i = 0; i < 100; i++)
+ canvas->drawPaint(blue_paint);
+ skia::RefPtr<SkPicture> picture =
+ skia::AdoptRef(recorder.endRecordingAsPicture());
+ size_t picture_size = SkPictureUtils::ApproximateBytesUsed(picture.get());
+ ASSERT_GE(picture_size, 100 * sizeof(SkPaint));
+ ASSERT_LE(picture_size, 200 * sizeof(SkPaint));
+
+ // Using a cached picture, we should get about the right size.
+ list = DisplayItemList::Create(layer_rect, true);
+ auto* item = list->CreateAndAppendItem<DrawingDisplayItem>();
+ item->SetNew(picture);
+ list->ProcessAppendedItems();
+ list->CreateAndCacheSkPicture();
+ memory_usage = list->PictureMemoryUsage();
+ EXPECT_GE(memory_usage, picture_size);
+ EXPECT_LE(memory_usage, 2 * picture_size);
+
+ // Using no cached picture, we should still get the right size.
+ list = DisplayItemList::Create(layer_rect, false);
+ item = list->CreateAndAppendItem<DrawingDisplayItem>();
+ item->SetNew(picture);
+ list->ProcessAppendedItems();
+ memory_usage = list->PictureMemoryUsage();
+ EXPECT_GE(memory_usage, picture_size);
+ EXPECT_LE(memory_usage, 2 * picture_size);
+
+ // To avoid double counting, we expect zero size to be computed if both the
+ // picture and items are retained (currently this only happens due to certain
+ // categories being traced).
+ list = new DisplayItemList(layer_rect, true, true);
+ item = list->CreateAndAppendItem<DrawingDisplayItem>();
+ item->SetNew(picture);
+ list->ProcessAppendedItems();
+ list->CreateAndCacheSkPicture();
+ memory_usage = list->PictureMemoryUsage();
+ EXPECT_EQ(static_cast<size_t>(0), memory_usage);
+}
+
+} // namespace cc
diff --git a/cc/playback/display_list_raster_source.cc b/cc/playback/display_list_raster_source.cc
new file mode 100644
index 0000000..0dcd025
--- /dev/null
+++ b/cc/playback/display_list_raster_source.cc
@@ -0,0 +1,235 @@
+// Copyright 2014 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/playback/display_list_raster_source.h"
+
+#include "base/trace_event/trace_event.h"
+#include "cc/base/region.h"
+#include "cc/debug/debug_colors.h"
+#include "cc/playback/display_item_list.h"
+#include "cc/playback/raster_source_helper.h"
+#include "skia/ext/analysis_canvas.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+
+namespace {
+
+#ifdef NDEBUG
+const bool kDefaultClearCanvasSetting = false;
+#else
+const bool kDefaultClearCanvasSetting = true;
+#endif
+
+} // namespace
+
+namespace cc {
+
+scoped_refptr<DisplayListRasterSource>
+DisplayListRasterSource::CreateFromDisplayListRecordingSource(
+ const DisplayListRecordingSource* other,
+ bool can_use_lcd_text) {
+ return make_scoped_refptr(
+ new DisplayListRasterSource(other, can_use_lcd_text));
+}
+
+DisplayListRasterSource::DisplayListRasterSource()
+ : background_color_(SK_ColorTRANSPARENT),
+ requires_clear_(true),
+ can_use_lcd_text_(true),
+ is_solid_color_(false),
+ solid_color_(SK_ColorTRANSPARENT),
+ clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
+ slow_down_raster_scale_factor_for_debug_(0),
+ should_attempt_to_use_distance_field_text_(false) {
+}
+
+DisplayListRasterSource::DisplayListRasterSource(
+ const DisplayListRecordingSource* other,
+ bool can_use_lcd_text)
+ : display_list_(other->display_list_),
+ background_color_(other->background_color_),
+ requires_clear_(other->requires_clear_),
+ can_use_lcd_text_(can_use_lcd_text),
+ is_solid_color_(other->is_solid_color_),
+ solid_color_(other->solid_color_),
+ recorded_viewport_(other->recorded_viewport_),
+ size_(other->size_),
+ clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
+ slow_down_raster_scale_factor_for_debug_(
+ other->slow_down_raster_scale_factor_for_debug_),
+ should_attempt_to_use_distance_field_text_(false) {
+}
+
+DisplayListRasterSource::DisplayListRasterSource(
+ const DisplayListRasterSource* other,
+ bool can_use_lcd_text)
+ : display_list_(other->display_list_),
+ background_color_(other->background_color_),
+ requires_clear_(other->requires_clear_),
+ can_use_lcd_text_(can_use_lcd_text),
+ is_solid_color_(other->is_solid_color_),
+ solid_color_(other->solid_color_),
+ recorded_viewport_(other->recorded_viewport_),
+ size_(other->size_),
+ clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
+ slow_down_raster_scale_factor_for_debug_(
+ other->slow_down_raster_scale_factor_for_debug_),
+ should_attempt_to_use_distance_field_text_(
+ other->should_attempt_to_use_distance_field_text_) {
+}
+
+DisplayListRasterSource::~DisplayListRasterSource() {
+}
+
+void DisplayListRasterSource::PlaybackToSharedCanvas(
+ SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const {
+ RasterCommon(canvas, NULL, canvas_rect, contents_scale);
+}
+
+void DisplayListRasterSource::RasterForAnalysis(skia::AnalysisCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const {
+ RasterCommon(canvas, canvas, canvas_rect, contents_scale);
+}
+
+void DisplayListRasterSource::PlaybackToCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const {
+ RasterSourceHelper::PrepareForPlaybackToCanvas(
+ canvas, canvas_rect, gfx::Rect(size_), contents_scale, background_color_,
+ clear_canvas_with_debug_color_, requires_clear_);
+
+ RasterCommon(canvas, NULL, canvas_rect, contents_scale);
+}
+
+void DisplayListRasterSource::RasterCommon(SkCanvas* canvas,
+ SkDrawPictureCallback* callback,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const {
+ canvas->translate(-canvas_rect.x(), -canvas_rect.y());
+ gfx::Rect content_rect =
+ gfx::ToEnclosingRect(gfx::ScaleRect(gfx::Rect(size_), contents_scale));
+ content_rect.Intersect(canvas_rect);
+
+ canvas->clipRect(gfx::RectToSkRect(content_rect), SkRegion::kIntersect_Op);
+
+ DCHECK(display_list_.get());
+ display_list_->Raster(canvas, callback, contents_scale);
+}
+
+skia::RefPtr<SkPicture> DisplayListRasterSource::GetFlattenedPicture() {
+ TRACE_EVENT0("cc", "DisplayListRasterSource::GetFlattenedPicture");
+
+ gfx::Rect display_list_rect(size_);
+ SkPictureRecorder recorder;
+ SkCanvas* canvas = recorder.beginRecording(display_list_rect.width(),
+ display_list_rect.height());
+ if (!display_list_rect.IsEmpty())
+ PlaybackToCanvas(canvas, display_list_rect, 1.0);
+ skia::RefPtr<SkPicture> picture =
+ skia::AdoptRef(recorder.endRecordingAsPicture());
+
+ return picture;
+}
+
+size_t DisplayListRasterSource::GetPictureMemoryUsage() const {
+ if (!display_list_)
+ return 0;
+ return display_list_->PictureMemoryUsage();
+}
+
+void DisplayListRasterSource::PerformSolidColorAnalysis(
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ RasterSource::SolidColorAnalysis* analysis) const {
+ DCHECK(analysis);
+ TRACE_EVENT0("cc", "DisplayListRasterSource::PerformSolidColorAnalysis");
+
+ gfx::Rect layer_rect =
+ gfx::ScaleToEnclosingRect(content_rect, 1.0f / contents_scale);
+
+ layer_rect.Intersect(gfx::Rect(size_));
+ skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height());
+ RasterForAnalysis(&canvas, layer_rect, 1.0f);
+ analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
+}
+
+void DisplayListRasterSource::GatherPixelRefs(
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ std::vector<SkPixelRef*>* pixel_refs) const {
+ DCHECK_EQ(0u, pixel_refs->size());
+
+ gfx::Rect layer_rect =
+ gfx::ScaleToEnclosingRect(content_rect, 1.0f / contents_scale);
+
+ PixelRefMap::Iterator iterator(layer_rect, display_list_.get());
+ while (iterator) {
+ pixel_refs->push_back(*iterator);
+ ++iterator;
+ }
+}
+
+bool DisplayListRasterSource::CoversRect(const gfx::Rect& content_rect,
+ float contents_scale) const {
+ if (size_.IsEmpty())
+ return false;
+ gfx::Rect layer_rect =
+ gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale);
+ layer_rect.Intersect(gfx::Rect(size_));
+
+ return recorded_viewport_.Contains(layer_rect);
+}
+
+gfx::Size DisplayListRasterSource::GetSize() const {
+ return size_;
+}
+
+bool DisplayListRasterSource::IsSolidColor() const {
+ return is_solid_color_;
+}
+
+SkColor DisplayListRasterSource::GetSolidColor() const {
+ DCHECK(IsSolidColor());
+ return solid_color_;
+}
+
+bool DisplayListRasterSource::HasRecordings() const {
+ return !!display_list_.get();
+}
+
+void DisplayListRasterSource::SetShouldAttemptToUseDistanceFieldText() {
+ should_attempt_to_use_distance_field_text_ = true;
+}
+
+bool DisplayListRasterSource::ShouldAttemptToUseDistanceFieldText() const {
+ return should_attempt_to_use_distance_field_text_;
+}
+
+void DisplayListRasterSource::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ if (display_list_.get())
+ TracedValue::AppendIDRef(display_list_.get(), array);
+}
+
+void DisplayListRasterSource::DidBeginTracing() {
+ if (display_list_.get())
+ display_list_->EmitTraceSnapshot();
+}
+
+bool DisplayListRasterSource::CanUseLCDText() const {
+ return can_use_lcd_text_;
+}
+
+scoped_refptr<RasterSource> DisplayListRasterSource::CreateCloneWithoutLCDText()
+ const {
+ bool can_use_lcd_text = false;
+ return scoped_refptr<RasterSource>(
+ new DisplayListRasterSource(this, can_use_lcd_text));
+}
+
+} // namespace cc
diff --git a/cc/playback/display_list_raster_source.h b/cc/playback/display_list_raster_source.h
new file mode 100644
index 0000000..e9010e5
--- /dev/null
+++ b/cc/playback/display_list_raster_source.h
@@ -0,0 +1,98 @@
+// Copyright 2014 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_PLAYBACK_DISPLAY_LIST_RASTER_SOURCE_H_
+#define CC_PLAYBACK_DISPLAY_LIST_RASTER_SOURCE_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/debug/rendering_stats_instrumentation.h"
+#include "cc/playback/display_list_recording_source.h"
+#include "cc/playback/raster_source.h"
+#include "skia/ext/analysis_canvas.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace cc {
+class DisplayItemList;
+
+class CC_EXPORT DisplayListRasterSource : public RasterSource {
+ public:
+ static scoped_refptr<DisplayListRasterSource>
+ CreateFromDisplayListRecordingSource(const DisplayListRecordingSource* other,
+ bool can_use_lcd_text);
+
+ // RasterSource overrides.
+ void PlaybackToCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const override;
+ void PlaybackToSharedCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const override;
+ void PerformSolidColorAnalysis(
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ RasterSource::SolidColorAnalysis* analysis) const override;
+ bool IsSolidColor() const override;
+ SkColor GetSolidColor() const override;
+ gfx::Size GetSize() const override;
+ void GatherPixelRefs(const gfx::Rect& content_rect,
+ float contents_scale,
+ std::vector<SkPixelRef*>* pixel_refs) const override;
+ bool CoversRect(const gfx::Rect& content_rect,
+ float contents_scale) const override;
+ bool HasRecordings() const override;
+ void SetShouldAttemptToUseDistanceFieldText() override;
+ bool ShouldAttemptToUseDistanceFieldText() const override;
+ void DidBeginTracing() override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+ skia::RefPtr<SkPicture> GetFlattenedPicture() override;
+ size_t GetPictureMemoryUsage() const override;
+ bool CanUseLCDText() const override;
+ scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const override;
+
+ protected:
+ DisplayListRasterSource();
+ DisplayListRasterSource(const DisplayListRecordingSource* other,
+ bool can_use_lcd_text);
+ DisplayListRasterSource(const DisplayListRasterSource* other,
+ bool can_use_lcd_text);
+ ~DisplayListRasterSource() override;
+
+ // These members are const as this raster source may be in use on another
+ // thread and so should not be touched after construction.
+ const scoped_refptr<DisplayItemList> display_list_;
+ const SkColor background_color_;
+ const bool requires_clear_;
+ const bool can_use_lcd_text_;
+ const bool is_solid_color_;
+ const SkColor solid_color_;
+ const gfx::Rect recorded_viewport_;
+ const gfx::Size size_;
+ const bool clear_canvas_with_debug_color_;
+ const int slow_down_raster_scale_factor_for_debug_;
+ // TODO(enne/vmiura): this has a read/write race between raster and compositor
+ // threads with multi-threaded Ganesh. Make this const or remove it.
+ bool should_attempt_to_use_distance_field_text_;
+
+ private:
+ // Called when analyzing a tile. We can use AnalysisCanvas as
+ // SkDrawPictureCallback, which allows us to early out from analysis.
+ void RasterForAnalysis(skia::AnalysisCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const;
+
+ void RasterCommon(SkCanvas* canvas,
+ SkDrawPictureCallback* callback,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayListRasterSource);
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_DISPLAY_LIST_RASTER_SOURCE_H_
diff --git a/cc/playback/display_list_recording_source.cc b/cc/playback/display_list_recording_source.cc
new file mode 100644
index 0000000..9d6e31f
--- /dev/null
+++ b/cc/playback/display_list_recording_source.cc
@@ -0,0 +1,192 @@
+// Copyright 2014 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/playback/display_list_recording_source.h"
+
+#include <algorithm>
+
+#include "cc/base/region.h"
+#include "cc/layers/content_layer_client.h"
+#include "cc/playback/display_item_list.h"
+#include "cc/playback/display_list_raster_source.h"
+#include "skia/ext/analysis_canvas.h"
+
+namespace {
+
+// Layout pixel buffer around the visible layer rect to record. Any base
+// picture that intersects the visible layer rect expanded by this distance
+// will be recorded.
+const int kPixelDistanceToRecord = 8000;
+// We don't perform solid color analysis on images that have more than 10 skia
+// operations.
+const int kOpCountThatIsOkToAnalyze = 10;
+
+} // namespace
+
+namespace cc {
+
+DisplayListRecordingSource::DisplayListRecordingSource(
+ const gfx::Size& grid_cell_size,
+ bool use_cached_picture)
+ : use_cached_picture_(use_cached_picture),
+ slow_down_raster_scale_factor_for_debug_(0),
+ gather_pixel_refs_(false),
+ requires_clear_(false),
+ is_solid_color_(false),
+ solid_color_(SK_ColorTRANSPARENT),
+ background_color_(SK_ColorTRANSPARENT),
+ pixel_record_distance_(kPixelDistanceToRecord),
+ grid_cell_size_(grid_cell_size),
+ is_suitable_for_gpu_rasterization_(true) {
+}
+
+DisplayListRecordingSource::~DisplayListRecordingSource() {
+}
+
+bool DisplayListRecordingSource::UpdateAndExpandInvalidation(
+ ContentLayerClient* painter,
+ Region* invalidation,
+ const gfx::Size& layer_size,
+ const gfx::Rect& visible_layer_rect,
+ int frame_number,
+ RecordingMode recording_mode) {
+ bool updated = false;
+
+ if (size_ != layer_size) {
+ size_ = layer_size;
+ updated = true;
+ }
+
+ gfx::Rect old_recorded_viewport = recorded_viewport_;
+ recorded_viewport_ = visible_layer_rect;
+ recorded_viewport_.Inset(-pixel_record_distance_, -pixel_record_distance_);
+ recorded_viewport_.Intersect(gfx::Rect(GetSize()));
+
+ if (recorded_viewport_ != old_recorded_viewport) {
+ // Invalidate newly-exposed and no-longer-exposed areas.
+ Region newly_exposed_region(recorded_viewport_);
+ newly_exposed_region.Subtract(old_recorded_viewport);
+ invalidation->Union(newly_exposed_region);
+
+ Region no_longer_exposed_region(old_recorded_viewport);
+ no_longer_exposed_region.Subtract(recorded_viewport_);
+ invalidation->Union(no_longer_exposed_region);
+
+ updated = true;
+ }
+
+ if (!updated && !invalidation->Intersects(recorded_viewport_))
+ return false;
+
+ ContentLayerClient::PaintingControlSetting painting_control =
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL;
+
+ switch (recording_mode) {
+ case RECORD_NORMALLY:
+ // Already setup for normal recording.
+ break;
+ case RECORD_WITH_PAINTING_DISABLED:
+ painting_control = ContentLayerClient::DISPLAY_LIST_PAINTING_DISABLED;
+ break;
+ case RECORD_WITH_CACHING_DISABLED:
+ painting_control = ContentLayerClient::DISPLAY_LIST_CACHING_DISABLED;
+ break;
+ case RECORD_WITH_CONSTRUCTION_DISABLED:
+ painting_control = ContentLayerClient::DISPLAY_LIST_CONSTRUCTION_DISABLED;
+ break;
+ default:
+ // case RecordingSource::RECORD_WITH_SK_NULL_CANVAS should not be reached
+ NOTREACHED();
+ }
+
+ int repeat_count = 1;
+ if (slow_down_raster_scale_factor_for_debug_ > 1) {
+ repeat_count = slow_down_raster_scale_factor_for_debug_;
+ painting_control = ContentLayerClient::DISPLAY_LIST_CACHING_DISABLED;
+ }
+ for (int i = 0; i < repeat_count; ++i) {
+ display_list_ =
+ DisplayItemList::Create(recorded_viewport_, use_cached_picture_);
+ painter->PaintContentsToDisplayList(display_list_.get(), recorded_viewport_,
+ painting_control);
+ }
+ display_list_->ProcessAppendedItems();
+ if (use_cached_picture_)
+ display_list_->CreateAndCacheSkPicture();
+
+ is_suitable_for_gpu_rasterization_ =
+ display_list_->IsSuitableForGpuRasterization();
+ DetermineIfSolidColor();
+ display_list_->EmitTraceSnapshot();
+ if (gather_pixel_refs_)
+ display_list_->GatherPixelRefs(grid_cell_size_);
+
+ return true;
+}
+
+gfx::Size DisplayListRecordingSource::GetSize() const {
+ return size_;
+}
+
+void DisplayListRecordingSource::SetEmptyBounds() {
+ size_ = gfx::Size();
+ Clear();
+}
+
+void DisplayListRecordingSource::SetSlowdownRasterScaleFactor(int factor) {
+ slow_down_raster_scale_factor_for_debug_ = factor;
+}
+
+void DisplayListRecordingSource::SetGatherPixelRefs(bool gather_pixel_refs) {
+ gather_pixel_refs_ = gather_pixel_refs;
+}
+
+void DisplayListRecordingSource::SetBackgroundColor(SkColor background_color) {
+ background_color_ = background_color;
+}
+
+void DisplayListRecordingSource::SetRequiresClear(bool requires_clear) {
+ requires_clear_ = requires_clear;
+}
+
+void DisplayListRecordingSource::SetUnsuitableForGpuRasterizationForTesting() {
+ is_suitable_for_gpu_rasterization_ = false;
+}
+
+bool DisplayListRecordingSource::IsSuitableForGpuRasterization() const {
+ return is_suitable_for_gpu_rasterization_;
+}
+
+scoped_refptr<RasterSource> DisplayListRecordingSource::CreateRasterSource(
+ bool can_use_lcd_text) const {
+ return scoped_refptr<RasterSource>(
+ DisplayListRasterSource::CreateFromDisplayListRecordingSource(
+ this, can_use_lcd_text));
+}
+
+gfx::Size DisplayListRecordingSource::GetTileGridSizeForTesting() const {
+ return gfx::Size();
+}
+
+void DisplayListRecordingSource::DetermineIfSolidColor() {
+ DCHECK(display_list_.get());
+ is_solid_color_ = false;
+ solid_color_ = SK_ColorTRANSPARENT;
+
+ if (display_list_->ApproximateOpCount() > kOpCountThatIsOkToAnalyze)
+ return;
+
+ gfx::Size layer_size = GetSize();
+ skia::AnalysisCanvas canvas(layer_size.width(), layer_size.height());
+ display_list_->Raster(&canvas, nullptr, 1.f);
+ is_solid_color_ = canvas.GetColorIfSolid(&solid_color_);
+}
+
+void DisplayListRecordingSource::Clear() {
+ recorded_viewport_ = gfx::Rect();
+ display_list_ = NULL;
+ is_solid_color_ = false;
+}
+
+} // namespace cc
diff --git a/cc/playback/display_list_recording_source.h b/cc/playback/display_list_recording_source.h
new file mode 100644
index 0000000..8d652fa
--- /dev/null
+++ b/cc/playback/display_list_recording_source.h
@@ -0,0 +1,70 @@
+// Copyright 2014 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_PLAYBACK_DISPLAY_LIST_RECORDING_SOURCE_H_
+#define CC_PLAYBACK_DISPLAY_LIST_RECORDING_SOURCE_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/playback/recording_source.h"
+
+namespace cc {
+class DisplayItemList;
+class DisplayListRasterSource;
+
+class CC_EXPORT DisplayListRecordingSource : public RecordingSource {
+ public:
+ DisplayListRecordingSource(const gfx::Size& grid_cell_size,
+ bool use_cached_picture);
+ ~DisplayListRecordingSource() override;
+
+ // RecordingSource overrides.
+ bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
+ Region* invalidation,
+ const gfx::Size& layer_size,
+ const gfx::Rect& visible_layer_rect,
+ int frame_number,
+ RecordingMode recording_mode) override;
+ scoped_refptr<RasterSource> CreateRasterSource(
+ bool can_use_lcd_text) const override;
+ gfx::Size GetSize() const final;
+ void SetEmptyBounds() override;
+ void SetSlowdownRasterScaleFactor(int factor) override;
+ void SetGatherPixelRefs(bool gather_pixel_refs) override;
+ void SetBackgroundColor(SkColor background_color) override;
+ void SetRequiresClear(bool requires_clear) override;
+ bool IsSuitableForGpuRasterization() const override;
+ void SetUnsuitableForGpuRasterizationForTesting() override;
+ gfx::Size GetTileGridSizeForTesting() const override;
+
+ protected:
+ void Clear();
+
+ const bool use_cached_picture_;
+
+ gfx::Rect recorded_viewport_;
+ gfx::Size size_;
+ int slow_down_raster_scale_factor_for_debug_;
+ bool gather_pixel_refs_;
+ bool requires_clear_;
+ bool is_solid_color_;
+ SkColor solid_color_;
+ SkColor background_color_;
+ int pixel_record_distance_;
+ gfx::Size grid_cell_size_;
+
+ scoped_refptr<DisplayItemList> display_list_;
+
+ private:
+ friend class DisplayListRasterSource;
+
+ void DetermineIfSolidColor();
+
+ bool is_suitable_for_gpu_rasterization_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisplayListRecordingSource);
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_DISPLAY_LIST_RECORDING_SOURCE_H_
diff --git a/cc/playback/display_list_recording_source_unittest.cc b/cc/playback/display_list_recording_source_unittest.cc
new file mode 100644
index 0000000..0d7ec7c
--- /dev/null
+++ b/cc/playback/display_list_recording_source_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright 2015 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 <vector>
+
+#include "cc/playback/display_list_raster_source.h"
+#include "cc/test/fake_display_list_recording_source.h"
+#include "cc/test/skia_common.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+TEST(DisplayListRecordingSourceTest, DiscardablePixelRefsWithTransform) {
+ gfx::Size grid_cell_size(128, 128);
+ gfx::Rect recorded_viewport(0, 0, 256, 256);
+
+ scoped_ptr<FakeDisplayListRecordingSource> recording_source =
+ FakeDisplayListRecordingSource::CreateRecordingSource(recorded_viewport);
+ recording_source->SetGridCellSize(grid_cell_size);
+ SkBitmap discardable_bitmap[2][2];
+ gfx::Transform identity_transform;
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[0][0]);
+ // Translate transform is equivalent to moving using point.
+ gfx::Transform translate_transform;
+ translate_transform.Translate(0, 130);
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[1][0]);
+ // This moves the bitmap to center of viewport and rotate, this would make
+ // this bitmap in all four tile grids.
+ gfx::Transform rotate_transform;
+ rotate_transform.Translate(112, 112);
+ rotate_transform.Rotate(45);
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[1][1]);
+
+ recording_source->add_draw_bitmap_with_transform(discardable_bitmap[0][0],
+ identity_transform);
+ recording_source->add_draw_bitmap_with_transform(discardable_bitmap[1][0],
+ translate_transform);
+ recording_source->add_draw_bitmap_with_transform(discardable_bitmap[1][1],
+ rotate_transform);
+ recording_source->SetGatherPixelRefs(true);
+ recording_source->Rerecord();
+
+ bool can_use_lcd_text = true;
+ scoped_refptr<DisplayListRasterSource> raster_source =
+ DisplayListRasterSource::CreateFromDisplayListRecordingSource(
+ recording_source.get(), can_use_lcd_text);
+
+ // Tile sized iterators. These should find only one pixel ref.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 1.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(2u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 2.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(2u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 64, 64), 0.5, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(2u, pixel_refs.size());
+ }
+
+ // Shifted tile sized iterators. These should find only one pixel ref.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(140, 140, 128, 128), 1.0,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(280, 280, 256, 256), 2.0,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(70, 70, 64, 64), 0.5, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+
+ // The rotated bitmap would still be in the top right tile.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(140, 0, 128, 128), 1.0,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+
+ // Layer sized iterators. These should find all 6 pixel refs, including 1
+ // pixel ref bitmap[0][0], 1 pixel ref for bitmap[1][0], and 4 pixel refs for
+ // bitmap[1][1].
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 1.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ // Top left tile with bitmap[0][0] and bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][1].pixelRef());
+ // Top right tile with bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ // Bottom left tile with bitmap[1][0] and bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[3] == discardable_bitmap[1][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[4] == discardable_bitmap[1][1].pixelRef());
+ // Bottom right tile with bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[5] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(6u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 512, 512), 2.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ // Top left tile with bitmap[0][0] and bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][1].pixelRef());
+ // Top right tile with bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ // Bottom left tile with bitmap[1][0] and bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[3] == discardable_bitmap[1][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[4] == discardable_bitmap[1][1].pixelRef());
+ // Bottom right tile with bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[5] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(6u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 0.5, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ // Top left tile with bitmap[0][0] and bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][1].pixelRef());
+ // Top right tile with bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ // Bottom left tile with bitmap[1][0] and bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[3] == discardable_bitmap[1][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[4] == discardable_bitmap[1][1].pixelRef());
+ // Bottom right tile with bitmap[1][1].
+ EXPECT_TRUE(pixel_refs[5] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(6u, pixel_refs.size());
+ }
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/playback/drawing_display_item.cc b/cc/playback/drawing_display_item.cc
new file mode 100644
index 0000000..51cd42e
--- /dev/null
+++ b/cc/playback/drawing_display_item.cc
@@ -0,0 +1,62 @@
+// Copyright 2014 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/playback/drawing_display_item.h"
+
+#include <string>
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "cc/debug/picture_debug_util.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDrawPictureCallback.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/utils/SkPictureUtils.h"
+
+namespace cc {
+
+DrawingDisplayItem::DrawingDisplayItem() {
+}
+
+DrawingDisplayItem::~DrawingDisplayItem() {
+}
+
+void DrawingDisplayItem::SetNew(skia::RefPtr<SkPicture> picture) {
+ picture_ = picture.Pass();
+ DisplayItem::SetNew(picture_->suitableForGpuRasterization(NULL),
+ picture_->approximateOpCount(),
+ SkPictureUtils::ApproximateBytesUsed(picture_.get()));
+}
+
+void DrawingDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ // SkPicture always does a wrapping save/restore on the canvas, so it is not
+ // necessary here.
+ if (callback)
+ picture_->playback(canvas, callback);
+ else
+ canvas->drawPicture(picture_.get());
+}
+
+void DrawingDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->BeginDictionary();
+ array->SetString("name", "DrawingDisplayItem");
+ array->SetString(
+ "cullRect",
+ base::StringPrintf("[%f,%f,%f,%f]", picture_->cullRect().x(),
+ picture_->cullRect().y(), picture_->cullRect().width(),
+ picture_->cullRect().height()));
+ std::string b64_picture;
+ PictureDebugUtil::SerializeAsBase64(picture_.get(), &b64_picture);
+ array->SetString("skp64", b64_picture);
+ array->EndDictionary();
+}
+
+void DrawingDisplayItem::CloneTo(DrawingDisplayItem* item) const {
+ item->SetNew(picture_);
+}
+
+} // namespace cc
diff --git a/cc/playback/drawing_display_item.h b/cc/playback/drawing_display_item.h
new file mode 100644
index 0000000..81ddbce
--- /dev/null
+++ b/cc/playback/drawing_display_item.h
@@ -0,0 +1,38 @@
+// Copyright 2014 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_PLAYBACK_DRAWING_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_DRAWING_DISPLAY_ITEM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/playback/display_item.h"
+#include "skia/ext/refptr.h"
+#include "ui/gfx/geometry/point_f.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+class SkPicture;
+
+namespace cc {
+
+class CC_EXPORT DrawingDisplayItem : public DisplayItem {
+ public:
+ DrawingDisplayItem();
+ ~DrawingDisplayItem() override;
+
+ void SetNew(skia::RefPtr<SkPicture> picture);
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+
+ void CloneTo(DrawingDisplayItem* item) const;
+
+ private:
+ skia::RefPtr<SkPicture> picture_;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_DRAWING_DISPLAY_ITEM_H_
diff --git a/cc/playback/filter_display_item.cc b/cc/playback/filter_display_item.cc
new file mode 100644
index 0000000..8e849cf
--- /dev/null
+++ b/cc/playback/filter_display_item.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 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/playback/filter_display_item.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "cc/output/render_surface_filters.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkXfermode.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+FilterDisplayItem::FilterDisplayItem() {
+}
+
+FilterDisplayItem::~FilterDisplayItem() {
+}
+
+void FilterDisplayItem::SetNew(const FilterOperations& filters,
+ const gfx::RectF& bounds) {
+ filters_ = filters;
+ bounds_ = bounds;
+
+ size_t memory_usage =
+ sizeof(skia::RefPtr<SkImageFilter>) + sizeof(gfx::RectF);
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 1 /* op_count */,
+ memory_usage);
+}
+
+void FilterDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->save();
+ canvas->translate(bounds_.x(), bounds_.y());
+
+ skia::RefPtr<SkImageFilter> image_filter =
+ RenderSurfaceFilters::BuildImageFilter(
+ filters_, gfx::SizeF(bounds_.width(), bounds_.height()));
+ SkRect boundaries;
+ image_filter->computeFastBounds(
+ SkRect::MakeWH(bounds_.width(), bounds_.height()), &boundaries);
+
+ SkPaint paint;
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ paint.setImageFilter(image_filter.get());
+ canvas->saveLayer(&boundaries, &paint);
+
+ canvas->translate(-bounds_.x(), -bounds_.y());
+}
+
+void FilterDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString(base::StringPrintf("FilterDisplayItem bounds: [%s]",
+ bounds_.ToString().c_str()));
+}
+
+EndFilterDisplayItem::EndFilterDisplayItem() {
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 0 /* op_count */,
+ 0 /* memory_usage */);
+}
+
+EndFilterDisplayItem::~EndFilterDisplayItem() {
+}
+
+void EndFilterDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->restore();
+ canvas->restore();
+}
+
+void EndFilterDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString("EndFilterDisplayItem");
+}
+
+} // namespace cc
diff --git a/cc/playback/filter_display_item.h b/cc/playback/filter_display_item.h
new file mode 100644
index 0000000..a999b38
--- /dev/null
+++ b/cc/playback/filter_display_item.h
@@ -0,0 +1,49 @@
+// Copyright 2014 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_PLAYBACK_FILTER_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_FILTER_DISPLAY_ITEM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/filter_operations.h"
+#include "cc/playback/display_item.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+
+namespace cc {
+
+class CC_EXPORT FilterDisplayItem : public DisplayItem {
+ public:
+ FilterDisplayItem();
+ ~FilterDisplayItem() override;
+
+ void SetNew(const FilterOperations& filters, const gfx::RectF& bounds);
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+
+ private:
+ FilterOperations filters_;
+ gfx::RectF bounds_;
+};
+
+class CC_EXPORT EndFilterDisplayItem : public DisplayItem {
+ public:
+ EndFilterDisplayItem();
+ ~EndFilterDisplayItem() override;
+
+ static scoped_ptr<EndFilterDisplayItem> Create() {
+ return make_scoped_ptr(new EndFilterDisplayItem());
+ }
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_FILTER_DISPLAY_ITEM_H_
diff --git a/cc/playback/float_clip_display_item.cc b/cc/playback/float_clip_display_item.cc
new file mode 100644
index 0000000..0f368a6
--- /dev/null
+++ b/cc/playback/float_clip_display_item.cc
@@ -0,0 +1,58 @@
+// Copyright 2015 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/playback/float_clip_display_item.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+FloatClipDisplayItem::FloatClipDisplayItem() {
+}
+
+FloatClipDisplayItem::~FloatClipDisplayItem() {
+}
+
+void FloatClipDisplayItem::SetNew(const gfx::RectF& clip_rect) {
+ clip_rect_ = clip_rect;
+
+ size_t memory_usage = sizeof(gfx::RectF);
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 1 /* op_count */,
+ memory_usage);
+}
+
+void FloatClipDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->save();
+ canvas->clipRect(gfx::RectFToSkRect(clip_rect_));
+}
+
+void FloatClipDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString(base::StringPrintf("FloatClipDisplayItem rect: [%s]",
+ clip_rect_.ToString().c_str()));
+}
+
+EndFloatClipDisplayItem::EndFloatClipDisplayItem() {
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 0 /* op_count */,
+ 0 /* memory_usage */);
+}
+
+EndFloatClipDisplayItem::~EndFloatClipDisplayItem() {
+}
+
+void EndFloatClipDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->restore();
+}
+
+void EndFloatClipDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString("EndFloatClipDisplayItem");
+}
+
+} // namespace cc
diff --git a/cc/playback/float_clip_display_item.h b/cc/playback/float_clip_display_item.h
new file mode 100644
index 0000000..2812189
--- /dev/null
+++ b/cc/playback/float_clip_display_item.h
@@ -0,0 +1,49 @@
+// Copyright 2015 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_PLAYBACK_FLOAT_CLIP_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_FLOAT_CLIP_DISPLAY_ITEM_H_
+
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/playback/display_item.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+
+namespace cc {
+
+class CC_EXPORT FloatClipDisplayItem : public DisplayItem {
+ public:
+ FloatClipDisplayItem();
+ ~FloatClipDisplayItem() override;
+
+ void SetNew(const gfx::RectF& clip_rect);
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+
+ private:
+ gfx::RectF clip_rect_;
+};
+
+class CC_EXPORT EndFloatClipDisplayItem : public DisplayItem {
+ public:
+ EndFloatClipDisplayItem();
+ ~EndFloatClipDisplayItem() override;
+
+ static scoped_ptr<EndFloatClipDisplayItem> Create() {
+ return make_scoped_ptr(new EndFloatClipDisplayItem());
+ }
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_FLOAT_CLIP_DISPLAY_ITEM_H_
diff --git a/cc/playback/largest_display_item.cc b/cc/playback/largest_display_item.cc
new file mode 100644
index 0000000..0ecd3d0
--- /dev/null
+++ b/cc/playback/largest_display_item.cc
@@ -0,0 +1,71 @@
+// Copyright 2015 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/playback/largest_display_item.h"
+
+#include <algorithm>
+
+#include "cc/playback/clip_display_item.h"
+#include "cc/playback/clip_path_display_item.h"
+#include "cc/playback/compositing_display_item.h"
+#include "cc/playback/drawing_display_item.h"
+#include "cc/playback/filter_display_item.h"
+#include "cc/playback/float_clip_display_item.h"
+#include "cc/playback/transform_display_item.h"
+
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace {
+const size_t kLargestDisplayItemSize = sizeof(cc::TransformDisplayItem);
+} // namespace
+
+namespace cc {
+
+size_t LargestDisplayItemSize() {
+ // Use compile assert to make sure largest is actually larger than all other
+ // type of display_items.
+ static_assert(sizeof(ClipDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. ClipDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(EndClipDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. EndClipDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(ClipPathDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. ClipPathDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(EndClipPathDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. EndClipPathDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(CompositingDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. CompositingDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(EndCompositingDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. EndCompositingDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(DrawingDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. DrawingDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(FilterDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. FilterDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(EndFilterDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. EndFilterDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(FloatClipDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. FloatClipDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(EndFloatClipDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. EndFloatClipDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(TransformDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. TransformDisplayItem"
+ " is currently largest.");
+ static_assert(sizeof(EndTransformDisplayItem) <= kLargestDisplayItemSize,
+ "Largest Draw Quad size needs update. EndTransformDisplayItem"
+ " is currently largest.");
+
+ return kLargestDisplayItemSize;
+}
+
+} // namespace cc
diff --git a/cc/playback/largest_display_item.h b/cc/playback/largest_display_item.h
new file mode 100644
index 0000000..cce5671
--- /dev/null
+++ b/cc/playback/largest_display_item.h
@@ -0,0 +1,17 @@
+// Copyright 2015 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_PLAYBACK_LARGEST_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_LARGEST_DISPLAY_ITEM_H_
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+CC_EXPORT size_t LargestDisplayItemSize();
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_LARGEST_DISPLAY_ITEM_H_
diff --git a/cc/playback/picture.cc b/cc/playback/picture.cc
new file mode 100644
index 0000000..4537bf7
--- /dev/null
+++ b/cc/playback/picture.cc
@@ -0,0 +1,340 @@
+// Copyright 2012 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/playback/picture.h"
+
+#include <set>
+#include <string>
+
+#include "base/base64.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "base/values.h"
+#include "cc/base/math_util.h"
+#include "cc/base/util.h"
+#include "cc/debug/picture_debug_util.h"
+#include "cc/debug/traced_picture.h"
+#include "cc/debug/traced_value.h"
+#include "cc/layers/content_layer_client.h"
+#include "skia/ext/pixel_ref_utils.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDrawPictureCallback.h"
+#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/utils/SkNullCanvas.h"
+#include "third_party/skia/include/utils/SkPictureUtils.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+namespace {
+
+bool DecodeBitmap(const void* buffer, size_t size, SkBitmap* bm) {
+ const unsigned char* data = static_cast<const unsigned char *>(buffer);
+
+ // Try PNG first.
+ if (gfx::PNGCodec::Decode(data, size, bm))
+ return true;
+
+ // Try JPEG.
+ scoped_ptr<SkBitmap> decoded_jpeg(gfx::JPEGCodec::Decode(data, size));
+ if (decoded_jpeg) {
+ *bm = *decoded_jpeg;
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+scoped_refptr<Picture> Picture::Create(
+ const gfx::Rect& layer_rect,
+ ContentLayerClient* client,
+ const gfx::Size& tile_grid_size,
+ bool gather_pixel_refs,
+ RecordingSource::RecordingMode recording_mode) {
+ scoped_refptr<Picture> picture =
+ make_scoped_refptr(new Picture(layer_rect, tile_grid_size));
+
+ picture->Record(client, recording_mode);
+ if (gather_pixel_refs)
+ picture->GatherPixelRefs();
+
+ return picture;
+}
+
+Picture::Picture(const gfx::Rect& layer_rect, const gfx::Size& tile_grid_size)
+ : layer_rect_(layer_rect), pixel_refs_(tile_grid_size) {
+ // Instead of recording a trace event for object creation here, we wait for
+ // the picture to be recorded in Picture::Record.
+}
+
+scoped_refptr<Picture> Picture::CreateFromSkpValue(const base::Value* value) {
+ // Decode the picture from base64.
+ std::string encoded;
+ if (!value->GetAsString(&encoded))
+ return NULL;
+
+ std::string decoded;
+ base::Base64Decode(encoded, &decoded);
+ SkMemoryStream stream(decoded.data(), decoded.size());
+
+ // Read the picture. This creates an empty picture on failure.
+ SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
+ if (skpicture == NULL)
+ return NULL;
+
+ gfx::Rect layer_rect(gfx::SkIRectToRect(skpicture->cullRect().roundOut()));
+ return make_scoped_refptr(new Picture(skpicture, layer_rect));
+}
+
+scoped_refptr<Picture> Picture::CreateFromValue(const base::Value* raw_value) {
+ const base::DictionaryValue* value = NULL;
+ if (!raw_value->GetAsDictionary(&value))
+ return NULL;
+
+ // Decode the picture from base64.
+ std::string encoded;
+ if (!value->GetString("skp64", &encoded))
+ return NULL;
+
+ std::string decoded;
+ base::Base64Decode(encoded, &decoded);
+ SkMemoryStream stream(decoded.data(), decoded.size());
+
+ const base::Value* layer_rect_value = NULL;
+ if (!value->Get("params.layer_rect", &layer_rect_value))
+ return NULL;
+
+ gfx::Rect layer_rect;
+ if (!MathUtil::FromValue(layer_rect_value, &layer_rect))
+ return NULL;
+
+ // Read the picture. This creates an empty picture on failure.
+ SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
+ if (skpicture == NULL)
+ return NULL;
+
+ return make_scoped_refptr(new Picture(skpicture, layer_rect));
+}
+
+Picture::Picture(SkPicture* picture, const gfx::Rect& layer_rect)
+ : layer_rect_(layer_rect),
+ picture_(skia::AdoptRef(picture)),
+ pixel_refs_(layer_rect.size()) {
+}
+
+Picture::Picture(const skia::RefPtr<SkPicture>& picture,
+ const gfx::Rect& layer_rect,
+ const PixelRefMap& pixel_refs)
+ : layer_rect_(layer_rect), picture_(picture), pixel_refs_(pixel_refs) {
+}
+
+Picture::~Picture() {
+ TRACE_EVENT_OBJECT_DELETED_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.picture"), "cc::Picture", this);
+}
+
+bool Picture::IsSuitableForGpuRasterization(const char** reason) const {
+ DCHECK(picture_);
+
+ // TODO(hendrikw): SkPicture::suitableForGpuRasterization takes a GrContext.
+ // Currently the GrContext isn't used, and should probably be removed from
+ // skia.
+ return picture_->suitableForGpuRasterization(nullptr, reason);
+}
+
+int Picture::ApproximateOpCount() const {
+ DCHECK(picture_);
+ return picture_->approximateOpCount();
+}
+
+size_t Picture::ApproximateMemoryUsage() const {
+ DCHECK(picture_);
+ return SkPictureUtils::ApproximateBytesUsed(picture_.get());
+}
+
+bool Picture::HasText() const {
+ DCHECK(picture_);
+ return picture_->hasText();
+}
+
+void Picture::Record(ContentLayerClient* painter,
+ RecordingSource::RecordingMode recording_mode) {
+ TRACE_EVENT2("cc",
+ "Picture::Record",
+ "data",
+ AsTraceableRecordData(),
+ "recording_mode",
+ recording_mode);
+
+ DCHECK(!picture_);
+
+ SkRTreeFactory factory;
+ SkPictureRecorder recorder;
+
+ skia::RefPtr<SkCanvas> canvas;
+ canvas = skia::SharePtr(recorder.beginRecording(
+ layer_rect_.width(), layer_rect_.height(), &factory,
+ SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag));
+
+ ContentLayerClient::PaintingControlSetting painting_control =
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL;
+
+ switch (recording_mode) {
+ case RecordingSource::RECORD_NORMALLY:
+ // Already setup for normal recording.
+ break;
+ case RecordingSource::RECORD_WITH_SK_NULL_CANVAS:
+ canvas = skia::AdoptRef(SkCreateNullCanvas());
+ break;
+ case RecordingSource::RECORD_WITH_PAINTING_DISABLED:
+ // We pass a disable flag through the paint calls when perfromance
+ // testing (the only time this case should ever arise) when we want to
+ // prevent the Blink GraphicsContext object from consuming any compute
+ // time.
+ canvas = skia::AdoptRef(SkCreateNullCanvas());
+ painting_control = ContentLayerClient::DISPLAY_LIST_PAINTING_DISABLED;
+ break;
+ case RecordingSource::RECORD_WITH_CACHING_DISABLED:
+ // This mode should give the same results as RECORD_NORMALLY.
+ painting_control = ContentLayerClient::DISPLAY_LIST_CACHING_DISABLED;
+ break;
+ default:
+ // case RecordingSource::RECORD_WITH_CONSTRUCTION_DISABLED should
+ // not be reached
+ NOTREACHED();
+ }
+
+ canvas->save();
+ canvas->translate(SkFloatToScalar(-layer_rect_.x()),
+ SkFloatToScalar(-layer_rect_.y()));
+
+ canvas->clipRect(gfx::RectToSkRect(layer_rect_));
+
+ painter->PaintContents(canvas.get(), layer_rect_, painting_control);
+
+ canvas->restore();
+ picture_ = skia::AdoptRef(recorder.endRecordingAsPicture());
+ DCHECK(picture_);
+
+ EmitTraceSnapshot();
+}
+
+void Picture::GatherPixelRefs() {
+ TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
+ "width", layer_rect_.width(),
+ "height", layer_rect_.height());
+
+ DCHECK(picture_);
+ DCHECK(pixel_refs_.empty());
+ if (!WillPlayBackBitmaps())
+ return;
+
+ pixel_refs_.GatherPixelRefsFromPicture(picture_.get());
+}
+
+int Picture::Raster(SkCanvas* canvas,
+ SkPicture::AbortCallback* callback,
+ const Region& negated_content_region,
+ float contents_scale) const {
+ TRACE_EVENT_BEGIN1(
+ "cc",
+ "Picture::Raster",
+ "data",
+ AsTraceableRasterData(contents_scale));
+
+ DCHECK(picture_);
+
+ canvas->save();
+
+ for (Region::Iterator it(negated_content_region); it.has_rect(); it.next())
+ canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
+
+ canvas->scale(contents_scale, contents_scale);
+ canvas->translate(layer_rect_.x(), layer_rect_.y());
+ if (callback) {
+ // If we have a callback, we need to call |draw()|, |drawPicture()| doesn't
+ // take a callback. This is used by |AnalysisCanvas| to early out.
+ picture_->playback(canvas, callback);
+ } else {
+ // Prefer to call |drawPicture()| on the canvas since it could place the
+ // entire picture on the canvas instead of parsing the skia operations.
+ canvas->drawPicture(picture_.get());
+ }
+ SkIRect bounds;
+ canvas->getClipDeviceBounds(&bounds);
+ canvas->restore();
+ TRACE_EVENT_END1(
+ "cc", "Picture::Raster",
+ "num_pixels_rasterized", bounds.width() * bounds.height());
+ return bounds.width() * bounds.height();
+}
+
+void Picture::Replay(SkCanvas* canvas, SkPicture::AbortCallback* callback) {
+ TRACE_EVENT_BEGIN0("cc", "Picture::Replay");
+ DCHECK(picture_);
+ picture_->playback(canvas, callback);
+ SkIRect bounds;
+ canvas->getClipDeviceBounds(&bounds);
+ TRACE_EVENT_END1("cc", "Picture::Replay",
+ "num_pixels_replayed", bounds.width() * bounds.height());
+}
+
+scoped_ptr<base::Value> Picture::AsValue() const {
+ // Encode the picture as base64.
+ scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
+ res->Set("params.layer_rect", MathUtil::AsValue(layer_rect_).release());
+ std::string b64_picture;
+ PictureDebugUtil::SerializeAsBase64(picture_.get(), &b64_picture);
+ res->SetString("skp64", b64_picture);
+ return res.Pass();
+}
+
+void Picture::EmitTraceSnapshot() const {
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
+ TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
+ "cc::Picture",
+ this,
+ TracedPicture::AsTraceablePicture(this));
+}
+
+void Picture::EmitTraceSnapshotAlias(Picture* original) const {
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
+ TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
+ "cc::Picture",
+ this,
+ TracedPicture::AsTraceablePictureAlias(original));
+}
+
+PixelRefMap::Iterator Picture::GetPixelRefMapIterator(
+ const gfx::Rect& layer_rect) const {
+ return PixelRefMap::Iterator(layer_rect, this);
+}
+
+scoped_refptr<base::trace_event::ConvertableToTraceFormat>
+ Picture::AsTraceableRasterData(float scale) const {
+ scoped_refptr<base::trace_event::TracedValue> raster_data =
+ new base::trace_event::TracedValue();
+ TracedValue::SetIDRef(this, raster_data.get(), "picture_id");
+ raster_data->SetDouble("scale", scale);
+ return raster_data;
+}
+
+scoped_refptr<base::trace_event::ConvertableToTraceFormat>
+ Picture::AsTraceableRecordData() const {
+ scoped_refptr<base::trace_event::TracedValue> record_data =
+ new base::trace_event::TracedValue();
+ TracedValue::SetIDRef(this, record_data.get(), "picture_id");
+ MathUtil::AddToTracedValue("layer_rect", layer_rect_, record_data.get());
+ return record_data;
+}
+
+} // namespace cc
diff --git a/cc/playback/picture.h b/cc/playback/picture.h
new file mode 100644
index 0000000..b19504b
--- /dev/null
+++ b/cc/playback/picture.h
@@ -0,0 +1,119 @@
+// Copyright 2012 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_PLAYBACK_PICTURE_H_
+#define CC_PLAYBACK_PICTURE_H_
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/base/cc_export.h"
+#include "cc/base/region.h"
+#include "cc/playback/pixel_ref_map.h"
+#include "cc/playback/recording_source.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkPixelRef;
+
+namespace base {
+class Value;
+}
+
+namespace skia {
+class AnalysisCanvas;
+}
+
+namespace cc {
+
+class ContentLayerClient;
+
+class CC_EXPORT Picture
+ : public base::RefCountedThreadSafe<Picture> {
+ public:
+ static scoped_refptr<Picture> Create(
+ const gfx::Rect& layer_rect,
+ ContentLayerClient* client,
+ const gfx::Size& tile_grid_size,
+ bool gather_pixels_refs,
+ RecordingSource::RecordingMode recording_mode);
+ static scoped_refptr<Picture> CreateFromValue(const base::Value* value);
+ static scoped_refptr<Picture> CreateFromSkpValue(const base::Value* value);
+
+ gfx::Rect LayerRect() const { return layer_rect_; }
+
+ // Has Record() been called yet?
+ bool HasRecording() const { return picture_.get() != NULL; }
+
+ bool IsSuitableForGpuRasterization(const char** reason) const;
+ int ApproximateOpCount() const;
+ size_t ApproximateMemoryUsage() const;
+
+ bool HasText() const;
+
+ // Apply this scale and raster the negated region into the canvas.
+ // |negated_content_region| specifies the region to be clipped out of the
+ // raster operation, i.e., the parts of the canvas which will not get drawn
+ // to.
+ int Raster(SkCanvas* canvas,
+ SkPicture::AbortCallback* callback,
+ const Region& negated_content_region,
+ float contents_scale) const;
+
+ // Draw the picture directly into the given canvas, without applying any
+ // clip/scale/layer transformations.
+ void Replay(SkCanvas* canvas, SkPicture::AbortCallback* callback = NULL);
+
+ scoped_ptr<base::Value> AsValue() const;
+
+ void EmitTraceSnapshot() const;
+ void EmitTraceSnapshotAlias(Picture* original) const;
+
+ bool WillPlayBackBitmaps() const { return picture_->willPlayBackBitmaps(); }
+
+ PixelRefMap::Iterator GetPixelRefMapIterator(
+ const gfx::Rect& layer_rect) const;
+
+ private:
+ Picture(const gfx::Rect& layer_rect, const gfx::Size& tile_grid_size);
+ // This constructor assumes SkPicture is already ref'd and transfers
+ // ownership to this picture.
+ Picture(const skia::RefPtr<SkPicture>&,
+ const gfx::Rect& layer_rect,
+ const PixelRefMap& pixel_refs);
+ // This constructor will call AdoptRef on the SkPicture.
+ Picture(SkPicture*, const gfx::Rect& layer_rect);
+ ~Picture();
+
+ // Record a paint operation. To be able to safely use this SkPicture for
+ // playback on a different thread this can only be called once.
+ void Record(ContentLayerClient* client,
+ RecordingSource::RecordingMode recording_mode);
+
+ // Gather pixel refs from recording.
+ void GatherPixelRefs();
+
+ gfx::Rect layer_rect_;
+ skia::RefPtr<SkPicture> picture_;
+
+ PixelRefMap pixel_refs_;
+
+ scoped_refptr<base::trace_event::ConvertableToTraceFormat>
+ AsTraceableRasterData(float scale) const;
+ scoped_refptr<base::trace_event::ConvertableToTraceFormat>
+ AsTraceableRecordData() const;
+
+ friend class base::RefCountedThreadSafe<Picture>;
+ friend class PixelRefMap::Iterator;
+ DISALLOW_COPY_AND_ASSIGN(Picture);
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_PICTURE_H_
diff --git a/cc/playback/picture_pile.cc b/cc/playback/picture_pile.cc
new file mode 100644
index 0000000..b7b2822
--- /dev/null
+++ b/cc/playback/picture_pile.cc
@@ -0,0 +1,684 @@
+// Copyright 2012 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/playback/picture_pile.h"
+
+#include <algorithm>
+#include <limits>
+#include <vector>
+
+#include "cc/base/region.h"
+#include "cc/playback/picture_pile_impl.h"
+#include "skia/ext/analysis_canvas.h"
+
+namespace {
+// Layout pixel buffer around the visible layer rect to record. Any base
+// picture that intersects the visible layer rect expanded by this distance
+// will be recorded.
+const int kPixelDistanceToRecord = 8000;
+// We don't perform solid color analysis on images that have more than 10 skia
+// operations.
+const int kOpCountThatIsOkToAnalyze = 10;
+
+// Dimensions of the tiles in this picture pile as well as the dimensions of
+// the base picture in each tile.
+const int kBasePictureSize = 512;
+
+// TODO(humper): The density threshold here is somewhat arbitrary; need a
+// way to set // this from the command line so we can write a benchmark
+// script and find a sweet spot.
+const float kDensityThreshold = 0.5f;
+
+bool rect_sort_y(const gfx::Rect& r1, const gfx::Rect& r2) {
+ return r1.y() < r2.y() || (r1.y() == r2.y() && r1.x() < r2.x());
+}
+
+bool rect_sort_x(const gfx::Rect& r1, const gfx::Rect& r2) {
+ return r1.x() < r2.x() || (r1.x() == r2.x() && r1.y() < r2.y());
+}
+
+float PerformClustering(const std::vector<gfx::Rect>& tiles,
+ std::vector<gfx::Rect>* clustered_rects) {
+ // These variables track the record area and invalid area
+ // for the entire clustering
+ int total_record_area = 0;
+ int total_invalid_area = 0;
+
+ // These variables track the record area and invalid area
+ // for the current cluster being constructed.
+ gfx::Rect cur_record_rect;
+ int cluster_record_area = 0, cluster_invalid_area = 0;
+
+ for (std::vector<gfx::Rect>::const_iterator it = tiles.begin();
+ it != tiles.end();
+ it++) {
+ gfx::Rect invalid_tile = *it;
+
+ // For each tile, we consider adding the invalid tile to the
+ // current record rectangle. Only add it if the amount of empty
+ // space created is below a density threshold.
+ int tile_area = invalid_tile.width() * invalid_tile.height();
+
+ gfx::Rect proposed_union = cur_record_rect;
+ proposed_union.Union(invalid_tile);
+ int proposed_area = proposed_union.width() * proposed_union.height();
+ float proposed_density =
+ static_cast<float>(cluster_invalid_area + tile_area) /
+ static_cast<float>(proposed_area);
+
+ if (proposed_density >= kDensityThreshold) {
+ // It's okay to add this invalid tile to the
+ // current recording rectangle.
+ cur_record_rect = proposed_union;
+ cluster_record_area = proposed_area;
+ cluster_invalid_area += tile_area;
+ total_invalid_area += tile_area;
+ } else {
+ // Adding this invalid tile to the current recording rectangle
+ // would exceed our badness threshold, so put the current rectangle
+ // in the list of recording rects, and start a new one.
+ clustered_rects->push_back(cur_record_rect);
+ total_record_area += cluster_record_area;
+ cur_record_rect = invalid_tile;
+ cluster_invalid_area = tile_area;
+ cluster_record_area = tile_area;
+ }
+ }
+
+ DCHECK(!cur_record_rect.IsEmpty());
+ clustered_rects->push_back(cur_record_rect);
+ total_record_area += cluster_record_area;;
+
+ DCHECK_NE(total_record_area, 0);
+
+ return static_cast<float>(total_invalid_area) /
+ static_cast<float>(total_record_area);
+}
+
+void ClusterTiles(const std::vector<gfx::Rect>& invalid_tiles,
+ std::vector<gfx::Rect>* record_rects) {
+ TRACE_EVENT1("cc", "ClusterTiles",
+ "count",
+ invalid_tiles.size());
+ if (invalid_tiles.size() <= 1) {
+ // Quickly handle the special case for common
+ // single-invalidation update, and also the less common
+ // case of no tiles passed in.
+ *record_rects = invalid_tiles;
+ return;
+ }
+
+ // Sort the invalid tiles by y coordinate.
+ std::vector<gfx::Rect> invalid_tiles_vertical = invalid_tiles;
+ std::sort(invalid_tiles_vertical.begin(),
+ invalid_tiles_vertical.end(),
+ rect_sort_y);
+
+ std::vector<gfx::Rect> vertical_clustering;
+ float vertical_density =
+ PerformClustering(invalid_tiles_vertical, &vertical_clustering);
+
+ // If vertical density is optimal, then we can return early.
+ if (vertical_density == 1.f) {
+ *record_rects = vertical_clustering;
+ return;
+ }
+
+ // Now try again with a horizontal sort, see which one is best
+ std::vector<gfx::Rect> invalid_tiles_horizontal = invalid_tiles;
+ std::sort(invalid_tiles_horizontal.begin(),
+ invalid_tiles_horizontal.end(),
+ rect_sort_x);
+
+ std::vector<gfx::Rect> horizontal_clustering;
+ float horizontal_density =
+ PerformClustering(invalid_tiles_horizontal, &horizontal_clustering);
+
+ if (vertical_density < horizontal_density) {
+ *record_rects = horizontal_clustering;
+ return;
+ }
+
+ *record_rects = vertical_clustering;
+}
+
+#ifdef NDEBUG
+const bool kDefaultClearCanvasSetting = false;
+#else
+const bool kDefaultClearCanvasSetting = true;
+#endif
+
+} // namespace
+
+namespace cc {
+
+PicturePile::PicturePile(float min_contents_scale,
+ const gfx::Size& tile_grid_size)
+ : min_contents_scale_(0),
+ slow_down_raster_scale_factor_for_debug_(0),
+ gather_pixel_refs_(false),
+ has_any_recordings_(false),
+ clear_canvas_with_debug_color_(kDefaultClearCanvasSetting),
+ requires_clear_(true),
+ is_solid_color_(false),
+ solid_color_(SK_ColorTRANSPARENT),
+ background_color_(SK_ColorTRANSPARENT),
+ pixel_record_distance_(kPixelDistanceToRecord),
+ is_suitable_for_gpu_rasterization_(true) {
+ tiling_.SetMaxTextureSize(gfx::Size(kBasePictureSize, kBasePictureSize));
+ SetMinContentsScale(min_contents_scale);
+ SetTileGridSize(tile_grid_size);
+}
+
+PicturePile::~PicturePile() {
+}
+
+bool PicturePile::UpdateAndExpandInvalidation(
+ ContentLayerClient* painter,
+ Region* invalidation,
+ const gfx::Size& layer_size,
+ const gfx::Rect& visible_layer_rect,
+ int frame_number,
+ RecordingSource::RecordingMode recording_mode) {
+ gfx::Rect interest_rect = visible_layer_rect;
+ interest_rect.Inset(-pixel_record_distance_, -pixel_record_distance_);
+ recorded_viewport_ = interest_rect;
+ recorded_viewport_.Intersect(gfx::Rect(layer_size));
+
+ bool updated = ApplyInvalidationAndResize(interest_rect, invalidation,
+ layer_size, frame_number);
+ std::vector<gfx::Rect> invalid_tiles;
+ GetInvalidTileRects(interest_rect, &invalid_tiles);
+ std::vector<gfx::Rect> record_rects;
+ ClusterTiles(invalid_tiles, &record_rects);
+
+ if (record_rects.empty())
+ return updated;
+
+ CreatePictures(painter, recording_mode, record_rects);
+
+ DetermineIfSolidColor();
+
+ has_any_recordings_ = true;
+ DCHECK(CanRasterSlowTileCheck(recorded_viewport_));
+ return true;
+}
+
+bool PicturePile::ApplyInvalidationAndResize(const gfx::Rect& interest_rect,
+ Region* invalidation,
+ const gfx::Size& layer_size,
+ int frame_number) {
+ bool updated = false;
+
+ Region synthetic_invalidation;
+ gfx::Size old_tiling_size = GetSize();
+ if (old_tiling_size != layer_size) {
+ tiling_.SetTilingSize(layer_size);
+ updated = true;
+ }
+
+ gfx::Rect interest_rect_over_tiles =
+ tiling_.ExpandRectToTileBounds(interest_rect);
+
+ if (old_tiling_size != layer_size) {
+ gfx::Size min_tiling_size(
+ std::min(GetSize().width(), old_tiling_size.width()),
+ std::min(GetSize().height(), old_tiling_size.height()));
+ gfx::Size max_tiling_size(
+ std::max(GetSize().width(), old_tiling_size.width()),
+ std::max(GetSize().height(), old_tiling_size.height()));
+
+ has_any_recordings_ = false;
+
+ // Drop recordings that are outside the new or old layer bounds or that
+ // changed size. Newly exposed areas are considered invalidated.
+ // Previously exposed areas that are now outside of bounds also need to
+ // be invalidated, as they may become part of raster when scale < 1.
+ std::vector<PictureMapKey> to_erase;
+ int min_toss_x = tiling_.num_tiles_x();
+ if (max_tiling_size.width() > min_tiling_size.width()) {
+ min_toss_x =
+ tiling_.FirstBorderTileXIndexFromSrcCoord(min_tiling_size.width());
+ }
+ int min_toss_y = tiling_.num_tiles_y();
+ if (max_tiling_size.height() > min_tiling_size.height()) {
+ min_toss_y =
+ tiling_.FirstBorderTileYIndexFromSrcCoord(min_tiling_size.height());
+ }
+ for (const auto& key_picture_pair : picture_map_) {
+ const PictureMapKey& key = key_picture_pair.first;
+ if (key.first < min_toss_x && key.second < min_toss_y) {
+ has_any_recordings_ = true;
+ continue;
+ }
+ to_erase.push_back(key);
+ }
+
+ for (size_t i = 0; i < to_erase.size(); ++i)
+ picture_map_.erase(to_erase[i]);
+
+ // If a recording is dropped and not re-recorded below, invalidate that
+ // full recording to cause any raster tiles that would use it to be
+ // dropped.
+ // If the recording will be replaced below, invalidate newly exposed
+ // areas and previously exposed areas to force raster tiles that include the
+ // old recording to know there is new recording to display.
+ gfx::Rect min_tiling_rect_over_tiles =
+ tiling_.ExpandRectToTileBounds(gfx::Rect(min_tiling_size));
+ if (min_toss_x < tiling_.num_tiles_x()) {
+ // The bounds which we want to invalidate are the tiles along the old
+ // edge of the pile when expanding, or the new edge of the pile when
+ // shrinking. In either case, it's the difference of the two, so we'll
+ // call this bounding box the DELTA EDGE RECT.
+ //
+ // In the picture below, the delta edge rect would be the bounding box of
+ // tiles {h,i,j}. |min_toss_x| would be equal to the horizontal index of
+ // the same tiles.
+ //
+ // min pile edge-v max pile edge-v
+ // ---------------+ - - - - - - - -+
+ // mmppssvvyybbeeh|h .
+ // mmppssvvyybbeeh|h .
+ // nnqqttwwzzccffi|i .
+ // nnqqttwwzzccffi|i .
+ // oorruuxxaaddggj|j .
+ // oorruuxxaaddggj|j .
+ // ---------------+ - - - - - - - -+ <- min pile edge
+ // .
+ // - - - - - - - - - - - - - - - -+ <- max pile edge
+ //
+ // If you were to slide a vertical beam from the left edge of the
+ // delta edge rect toward the right, it would either hit the right edge
+ // of the delta edge rect, or the interest rect (expanded to the bounds
+ // of the tiles it touches). The same is true for a beam parallel to
+ // any of the four edges, sliding across the delta edge rect. We use
+ // the union of these four rectangles generated by these beams to
+ // determine which part of the delta edge rect is outside of the expanded
+ // interest rect.
+ //
+ // Case 1: Intersect rect is outside the delta edge rect. It can be
+ // either on the left or the right. The |left_rect| and |right_rect|,
+ // cover this case, one will be empty and one will cover the full
+ // delta edge rect. In the picture below, |left_rect| would cover the
+ // delta edge rect, and |right_rect| would be empty.
+ // +----------------------+ |^^^^^^^^^^^^^^^|
+ // |===> DELTA EDGE RECT | | |
+ // |===> | | INTEREST RECT |
+ // |===> | | |
+ // |===> | | |
+ // +----------------------+ |vvvvvvvvvvvvvvv|
+ //
+ // Case 2: Interest rect is inside the delta edge rect. It will always
+ // fill the entire delta edge rect horizontally since the old edge rect
+ // is a single tile wide, and the interest rect has been expanded to the
+ // bounds of the tiles it touches. In this case the |left_rect| and
+ // |right_rect| will be empty, but the case is handled by the |top_rect|
+ // and |bottom_rect|. In the picture below, neither the |top_rect| nor
+ // |bottom_rect| would empty, they would each cover the area of the old
+ // edge rect outside the expanded interest rect.
+ // +-----------------+
+ // |:::::::::::::::::|
+ // |:::::::::::::::::|
+ // |vvvvvvvvvvvvvvvvv|
+ // | |
+ // +-----------------+
+ // | INTEREST RECT |
+ // | |
+ // +-----------------+
+ // | |
+ // | DELTA EDGE RECT |
+ // +-----------------+
+ //
+ // Lastly, we need to consider tiles inside the expanded interest rect.
+ // For those tiles, we want to invalidate exactly the newly exposed
+ // pixels. In the picture below the tiles in the delta edge rect have
+ // been resized and the area covered by periods must be invalidated. The
+ // |exposed_rect| will cover exactly that area.
+ // v-min pile edge
+ // +---------+-------+
+ // | ........|
+ // | ........|
+ // | DELTA EDGE.RECT.|
+ // | ........|
+ // | ........|
+ // | ........|
+ // | ........|
+ // | ........|
+ // | ........|
+ // +---------+-------+
+
+ int left = tiling_.TilePositionX(min_toss_x);
+ int right = left + tiling_.TileSizeX(min_toss_x);
+ int top = min_tiling_rect_over_tiles.y();
+ int bottom = min_tiling_rect_over_tiles.bottom();
+
+ int left_until = std::min(interest_rect_over_tiles.x(), right);
+ int right_until = std::max(interest_rect_over_tiles.right(), left);
+ int top_until = std::min(interest_rect_over_tiles.y(), bottom);
+ int bottom_until = std::max(interest_rect_over_tiles.bottom(), top);
+
+ int exposed_left = min_tiling_size.width();
+ int exposed_left_until = max_tiling_size.width();
+ int exposed_top = top;
+ int exposed_bottom = max_tiling_size.height();
+ DCHECK_GE(exposed_left, left);
+
+ gfx::Rect left_rect(left, top, left_until - left, bottom - top);
+ gfx::Rect right_rect(right_until, top, right - right_until, bottom - top);
+ gfx::Rect top_rect(left, top, right - left, top_until - top);
+ gfx::Rect bottom_rect(
+ left, bottom_until, right - left, bottom - bottom_until);
+ gfx::Rect exposed_rect(exposed_left,
+ exposed_top,
+ exposed_left_until - exposed_left,
+ exposed_bottom - exposed_top);
+ synthetic_invalidation.Union(left_rect);
+ synthetic_invalidation.Union(right_rect);
+ synthetic_invalidation.Union(top_rect);
+ synthetic_invalidation.Union(bottom_rect);
+ synthetic_invalidation.Union(exposed_rect);
+ }
+ if (min_toss_y < tiling_.num_tiles_y()) {
+ // The same thing occurs here as in the case above, but the invalidation
+ // rect is the bounding box around the bottom row of tiles in the min
+ // pile. This would be tiles {o,r,u,x,a,d,g,j} in the above picture.
+
+ int top = tiling_.TilePositionY(min_toss_y);
+ int bottom = top + tiling_.TileSizeY(min_toss_y);
+ int left = min_tiling_rect_over_tiles.x();
+ int right = min_tiling_rect_over_tiles.right();
+
+ int top_until = std::min(interest_rect_over_tiles.y(), bottom);
+ int bottom_until = std::max(interest_rect_over_tiles.bottom(), top);
+ int left_until = std::min(interest_rect_over_tiles.x(), right);
+ int right_until = std::max(interest_rect_over_tiles.right(), left);
+
+ int exposed_top = min_tiling_size.height();
+ int exposed_top_until = max_tiling_size.height();
+ int exposed_left = left;
+ int exposed_right = max_tiling_size.width();
+ DCHECK_GE(exposed_top, top);
+
+ gfx::Rect left_rect(left, top, left_until - left, bottom - top);
+ gfx::Rect right_rect(right_until, top, right - right_until, bottom - top);
+ gfx::Rect top_rect(left, top, right - left, top_until - top);
+ gfx::Rect bottom_rect(
+ left, bottom_until, right - left, bottom - bottom_until);
+ gfx::Rect exposed_rect(exposed_left,
+ exposed_top,
+ exposed_right - exposed_left,
+ exposed_top_until - exposed_top);
+ synthetic_invalidation.Union(left_rect);
+ synthetic_invalidation.Union(right_rect);
+ synthetic_invalidation.Union(top_rect);
+ synthetic_invalidation.Union(bottom_rect);
+ synthetic_invalidation.Union(exposed_rect);
+ }
+ }
+
+ // Detect cases where the full pile is invalidated, in this situation we
+ // can just drop/invalidate everything.
+ if (invalidation->Contains(gfx::Rect(old_tiling_size)) ||
+ invalidation->Contains(gfx::Rect(GetSize()))) {
+ updated = !picture_map_.empty();
+ picture_map_.clear();
+ } else {
+ // Expand invalidation that is on tiles that aren't in the interest rect and
+ // will not be re-recorded below. These tiles are no longer valid and should
+ // be considerered fully invalid, so we can know to not keep around raster
+ // tiles that intersect with these recording tiles.
+ Region invalidation_expanded_to_full_tiles;
+
+ for (Region::Iterator i(*invalidation); i.has_rect(); i.next()) {
+ gfx::Rect invalid_rect = i.rect();
+
+ // This rect covers the bounds (excluding borders) of all tiles whose
+ // bounds (including borders) touch the |interest_rect|. This matches
+ // the iteration of the |invalid_rect| below which includes borders when
+ // calling Invalidate() on pictures.
+ gfx::Rect invalid_rect_outside_interest_rect_tiles =
+ tiling_.ExpandRectToTileBounds(invalid_rect);
+ // We subtract the |interest_rect_over_tiles| which represents the bounds
+ // of tiles that will be re-recorded below. This matches the iteration of
+ // |interest_rect| below which includes borders.
+ // TODO(danakj): We should have a Rect-subtract-Rect-to-2-rects operator
+ // instead of using Rect::Subtract which gives you the bounding box of the
+ // subtraction.
+ invalid_rect_outside_interest_rect_tiles.Subtract(
+ interest_rect_over_tiles);
+ invalidation_expanded_to_full_tiles.Union(
+ invalid_rect_outside_interest_rect_tiles);
+
+ // Split this inflated invalidation across tile boundaries and apply it
+ // to all tiles that it touches.
+ bool include_borders = true;
+ for (TilingData::Iterator iter(&tiling_, invalid_rect, include_borders);
+ iter;
+ ++iter) {
+ const PictureMapKey& key = iter.index();
+
+ PictureMap::iterator picture_it = picture_map_.find(key);
+ if (picture_it == picture_map_.end())
+ continue;
+
+ updated = true;
+ picture_map_.erase(key);
+
+ // Invalidate drops the picture so the whole tile better be invalidated
+ // if it won't be re-recorded below.
+ DCHECK_IMPLIES(!tiling_.TileBounds(key.first, key.second)
+ .Intersects(interest_rect_over_tiles),
+ invalidation_expanded_to_full_tiles.Contains(
+ tiling_.TileBounds(key.first, key.second)));
+ }
+ }
+ invalidation->Union(invalidation_expanded_to_full_tiles);
+ }
+
+ invalidation->Union(synthetic_invalidation);
+ return updated;
+}
+
+void PicturePile::GetInvalidTileRects(const gfx::Rect& interest_rect,
+ std::vector<gfx::Rect>* invalid_tiles) {
+ // Make a list of all invalid tiles; we will attempt to
+ // cluster these into multiple invalidation regions.
+ bool include_borders = true;
+ for (TilingData::Iterator it(&tiling_, interest_rect, include_borders); it;
+ ++it) {
+ const PictureMapKey& key = it.index();
+ if (picture_map_.find(key) == picture_map_.end())
+ invalid_tiles->push_back(tiling_.TileBounds(key.first, key.second));
+ }
+}
+
+void PicturePile::CreatePictures(ContentLayerClient* painter,
+ RecordingSource::RecordingMode recording_mode,
+ const std::vector<gfx::Rect>& record_rects) {
+ for (const auto& record_rect : record_rects) {
+ gfx::Rect padded_record_rect = PadRect(record_rect);
+
+ int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
+ scoped_refptr<Picture> picture;
+
+ for (int i = 0; i < repeat_count; i++) {
+ picture = Picture::Create(padded_record_rect, painter, tile_grid_size_,
+ gather_pixel_refs_, recording_mode);
+ // Note the '&&' with previous is-suitable state.
+ // This means that once a picture-pile becomes unsuitable for gpu
+ // rasterization due to some content, it will continue to be unsuitable
+ // even if that content is replaced by gpu-friendly content.
+ // This is an optimization to avoid iterating though all pictures in
+ // the pile after each invalidation.
+ if (is_suitable_for_gpu_rasterization_) {
+ const char* reason = nullptr;
+ is_suitable_for_gpu_rasterization_ &=
+ picture->IsSuitableForGpuRasterization(&reason);
+
+ if (!is_suitable_for_gpu_rasterization_) {
+ TRACE_EVENT_INSTANT1("cc", "GPU Rasterization Veto",
+ TRACE_EVENT_SCOPE_THREAD, "reason", reason);
+ }
+ }
+ }
+
+ bool found_tile_for_recorded_picture = false;
+
+ bool include_borders = true;
+ for (TilingData::Iterator it(&tiling_, padded_record_rect, include_borders);
+ it; ++it) {
+ const PictureMapKey& key = it.index();
+ gfx::Rect tile = PaddedRect(key);
+ if (padded_record_rect.Contains(tile)) {
+ picture_map_[key] = picture;
+ found_tile_for_recorded_picture = true;
+ }
+ }
+ DCHECK(found_tile_for_recorded_picture);
+ }
+}
+
+scoped_refptr<RasterSource> PicturePile::CreateRasterSource(
+ bool can_use_lcd_text) const {
+ return scoped_refptr<RasterSource>(
+ PicturePileImpl::CreateFromPicturePile(this, can_use_lcd_text));
+}
+
+gfx::Size PicturePile::GetSize() const {
+ return tiling_.tiling_size();
+}
+
+void PicturePile::SetEmptyBounds() {
+ tiling_.SetTilingSize(gfx::Size());
+ Clear();
+}
+
+void PicturePile::SetMinContentsScale(float min_contents_scale) {
+ DCHECK(min_contents_scale);
+ if (min_contents_scale_ == min_contents_scale)
+ return;
+
+ // Picture contents are played back scaled. When the final contents scale is
+ // less than 1 (i.e. low res), then multiple recorded pixels will be used
+ // to raster one final pixel. To avoid splitting a final pixel across
+ // pictures (which would result in incorrect rasterization due to blending), a
+ // buffer margin is added so that any picture can be snapped to integral
+ // final pixels.
+ //
+ // For example, if a 1/4 contents scale is used, then that would be 3 buffer
+ // pixels, since that's the minimum number of pixels to add so that resulting
+ // content can be snapped to a four pixel aligned grid.
+ int buffer_pixels = static_cast<int>(ceil(1 / min_contents_scale) - 1);
+ buffer_pixels = std::max(0, buffer_pixels);
+ SetBufferPixels(buffer_pixels);
+ min_contents_scale_ = min_contents_scale;
+}
+
+void PicturePile::SetSlowdownRasterScaleFactor(int factor) {
+ slow_down_raster_scale_factor_for_debug_ = factor;
+}
+
+void PicturePile::SetGatherPixelRefs(bool gather_pixel_refs) {
+ gather_pixel_refs_ = gather_pixel_refs;
+}
+
+void PicturePile::SetBackgroundColor(SkColor background_color) {
+ background_color_ = background_color;
+}
+
+void PicturePile::SetRequiresClear(bool requires_clear) {
+ requires_clear_ = requires_clear;
+}
+
+bool PicturePile::IsSuitableForGpuRasterization() const {
+ return is_suitable_for_gpu_rasterization_;
+}
+
+void PicturePile::SetTileGridSize(const gfx::Size& tile_grid_size) {
+ DCHECK_GT(tile_grid_size.width(), 0);
+ DCHECK_GT(tile_grid_size.height(), 0);
+
+ tile_grid_size_ = tile_grid_size;
+}
+
+void PicturePile::SetUnsuitableForGpuRasterizationForTesting() {
+ is_suitable_for_gpu_rasterization_ = false;
+}
+
+gfx::Size PicturePile::GetTileGridSizeForTesting() const {
+ return tile_grid_size_;
+}
+
+bool PicturePile::CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const {
+ bool include_borders = false;
+ for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
+ tile_iter; ++tile_iter) {
+ PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
+ if (map_iter == picture_map_.end())
+ return false;
+ }
+ return true;
+}
+
+void PicturePile::DetermineIfSolidColor() {
+ is_solid_color_ = false;
+ solid_color_ = SK_ColorTRANSPARENT;
+
+ if (picture_map_.empty()) {
+ return;
+ }
+
+ PictureMap::const_iterator it = picture_map_.begin();
+ const Picture* picture = it->second.get();
+
+ // Missing recordings due to frequent invalidations or being too far away
+ // from the interest rect will cause the a null picture to exist.
+ if (!picture)
+ return;
+
+ // Don't bother doing more work if the first image is too complicated.
+ if (picture->ApproximateOpCount() > kOpCountThatIsOkToAnalyze)
+ return;
+
+ // Make sure all of the mapped images point to the same picture.
+ for (++it; it != picture_map_.end(); ++it) {
+ if (it->second.get() != picture)
+ return;
+ }
+
+ gfx::Size layer_size = GetSize();
+ skia::AnalysisCanvas canvas(layer_size.width(), layer_size.height());
+
+ picture->Raster(&canvas, nullptr, Region(), 1.0f);
+ is_solid_color_ = canvas.GetColorIfSolid(&solid_color_);
+}
+
+gfx::Rect PicturePile::PaddedRect(const PictureMapKey& key) const {
+ gfx::Rect tile = tiling_.TileBounds(key.first, key.second);
+ return PadRect(tile);
+}
+
+gfx::Rect PicturePile::PadRect(const gfx::Rect& rect) const {
+ gfx::Rect padded_rect = rect;
+ padded_rect.Inset(-buffer_pixels(), -buffer_pixels(), -buffer_pixels(),
+ -buffer_pixels());
+ return padded_rect;
+}
+
+void PicturePile::Clear() {
+ picture_map_.clear();
+ recorded_viewport_ = gfx::Rect();
+ has_any_recordings_ = false;
+ is_solid_color_ = false;
+}
+
+void PicturePile::SetBufferPixels(int new_buffer_pixels) {
+ if (new_buffer_pixels == buffer_pixels())
+ return;
+
+ Clear();
+ tiling_.SetBorderTexels(new_buffer_pixels);
+}
+
+} // namespace cc
diff --git a/cc/playback/picture_pile.h b/cc/playback/picture_pile.h
new file mode 100644
index 0000000..889e9f5
--- /dev/null
+++ b/cc/playback/picture_pile.h
@@ -0,0 +1,107 @@
+// Copyright 2012 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_PLAYBACK_PICTURE_PILE_H_
+#define CC_PLAYBACK_PICTURE_PILE_H_
+
+#include <bitset>
+#include <utility>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/memory/ref_counted.h"
+#include "cc/base/tiling_data.h"
+#include "cc/playback/picture.h"
+
+namespace cc {
+class PicturePileImpl;
+
+class CC_EXPORT PicturePile : public RecordingSource {
+ public:
+ PicturePile(float min_contents_scale, const gfx::Size& tile_grid_size);
+ ~PicturePile() override;
+
+ // RecordingSource overrides.
+ bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
+ Region* invalidation,
+ const gfx::Size& layer_size,
+ const gfx::Rect& visible_layer_rect,
+ int frame_number,
+ RecordingMode recording_mode) override;
+ scoped_refptr<RasterSource> CreateRasterSource(
+ bool can_use_lcd_text) const override;
+ gfx::Size GetSize() const final;
+ void SetEmptyBounds() override;
+ void SetSlowdownRasterScaleFactor(int factor) override;
+ void SetGatherPixelRefs(bool gather_pixel_refs) override;
+ void SetBackgroundColor(SkColor background_color) override;
+ void SetRequiresClear(bool requires_clear) override;
+ bool IsSuitableForGpuRasterization() const override;
+ void SetUnsuitableForGpuRasterizationForTesting() override;
+ gfx::Size GetTileGridSizeForTesting() const override;
+
+ typedef std::pair<int, int> PictureMapKey;
+ typedef base::hash_map<PictureMapKey, scoped_refptr<const Picture>>
+ PictureMap;
+
+ // An internal CanRaster check that goes to the picture_map rather than
+ // using the recorded_viewport hint.
+ bool CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const;
+
+ void Clear();
+
+ void SetMinContentsScale(float min_contents_scale);
+ void SetTileGridSize(const gfx::Size& tile_grid_size);
+
+ gfx::Rect PaddedRect(const PictureMapKey& key) const;
+ gfx::Rect PadRect(const gfx::Rect& rect) const;
+
+ int buffer_pixels() const { return tiling_.border_texels(); }
+
+ // A picture pile is a tiled set of pictures. The picture map is a map of tile
+ // indices to picture infos.
+ PictureMap picture_map_;
+ TilingData tiling_;
+
+ // If non-empty, all pictures tiles inside this rect are recorded. There may
+ // be recordings outside this rect, but everything inside the rect is
+ // recorded.
+ gfx::Rect recorded_viewport_;
+ float min_contents_scale_;
+ gfx::Size tile_grid_size_;
+ int slow_down_raster_scale_factor_for_debug_;
+ bool gather_pixel_refs_;
+ // A hint about whether there are any recordings. This may be a false
+ // positive.
+ bool has_any_recordings_;
+ bool clear_canvas_with_debug_color_;
+ bool requires_clear_;
+ bool is_solid_color_;
+ SkColor solid_color_;
+ SkColor background_color_;
+ int pixel_record_distance_;
+
+ private:
+ friend class PicturePileImpl;
+
+ void CreatePictures(ContentLayerClient* painter,
+ RecordingMode recording_mode,
+ const std::vector<gfx::Rect>& record_rects);
+ void GetInvalidTileRects(const gfx::Rect& interest_rect,
+ std::vector<gfx::Rect>* invalid_tiles);
+ bool ApplyInvalidationAndResize(const gfx::Rect& interest_rect,
+ Region* invalidation,
+ const gfx::Size& layer_size,
+ int frame_number);
+ void DetermineIfSolidColor();
+ void SetBufferPixels(int buffer_pixels);
+
+ bool is_suitable_for_gpu_rasterization_;
+
+ DISALLOW_COPY_AND_ASSIGN(PicturePile);
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_PICTURE_PILE_H_
diff --git a/cc/playback/picture_pile_impl.cc b/cc/playback/picture_pile_impl.cc
new file mode 100644
index 0000000..96c6f63
--- /dev/null
+++ b/cc/playback/picture_pile_impl.cc
@@ -0,0 +1,463 @@
+// Copyright 2012 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 <algorithm>
+#include <limits>
+#include <set>
+
+#include "base/trace_event/trace_event.h"
+#include "cc/base/region.h"
+#include "cc/debug/debug_colors.h"
+#include "cc/playback/picture_pile_impl.h"
+#include "cc/playback/raster_source_helper.h"
+#include "skia/ext/analysis_canvas.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+
+namespace cc {
+
+scoped_refptr<PicturePileImpl> PicturePileImpl::CreateFromPicturePile(
+ const PicturePile* other,
+ bool can_use_lcd_text) {
+ return make_scoped_refptr(new PicturePileImpl(other, can_use_lcd_text));
+}
+
+PicturePileImpl::PicturePileImpl()
+ : background_color_(SK_ColorTRANSPARENT),
+ requires_clear_(true),
+ can_use_lcd_text_(true),
+ is_solid_color_(false),
+ solid_color_(SK_ColorTRANSPARENT),
+ has_any_recordings_(false),
+ clear_canvas_with_debug_color_(false),
+ min_contents_scale_(0.f),
+ slow_down_raster_scale_factor_for_debug_(0),
+ should_attempt_to_use_distance_field_text_(false),
+ picture_memory_usage_(0) {
+}
+
+PicturePileImpl::PicturePileImpl(const PicturePile* other,
+ bool can_use_lcd_text)
+ : picture_map_(other->picture_map_),
+ tiling_(other->tiling_),
+ background_color_(other->background_color_),
+ requires_clear_(other->requires_clear_),
+ can_use_lcd_text_(can_use_lcd_text),
+ is_solid_color_(other->is_solid_color_),
+ solid_color_(other->solid_color_),
+ recorded_viewport_(other->recorded_viewport_),
+ has_any_recordings_(other->has_any_recordings_),
+ clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
+ min_contents_scale_(other->min_contents_scale_),
+ slow_down_raster_scale_factor_for_debug_(
+ other->slow_down_raster_scale_factor_for_debug_),
+ should_attempt_to_use_distance_field_text_(false),
+ picture_memory_usage_(0) {
+ // Figure out the picture size upon construction.
+ base::hash_set<const Picture*> pictures_seen;
+ for (const auto& map_value : picture_map_) {
+ const Picture* picture = map_value.second.get();
+ if (pictures_seen.insert(picture).second)
+ picture_memory_usage_ += picture->ApproximateMemoryUsage();
+ }
+}
+
+PicturePileImpl::PicturePileImpl(const PicturePileImpl* other,
+ bool can_use_lcd_text)
+ : picture_map_(other->picture_map_),
+ tiling_(other->tiling_),
+ background_color_(other->background_color_),
+ requires_clear_(other->requires_clear_),
+ can_use_lcd_text_(can_use_lcd_text),
+ is_solid_color_(other->is_solid_color_),
+ solid_color_(other->solid_color_),
+ recorded_viewport_(other->recorded_viewport_),
+ has_any_recordings_(other->has_any_recordings_),
+ clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
+ min_contents_scale_(other->min_contents_scale_),
+ slow_down_raster_scale_factor_for_debug_(
+ other->slow_down_raster_scale_factor_for_debug_),
+ should_attempt_to_use_distance_field_text_(
+ other->should_attempt_to_use_distance_field_text_),
+ picture_memory_usage_(other->picture_memory_usage_) {
+}
+
+PicturePileImpl::~PicturePileImpl() {
+}
+
+void PicturePileImpl::PlaybackToSharedCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const {
+ RasterCommon(canvas, NULL, canvas_rect, contents_scale);
+}
+
+void PicturePileImpl::RasterForAnalysis(skia::AnalysisCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const {
+ RasterCommon(canvas, canvas, canvas_rect, contents_scale);
+}
+
+void PicturePileImpl::PlaybackToCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const {
+ RasterSourceHelper::PrepareForPlaybackToCanvas(
+ canvas, canvas_rect, gfx::Rect(tiling_.tiling_size()), contents_scale,
+ background_color_, clear_canvas_with_debug_color_, requires_clear_);
+ RasterCommon(canvas, NULL, canvas_rect, contents_scale);
+}
+
+void PicturePileImpl::CoalesceRasters(const gfx::Rect& canvas_rect,
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ PictureRegionMap* results) const {
+ DCHECK(results);
+ // Rasterize the collection of relevant picture piles.
+ gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
+ content_rect, 1.f / contents_scale);
+
+ // Make sure pictures don't overlap by keeping track of previous right/bottom.
+ int min_content_left = -1;
+ int min_content_top = -1;
+ int last_row_index = -1;
+ int last_col_index = -1;
+ gfx::Rect last_content_rect;
+
+ // Coalesce rasters of the same picture into different rects:
+ // - Compute the clip of each of the pile chunks,
+ // - Subtract it from the canvas rect to get difference region
+ // - Later, use the difference region to subtract each of the comprising
+ // rects from the canvas.
+ // Note that in essence, we're trying to mimic clipRegion with intersect op
+ // that also respects the current canvas transform and clip. In order to use
+ // the canvas transform, we must stick to clipRect operations (clipRegion
+ // ignores the transform). Intersect then can be written as subtracting the
+ // negation of the region we're trying to intersect. Luckily, we know that all
+ // of the rects will have to fit into |content_rect|, so we can start with
+ // that and subtract chunk rects to get the region that we need to subtract
+ // from the canvas. Then, we can use clipRect with difference op to subtract
+ // each rect in the region.
+ bool include_borders = true;
+ for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
+ tile_iter;
+ ++tile_iter) {
+ PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
+ if (map_iter == picture_map_.end())
+ continue;
+ const Picture* picture = map_iter->second.get();
+ DCHECK(picture);
+
+ // This is intentionally *enclosed* rect, so that the clip is aligned on
+ // integral post-scale content pixels and does not extend past the edges
+ // of the picture chunk's layer rect. The min_contents_scale enforces that
+ // enough buffer pixels have been added such that the enclosed rect
+ // encompasses all invalidated pixels at any larger scale level.
+ gfx::Rect chunk_rect = PaddedRect(tile_iter.index());
+ gfx::Rect content_clip =
+ gfx::ScaleToEnclosedRect(chunk_rect, contents_scale);
+ DCHECK(!content_clip.IsEmpty()) << "Layer rect: "
+ << picture->LayerRect().ToString()
+ << "Contents scale: " << contents_scale;
+ content_clip.Intersect(canvas_rect);
+
+ // Make sure iterator goes top->bottom.
+ DCHECK_GE(tile_iter.index_y(), last_row_index);
+ if (tile_iter.index_y() > last_row_index) {
+ // First tile in a new row.
+ min_content_left = content_clip.x();
+ min_content_top = last_content_rect.bottom();
+ } else {
+ // Make sure iterator goes left->right.
+ DCHECK_GT(tile_iter.index_x(), last_col_index);
+ min_content_left = last_content_rect.right();
+ min_content_top = last_content_rect.y();
+ }
+
+ last_col_index = tile_iter.index_x();
+ last_row_index = tile_iter.index_y();
+
+ // Only inset if the content_clip is less than then previous min.
+ int inset_left = std::max(0, min_content_left - content_clip.x());
+ int inset_top = std::max(0, min_content_top - content_clip.y());
+ content_clip.Inset(inset_left, inset_top, 0, 0);
+
+ PictureRegionMap::iterator it = results->find(picture);
+ Region* clip_region;
+ if (it == results->end()) {
+ // The clip for a set of coalesced pictures starts out clipping the entire
+ // canvas. Each picture added to the set must subtract its own bounds
+ // from the clip region, poking a hole so that the picture is unclipped.
+ clip_region = &(*results)[picture];
+ *clip_region = canvas_rect;
+ } else {
+ clip_region = &it->second;
+ }
+
+ DCHECK(clip_region->Contains(content_clip))
+ << "Content clips should not overlap.";
+ clip_region->Subtract(content_clip);
+ last_content_rect = content_clip;
+ }
+}
+
+void PicturePileImpl::RasterCommon(SkCanvas* canvas,
+ SkDrawPictureCallback* callback,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const {
+ DCHECK(contents_scale >= min_contents_scale_);
+
+ canvas->translate(-canvas_rect.x(), -canvas_rect.y());
+ gfx::Rect content_tiling_rect = gfx::ToEnclosingRect(
+ gfx::ScaleRect(gfx::Rect(tiling_.tiling_size()), contents_scale));
+ content_tiling_rect.Intersect(canvas_rect);
+
+ canvas->clipRect(gfx::RectToSkRect(content_tiling_rect),
+ SkRegion::kIntersect_Op);
+
+ PictureRegionMap picture_region_map;
+ CoalesceRasters(
+ canvas_rect, content_tiling_rect, contents_scale, &picture_region_map);
+
+#ifndef NDEBUG
+ Region total_clip;
+#endif // NDEBUG
+
+ // Iterate the coalesced map and use each picture's region
+ // to clip the canvas.
+ for (PictureRegionMap::iterator it = picture_region_map.begin();
+ it != picture_region_map.end();
+ ++it) {
+ const Picture* picture = it->first;
+ Region negated_clip_region = it->second;
+
+#ifndef NDEBUG
+ Region positive_clip = content_tiling_rect;
+ positive_clip.Subtract(negated_clip_region);
+ // Make sure we never rasterize the same region twice.
+ DCHECK(!total_clip.Intersects(positive_clip));
+ total_clip.Union(positive_clip);
+#endif // NDEBUG
+
+ int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
+
+ for (int j = 0; j < repeat_count; ++j)
+ picture->Raster(canvas, callback, negated_clip_region, contents_scale);
+ }
+
+#ifndef NDEBUG
+ // Fill the clip with debug color. This allows us to
+ // distinguish between non painted areas and problems with missing
+ // pictures.
+ SkPaint paint;
+ for (Region::Iterator it(total_clip); it.has_rect(); it.next())
+ canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
+ paint.setColor(DebugColors::MissingPictureFillColor());
+ paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+ canvas->drawPaint(paint);
+#endif // NDEBUG
+}
+
+skia::RefPtr<SkPicture> PicturePileImpl::GetFlattenedPicture() {
+ TRACE_EVENT0("cc", "PicturePileImpl::GetFlattenedPicture");
+
+ gfx::Rect tiling_rect(tiling_.tiling_size());
+ SkPictureRecorder recorder;
+ SkCanvas* canvas =
+ recorder.beginRecording(tiling_rect.width(), tiling_rect.height());
+ if (!tiling_rect.IsEmpty())
+ PlaybackToCanvas(canvas, tiling_rect, 1.0);
+ skia::RefPtr<SkPicture> picture =
+ skia::AdoptRef(recorder.endRecordingAsPicture());
+
+ return picture;
+}
+
+size_t PicturePileImpl::GetPictureMemoryUsage() const {
+ return picture_memory_usage_;
+}
+
+void PicturePileImpl::PerformSolidColorAnalysis(
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ RasterSource::SolidColorAnalysis* analysis) const {
+ DCHECK(analysis);
+ TRACE_EVENT0("cc", "PicturePileImpl::PerformSolidColorAnalysis");
+
+ gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
+ content_rect, 1.0f / contents_scale);
+
+ layer_rect.Intersect(gfx::Rect(tiling_.tiling_size()));
+
+ skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height());
+
+ RasterForAnalysis(&canvas, layer_rect, 1.0f);
+
+ analysis->is_solid_color = canvas.GetColorIfSolid(&analysis->solid_color);
+}
+
+void PicturePileImpl::GatherPixelRefs(
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ std::vector<SkPixelRef*>* pixel_refs) const {
+ DCHECK_EQ(0u, pixel_refs->size());
+ for (PixelRefIterator iter(content_rect, contents_scale, this); iter;
+ ++iter) {
+ pixel_refs->push_back(*iter);
+ }
+}
+
+bool PicturePileImpl::CoversRect(const gfx::Rect& content_rect,
+ float contents_scale) const {
+ if (tiling_.tiling_size().IsEmpty())
+ return false;
+ gfx::Rect layer_rect =
+ gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale);
+ layer_rect.Intersect(gfx::Rect(tiling_.tiling_size()));
+
+ // Common case inside of viewport to avoid the slower map lookups.
+ if (recorded_viewport_.Contains(layer_rect)) {
+ // Sanity check that there are no false positives in recorded_viewport_.
+ DCHECK(CanRasterSlowTileCheck(layer_rect));
+ return true;
+ }
+
+ return CanRasterSlowTileCheck(layer_rect);
+}
+
+gfx::Size PicturePileImpl::GetSize() const {
+ return tiling_.tiling_size();
+}
+
+bool PicturePileImpl::IsSolidColor() const {
+ return is_solid_color_;
+}
+
+SkColor PicturePileImpl::GetSolidColor() const {
+ DCHECK(IsSolidColor());
+ return solid_color_;
+}
+
+bool PicturePileImpl::HasRecordings() const {
+ return has_any_recordings_;
+}
+
+gfx::Rect PicturePileImpl::PaddedRect(const PictureMapKey& key) const {
+ gfx::Rect padded_rect = tiling_.TileBounds(key.first, key.second);
+ padded_rect.Inset(-buffer_pixels(), -buffer_pixels(), -buffer_pixels(),
+ -buffer_pixels());
+ return padded_rect;
+}
+
+bool PicturePileImpl::CanRasterSlowTileCheck(
+ const gfx::Rect& layer_rect) const {
+ bool include_borders = false;
+ for (TilingData::Iterator tile_iter(&tiling_, layer_rect, include_borders);
+ tile_iter; ++tile_iter) {
+ PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
+ if (map_iter == picture_map_.end())
+ return false;
+ }
+ return true;
+}
+
+void PicturePileImpl::SetShouldAttemptToUseDistanceFieldText() {
+ should_attempt_to_use_distance_field_text_ = true;
+}
+
+bool PicturePileImpl::ShouldAttemptToUseDistanceFieldText() const {
+ return should_attempt_to_use_distance_field_text_;
+}
+
+void PicturePileImpl::AsValueInto(
+ base::trace_event::TracedValue* pictures) const {
+ gfx::Rect tiling_rect(tiling_.tiling_size());
+ std::set<const void*> appended_pictures;
+ bool include_borders = true;
+ for (TilingData::Iterator tile_iter(&tiling_, tiling_rect, include_borders);
+ tile_iter; ++tile_iter) {
+ PictureMap::const_iterator map_iter = picture_map_.find(tile_iter.index());
+ if (map_iter == picture_map_.end())
+ continue;
+
+ const Picture* picture = map_iter->second.get();
+ if (appended_pictures.count(picture) == 0) {
+ appended_pictures.insert(picture);
+ TracedValue::AppendIDRef(picture, pictures);
+ }
+ }
+}
+
+bool PicturePileImpl::CanUseLCDText() const {
+ return can_use_lcd_text_;
+}
+
+scoped_refptr<RasterSource> PicturePileImpl::CreateCloneWithoutLCDText() const {
+ DCHECK(CanUseLCDText());
+ bool can_use_lcd_text = false;
+ return scoped_refptr<RasterSource>(
+ new PicturePileImpl(this, can_use_lcd_text));
+}
+
+PicturePileImpl::PixelRefIterator::PixelRefIterator(
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ const PicturePileImpl* picture_pile)
+ : picture_pile_(picture_pile),
+ layer_rect_(
+ gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale)),
+ tile_iterator_(&picture_pile_->tiling_,
+ layer_rect_,
+ false /* include_borders */) {
+ // Early out if there isn't a single tile.
+ if (!tile_iterator_)
+ return;
+
+ AdvanceToTilePictureWithPixelRefs();
+}
+
+PicturePileImpl::PixelRefIterator::~PixelRefIterator() {
+}
+
+PicturePileImpl::PixelRefIterator&
+ PicturePileImpl::PixelRefIterator::operator++() {
+ ++pixel_ref_iterator_;
+ if (pixel_ref_iterator_)
+ return *this;
+
+ ++tile_iterator_;
+ AdvanceToTilePictureWithPixelRefs();
+ return *this;
+}
+
+void PicturePileImpl::PixelRefIterator::AdvanceToTilePictureWithPixelRefs() {
+ for (; tile_iterator_; ++tile_iterator_) {
+ PictureMap::const_iterator it =
+ picture_pile_->picture_map_.find(tile_iterator_.index());
+ if (it == picture_pile_->picture_map_.end())
+ continue;
+
+ const Picture* picture = it->second.get();
+ if ((processed_pictures_.count(picture) != 0) ||
+ !picture->WillPlayBackBitmaps())
+ continue;
+
+ processed_pictures_.insert(picture);
+ pixel_ref_iterator_ = picture->GetPixelRefMapIterator(layer_rect_);
+ if (pixel_ref_iterator_)
+ break;
+ }
+}
+
+void PicturePileImpl::DidBeginTracing() {
+ std::set<const void*> processed_pictures;
+ for (const auto& map_pair : picture_map_) {
+ const Picture* picture = map_pair.second.get();
+ if (processed_pictures.count(picture) == 0) {
+ picture->EmitTraceSnapshot();
+ processed_pictures.insert(picture);
+ }
+ }
+}
+
+} // namespace cc
diff --git a/cc/playback/picture_pile_impl.h b/cc/playback/picture_pile_impl.h
new file mode 100644
index 0000000..e8be323
--- /dev/null
+++ b/cc/playback/picture_pile_impl.h
@@ -0,0 +1,159 @@
+// Copyright 2012 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_PLAYBACK_PICTURE_PILE_IMPL_H_
+#define CC_PLAYBACK_PICTURE_PILE_IMPL_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "base/time/time.h"
+#include "cc/base/cc_export.h"
+#include "cc/debug/rendering_stats_instrumentation.h"
+#include "cc/playback/picture_pile.h"
+#include "cc/playback/pixel_ref_map.h"
+#include "cc/playback/raster_source.h"
+#include "skia/ext/analysis_canvas.h"
+#include "skia/ext/refptr.h"
+
+class SkCanvas;
+class SkPicture;
+class SkPixelRef;
+
+namespace gfx {
+class Rect;
+}
+
+namespace cc {
+
+class CC_EXPORT PicturePileImpl : public RasterSource {
+ public:
+ static scoped_refptr<PicturePileImpl> CreateFromPicturePile(
+ const PicturePile* other,
+ bool can_use_lcd_text);
+
+ // RasterSource overrides. See RasterSource header for full description.
+ // When slow-down-raster-scale-factor is set to a value greater than 1, the
+ // reported rasterize time (in stats_instrumentation) is the minimum measured
+ // value over all runs.
+ void PlaybackToCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const override;
+ void PlaybackToSharedCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const override;
+ void PerformSolidColorAnalysis(
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ RasterSource::SolidColorAnalysis* analysis) const override;
+ void GatherPixelRefs(const gfx::Rect& content_rect,
+ float contents_scale,
+ std::vector<SkPixelRef*>* pixel_refs) const override;
+ bool CoversRect(const gfx::Rect& content_rect,
+ float contents_scale) const override;
+ void SetShouldAttemptToUseDistanceFieldText() override;
+ bool ShouldAttemptToUseDistanceFieldText() const override;
+ gfx::Size GetSize() const override;
+ bool IsSolidColor() const override;
+ SkColor GetSolidColor() const override;
+ bool HasRecordings() const override;
+ bool CanUseLCDText() const override;
+ scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const override;
+
+ // Tracing functionality.
+ void DidBeginTracing() override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+ skia::RefPtr<SkPicture> GetFlattenedPicture() override;
+ size_t GetPictureMemoryUsage() const override;
+
+ // Iterator used to return SkPixelRefs from this picture pile.
+ // Public for testing.
+ class CC_EXPORT PixelRefIterator {
+ public:
+ PixelRefIterator(const gfx::Rect& content_rect,
+ float contents_scale,
+ const PicturePileImpl* picture_pile);
+ ~PixelRefIterator();
+
+ SkPixelRef* operator->() const { return *pixel_ref_iterator_; }
+ SkPixelRef* operator*() const { return *pixel_ref_iterator_; }
+ PixelRefIterator& operator++();
+ operator bool() const { return pixel_ref_iterator_; }
+
+ private:
+ void AdvanceToTilePictureWithPixelRefs();
+
+ const PicturePileImpl* picture_pile_;
+ gfx::Rect layer_rect_;
+ TilingData::Iterator tile_iterator_;
+ PixelRefMap::Iterator pixel_ref_iterator_;
+ std::set<const void*> processed_pictures_;
+ };
+
+ protected:
+ friend class PicturePile;
+ friend class PixelRefIterator;
+
+ using PictureMapKey = PicturePile::PictureMapKey;
+ using PictureMap = PicturePile::PictureMap;
+
+ PicturePileImpl();
+ explicit PicturePileImpl(const PicturePile* other, bool can_use_lcd_text);
+ explicit PicturePileImpl(const PicturePileImpl* other, bool can_use_lcd_text);
+ ~PicturePileImpl() override;
+
+ int buffer_pixels() const { return tiling_.border_texels(); }
+
+ // These members are const as this raster source may be in use on another
+ // thread and so should not be touched after construction.
+ const PictureMap picture_map_;
+ const TilingData tiling_;
+ const SkColor background_color_;
+ const bool requires_clear_;
+ const bool can_use_lcd_text_;
+ const bool is_solid_color_;
+ const SkColor solid_color_;
+ const gfx::Rect recorded_viewport_;
+ const bool has_any_recordings_;
+ const bool clear_canvas_with_debug_color_;
+ const float min_contents_scale_;
+ const int slow_down_raster_scale_factor_for_debug_;
+ // TODO(enne/vmiura): this has a read/write race between raster and compositor
+ // threads with multi-threaded Ganesh. Make this const or remove it.
+ bool should_attempt_to_use_distance_field_text_;
+
+ size_t picture_memory_usage_;
+
+ private:
+ typedef std::map<const Picture*, Region> PictureRegionMap;
+
+ // Called when analyzing a tile. We can use AnalysisCanvas as
+ // SkDrawPictureCallback, which allows us to early out from analysis.
+ void RasterForAnalysis(skia::AnalysisCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const;
+
+ void CoalesceRasters(const gfx::Rect& canvas_rect,
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ PictureRegionMap* result) const;
+
+ void RasterCommon(SkCanvas* canvas,
+ SkDrawPictureCallback* callback,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const;
+
+ // An internal CanRaster check that goes to the picture_map rather than
+ // using the recorded_viewport hint.
+ bool CanRasterSlowTileCheck(const gfx::Rect& layer_rect) const;
+
+ gfx::Rect PaddedRect(const PictureMapKey& key) const;
+
+ DISALLOW_COPY_AND_ASSIGN(PicturePileImpl);
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_PICTURE_PILE_IMPL_H_
diff --git a/cc/playback/picture_pile_impl_perftest.cc b/cc/playback/picture_pile_impl_perftest.cc
new file mode 100644
index 0000000..a70fe68
--- /dev/null
+++ b/cc/playback/picture_pile_impl_perftest.cc
@@ -0,0 +1,83 @@
+// Copyright 2014 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/playback/picture_pile_impl.h"
+
+#include "cc/debug/lap_timer.h"
+#include "cc/test/fake_picture_pile_impl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+namespace cc {
+namespace {
+
+const int kTimeLimitMillis = 2000;
+const int kWarmupRuns = 5;
+const int kTimeCheckInterval = 10;
+
+const int kTileSize = 100;
+const int kLayerSize = 1000;
+
+class PicturePileImplPerfTest : public testing::Test {
+ public:
+ PicturePileImplPerfTest()
+ : timer_(kWarmupRuns,
+ base::TimeDelta::FromMilliseconds(kTimeLimitMillis),
+ kTimeCheckInterval) {}
+
+ void RunAnalyzeTest(const std::string& test_name, float contents_scale) {
+ scoped_refptr<PicturePileImpl> pile = FakePicturePileImpl::CreateFilledPile(
+ gfx::Size(kTileSize, kTileSize), gfx::Size(kLayerSize, kLayerSize));
+ // Content rect that will align with top-left tile at scale 1.0.
+ gfx::Rect content_rect(0, 0, kTileSize, kTileSize);
+
+ RasterSource::SolidColorAnalysis analysis;
+ timer_.Reset();
+ do {
+ pile->PerformSolidColorAnalysis(content_rect, contents_scale, &analysis);
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
+
+ perf_test::PrintResult(
+ "analyze", "", test_name, timer_.LapsPerSecond(), "runs/s", true);
+ }
+
+ void RunRasterTest(const std::string& test_name, float contents_scale) {
+ scoped_refptr<PicturePileImpl> pile = FakePicturePileImpl::CreateFilledPile(
+ gfx::Size(kTileSize, kTileSize), gfx::Size(kLayerSize, kLayerSize));
+ // Content rect that will align with top-left tile at scale 1.0.
+ gfx::Rect content_rect(0, 0, kTileSize, kTileSize);
+
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(1, 1);
+ SkCanvas canvas(bitmap);
+
+ timer_.Reset();
+ do {
+ pile->PlaybackToCanvas(&canvas, content_rect, contents_scale);
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
+
+ perf_test::PrintResult(
+ "raster", "", test_name, timer_.LapsPerSecond(), "runs/s", true);
+ }
+
+ private:
+ LapTimer timer_;
+};
+
+TEST_F(PicturePileImplPerfTest, Analyze) {
+ RunAnalyzeTest("1", 1.0f);
+ RunAnalyzeTest("4", 0.5f);
+ RunAnalyzeTest("100", 0.1f);
+}
+
+TEST_F(PicturePileImplPerfTest, Raster) {
+ RunRasterTest("1", 1.0f);
+ RunRasterTest("4", 0.5f);
+ RunRasterTest("100", 0.1f);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/playback/picture_pile_impl_unittest.cc b/cc/playback/picture_pile_impl_unittest.cc
new file mode 100644
index 0000000..14b06ee
--- /dev/null
+++ b/cc/playback/picture_pile_impl_unittest.cc
@@ -0,0 +1,549 @@
+// 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/memory/scoped_ptr.h"
+#include "cc/test/fake_picture_pile_impl.h"
+#include "cc/test/skia_common.h"
+#include "skia/ext/refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size_conversions.h"
+
+namespace cc {
+namespace {
+
+TEST(PicturePileImplTest, AnalyzeIsSolidUnscaled) {
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+
+ scoped_ptr<FakePicturePile> recording_source =
+ FakePicturePile::CreateFilledPile(tile_size, layer_bounds);
+
+ SkPaint solid_paint;
+ SkColor solid_color = SkColorSetARGB(255, 12, 23, 34);
+ solid_paint.setColor(solid_color);
+
+ SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67);
+ SkPaint non_solid_paint;
+ non_solid_paint.setColor(non_solid_color);
+
+ recording_source->add_draw_rect_with_paint(gfx::Rect(0, 0, 400, 400),
+ solid_paint);
+ recording_source->Rerecord();
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+
+ // Ensure everything is solid.
+ for (int y = 0; y <= 300; y += 100) {
+ for (int x = 0; x <= 300; x += 100) {
+ RasterSource::SolidColorAnalysis analysis;
+ gfx::Rect rect(x, y, 100, 100);
+ pile->PerformSolidColorAnalysis(rect, 1.0, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color) << rect.ToString();
+ EXPECT_EQ(analysis.solid_color, solid_color) << rect.ToString();
+ }
+ }
+
+ // Add one non-solid pixel and recreate the raster source.
+ recording_source->add_draw_rect_with_paint(gfx::Rect(50, 50, 1, 1),
+ non_solid_paint);
+ recording_source->Rerecord();
+ pile = FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+
+ RasterSource::SolidColorAnalysis analysis;
+ pile->PerformSolidColorAnalysis(gfx::Rect(0, 0, 100, 100), 1.0, &analysis);
+ EXPECT_FALSE(analysis.is_solid_color);
+
+ pile->PerformSolidColorAnalysis(gfx::Rect(100, 0, 100, 100), 1.0, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, solid_color);
+
+ // Boundaries should be clipped.
+ analysis.is_solid_color = false;
+ pile->PerformSolidColorAnalysis(gfx::Rect(350, 0, 100, 100), 1.0, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, solid_color);
+
+ analysis.is_solid_color = false;
+ pile->PerformSolidColorAnalysis(gfx::Rect(0, 350, 100, 100), 1.0, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, solid_color);
+
+ analysis.is_solid_color = false;
+ pile->PerformSolidColorAnalysis(gfx::Rect(350, 350, 100, 100), 1.0,
+ &analysis);
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, solid_color);
+}
+
+TEST(PicturePileImplTest, AnalyzeIsSolidScaled) {
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+
+ scoped_ptr<FakePicturePile> recording_source =
+ FakePicturePile::CreateFilledPile(tile_size, layer_bounds);
+
+ SkColor solid_color = SkColorSetARGB(255, 12, 23, 34);
+ SkPaint solid_paint;
+ solid_paint.setColor(solid_color);
+
+ SkColor non_solid_color = SkColorSetARGB(128, 45, 56, 67);
+ SkPaint non_solid_paint;
+ non_solid_paint.setColor(non_solid_color);
+
+ recording_source->add_draw_rect_with_paint(gfx::Rect(0, 0, 400, 400),
+ solid_paint);
+ recording_source->Rerecord();
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+
+ // Ensure everything is solid.
+ for (int y = 0; y <= 30; y += 10) {
+ for (int x = 0; x <= 30; x += 10) {
+ RasterSource::SolidColorAnalysis analysis;
+ gfx::Rect rect(x, y, 10, 10);
+ pile->PerformSolidColorAnalysis(rect, 0.1f, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color) << rect.ToString();
+ EXPECT_EQ(analysis.solid_color, solid_color) << rect.ToString();
+ }
+ }
+
+ // Add one non-solid pixel and recreate the raster source.
+ recording_source->add_draw_rect_with_paint(gfx::Rect(50, 50, 1, 1),
+ non_solid_paint);
+ recording_source->Rerecord();
+ pile = FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+
+ RasterSource::SolidColorAnalysis analysis;
+ pile->PerformSolidColorAnalysis(gfx::Rect(0, 0, 10, 10), 0.1f, &analysis);
+ EXPECT_FALSE(analysis.is_solid_color);
+
+ pile->PerformSolidColorAnalysis(gfx::Rect(10, 0, 10, 10), 0.1f, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, solid_color);
+
+ // Boundaries should be clipped.
+ analysis.is_solid_color = false;
+ pile->PerformSolidColorAnalysis(gfx::Rect(35, 0, 10, 10), 0.1f, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, solid_color);
+
+ analysis.is_solid_color = false;
+ pile->PerformSolidColorAnalysis(gfx::Rect(0, 35, 10, 10), 0.1f, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, solid_color);
+
+ analysis.is_solid_color = false;
+ pile->PerformSolidColorAnalysis(gfx::Rect(35, 35, 10, 10), 0.1f, &analysis);
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, solid_color);
+}
+
+TEST(PicturePileImplTest, AnalyzeIsSolidEmpty) {
+ gfx::Size tile_size(100, 100);
+ gfx::Size layer_bounds(400, 400);
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds);
+ RasterSource::SolidColorAnalysis analysis;
+ EXPECT_FALSE(analysis.is_solid_color);
+
+ pile->PerformSolidColorAnalysis(gfx::Rect(0, 0, 400, 400), 1.f, &analysis);
+
+ EXPECT_TRUE(analysis.is_solid_color);
+ EXPECT_EQ(analysis.solid_color, SkColorSetARGB(0, 0, 0, 0));
+}
+
+TEST(PicturePileImplTest, PixelRefIteratorDiscardableRefsOneTile) {
+ gfx::Size tile_size(256, 256);
+ gfx::Size layer_bounds(512, 512);
+
+ scoped_ptr<FakePicturePile> recording_source =
+ FakePicturePile::CreateFilledPile(tile_size, layer_bounds);
+
+ SkBitmap discardable_bitmap[2][2];
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[0][0]);
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[0][1]);
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[1][1]);
+
+ // Discardable pixel refs are found in the following cells:
+ // |---|---|
+ // | x | x |
+ // |---|---|
+ // | | x |
+ // |---|---|
+ recording_source->add_draw_bitmap(discardable_bitmap[0][0], gfx::Point(0, 0));
+ recording_source->add_draw_bitmap(discardable_bitmap[0][1],
+ gfx::Point(260, 0));
+ recording_source->add_draw_bitmap(discardable_bitmap[1][1],
+ gfx::Point(260, 260));
+ recording_source->SetGatherPixelRefs(true);
+ recording_source->Rerecord();
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+
+ // Tile sized iterators. These should find only one pixel ref.
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(0, 0, 256, 256), 1.0, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][0].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(0, 0, 512, 512), 2.0, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][0].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(0, 0, 128, 128), 0.5, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][0].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ // Shifted tile sized iterators. These should find only one pixel ref.
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(260, 260, 256, 256), 1.0, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][1].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(520, 520, 512, 512), 2.0, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][1].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(130, 130, 128, 128), 0.5, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][1].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ // Ensure there's no discardable pixel refs in the empty cell
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(0, 256, 256, 256), 1.0, pile.get());
+ EXPECT_FALSE(iterator);
+ }
+ // Layer sized iterators. These should find three pixel ref.
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(0, 0, 512, 512), 1.0, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][1].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][1].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(0, 0, 1024, 1024), 2.0, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][1].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][1].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ {
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(0, 0, 256, 256), 0.5, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][1].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][1].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+
+ // Copy test.
+ PicturePileImpl::PixelRefIterator iterator(
+ gfx::Rect(0, 0, 512, 512), 1.0, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0][1].pixelRef());
+
+ // copy now points to the same spot as iterator,
+ // but both can be incremented independently.
+ PicturePileImpl::PixelRefIterator copy = iterator;
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][1].pixelRef());
+ EXPECT_FALSE(++iterator);
+
+ EXPECT_TRUE(copy);
+ EXPECT_TRUE(*copy == discardable_bitmap[0][1].pixelRef());
+ EXPECT_TRUE(++copy);
+ EXPECT_TRUE(*copy == discardable_bitmap[1][1].pixelRef());
+ EXPECT_FALSE(++copy);
+}
+
+TEST(PicturePileImplTest, RasterFullContents) {
+ gfx::Size tile_size(1000, 1000);
+ gfx::Size layer_bounds(3, 5);
+ float contents_scale = 1.5f;
+ float raster_divisions = 2.f;
+
+ scoped_ptr<FakePicturePile> recording_source =
+ FakePicturePile::CreateFilledPile(tile_size, layer_bounds);
+ recording_source->SetBackgroundColor(SK_ColorBLACK);
+ recording_source->SetIsSolidColor(false);
+ recording_source->SetRequiresClear(false);
+ recording_source->SetClearCanvasWithDebugColor(false);
+
+ // Because the caller sets content opaque, it also promises that it
+ // has at least filled in layer_bounds opaquely.
+ SkPaint white_paint;
+ white_paint.setColor(SK_ColorWHITE);
+ recording_source->add_draw_rect_with_paint(gfx::Rect(layer_bounds),
+ white_paint);
+
+ recording_source->SetMinContentsScale(contents_scale);
+ recording_source->Rerecord();
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+
+ gfx::Size content_bounds(
+ gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale)));
+
+ // Simulate drawing into different tiles at different offsets.
+ int step_x = std::ceil(content_bounds.width() / raster_divisions);
+ int step_y = std::ceil(content_bounds.height() / raster_divisions);
+ for (int offset_x = 0; offset_x < content_bounds.width();
+ offset_x += step_x) {
+ for (int offset_y = 0; offset_y < content_bounds.height();
+ offset_y += step_y) {
+ gfx::Rect content_rect(offset_x, offset_y, step_x, step_y);
+ content_rect.Intersect(gfx::Rect(content_bounds));
+
+ // Simulate a canvas rect larger than the content rect. Every pixel
+ // up to one pixel outside the content rect is guaranteed to be opaque.
+ // Outside of that is undefined.
+ gfx::Rect canvas_rect(content_rect);
+ canvas_rect.Inset(0, 0, -1, -1);
+
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(canvas_rect.width(), canvas_rect.height());
+ SkCanvas canvas(bitmap);
+ canvas.clear(SK_ColorTRANSPARENT);
+
+ pile->PlaybackToCanvas(&canvas, canvas_rect, contents_scale);
+
+ SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
+ int num_pixels = bitmap.width() * bitmap.height();
+ bool all_white = true;
+ for (int i = 0; i < num_pixels; ++i) {
+ EXPECT_EQ(SkColorGetA(pixels[i]), 255u);
+ all_white &= (SkColorGetR(pixels[i]) == 255);
+ all_white &= (SkColorGetG(pixels[i]) == 255);
+ all_white &= (SkColorGetB(pixels[i]) == 255);
+ }
+
+ // If the canvas doesn't extend past the edge of the content,
+ // it should be entirely white. Otherwise, the edge of the content
+ // will be non-white.
+ EXPECT_EQ(all_white, gfx::Rect(content_bounds).Contains(canvas_rect));
+ }
+ }
+}
+
+TEST(PicturePileImpl, RasterContentsTransparent) {
+ gfx::Size tile_size(1000, 1000);
+ gfx::Size layer_bounds(5, 3);
+ float contents_scale = 0.5f;
+
+ scoped_ptr<FakePicturePile> recording_source =
+ FakePicturePile::CreateFilledPile(tile_size, layer_bounds);
+ recording_source->SetBackgroundColor(SK_ColorTRANSPARENT);
+ recording_source->SetRequiresClear(true);
+ recording_source->SetMinContentsScale(contents_scale);
+ recording_source->SetClearCanvasWithDebugColor(false);
+ recording_source->Rerecord();
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+ gfx::Size content_bounds(
+ gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale)));
+
+ gfx::Rect canvas_rect(content_bounds);
+ canvas_rect.Inset(0, 0, -1, -1);
+
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(canvas_rect.width(), canvas_rect.height());
+ SkCanvas canvas(bitmap);
+
+ pile->PlaybackToCanvas(&canvas, canvas_rect, contents_scale);
+
+ SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels());
+ int num_pixels = bitmap.width() * bitmap.height();
+ for (int i = 0; i < num_pixels; ++i) {
+ EXPECT_EQ(SkColorGetA(pixels[i]), 0u);
+ }
+}
+
+class OverlapTest : public ::testing::TestWithParam<float> {
+ public:
+ static float MinContentsScale() { return 1.f / 4.f; }
+};
+
+TEST_P(OverlapTest, NoOverlap) {
+ gfx::Size tile_size(10, 10);
+ gfx::Size layer_bounds(30, 30);
+ gfx::Size bigger_than_layer_bounds(300, 300);
+ float contents_scale = GetParam();
+ // Pick an opaque color to not have to deal with premultiplication off-by-one.
+ SkColor test_color = SkColorSetARGB(255, 45, 56, 67);
+
+ scoped_ptr<FakePicturePile> recording_source =
+ FakePicturePile::CreateFilledPile(tile_size, layer_bounds);
+ recording_source->SetBackgroundColor(SK_ColorTRANSPARENT);
+ recording_source->SetRequiresClear(true);
+ recording_source->SetMinContentsScale(MinContentsScale());
+ recording_source->SetClearCanvasWithDebugColor(true);
+
+ SkPaint color_paint;
+ color_paint.setColor(test_color);
+ // Additive paint, so that if two paints overlap, the color will change.
+ color_paint.setXfermodeMode(SkXfermode::kPlus_Mode);
+ // Paint outside the layer to make sure that blending works.
+ recording_source->add_draw_rect_with_paint(
+ gfx::RectF(bigger_than_layer_bounds), color_paint);
+ recording_source->Rerecord();
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+ gfx::Size content_bounds(
+ gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale)));
+
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(content_bounds.width(), content_bounds.height());
+ SkCanvas canvas(bitmap);
+
+ pile->PlaybackToCanvas(&canvas, gfx::Rect(content_bounds), contents_scale);
+
+ for (int y = 0; y < bitmap.height(); y++) {
+ for (int x = 0; x < bitmap.width(); x++) {
+ SkColor color = bitmap.getColor(x, y);
+ EXPECT_EQ(SkColorGetR(test_color), SkColorGetR(color)) << "x: " << x
+ << ", y: " << y;
+ EXPECT_EQ(SkColorGetG(test_color), SkColorGetG(color)) << "x: " << x
+ << ", y: " << y;
+ EXPECT_EQ(SkColorGetB(test_color), SkColorGetB(color)) << "x: " << x
+ << ", y: " << y;
+ EXPECT_EQ(SkColorGetA(test_color), SkColorGetA(color)) << "x: " << x
+ << ", y: " << y;
+ if (test_color != color)
+ break;
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(PicturePileImpl,
+ OverlapTest,
+ ::testing::Values(1.f, 0.873f, 1.f / 4.f, 4.f));
+
+TEST(PicturePileImplTest, PixelRefIteratorBorders) {
+ // 3 tile width / 1 tile height pile
+ gfx::Size tile_size(128, 128);
+ gfx::Size layer_bounds(320, 128);
+
+ // Fake picture pile uses a tile grid the size of the tile. So,
+ // any iteration that intersects with a tile will return all pixel refs
+ // inside of it.
+ scoped_ptr<FakePicturePile> recording_source =
+ FakePicturePile::CreateFilledPile(tile_size, layer_bounds);
+ recording_source->SetMinContentsScale(0.5f);
+
+ // Bitmaps 0-2 are exactly on tiles 0-2, so that they overlap the borders
+ // of adjacent tiles.
+ gfx::Rect bitmap_rects[] = {
+ recording_source->tiling().TileBounds(0, 0),
+ recording_source->tiling().TileBounds(1, 0),
+ recording_source->tiling().TileBounds(2, 0),
+ };
+ SkBitmap discardable_bitmap[arraysize(bitmap_rects)];
+
+ for (size_t i = 0; i < arraysize(bitmap_rects); ++i) {
+ CreateBitmap(bitmap_rects[i].size(), "discardable", &discardable_bitmap[i]);
+ recording_source->add_draw_bitmap(discardable_bitmap[i],
+ bitmap_rects[i].origin());
+ }
+
+ recording_source->SetGatherPixelRefs(true);
+ recording_source->Rerecord();
+
+ scoped_refptr<FakePicturePileImpl> pile =
+ FakePicturePileImpl::CreateFromPile(recording_source.get(), nullptr);
+
+ // Sanity check that bitmaps 0-2 intersect the borders of their adjacent
+ // tiles, but not the actual tiles.
+ EXPECT_TRUE(
+ bitmap_rects[0].Intersects(pile->tiling().TileBoundsWithBorder(1, 0)));
+ EXPECT_FALSE(bitmap_rects[0].Intersects(pile->tiling().TileBounds(1, 0)));
+ EXPECT_TRUE(
+ bitmap_rects[1].Intersects(pile->tiling().TileBoundsWithBorder(0, 0)));
+ EXPECT_FALSE(bitmap_rects[1].Intersects(pile->tiling().TileBounds(0, 0)));
+ EXPECT_TRUE(
+ bitmap_rects[1].Intersects(pile->tiling().TileBoundsWithBorder(2, 0)));
+ EXPECT_FALSE(bitmap_rects[1].Intersects(pile->tiling().TileBounds(2, 0)));
+ EXPECT_TRUE(
+ bitmap_rects[2].Intersects(pile->tiling().TileBoundsWithBorder(1, 0)));
+ EXPECT_FALSE(bitmap_rects[2].Intersects(pile->tiling().TileBounds(1, 0)));
+
+ // Tile-sized iterators.
+ {
+ // Because tile 0's borders extend onto tile 1, it will include both
+ // bitmap 0 and 1. However, it should *not* include bitmap 2.
+ PicturePileImpl::PixelRefIterator iterator(
+ pile->tiling().TileBounds(0, 0), 1.f, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ {
+ // Tile 1 + borders hits all bitmaps.
+ PicturePileImpl::PixelRefIterator iterator(
+ pile->tiling().TileBounds(1, 0), 1.f, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[0].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+ {
+ // Tile 2 should not include bitmap 0, which is only on tile 0 and the
+ // borders of tile 1.
+ PicturePileImpl::PixelRefIterator iterator(
+ pile->tiling().TileBounds(2, 0), 1.f, pile.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/playback/picture_pile_unittest.cc b/cc/playback/picture_pile_unittest.cc
new file mode 100644
index 0000000..f929aef
--- /dev/null
+++ b/cc/playback/picture_pile_unittest.cc
@@ -0,0 +1,1345 @@
+// 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 <map>
+#include <utility>
+
+#include "cc/playback/picture_pile.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_picture_pile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/size_conversions.h"
+
+namespace cc {
+namespace {
+
+class PicturePileTestBase {
+ public:
+ PicturePileTestBase()
+ : min_scale_(0.125),
+ pile_(min_scale_, gfx::Size(1000, 1000)),
+ frame_number_(0) {}
+
+ void InitializeData() {
+ pile_.SetTileGridSize(gfx::Size(1000, 1000));
+ pile_.SetMinContentsScale(min_scale_);
+ client_ = FakeContentLayerClient();
+ SetTilingSize(pile_.tiling().max_texture_size());
+ }
+
+ void SetTilingSize(const gfx::Size& tiling_size) {
+ Region invalidation;
+ gfx::Rect viewport_rect(tiling_size);
+ UpdateAndExpandInvalidation(&invalidation, tiling_size, viewport_rect);
+ }
+
+ gfx::Size tiling_size() const { return pile_.GetSize(); }
+ gfx::Rect tiling_rect() const { return gfx::Rect(pile_.GetSize()); }
+
+ bool UpdateAndExpandInvalidation(Region* invalidation,
+ const gfx::Size& layer_size,
+ const gfx::Rect& visible_layer_rect) {
+ frame_number_++;
+ return pile_.UpdateAndExpandInvalidation(&client_, invalidation, layer_size,
+ visible_layer_rect, frame_number_,
+ RecordingSource::RECORD_NORMALLY);
+ }
+
+ bool UpdateWholePile() {
+ Region invalidation = tiling_rect();
+ bool result = UpdateAndExpandInvalidation(&invalidation, tiling_size(),
+ tiling_rect());
+ EXPECT_EQ(tiling_rect().ToString(), invalidation.ToString());
+ return result;
+ }
+
+ FakeContentLayerClient client_;
+ float min_scale_;
+ FakePicturePile pile_;
+ int frame_number_;
+};
+
+class PicturePileTest : public PicturePileTestBase, public testing::Test {
+ public:
+ void SetUp() override { InitializeData(); }
+};
+
+TEST_F(PicturePileTest, InvalidationOnTileBorderOutsideInterestRect) {
+ // Don't expand the interest rect past what we invalidate.
+ pile_.SetPixelRecordDistance(0);
+
+ gfx::Size tile_size(100, 100);
+ pile_.tiling().SetMaxTextureSize(tile_size);
+
+ gfx::Size pile_size(400, 400);
+ SetTilingSize(pile_size);
+
+ // We have multiple tiles.
+ EXPECT_GT(pile_.tiling().num_tiles_x(), 2);
+ EXPECT_GT(pile_.tiling().num_tiles_y(), 2);
+
+ // Record everything.
+ Region invalidation(tiling_rect());
+ UpdateAndExpandInvalidation(&invalidation, tiling_size(), tiling_rect());
+
+ // +----------+-----------------+-----------+
+ // | | VVVV 1,0| |
+ // | | VVVV | |
+ // | | VVVV | |
+ // | ...|.................|... |
+ // | ...|.................|... |
+ // +----------+-----------------+-----------+
+ // | ...| |... |
+ // | ...| |... |
+ // | ...| |... |
+ // | ...| |... |
+ // | ...| 1,1|... |
+ // +----------+-----------------+-----------+
+ // | ...|.................|... |
+ // | ...|.................|... |
+ // +----------+-----------------+-----------+
+ //
+ // .. = border pixels for tile 1,1
+ // VV = interest rect (what we will record)
+ //
+ // The first invalidation is inside VV, so it does not touch border pixels of
+ // tile 1,1.
+ //
+ // The second invalidation goes below VV into the .. border pixels of 1,1.
+
+ // This is the VV interest rect which will be entirely inside 1,0 and not
+ // touch the border of 1,1.
+ gfx::Rect interest_rect(
+ pile_.tiling().TilePositionX(1) + pile_.tiling().border_texels(),
+ 0,
+ 10,
+ pile_.tiling().TileSizeY(0) - pile_.tiling().border_texels());
+
+ // Invalidate tile 1,0 only. This is a rect that avoids the borders of any
+ // other tiles.
+ gfx::Rect invalidate_tile = interest_rect;
+ // This should cause the tile 1,0 to be invalidated and re-recorded. The
+ // invalidation did not need to be expanded.
+ invalidation = invalidate_tile;
+ UpdateAndExpandInvalidation(&invalidation, tiling_size(), interest_rect);
+ EXPECT_EQ(invalidate_tile, invalidation);
+
+ // Invalidate tile 1,0 and 1,1 by invalidating something that only touches the
+ // border of 1,1 (and is inside the tile bounds of 1,0). This is a 10px wide
+ // strip from the top of the tiling onto the border pixels of tile 1,1 that
+ // avoids border pixels of any other tiles.
+ gfx::Rect invalidate_border = interest_rect;
+ invalidate_border.Inset(0, 0, 0, -1);
+ // This should cause the tile 1,0 and 1,1 to be invalidated. The 1,1 tile will
+ // not be re-recorded since it does not touch the interest rect, so the
+ // invalidation should be expanded to cover all of 1,1.
+ invalidation = invalidate_border;
+ UpdateAndExpandInvalidation(&invalidation, tiling_size(), interest_rect);
+ Region expected_invalidation = invalidate_border;
+ expected_invalidation.Union(pile_.tiling().TileBounds(1, 1));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+}
+
+TEST_F(PicturePileTest, SmallInvalidateInflated) {
+ // Invalidate something inside a tile.
+ Region invalidate_rect(gfx::Rect(50, 50, 1, 1));
+ UpdateAndExpandInvalidation(&invalidate_rect, tiling_size(), tiling_rect());
+ EXPECT_EQ(gfx::Rect(50, 50, 1, 1).ToString(), invalidate_rect.ToString());
+
+ EXPECT_EQ(1, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(1, pile_.tiling().num_tiles_y());
+
+ PicturePile::PictureMapKey key = FakePicturePile::PictureMapKey(0, 0);
+ PicturePile::PictureMap::iterator it = pile_.picture_map().find(key);
+ EXPECT_TRUE(it != pile_.picture_map().end());
+ const Picture* picture = it->second.get();
+ EXPECT_TRUE(picture);
+
+ gfx::Rect picture_rect =
+ gfx::ScaleToEnclosedRect(picture->LayerRect(), min_scale_);
+
+ // The the picture should be large enough that scaling it never makes a rect
+ // smaller than 1 px wide or tall.
+ EXPECT_FALSE(picture_rect.IsEmpty()) << "Picture rect "
+ << picture_rect.ToString();
+}
+
+TEST_F(PicturePileTest, LargeInvalidateInflated) {
+ // Invalidate something inside a tile.
+ Region invalidate_rect(gfx::Rect(50, 50, 100, 100));
+ UpdateAndExpandInvalidation(&invalidate_rect, tiling_size(), tiling_rect());
+ EXPECT_EQ(gfx::Rect(50, 50, 100, 100).ToString(), invalidate_rect.ToString());
+
+ EXPECT_EQ(1, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(1, pile_.tiling().num_tiles_y());
+
+ PicturePile::PictureMapKey key = FakePicturePile::PictureMapKey(0, 0);
+ PicturePile::PictureMap::iterator it = pile_.picture_map().find(key);
+ EXPECT_TRUE(it != pile_.picture_map().end());
+ const Picture* picture = it->second.get();
+ EXPECT_TRUE(picture);
+
+ int expected_inflation = pile_.buffer_pixels();
+
+ gfx::Rect base_picture_rect(tiling_size());
+ base_picture_rect.Inset(-expected_inflation, -expected_inflation);
+ EXPECT_EQ(base_picture_rect.ToString(), picture->LayerRect().ToString());
+}
+
+TEST_F(PicturePileTest, ClearingInvalidatesRecordedRect) {
+ gfx::Rect rect(0, 0, 5, 5);
+ EXPECT_TRUE(pile_.CanRasterLayerRect(rect));
+ EXPECT_TRUE(pile_.CanRasterSlowTileCheck(rect));
+
+ pile_.Clear();
+
+ // Make sure both the cache-aware check (using recorded region) and the normal
+ // check are both false after clearing.
+ EXPECT_FALSE(pile_.CanRasterLayerRect(rect));
+ EXPECT_FALSE(pile_.CanRasterSlowTileCheck(rect));
+}
+
+TEST_F(PicturePileTest, NoInvalidationValidViewport) {
+ // This test validates that the recorded_viewport cache of full tiles
+ // is still valid for some use cases. If it's not, it's a performance
+ // issue because CanRaster checks will go down the slow path.
+ EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty());
+
+ // No invalidation, same viewport.
+ Region invalidation;
+ UpdateAndExpandInvalidation(&invalidation, tiling_size(), tiling_rect());
+ EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty());
+ EXPECT_EQ(Region().ToString(), invalidation.ToString());
+
+ // Partial invalidation, same viewport.
+ invalidation = gfx::Rect(0, 0, 1, 1);
+ UpdateAndExpandInvalidation(&invalidation, tiling_size(), tiling_rect());
+ EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty());
+ EXPECT_EQ(gfx::Rect(0, 0, 1, 1).ToString(), invalidation.ToString());
+
+ // No invalidation, changing viewport.
+ invalidation = Region();
+ UpdateAndExpandInvalidation(&invalidation, tiling_size(),
+ gfx::Rect(5, 5, 5, 5));
+ EXPECT_TRUE(!pile_.recorded_viewport().IsEmpty());
+ EXPECT_EQ(Region().ToString(), invalidation.ToString());
+}
+
+TEST_F(PicturePileTest, BigFullLayerInvalidation) {
+ gfx::Size huge_layer_size(100000000, 100000000);
+ gfx::Rect viewport(300000, 400000, 5000, 6000);
+
+ // Resize the pile.
+ Region invalidation;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+
+ // Invalidating a huge layer should be fast.
+ base::TimeTicks start = base::TimeTicks::Now();
+ invalidation = gfx::Rect(huge_layer_size);
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+ base::TimeTicks end = base::TimeTicks::Now();
+ base::TimeDelta length = end - start;
+ // This is verrrry generous to avoid flake.
+ EXPECT_LT(length.InSeconds(), 5);
+}
+
+TEST_F(PicturePileTest, BigFullLayerInvalidationWithResizeGrow) {
+ gfx::Size huge_layer_size(100000000, 100000000);
+ gfx::Rect viewport(300000, 400000, 5000, 6000);
+
+ // Resize the pile.
+ Region invalidation;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+
+ // Resize the pile even larger, while invalidating everything in the old size.
+ // Invalidating the whole thing should be fast.
+ base::TimeTicks start = base::TimeTicks::Now();
+ gfx::Size bigger_layer_size(huge_layer_size.width() * 2,
+ huge_layer_size.height() * 2);
+ invalidation = gfx::Rect(huge_layer_size);
+ UpdateAndExpandInvalidation(&invalidation, bigger_layer_size, viewport);
+ base::TimeTicks end = base::TimeTicks::Now();
+ base::TimeDelta length = end - start;
+ // This is verrrry generous to avoid flake.
+ EXPECT_LT(length.InSeconds(), 5);
+}
+
+TEST_F(PicturePileTest, BigFullLayerInvalidationWithResizeShrink) {
+ gfx::Size huge_layer_size(100000000, 100000000);
+ gfx::Rect viewport(300000, 400000, 5000, 6000);
+
+ // Resize the pile.
+ Region invalidation;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+
+ // Resize the pile smaller, while invalidating everything in the new size.
+ // Invalidating the whole thing should be fast.
+ base::TimeTicks start = base::TimeTicks::Now();
+ gfx::Size smaller_layer_size(huge_layer_size.width() - 1000,
+ huge_layer_size.height() - 1000);
+ invalidation = gfx::Rect(smaller_layer_size);
+ UpdateAndExpandInvalidation(&invalidation, smaller_layer_size, viewport);
+ base::TimeTicks end = base::TimeTicks::Now();
+ base::TimeDelta length = end - start;
+ // This is verrrry generous to avoid flake.
+ EXPECT_LT(length.InSeconds(), 5);
+}
+
+TEST_F(PicturePileTest, InvalidationOutsideRecordingRect) {
+ gfx::Size huge_layer_size(10000000, 20000000);
+ gfx::Rect viewport(300000, 400000, 5000, 6000);
+
+ // Resize the pile and set up the interest rect.
+ Region invalidation;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+
+ // Invalidation inside the recording rect does not need to be expanded.
+ invalidation = viewport;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+ EXPECT_EQ(viewport.ToString(), invalidation.ToString());
+
+ // Invalidation outside the recording rect should expand to the tiles it
+ // covers.
+ gfx::Rect recorded_over_tiles =
+ pile_.tiling().ExpandRectToTileBounds(pile_.recorded_viewport());
+ gfx::Rect invalidation_outside(
+ recorded_over_tiles.right(), recorded_over_tiles.y(), 30, 30);
+ invalidation = invalidation_outside;
+ UpdateAndExpandInvalidation(&invalidation, huge_layer_size, viewport);
+ gfx::Rect expanded_recorded_viewport =
+ pile_.tiling().ExpandRectToTileBounds(pile_.recorded_viewport());
+ Region expected_invalidation =
+ pile_.tiling().ExpandRectToTileBounds(invalidation_outside);
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+}
+
+enum Corner {
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM_LEFT,
+ BOTTOM_RIGHT,
+};
+
+class PicturePileResizeCornerTest : public PicturePileTestBase,
+ public testing::TestWithParam<Corner> {
+ protected:
+ void SetUp() override { InitializeData(); }
+
+ static gfx::Rect CornerSinglePixelRect(Corner corner, const gfx::Size& s) {
+ switch (corner) {
+ case TOP_LEFT:
+ return gfx::Rect(0, 0, 1, 1);
+ case TOP_RIGHT:
+ return gfx::Rect(s.width() - 1, 0, 1, 1);
+ case BOTTOM_LEFT:
+ return gfx::Rect(0, s.height() - 1, 1, 1);
+ case BOTTOM_RIGHT:
+ return gfx::Rect(s.width() - 1, s.height() - 1, 1, 1);
+ }
+ NOTREACHED();
+ return gfx::Rect();
+ }
+};
+
+TEST_P(PicturePileResizeCornerTest, ResizePileOutsideInterestRect) {
+ Corner corner = GetParam();
+
+ // This size chosen to be larger than the interest rect size, which is
+ // at least kPixelDistanceToRecord * 2 in each dimension.
+ int tile_size = 100000;
+ // The small number subtracted keeps the last tile in each axis larger than
+ // the interest rect also.
+ int offset = -100;
+ gfx::Size base_tiling_size(6 * tile_size + offset, 6 * tile_size + offset);
+ gfx::Size grow_down_tiling_size(6 * tile_size + offset,
+ 8 * tile_size + offset);
+ gfx::Size grow_right_tiling_size(8 * tile_size + offset,
+ 6 * tile_size + offset);
+ gfx::Size grow_both_tiling_size(8 * tile_size + offset,
+ 8 * tile_size + offset);
+
+ Region invalidation;
+ Region expected_invalidation;
+
+ pile_.tiling().SetMaxTextureSize(gfx::Size(tile_size, tile_size));
+ SetTilingSize(base_tiling_size);
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ UpdateAndExpandInvalidation(
+ &invalidation,
+ grow_down_tiling_size,
+ CornerSinglePixelRect(corner, grow_down_tiling_size));
+
+ // We should have lost all of the recordings in the bottom row as none of them
+ // are in the current interest rect (which is either the above or below it).
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(8, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 6; ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_EQ(j < 5, it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated all new pixels in the recording.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // But the new pixels don't cover the whole bottom row.
+ gfx::Rect bottom_row = gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+ pile_.tiling().TileBounds(5, 5));
+ EXPECT_FALSE(expected_invalidation.Contains(bottom_row));
+ // We invalidated the entire old bottom row.
+ expected_invalidation.Union(bottom_row);
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation,
+ base_tiling_size,
+ CornerSinglePixelRect(corner, base_tiling_size));
+
+ // When shrinking, we should have lost all the recordings in the bottom row
+ // not touching the interest rect.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ case TOP_RIGHT:
+ expect_tile = j < 5;
+ break;
+ case BOTTOM_LEFT:
+ // The interest rect in the bottom left tile means we'll record it.
+ expect_tile = j < 5 || (j == 5 && i == 0);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = j < 5 || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.get());
+ }
+ }
+
+ // When shrinking, the previously exposed region is invalidated.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // The whole bottom row of tiles (except any with the interest rect) are
+ // dropped.
+ gfx::Rect bottom_row_minus_existing_corner = gfx::UnionRects(
+ pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5));
+ switch (corner) {
+ case TOP_LEFT:
+ case TOP_RIGHT:
+ // No tiles are kept in the changed region because it doesn't
+ // intersect with the interest rect.
+ break;
+ case BOTTOM_LEFT:
+ bottom_row_minus_existing_corner.Subtract(
+ pile_.tiling().TileBounds(0, 5));
+ break;
+ case BOTTOM_RIGHT:
+ bottom_row_minus_existing_corner.Subtract(
+ pile_.tiling().TileBounds(5, 5));
+ break;
+ }
+
+ expected_invalidation.Union(bottom_row_minus_existing_corner);
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(
+ &invalidation,
+ grow_right_tiling_size,
+ CornerSinglePixelRect(corner, grow_right_tiling_size));
+
+ // We should have lost all of the recordings in the right column as none of
+ // them are in the current interest rect (which is either entirely left or
+ // right of it).
+ EXPECT_EQ(8, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 6; ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_EQ(i < 5, it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated all new pixels in the recording.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // But the new pixels don't cover the whole right_column.
+ gfx::Rect right_column = gfx::UnionRects(pile_.tiling().TileBounds(5, 0),
+ pile_.tiling().TileBounds(5, 5));
+ EXPECT_FALSE(expected_invalidation.Contains(right_column));
+ // We invalidated the entire old right column.
+ expected_invalidation.Union(right_column);
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation,
+ base_tiling_size,
+ CornerSinglePixelRect(corner, base_tiling_size));
+
+ // When shrinking, we should have lost all the recordings in the right column
+ // not touching the interest rect.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ case BOTTOM_LEFT:
+ // No tiles are kept in the changed region because it doesn't
+ // intersect with the interest rect.
+ expect_tile = i < 5;
+ break;
+ case TOP_RIGHT:
+ // The interest rect in the top right tile means we'll record it.
+ expect_tile = i < 5 || (j == 0 && i == 5);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = i < 5 || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.get());
+ }
+ }
+
+ // When shrinking, the previously exposed region is invalidated.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // The whole right column of tiles (except for ones with the interest rect)
+ // are dropped.
+ gfx::Rect right_column_minus_existing_corner = gfx::UnionRects(
+ pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5));
+ switch (corner) {
+ case TOP_LEFT:
+ case BOTTOM_LEFT:
+ break;
+ case TOP_RIGHT:
+ right_column_minus_existing_corner.Subtract(
+ pile_.tiling().TileBounds(5, 0));
+ break;
+ case BOTTOM_RIGHT:
+ right_column_minus_existing_corner.Subtract(
+ pile_.tiling().TileBounds(5, 5));
+ break;
+ }
+ expected_invalidation.Union(right_column_minus_existing_corner);
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(
+ &invalidation,
+ grow_both_tiling_size,
+ CornerSinglePixelRect(corner, grow_both_tiling_size));
+
+ // We should have lost the recordings in the right column and bottom row.
+ EXPECT_EQ(8, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(8, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 6; ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_EQ(i < 5 && j < 5, it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated all new pixels in the recording.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // But the new pixels don't cover the whole right column or bottom row.
+ Region right_column_and_bottom_row =
+ UnionRegions(gfx::UnionRects(pile_.tiling().TileBounds(5, 0),
+ pile_.tiling().TileBounds(5, 5)),
+ gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+ pile_.tiling().TileBounds(5, 5)));
+ EXPECT_FALSE(expected_invalidation.Contains(right_column_and_bottom_row));
+ // We invalidated the entire old right column and the old bottom row.
+ expected_invalidation.Union(right_column_and_bottom_row);
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, base_tiling_size,
+ CornerSinglePixelRect(corner, base_tiling_size));
+
+ // We should have lost the recordings in the right column and bottom row,
+ // except where it intersects the interest rect.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ expect_tile = i < 5 && j < 5;
+ break;
+ case TOP_RIGHT:
+ // The interest rect in the top right tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5);
+ break;
+ case BOTTOM_LEFT:
+ // The interest rect in the bottom left tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.get()) << i << ","
+ << j;
+ }
+ }
+
+ // We invalidated all previous pixels in the recording.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+ gfx::Rect(base_tiling_size));
+ // The whole right column and bottom row of tiles (except for ones with the
+ // interest rect) are dropped.
+ Region right_column_and_bottom_row_minus_existing_corner =
+ right_column_and_bottom_row;
+ switch (corner) {
+ case TOP_LEFT:
+ break;
+ case BOTTOM_LEFT:
+ right_column_and_bottom_row_minus_existing_corner.Subtract(
+ pile_.tiling().TileBounds(0, 5));
+ break;
+ case TOP_RIGHT:
+ right_column_and_bottom_row_minus_existing_corner.Subtract(
+ pile_.tiling().TileBounds(5, 0));
+ break;
+ case BOTTOM_RIGHT:
+ right_column_and_bottom_row_minus_existing_corner.Subtract(
+ pile_.tiling().TileBounds(5, 5));
+ break;
+ }
+ expected_invalidation.Union(
+ right_column_and_bottom_row_minus_existing_corner);
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+}
+
+TEST_P(PicturePileResizeCornerTest, SmallResizePileOutsideInterestRect) {
+ Corner corner = GetParam();
+
+ // This size chosen to be larger than the interest rect size, which is
+ // at least kPixelDistanceToRecord * 2 in each dimension.
+ int tile_size = 100000;
+ // The small number subtracted keeps the last tile in each axis larger than
+ // the interest rect also.
+ int offset = -100;
+ gfx::Size base_tiling_size(6 * tile_size + offset, 6 * tile_size + offset);
+ gfx::Size grow_down_tiling_size(6 * tile_size + offset,
+ 6 * tile_size + offset + 5);
+ gfx::Size grow_right_tiling_size(6 * tile_size + offset + 5,
+ 6 * tile_size + offset);
+ gfx::Size grow_both_tiling_size(6 * tile_size + offset + 5,
+ 6 * tile_size + offset + 5);
+
+ Region invalidation;
+ Region expected_invalidation;
+
+ pile_.tiling().SetMaxTextureSize(gfx::Size(tile_size, tile_size));
+ SetTilingSize(base_tiling_size);
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // In this test (unlike the large resize test), as all growing and shrinking
+ // happens within tiles, the resulting invalidation is symmetrical, so use
+ // this enum to repeat the test both ways.
+ enum ChangeDirection { GROW, SHRINK, LAST_DIRECTION = SHRINK };
+
+ // Grow downward.
+ for (int dir = 0; dir <= LAST_DIRECTION; ++dir) {
+ gfx::Size new_tiling_size =
+ dir == GROW ? grow_down_tiling_size : base_tiling_size;
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, new_tiling_size,
+ CornerSinglePixelRect(corner, new_tiling_size));
+
+ // We should have lost the recordings in the bottom row that do not
+ // intersect the interest rect.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ case TOP_RIGHT:
+ expect_tile = j < 5;
+ break;
+ case BOTTOM_LEFT:
+ // The interest rect in the bottom left tile means we'll record it.
+ expect_tile = j < 5 || (j == 5 && i == 0);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = j < 5 || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the bottom row outside the new interest rect. The tile
+ // that insects the interest rect in invalidated only on its newly
+ // exposed or previously exposed pixels.
+ if (dir == GROW) {
+ // Only calculate the expected invalidation while growing, as the tile
+ // bounds post-growing is the newly exposed / previously exposed sizes.
+ // Post-shrinking, the tile bounds are smaller, so can't be used.
+ switch (corner) {
+ case TOP_LEFT:
+ case TOP_RIGHT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(5, 5));
+ break;
+ case BOTTOM_LEFT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(1, 5), pile_.tiling().TileBounds(5, 5));
+ expected_invalidation.Union(SubtractRects(
+ pile_.tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size)));
+ break;
+ case BOTTOM_RIGHT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(0, 5), pile_.tiling().TileBounds(4, 5));
+ expected_invalidation.Union(SubtractRects(
+ pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
+ break;
+ }
+ }
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+ }
+
+ // Grow right.
+ for (int dir = 0; dir <= LAST_DIRECTION; ++dir) {
+ gfx::Size new_tiling_size =
+ dir == GROW ? grow_right_tiling_size : base_tiling_size;
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, new_tiling_size,
+ CornerSinglePixelRect(corner, new_tiling_size));
+
+ // We should have lost the recordings in the right column.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ case BOTTOM_LEFT:
+ expect_tile = i < 5;
+ break;
+ case TOP_RIGHT:
+ // The interest rect in the top right tile means we'll record it.
+ expect_tile = i < 5 || (j == 0 && i == 5);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = i < 5 || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the right column outside the new interest rect. The tile
+ // that insects the interest rect in invalidated only on its new or
+ // previously exposed pixels.
+ if (dir == GROW) {
+ // Calculate the expected invalidation the first time through the loop.
+ switch (corner) {
+ case TOP_LEFT:
+ case BOTTOM_LEFT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5));
+ break;
+ case TOP_RIGHT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(5, 1), pile_.tiling().TileBounds(5, 5));
+ expected_invalidation.Union(SubtractRects(
+ pile_.tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size)));
+ break;
+ case BOTTOM_RIGHT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 4));
+ expected_invalidation.Union(SubtractRects(
+ pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
+ break;
+ }
+ }
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+ }
+
+ // Grow both.
+ for (int dir = 0; dir <= LAST_DIRECTION; ++dir) {
+ gfx::Size new_tiling_size =
+ dir == GROW ? grow_both_tiling_size : base_tiling_size;
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, new_tiling_size,
+ CornerSinglePixelRect(corner, new_tiling_size));
+
+ // We should have lost the recordings in the right column and bottom row.
+ // The tile that insects the interest rect in invalidated only on its new
+ // or previously exposed pixels.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ bool expect_tile;
+ switch (corner) {
+ case TOP_LEFT:
+ expect_tile = i < 5 && j < 5;
+ break;
+ case TOP_RIGHT:
+ // The interest rect in the top right tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5);
+ break;
+ case BOTTOM_LEFT:
+ // The interest rect in the bottom left tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0);
+ break;
+ case BOTTOM_RIGHT:
+ // The interest rect in the bottom right tile means we'll record it.
+ expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5);
+ break;
+ }
+ EXPECT_EQ(expect_tile, it != map.end() && it->second.get()) << i << ","
+ << j;
+ }
+ }
+
+ // We invalidated the right column and the bottom row outside the new
+ // interest rect. The tile that insects the interest rect in invalidated
+ // only on its new or previous exposed pixels.
+ if (dir == GROW) {
+ // Calculate the expected invalidation the first time through the loop.
+ switch (corner) {
+ case TOP_LEFT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5));
+ expected_invalidation.Union(
+ gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+ pile_.tiling().TileBounds(5, 5)));
+ break;
+ case TOP_RIGHT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(5, 1), pile_.tiling().TileBounds(5, 5));
+ expected_invalidation.Union(
+ gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+ pile_.tiling().TileBounds(5, 5)));
+ expected_invalidation.Union(SubtractRects(
+ pile_.tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size)));
+ break;
+ case BOTTOM_LEFT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 5));
+ expected_invalidation.Union(
+ gfx::UnionRects(pile_.tiling().TileBounds(1, 5),
+ pile_.tiling().TileBounds(5, 5)));
+ expected_invalidation.Union(SubtractRects(
+ pile_.tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size)));
+ break;
+ case BOTTOM_RIGHT:
+ expected_invalidation = gfx::UnionRects(
+ pile_.tiling().TileBounds(5, 0), pile_.tiling().TileBounds(5, 4));
+ expected_invalidation.Union(
+ gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+ pile_.tiling().TileBounds(4, 5)));
+ expected_invalidation.Union(SubtractRegions(
+ pile_.tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size)));
+ break;
+ }
+ }
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ PicturePileResizeCornerTests,
+ PicturePileResizeCornerTest,
+ ::testing::Values(TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT));
+
+TEST_F(PicturePileTest, ResizePileInsideInterestRect) {
+ // This size chosen to be small enough that all the rects below fit inside the
+ // the interest rect, so they are smaller than kPixelDistanceToRecord in each
+ // dimension.
+ int tile_size = 100;
+ gfx::Size base_tiling_size(5 * tile_size, 5 * tile_size);
+ gfx::Size grow_down_tiling_size(5 * tile_size, 7 * tile_size);
+ gfx::Size grow_right_tiling_size(7 * tile_size, 5 * tile_size);
+ gfx::Size grow_both_tiling_size(7 * tile_size, 7 * tile_size);
+
+ Region invalidation;
+ Region expected_invalidation;
+
+ pile_.tiling().SetMaxTextureSize(gfx::Size(tile_size, tile_size));
+ SetTilingSize(base_tiling_size);
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ UpdateAndExpandInvalidation(
+ &invalidation, grow_down_tiling_size, gfx::Rect(1, 1));
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(8, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the newly exposed pixels on the bottom row of tiles.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+ gfx::Rect(base_tiling_size));
+ Region bottom_row_new_pixels =
+ SubtractRegions(gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+ pile_.tiling().TileBounds(5, 5)),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(expected_invalidation.Contains(bottom_row_new_pixels));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the previously exposed pixels on the bottom row of tiles.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(expected_invalidation.Contains(bottom_row_new_pixels));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(
+ &invalidation, grow_right_tiling_size, gfx::Rect(1, 1));
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(8, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the newly exposed pixels on the right column of tiles.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+ gfx::Rect(base_tiling_size));
+ Region right_column_new_pixels =
+ SubtractRegions(gfx::UnionRects(pile_.tiling().TileBounds(5, 0),
+ pile_.tiling().TileBounds(5, 5)),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(expected_invalidation.Contains(right_column_new_pixels));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+
+ // We should have lost the recordings that are now outside the tiling only.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the previously exposed pixels on the right column of tiles.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(expected_invalidation.Contains(right_column_new_pixels));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(
+ &invalidation, grow_both_tiling_size, gfx::Rect(1, 1));
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(8, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(8, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the newly exposed pixels on the bottom row and right column
+ // of tiles.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+ gfx::Rect(base_tiling_size));
+ Region bottom_row_and_right_column_new_pixels = SubtractRegions(
+ UnionRegions(gfx::UnionRects(pile_.tiling().TileBounds(0, 5),
+ pile_.tiling().TileBounds(5, 5)),
+ gfx::UnionRects(pile_.tiling().TileBounds(5, 0),
+ pile_.tiling().TileBounds(5, 5))),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(
+ expected_invalidation.Contains(bottom_row_and_right_column_new_pixels));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect());
+
+ // We should have lost the recordings that are now outside the tiling only.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the previously exposed pixels on the bottom row and right
+ // column of tiles.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_TRUE(
+ expected_invalidation.Contains(bottom_row_and_right_column_new_pixels));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+}
+
+TEST_F(PicturePileTest, SmallResizePileInsideInterestRect) {
+ // This size chosen to be small enough that all the rects below fit inside the
+ // the interest rect, so they are smaller than kPixelDistanceToRecord in each
+ // dimension.
+ int tile_size = 100;
+ gfx::Size base_tiling_size(5 * tile_size, 5 * tile_size);
+ gfx::Size grow_down_tiling_size(5 * tile_size, 5 * tile_size + 5);
+ gfx::Size grow_right_tiling_size(5 * tile_size + 5, 5 * tile_size);
+ gfx::Size grow_both_tiling_size(5 * tile_size + 5, 5 * tile_size + 5);
+
+ Region invalidation;
+ Region expected_invalidation;
+
+ pile_.tiling().SetMaxTextureSize(gfx::Size(tile_size, tile_size));
+ SetTilingSize(base_tiling_size);
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ UpdateAndExpandInvalidation(
+ &invalidation, grow_down_tiling_size, gfx::Rect(1, 1));
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the newly exposed pixels.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the previously exposed pixels.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(
+ &invalidation, grow_right_tiling_size, gfx::Rect(1, 1));
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the newly exposed pixels.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1));
+
+ // We should have lost the recordings that are now outside the tiling only.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the previously exposed pixels.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(
+ &invalidation, grow_both_tiling_size, gfx::Rect(1, 1));
+
+ // We should have a recording for every tile.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the newly exposed pixels.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+
+ UpdateWholePile();
+ UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect());
+
+ // We should have lost the recordings that are now outside the tiling only.
+ EXPECT_EQ(6, pile_.tiling().num_tiles_x());
+ EXPECT_EQ(6, pile_.tiling().num_tiles_y());
+ for (int i = 0; i < pile_.tiling().num_tiles_x(); ++i) {
+ for (int j = 0; j < pile_.tiling().num_tiles_y(); ++j) {
+ FakePicturePile::PictureMapKey key(i, j);
+ FakePicturePile::PictureMap& map = pile_.picture_map();
+ FakePicturePile::PictureMap::iterator it = map.find(key);
+ EXPECT_TRUE(it != map.end() && it->second.get());
+ }
+ }
+
+ // We invalidated the previously exposed pixels.
+ expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size),
+ gfx::Rect(base_tiling_size));
+ EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString());
+ invalidation.Clear();
+}
+
+TEST_F(PicturePileTest, SolidRectangleIsSolid) {
+ // If the client has no contents, the solid state will be true.
+ Region invalidation1(tiling_rect());
+ UpdateAndExpandInvalidation(&invalidation1, tiling_size(), tiling_rect());
+ EXPECT_TRUE(pile_.is_solid_color());
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), pile_.solid_color());
+
+ // If there is a single rect that covers the view, the solid
+ // state will be true.
+ SkPaint paint;
+ paint.setColor(SK_ColorCYAN);
+ client_.add_draw_rect(tiling_rect(), paint);
+ Region invalidation2(tiling_rect());
+ UpdateAndExpandInvalidation(&invalidation2, tiling_size(), tiling_rect());
+ EXPECT_TRUE(pile_.is_solid_color());
+ EXPECT_EQ(SK_ColorCYAN, pile_.solid_color());
+
+ // If a second smaller rect is draw that doesn't cover the viewport
+ // completely, the solid state will be false.
+ gfx::Rect smallRect = tiling_rect();
+ smallRect.Inset(10, 10, 10, 10);
+ client_.add_draw_rect(smallRect, paint);
+ Region invalidation3(tiling_rect());
+ UpdateAndExpandInvalidation(&invalidation3, tiling_size(), tiling_rect());
+ EXPECT_FALSE(pile_.is_solid_color());
+
+ // If a third rect is drawn over everything, we should be solid again.
+ paint.setColor(SK_ColorRED);
+ client_.add_draw_rect(tiling_rect(), paint);
+ Region invalidation4(tiling_rect());
+ UpdateAndExpandInvalidation(&invalidation4, tiling_size(), tiling_rect());
+ EXPECT_TRUE(pile_.is_solid_color());
+ EXPECT_EQ(SK_ColorRED, pile_.solid_color());
+
+ // If we draw too many, we don't bother doing the analysis and we should no
+ // longer be in a solid state. There are 8 rects, two clips and a translate.
+ client_.add_draw_rect(tiling_rect(), paint);
+ client_.add_draw_rect(tiling_rect(), paint);
+ client_.add_draw_rect(tiling_rect(), paint);
+ client_.add_draw_rect(tiling_rect(), paint);
+ client_.add_draw_rect(tiling_rect(), paint);
+ Region invalidation5(tiling_rect());
+ UpdateAndExpandInvalidation(&invalidation5, tiling_size(), tiling_rect());
+ EXPECT_FALSE(pile_.is_solid_color());
+}
+
+TEST_F(PicturePileTest, NonSolidRectangleOnOffsettedLayerIsNonSolid) {
+ gfx::Rect visible_rect(tiling_rect());
+ visible_rect.Offset(gfx::Vector2d(1000, 1000));
+ // The picture pile requires that the tiling completely encompass the viewport
+ // to make this test work correctly since the recorded viewport is an
+ // intersection of the tile size and viewport rect. This is possibly a flaw
+ // in |PicturePile|.
+ gfx::Size tiling_size(visible_rect.right(), visible_rect.bottom());
+ // |Setup()| will create pictures here that mess with the test, clear it!
+ pile_.Clear();
+
+ SkPaint paint;
+ paint.setColor(SK_ColorCYAN);
+
+ // Add a rect that doesn't cover the viewport completely, the solid state
+ // will be false.
+ gfx::Rect smallRect = visible_rect;
+ smallRect.Inset(10, 10, 10, 10);
+ client_.add_draw_rect(smallRect, paint);
+ Region invalidation(visible_rect);
+ UpdateAndExpandInvalidation(&invalidation, tiling_size, visible_rect);
+ EXPECT_FALSE(pile_.is_solid_color());
+}
+
+TEST_F(PicturePileTest, SetEmptyBounds) {
+ EXPECT_TRUE(pile_.is_solid_color());
+ EXPECT_FALSE(pile_.GetSize().IsEmpty());
+ EXPECT_FALSE(pile_.picture_map().empty());
+ EXPECT_TRUE(pile_.HasRecordings());
+ pile_.SetEmptyBounds();
+ EXPECT_FALSE(pile_.is_solid_color());
+ EXPECT_TRUE(pile_.GetSize().IsEmpty());
+ EXPECT_TRUE(pile_.picture_map().empty());
+ EXPECT_FALSE(pile_.HasRecordings());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/playback/picture_unittest.cc b/cc/playback/picture_unittest.cc
new file mode 100644
index 0000000..53310e3
--- /dev/null
+++ b/cc/playback/picture_unittest.cc
@@ -0,0 +1,181 @@
+// 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 "cc/playback/picture.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/skia_common.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkGraphics.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+namespace {
+
+TEST(PictureTest, AsBase64String) {
+ SkGraphics::Init();
+
+ gfx::Rect layer_rect(100, 100);
+
+ gfx::Size tile_grid_size(100, 100);
+
+ FakeContentLayerClient content_layer_client;
+
+ scoped_ptr<base::Value> tmp;
+
+ SkPaint red_paint;
+ red_paint.setColor(SkColorSetARGB(255, 255, 0, 0));
+ SkPaint green_paint;
+ green_paint.setColor(SkColorSetARGB(255, 0, 255, 0));
+
+ // Invalid picture (not a dict).
+ tmp.reset(new base::StringValue("abc!@#$%"));
+ scoped_refptr<Picture> invalid_picture =
+ Picture::CreateFromValue(tmp.get());
+ EXPECT_FALSE(invalid_picture.get());
+
+ // Single full-size rect picture.
+ content_layer_client.add_draw_rect(layer_rect, red_paint);
+
+ scoped_refptr<Picture> one_rect_picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, false,
+ RecordingSource::RECORD_NORMALLY);
+ scoped_ptr<base::Value> serialized_one_rect(one_rect_picture->AsValue());
+
+ // Reconstruct the picture.
+ scoped_refptr<Picture> one_rect_picture_check =
+ Picture::CreateFromValue(serialized_one_rect.get());
+ EXPECT_TRUE(one_rect_picture_check);
+
+ // Check for equivalence.
+ unsigned char one_rect_buffer[4 * 100 * 100] = {0};
+ DrawPicture(one_rect_buffer, layer_rect, one_rect_picture);
+ unsigned char one_rect_buffer_check[4 * 100 * 100] = {0};
+ DrawPicture(one_rect_buffer_check, layer_rect, one_rect_picture_check);
+
+ EXPECT_EQ(one_rect_picture->LayerRect(), one_rect_picture_check->LayerRect());
+ EXPECT_EQ(0, memcmp(one_rect_buffer, one_rect_buffer_check, 4 * 100 * 100));
+
+ // Two rect picture.
+ content_layer_client.add_draw_rect(gfx::Rect(25, 25, 50, 50), green_paint);
+
+ scoped_refptr<Picture> two_rect_picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, false,
+ RecordingSource::RECORD_NORMALLY);
+
+ scoped_ptr<base::Value> serialized_two_rect(two_rect_picture->AsValue());
+
+ // Reconstruct the picture.
+ scoped_refptr<Picture> two_rect_picture_check =
+ Picture::CreateFromValue(serialized_two_rect.get());
+ EXPECT_TRUE(two_rect_picture_check);
+
+ // Check for equivalence.
+ unsigned char two_rect_buffer[4 * 100 * 100] = {0};
+ DrawPicture(two_rect_buffer, layer_rect, two_rect_picture);
+ unsigned char two_rect_buffer_check[4 * 100 * 100] = {0};
+ DrawPicture(two_rect_buffer_check, layer_rect, two_rect_picture_check);
+
+ EXPECT_EQ(two_rect_picture->LayerRect(), two_rect_picture_check->LayerRect());
+ EXPECT_EQ(0, memcmp(two_rect_buffer, two_rect_buffer_check, 4 * 100 * 100));
+}
+
+TEST(PictureTest, CreateFromSkpValue) {
+ SkGraphics::Init();
+
+ gfx::Rect layer_rect(100, 200);
+
+ gfx::Size tile_grid_size(100, 200);
+
+ FakeContentLayerClient content_layer_client;
+
+ scoped_ptr<base::Value> tmp;
+
+ SkPaint red_paint;
+ red_paint.setColor(SkColorSetARGB(255, 255, 0, 0));
+ SkPaint green_paint;
+ green_paint.setColor(SkColorSetARGB(255, 0, 255, 0));
+
+ // Invalid picture (not a dict).
+ tmp.reset(new base::StringValue("abc!@#$%"));
+ scoped_refptr<Picture> invalid_picture =
+ Picture::CreateFromSkpValue(tmp.get());
+ EXPECT_TRUE(!invalid_picture.get());
+
+ // Single full-size rect picture.
+ content_layer_client.add_draw_rect(layer_rect, red_paint);
+ scoped_refptr<Picture> one_rect_picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, false,
+ RecordingSource::RECORD_NORMALLY);
+ scoped_ptr<base::Value> serialized_one_rect(
+ one_rect_picture->AsValue());
+
+ const base::DictionaryValue* value = NULL;
+ EXPECT_TRUE(serialized_one_rect->GetAsDictionary(&value));
+
+ // Decode the picture from base64.
+ const base::Value* skp_value;
+ EXPECT_TRUE(value->Get("skp64", &skp_value));
+
+ // Reconstruct the picture.
+ scoped_refptr<Picture> one_rect_picture_check =
+ Picture::CreateFromSkpValue(skp_value);
+ EXPECT_TRUE(one_rect_picture_check);
+
+ EXPECT_EQ(100, one_rect_picture_check->LayerRect().width());
+ EXPECT_EQ(200, one_rect_picture_check->LayerRect().height());
+}
+
+TEST(PictureTest, RecordingModes) {
+ SkGraphics::Init();
+
+ gfx::Rect layer_rect(100, 200);
+
+ gfx::Size tile_grid_size(100, 200);
+
+ FakeContentLayerClient content_layer_client;
+ EXPECT_EQ(NULL, content_layer_client.last_canvas());
+
+ scoped_refptr<Picture> picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, false,
+ RecordingSource::RECORD_NORMALLY);
+ EXPECT_TRUE(content_layer_client.last_canvas() != NULL);
+ EXPECT_EQ(ContentLayerClient::PAINTING_BEHAVIOR_NORMAL,
+ content_layer_client.last_painting_control());
+ EXPECT_TRUE(picture.get());
+
+ picture = Picture::Create(layer_rect, &content_layer_client, tile_grid_size,
+ false, RecordingSource::RECORD_WITH_SK_NULL_CANVAS);
+ EXPECT_TRUE(content_layer_client.last_canvas() != NULL);
+ EXPECT_EQ(ContentLayerClient::PAINTING_BEHAVIOR_NORMAL,
+ content_layer_client.last_painting_control());
+ EXPECT_TRUE(picture.get());
+
+ picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, false,
+ RecordingSource::RECORD_WITH_PAINTING_DISABLED);
+ EXPECT_TRUE(content_layer_client.last_canvas() != NULL);
+ EXPECT_EQ(ContentLayerClient::DISPLAY_LIST_PAINTING_DISABLED,
+ content_layer_client.last_painting_control());
+ EXPECT_TRUE(picture.get());
+
+ picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, false,
+ RecordingSource::RECORD_WITH_CACHING_DISABLED);
+ EXPECT_TRUE(content_layer_client.last_canvas() != NULL);
+ EXPECT_EQ(ContentLayerClient::DISPLAY_LIST_CACHING_DISABLED,
+ content_layer_client.last_painting_control());
+ EXPECT_TRUE(picture.get());
+
+ // RECORD_WITH_CONSTRUCTION_DISABLED is not supported for Picture.
+
+ EXPECT_EQ(5, RecordingSource::RECORDING_MODE_COUNT);
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/playback/pixel_ref_map.cc b/cc/playback/pixel_ref_map.cc
new file mode 100644
index 0000000..bb5a84c
--- /dev/null
+++ b/cc/playback/pixel_ref_map.cc
@@ -0,0 +1,172 @@
+// Copyright 2015 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/playback/pixel_ref_map.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "cc/base/util.h"
+#include "cc/playback/display_item_list.h"
+#include "cc/playback/picture.h"
+#include "skia/ext/pixel_ref_utils.h"
+
+namespace cc {
+
+PixelRefMap::PixelRefMap(const gfx::Size& cell_size) : cell_size_(cell_size) {
+ DCHECK(!cell_size.IsEmpty());
+}
+
+PixelRefMap::~PixelRefMap() {
+}
+
+void PixelRefMap::GatherPixelRefsFromPicture(SkPicture* picture) {
+ DCHECK(picture);
+
+ int min_x = std::numeric_limits<int>::max();
+ int min_y = std::numeric_limits<int>::max();
+ int max_x = 0;
+ int max_y = 0;
+
+ skia::DiscardablePixelRefList pixel_refs;
+ skia::PixelRefUtils::GatherDiscardablePixelRefs(picture, &pixel_refs);
+ for (skia::DiscardablePixelRefList::const_iterator it = pixel_refs.begin();
+ it != pixel_refs.end(); ++it) {
+ gfx::Point min(
+ RoundDown(static_cast<int>(it->pixel_ref_rect.x()), cell_size_.width()),
+ RoundDown(static_cast<int>(it->pixel_ref_rect.y()),
+ cell_size_.height()));
+ gfx::Point max(
+ RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.right())),
+ cell_size_.width()),
+ RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.bottom())),
+ cell_size_.height()));
+
+ for (int y = min.y(); y <= max.y(); y += cell_size_.height()) {
+ for (int x = min.x(); x <= max.x(); x += cell_size_.width()) {
+ PixelRefMapKey key(x, y);
+ data_hash_map_[key].push_back(it->pixel_ref);
+ }
+ }
+
+ min_x = std::min(min_x, min.x());
+ min_y = std::min(min_y, min.y());
+ max_x = std::max(max_x, max.x());
+ max_y = std::max(max_y, max.y());
+ }
+
+ min_pixel_cell_ = gfx::Point(min_x, min_y);
+ max_pixel_cell_ = gfx::Point(max_x, max_y);
+}
+
+base::LazyInstance<PixelRefs> PixelRefMap::Iterator::empty_pixel_refs_;
+
+PixelRefMap::Iterator::Iterator()
+ : target_pixel_ref_map_(NULL),
+ current_pixel_refs_(empty_pixel_refs_.Pointer()),
+ current_index_(0),
+ min_point_(-1, -1),
+ max_point_(-1, -1),
+ current_x_(0),
+ current_y_(0) {
+}
+
+PixelRefMap::Iterator::Iterator(const gfx::Rect& rect, const Picture* picture)
+ : target_pixel_ref_map_(&(picture->pixel_refs_)),
+ current_pixel_refs_(empty_pixel_refs_.Pointer()),
+ current_index_(0) {
+ map_layer_rect_ = picture->layer_rect_;
+ PointToFirstPixelRef(rect);
+}
+
+PixelRefMap::Iterator::Iterator(const gfx::Rect& rect,
+ const DisplayItemList* display_list)
+ : target_pixel_ref_map_(display_list->pixel_refs_.get()),
+ current_pixel_refs_(empty_pixel_refs_.Pointer()),
+ current_index_(0) {
+ map_layer_rect_ = display_list->layer_rect_;
+ PointToFirstPixelRef(rect);
+}
+
+PixelRefMap::Iterator::~Iterator() {
+}
+
+PixelRefMap::Iterator& PixelRefMap::Iterator::operator++() {
+ ++current_index_;
+ // If we're not at the end of the list, then we have the next item.
+ if (current_index_ < current_pixel_refs_->size())
+ return *this;
+
+ DCHECK(current_y_ <= max_point_.y());
+ while (true) {
+ gfx::Size cell_size = target_pixel_ref_map_->cell_size_;
+
+ // Advance the current grid cell.
+ current_x_ += cell_size.width();
+ if (current_x_ > max_point_.x()) {
+ current_y_ += cell_size.height();
+ current_x_ = min_point_.x();
+ if (current_y_ > max_point_.y()) {
+ current_pixel_refs_ = empty_pixel_refs_.Pointer();
+ current_index_ = 0;
+ break;
+ }
+ }
+
+ // If there are no pixel refs at this grid cell, keep incrementing.
+ PixelRefMapKey key(current_x_, current_y_);
+ PixelRefHashmap::const_iterator iter =
+ target_pixel_ref_map_->data_hash_map_.find(key);
+ if (iter == target_pixel_ref_map_->data_hash_map_.end())
+ continue;
+
+ // We found a non-empty list: store it and get the first pixel ref.
+ current_pixel_refs_ = &iter->second;
+ current_index_ = 0;
+ break;
+ }
+ return *this;
+}
+
+void PixelRefMap::Iterator::PointToFirstPixelRef(const gfx::Rect& rect) {
+ gfx::Rect query_rect(rect);
+ // Early out if the query rect doesn't intersect this picture.
+ if (!query_rect.Intersects(map_layer_rect_) || !target_pixel_ref_map_) {
+ min_point_ = gfx::Point(0, 0);
+ max_point_ = gfx::Point(0, 0);
+ current_x_ = 1;
+ current_y_ = 1;
+ return;
+ }
+
+ // First, subtract the layer origin as cells are stored in layer space.
+ query_rect.Offset(-map_layer_rect_.OffsetFromOrigin());
+
+ DCHECK(!target_pixel_ref_map_->cell_size_.IsEmpty());
+ gfx::Size cell_size(target_pixel_ref_map_->cell_size_);
+ // We have to find a cell_size aligned point that corresponds to
+ // query_rect. Point is a multiple of cell_size.
+ min_point_ = gfx::Point(RoundDown(query_rect.x(), cell_size.width()),
+ RoundDown(query_rect.y(), cell_size.height()));
+ max_point_ =
+ gfx::Point(RoundDown(query_rect.right() - 1, cell_size.width()),
+ RoundDown(query_rect.bottom() - 1, cell_size.height()));
+
+ // Limit the points to known pixel ref boundaries.
+ min_point_ = gfx::Point(
+ std::max(min_point_.x(), target_pixel_ref_map_->min_pixel_cell_.x()),
+ std::max(min_point_.y(), target_pixel_ref_map_->min_pixel_cell_.y()));
+ max_point_ = gfx::Point(
+ std::min(max_point_.x(), target_pixel_ref_map_->max_pixel_cell_.x()),
+ std::min(max_point_.y(), target_pixel_ref_map_->max_pixel_cell_.y()));
+
+ // Make the current x be cell_size.width() less than min point, so that
+ // the first increment will point at min_point_.
+ current_x_ = min_point_.x() - cell_size.width();
+ current_y_ = min_point_.y();
+ if (current_y_ <= max_point_.y())
+ ++(*this);
+}
+
+} // namespace cc
diff --git a/cc/playback/pixel_ref_map.h b/cc/playback/pixel_ref_map.h
new file mode 100644
index 0000000..e154521
--- /dev/null
+++ b/cc/playback/pixel_ref_map.h
@@ -0,0 +1,94 @@
+// Copyright 2015 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_PLAYBACK_PIXEL_REF_MAP_H_
+#define CC_PLAYBACK_PIXEL_REF_MAP_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+class SkPixelRef;
+
+namespace cc {
+
+class Picture;
+class DisplayItemList;
+
+typedef std::pair<int, int> PixelRefMapKey;
+typedef std::vector<SkPixelRef*> PixelRefs;
+typedef base::hash_map<PixelRefMapKey, PixelRefs> PixelRefHashmap;
+
+// This class is used and owned by cc Picture class. It is used to gather pixel
+// refs which would happen after record. It takes in |cell_size| to decide how
+// big each grid cell should be.
+class CC_EXPORT PixelRefMap {
+ public:
+ explicit PixelRefMap(const gfx::Size& cell_size);
+ ~PixelRefMap();
+ void GatherPixelRefsFromPicture(SkPicture* picture);
+
+ bool empty() const { return data_hash_map_.empty(); }
+
+ // This iterator imprecisely returns the set of pixel refs that are needed to
+ // raster this layer rect from this picture. Internally, pixel refs are
+ // clumped into tile grid buckets, so there may be false positives.
+ class CC_EXPORT Iterator {
+ public:
+ // Default iterator constructor that is used as place holder for invalid
+ // Iterator.
+ Iterator();
+ Iterator(const gfx::Rect& layer_rect, const Picture* picture);
+ Iterator(const gfx::Rect& layer_rect, const DisplayItemList* picture);
+ ~Iterator();
+
+ SkPixelRef* operator->() const {
+ DCHECK_LT(current_index_, current_pixel_refs_->size());
+ return (*current_pixel_refs_)[current_index_];
+ }
+
+ SkPixelRef* operator*() const {
+ DCHECK_LT(current_index_, current_pixel_refs_->size());
+ return (*current_pixel_refs_)[current_index_];
+ }
+
+ Iterator& operator++();
+ operator bool() const {
+ return current_index_ < current_pixel_refs_->size();
+ }
+
+ private:
+ void PointToFirstPixelRef(const gfx::Rect& query_rect);
+
+ static base::LazyInstance<PixelRefs> empty_pixel_refs_;
+ const PixelRefMap* target_pixel_ref_map_;
+ const PixelRefs* current_pixel_refs_;
+ unsigned current_index_;
+
+ gfx::Rect map_layer_rect_;
+
+ gfx::Point min_point_;
+ gfx::Point max_point_;
+ int current_x_;
+ int current_y_;
+ };
+
+ private:
+ gfx::Point min_pixel_cell_;
+ gfx::Point max_pixel_cell_;
+ gfx::Size cell_size_;
+
+ PixelRefHashmap data_hash_map_;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_PIXEL_REF_MAP_H_
diff --git a/cc/playback/pixel_ref_map_unittest.cc b/cc/playback/pixel_ref_map_unittest.cc
new file mode 100644
index 0000000..af88b44
--- /dev/null
+++ b/cc/playback/pixel_ref_map_unittest.cc
@@ -0,0 +1,292 @@
+// Copyright 2015 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/playback/pixel_ref_map.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "cc/playback/picture.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/skia_common.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkGraphics.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+namespace {
+
+TEST(PixelRefMapTest, PixelRefMapIterator) {
+ gfx::Rect layer_rect(2048, 2048);
+
+ gfx::Size tile_grid_size(512, 512);
+
+ FakeContentLayerClient content_layer_client;
+
+ // Discardable pixel refs are found in the following grids:
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ SkBitmap discardable_bitmap[4][4];
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ if ((x + y) & 1) {
+ CreateBitmap(gfx::Size(500, 500), "discardable",
+ &discardable_bitmap[y][x]);
+ SkPaint paint;
+ content_layer_client.add_draw_bitmap(
+ discardable_bitmap[y][x], gfx::Point(x * 512 + 6, y * 512 + 6),
+ paint);
+ }
+ }
+ }
+
+ scoped_refptr<Picture> picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, true,
+ RecordingSource::RECORD_NORMALLY);
+
+ // Default iterator does not have any pixel refs.
+ {
+ PixelRefMap::Iterator iterator;
+ EXPECT_FALSE(iterator);
+ }
+
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ PixelRefMap::Iterator iterator(gfx::Rect(x * 512, y * 512, 500, 500),
+ picture.get());
+ if ((x + y) & 1) {
+ EXPECT_TRUE(iterator) << x << " " << y;
+ EXPECT_TRUE(*iterator == discardable_bitmap[y][x].pixelRef())
+ << x << " " << y;
+ EXPECT_FALSE(++iterator) << x << " " << y;
+ } else {
+ EXPECT_FALSE(iterator) << x << " " << y;
+ }
+ }
+ }
+ // Capture 4 pixel refs.
+ {
+ PixelRefMap::Iterator iterator(gfx::Rect(512, 512, 2048, 2048),
+ picture.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][2].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2][1].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2][3].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[3][2].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+
+ // Copy test.
+ PixelRefMap::Iterator iterator(gfx::Rect(512, 512, 2048, 2048),
+ picture.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][2].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2][1].pixelRef());
+
+ // copy now points to the same spot as iterator,
+ // but both can be incremented independently.
+ PixelRefMap::Iterator copy = iterator;
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2][3].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[3][2].pixelRef());
+ EXPECT_FALSE(++iterator);
+
+ EXPECT_TRUE(copy);
+ EXPECT_TRUE(*copy == discardable_bitmap[2][1].pixelRef());
+ EXPECT_TRUE(++copy);
+ EXPECT_TRUE(*copy == discardable_bitmap[2][3].pixelRef());
+ EXPECT_TRUE(++copy);
+ EXPECT_TRUE(*copy == discardable_bitmap[3][2].pixelRef());
+ EXPECT_FALSE(++copy);
+}
+
+TEST(PixelRefMapTest, PixelRefMapIteratorNonZeroLayer) {
+ gfx::Rect layer_rect(1024, 0, 2048, 2048);
+
+ gfx::Size tile_grid_size(512, 512);
+
+ FakeContentLayerClient content_layer_client;
+
+ // Discardable pixel refs are found in the following grids:
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ SkBitmap discardable_bitmap[4][4];
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ if ((x + y) & 1) {
+ CreateBitmap(gfx::Size(500, 500), "discardable",
+ &discardable_bitmap[y][x]);
+ SkPaint paint;
+ content_layer_client.add_draw_bitmap(
+ discardable_bitmap[y][x],
+ gfx::Point(1024 + x * 512 + 6, y * 512 + 6), paint);
+ }
+ }
+ }
+
+ scoped_refptr<Picture> picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, true,
+ RecordingSource::RECORD_NORMALLY);
+
+ // Default iterator does not have any pixel refs.
+ {
+ PixelRefMap::Iterator iterator;
+ EXPECT_FALSE(iterator);
+ }
+
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ PixelRefMap::Iterator iterator(
+ gfx::Rect(1024 + x * 512, y * 512, 500, 500), picture.get());
+ if ((x + y) & 1) {
+ EXPECT_TRUE(iterator) << x << " " << y;
+ EXPECT_TRUE(*iterator == discardable_bitmap[y][x].pixelRef());
+ EXPECT_FALSE(++iterator) << x << " " << y;
+ } else {
+ EXPECT_FALSE(iterator) << x << " " << y;
+ }
+ }
+ }
+ // Capture 4 pixel refs.
+ {
+ PixelRefMap::Iterator iterator(gfx::Rect(1024 + 512, 512, 2048, 2048),
+ picture.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][2].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2][1].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2][3].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[3][2].pixelRef());
+ EXPECT_FALSE(++iterator);
+ }
+
+ // Copy test.
+ {
+ PixelRefMap::Iterator iterator(gfx::Rect(1024 + 512, 512, 2048, 2048),
+ picture.get());
+ EXPECT_TRUE(iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[1][2].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2][1].pixelRef());
+
+ // copy now points to the same spot as iterator,
+ // but both can be incremented independently.
+ PixelRefMap::Iterator copy = iterator;
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[2][3].pixelRef());
+ EXPECT_TRUE(++iterator);
+ EXPECT_TRUE(*iterator == discardable_bitmap[3][2].pixelRef());
+ EXPECT_FALSE(++iterator);
+
+ EXPECT_TRUE(copy);
+ EXPECT_TRUE(*copy == discardable_bitmap[2][1].pixelRef());
+ EXPECT_TRUE(++copy);
+ EXPECT_TRUE(*copy == discardable_bitmap[2][3].pixelRef());
+ EXPECT_TRUE(++copy);
+ EXPECT_TRUE(*copy == discardable_bitmap[3][2].pixelRef());
+ EXPECT_FALSE(++copy);
+ }
+
+ // Non intersecting rects
+ {
+ PixelRefMap::Iterator iterator(gfx::Rect(0, 0, 1000, 1000), picture.get());
+ EXPECT_FALSE(iterator);
+ }
+ {
+ PixelRefMap::Iterator iterator(gfx::Rect(3500, 0, 1000, 1000),
+ picture.get());
+ EXPECT_FALSE(iterator);
+ }
+ {
+ PixelRefMap::Iterator iterator(gfx::Rect(0, 1100, 1000, 1000),
+ picture.get());
+ EXPECT_FALSE(iterator);
+ }
+ {
+ PixelRefMap::Iterator iterator(gfx::Rect(3500, 1100, 1000, 1000),
+ picture.get());
+ EXPECT_FALSE(iterator);
+ }
+}
+
+TEST(PixelRefMapTest, PixelRefMapIteratorOnePixelQuery) {
+ gfx::Rect layer_rect(2048, 2048);
+
+ gfx::Size tile_grid_size(512, 512);
+
+ FakeContentLayerClient content_layer_client;
+
+ // Discardable pixel refs are found in the following grids:
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ SkBitmap discardable_bitmap[4][4];
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ if ((x + y) & 1) {
+ CreateBitmap(gfx::Size(500, 500), "discardable",
+ &discardable_bitmap[y][x]);
+ SkPaint paint;
+ content_layer_client.add_draw_bitmap(
+ discardable_bitmap[y][x], gfx::Point(x * 512 + 6, y * 512 + 6),
+ paint);
+ }
+ }
+ }
+
+ scoped_refptr<Picture> picture =
+ Picture::Create(layer_rect, &content_layer_client, tile_grid_size, true,
+ RecordingSource::RECORD_NORMALLY);
+
+ // Default iterator does not have any pixel refs.
+ {
+ PixelRefMap::Iterator iterator;
+ EXPECT_FALSE(iterator);
+ }
+
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ PixelRefMap::Iterator iterator(gfx::Rect(x * 512, y * 512 + 256, 1, 1),
+ picture.get());
+ if ((x + y) & 1) {
+ EXPECT_TRUE(iterator) << x << " " << y;
+ EXPECT_TRUE(*iterator == discardable_bitmap[y][x].pixelRef());
+ EXPECT_FALSE(++iterator) << x << " " << y;
+ } else {
+ EXPECT_FALSE(iterator) << x << " " << y;
+ }
+ }
+ }
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/playback/raster_source.h b/cc/playback/raster_source.h
new file mode 100644
index 0000000..c0a484a
--- /dev/null
+++ b/cc/playback/raster_source.h
@@ -0,0 +1,116 @@
+// Copyright 2014 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_PLAYBACK_RASTER_SOURCE_H_
+#define CC_PLAYBACK_RASTER_SOURCE_H_
+
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
+#include "cc/debug/traced_value.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+class SkCanvas;
+class SkPicture;
+
+namespace cc {
+
+class Picture;
+
+class CC_EXPORT RasterSource : public base::RefCountedThreadSafe<RasterSource> {
+ public:
+ struct CC_EXPORT SolidColorAnalysis {
+ SolidColorAnalysis()
+ : is_solid_color(false), solid_color(SK_ColorTRANSPARENT) {}
+ ~SolidColorAnalysis() {}
+
+ bool is_solid_color;
+ SkColor solid_color;
+ };
+
+ // Raster a subrect of this RasterSource into the given canvas. It is
+ // assumed that contents_scale has already been applied to this canvas.
+ // Writes the total number of pixels rasterized and the time spent
+ // rasterizing to the stats if the respective pointer is not nullptr.
+ // It is assumed that the canvas passed here will only be rasterized by
+ // this raster source via this call.
+ virtual void PlaybackToCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const = 0;
+
+ // Similar to above, except that the canvas passed here can (or was already)
+ // rasterized into by another raster source. That is, it is not safe to clear
+ // the canvas or discard its underlying memory.
+ virtual void PlaybackToSharedCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ float contents_scale) const = 0;
+
+ // Analyze to determine if the given rect at given scale is of solid color in
+ // this raster source.
+ virtual void PerformSolidColorAnalysis(
+ const gfx::Rect& content_rect,
+ float contents_scale,
+ SolidColorAnalysis* analysis) const = 0;
+
+ // Returns true iff the whole raster source is of solid color.
+ virtual bool IsSolidColor() const = 0;
+
+ // Returns the color of the raster source if it is solid color. The results
+ // are unspecified if IsSolidColor returns false.
+ virtual SkColor GetSolidColor() const = 0;
+
+ // Returns the size of this raster source.
+ virtual gfx::Size GetSize() const = 0;
+
+ // Populate the given list with all SkPixelRefs that may overlap the given
+ // rect at given scale.
+ virtual void GatherPixelRefs(const gfx::Rect& content_rect,
+ float contents_scale,
+ std::vector<SkPixelRef*>* pixel_refs) const = 0;
+
+ // Return true iff this raster source can raster the given rect at given
+ // scale.
+ virtual bool CoversRect(const gfx::Rect& content_rect,
+ float contents_scale) const = 0;
+
+ // Returns true if this raster source has anything to rasterize.
+ virtual bool HasRecordings() const = 0;
+
+ // Informs the raster source that it should attempt to use distance field text
+ // during rasterization.
+ virtual void SetShouldAttemptToUseDistanceFieldText() = 0;
+
+ // Return true iff this raster source would benefit from using distance
+ // field text.
+ virtual bool ShouldAttemptToUseDistanceFieldText() const = 0;
+
+ // Tracing functionality.
+ virtual void DidBeginTracing() = 0;
+ virtual void AsValueInto(base::trace_event::TracedValue* array) const = 0;
+ virtual skia::RefPtr<SkPicture> GetFlattenedPicture() = 0;
+ virtual size_t GetPictureMemoryUsage() const = 0;
+
+ // Return true if LCD anti-aliasing may be used when rastering text.
+ virtual bool CanUseLCDText() const = 0;
+
+ virtual scoped_refptr<RasterSource> CreateCloneWithoutLCDText() const = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<RasterSource>;
+
+ RasterSource() {}
+ virtual ~RasterSource() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RasterSource);
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_RASTER_SOURCE_H_
diff --git a/cc/playback/raster_source_helper.cc b/cc/playback/raster_source_helper.cc
new file mode 100644
index 0000000..6510b3c
--- /dev/null
+++ b/cc/playback/raster_source_helper.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 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/playback/raster_source_helper.h"
+
+#include "base/trace_event/trace_event.h"
+#include "cc/debug/debug_colors.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+void RasterSourceHelper::PrepareForPlaybackToCanvas(
+ SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ const gfx::Rect& source_rect,
+ float contents_scale,
+ SkColor background_color,
+ bool clear_canvas_with_debug_color,
+ bool requires_clear) {
+ canvas->discard();
+ if (clear_canvas_with_debug_color) {
+ // Any non-painted areas in the content bounds will be left in this color.
+ canvas->clear(DebugColors::NonPaintedFillColor());
+ }
+
+ // If this raster source has opaque contents, it is guaranteeing that it will
+ // draw an opaque rect the size of the layer. If it is not, then we must
+ // clear this canvas ourselves.
+ if (requires_clear) {
+ TRACE_EVENT_INSTANT0("cc", "SkCanvas::clear", TRACE_EVENT_SCOPE_THREAD);
+ // Clearing is about ~4x faster than drawing a rect even if the content
+ // isn't covering a majority of the canvas.
+ canvas->clear(SK_ColorTRANSPARENT);
+ } else {
+ // Even if completely covered, for rasterizations that touch the edge of the
+ // layer, we also need to raster the background color underneath the last
+ // texel (since the recording won't cover it) and outside the last texel
+ // (due to linear filtering when using this texture).
+ gfx::Rect content_rect =
+ gfx::ToEnclosingRect(gfx::ScaleRect(source_rect, contents_scale));
+
+ // The final texel of content may only be partially covered by a
+ // rasterization; this rect represents the content rect that is fully
+ // covered by content.
+ gfx::Rect deflated_content_rect = content_rect;
+ deflated_content_rect.Inset(0, 0, 1, 1);
+ if (!deflated_content_rect.Contains(canvas_rect)) {
+ if (clear_canvas_with_debug_color) {
+ // Any non-painted areas outside of the content bounds are left in
+ // this color. If this is seen then it means that cc neglected to
+ // rerasterize a tile that used to intersect with the content rect
+ // after the content bounds grew.
+ canvas->save();
+ canvas->translate(-canvas_rect.x(), -canvas_rect.y());
+ canvas->clipRect(gfx::RectToSkRect(content_rect),
+ SkRegion::kDifference_Op);
+ canvas->drawColor(DebugColors::MissingResizeInvalidations(),
+ SkXfermode::kSrc_Mode);
+ canvas->restore();
+ }
+
+ // Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
+ // faster than clearing, so special case this.
+ canvas->save();
+ canvas->translate(-canvas_rect.x(), -canvas_rect.y());
+ gfx::Rect inflated_content_rect = content_rect;
+ inflated_content_rect.Inset(0, 0, -1, -1);
+ canvas->clipRect(gfx::RectToSkRect(inflated_content_rect),
+ SkRegion::kReplace_Op);
+ canvas->clipRect(gfx::RectToSkRect(deflated_content_rect),
+ SkRegion::kDifference_Op);
+ canvas->drawColor(background_color, SkXfermode::kSrc_Mode);
+ canvas->restore();
+ }
+ }
+}
+
+} // namespace cc
diff --git a/cc/playback/raster_source_helper.h b/cc/playback/raster_source_helper.h
new file mode 100644
index 0000000..3051303
--- /dev/null
+++ b/cc/playback/raster_source_helper.h
@@ -0,0 +1,29 @@
+// Copyright 2014 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_PLAYBACK_RASTER_SOURCE_HELPER_H_
+#define CC_PLAYBACK_RASTER_SOURCE_HELPER_H_
+
+#include "cc/base/cc_export.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/rect.h"
+
+class SkCanvas;
+
+namespace cc {
+
+class CC_EXPORT RasterSourceHelper {
+ public:
+ static void PrepareForPlaybackToCanvas(SkCanvas* canvas,
+ const gfx::Rect& canvas_rect,
+ const gfx::Rect& source_rect,
+ float contents_scale,
+ SkColor background_color,
+ bool clear_canvas_with_debug_color,
+ bool requires_clear);
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_RASTER_SOURCE_HELPER_H_
diff --git a/cc/playback/recording_source.h b/cc/playback/recording_source.h
new file mode 100644
index 0000000..cf8c7c4
--- /dev/null
+++ b/cc/playback/recording_source.h
@@ -0,0 +1,63 @@
+// Copyright 2014 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_PLAYBACK_RECORDING_SOURCE_H_
+#define CC_PLAYBACK_RECORDING_SOURCE_H_
+
+#include "base/memory/ref_counted.h"
+#include "cc/base/cc_export.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+class ContentLayerClient;
+class Region;
+class RasterSource;
+
+class CC_EXPORT RecordingSource {
+ public:
+ // TODO(schenney) Remove RECORD_WITH_SK_NULL_CANVAS when we no longer
+ // support a non-Slimming Paint path.
+ enum RecordingMode {
+ RECORD_NORMALLY,
+ RECORD_WITH_SK_NULL_CANVAS,
+ RECORD_WITH_PAINTING_DISABLED,
+ RECORD_WITH_CACHING_DISABLED,
+ RECORD_WITH_CONSTRUCTION_DISABLED,
+ RECORDING_MODE_COUNT, // Must be the last entry.
+ };
+
+ virtual ~RecordingSource() {}
+ // Re-record parts of the picture that are invalid.
+ // Invalidations are in layer space, and will be expanded to cover everything
+ // that was either recorded/changed or that has no recording, leaving out only
+ // pieces that we had a recording for and it was not changed.
+ // Return true iff the pile was modified.
+ virtual bool UpdateAndExpandInvalidation(ContentLayerClient* painter,
+ Region* invalidation,
+ const gfx::Size& layer_size,
+ const gfx::Rect& visible_layer_rect,
+ int frame_number,
+ RecordingMode recording_mode) = 0;
+
+ virtual scoped_refptr<RasterSource> CreateRasterSource(
+ bool can_use_lcd_text) const = 0;
+
+ virtual gfx::Size GetSize() const = 0;
+ virtual void SetEmptyBounds() = 0;
+ virtual void SetSlowdownRasterScaleFactor(int factor) = 0;
+ virtual void SetGatherPixelRefs(bool gather_pixel_refs) = 0;
+ virtual void SetBackgroundColor(SkColor background_color) = 0;
+ virtual void SetRequiresClear(bool requires_clear) = 0;
+ virtual bool IsSuitableForGpuRasterization() const = 0;
+
+ // TODO(hendrikw): Figure out how to remove this.
+ virtual void SetUnsuitableForGpuRasterizationForTesting() = 0;
+ virtual gfx::Size GetTileGridSizeForTesting() const = 0;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_RECORDING_SOURCE_H_
diff --git a/cc/playback/recording_source_unittest.cc b/cc/playback/recording_source_unittest.cc
new file mode 100644
index 0000000..01a89a1
--- /dev/null
+++ b/cc/playback/recording_source_unittest.cc
@@ -0,0 +1,470 @@
+// Copyright 2015 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 <vector>
+
+#include "cc/playback/display_list_raster_source.h"
+#include "cc/test/fake_display_list_recording_source.h"
+#include "cc/test/fake_picture_pile.h"
+#include "cc/test/fake_picture_pile_impl.h"
+#include "cc/test/impl_side_painting_settings.h"
+#include "cc/test/skia_common.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+namespace {
+
+template <class T>
+scoped_ptr<T> CreateRecordingSource(const gfx::Rect& viewport,
+ const gfx::Size& grid_cell_size);
+
+template <>
+scoped_ptr<FakePicturePile> CreateRecordingSource<FakePicturePile>(
+ const gfx::Rect& viewport,
+ const gfx::Size& grid_cell_size) {
+ return FakePicturePile::CreateFilledPile(grid_cell_size, viewport.size());
+}
+
+template <>
+scoped_ptr<FakeDisplayListRecordingSource> CreateRecordingSource<
+ FakeDisplayListRecordingSource>(const gfx::Rect& viewport,
+ const gfx::Size& grid_cell_size) {
+ scoped_ptr<FakeDisplayListRecordingSource> recording_source =
+ FakeDisplayListRecordingSource::CreateRecordingSource(viewport);
+ recording_source->SetGridCellSize(grid_cell_size);
+
+ return recording_source.Pass();
+}
+
+template <class T>
+scoped_refptr<RasterSource> CreateRasterSource(T* recording_source);
+
+template <>
+scoped_refptr<RasterSource> CreateRasterSource(
+ FakePicturePile* recording_source) {
+ return FakePicturePileImpl::CreateFromPile(recording_source, nullptr);
+}
+
+template <>
+scoped_refptr<RasterSource> CreateRasterSource(
+ FakeDisplayListRecordingSource* recording_source) {
+ bool can_use_lcd_text = true;
+ return DisplayListRasterSource::CreateFromDisplayListRecordingSource(
+ recording_source, can_use_lcd_text);
+}
+
+template <typename T>
+class RecordingSourceTest : public testing::Test {};
+
+using testing::Types;
+
+typedef Types<FakePicturePile, FakeDisplayListRecordingSource>
+ RecordingSourceImplementations;
+
+TYPED_TEST_CASE(RecordingSourceTest, RecordingSourceImplementations);
+
+TYPED_TEST(RecordingSourceTest, NoGatherPixelRefEmptyPixelRefs) {
+ gfx::Size grid_cell_size(128, 128);
+ gfx::Rect recorded_viewport(0, 0, 256, 256);
+
+ scoped_ptr<TypeParam> recording_source =
+ CreateRecordingSource<TypeParam>(recorded_viewport, grid_cell_size);
+ recording_source->SetGatherPixelRefs(false);
+ recording_source->Rerecord();
+
+ scoped_refptr<RasterSource> raster_source =
+ CreateRasterSource<TypeParam>(recording_source.get());
+
+ // If recording source do not gather pixel ref, raster source is not going to
+ // get pixel refs.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(recorded_viewport, 1.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+}
+
+TYPED_TEST(RecordingSourceTest, EmptyPixelRefs) {
+ gfx::Size grid_cell_size(128, 128);
+ gfx::Rect recorded_viewport(0, 0, 256, 256);
+
+ scoped_ptr<TypeParam> recording_source =
+ CreateRecordingSource<TypeParam>(recorded_viewport, grid_cell_size);
+ recording_source->SetGatherPixelRefs(true);
+ recording_source->Rerecord();
+
+ scoped_refptr<RasterSource> raster_source =
+ CreateRasterSource<TypeParam>(recording_source.get());
+
+ // Tile sized iterators.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 1.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 2.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 64, 64), 0.5, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ // Shifted tile sized iterators.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(140, 140, 128, 128), 1.0,
+ &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(280, 280, 256, 256), 2.0,
+ &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(70, 70, 64, 64), 0.5, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ // Layer sized iterators.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 1.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 512, 512), 2.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 0.5, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+}
+
+TYPED_TEST(RecordingSourceTest, NoDiscardablePixelRefs) {
+ gfx::Size grid_cell_size(128, 128);
+ gfx::Rect recorded_viewport(0, 0, 256, 256);
+
+ scoped_ptr<TypeParam> recording_source =
+ CreateRecordingSource<TypeParam>(recorded_viewport, grid_cell_size);
+
+ SkPaint simple_paint;
+ simple_paint.setColor(SkColorSetARGB(255, 12, 23, 34));
+
+ SkBitmap non_discardable_bitmap;
+ CreateBitmap(gfx::Size(128, 128), "notdiscardable", &non_discardable_bitmap);
+
+ recording_source->add_draw_rect_with_paint(gfx::Rect(0, 0, 256, 256),
+ simple_paint);
+ recording_source->add_draw_rect_with_paint(gfx::Rect(128, 128, 512, 512),
+ simple_paint);
+ recording_source->add_draw_rect_with_paint(gfx::Rect(512, 0, 256, 256),
+ simple_paint);
+ recording_source->add_draw_rect_with_paint(gfx::Rect(0, 512, 256, 256),
+ simple_paint);
+ recording_source->add_draw_bitmap(non_discardable_bitmap, gfx::Point(128, 0));
+ recording_source->add_draw_bitmap(non_discardable_bitmap, gfx::Point(0, 128));
+ recording_source->add_draw_bitmap(non_discardable_bitmap,
+ gfx::Point(150, 150));
+ recording_source->SetGatherPixelRefs(true);
+ recording_source->Rerecord();
+
+ scoped_refptr<RasterSource> raster_source =
+ CreateRasterSource<TypeParam>(recording_source.get());
+
+ // Tile sized iterators.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 1.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 2.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 64, 64), 0.5, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ // Shifted tile sized iterators.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(140, 140, 128, 128), 1.0,
+ &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(280, 280, 256, 256), 2.0,
+ &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(70, 70, 64, 64), 0.5, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ // Layer sized iterators.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 1.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 512, 512), 2.0, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 0.5, &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+}
+
+TYPED_TEST(RecordingSourceTest, DiscardablePixelRefs) {
+ gfx::Size grid_cell_size(128, 128);
+ gfx::Rect recorded_viewport(0, 0, 256, 256);
+
+ scoped_ptr<TypeParam> recording_source =
+ CreateRecordingSource<TypeParam>(recorded_viewport, grid_cell_size);
+
+ SkBitmap discardable_bitmap[2][2];
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[0][0]);
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[1][0]);
+ CreateBitmap(gfx::Size(32, 32), "discardable", &discardable_bitmap[1][1]);
+
+ // Discardable pixel refs are found in the following cells:
+ // |---|---|
+ // | x | |
+ // |---|---|
+ // | x | x |
+ // |---|---|
+ recording_source->add_draw_bitmap(discardable_bitmap[0][0], gfx::Point(0, 0));
+ recording_source->add_draw_bitmap(discardable_bitmap[1][0],
+ gfx::Point(0, 130));
+ recording_source->add_draw_bitmap(discardable_bitmap[1][1],
+ gfx::Point(140, 140));
+ recording_source->SetGatherPixelRefs(true);
+ recording_source->Rerecord();
+
+ scoped_refptr<RasterSource> raster_source =
+ CreateRasterSource<TypeParam>(recording_source.get());
+
+ // Tile sized iterators. These should find only one pixel ref.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 1.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 2.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 64, 64), 0.5, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+
+ // Shifted tile sized iterators. These should find only one pixel ref.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(140, 140, 128, 128), 1.0,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(280, 280, 256, 256), 2.0,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(70, 70, 64, 64), 0.5, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+
+ // Ensure there's no discardable pixel refs in the empty cell
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(140, 0, 128, 128), 1.0,
+ &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+
+ // Layer sized iterators. These should find all 3 pixel refs.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 1.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(3u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 512, 512), 2.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(3u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 0.5, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[1][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(3u, pixel_refs.size());
+ }
+}
+
+TYPED_TEST(RecordingSourceTest, DiscardablePixelRefsBaseNonDiscardable) {
+ gfx::Size grid_cell_size(256, 256);
+ gfx::Rect recorded_viewport(0, 0, 512, 512);
+
+ scoped_ptr<TypeParam> recording_source =
+ CreateRecordingSource<TypeParam>(recorded_viewport, grid_cell_size);
+
+ SkBitmap non_discardable_bitmap;
+ CreateBitmap(gfx::Size(512, 512), "notdiscardable", &non_discardable_bitmap);
+
+ SkBitmap discardable_bitmap[2][2];
+ CreateBitmap(gfx::Size(128, 128), "discardable", &discardable_bitmap[0][0]);
+ CreateBitmap(gfx::Size(128, 128), "discardable", &discardable_bitmap[0][1]);
+ CreateBitmap(gfx::Size(128, 128), "discardable", &discardable_bitmap[1][1]);
+
+ // One large non-discardable bitmap covers the whole grid.
+ // Discardable pixel refs are found in the following cells:
+ // |---|---|
+ // | x | x |
+ // |---|---|
+ // | | x |
+ // |---|---|
+ recording_source->add_draw_bitmap(non_discardable_bitmap, gfx::Point(0, 0));
+ recording_source->add_draw_bitmap(discardable_bitmap[0][0], gfx::Point(0, 0));
+ recording_source->add_draw_bitmap(discardable_bitmap[0][1],
+ gfx::Point(260, 0));
+ recording_source->add_draw_bitmap(discardable_bitmap[1][1],
+ gfx::Point(260, 260));
+ recording_source->SetGatherPixelRefs(true);
+ recording_source->Rerecord();
+
+ scoped_refptr<RasterSource> raster_source =
+ CreateRasterSource<TypeParam>(recording_source.get());
+
+ // Tile sized iterators. These should find only one pixel ref.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 1.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 512, 512), 2.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 128, 128), 0.5, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ // Shifted tile sized iterators. These should find only one pixel ref.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(260, 260, 256, 256), 1.0,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(520, 520, 512, 512), 2.0,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(130, 130, 128, 128), 0.5,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(1u, pixel_refs.size());
+ }
+ // Ensure there's no discardable pixel refs in the empty cell
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 256, 256, 256), 1.0,
+ &pixel_refs);
+ EXPECT_TRUE(pixel_refs.empty());
+ }
+ // Layer sized iterators. These should find three pixel ref.
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 512, 512), 1.0, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[0][1].pixelRef());
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(3u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 1024, 1024), 2.0,
+ &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[0][1].pixelRef());
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(3u, pixel_refs.size());
+ }
+ {
+ std::vector<SkPixelRef*> pixel_refs;
+ raster_source->GatherPixelRefs(gfx::Rect(0, 0, 256, 256), 0.5, &pixel_refs);
+ EXPECT_FALSE(pixel_refs.empty());
+ EXPECT_TRUE(pixel_refs[0] == discardable_bitmap[0][0].pixelRef());
+ EXPECT_TRUE(pixel_refs[1] == discardable_bitmap[0][1].pixelRef());
+ EXPECT_TRUE(pixel_refs[2] == discardable_bitmap[1][1].pixelRef());
+ EXPECT_EQ(3u, pixel_refs.size());
+ }
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/playback/transform_display_item.cc b/cc/playback/transform_display_item.cc
new file mode 100644
index 0000000..1f4e5db
--- /dev/null
+++ b/cc/playback/transform_display_item.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 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/playback/transform_display_item.h"
+
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+
+namespace cc {
+
+TransformDisplayItem::TransformDisplayItem()
+ : transform_(gfx::Transform::kSkipInitialization) {
+}
+
+TransformDisplayItem::~TransformDisplayItem() {
+}
+
+void TransformDisplayItem::SetNew(const gfx::Transform& transform) {
+ transform_ = transform;
+
+ size_t memory_usage = sizeof(gfx::Transform);
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 1 /* op_count */,
+ memory_usage);
+}
+
+void TransformDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->save();
+ if (!transform_.IsIdentity())
+ canvas->concat(transform_.matrix());
+}
+
+void TransformDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString(base::StringPrintf("TransformDisplayItem transform: [%s]",
+ transform_.ToString().c_str()));
+}
+
+EndTransformDisplayItem::EndTransformDisplayItem() {
+ DisplayItem::SetNew(true /* suitable_for_gpu_raster */, 0 /* op_count */,
+ 0 /* memory_usage */);
+}
+
+EndTransformDisplayItem::~EndTransformDisplayItem() {
+}
+
+void EndTransformDisplayItem::Raster(SkCanvas* canvas,
+ SkDrawPictureCallback* callback) const {
+ canvas->restore();
+}
+
+void EndTransformDisplayItem::AsValueInto(
+ base::trace_event::TracedValue* array) const {
+ array->AppendString("EndTransformDisplayItem");
+}
+
+} // namespace cc
diff --git a/cc/playback/transform_display_item.h b/cc/playback/transform_display_item.h
new file mode 100644
index 0000000..731249d
--- /dev/null
+++ b/cc/playback/transform_display_item.h
@@ -0,0 +1,47 @@
+// Copyright 2014 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_PLAYBACK_TRANSFORM_DISPLAY_ITEM_H_
+#define CC_PLAYBACK_TRANSFORM_DISPLAY_ITEM_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/playback/display_item.h"
+#include "ui/gfx/transform.h"
+
+class SkCanvas;
+class SkDrawPictureCallback;
+
+namespace cc {
+
+class CC_EXPORT TransformDisplayItem : public DisplayItem {
+ public:
+ TransformDisplayItem();
+ ~TransformDisplayItem() override;
+
+ void SetNew(const gfx::Transform& transform);
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+
+ private:
+ gfx::Transform transform_;
+};
+
+class CC_EXPORT EndTransformDisplayItem : public DisplayItem {
+ public:
+ EndTransformDisplayItem();
+ ~EndTransformDisplayItem() override;
+
+ static scoped_ptr<EndTransformDisplayItem> Create() {
+ return make_scoped_ptr(new EndTransformDisplayItem());
+ }
+
+ void Raster(SkCanvas* canvas, SkDrawPictureCallback* callback) const override;
+ void AsValueInto(base::trace_event::TracedValue* array) const override;
+};
+
+} // namespace cc
+
+#endif // CC_PLAYBACK_TRANSFORM_DISPLAY_ITEM_H_