summaryrefslogtreecommitdiff
path: root/chromium/cc/paint
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/cc/paint')
-rw-r--r--chromium/cc/paint/BUILD.gn40
-rw-r--r--chromium/cc/paint/clip_display_item.cc23
-rw-r--r--chromium/cc/paint/clip_display_item.h46
-rw-r--r--chromium/cc/paint/clip_path_display_item.cc19
-rw-r--r--chromium/cc/paint/clip_path_display_item.h42
-rw-r--r--chromium/cc/paint/compositing_display_item.cc32
-rw-r--r--chromium/cc/paint/compositing_display_item.h52
-rw-r--r--chromium/cc/paint/discardable_image_map.cc305
-rw-r--r--chromium/cc/paint/discardable_image_map.h69
-rw-r--r--chromium/cc/paint/discardable_image_map_unittest.cc652
-rw-r--r--chromium/cc/paint/display_item.h47
-rw-r--r--chromium/cc/paint/display_item_list.cc501
-rw-r--r--chromium/cc/paint/display_item_list.h209
-rw-r--r--chromium/cc/paint/display_item_list_unittest.cc707
-rw-r--r--chromium/cc/paint/draw_image.cc50
-rw-r--r--chromium/cc/paint/draw_image.h65
-rw-r--r--chromium/cc/paint/drawing_display_item.cc30
-rw-r--r--chromium/cc/paint/drawing_display_item.h32
-rw-r--r--chromium/cc/paint/filter_display_item.cc20
-rw-r--r--chromium/cc/paint/filter_display_item.h45
-rw-r--r--chromium/cc/paint/float_clip_display_item.cc19
-rw-r--r--chromium/cc/paint/float_clip_display_item.h37
-rw-r--r--chromium/cc/paint/image_id.h20
-rw-r--r--chromium/cc/paint/largest_display_item.cc78
-rw-r--r--chromium/cc/paint/largest_display_item.h18
-rw-r--r--chromium/cc/paint/paint_canvas.cc30
-rw-r--r--chromium/cc/paint/paint_canvas.h222
-rw-r--r--chromium/cc/paint/paint_flags.h204
-rw-r--r--chromium/cc/paint/paint_record.h9
-rw-r--r--chromium/cc/paint/paint_recorder.cc12
-rw-r--r--chromium/cc/paint/paint_recorder.h49
-rw-r--r--chromium/cc/paint/paint_shader.h12
-rw-r--r--chromium/cc/paint/paint_surface.h14
-rw-r--r--chromium/cc/paint/skia_paint_canvas.cc311
-rw-r--r--chromium/cc/paint/skia_paint_canvas.h165
-rw-r--r--chromium/cc/paint/transform_display_item.cc20
-rw-r--r--chromium/cc/paint/transform_display_item.h37
37 files changed, 4169 insertions, 74 deletions
diff --git a/chromium/cc/paint/BUILD.gn b/chromium/cc/paint/BUILD.gn
index a37d4a6dae5..c442f045829 100644
--- a/chromium/cc/paint/BUILD.gn
+++ b/chromium/cc/paint/BUILD.gn
@@ -7,21 +7,57 @@ import("//cc/cc.gni")
cc_component("paint") {
output_name = "cc_paint"
sources = [
+ "clip_display_item.cc",
+ "clip_display_item.h",
+ "clip_path_display_item.cc",
+ "clip_path_display_item.h",
+ "compositing_display_item.cc",
+ "compositing_display_item.h",
+ "discardable_image_map.cc",
+ "discardable_image_map.h",
+ "display_item.h",
+ "display_item_list.cc",
+ "display_item_list.h",
+ "draw_image.cc",
+ "draw_image.h",
+ "drawing_display_item.cc",
+ "drawing_display_item.h",
+ "filter_display_item.cc",
+ "filter_display_item.h",
+ "float_clip_display_item.cc",
+ "float_clip_display_item.h",
+ "image_id.h",
+ "largest_display_item.cc",
+ "largest_display_item.h",
"paint_canvas.cc",
"paint_canvas.h",
"paint_export.h",
"paint_flags.h",
"paint_record.h",
+ "paint_recorder.cc",
"paint_recorder.h",
"paint_shader.h",
- "paint_surface.h",
+ "skia_paint_canvas.cc",
+ "skia_paint_canvas.h",
+ "transform_display_item.cc",
+ "transform_display_item.h",
]
defines = [ "CC_PAINT_IMPLEMENTATION=1" ]
# cc/paint is intended to be a separate component from cc that can be
- # included in Blink. This component should never include //base.
+ # included in Blink. This component should never publicly include
+ # anything that Blink core wouldn't include (e.g. base).
public_deps = [
+ "//cc/base",
+ "//cc/debug",
"//skia",
+ "//ui/gfx:color_space",
+ "//ui/gfx:geometry_skia",
+ "//ui/gfx/geometry",
+ ]
+
+ deps = [
+ "//base",
]
}
diff --git a/chromium/cc/paint/clip_display_item.cc b/chromium/cc/paint/clip_display_item.cc
new file mode 100644
index 00000000000..e2095c00b73
--- /dev/null
+++ b/chromium/cc/paint/clip_display_item.cc
@@ -0,0 +1,23 @@
+// 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/paint/clip_display_item.h"
+
+namespace cc {
+
+ClipDisplayItem::ClipDisplayItem(const gfx::Rect& clip_rect,
+ std::vector<SkRRect> rounded_clip_rects,
+ bool antialias)
+ : DisplayItem(CLIP),
+ clip_rect(clip_rect),
+ rounded_clip_rects(std::move(rounded_clip_rects)),
+ antialias(antialias) {}
+
+ClipDisplayItem::~ClipDisplayItem() = default;
+
+EndClipDisplayItem::EndClipDisplayItem() : DisplayItem(END_CLIP) {}
+
+EndClipDisplayItem::~EndClipDisplayItem() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/clip_display_item.h b/chromium/cc/paint/clip_display_item.h
new file mode 100644
index 00000000000..a969d48dd5b
--- /dev/null
+++ b/chromium/cc/paint/clip_display_item.h
@@ -0,0 +1,46 @@
+// 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_PAINT_CLIP_DISPLAY_ITEM_H_
+#define CC_PAINT_CLIP_DISPLAY_ITEM_H_
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "cc/paint/display_item.h"
+#include "cc/paint/paint_export.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT ClipDisplayItem : public DisplayItem {
+ public:
+ ClipDisplayItem(const gfx::Rect& clip_rect,
+ std::vector<SkRRect> rounded_clip_rects,
+ bool antialias);
+ ~ClipDisplayItem() override;
+
+ size_t ExternalMemoryUsage() const {
+ return rounded_clip_rects.capacity() * sizeof(rounded_clip_rects[0]);
+ }
+ int ApproximateOpCount() const { return 1; }
+
+ const gfx::Rect clip_rect;
+ const std::vector<SkRRect> rounded_clip_rects;
+ const bool antialias;
+};
+
+class CC_PAINT_EXPORT EndClipDisplayItem : public DisplayItem {
+ public:
+ EndClipDisplayItem();
+ ~EndClipDisplayItem() override;
+
+ int ApproximateOpCount() const { return 0; }
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_CLIP_DISPLAY_ITEM_H_
diff --git a/chromium/cc/paint/clip_path_display_item.cc b/chromium/cc/paint/clip_path_display_item.cc
new file mode 100644
index 00000000000..5882e5db928
--- /dev/null
+++ b/chromium/cc/paint/clip_path_display_item.cc
@@ -0,0 +1,19 @@
+// 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/paint/clip_path_display_item.h"
+
+namespace cc {
+
+ClipPathDisplayItem::ClipPathDisplayItem(const SkPath& clip_path,
+ bool antialias)
+ : DisplayItem(CLIP_PATH), clip_path(clip_path), antialias(antialias) {}
+
+ClipPathDisplayItem::~ClipPathDisplayItem() = default;
+
+EndClipPathDisplayItem::EndClipPathDisplayItem() : DisplayItem(END_CLIP_PATH) {}
+
+EndClipPathDisplayItem::~EndClipPathDisplayItem() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/clip_path_display_item.h b/chromium/cc/paint/clip_path_display_item.h
new file mode 100644
index 00000000000..54de9992233
--- /dev/null
+++ b/chromium/cc/paint/clip_path_display_item.h
@@ -0,0 +1,42 @@
+// 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_PAINT_CLIP_PATH_DISPLAY_ITEM_H_
+#define CC_PAINT_CLIP_PATH_DISPLAY_ITEM_H_
+
+#include <stddef.h>
+
+#include "cc/paint/display_item.h"
+#include "cc/paint/paint_export.h"
+#include "third_party/skia/include/core/SkPath.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT ClipPathDisplayItem : public DisplayItem {
+ public:
+ ClipPathDisplayItem(const SkPath& path, bool antialias);
+ ~ClipPathDisplayItem() override;
+
+ size_t ExternalMemoryUsage() const {
+ // The size of SkPath's external storage is not currently accounted for (and
+ // may well be shared anyway).
+ return 0;
+ }
+ int ApproximateOpCount() const { return 1; }
+
+ const SkPath clip_path;
+ const bool antialias;
+};
+
+class CC_PAINT_EXPORT EndClipPathDisplayItem : public DisplayItem {
+ public:
+ EndClipPathDisplayItem();
+ ~EndClipPathDisplayItem() override;
+
+ int ApproximateOpCount() const { return 0; }
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_CLIP_PATH_DISPLAY_ITEM_H_
diff --git a/chromium/cc/paint/compositing_display_item.cc b/chromium/cc/paint/compositing_display_item.cc
new file mode 100644
index 00000000000..9cda4580bdb
--- /dev/null
+++ b/chromium/cc/paint/compositing_display_item.cc
@@ -0,0 +1,32 @@
+// 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/paint/compositing_display_item.h"
+
+#include "third_party/skia/include/core/SkColorFilter.h"
+
+namespace cc {
+
+CompositingDisplayItem::CompositingDisplayItem(
+ uint8_t alpha,
+ SkBlendMode xfermode,
+ SkRect* bounds,
+ sk_sp<SkColorFilter> color_filter,
+ bool lcd_text_requires_opaque_layer)
+ : DisplayItem(COMPOSITING),
+ alpha(alpha),
+ xfermode(xfermode),
+ has_bounds(!!bounds),
+ bounds(bounds ? SkRect(*bounds) : SkRect()),
+ color_filter(std::move(color_filter)),
+ lcd_text_requires_opaque_layer(lcd_text_requires_opaque_layer) {}
+
+CompositingDisplayItem::~CompositingDisplayItem() = default;
+
+EndCompositingDisplayItem::EndCompositingDisplayItem()
+ : DisplayItem(END_COMPOSITING) {}
+
+EndCompositingDisplayItem::~EndCompositingDisplayItem() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/compositing_display_item.h b/chromium/cc/paint/compositing_display_item.h
new file mode 100644
index 00000000000..266ab4d2ea8
--- /dev/null
+++ b/chromium/cc/paint/compositing_display_item.h
@@ -0,0 +1,52 @@
+// 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_PAINT_COMPOSITING_DISPLAY_ITEM_H_
+#define CC_PAINT_COMPOSITING_DISPLAY_ITEM_H_
+
+#include <stddef.h>
+
+#include "cc/paint/display_item.h"
+#include "cc/paint/paint_export.h"
+#include "third_party/skia/include/core/SkBlendMode.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT CompositingDisplayItem : public DisplayItem {
+ public:
+ CompositingDisplayItem(uint8_t alpha,
+ SkBlendMode xfermode,
+ SkRect* bounds,
+ sk_sp<SkColorFilter> color_filter,
+ bool lcd_text_requires_opaque_layer);
+ ~CompositingDisplayItem() override;
+
+ size_t ExternalMemoryUsage() const {
+ // TODO(pdr): Include color_filter's memory here.
+ return 0;
+ }
+ int ApproximateOpCount() const { return 1; }
+
+ const uint8_t alpha;
+ const SkBlendMode xfermode;
+ const bool has_bounds;
+ const SkRect bounds;
+ const sk_sp<SkColorFilter> color_filter;
+ const bool lcd_text_requires_opaque_layer;
+};
+
+class CC_PAINT_EXPORT EndCompositingDisplayItem : public DisplayItem {
+ public:
+ EndCompositingDisplayItem();
+ ~EndCompositingDisplayItem() override;
+
+ int ApproximateOpCount() const { return 0; }
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_COMPOSITING_DISPLAY_ITEM_H_
diff --git a/chromium/cc/paint/discardable_image_map.cc b/chromium/cc/paint/discardable_image_map.cc
new file mode 100644
index 00000000000..c1a3b57ccb5
--- /dev/null
+++ b/chromium/cc/paint/discardable_image_map.cc
@@ -0,0 +1,305 @@
+// 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/paint/discardable_image_map.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <limits>
+
+#include "base/containers/adapters.h"
+#include "base/memory/ptr_util.h"
+#include "cc/base/math_util.h"
+#include "cc/paint/display_item_list.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/utils/SkNWayCanvas.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+SkRect MapRect(const SkMatrix& matrix, const SkRect& src) {
+ SkRect dst;
+ matrix.mapRect(&dst, src);
+ return dst;
+}
+
+// Returns a rect clamped to |max_size|. Note that |paint_rect| should intersect
+// or be contained by a rect defined by (0, 0) and |max_size|.
+gfx::Rect SafeClampPaintRectToSize(const SkRect& paint_rect,
+ const gfx::Size& max_size) {
+ // bounds_rect.x() + bounds_rect.width() (aka bounds_rect.right()) might
+ // overflow integer bounds, so do custom intersect, since gfx::Rect::Intersect
+ // uses bounds_rect.right().
+ gfx::RectF bounds_rect = gfx::SkRectToRectF(paint_rect);
+ float x_offset_if_negative = bounds_rect.x() < 0.f ? bounds_rect.x() : 0.f;
+ float y_offset_if_negative = bounds_rect.y() < 0.f ? bounds_rect.y() : 0.f;
+ bounds_rect.set_x(std::max(0.f, bounds_rect.x()));
+ bounds_rect.set_y(std::max(0.f, bounds_rect.y()));
+
+ // Verify that the rects intersect or that bound_rect is contained by
+ // max_size.
+ DCHECK_GE(bounds_rect.width(), -x_offset_if_negative);
+ DCHECK_GE(bounds_rect.height(), -y_offset_if_negative);
+ DCHECK_GE(max_size.width(), bounds_rect.x());
+ DCHECK_GE(max_size.height(), bounds_rect.y());
+
+ bounds_rect.set_width(std::min(bounds_rect.width() + x_offset_if_negative,
+ max_size.width() - bounds_rect.x()));
+ bounds_rect.set_height(std::min(bounds_rect.height() + y_offset_if_negative,
+ max_size.height() - bounds_rect.y()));
+ return gfx::ToEnclosingRect(bounds_rect);
+}
+
+namespace {
+
+// We're using an NWay canvas with no added canvases, so in effect
+// non-overridden functions are no-ops.
+class DiscardableImagesMetadataCanvas : public SkNWayCanvas {
+ public:
+ DiscardableImagesMetadataCanvas(
+ int width,
+ int height,
+ std::vector<std::pair<DrawImage, gfx::Rect>>* image_set,
+ std::unordered_map<ImageId, gfx::Rect>* image_id_to_rect)
+ : SkNWayCanvas(width, height),
+ image_set_(image_set),
+ image_id_to_rect_(image_id_to_rect),
+ canvas_bounds_(SkRect::MakeIWH(width, height)),
+ canvas_size_(width, height) {}
+
+ protected:
+ // we need to "undo" the behavior of SkNWayCanvas, which will try to forward
+ // it.
+ void onDrawPicture(const SkPicture* picture,
+ const SkMatrix* matrix,
+ const SkPaint* paint) override {
+ SkCanvas::onDrawPicture(picture, matrix, paint);
+ }
+
+ void onDrawImage(const SkImage* image,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint* paint) override {
+ const SkMatrix& ctm = getTotalMatrix();
+ AddImage(
+ sk_ref_sp(image), SkRect::MakeIWH(image->width(), image->height()),
+ MapRect(ctm, SkRect::MakeXYWH(x, y, image->width(), image->height())),
+ ctm, paint);
+ }
+
+ void onDrawImageRect(const SkImage* image,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint* paint,
+ SrcRectConstraint) override {
+ const SkMatrix& ctm = getTotalMatrix();
+ SkRect src_storage;
+ if (!src) {
+ src_storage = SkRect::MakeIWH(image->width(), image->height());
+ src = &src_storage;
+ }
+ SkMatrix matrix;
+ matrix.setRectToRect(*src, dst, SkMatrix::kFill_ScaleToFit);
+ matrix.preConcat(ctm);
+ AddImage(sk_ref_sp(image), *src, MapRect(ctm, dst), matrix, paint);
+ }
+
+ void onDrawImageNine(const SkImage* image,
+ const SkIRect& center,
+ const SkRect& dst,
+ const SkPaint* paint) override {
+ // No cc embedder issues image nine calls.
+ NOTREACHED();
+ }
+
+ void onDrawRect(const SkRect& r, const SkPaint& paint) override {
+ AddPaintImage(r, paint);
+ }
+
+ void onDrawPath(const SkPath& path, const SkPaint& paint) override {
+ AddPaintImage(path.getBounds(), paint);
+ }
+
+ void onDrawOval(const SkRect& r, const SkPaint& paint) override {
+ AddPaintImage(r, paint);
+ }
+
+ void onDrawArc(const SkRect& r,
+ SkScalar start_angle,
+ SkScalar sweep_angle,
+ bool use_center,
+ const SkPaint& paint) override {
+ AddPaintImage(r, paint);
+ }
+
+ void onDrawRRect(const SkRRect& rr, const SkPaint& paint) override {
+ AddPaintImage(rr.rect(), paint);
+ }
+
+ SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
+ saved_paints_.push_back(rec.fPaint ? *rec.fPaint : SkPaint());
+ return SkNWayCanvas::getSaveLayerStrategy(rec);
+ }
+
+ void willSave() override {
+ saved_paints_.push_back(SkPaint());
+ return SkNWayCanvas::willSave();
+ }
+
+ void willRestore() override {
+ DCHECK_GT(saved_paints_.size(), 0u);
+ saved_paints_.pop_back();
+ SkNWayCanvas::willRestore();
+ }
+
+ private:
+ bool ComputePaintBounds(const SkRect& rect,
+ const SkPaint* current_paint,
+ SkRect* paint_bounds) {
+ *paint_bounds = rect;
+ if (current_paint) {
+ if (!current_paint->canComputeFastBounds())
+ return false;
+ *paint_bounds =
+ current_paint->computeFastBounds(*paint_bounds, paint_bounds);
+ }
+
+ for (const auto& paint : base::Reversed(saved_paints_)) {
+ if (!paint.canComputeFastBounds())
+ return false;
+ *paint_bounds = paint.computeFastBounds(*paint_bounds, paint_bounds);
+ }
+
+ return true;
+ }
+
+ void AddImage(sk_sp<const SkImage> image,
+ const SkRect& src_rect,
+ const SkRect& rect,
+ const SkMatrix& matrix,
+ const SkPaint* paint) {
+ if (!image->isLazyGenerated())
+ return;
+
+ SkRect paint_rect;
+ bool computed_paint_bounds = ComputePaintBounds(rect, paint, &paint_rect);
+ if (!computed_paint_bounds) {
+ // TODO(vmpstr): UMA this case.
+ paint_rect = canvas_bounds_;
+ }
+
+ if (!paint_rect.intersects(canvas_bounds_))
+ return;
+
+ SkFilterQuality filter_quality = kNone_SkFilterQuality;
+ if (paint) {
+ filter_quality = paint->getFilterQuality();
+ }
+
+ SkIRect src_irect;
+ src_rect.roundOut(&src_irect);
+ gfx::Rect image_rect = SafeClampPaintRectToSize(paint_rect, canvas_size_);
+
+ // During raster, we use the device clip bounds on the canvas, which outsets
+ // the actual clip by 1 due to the possibility of antialiasing. Account for
+ // this here by outsetting the image rect by 1. Note that this only affects
+ // queries into the rtree, which will now return images that only touch the
+ // bounds of the query rect.
+ //
+ // Note that it's not sufficient for us to inset the device clip bounds at
+ // raster time, since we might be sending a larger-than-one-item display
+ // item to skia, which means that skia will internally determine whether to
+ // raster the picture (using device clip bounds that are outset).
+ image_rect.Inset(-1, -1);
+
+ // The true target color space will be assigned when it is known, in
+ // GetDiscardableImagesInRect.
+ gfx::ColorSpace target_color_space;
+
+ (*image_id_to_rect_)[image->uniqueID()].Union(image_rect);
+ image_set_->push_back(
+ std::make_pair(DrawImage(std::move(image), src_irect, filter_quality,
+ matrix, target_color_space),
+ image_rect));
+ }
+
+ // Currently this function only handles extracting images from SkImageShaders
+ // embedded in SkPaints. Other embedded image cases, such as SkPictures,
+ // are not yet handled.
+ void AddPaintImage(const SkRect& rect, const SkPaint& paint) {
+ SkShader* shader = paint.getShader();
+ if (shader) {
+ SkMatrix matrix;
+ SkShader::TileMode xy[2];
+ SkImage* image = shader->isAImage(&matrix, xy);
+ if (image) {
+ const SkMatrix& ctm = getTotalMatrix();
+ matrix.postConcat(ctm);
+ // TODO(ericrk): Handle cases where we only need a sub-rect from the
+ // image. crbug.com/671821
+ AddImage(sk_ref_sp(image), SkRect::MakeFromIRect(image->bounds()),
+ MapRect(ctm, rect), matrix, &paint);
+ }
+ }
+ }
+
+ std::vector<std::pair<DrawImage, gfx::Rect>>* image_set_;
+ std::unordered_map<ImageId, gfx::Rect>* image_id_to_rect_;
+ const SkRect canvas_bounds_;
+ const gfx::Size canvas_size_;
+ std::vector<SkPaint> saved_paints_;
+};
+
+} // namespace
+
+DiscardableImageMap::DiscardableImageMap() {}
+
+DiscardableImageMap::~DiscardableImageMap() {}
+
+std::unique_ptr<SkCanvas> DiscardableImageMap::BeginGeneratingMetadata(
+ const gfx::Size& bounds) {
+ DCHECK(all_images_.empty());
+ return base::MakeUnique<DiscardableImagesMetadataCanvas>(
+ bounds.width(), bounds.height(), &all_images_, &image_id_to_rect_);
+}
+
+void DiscardableImageMap::EndGeneratingMetadata() {
+ images_rtree_.Build(all_images_,
+ [](const std::pair<DrawImage, gfx::Rect>& image) {
+ return image.second;
+ });
+}
+
+void DiscardableImageMap::GetDiscardableImagesInRect(
+ const gfx::Rect& rect,
+ float contents_scale,
+ const gfx::ColorSpace& target_color_space,
+ std::vector<DrawImage>* images) const {
+ std::vector<size_t> indices;
+ images_rtree_.Search(rect, &indices);
+ for (size_t index : indices) {
+ images->push_back(all_images_[index]
+ .first.ApplyScale(contents_scale)
+ .ApplyTargetColorSpace(target_color_space));
+ }
+}
+
+gfx::Rect DiscardableImageMap::GetRectForImage(ImageId image_id) const {
+ const auto& it = image_id_to_rect_.find(image_id);
+ return it == image_id_to_rect_.end() ? gfx::Rect() : it->second;
+}
+
+DiscardableImageMap::ScopedMetadataGenerator::ScopedMetadataGenerator(
+ DiscardableImageMap* image_map,
+ const gfx::Size& bounds)
+ : image_map_(image_map),
+ metadata_canvas_(image_map->BeginGeneratingMetadata(bounds)) {}
+
+DiscardableImageMap::ScopedMetadataGenerator::~ScopedMetadataGenerator() {
+ image_map_->EndGeneratingMetadata();
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/discardable_image_map.h b/chromium/cc/paint/discardable_image_map.h
new file mode 100644
index 00000000000..0e006838834
--- /dev/null
+++ b/chromium/cc/paint/discardable_image_map.h
@@ -0,0 +1,69 @@
+// 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_PAINT_DISCARDABLE_IMAGE_MAP_H_
+#define CC_PAINT_DISCARDABLE_IMAGE_MAP_H_
+
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "cc/base/rtree.h"
+#include "cc/paint/draw_image.h"
+#include "cc/paint/image_id.h"
+#include "cc/paint/paint_export.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace cc {
+
+// Helper function to apply the matrix to the rect and return the result.
+SkRect MapRect(const SkMatrix& matrix, const SkRect& src);
+
+// This class is used for generating discardable images data (see DrawImage
+// for the type of data it stores). It allows the client to query a particular
+// rect and get back a list of DrawImages in that rect.
+class CC_PAINT_EXPORT DiscardableImageMap {
+ public:
+ class CC_PAINT_EXPORT ScopedMetadataGenerator {
+ public:
+ ScopedMetadataGenerator(DiscardableImageMap* image_map,
+ const gfx::Size& bounds);
+ ~ScopedMetadataGenerator();
+
+ SkCanvas* canvas() { return metadata_canvas_.get(); }
+
+ private:
+ DiscardableImageMap* image_map_;
+ std::unique_ptr<SkCanvas> metadata_canvas_;
+ };
+
+ DiscardableImageMap();
+ ~DiscardableImageMap();
+
+ bool empty() const { return all_images_.empty(); }
+ void GetDiscardableImagesInRect(const gfx::Rect& rect,
+ float contents_scale,
+ const gfx::ColorSpace& target_color_space,
+ std::vector<DrawImage>* images) const;
+ gfx::Rect GetRectForImage(ImageId image_id) const;
+
+ private:
+ friend class ScopedMetadataGenerator;
+ friend class DiscardableImageMapTest;
+
+ std::unique_ptr<SkCanvas> BeginGeneratingMetadata(const gfx::Size& bounds);
+ void EndGeneratingMetadata();
+
+ std::vector<std::pair<DrawImage, gfx::Rect>> all_images_;
+ std::unordered_map<ImageId, gfx::Rect> image_id_to_rect_;
+
+ RTree images_rtree_;
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_DISCARDABLE_IMAGE_MAP_H_
diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc
new file mode 100644
index 00000000000..eb687ad27df
--- /dev/null
+++ b/chromium/cc/paint/discardable_image_map_unittest.cc
@@ -0,0 +1,652 @@
+// 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/paint/discardable_image_map.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "base/values.h"
+#include "cc/base/region.h"
+#include "cc/test/fake_content_layer_client.h"
+#include "cc/test/fake_recording_source.h"
+#include "cc/test/skia_common.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkGraphics.h"
+#include "third_party/skia/include/core/SkImageGenerator.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+namespace {
+
+struct PositionScaleDrawImage {
+ PositionScaleDrawImage(sk_sp<const SkImage> image,
+ const gfx::Rect& image_rect,
+ const SkSize& scale)
+ : image(std::move(image)), image_rect(image_rect), scale(scale) {}
+ sk_sp<const SkImage> image;
+ gfx::Rect image_rect;
+ SkSize scale;
+};
+
+} // namespace
+
+class DiscardableImageMapTest : public testing::Test {
+ public:
+ std::vector<PositionScaleDrawImage> GetDiscardableImagesInRect(
+ const DiscardableImageMap& image_map,
+ const gfx::Rect& rect) {
+ std::vector<DrawImage> draw_images;
+ // Choose a not-SRGB-and-not-invalid target color space to verify that it
+ // is passed correctly to the resulting DrawImages.
+ gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateXYZD50();
+ image_map.GetDiscardableImagesInRect(rect, 1.f, target_color_space,
+ &draw_images);
+
+ std::vector<size_t> indices;
+ image_map.images_rtree_.Search(rect, &indices);
+ std::vector<PositionScaleDrawImage> position_draw_images;
+ for (size_t index : indices) {
+ position_draw_images.push_back(
+ PositionScaleDrawImage(image_map.all_images_[index].first.image(),
+ image_map.all_images_[index].second,
+ image_map.all_images_[index].first.scale()));
+ }
+
+ EXPECT_EQ(draw_images.size(), position_draw_images.size());
+ for (size_t i = 0; i < draw_images.size(); ++i) {
+ EXPECT_TRUE(draw_images[i].image() == position_draw_images[i].image);
+ EXPECT_EQ(draw_images[i].target_color_space(), target_color_space);
+ }
+ return position_draw_images;
+ }
+
+ // Note that the image rtree outsets the images by 1, see the comment in
+ // DiscardableImagesMetadataCanvas::AddImage.
+ std::vector<gfx::Rect> InsetImageRects(
+ const std::vector<PositionScaleDrawImage>& images) {
+ std::vector<gfx::Rect> result;
+ for (auto& image : images) {
+ result.push_back(image.image_rect);
+ result.back().Inset(1, 1, 1, 1);
+ }
+ return result;
+ }
+};
+
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectTest) {
+ gfx::Rect visible_rect(2048, 2048);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ // Discardable pixel refs are found in the following grids:
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ sk_sp<SkImage> discardable_image[4][4];
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ if ((x + y) & 1) {
+ discardable_image[y][x] = CreateDiscardableImage(gfx::Size(500, 500));
+ PaintFlags flags;
+ content_layer_client.add_draw_image(
+ discardable_image[y][x], gfx::Point(x * 512 + 6, y * 512 + 6),
+ flags);
+ }
+ }
+ }
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f);
+ }
+
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
+ image_map, gfx::Rect(x * 512, y * 512, 500, 500));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ if ((x + y) & 1) {
+ EXPECT_EQ(1u, images.size()) << x << " " << y;
+ EXPECT_TRUE(images[0].image == discardable_image[y][x])
+ << x << " " << y;
+ EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
+ inset_rects[0]);
+ EXPECT_EQ(images[0].image_rect,
+ image_map.GetRectForImage(images[0].image->uniqueID()));
+ } else {
+ EXPECT_EQ(0u, images.size()) << x << " " << y;
+ }
+ }
+ }
+
+ // Capture 4 pixel refs.
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(512, 512, 2048, 2048));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ EXPECT_EQ(4u, images.size());
+
+ EXPECT_TRUE(images[0].image == discardable_image[1][2]);
+ EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]);
+ EXPECT_EQ(images[0].image_rect,
+ image_map.GetRectForImage(images[0].image->uniqueID()));
+
+ EXPECT_TRUE(images[1].image == discardable_image[2][1]);
+ EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]);
+ EXPECT_EQ(images[1].image_rect,
+ image_map.GetRectForImage(images[1].image->uniqueID()));
+
+ EXPECT_TRUE(images[2].image == discardable_image[2][3]);
+ EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[2]);
+ EXPECT_EQ(images[2].image_rect,
+ image_map.GetRectForImage(images[2].image->uniqueID()));
+
+ EXPECT_TRUE(images[3].image == discardable_image[3][2]);
+ EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500), inset_rects[3]);
+ EXPECT_EQ(images[3].image_rect,
+ image_map.GetRectForImage(images[3].image->uniqueID()));
+}
+
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectNonZeroLayer) {
+ gfx::Rect visible_rect(1024, 0, 2048, 2048);
+ // Make sure visible rect fits into the layer size.
+ gfx::Size layer_size(visible_rect.right(), visible_rect.bottom());
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(layer_size);
+
+ // Discardable pixel refs are found in the following grids:
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ sk_sp<SkImage> discardable_image[4][4];
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ if ((x + y) & 1) {
+ discardable_image[y][x] = CreateDiscardableImage(gfx::Size(500, 500));
+ PaintFlags flags;
+ content_layer_client.add_draw_image(
+ discardable_image[y][x],
+ gfx::Point(1024 + x * 512 + 6, y * 512 + 6), flags);
+ }
+ }
+ }
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ layer_size);
+ display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f);
+ }
+
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
+ image_map, gfx::Rect(1024 + x * 512, y * 512, 500, 500));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ if ((x + y) & 1) {
+ EXPECT_EQ(1u, images.size()) << x << " " << y;
+ EXPECT_TRUE(images[0].image == discardable_image[y][x])
+ << x << " " << y;
+ EXPECT_EQ(gfx::Rect(1024 + x * 512 + 6, y * 512 + 6, 500, 500),
+ inset_rects[0]);
+ EXPECT_EQ(images[0].image_rect,
+ image_map.GetRectForImage(images[0].image->uniqueID()));
+ } else {
+ EXPECT_EQ(0u, images.size()) << x << " " << y;
+ }
+ }
+ }
+ // Capture 4 pixel refs.
+ {
+ std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
+ image_map, gfx::Rect(1024 + 512, 512, 2048, 2048));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ EXPECT_EQ(4u, images.size());
+
+ EXPECT_TRUE(images[0].image == discardable_image[1][2]);
+ EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]);
+ EXPECT_EQ(images[0].image_rect,
+ image_map.GetRectForImage(images[0].image->uniqueID()));
+
+ EXPECT_TRUE(images[1].image == discardable_image[2][1]);
+ EXPECT_EQ(gfx::Rect(1024 + 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]);
+ EXPECT_EQ(images[1].image_rect,
+ image_map.GetRectForImage(images[1].image->uniqueID()));
+
+ EXPECT_TRUE(images[2].image == discardable_image[2][3]);
+ EXPECT_EQ(gfx::Rect(1024 + 3 * 512 + 6, 2 * 512 + 6, 500, 500),
+ inset_rects[2]);
+ EXPECT_EQ(images[2].image_rect,
+ image_map.GetRectForImage(images[2].image->uniqueID()));
+
+ EXPECT_TRUE(images[3].image == discardable_image[3][2]);
+ EXPECT_EQ(gfx::Rect(1024 + 2 * 512 + 6, 3 * 512 + 6, 500, 500),
+ inset_rects[3]);
+ EXPECT_EQ(images[3].image_rect,
+ image_map.GetRectForImage(images[3].image->uniqueID()));
+ }
+
+ // Non intersecting rects
+ {
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1000, 1000));
+ EXPECT_EQ(0u, images.size());
+ }
+ {
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(3500, 0, 1000, 1000));
+ EXPECT_EQ(0u, images.size());
+ }
+ {
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(0, 1100, 1000, 1000));
+ EXPECT_EQ(0u, images.size());
+ }
+ {
+ std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
+ image_map, gfx::Rect(3500, 1100, 1000, 1000));
+ EXPECT_EQ(0u, images.size());
+ }
+
+ // Image not present in the list.
+ {
+ sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(500, 500));
+ EXPECT_EQ(gfx::Rect(), image_map.GetRectForImage(image->uniqueID()));
+ }
+}
+
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectOnePixelQuery) {
+ gfx::Rect visible_rect(2048, 2048);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ // Discardable pixel refs are found in the following grids:
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ sk_sp<SkImage> discardable_image[4][4];
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ if ((x + y) & 1) {
+ discardable_image[y][x] = CreateDiscardableImage(gfx::Size(500, 500));
+ PaintFlags flags;
+ content_layer_client.add_draw_image(
+ discardable_image[y][x], gfx::Point(x * 512 + 6, y * 512 + 6),
+ flags);
+ }
+ }
+ }
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f);
+ }
+
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
+ image_map, gfx::Rect(x * 512 + 256, y * 512 + 256, 1, 1));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ if ((x + y) & 1) {
+ EXPECT_EQ(1u, images.size()) << x << " " << y;
+ EXPECT_TRUE(images[0].image == discardable_image[y][x])
+ << x << " " << y;
+ EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
+ inset_rects[0]);
+ EXPECT_EQ(images[0].image_rect,
+ image_map.GetRectForImage(images[0].image->uniqueID()));
+ } else {
+ EXPECT_EQ(0u, images.size()) << x << " " << y;
+ }
+ }
+ }
+}
+
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMassiveImage) {
+ gfx::Rect visible_rect(2048, 2048);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ sk_sp<SkImage> discardable_image =
+ CreateDiscardableImage(gfx::Size(1 << 25, 1 << 25));
+ PaintFlags flags;
+ content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0),
+ flags);
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f);
+ }
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ EXPECT_EQ(1u, images.size());
+ EXPECT_TRUE(images[0].image == discardable_image);
+ EXPECT_EQ(gfx::Rect(0, 0, 2048, 2048), inset_rects[0]);
+ EXPECT_EQ(images[0].image_rect,
+ image_map.GetRectForImage(images[0].image->uniqueID()));
+}
+
+TEST_F(DiscardableImageMapTest, PaintDestroyedWhileImageIsDrawn) {
+ gfx::Rect visible_rect(2048, 2048);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ sk_sp<SkImage> discardable_image = CreateDiscardableImage(gfx::Size(10, 10));
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ {
+ std::unique_ptr<SkPaint> paint(new SkPaint());
+ generator.canvas()->saveLayer(gfx::RectToSkRect(visible_rect),
+ paint.get());
+ }
+ generator.canvas()->drawImage(discardable_image, 0, 0, nullptr);
+ generator.canvas()->restore();
+ }
+
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
+ EXPECT_EQ(1u, images.size());
+ EXPECT_TRUE(images[0].image == discardable_image);
+}
+
+TEST_F(DiscardableImageMapTest, NullPaintOnSaveLayer) {
+ gfx::Rect visible_rect(2048, 2048);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ sk_sp<SkImage> discardable_image = CreateDiscardableImage(gfx::Size(10, 10));
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ SkPaint* null_paint = nullptr;
+ generator.canvas()->saveLayer(gfx::RectToSkRect(visible_rect), null_paint);
+ generator.canvas()->drawImage(discardable_image, 0, 0, nullptr);
+ generator.canvas()->restore();
+ }
+
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
+ EXPECT_EQ(1u, images.size());
+ EXPECT_TRUE(images[0].image == discardable_image);
+}
+
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImage) {
+ gfx::Rect visible_rect(2048, 2048);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ int dimension = std::numeric_limits<int>::max();
+ sk_sp<SkImage> discardable_image =
+ CreateDiscardableImage(gfx::Size(dimension, dimension));
+ PaintFlags flags;
+ content_layer_client.add_draw_image(discardable_image, gfx::Point(42, 42),
+ flags);
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f);
+ }
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(42, 42, 1, 1));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ EXPECT_EQ(1u, images.size());
+ EXPECT_TRUE(images[0].image == discardable_image);
+ EXPECT_EQ(gfx::Rect(42, 42, 2006, 2006), inset_rects[0]);
+ EXPECT_EQ(images[0].image_rect,
+ image_map.GetRectForImage(images[0].image->uniqueID()));
+}
+
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesInRectMaxImageMaxLayer) {
+ // At large values of integer x, x != static_cast<int>(static_cast<float>(x)).
+ // So, make sure the dimension can be converted back and forth for the
+ // purposes of the unittest. Also, at near max int values, Skia seems to skip
+ // some draw calls, so we subtract 64 since we only care about "really large"
+ // values, not necessarily max int values.
+ int dimension = static_cast<int>(
+ static_cast<float>(std::numeric_limits<int>::max() - 64));
+ gfx::Rect visible_rect(dimension, dimension);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ sk_sp<SkImage> discardable_image =
+ CreateDiscardableImage(gfx::Size(dimension, dimension));
+ PaintFlags flags;
+ content_layer_client.add_draw_image(discardable_image, gfx::Point(0, 0),
+ flags);
+ content_layer_client.add_draw_image(discardable_image, gfx::Point(10000, 0),
+ flags);
+ content_layer_client.add_draw_image(discardable_image,
+ gfx::Point(-10000, 500), flags);
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f);
+ }
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ EXPECT_EQ(1u, images.size());
+ EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), inset_rects[0]);
+
+ images = GetDiscardableImagesInRect(image_map, gfx::Rect(10000, 0, 1, 1));
+ inset_rects = InsetImageRects(images);
+ EXPECT_EQ(2u, images.size());
+ EXPECT_EQ(gfx::Rect(10000, 0, dimension - 10000, dimension), inset_rects[1]);
+ EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), inset_rects[0]);
+
+ // Since we adjust negative offsets before using ToEnclosingRect, the expected
+ // width will be converted to float, which means that we lose some precision.
+ // The expected value is whatever the value is converted to float and then
+ // back to int.
+ int expected10k = static_cast<int>(static_cast<float>(dimension - 10000));
+ images = GetDiscardableImagesInRect(image_map, gfx::Rect(0, 500, 1, 1));
+ inset_rects = InsetImageRects(images);
+ EXPECT_EQ(2u, images.size());
+ EXPECT_EQ(gfx::Rect(0, 500, expected10k, dimension - 500), inset_rects[1]);
+ EXPECT_EQ(gfx::Rect(0, 0, dimension, dimension), inset_rects[0]);
+
+ EXPECT_EQ(images[0].image_rect,
+ image_map.GetRectForImage(discardable_image->uniqueID()));
+}
+
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesRectInBounds) {
+ gfx::Rect visible_rect(1000, 1000);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ sk_sp<SkImage> discardable_image =
+ CreateDiscardableImage(gfx::Size(100, 100));
+ sk_sp<SkImage> long_discardable_image =
+ CreateDiscardableImage(gfx::Size(10000, 100));
+
+ PaintFlags flags;
+ content_layer_client.add_draw_image(discardable_image, gfx::Point(-10, -11),
+ flags);
+ content_layer_client.add_draw_image(discardable_image, gfx::Point(950, 951),
+ flags);
+ content_layer_client.add_draw_image(long_discardable_image,
+ gfx::Point(-100, 500), flags);
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ display_list->Raster(generator.canvas(), nullptr, visible_rect, 1.f);
+ }
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(0, 0, 1, 1));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ EXPECT_EQ(1u, images.size());
+ EXPECT_EQ(gfx::Rect(0, 0, 90, 89), inset_rects[0]);
+
+ images = GetDiscardableImagesInRect(image_map, gfx::Rect(999, 999, 1, 1));
+ inset_rects = InsetImageRects(images);
+ EXPECT_EQ(1u, images.size());
+ EXPECT_EQ(gfx::Rect(950, 951, 50, 49), inset_rects[0]);
+
+ images = GetDiscardableImagesInRect(image_map, gfx::Rect(0, 500, 1, 1));
+ inset_rects = InsetImageRects(images);
+ EXPECT_EQ(1u, images.size());
+ EXPECT_EQ(gfx::Rect(0, 500, 1000, 100), inset_rects[0]);
+
+ gfx::Rect discardable_image_rect;
+ discardable_image_rect.Union(gfx::Rect(0, 0, 90, 89));
+ discardable_image_rect.Union(gfx::Rect(950, 951, 50, 49));
+ discardable_image_rect.Inset(-1, -1, -1, -1);
+ EXPECT_EQ(discardable_image_rect,
+ image_map.GetRectForImage(discardable_image->uniqueID()));
+
+ EXPECT_EQ(gfx::Rect(-1, 499, 1002, 102),
+ image_map.GetRectForImage(long_discardable_image->uniqueID()));
+}
+
+TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) {
+ gfx::Rect visible_rect(2048, 2048);
+ FakeContentLayerClient content_layer_client;
+ content_layer_client.set_bounds(visible_rect.size());
+
+ // Discardable pixel refs are found in the following grids:
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ // | | x | | x |
+ // |---|---|---|---|
+ // | x | | x | |
+ // |---|---|---|---|
+ sk_sp<SkImage> discardable_image[4][4];
+
+ // Skia doesn't allow shader instantiation with non-invertible local
+ // transforms, so we can't let the scale drop all the way to 0.
+ static constexpr float kMinScale = 0.1f;
+
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ if ((x + y) & 1) {
+ discardable_image[y][x] = CreateDiscardableImage(gfx::Size(500, 500));
+ SkMatrix scale = SkMatrix::MakeScale(std::max(x * 0.5f, kMinScale),
+ std::max(y * 0.5f, kMinScale));
+ PaintFlags flags;
+ flags.setShader(discardable_image[y][x]->makeShader(
+ SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &scale));
+ content_layer_client.add_draw_rect(
+ gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500), flags);
+ }
+ }
+ }
+
+ scoped_refptr<DisplayItemList> display_list =
+ content_layer_client.PaintContentsToDisplayList(
+ ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);
+
+ DiscardableImageMap image_map;
+ {
+ DiscardableImageMap::ScopedMetadataGenerator generator(&image_map,
+ visible_rect.size());
+ display_list->Raster(generator.canvas(), nullptr, gfx::Rect(), 1.f);
+ }
+
+ for (int y = 0; y < 4; ++y) {
+ for (int x = 0; x < 4; ++x) {
+ std::vector<PositionScaleDrawImage> images = GetDiscardableImagesInRect(
+ image_map, gfx::Rect(x * 512, y * 512, 500, 500));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ if ((x + y) & 1) {
+ EXPECT_EQ(1u, images.size()) << x << " " << y;
+ EXPECT_TRUE(images[0].image == discardable_image[y][x])
+ << x << " " << y;
+ EXPECT_EQ(gfx::Rect(x * 512 + 6, y * 512 + 6, 500, 500),
+ inset_rects[0]);
+ EXPECT_EQ(std::max(x * 0.5f, kMinScale), images[0].scale.fWidth);
+ EXPECT_EQ(std::max(y * 0.5f, kMinScale), images[0].scale.fHeight);
+ } else {
+ EXPECT_EQ(0u, images.size()) << x << " " << y;
+ }
+ }
+ }
+
+ // Capture 4 pixel refs.
+ std::vector<PositionScaleDrawImage> images =
+ GetDiscardableImagesInRect(image_map, gfx::Rect(512, 512, 2048, 2048));
+ std::vector<gfx::Rect> inset_rects = InsetImageRects(images);
+ EXPECT_EQ(4u, images.size());
+ EXPECT_TRUE(images[0].image == discardable_image[1][2]);
+ EXPECT_EQ(gfx::Rect(2 * 512 + 6, 512 + 6, 500, 500), inset_rects[0]);
+ EXPECT_TRUE(images[1].image == discardable_image[2][1]);
+ EXPECT_EQ(gfx::Rect(512 + 6, 2 * 512 + 6, 500, 500), inset_rects[1]);
+ EXPECT_TRUE(images[2].image == discardable_image[2][3]);
+ EXPECT_EQ(gfx::Rect(3 * 512 + 6, 2 * 512 + 6, 500, 500), inset_rects[2]);
+ EXPECT_TRUE(images[3].image == discardable_image[3][2]);
+ EXPECT_EQ(gfx::Rect(2 * 512 + 6, 3 * 512 + 6, 500, 500), inset_rects[3]);
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/display_item.h b/chromium/cc/paint/display_item.h
new file mode 100644
index 00000000000..74a3c5965de
--- /dev/null
+++ b/chromium/cc/paint/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_PAINT_DISPLAY_ITEM_H_
+#define CC_PAINT_DISPLAY_ITEM_H_
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "cc/cc_export.h"
+#include "cc/debug/traced_value.h"
+#include "cc/paint/paint_export.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT DisplayItem {
+ public:
+ virtual ~DisplayItem() = default;
+
+ enum Type {
+ CLIP,
+ END_CLIP,
+ CLIP_PATH,
+ END_CLIP_PATH,
+ COMPOSITING,
+ END_COMPOSITING,
+ DRAWING,
+ FILTER,
+ END_FILTER,
+ FLOAT_CLIP,
+ END_FLOAT_CLIP,
+ TRANSFORM,
+ END_TRANSFORM,
+ };
+ const Type type;
+
+ protected:
+ explicit DisplayItem(Type type) : type(type) {}
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_DISPLAY_ITEM_H_
diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc
new file mode 100644
index 00000000000..83599482f53
--- /dev/null
+++ b/chromium/cc/paint/display_item_list.cc
@@ -0,0 +1,501 @@
+// 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/paint/display_item_list.h"
+
+#include <stddef.h>
+
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "cc/base/math_util.h"
+#include "cc/base/render_surface_filters.h"
+#include "cc/debug/picture_debug_util.h"
+#include "cc/paint/clip_display_item.h"
+#include "cc/paint/clip_path_display_item.h"
+#include "cc/paint/compositing_display_item.h"
+#include "cc/paint/drawing_display_item.h"
+#include "cc/paint/filter_display_item.h"
+#include "cc/paint/float_clip_display_item.h"
+#include "cc/paint/largest_display_item.h"
+#include "cc/paint/transform_display_item.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/SkPictureRecorder.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+namespace {
+
+// We don't perform per-layer solid color analysis when there are too many skia
+// operations.
+const int kOpCountThatIsOkToAnalyze = 10;
+
+bool GetCanvasClipBounds(SkCanvas* canvas, gfx::Rect* clip_bounds) {
+ SkRect canvas_clip_bounds;
+ if (!canvas->getLocalClipBounds(&canvas_clip_bounds))
+ return false;
+ *clip_bounds = ToEnclosingRect(gfx::SkRectToRectF(canvas_clip_bounds));
+ return true;
+}
+
+const int kDefaultNumDisplayItemsToReserve = 100;
+
+NOINLINE DISABLE_CFI_PERF void RasterItem(const DisplayItem& base_item,
+ SkCanvas* canvas,
+ SkPicture::AbortCallback* callback) {
+ switch (base_item.type) {
+ case DisplayItem::CLIP: {
+ const auto& item = static_cast<const ClipDisplayItem&>(base_item);
+ canvas->save();
+ canvas->clipRect(gfx::RectToSkRect(item.clip_rect), item.antialias);
+ for (const auto& rrect : item.rounded_clip_rects) {
+ if (rrect.isRect()) {
+ canvas->clipRect(rrect.rect(), item.antialias);
+ } else {
+ canvas->clipRRect(rrect, item.antialias);
+ }
+ }
+ break;
+ }
+ case DisplayItem::END_CLIP:
+ canvas->restore();
+ break;
+ case DisplayItem::CLIP_PATH: {
+ const auto& item = static_cast<const ClipPathDisplayItem&>(base_item);
+ canvas->save();
+ canvas->clipPath(item.clip_path, item.antialias);
+ break;
+ }
+ case DisplayItem::END_CLIP_PATH:
+ canvas->restore();
+ break;
+ case DisplayItem::COMPOSITING: {
+ const auto& item = static_cast<const CompositingDisplayItem&>(base_item);
+ SkPaint paint;
+ paint.setBlendMode(item.xfermode);
+ paint.setAlpha(item.alpha);
+ paint.setColorFilter(item.color_filter);
+ const SkRect* bounds = item.has_bounds ? &item.bounds : nullptr;
+ if (item.lcd_text_requires_opaque_layer)
+ canvas->saveLayer(bounds, &paint);
+ else
+ canvas->saveLayerPreserveLCDTextRequests(bounds, &paint);
+ break;
+ }
+ case DisplayItem::END_COMPOSITING:
+ canvas->restore();
+ break;
+ case DisplayItem::DRAWING: {
+ const auto& item = static_cast<const DrawingDisplayItem&>(base_item);
+ if (canvas->quickReject(item.picture->cullRect()))
+ break;
+
+ // SkPicture always does a wrapping save/restore on the canvas, so it is
+ // not necessary here.
+ item.picture->playback(canvas, callback);
+ break;
+ }
+ case DisplayItem::FLOAT_CLIP: {
+ const auto& item = static_cast<const FloatClipDisplayItem&>(base_item);
+ canvas->save();
+ canvas->clipRect(gfx::RectFToSkRect(item.clip_rect));
+ break;
+ }
+ case DisplayItem::END_FLOAT_CLIP:
+ canvas->restore();
+ break;
+ case DisplayItem::FILTER: {
+ const auto& item = static_cast<const FilterDisplayItem&>(base_item);
+ canvas->save();
+ canvas->translate(item.origin.x(), item.origin.y());
+
+ sk_sp<SkImageFilter> image_filter =
+ RenderSurfaceFilters::BuildImageFilter(item.filters,
+ item.bounds.size());
+ SkRect boundaries = RectFToSkRect(item.bounds);
+ boundaries.offset(-item.origin.x(), -item.origin.y());
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrcOver);
+ paint.setImageFilter(std::move(image_filter));
+ canvas->saveLayer(&boundaries, &paint);
+
+ canvas->translate(-item.origin.x(), -item.origin.y());
+ break;
+ }
+ case DisplayItem::END_FILTER:
+ canvas->restore();
+ canvas->restore();
+ break;
+ case DisplayItem::TRANSFORM: {
+ const auto& item = static_cast<const TransformDisplayItem&>(base_item);
+ canvas->save();
+ if (!item.transform.IsIdentity())
+ canvas->concat(item.transform.matrix());
+ break;
+ }
+ case DisplayItem::END_TRANSFORM:
+ canvas->restore();
+ break;
+ }
+}
+
+} // namespace
+
+DisplayItemList::DisplayItemList()
+ : items_(LargestDisplayItemSize(),
+ LargestDisplayItemSize() * kDefaultNumDisplayItemsToReserve) {}
+
+DisplayItemList::~DisplayItemList() = default;
+
+void DisplayItemList::Raster(SkCanvas* canvas,
+ SkPicture::AbortCallback* callback,
+ const gfx::Rect& canvas_target_playback_rect,
+ float contents_scale) const {
+ canvas->save();
+ if (!canvas_target_playback_rect.IsEmpty()) {
+ // canvas_target_playback_rect is specified in device space. We can't
+ // use clipRect because canvas CTM will be applied on it. Use clipRegion
+ // instead because it ignores canvas CTM.
+ SkRegion device_clip;
+ device_clip.setRect(gfx::RectToSkIRect(canvas_target_playback_rect));
+ canvas->clipRegion(device_clip);
+ }
+ canvas->scale(contents_scale, contents_scale);
+ Raster(canvas, callback);
+ canvas->restore();
+}
+
+void DisplayItemList::Raster(SkCanvas* canvas,
+ SkPicture::AbortCallback* callback) const {
+ gfx::Rect canvas_playback_rect;
+ if (!GetCanvasClipBounds(canvas, &canvas_playback_rect))
+ return;
+
+ std::vector<size_t> indices;
+ rtree_.Search(canvas_playback_rect, &indices);
+ for (size_t index : indices) {
+ RasterItem(items_[index], canvas, callback);
+
+ // We use a callback during solid color analysis on the compositor thread to
+ // break out early. Since we're handling a sequence of pictures via rtree
+ // query results ourselves, we have to respect the callback and early out.
+ if (callback && callback->abort())
+ break;
+ }
+}
+
+void DisplayItemList::GrowCurrentBeginItemVisualRect(
+ const gfx::Rect& visual_rect) {
+ if (!begin_item_indices_.empty())
+ visual_rects_[begin_item_indices_.back()].Union(visual_rect);
+}
+
+void DisplayItemList::Finalize() {
+ TRACE_EVENT0("cc", "DisplayItemList::Finalize");
+ DCHECK(items_.size() == visual_rects_.size())
+ << "items.size() " << items_.size() << " visual_rects.size() "
+ << visual_rects_.size();
+ rtree_.Build(visual_rects_);
+
+ if (!retain_visual_rects_)
+ // This clears both the vector and the vector's capacity, since
+ // visual_rects won't be used anymore.
+ std::vector<gfx::Rect>().swap(visual_rects_);
+}
+
+bool DisplayItemList::IsSuitableForGpuRasterization() const {
+ // TODO(wkorman): 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. See crbug.com/513016.
+ return all_items_are_suitable_for_gpu_rasterization_;
+}
+
+int DisplayItemList::ApproximateOpCount() const {
+ return approximate_op_count_;
+}
+
+size_t DisplayItemList::ApproximateMemoryUsage() const {
+ size_t memory_usage = sizeof(*this);
+
+ size_t external_memory_usage = 0;
+ for (const auto& item : items_) {
+ size_t bytes = 0;
+ switch (item.type) {
+ case DisplayItem::CLIP:
+ bytes = static_cast<const ClipDisplayItem&>(item).ExternalMemoryUsage();
+ break;
+ case DisplayItem::CLIP_PATH:
+ bytes =
+ static_cast<const ClipPathDisplayItem&>(item).ExternalMemoryUsage();
+ break;
+ case DisplayItem::COMPOSITING:
+ bytes = static_cast<const CompositingDisplayItem&>(item)
+ .ExternalMemoryUsage();
+ break;
+ case DisplayItem::DRAWING:
+ bytes =
+ static_cast<const DrawingDisplayItem&>(item).ExternalMemoryUsage();
+ break;
+ case DisplayItem::FLOAT_CLIP:
+ bytes = static_cast<const FloatClipDisplayItem&>(item)
+ .ExternalMemoryUsage();
+ break;
+ case DisplayItem::FILTER:
+ bytes =
+ static_cast<const FilterDisplayItem&>(item).ExternalMemoryUsage();
+ break;
+ case DisplayItem::TRANSFORM:
+ bytes = static_cast<const TransformDisplayItem&>(item)
+ .ExternalMemoryUsage();
+ break;
+ case DisplayItem::END_CLIP:
+ case DisplayItem::END_CLIP_PATH:
+ case DisplayItem::END_COMPOSITING:
+ case DisplayItem::END_FLOAT_CLIP:
+ case DisplayItem::END_FILTER:
+ case DisplayItem::END_TRANSFORM:
+ break;
+ }
+ external_memory_usage += bytes;
+ }
+
+ // Memory outside this class due to |items_|.
+ memory_usage += items_.GetCapacityInBytes() + external_memory_usage;
+
+ // TODO(jbroman): Does anything else owned by this class substantially
+ // contribute to memory usage?
+ // TODO(vmpstr): Probably DiscardableImageMap is worth counting here.
+
+ return memory_usage;
+}
+
+bool DisplayItemList::ShouldBeAnalyzedForSolidColor() const {
+ return ApproximateOpCount() <= kOpCountThatIsOkToAnalyze;
+}
+
+void DisplayItemList::EmitTraceSnapshot() const {
+ bool include_items;
+ TRACE_EVENT_CATEGORY_GROUP_ENABLED(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items"), &include_items);
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.display_items") ","
+ TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
+ TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
+ "cc::DisplayItemList", this, CreateTracedValue(include_items));
+}
+
+std::unique_ptr<base::trace_event::TracedValue>
+DisplayItemList::CreateTracedValue(bool include_items) const {
+ auto state = base::MakeUnique<base::trace_event::TracedValue>();
+ state->BeginDictionary("params");
+
+ if (include_items) {
+ state->BeginArray("items");
+
+ auto visual_rects_it = visual_rects_.begin();
+ for (const DisplayItem& base_item : items_) {
+ gfx::Rect visual_rect;
+ if (visual_rects_it != visual_rects_.end()) {
+ visual_rect = *visual_rects_it;
+ ++visual_rects_it;
+ }
+
+ switch (base_item.type) {
+ case DisplayItem::CLIP: {
+ const auto& item = static_cast<const ClipDisplayItem&>(base_item);
+ std::string output =
+ base::StringPrintf("ClipDisplayItem rect: [%s] visualRect: [%s]",
+ item.clip_rect.ToString().c_str(),
+ visual_rect.ToString().c_str());
+ for (const SkRRect& rounded_rect : item.rounded_clip_rects) {
+ base::StringAppendF(
+ &output, " rounded_rect: [rect: [%s]",
+ gfx::SkRectToRectF(rounded_rect.rect()).ToString().c_str());
+ base::StringAppendF(&output, " radii: [");
+ SkVector upper_left_radius =
+ rounded_rect.radii(SkRRect::kUpperLeft_Corner);
+ base::StringAppendF(&output, "[%f,%f],", upper_left_radius.x(),
+ upper_left_radius.y());
+ SkVector upper_right_radius =
+ rounded_rect.radii(SkRRect::kUpperRight_Corner);
+ base::StringAppendF(&output, " [%f,%f],", upper_right_radius.x(),
+ upper_right_radius.y());
+ SkVector lower_right_radius =
+ rounded_rect.radii(SkRRect::kLowerRight_Corner);
+ base::StringAppendF(&output, " [%f,%f],", lower_right_radius.x(),
+ lower_right_radius.y());
+ SkVector lower_left_radius =
+ rounded_rect.radii(SkRRect::kLowerLeft_Corner);
+ base::StringAppendF(&output, " [%f,%f]]", lower_left_radius.x(),
+ lower_left_radius.y());
+ }
+ state->AppendString(output);
+ break;
+ }
+ case DisplayItem::END_CLIP:
+ state->AppendString(
+ base::StringPrintf("EndClipDisplayItem visualRect: [%s]",
+ visual_rect.ToString().c_str()));
+ break;
+ case DisplayItem::CLIP_PATH: {
+ const auto& item = static_cast<const ClipPathDisplayItem&>(base_item);
+ state->AppendString(base::StringPrintf(
+ "ClipPathDisplayItem length: %d visualRect: [%s]",
+ item.clip_path.countPoints(), visual_rect.ToString().c_str()));
+ break;
+ }
+ case DisplayItem::END_CLIP_PATH:
+ state->AppendString(
+ base::StringPrintf("EndClipPathDisplayItem visualRect: [%s]",
+ visual_rect.ToString().c_str()));
+ break;
+ case DisplayItem::COMPOSITING: {
+ const auto& item =
+ static_cast<const CompositingDisplayItem&>(base_item);
+ std::string output = base::StringPrintf(
+ "CompositingDisplayItem alpha: %d, xfermode: %d, visualRect: "
+ "[%s]",
+ item.alpha, static_cast<int>(item.xfermode),
+ visual_rect.ToString().c_str());
+ if (item.has_bounds) {
+ base::StringAppendF(
+ &output, ", bounds: [%s]",
+ gfx::SkRectToRectF(item.bounds).ToString().c_str());
+ }
+ state->AppendString(output);
+ break;
+ }
+ case DisplayItem::END_COMPOSITING:
+ state->AppendString(
+ base::StringPrintf("EndCompositingDisplayItem visualRect: [%s]",
+ visual_rect.ToString().c_str()));
+ break;
+ case DisplayItem::DRAWING: {
+ const auto& item = static_cast<const DrawingDisplayItem&>(base_item);
+ state->BeginDictionary();
+ state->SetString("name", "DrawingDisplayItem");
+
+ state->BeginArray("visualRect");
+ state->AppendInteger(visual_rect.x());
+ state->AppendInteger(visual_rect.y());
+ state->AppendInteger(visual_rect.width());
+ state->AppendInteger(visual_rect.height());
+ state->EndArray();
+
+ state->BeginArray("cullRect");
+ state->AppendInteger(item.picture->cullRect().x());
+ state->AppendInteger(item.picture->cullRect().y());
+ state->AppendInteger(item.picture->cullRect().width());
+ state->AppendInteger(item.picture->cullRect().height());
+ state->EndArray();
+
+ std::string b64_picture;
+ PictureDebugUtil::SerializeAsBase64(ToSkPicture(item.picture).get(),
+ &b64_picture);
+ state->SetString("skp64", b64_picture);
+ state->EndDictionary();
+ break;
+ }
+ case DisplayItem::FILTER: {
+ const auto& item = static_cast<const FilterDisplayItem&>(base_item);
+ state->AppendString(base::StringPrintf(
+ "FilterDisplayItem bounds: [%s] visualRect: [%s]",
+ item.bounds.ToString().c_str(), visual_rect.ToString().c_str()));
+ break;
+ }
+ case DisplayItem::END_FILTER:
+ state->AppendString(
+ base::StringPrintf("EndFilterDisplayItem visualRect: [%s]",
+ visual_rect.ToString().c_str()));
+ break;
+ case DisplayItem::FLOAT_CLIP: {
+ const auto& item =
+ static_cast<const FloatClipDisplayItem&>(base_item);
+ state->AppendString(base::StringPrintf(
+ "FloatClipDisplayItem rect: [%s] visualRect: [%s]",
+ item.clip_rect.ToString().c_str(),
+ visual_rect.ToString().c_str()));
+ break;
+ }
+ case DisplayItem::END_FLOAT_CLIP:
+ state->AppendString(
+ base::StringPrintf("EndFloatClipDisplayItem visualRect: [%s]",
+ visual_rect.ToString().c_str()));
+ break;
+ case DisplayItem::TRANSFORM: {
+ const auto& item =
+ static_cast<const TransformDisplayItem&>(base_item);
+ state->AppendString(base::StringPrintf(
+ "TransformDisplayItem transform: [%s] visualRect: [%s]",
+ item.transform.ToString().c_str(),
+ visual_rect.ToString().c_str()));
+ break;
+ }
+ case DisplayItem::END_TRANSFORM:
+ state->AppendString(
+ base::StringPrintf("EndTransformDisplayItem visualRect: [%s]",
+ visual_rect.ToString().c_str()));
+ break;
+ }
+ }
+ state->EndArray(); // "items".
+ }
+
+ MathUtil::AddToTracedValue("layer_rect", rtree_.GetBounds(), state.get());
+ state->EndDictionary(); // "params".
+
+ {
+ SkPictureRecorder recorder;
+ gfx::Rect bounds = rtree_.GetBounds();
+ SkCanvas* canvas = recorder.beginRecording(bounds.width(), bounds.height());
+ canvas->translate(-bounds.x(), -bounds.y());
+ canvas->clipRect(gfx::RectToSkRect(bounds));
+ Raster(canvas, nullptr, gfx::Rect(), 1.f);
+ sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
+
+ std::string b64_picture;
+ PictureDebugUtil::SerializeAsBase64(picture.get(), &b64_picture);
+ state->SetString("skp64", b64_picture);
+ }
+
+ return state;
+}
+
+void DisplayItemList::GenerateDiscardableImagesMetadata() {
+ // This should be only called once.
+ DCHECK(image_map_.empty());
+
+ gfx::Rect bounds = rtree_.GetBounds();
+ DiscardableImageMap::ScopedMetadataGenerator generator(
+ &image_map_, gfx::Size(bounds.right(), bounds.bottom()));
+ auto* canvas = generator.canvas();
+ for (const auto& item : items_)
+ RasterItem(item, canvas, nullptr);
+}
+
+void DisplayItemList::GetDiscardableImagesInRect(
+ const gfx::Rect& rect,
+ float contents_scale,
+ const gfx::ColorSpace& target_color_space,
+ std::vector<DrawImage>* images) {
+ image_map_.GetDiscardableImagesInRect(rect, contents_scale,
+ target_color_space, images);
+}
+
+gfx::Rect DisplayItemList::GetRectForImage(ImageId image_id) const {
+ return image_map_.GetRectForImage(image_id);
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h
new file mode 100644
index 00000000000..b65707dc6e0
--- /dev/null
+++ b/chromium/cc/paint/display_item_list.h
@@ -0,0 +1,209 @@
+// 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_PAINT_DISPLAY_ITEM_LIST_H_
+#define CC_PAINT_DISPLAY_ITEM_LIST_H_
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/trace_event.h"
+#include "cc/base/contiguous_container.h"
+#include "cc/base/rtree.h"
+#include "cc/paint/discardable_image_map.h"
+#include "cc/paint/display_item.h"
+#include "cc/paint/image_id.h"
+#include "cc/paint/paint_export.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+
+class SkCanvas;
+
+namespace base {
+namespace trace_event {
+class TracedValue;
+}
+}
+
+namespace cc {
+class DisplayItem;
+
+class CC_PAINT_EXPORT DisplayItemList
+ : public base::RefCountedThreadSafe<DisplayItemList> {
+ public:
+ DisplayItemList();
+
+ // TODO(trchen): Deprecated. Apply clip and scale on the canvas instead.
+ void Raster(SkCanvas* canvas,
+ SkPicture::AbortCallback* callback,
+ const gfx::Rect& canvas_target_playback_rect,
+ float contents_scale) const;
+
+ void Raster(SkCanvas* canvas, SkPicture::AbortCallback* callback) const;
+
+ // Because processing happens in these CreateAndAppend functions, all the set
+ // up for the item should be done via the args, which is why the return type
+ // needs to be const, to prevent set-after-processing mistakes.
+
+ // Most paired begin item types default to an empty visual rect, which will
+ // subsequently be grown as needed to encompass any contained items that draw
+ // content, such as drawing or filter items.
+ template <typename DisplayItemType, typename... Args>
+ const DisplayItemType& CreateAndAppendPairedBeginItem(Args&&... args) {
+ return CreateAndAppendPairedBeginItemWithVisualRect<DisplayItemType>(
+ gfx::Rect(), std::forward<Args>(args)...);
+ }
+
+ // This method variant is exposed to allow filters to specify their visual
+ // rect since they may draw content despite containing no drawing items.
+ template <typename DisplayItemType, typename... Args>
+ const DisplayItemType& CreateAndAppendPairedBeginItemWithVisualRect(
+ const gfx::Rect& visual_rect,
+ Args&&... args) {
+ size_t item_index = visual_rects_.size();
+ visual_rects_.push_back(visual_rect);
+ begin_item_indices_.push_back(item_index);
+
+ return AllocateAndConstruct<DisplayItemType>(std::forward<Args>(args)...);
+ }
+
+ template <typename DisplayItemType, typename... Args>
+ const DisplayItemType& CreateAndAppendPairedEndItem(Args&&... args) {
+ DCHECK(!begin_item_indices_.empty());
+ size_t last_begin_index = begin_item_indices_.back();
+ begin_item_indices_.pop_back();
+
+ // Note that we are doing two separate things below:
+ //
+ // 1. Appending a new rect to the |visual_rects| list associated with
+ // the newly-being-added paired end item, with that visual rect
+ // having same bounds as its paired begin item, referenced via
+ // |last_begin_index|. The paired begin item may or may not be the
+ // current last visual rect in |visual_rects|, and its bounds has
+ // potentially been grown via calls to CreateAndAppendDrawingItem().
+ //
+ // 2. If there is still a containing paired begin item after closing the
+ // pair ended in this method call, growing that item's visual rect to
+ // incorporate the bounds of the now-finished pair.
+ //
+ // Thus we're carefully pushing and growing by the visual rect of the
+ // paired begin item we're closing in this method call, which is not
+ // necessarily the same as |visual_rects.back()|, and given that the
+ // |visual_rects| list is mutated in step 1 before step 2, we also can't
+ // shorten the reference via a |const auto| reference. We could make a
+ // copy of the rect before list mutation, but that would incur copy
+ // overhead.
+
+ // Ending bounds match the starting bounds.
+ visual_rects_.push_back(visual_rects_[last_begin_index]);
+
+ // The block that ended needs to be included in the bounds of the enclosing
+ // block.
+ GrowCurrentBeginItemVisualRect(visual_rects_[last_begin_index]);
+
+ return AllocateAndConstruct<DisplayItemType>(std::forward<Args>(args)...);
+ }
+
+ template <typename DisplayItemType, typename... Args>
+ const DisplayItemType& CreateAndAppendDrawingItem(
+ const gfx::Rect& visual_rect,
+ Args&&... args) {
+ visual_rects_.push_back(visual_rect);
+ GrowCurrentBeginItemVisualRect(visual_rect);
+
+ return AllocateAndConstruct<DisplayItemType>(std::forward<Args>(args)...);
+ }
+
+ // Called after all items are appended, to process the items and, if
+ // applicable, create an internally cached SkPicture.
+ void Finalize();
+
+ void SetIsSuitableForGpuRasterization(bool is_suitable) {
+ all_items_are_suitable_for_gpu_rasterization_ = is_suitable;
+ }
+ bool IsSuitableForGpuRasterization() const;
+
+ int ApproximateOpCount() const;
+ size_t ApproximateMemoryUsage() const;
+ bool ShouldBeAnalyzedForSolidColor() const;
+
+ void EmitTraceSnapshot() const;
+
+ void GenerateDiscardableImagesMetadata();
+ void GetDiscardableImagesInRect(const gfx::Rect& rect,
+ float contents_scale,
+ const gfx::ColorSpace& target_color_space,
+ std::vector<DrawImage>* images);
+ gfx::Rect GetRectForImage(ImageId image_id) const;
+
+ void SetRetainVisualRectsForTesting(bool retain) {
+ retain_visual_rects_ = retain;
+ }
+
+ size_t size() const { return items_.size(); }
+
+ gfx::Rect VisualRectForTesting(int index) { return visual_rects_[index]; }
+
+ ContiguousContainer<DisplayItem>::const_iterator begin() const {
+ return items_.begin();
+ }
+
+ ContiguousContainer<DisplayItem>::const_iterator end() const {
+ return items_.end();
+ }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, AsValueWithNoItems);
+ FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, AsValueWithItems);
+
+ ~DisplayItemList();
+
+ std::unique_ptr<base::trace_event::TracedValue> CreateTracedValue(
+ bool include_items) const;
+
+ // If we're currently within a paired display item block, unions the
+ // given visual rect with the begin display item's visual rect.
+ void GrowCurrentBeginItemVisualRect(const gfx::Rect& visual_rect);
+
+ template <typename DisplayItemType, typename... Args>
+ const DisplayItemType& AllocateAndConstruct(Args&&... args) {
+ auto* item = &items_.AllocateAndConstruct<DisplayItemType>(
+ std::forward<Args>(args)...);
+ approximate_op_count_ += item->ApproximateOpCount();
+ return *item;
+ }
+
+ RTree rtree_;
+ DiscardableImageMap image_map_;
+ ContiguousContainer<DisplayItem> items_;
+
+ // The visual rects associated with each of the display items in the
+ // display item list. There is one rect per display item, and the
+ // position in |visual_rects| matches the position of the item in
+ // |items| . These rects are intentionally kept separate
+ // because they are not needed while walking the |items| for raster.
+ std::vector<gfx::Rect> visual_rects_;
+ std::vector<size_t> begin_item_indices_;
+
+ int approximate_op_count_ = 0;
+ bool all_items_are_suitable_for_gpu_rasterization_ = true;
+ // For testing purposes only. Whether to keep visual rects across calls to
+ // Finalize().
+ bool retain_visual_rects_ = false;
+
+ friend class base::RefCountedThreadSafe<DisplayItemList>;
+ FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, ApproximateMemoryUsage);
+ DISALLOW_COPY_AND_ASSIGN(DisplayItemList);
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_DISPLAY_ITEM_LIST_H_
diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc
new file mode 100644
index 00000000000..f1b9e759f9b
--- /dev/null
+++ b/chromium/cc/paint/display_item_list_unittest.cc
@@ -0,0 +1,707 @@
+// 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/paint/display_item_list.h"
+
+#include <stddef.h>
+
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "cc/base/filter_operation.h"
+#include "cc/base/filter_operations.h"
+#include "cc/paint/clip_display_item.h"
+#include "cc/paint/clip_path_display_item.h"
+#include "cc/paint/compositing_display_item.h"
+#include "cc/paint/drawing_display_item.h"
+#include "cc/paint/filter_display_item.h"
+
+#include "cc/paint/float_clip_display_item.h"
+#include "cc/paint/paint_canvas.h"
+#include "cc/paint/paint_flags.h"
+#include "cc/paint/paint_record.h"
+#include "cc/paint/paint_recorder.h"
+#include "cc/paint/transform_display_item.h"
+#include "cc/test/geometry_test_utils.h"
+#include "cc/test/pixel_test_utils.h"
+#include "cc/test/skia_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
+#include "third_party/skia/include/effects/SkImageSource.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/skia_util.h"
+
+namespace cc {
+
+namespace {
+
+bool CompareN32Pixels(void* actual_pixels,
+ void* expected_pixels,
+ int width,
+ int height) {
+ if (memcmp(actual_pixels, expected_pixels, 4 * width * height) == 0)
+ return true;
+
+ SkImageInfo actual_info = SkImageInfo::MakeN32Premul(width, height);
+ SkBitmap actual_bitmap;
+ actual_bitmap.installPixels(actual_info, actual_pixels,
+ actual_info.minRowBytes());
+
+ SkImageInfo expected_info = SkImageInfo::MakeN32Premul(width, height);
+ SkBitmap expected_bitmap;
+ expected_bitmap.installPixels(expected_info, expected_pixels,
+ expected_info.minRowBytes());
+
+ std::string gen_bmp_data_url = GetPNGDataUrl(actual_bitmap);
+ std::string ref_bmp_data_url = GetPNGDataUrl(expected_bitmap);
+
+ LOG(ERROR) << "Pixels do not match!";
+ LOG(ERROR) << "Actual: " << gen_bmp_data_url;
+ LOG(ERROR) << "Expected: " << ref_bmp_data_url;
+ return false;
+}
+
+const gfx::Rect kVisualRect(0, 0, 42, 42);
+
+sk_sp<const PaintRecord> CreateRectPicture(const gfx::Rect& bounds) {
+ PaintRecorder recorder;
+ PaintCanvas* canvas =
+ recorder.beginRecording(bounds.width(), bounds.height());
+ canvas->drawRect(
+ SkRect::MakeXYWH(bounds.x(), bounds.y(), bounds.width(), bounds.height()),
+ PaintFlags());
+ return recorder.finishRecordingAsPicture();
+}
+
+void AppendFirstSerializationTestPicture(scoped_refptr<DisplayItemList> list,
+ const gfx::Size& layer_size) {
+ gfx::PointF offset(2.f, 3.f);
+ PaintRecorder recorder;
+
+ PaintFlags red_paint;
+ red_paint.setColor(SK_ColorRED);
+
+ PaintCanvas* canvas = recorder.beginRecording(SkRect::MakeXYWH(
+ offset.x(), offset.y(), layer_size.width(), layer_size.height()));
+ canvas->translate(offset.x(), offset.y());
+ canvas->drawRect(SkRect::MakeWH(4, 4), red_paint);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ kVisualRect, recorder.finishRecordingAsPicture());
+}
+
+} // namespace
+
+TEST(DisplayItemListTest, SingleDrawingItem) {
+ gfx::Rect layer_rect(100, 100);
+ PaintRecorder recorder;
+ PaintFlags blue_flags;
+ blue_flags.setColor(SK_ColorBLUE);
+ PaintFlags red_paint;
+ red_paint.setColor(SK_ColorRED);
+ unsigned char pixels[4 * 100 * 100] = {0};
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ gfx::PointF offset(8.f, 9.f);
+ gfx::RectF recording_rect(offset, gfx::SizeF(layer_rect.size()));
+ PaintCanvas* canvas =
+ recorder.beginRecording(gfx::RectFToSkRect(recording_rect));
+ canvas->translate(offset.x(), offset.y());
+ canvas->drawRect(SkRect::MakeLTRB(0.f, 0.f, 60.f, 60.f), red_paint);
+ canvas->drawRect(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f), blue_flags);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ kVisualRect, recorder.finishRecordingAsPicture());
+ list->Finalize();
+ 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());
+ SkiaPaintCanvas expected_canvas(expected_bitmap);
+ expected_canvas.clipRect(gfx::RectToSkRect(layer_rect));
+ expected_canvas.drawRect(
+ SkRect::MakeLTRB(0.f + offset.x(), 0.f + offset.y(), 60.f + offset.x(),
+ 60.f + offset.y()),
+ red_paint);
+ expected_canvas.drawRect(
+ SkRect::MakeLTRB(50.f + offset.x(), 50.f + offset.y(), 75.f + offset.x(),
+ 75.f + offset.y()),
+ blue_flags);
+
+ EXPECT_TRUE(CompareN32Pixels(pixels, expected_pixels, 100, 100));
+}
+
+TEST(DisplayItemListTest, ClipItem) {
+ gfx::Rect layer_rect(100, 100);
+ PaintRecorder recorder;
+ PaintFlags blue_flags;
+ blue_flags.setColor(SK_ColorBLUE);
+ PaintFlags red_paint;
+ red_paint.setColor(SK_ColorRED);
+ unsigned char pixels[4 * 100 * 100] = {0};
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ gfx::PointF first_offset(8.f, 9.f);
+ gfx::RectF first_recording_rect(first_offset, gfx::SizeF(layer_rect.size()));
+ PaintCanvas* canvas =
+ recorder.beginRecording(gfx::RectFToSkRect(first_recording_rect));
+ canvas->translate(first_offset.x(), first_offset.y());
+ canvas->drawRect(SkRect::MakeWH(60, 60), red_paint);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ kVisualRect, recorder.finishRecordingAsPicture());
+
+ gfx::Rect clip_rect(60, 60, 10, 10);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_rect, std::vector<SkRRect>(), true);
+
+ gfx::PointF second_offset(2.f, 3.f);
+ gfx::RectF second_recording_rect(second_offset,
+ gfx::SizeF(layer_rect.size()));
+ canvas = recorder.beginRecording(gfx::RectFToSkRect(second_recording_rect));
+ canvas->translate(second_offset.x(), second_offset.y());
+ canvas->drawRect(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f), blue_flags);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ kVisualRect, recorder.finishRecordingAsPicture());
+
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+ list->Finalize();
+
+ 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());
+ SkiaPaintCanvas expected_canvas(expected_bitmap);
+ expected_canvas.clipRect(gfx::RectToSkRect(layer_rect));
+ expected_canvas.drawRect(
+ SkRect::MakeLTRB(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.drawRect(
+ SkRect::MakeLTRB(50.f + second_offset.x(), 50.f + second_offset.y(),
+ 75.f + second_offset.x(), 75.f + second_offset.y()),
+ blue_flags);
+
+ EXPECT_TRUE(CompareN32Pixels(pixels, expected_pixels, 100, 100));
+}
+
+TEST(DisplayItemListTest, TransformItem) {
+ gfx::Rect layer_rect(100, 100);
+ PaintRecorder recorder;
+ PaintFlags blue_flags;
+ blue_flags.setColor(SK_ColorBLUE);
+ PaintFlags red_paint;
+ red_paint.setColor(SK_ColorRED);
+ unsigned char pixels[4 * 100 * 100] = {0};
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ gfx::PointF first_offset(8.f, 9.f);
+ gfx::RectF first_recording_rect(first_offset, gfx::SizeF(layer_rect.size()));
+ PaintCanvas* canvas =
+ recorder.beginRecording(gfx::RectFToSkRect(first_recording_rect));
+ canvas->translate(first_offset.x(), first_offset.y());
+ canvas->drawRect(SkRect::MakeWH(60, 60), red_paint);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ kVisualRect, recorder.finishRecordingAsPicture());
+
+ gfx::Transform transform;
+ transform.Rotate(45.0);
+ list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(transform);
+
+ gfx::PointF second_offset(2.f, 3.f);
+ gfx::RectF second_recording_rect(second_offset,
+ gfx::SizeF(layer_rect.size()));
+ canvas = recorder.beginRecording(gfx::RectFToSkRect(second_recording_rect));
+ canvas->translate(second_offset.x(), second_offset.y());
+ canvas->drawRect(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f), blue_flags);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ kVisualRect, recorder.finishRecordingAsPicture());
+
+ list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>();
+ list->Finalize();
+
+ 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());
+ SkiaPaintCanvas expected_canvas(expected_bitmap);
+ expected_canvas.clipRect(gfx::RectToSkRect(layer_rect));
+ expected_canvas.drawRect(
+ SkRect::MakeLTRB(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.drawRect(
+ SkRect::MakeLTRB(50.f + second_offset.x(), 50.f + second_offset.y(),
+ 75.f + second_offset.x(), 75.f + second_offset.y()),
+ blue_flags);
+
+ EXPECT_TRUE(CompareN32Pixels(pixels, expected_pixels, 100, 100));
+}
+
+TEST(DisplayItemListTest, FilterItem) {
+ gfx::Rect layer_rect(100, 100);
+ FilterOperations filters;
+ unsigned char pixels[4 * 100 * 100] = {0};
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ sk_sp<SkSurface> source_surface = SkSurface::MakeRasterN32Premul(50, 50);
+ SkCanvas* source_canvas = source_surface->getCanvas();
+ source_canvas->clear(SkColorSetRGB(128, 128, 128));
+ sk_sp<SkImage> source_image = source_surface->makeImageSnapshot();
+
+ // 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.
+ // SkImageSource), 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
+ // SkImageSource filter in |filters|. Here, |src| is |filter_bounds|, defined
+ // below.
+ sk_sp<SkImageFilter> image_filter = SkImageSource::Make(source_image);
+ filters.Append(FilterOperation::CreateReferenceFilter(image_filter));
+ filters.Append(FilterOperation::CreateBrightnessFilter(0.5f));
+ gfx::RectF filter_bounds(10.f, 10.f, 50.f, 50.f);
+ list->CreateAndAppendPairedBeginItem<FilterDisplayItem>(
+ filters, filter_bounds, filter_bounds.origin());
+
+ // Include a rect drawing so that filter is actually applied to something.
+ {
+ PaintRecorder recorder;
+
+ PaintFlags red_paint;
+ red_paint.setColor(SK_ColorRED);
+
+ PaintCanvas* canvas = recorder.beginRecording(
+ SkRect::MakeXYWH(0, 0, layer_rect.width(), layer_rect.height()));
+ canvas->drawRect(
+ SkRect::MakeLTRB(filter_bounds.x(), filter_bounds.y(),
+ filter_bounds.right(), filter_bounds.bottom()),
+ red_paint);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ ToNearestRect(filter_bounds), recorder.finishRecordingAsPicture());
+ }
+
+ list->CreateAndAppendPairedEndItem<EndFilterDisplayItem>();
+ list->Finalize();
+
+ DrawDisplayList(pixels, layer_rect, list);
+
+ SkBitmap expected_bitmap;
+ unsigned char expected_pixels[4 * 100 * 100] = {0};
+ PaintFlags 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());
+ SkiaPaintCanvas expected_canvas(expected_bitmap);
+ expected_canvas.drawRect(RectFToSkRect(filter_bounds), paint);
+
+ EXPECT_TRUE(CompareN32Pixels(pixels, expected_pixels, 100, 100));
+}
+
+TEST(DisplayItemListTest, ApproximateMemoryUsage) {
+ const int kNumCommandsInTestSkPicture = 1000;
+ size_t memory_usage;
+
+ // Make an PaintRecord whose size is known.
+ gfx::Rect layer_rect(100, 100);
+ PaintRecorder recorder;
+ PaintFlags blue_flags;
+ blue_flags.setColor(SK_ColorBLUE);
+ PaintCanvas* canvas = recorder.beginRecording(gfx::RectToSkRect(layer_rect));
+ for (int i = 0; i < kNumCommandsInTestSkPicture; i++)
+ canvas->drawRect(SkRect(), blue_flags);
+ sk_sp<PaintRecord> record = recorder.finishRecordingAsPicture();
+ size_t record_size = record->approximateBytesUsed();
+ ASSERT_GE(record_size, kNumCommandsInTestSkPicture * sizeof(SkRect));
+
+ auto list = make_scoped_refptr(new DisplayItemList);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(kVisualRect, record);
+ list->Finalize();
+ memory_usage = list->ApproximateMemoryUsage();
+ EXPECT_GE(memory_usage, record_size);
+ EXPECT_LE(memory_usage, 2 * record_size);
+}
+
+TEST(DisplayItemListTest, AsValueWithNoItems) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+ list->SetRetainVisualRectsForTesting(true);
+ list->Finalize();
+
+ std::string value = list->CreateTracedValue(true)->ToString();
+ EXPECT_EQ(value.find("\"layer_rect\": [0,0,0,0]"), std::string::npos);
+ EXPECT_NE(value.find("\"items\":[]"), std::string::npos);
+ EXPECT_EQ(value.find("visualRect: [0,0 42x42]"), std::string::npos);
+ EXPECT_NE(value.find("\"skp64\":"), std::string::npos);
+
+ value = list->CreateTracedValue(false)->ToString();
+ EXPECT_EQ(value.find("\"layer_rect\": [0,0,0,0]"), std::string::npos);
+ EXPECT_EQ(value.find("\"items\":"), std::string::npos);
+ EXPECT_EQ(value.find("visualRect: [0,0 42x42]"), std::string::npos);
+ EXPECT_NE(value.find("\"skp64\":"), std::string::npos);
+}
+
+TEST(DisplayItemListTest, AsValueWithItems) {
+ gfx::Rect layer_rect = gfx::Rect(1, 2, 8, 9);
+ auto list = make_scoped_refptr(new DisplayItemList);
+ list->SetRetainVisualRectsForTesting(true);
+ gfx::Transform transform;
+ transform.Translate(6.f, 7.f);
+ list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(transform);
+ AppendFirstSerializationTestPicture(list, layer_rect.size());
+ list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>();
+ list->Finalize();
+
+ std::string value = list->CreateTracedValue(true)->ToString();
+ EXPECT_EQ(value.find("\"layer_rect\": [0,0,42,42]"), std::string::npos);
+ EXPECT_NE(value.find("{\"items\":[\"TransformDisplayItem"),
+ std::string::npos);
+ EXPECT_NE(value.find("visualRect: [0,0 42x42]"), std::string::npos);
+ EXPECT_NE(value.find("\"skp64\":"), std::string::npos);
+
+ value = list->CreateTracedValue(false)->ToString();
+ EXPECT_EQ(value.find("\"layer_rect\": [0,0,42,42]"), std::string::npos);
+ EXPECT_EQ(value.find("{\"items\":[\"TransformDisplayItem"),
+ std::string::npos);
+ EXPECT_EQ(value.find("visualRect: [0,0 42x42]"), std::string::npos);
+ EXPECT_NE(value.find("\"skp64\":"), std::string::npos);
+}
+
+TEST(DisplayItemListTest, SizeEmpty) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+ EXPECT_EQ(0u, list->size());
+}
+
+TEST(DisplayItemListTest, SizeOne) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+ gfx::Rect drawing_bounds(5, 6, 1, 1);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_bounds, CreateRectPicture(drawing_bounds));
+ EXPECT_EQ(1u, list->size());
+}
+
+TEST(DisplayItemListTest, SizeMultiple) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+ EXPECT_EQ(2u, list->size());
+}
+
+TEST(DisplayItemListTest, AppendVisualRectSimple) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // One drawing: D.
+
+ gfx::Rect drawing_bounds(5, 6, 7, 8);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_bounds, CreateRectPicture(drawing_bounds));
+
+ EXPECT_EQ(1u, list->size());
+ EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(0));
+}
+
+TEST(DisplayItemListTest, AppendVisualRectEmptyBlock) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // One block: B1, E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(2u, list->size());
+ EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(1));
+}
+
+TEST(DisplayItemListTest, AppendVisualRectEmptyBlockContainingEmptyBlock) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // Two nested blocks: B1, B2, E2, E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+ list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform());
+ list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>();
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(4u, list->size());
+ EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(2));
+ EXPECT_RECT_EQ(gfx::Rect(), list->VisualRectForTesting(3));
+}
+
+TEST(DisplayItemListTest, AppendVisualRectBlockContainingDrawing) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // One block with one drawing: B1, Da, E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ gfx::Rect drawing_bounds(5, 6, 1, 1);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_bounds, CreateRectPicture(drawing_bounds));
+
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(3u, list->size());
+ EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(2));
+}
+
+TEST(DisplayItemListTest, AppendVisualRectBlockContainingEscapedDrawing) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // One block with one drawing: B1, Da (escapes), E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ gfx::Rect drawing_bounds(1, 2, 3, 4);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_bounds, CreateRectPicture(drawing_bounds));
+
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(3u, list->size());
+ EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(drawing_bounds, list->VisualRectForTesting(2));
+}
+
+TEST(DisplayItemListTest,
+ AppendVisualRectDrawingFollowedByBlockContainingEscapedDrawing) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // One drawing followed by one block with one drawing: Da, B1, Db (escapes),
+ // E1.
+
+ gfx::Rect drawing_a_bounds(1, 2, 3, 4);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_a_bounds, CreateRectPicture(drawing_a_bounds));
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ gfx::Rect drawing_b_bounds(13, 14, 1, 1);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_b_bounds, CreateRectPicture(drawing_b_bounds));
+
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(4u, list->size());
+ EXPECT_RECT_EQ(drawing_a_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(2));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(3));
+}
+
+TEST(DisplayItemListTest, AppendVisualRectTwoBlocksTwoDrawings) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // Multiple nested blocks with drawings amidst: B1, Da, B2, Db, E2, E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ gfx::Rect drawing_a_bounds(5, 6, 1, 1);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_a_bounds, CreateRectPicture(drawing_a_bounds));
+
+ list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform());
+
+ gfx::Rect drawing_b_bounds(7, 8, 1, 1);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_b_bounds, CreateRectPicture(drawing_b_bounds));
+
+ list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>();
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(6u, list->size());
+ gfx::Rect merged_drawing_bounds = gfx::Rect(drawing_a_bounds);
+ merged_drawing_bounds.Union(drawing_b_bounds);
+ EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(drawing_a_bounds, list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(2));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(3));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(4));
+ EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(5));
+}
+
+TEST(DisplayItemListTest,
+ AppendVisualRectTwoBlocksTwoDrawingsInnerDrawingEscaped) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // Multiple nested blocks with drawings amidst: B1, Da, B2, Db (escapes), E2,
+ // E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ gfx::Rect drawing_a_bounds(5, 6, 1, 1);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_a_bounds, CreateRectPicture(drawing_a_bounds));
+
+ list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform());
+
+ gfx::Rect drawing_b_bounds(1, 2, 3, 4);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_b_bounds, CreateRectPicture(drawing_b_bounds));
+
+ list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>();
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(6u, list->size());
+ gfx::Rect merged_drawing_bounds = gfx::Rect(drawing_a_bounds);
+ merged_drawing_bounds.Union(drawing_b_bounds);
+ EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(drawing_a_bounds, list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(2));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(3));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(4));
+ EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(5));
+}
+
+TEST(DisplayItemListTest,
+ AppendVisualRectTwoBlocksTwoDrawingsOuterDrawingEscaped) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // Multiple nested blocks with drawings amidst: B1, Da (escapes), B2, Db, E2,
+ // E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ gfx::Rect drawing_a_bounds(1, 2, 3, 4);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_a_bounds, CreateRectPicture(drawing_a_bounds));
+
+ list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform());
+
+ gfx::Rect drawing_b_bounds(7, 8, 1, 1);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_b_bounds, CreateRectPicture(drawing_b_bounds));
+
+ list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>();
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(6u, list->size());
+ gfx::Rect merged_drawing_bounds = gfx::Rect(drawing_a_bounds);
+ merged_drawing_bounds.Union(drawing_b_bounds);
+ EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(drawing_a_bounds, list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(2));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(3));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(4));
+ EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(5));
+}
+
+TEST(DisplayItemListTest,
+ AppendVisualRectTwoBlocksTwoDrawingsBothDrawingsEscaped) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // Multiple nested blocks with drawings amidst:
+ // B1, Da (escapes to the right), B2, Db (escapes to the left), E2, E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ gfx::Rect drawing_a_bounds(13, 14, 1, 1);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_a_bounds, CreateRectPicture(drawing_a_bounds));
+
+ list->CreateAndAppendPairedBeginItem<TransformDisplayItem>(gfx::Transform());
+
+ gfx::Rect drawing_b_bounds(1, 2, 3, 4);
+ list->CreateAndAppendDrawingItem<DrawingDisplayItem>(
+ drawing_b_bounds, CreateRectPicture(drawing_b_bounds));
+
+ list->CreateAndAppendPairedEndItem<EndTransformDisplayItem>();
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(6u, list->size());
+ gfx::Rect merged_drawing_bounds = gfx::Rect(drawing_a_bounds);
+ merged_drawing_bounds.Union(drawing_b_bounds);
+ EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(drawing_a_bounds, list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(2));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(3));
+ EXPECT_RECT_EQ(drawing_b_bounds, list->VisualRectForTesting(4));
+ EXPECT_RECT_EQ(merged_drawing_bounds, list->VisualRectForTesting(5));
+}
+
+TEST(DisplayItemListTest, AppendVisualRectOneFilterNoDrawings) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // One filter containing no drawings: Bf, Ef
+
+ gfx::Rect filter_bounds(5, 6, 1, 1);
+ list->CreateAndAppendPairedBeginItemWithVisualRect<FilterDisplayItem>(
+ filter_bounds, FilterOperations(), gfx::RectF(filter_bounds),
+ gfx::PointF(filter_bounds.origin()));
+
+ list->CreateAndAppendPairedEndItem<EndFilterDisplayItem>();
+
+ EXPECT_EQ(2u, list->size());
+ EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(1));
+}
+
+TEST(DisplayItemListTest, AppendVisualRectBlockContainingFilterNoDrawings) {
+ auto list = make_scoped_refptr(new DisplayItemList);
+
+ // One block containing one filter and no drawings: B1, Bf, Ef, E1.
+
+ gfx::Rect clip_bounds(5, 6, 7, 8);
+ list->CreateAndAppendPairedBeginItem<ClipDisplayItem>(
+ clip_bounds, std::vector<SkRRect>(), true);
+
+ gfx::Rect filter_bounds(5, 6, 1, 1);
+ list->CreateAndAppendPairedBeginItemWithVisualRect<FilterDisplayItem>(
+ filter_bounds, FilterOperations(), gfx::RectF(filter_bounds),
+ gfx::PointF(filter_bounds.origin()));
+
+ list->CreateAndAppendPairedEndItem<EndFilterDisplayItem>();
+ list->CreateAndAppendPairedEndItem<EndClipDisplayItem>();
+
+ EXPECT_EQ(4u, list->size());
+ EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(0));
+ EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(1));
+ EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(2));
+ EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(3));
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/draw_image.cc b/chromium/cc/paint/draw_image.cc
new file mode 100644
index 00000000000..6c0bb644864
--- /dev/null
+++ b/chromium/cc/paint/draw_image.cc
@@ -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.
+
+#include "cc/paint/draw_image.h"
+
+namespace cc {
+namespace {
+
+// Helper funciton to extract a scale from the matrix. Returns true on success
+// and false on failure.
+bool ExtractScale(const SkMatrix& matrix, SkSize* scale) {
+ *scale = SkSize::Make(matrix.getScaleX(), matrix.getScaleY());
+ if (matrix.getType() & SkMatrix::kAffine_Mask) {
+ if (!matrix.decomposeScale(scale)) {
+ scale->set(1, 1);
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+DrawImage::DrawImage()
+ : image_(nullptr),
+ src_rect_(SkIRect::MakeXYWH(0, 0, 0, 0)),
+ filter_quality_(kNone_SkFilterQuality),
+ matrix_(SkMatrix::I()),
+ scale_(SkSize::Make(1.f, 1.f)),
+ matrix_is_decomposable_(true) {}
+
+DrawImage::DrawImage(sk_sp<const SkImage> image,
+ const SkIRect& src_rect,
+ SkFilterQuality filter_quality,
+ const SkMatrix& matrix,
+ const gfx::ColorSpace& target_color_space)
+ : image_(std::move(image)),
+ src_rect_(src_rect),
+ filter_quality_(filter_quality),
+ matrix_(matrix),
+ target_color_space_(target_color_space) {
+ matrix_is_decomposable_ = ExtractScale(matrix_, &scale_);
+}
+
+DrawImage::DrawImage(const DrawImage& other) = default;
+
+DrawImage::~DrawImage() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/draw_image.h b/chromium/cc/paint/draw_image.h
new file mode 100644
index 00000000000..78513b90d1e
--- /dev/null
+++ b/chromium/cc/paint/draw_image.h
@@ -0,0 +1,65 @@
+// 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_PAINT_DRAW_IMAGE_H_
+#define CC_PAINT_DRAW_IMAGE_H_
+
+#include "cc/paint/paint_export.h"
+#include "third_party/skia/include/core/SkFilterQuality.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/color_space.h"
+#include "ui/gfx/geometry/size_f.h"
+
+namespace cc {
+
+// TODO(vmpstr): This should probably be DISALLOW_COPY_AND_ASSIGN and transport
+// it around using a pointer, since it became kind of large. Profile.
+class CC_PAINT_EXPORT DrawImage {
+ public:
+ DrawImage();
+ DrawImage(sk_sp<const SkImage> image,
+ const SkIRect& src_rect,
+ SkFilterQuality filter_quality,
+ const SkMatrix& matrix,
+ const gfx::ColorSpace& target_color_space);
+ DrawImage(const DrawImage& other);
+ ~DrawImage();
+
+ const sk_sp<const SkImage>& image() const { return image_; }
+ const SkSize& scale() const { return scale_; }
+ const SkIRect& src_rect() const { return src_rect_; }
+ SkFilterQuality filter_quality() const { return filter_quality_; }
+ bool matrix_is_decomposable() const { return matrix_is_decomposable_; }
+ const SkMatrix& matrix() const { return matrix_; }
+ const gfx::ColorSpace& target_color_space() const {
+ return target_color_space_;
+ }
+
+ DrawImage ApplyScale(float scale) const {
+ SkMatrix scaled_matrix = matrix_;
+ scaled_matrix.preScale(scale, scale);
+ return DrawImage(image_, src_rect_, filter_quality_, scaled_matrix,
+ target_color_space_);
+ }
+ DrawImage ApplyTargetColorSpace(const gfx::ColorSpace& target_color_space) {
+ return DrawImage(image_, src_rect_, filter_quality_, matrix_,
+ target_color_space);
+ }
+
+ private:
+ sk_sp<const SkImage> image_;
+ SkIRect src_rect_;
+ SkFilterQuality filter_quality_;
+ SkMatrix matrix_;
+ SkSize scale_;
+ bool matrix_is_decomposable_;
+ gfx::ColorSpace target_color_space_;
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_DRAW_IMAGE_H_
diff --git a/chromium/cc/paint/drawing_display_item.cc b/chromium/cc/paint/drawing_display_item.cc
new file mode 100644
index 00000000000..60b09ee0e27
--- /dev/null
+++ b/chromium/cc/paint/drawing_display_item.cc
@@ -0,0 +1,30 @@
+// 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/paint/drawing_display_item.h"
+
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace cc {
+
+DrawingDisplayItem::DrawingDisplayItem() : DisplayItem(DRAWING) {}
+
+DrawingDisplayItem::DrawingDisplayItem(sk_sp<const PaintRecord> record)
+ : DisplayItem(DRAWING), picture(std::move(record)) {}
+
+DrawingDisplayItem::DrawingDisplayItem(const DrawingDisplayItem& item)
+ : DisplayItem(DRAWING), picture(item.picture) {}
+
+DrawingDisplayItem::~DrawingDisplayItem() = default;
+
+size_t DrawingDisplayItem::ExternalMemoryUsage() const {
+ return picture->approximateBytesUsed();
+}
+
+DISABLE_CFI_PERF
+int DrawingDisplayItem::ApproximateOpCount() const {
+ return picture->approximateOpCount();
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/drawing_display_item.h b/chromium/cc/paint/drawing_display_item.h
new file mode 100644
index 00000000000..56c7b17aef2
--- /dev/null
+++ b/chromium/cc/paint/drawing_display_item.h
@@ -0,0 +1,32 @@
+// 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_PAINT_DRAWING_DISPLAY_ITEM_H_
+#define CC_PAINT_DRAWING_DISPLAY_ITEM_H_
+
+#include <stddef.h>
+
+#include "cc/paint/display_item.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_record.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT DrawingDisplayItem : public DisplayItem {
+ public:
+ DrawingDisplayItem();
+ explicit DrawingDisplayItem(sk_sp<const PaintRecord> record);
+ explicit DrawingDisplayItem(const DrawingDisplayItem& item);
+ ~DrawingDisplayItem() override;
+
+ size_t ExternalMemoryUsage() const;
+ int ApproximateOpCount() const;
+
+ const sk_sp<const PaintRecord> picture;
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_DRAWING_DISPLAY_ITEM_H_
diff --git a/chromium/cc/paint/filter_display_item.cc b/chromium/cc/paint/filter_display_item.cc
new file mode 100644
index 00000000000..de8f0d3d31e
--- /dev/null
+++ b/chromium/cc/paint/filter_display_item.cc
@@ -0,0 +1,20 @@
+// 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/paint/filter_display_item.h"
+
+namespace cc {
+
+FilterDisplayItem::FilterDisplayItem(const FilterOperations& filters,
+ const gfx::RectF& bounds,
+ const gfx::PointF& origin)
+ : DisplayItem(FILTER), filters(filters), bounds(bounds), origin(origin) {}
+
+FilterDisplayItem::~FilterDisplayItem() = default;
+
+EndFilterDisplayItem::EndFilterDisplayItem() : DisplayItem(END_FILTER) {}
+
+EndFilterDisplayItem::~EndFilterDisplayItem() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/filter_display_item.h b/chromium/cc/paint/filter_display_item.h
new file mode 100644
index 00000000000..c4f18dcbc85
--- /dev/null
+++ b/chromium/cc/paint/filter_display_item.h
@@ -0,0 +1,45 @@
+// 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_PAINT_FILTER_DISPLAY_ITEM_H_
+#define CC_PAINT_FILTER_DISPLAY_ITEM_H_
+
+#include "cc/base/filter_operations.h"
+#include "cc/paint/display_item.h"
+#include "cc/paint/paint_export.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT FilterDisplayItem : public DisplayItem {
+ public:
+ FilterDisplayItem(const FilterOperations& filters,
+ const gfx::RectF& bounds,
+ const gfx::PointF& origin);
+ ~FilterDisplayItem() override;
+
+ size_t ExternalMemoryUsage() const {
+ // FilterOperations doesn't expose its capacity, but size is probably good
+ // enough.
+ return filters.size() * sizeof(filters.at(0));
+ }
+ int ApproximateOpCount() const { return 1; }
+
+ const FilterOperations filters;
+ const gfx::RectF bounds;
+ const gfx::PointF origin;
+};
+
+class CC_PAINT_EXPORT EndFilterDisplayItem : public DisplayItem {
+ public:
+ EndFilterDisplayItem();
+ ~EndFilterDisplayItem() override;
+
+ int ApproximateOpCount() const { return 0; }
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_FILTER_DISPLAY_ITEM_H_
diff --git a/chromium/cc/paint/float_clip_display_item.cc b/chromium/cc/paint/float_clip_display_item.cc
new file mode 100644
index 00000000000..85cd5adff90
--- /dev/null
+++ b/chromium/cc/paint/float_clip_display_item.cc
@@ -0,0 +1,19 @@
+// 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/paint/float_clip_display_item.h"
+
+namespace cc {
+
+FloatClipDisplayItem::FloatClipDisplayItem(const gfx::RectF& clip_rect)
+ : DisplayItem(FLOAT_CLIP), clip_rect(clip_rect) {}
+
+FloatClipDisplayItem::~FloatClipDisplayItem() = default;
+
+EndFloatClipDisplayItem::EndFloatClipDisplayItem()
+ : DisplayItem(END_FLOAT_CLIP) {}
+
+EndFloatClipDisplayItem::~EndFloatClipDisplayItem() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/float_clip_display_item.h b/chromium/cc/paint/float_clip_display_item.h
new file mode 100644
index 00000000000..43d751b36ce
--- /dev/null
+++ b/chromium/cc/paint/float_clip_display_item.h
@@ -0,0 +1,37 @@
+// 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_PAINT_FLOAT_CLIP_DISPLAY_ITEM_H_
+#define CC_PAINT_FLOAT_CLIP_DISPLAY_ITEM_H_
+
+#include <stddef.h>
+
+#include "cc/paint/display_item.h"
+#include "cc/paint/paint_export.h"
+#include "ui/gfx/geometry/rect_f.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT FloatClipDisplayItem : public DisplayItem {
+ public:
+ explicit FloatClipDisplayItem(const gfx::RectF& clip_rect);
+ ~FloatClipDisplayItem() override;
+
+ size_t ExternalMemoryUsage() const { return 0; }
+ int ApproximateOpCount() const { return 1; }
+
+ const gfx::RectF clip_rect;
+};
+
+class CC_PAINT_EXPORT EndFloatClipDisplayItem : public DisplayItem {
+ public:
+ EndFloatClipDisplayItem();
+ ~EndFloatClipDisplayItem() override;
+
+ int ApproximateOpCount() const { return 0; }
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_FLOAT_CLIP_DISPLAY_ITEM_H_
diff --git a/chromium/cc/paint/image_id.h b/chromium/cc/paint/image_id.h
new file mode 100644
index 00000000000..0ffaafd782f
--- /dev/null
+++ b/chromium/cc/paint/image_id.h
@@ -0,0 +1,20 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_IMAGE_ID_H_
+#define CC_PAINT_IMAGE_ID_H_
+
+#include <stdint.h>
+#include <unordered_set>
+
+#include "base/containers/flat_set.h"
+
+namespace cc {
+
+using ImageId = uint32_t;
+using ImageIdFlatSet = base::flat_set<ImageId>;
+
+} // namespace cc
+
+#endif // CC_PAINT_IMAGE_ID_H_
diff --git a/chromium/cc/paint/largest_display_item.cc b/chromium/cc/paint/largest_display_item.cc
new file mode 100644
index 00000000000..448c2b85670
--- /dev/null
+++ b/chromium/cc/paint/largest_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/paint/largest_display_item.h"
+
+#include <stddef.h>
+
+#include <algorithm>
+
+#include "cc/paint/clip_display_item.h"
+#include "cc/paint/clip_path_display_item.h"
+#include "cc/paint/compositing_display_item.h"
+#include "cc/paint/drawing_display_item.h"
+#include "cc/paint/filter_display_item.h"
+#include "cc/paint/float_clip_display_item.h"
+#include "cc/paint/transform_display_item.h"
+
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace {
+// Either FilterDisplayItem or TransformDisplayItem is largest. It depends on
+// the platform.
+constexpr size_t kLargestDisplayItemSize =
+ sizeof(cc::FilterDisplayItem) > sizeof(cc::TransformDisplayItem)
+ ? sizeof(cc::FilterDisplayItem)
+ : 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/chromium/cc/paint/largest_display_item.h b/chromium/cc/paint/largest_display_item.h
new file mode 100644
index 00000000000..e885ced9bf0
--- /dev/null
+++ b/chromium/cc/paint/largest_display_item.h
@@ -0,0 +1,18 @@
+// 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_PAINT_LARGEST_DISPLAY_ITEM_H_
+#define CC_PAINT_LARGEST_DISPLAY_ITEM_H_
+
+#include <stddef.h>
+
+#include "cc/paint/paint_export.h"
+
+namespace cc {
+
+CC_PAINT_EXPORT size_t LargestDisplayItemSize();
+
+} // namespace cc
+
+#endif // CC_PAINT_LARGEST_DISPLAY_ITEM_H_
diff --git a/chromium/cc/paint/paint_canvas.cc b/chromium/cc/paint/paint_canvas.cc
index 53dab8fb45b..4aab0652b19 100644
--- a/chromium/cc/paint/paint_canvas.cc
+++ b/chromium/cc/paint/paint_canvas.cc
@@ -4,6 +4,10 @@
#include "cc/paint/paint_canvas.h"
+#include "base/memory/ptr_util.h"
+#include "cc/paint/paint_record.h"
+#include "cc/paint/paint_recorder.h"
+#include "third_party/skia/include/core/SkAnnotation.h"
#include "third_party/skia/include/core/SkMetaData.h"
#if defined(OS_MACOSX)
@@ -14,32 +18,8 @@ const char kIsPreviewMetafileKey[] = "CrIsPreviewMetafile";
namespace cc {
-PaintCanvasPassThrough::PaintCanvasPassThrough(SkCanvas* canvas)
- : SkNWayCanvas(canvas->getBaseLayerSize().width(),
- canvas->getBaseLayerSize().height()) {
- SkIRect raster_bounds;
- canvas->getDeviceClipBounds(&raster_bounds);
- clipRect(SkRect::MakeFromIRect(raster_bounds));
- setMatrix(canvas->getTotalMatrix());
- addCanvas(canvas);
-}
-
-PaintCanvasPassThrough::PaintCanvasPassThrough(int width, int height)
- : SkNWayCanvas(width, height) {}
-
-PaintCanvasPassThrough::~PaintCanvasPassThrough() = default;
-
bool ToPixmap(PaintCanvas* canvas, SkPixmap* output) {
- SkImageInfo info;
- size_t row_bytes;
- void* pixels = canvas->accessTopLayerPixels(&info, &row_bytes);
- if (!pixels) {
- output->reset();
- return false;
- }
-
- output->reset(info, pixels, row_bytes);
- return true;
+ return canvas->ToPixmap(output);
}
#if defined(OS_MACOSX)
diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h
index 649096cf8ea..daacc301788 100644
--- a/chromium/cc/paint/paint_canvas.h
+++ b/chromium/cc/paint/paint_canvas.h
@@ -5,26 +5,228 @@
#ifndef CC_PAINT_PAINT_CANVAS_H_
#define CC_PAINT_PAINT_CANVAS_H_
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
#include "build/build_config.h"
#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_record.h"
#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/utils/SkNWayCanvas.h"
-#include "third_party/skia/include/utils/SkNoDrawCanvas.h"
namespace cc {
-using PaintCanvas = SkCanvas;
-using PaintCanvasNoDraw = SkNoDrawCanvas;
-using PaintCanvasAutoRestore = SkAutoCanvasRestore;
+class DisplayItemList;
+class PaintFlags;
-class CC_PAINT_EXPORT PaintCanvasPassThrough : public SkNWayCanvas {
+class CC_PAINT_EXPORT PaintCanvas {
public:
- explicit PaintCanvasPassThrough(SkCanvas* canvas);
- PaintCanvasPassThrough(int width, int height);
- ~PaintCanvasPassThrough() override;
+ virtual ~PaintCanvas() {}
+
+ virtual SkMetaData& getMetaData() = 0;
+ virtual SkImageInfo imageInfo() const = 0;
+
+ // TODO(enne): It would be nice to get rid of flush() entirely, as it
+ // doesn't really make sense for recording. However, this gets used by
+ // SkCanvasVideoRenderer which takes a PaintCanvas to paint both
+ // software and hardware video. This is super entangled with ImageBuffer
+ // and canvas/video painting in Blink where the same paths are used for
+ // both recording and gpu work.
+ virtual void flush() = 0;
+
+ virtual SkISize getBaseLayerSize() const = 0;
+ virtual bool writePixels(const SkImageInfo& info,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y) = 0;
+ virtual int save() = 0;
+ virtual int saveLayer(const SkRect* bounds, const PaintFlags* flags) = 0;
+ virtual int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) = 0;
+
+ virtual void restore() = 0;
+ virtual int getSaveCount() const = 0;
+ virtual void restoreToCount(int save_count) = 0;
+ virtual void translate(SkScalar dx, SkScalar dy) = 0;
+ virtual void scale(SkScalar sx, SkScalar sy) = 0;
+ virtual void rotate(SkScalar degrees) = 0;
+ virtual void concat(const SkMatrix& matrix) = 0;
+ virtual void setMatrix(const SkMatrix& matrix) = 0;
+
+ virtual void clipRect(const SkRect& rect,
+ SkClipOp op,
+ bool do_anti_alias) = 0;
+ void clipRect(const SkRect& rect, SkClipOp op) { clipRect(rect, op, false); }
+ void clipRect(const SkRect& rect, bool do_anti_alias) {
+ clipRect(rect, SkClipOp::kIntersect, do_anti_alias);
+ }
+ void clipRect(const SkRect& rect) {
+ clipRect(rect, SkClipOp::kIntersect, false);
+ }
+
+ virtual void clipRRect(const SkRRect& rrect,
+ SkClipOp op,
+ bool do_anti_alias) = 0;
+ void clipRRect(const SkRRect& rrect, bool do_anti_alias) {
+ clipRRect(rrect, SkClipOp::kIntersect, do_anti_alias);
+ }
+ void clipRRect(const SkRRect& rrect, SkClipOp op) {
+ clipRRect(rrect, op, false);
+ }
+ void clipRRect(const SkRRect& rrect) {
+ clipRRect(rrect, SkClipOp::kIntersect, false);
+ }
+
+ virtual void clipPath(const SkPath& path,
+ SkClipOp op,
+ bool do_anti_alias) = 0;
+ void clipPath(const SkPath& path, SkClipOp op) { clipPath(path, op, false); }
+ void clipPath(const SkPath& path, bool do_anti_alias) {
+ clipPath(path, SkClipOp::kIntersect, do_anti_alias);
+ }
+
+ virtual bool quickReject(const SkRect& rect) const = 0;
+ virtual bool quickReject(const SkPath& path) const = 0;
+ virtual SkRect getLocalClipBounds() const = 0;
+ virtual bool getLocalClipBounds(SkRect* bounds) const = 0;
+ virtual SkIRect getDeviceClipBounds() const = 0;
+ virtual bool getDeviceClipBounds(SkIRect* bounds) const = 0;
+ virtual void drawColor(SkColor color, SkBlendMode mode) = 0;
+ void drawColor(SkColor color) { drawColor(color, SkBlendMode::kSrcOver); }
+ virtual void clear(SkColor color) = 0;
+
+ virtual void drawLine(SkScalar x0,
+ SkScalar y0,
+ SkScalar x1,
+ SkScalar y1,
+ const PaintFlags& flags) = 0;
+ virtual void drawRect(const SkRect& rect, const PaintFlags& flags) = 0;
+ virtual void drawIRect(const SkIRect& rect, const PaintFlags& flags) = 0;
+ virtual void drawOval(const SkRect& oval, const PaintFlags& flags) = 0;
+ virtual void drawRRect(const SkRRect& rrect, const PaintFlags& flags) = 0;
+ virtual void drawDRRect(const SkRRect& outer,
+ const SkRRect& inner,
+ const PaintFlags& flags) = 0;
+ virtual void drawCircle(SkScalar cx,
+ SkScalar cy,
+ SkScalar radius,
+ const PaintFlags& flags) = 0;
+ virtual void drawArc(const SkRect& oval,
+ SkScalar start_angle,
+ SkScalar sweep_angle,
+ bool use_center,
+ const PaintFlags& flags) = 0;
+ virtual void drawRoundRect(const SkRect& rect,
+ SkScalar rx,
+ SkScalar ry,
+ const PaintFlags& flags) = 0;
+ virtual void drawPath(const SkPath& path, const PaintFlags& flags) = 0;
+ virtual void drawImage(sk_sp<const SkImage> image,
+ SkScalar left,
+ SkScalar top,
+ const PaintFlags* flags) = 0;
+ void drawImage(sk_sp<const SkImage> image, SkScalar left, SkScalar top) {
+ drawImage(image, left, top, nullptr);
+ }
+
+ enum SrcRectConstraint {
+ kStrict_SrcRectConstraint = SkCanvas::kStrict_SrcRectConstraint,
+ kFast_SrcRectConstraint = SkCanvas::kFast_SrcRectConstraint,
+ };
+
+ virtual void drawImageRect(sk_sp<const SkImage> image,
+ const SkRect& src,
+ const SkRect& dst,
+ const PaintFlags* flags,
+ SrcRectConstraint constraint) = 0;
+ virtual void drawBitmap(const SkBitmap& bitmap,
+ SkScalar left,
+ SkScalar top,
+ const PaintFlags* flags) = 0;
+ void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top) {
+ drawBitmap(bitmap, left, top, nullptr);
+ }
+
+ virtual void drawText(const void* text,
+ size_t byte_length,
+ SkScalar x,
+ SkScalar y,
+ const PaintFlags& flags) = 0;
+ virtual void drawPosText(const void* text,
+ size_t byte_length,
+ const SkPoint pos[],
+ const PaintFlags& flags) = 0;
+ virtual void drawTextBlob(sk_sp<SkTextBlob> blob,
+ SkScalar x,
+ SkScalar y,
+ const PaintFlags& flags) = 0;
+
+ virtual void drawDisplayItemList(
+ scoped_refptr<DisplayItemList> display_item_list) = 0;
+
+ virtual void drawPicture(sk_sp<const PaintRecord> record) = 0;
+
+ virtual bool isClipEmpty() const = 0;
+ virtual bool isClipRect() const = 0;
+ virtual const SkMatrix& getTotalMatrix() const = 0;
+
+ // For GraphicsContextCanvas only. Maybe this could be rewritten?
+ virtual void temporary_internal_describeTopLayer(SkMatrix* matrix,
+ SkIRect* clip_bounds) = 0;
+
+ virtual bool ToPixmap(SkPixmap* output) = 0;
+
+ enum class AnnotationType {
+ URL,
+ NAMED_DESTINATION,
+ LINK_TO_DESTINATION,
+ };
+ virtual void Annotate(AnnotationType type,
+ const SkRect& rect,
+ sk_sp<SkData> data) = 0;
+
+ // TODO(enne): maybe this should live on PaintRecord, but that's not
+ // possible when PaintRecord is a typedef.
+ virtual void PlaybackPaintRecord(sk_sp<const PaintRecord> record) = 0;
+
+ protected:
+ friend class PaintSurface;
+ friend class PaintRecorder;
+ friend CC_PAINT_EXPORT bool ToPixmap(PaintCanvas* canvas, SkPixmap* output);
+};
+
+class CC_PAINT_EXPORT PaintCanvasAutoRestore {
+ public:
+ PaintCanvasAutoRestore(PaintCanvas* canvas, bool save) : canvas_(canvas) {
+ if (canvas_) {
+ save_count_ = canvas_->getSaveCount();
+ if (save) {
+ canvas_->save();
+ }
+ }
+ }
+
+ ~PaintCanvasAutoRestore() {
+ if (canvas_) {
+ canvas_->restoreToCount(save_count_);
+ }
+ }
+
+ void restore() {
+ if (canvas_) {
+ canvas_->restoreToCount(save_count_);
+ canvas_ = nullptr;
+ }
+ }
+
+ private:
+ PaintCanvas* canvas_ = nullptr;
+ int save_count_ = 0;
};
-// TODO(enne): Move all these functions into PaintCanvas.
+// TODO(enne): Move all these functions into PaintCanvas. These are only
+// separate now to make the transition to concrete types easier by keeping
+// the base PaintCanvas type equivalent to the SkCanvas interface and
+// all these helper functions potentially operating on both.
// PaintCanvas equivalent of skia::GetWritablePixels.
CC_PAINT_EXPORT bool ToPixmap(PaintCanvas* canvas, SkPixmap* output);
diff --git a/chromium/cc/paint/paint_flags.h b/chromium/cc/paint/paint_flags.h
index aa112452121..b7e96c68e2e 100644
--- a/chromium/cc/paint/paint_flags.h
+++ b/chromium/cc/paint/paint_flags.h
@@ -5,14 +5,212 @@
#ifndef CC_PAINT_PAINT_FLAGS_H_
#define CC_PAINT_PAINT_FLAGS_H_
+#include "base/compiler_specific.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_shader.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColorFilter.h"
+#include "third_party/skia/include/core/SkDrawLooper.h"
+#include "third_party/skia/include/core/SkImageFilter.h"
+#include "third_party/skia/include/core/SkMaskFilter.h"
#include "third_party/skia/include/core/SkPaint.h"
+#include "third_party/skia/include/core/SkPathEffect.h"
+#include "third_party/skia/include/core/SkTypeface.h"
namespace cc {
-using PaintFlags = SkPaint;
+class CC_PAINT_EXPORT PaintFlags {
+ public:
+ enum Style {
+ kFill_Style = SkPaint::kFill_Style,
+ kStroke_Style = SkPaint::kStroke_Style,
+ kStrokeAndFill_Style = SkPaint::kStrokeAndFill_Style,
+ };
+ ALWAYS_INLINE Style getStyle() const {
+ return static_cast<Style>(paint_.getStyle());
+ }
+ ALWAYS_INLINE void setStyle(Style style) {
+ paint_.setStyle(static_cast<SkPaint::Style>(style));
+ }
+ ALWAYS_INLINE SkColor getColor() const { return paint_.getColor(); }
+ ALWAYS_INLINE void setColor(SkColor color) { paint_.setColor(color); }
+ ALWAYS_INLINE void setARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
+ paint_.setARGB(a, r, g, b);
+ }
+ ALWAYS_INLINE uint8_t getAlpha() const { return paint_.getAlpha(); }
+ ALWAYS_INLINE void setAlpha(U8CPU a) { paint_.setAlpha(a); }
+ ALWAYS_INLINE void setBlendMode(SkBlendMode mode) {
+ paint_.setBlendMode(mode);
+ }
+ ALWAYS_INLINE SkBlendMode getBlendMode() const {
+ return paint_.getBlendMode();
+ }
+ ALWAYS_INLINE bool isSrcOver() const { return paint_.isSrcOver(); }
+ ALWAYS_INLINE bool isAntiAlias() const { return paint_.isAntiAlias(); }
+ ALWAYS_INLINE void setAntiAlias(bool aa) { paint_.setAntiAlias(aa); }
+ ALWAYS_INLINE bool isVerticalText() const { return paint_.isVerticalText(); }
+ ALWAYS_INLINE void setVerticalText(bool vertical) {
+ paint_.setVerticalText(vertical);
+ }
+ ALWAYS_INLINE bool isSubpixelText() const { return paint_.isSubpixelText(); }
+ ALWAYS_INLINE void setSubpixelText(bool subpixel_text) {
+ paint_.setSubpixelText(subpixel_text);
+ }
+ ALWAYS_INLINE bool isLCDRenderText() const {
+ return paint_.isLCDRenderText();
+ }
+ ALWAYS_INLINE void setLCDRenderText(bool lcd_text) {
+ paint_.setLCDRenderText(lcd_text);
+ }
+ enum Hinting {
+ kNo_Hinting = SkPaint::kNo_Hinting,
+ kSlight_Hinting = SkPaint::kSlight_Hinting,
+ kNormal_Hinting = SkPaint::kNormal_Hinting, //!< this is the default
+ kFull_Hinting = SkPaint::kFull_Hinting
+ };
+ ALWAYS_INLINE Hinting getHinting() const {
+ return static_cast<Hinting>(paint_.getHinting());
+ }
+ ALWAYS_INLINE void setHinting(Hinting hinting_level) {
+ paint_.setHinting(static_cast<SkPaint::Hinting>(hinting_level));
+ }
+ ALWAYS_INLINE bool isAutohinted() const { return paint_.isAutohinted(); }
+ ALWAYS_INLINE void setAutohinted(bool use_auto_hinter) {
+ paint_.setAutohinted(use_auto_hinter);
+ }
+ ALWAYS_INLINE bool isDither() const { return paint_.isDither(); }
+ ALWAYS_INLINE void setDither(bool dither) { paint_.setDither(dither); }
+ enum TextEncoding {
+ kUTF8_TextEncoding = SkPaint::kUTF8_TextEncoding,
+ kUTF16_TextEncoding = SkPaint::kUTF16_TextEncoding,
+ kUTF32_TextEncoding = SkPaint::kUTF32_TextEncoding,
+ kGlyphID_TextEncoding = SkPaint::kGlyphID_TextEncoding
+ };
+ ALWAYS_INLINE TextEncoding getTextEncoding() const {
+ return static_cast<TextEncoding>(paint_.getTextEncoding());
+ }
+ ALWAYS_INLINE void setTextEncoding(TextEncoding encoding) {
+ paint_.setTextEncoding(static_cast<SkPaint::TextEncoding>(encoding));
+ }
+ ALWAYS_INLINE SkScalar getTextSize() const { return paint_.getTextSize(); }
+ ALWAYS_INLINE void setTextSize(SkScalar textSize) {
+ paint_.setTextSize(textSize);
+ }
+ ALWAYS_INLINE void setFilterQuality(SkFilterQuality quality) {
+ paint_.setFilterQuality(quality);
+ }
+ ALWAYS_INLINE SkFilterQuality getFilterQuality() const {
+ return paint_.getFilterQuality();
+ }
+ ALWAYS_INLINE SkScalar getStrokeWidth() const {
+ return paint_.getStrokeWidth();
+ }
+ ALWAYS_INLINE void setStrokeWidth(SkScalar width) {
+ paint_.setStrokeWidth(width);
+ }
+ ALWAYS_INLINE SkScalar getStrokeMiter() const {
+ return paint_.getStrokeMiter();
+ }
+ ALWAYS_INLINE void setStrokeMiter(SkScalar miter) {
+ paint_.setStrokeMiter(miter);
+ }
+ enum Cap {
+ kButt_Cap = SkPaint::kButt_Cap, //!< begin/end contours with no extension
+ kRound_Cap = SkPaint::kRound_Cap, //!< begin/end contours with a
+ //! semi-circle extension
+ kSquare_Cap = SkPaint::kSquare_Cap, //!< begin/end contours with a half
+ //! square extension
+ kLast_Cap = kSquare_Cap,
+ kDefault_Cap = kButt_Cap
+ };
+ ALWAYS_INLINE Cap getStrokeCap() const {
+ return static_cast<Cap>(paint_.getStrokeCap());
+ }
+ ALWAYS_INLINE void setStrokeCap(Cap cap) {
+ paint_.setStrokeCap(static_cast<SkPaint::Cap>(cap));
+ }
+ enum Join {
+ kMiter_Join = SkPaint::kMiter_Join,
+ kRound_Join = SkPaint::kRound_Join,
+ kBevel_Join = SkPaint::kBevel_Join,
+ kLast_Join = kBevel_Join,
+ kDefault_Join = kMiter_Join
+ };
+ ALWAYS_INLINE Join getStrokeJoin() const {
+ return static_cast<Join>(paint_.getStrokeJoin());
+ }
+ ALWAYS_INLINE void setStrokeJoin(Join join) {
+ paint_.setStrokeJoin(static_cast<SkPaint::Join>(join));
+ }
+ ALWAYS_INLINE SkTypeface* getTypeface() const { return paint_.getTypeface(); }
+ ALWAYS_INLINE void setTypeface(sk_sp<SkTypeface> typeface) {
+ paint_.setTypeface(std::move(typeface));
+ }
+ ALWAYS_INLINE SkColorFilter* getColorFilter() const {
+ return paint_.getColorFilter();
+ }
+ ALWAYS_INLINE void setColorFilter(sk_sp<SkColorFilter> filter) {
+ paint_.setColorFilter(std::move(filter));
+ }
+ ALWAYS_INLINE SkMaskFilter* getMaskFilter() const {
+ return paint_.getMaskFilter();
+ }
+ ALWAYS_INLINE void setMaskFilter(sk_sp<SkMaskFilter> mask) {
+ paint_.setMaskFilter(std::move(mask));
+ }
+ ALWAYS_INLINE PaintShader* getShader() const { return paint_.getShader(); }
+ ALWAYS_INLINE void setShader(sk_sp<PaintShader> shader) {
+ paint_.setShader(std::move(shader));
+ }
+ ALWAYS_INLINE SkPathEffect* getPathEffect() const {
+ return paint_.getPathEffect();
+ }
+ ALWAYS_INLINE void setPathEffect(sk_sp<SkPathEffect> effect) {
+ paint_.setPathEffect(std::move(effect));
+ }
+ ALWAYS_INLINE bool getFillPath(const SkPath& src,
+ SkPath* dst,
+ const SkRect* cullRect = nullptr,
+ SkScalar resScale = 1) const {
+ return paint_.getFillPath(src, dst, cullRect, resScale);
+ }
+ ALWAYS_INLINE sk_sp<SkImageFilter> refImageFilter() const {
+ return paint_.refImageFilter();
+ }
+ ALWAYS_INLINE SkImageFilter* getImageFilter() const {
+ return paint_.getImageFilter();
+ }
+ void setImageFilter(sk_sp<SkImageFilter> filter) {
+ paint_.setImageFilter(std::move(filter));
+ }
+ ALWAYS_INLINE SkDrawLooper* getDrawLooper() const {
+ return paint_.getDrawLooper();
+ }
+ ALWAYS_INLINE SkDrawLooper* getLooper() const { return paint_.getLooper(); }
+ ALWAYS_INLINE void setLooper(sk_sp<SkDrawLooper> looper) {
+ paint_.setLooper(std::move(looper));
+ }
+ ALWAYS_INLINE bool canComputeFastBounds() const {
+ return paint_.canComputeFastBounds();
+ }
+ ALWAYS_INLINE const SkRect& computeFastBounds(const SkRect& orig,
+ SkRect* storage) const {
+ return paint_.computeFastBounds(orig, storage);
+ }
-inline const SkPaint& ToSkPaint(const PaintFlags& flags) {
- return flags;
+ private:
+ friend const SkPaint& ToSkPaint(const PaintFlags& flags);
+ friend const SkPaint* ToSkPaint(const PaintFlags* flags);
+
+ SkPaint paint_;
+};
+
+ALWAYS_INLINE const SkPaint& ToSkPaint(const PaintFlags& flags) {
+ return flags.paint_;
+}
+
+ALWAYS_INLINE const SkPaint* ToSkPaint(const PaintFlags* flags) {
+ return &flags->paint_;
}
} // namespace cc
diff --git a/chromium/cc/paint/paint_record.h b/chromium/cc/paint/paint_record.h
index a376394cc84..8506606b59f 100644
--- a/chromium/cc/paint/paint_record.h
+++ b/chromium/cc/paint/paint_record.h
@@ -6,20 +6,11 @@
#define CC_PAINT_PAINT_RECORD_H_
#include "third_party/skia/include/core/SkPicture.h"
-#include "third_party/skia/include/utils/SkPictureUtils.h"
namespace cc {
using PaintRecord = SkPicture;
-inline const SkPicture* ToSkPicture(const PaintRecord* record) {
- return record;
-}
-
-inline SkPicture* ToSkPicture(PaintRecord* record) {
- return record;
-}
-
inline sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record) {
return record;
}
diff --git a/chromium/cc/paint/paint_recorder.cc b/chromium/cc/paint/paint_recorder.cc
new file mode 100644
index 00000000000..672f0712725
--- /dev/null
+++ b/chromium/cc/paint/paint_recorder.cc
@@ -0,0 +1,12 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_recorder.h"
+
+namespace cc {
+
+PaintRecorder::PaintRecorder() = default;
+PaintRecorder::~PaintRecorder() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/paint_recorder.h b/chromium/cc/paint/paint_recorder.h
index be866fddf1d..2bbea83b981 100644
--- a/chromium/cc/paint/paint_recorder.h
+++ b/chromium/cc/paint/paint_recorder.h
@@ -5,10 +5,55 @@
#ifndef CC_PAINT_PAINT_RECORDER_H_
#define CC_PAINT_PAINT_RECORDER_H_
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
+#include "cc/paint/paint_canvas.h"
+#include "cc/paint/paint_record.h"
+#include "cc/paint/skia_paint_canvas.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
namespace cc {
-using PaintRecorder = SkPictureRecorder;
-}
+
+class CC_PAINT_EXPORT PaintRecorder {
+ public:
+ PaintRecorder();
+ ~PaintRecorder();
+
+ ALWAYS_INLINE PaintCanvas* beginRecording(const SkRect& bounds) {
+ uint32_t record_flags = 0;
+ canvas_.emplace(recorder_.beginRecording(bounds, nullptr, record_flags));
+ return getRecordingCanvas();
+ }
+
+ ALWAYS_INLINE PaintCanvas* beginRecording(SkScalar width, SkScalar height) {
+ uint32_t record_flags = 0;
+ canvas_.emplace(
+ recorder_.beginRecording(width, height, nullptr, record_flags));
+ return getRecordingCanvas();
+ }
+
+ // Only valid between between and finish recording.
+ ALWAYS_INLINE PaintCanvas* getRecordingCanvas() {
+ return canvas_.has_value() ? &canvas_.value() : nullptr;
+ }
+
+ ALWAYS_INLINE sk_sp<PaintRecord> finishRecordingAsPicture() {
+ sk_sp<SkPicture> picture = recorder_.finishRecordingAsPicture();
+ // Some users (e.g. printing) use the existence of the recording canvas
+ // to know if recording is finished, so reset it here.
+ canvas_.reset();
+ return sk_ref_sp(static_cast<PaintRecord*>(picture.get()));
+ }
+
+ private:
+ SkPictureRecorder recorder_;
+ base::Optional<SkiaPaintCanvas> canvas_;
+
+ DISALLOW_COPY_AND_ASSIGN(PaintRecorder);
+};
+
+} // namespace cc
#endif // CC_PAINT_PAINT_RECORDER_H_
diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h
index f519703eb88..6afdaa650de 100644
--- a/chromium/cc/paint/paint_shader.h
+++ b/chromium/cc/paint/paint_shader.h
@@ -16,14 +16,7 @@ inline sk_sp<PaintShader> WrapSkShader(sk_sp<SkShader> shader) {
return shader;
}
-inline sk_sp<PaintShader> MakePaintShaderImage(const SkImage* image,
- SkShader::TileMode tx,
- SkShader::TileMode ty,
- const SkMatrix* local_matrix) {
- return image->makeShader(tx, ty, local_matrix);
-}
-
-inline sk_sp<PaintShader> MakePaintShaderImage(const sk_sp<SkImage> image,
+inline sk_sp<PaintShader> MakePaintShaderImage(sk_sp<const SkImage> image,
SkShader::TileMode tx,
SkShader::TileMode ty,
const SkMatrix* local_matrix) {
@@ -35,7 +28,8 @@ inline sk_sp<PaintShader> MakePaintShaderRecord(sk_sp<PaintRecord> record,
SkShader::TileMode ty,
const SkMatrix* local_matrix,
const SkRect* tile) {
- return SkShader::MakePictureShader(record, tx, ty, local_matrix, tile);
+ return SkShader::MakePictureShader(ToSkPicture(record), tx, ty, local_matrix,
+ tile);
}
} // namespace cc
diff --git a/chromium/cc/paint/paint_surface.h b/chromium/cc/paint/paint_surface.h
deleted file mode 100644
index 34165bf16ed..00000000000
--- a/chromium/cc/paint/paint_surface.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CC_PAINT_PAINT_SURFACE_H_
-#define CC_PAINT_PAINT_SURFACE_H_
-
-#include "third_party/skia/include/core/SkSurface.h"
-
-namespace cc {
-using PaintSurface = SkSurface;
-}
-
-#endif // CC_PAINT_PAINT_SURFACE_H_
diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc
new file mode 100644
index 00000000000..94b0cfb7a30
--- /dev/null
+++ b/chromium/cc/paint/skia_paint_canvas.cc
@@ -0,0 +1,311 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/paint/paint_canvas.h"
+
+#include "base/memory/ptr_util.h"
+#include "cc/paint/display_item_list.h"
+#include "cc/paint/paint_record.h"
+#include "cc/paint/paint_recorder.h"
+#include "third_party/skia/include/core/SkAnnotation.h"
+#include "third_party/skia/include/core/SkMetaData.h"
+#include "third_party/skia/include/utils/SkNWayCanvas.h"
+
+namespace cc {
+
+SkiaPaintCanvas::SkiaPaintCanvas(SkCanvas* canvas) : canvas_(canvas) {}
+
+SkiaPaintCanvas::SkiaPaintCanvas(const SkBitmap& bitmap)
+ : canvas_(new SkCanvas(bitmap)), owned_(canvas_) {}
+
+SkiaPaintCanvas::SkiaPaintCanvas(const SkBitmap& bitmap,
+ const SkSurfaceProps& props)
+ : canvas_(new SkCanvas(bitmap, props)), owned_(canvas_) {}
+
+SkiaPaintCanvas::SkiaPaintCanvas(SkiaPaintCanvas&& other) = default;
+SkiaPaintCanvas::~SkiaPaintCanvas() = default;
+
+SkMetaData& SkiaPaintCanvas::getMetaData() {
+ return canvas_->getMetaData();
+}
+
+SkImageInfo SkiaPaintCanvas::imageInfo() const {
+ return canvas_->imageInfo();
+}
+
+void SkiaPaintCanvas::flush() {
+ canvas_->flush();
+}
+
+SkISize SkiaPaintCanvas::getBaseLayerSize() const {
+ return canvas_->getBaseLayerSize();
+}
+
+bool SkiaPaintCanvas::writePixels(const SkImageInfo& info,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y) {
+ return canvas_->writePixels(info, pixels, row_bytes, x, y);
+}
+
+int SkiaPaintCanvas::save() {
+ return canvas_->save();
+}
+
+int SkiaPaintCanvas::saveLayer(const SkRect* bounds, const PaintFlags* flags) {
+ return canvas_->saveLayer(bounds, ToSkPaint(flags));
+}
+
+int SkiaPaintCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) {
+ return canvas_->saveLayerAlpha(bounds, alpha);
+}
+
+void SkiaPaintCanvas::restore() {
+ canvas_->restore();
+}
+
+int SkiaPaintCanvas::getSaveCount() const {
+ return canvas_->getSaveCount();
+}
+
+void SkiaPaintCanvas::restoreToCount(int save_count) {
+ canvas_->restoreToCount(save_count);
+}
+
+void SkiaPaintCanvas::translate(SkScalar dx, SkScalar dy) {
+ canvas_->translate(dx, dy);
+}
+
+void SkiaPaintCanvas::scale(SkScalar sx, SkScalar sy) {
+ canvas_->scale(sx, sy);
+}
+
+void SkiaPaintCanvas::rotate(SkScalar degrees) {
+ canvas_->rotate(degrees);
+}
+
+void SkiaPaintCanvas::concat(const SkMatrix& matrix) {
+ canvas_->concat(matrix);
+}
+
+void SkiaPaintCanvas::setMatrix(const SkMatrix& matrix) {
+ canvas_->setMatrix(matrix);
+}
+
+void SkiaPaintCanvas::clipRect(const SkRect& rect,
+ SkClipOp op,
+ bool do_anti_alias) {
+ canvas_->clipRect(rect, op, do_anti_alias);
+}
+
+void SkiaPaintCanvas::clipRRect(const SkRRect& rrect,
+ SkClipOp op,
+ bool do_anti_alias) {
+ canvas_->clipRRect(rrect, op, do_anti_alias);
+}
+
+void SkiaPaintCanvas::clipPath(const SkPath& path,
+ SkClipOp op,
+ bool do_anti_alias) {
+ canvas_->clipPath(path, op, do_anti_alias);
+}
+
+bool SkiaPaintCanvas::quickReject(const SkRect& rect) const {
+ return canvas_->quickReject(rect);
+}
+
+bool SkiaPaintCanvas::quickReject(const SkPath& path) const {
+ return canvas_->quickReject(path);
+}
+
+SkRect SkiaPaintCanvas::getLocalClipBounds() const {
+ return canvas_->getLocalClipBounds();
+}
+
+bool SkiaPaintCanvas::getLocalClipBounds(SkRect* bounds) const {
+ return canvas_->getLocalClipBounds(bounds);
+}
+
+SkIRect SkiaPaintCanvas::getDeviceClipBounds() const {
+ return canvas_->getDeviceClipBounds();
+}
+
+bool SkiaPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const {
+ return canvas_->getDeviceClipBounds(bounds);
+}
+
+void SkiaPaintCanvas::drawColor(SkColor color, SkBlendMode mode) {
+ canvas_->drawColor(color, mode);
+}
+
+void SkiaPaintCanvas::clear(SkColor color) {
+ canvas_->clear(color);
+}
+
+void SkiaPaintCanvas::drawLine(SkScalar x0,
+ SkScalar y0,
+ SkScalar x1,
+ SkScalar y1,
+ const PaintFlags& flags) {
+ SkiaPaintCanvas::canvas_->drawLine(x0, y0, x1, y1, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) {
+ canvas_->drawRect(rect, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawIRect(const SkIRect& rect, const PaintFlags& flags) {
+ canvas_->drawIRect(rect, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) {
+ canvas_->drawOval(oval, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawRRect(const SkRRect& rrect, const PaintFlags& flags) {
+ canvas_->drawRRect(rrect, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawDRRect(const SkRRect& outer,
+ const SkRRect& inner,
+ const PaintFlags& flags) {
+ canvas_->drawDRRect(outer, inner, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawCircle(SkScalar cx,
+ SkScalar cy,
+ SkScalar radius,
+ const PaintFlags& flags) {
+ canvas_->drawCircle(cx, cy, radius, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawArc(const SkRect& oval,
+ SkScalar start_angle,
+ SkScalar sweep_angle,
+ bool use_center,
+ const PaintFlags& flags) {
+ canvas_->drawArc(oval, start_angle, sweep_angle, use_center,
+ ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawRoundRect(const SkRect& rect,
+ SkScalar rx,
+ SkScalar ry,
+ const PaintFlags& flags) {
+ canvas_->drawRoundRect(rect, rx, ry, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) {
+ canvas_->drawPath(path, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawImage(sk_sp<const SkImage> image,
+ SkScalar left,
+ SkScalar top,
+ const PaintFlags* flags) {
+ canvas_->drawImage(image.get(), left, top, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawImageRect(sk_sp<const SkImage> image,
+ const SkRect& src,
+ const SkRect& dst,
+ const PaintFlags* flags,
+ SrcRectConstraint constraint) {
+ canvas_->drawImageRect(image.get(), src, dst, ToSkPaint(flags),
+ static_cast<SkCanvas::SrcRectConstraint>(constraint));
+}
+
+void SkiaPaintCanvas::drawBitmap(const SkBitmap& bitmap,
+ SkScalar left,
+ SkScalar top,
+ const PaintFlags* flags) {
+ canvas_->drawBitmap(bitmap, left, top, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawText(const void* text,
+ size_t byte_length,
+ SkScalar x,
+ SkScalar y,
+ const PaintFlags& flags) {
+ canvas_->drawText(text, byte_length, x, y, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawPosText(const void* text,
+ size_t byte_length,
+ const SkPoint pos[],
+ const PaintFlags& flags) {
+ canvas_->drawPosText(text, byte_length, pos, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob,
+ SkScalar x,
+ SkScalar y,
+ const PaintFlags& flags) {
+ canvas_->drawTextBlob(blob.get(), x, y, ToSkPaint(flags));
+}
+
+void SkiaPaintCanvas::drawDisplayItemList(
+ scoped_refptr<DisplayItemList> display_item_list) {
+ display_item_list->Raster(canvas_, nullptr);
+}
+
+void SkiaPaintCanvas::drawPicture(sk_sp<const PaintRecord> record) {
+ record->playback(canvas_);
+}
+
+bool SkiaPaintCanvas::isClipEmpty() const {
+ return canvas_->isClipEmpty();
+}
+
+bool SkiaPaintCanvas::isClipRect() const {
+ return canvas_->isClipRect();
+}
+
+const SkMatrix& SkiaPaintCanvas::getTotalMatrix() const {
+ return canvas_->getTotalMatrix();
+}
+
+void SkiaPaintCanvas::temporary_internal_describeTopLayer(
+ SkMatrix* matrix,
+ SkIRect* clip_bounds) {
+ return canvas_->temporary_internal_describeTopLayer(matrix, clip_bounds);
+}
+
+void SkiaPaintCanvas::PlaybackPaintRecord(sk_sp<const PaintRecord> record) {
+ record->playback(canvas_);
+}
+
+bool SkiaPaintCanvas::ToPixmap(SkPixmap* output) {
+ SkImageInfo info;
+ size_t row_bytes;
+ void* pixels = canvas_->accessTopLayerPixels(&info, &row_bytes);
+ if (!pixels) {
+ output->reset();
+ return false;
+ }
+
+ output->reset(info, pixels, row_bytes);
+ return true;
+}
+
+void SkiaPaintCanvas::Annotate(AnnotationType type,
+ const SkRect& rect,
+ sk_sp<SkData> data) {
+ switch (type) {
+ case AnnotationType::URL:
+ SkAnnotateRectWithURL(canvas_, rect, data.get());
+ break;
+ case AnnotationType::LINK_TO_DESTINATION:
+ SkAnnotateLinkToDestination(canvas_, rect, data.get());
+ break;
+ case AnnotationType::NAMED_DESTINATION: {
+ SkPoint point = SkPoint::Make(rect.x(), rect.y());
+ SkAnnotateNamedDestination(canvas_, point, data.get());
+ break;
+ }
+ }
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h
new file mode 100644
index 00000000000..669ca9b3e0b
--- /dev/null
+++ b/chromium/cc/paint/skia_paint_canvas.h
@@ -0,0 +1,165 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CC_PAINT_SKIA_PAINT_CANVAS_H_
+#define CC_PAINT_SKIA_PAINT_CANVAS_H_
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "cc/paint/paint_canvas.h"
+#include "cc/paint/paint_flags.h"
+#include "cc/paint/paint_record.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+
+namespace cc {
+
+class PaintFlags;
+
+// A PaintCanvas derived class that passes PaintCanvas APIs through to
+// an SkCanvas. This is more efficient than recording to a PaintRecord
+// and then playing back to an SkCanvas.
+class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas {
+ public:
+ explicit SkiaPaintCanvas(SkCanvas* canvas);
+ explicit SkiaPaintCanvas(const SkBitmap& bitmap);
+ explicit SkiaPaintCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props);
+ explicit SkiaPaintCanvas(SkiaPaintCanvas&& other);
+ ~SkiaPaintCanvas() override;
+
+ SkiaPaintCanvas& operator=(SkiaPaintCanvas&& other) = default;
+
+ SkMetaData& getMetaData() override;
+ SkImageInfo imageInfo() const override;
+
+ void flush() override;
+
+ SkISize getBaseLayerSize() const override;
+ bool writePixels(const SkImageInfo& info,
+ const void* pixels,
+ size_t row_bytes,
+ int x,
+ int y) override;
+ int save() override;
+ int saveLayer(const SkRect* bounds, const PaintFlags* flags) override;
+ int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) override;
+
+ void restore() override;
+ int getSaveCount() const override;
+ void restoreToCount(int save_count) override;
+ void translate(SkScalar dx, SkScalar dy) override;
+ void scale(SkScalar sx, SkScalar sy) override;
+ void rotate(SkScalar degrees) override;
+ void concat(const SkMatrix& matrix) override;
+ void setMatrix(const SkMatrix& matrix) override;
+
+ void clipRect(const SkRect& rect, SkClipOp op, bool do_anti_alias) override;
+ void clipRRect(const SkRRect& rrect,
+ SkClipOp op,
+ bool do_anti_alias) override;
+ void clipPath(const SkPath& path, SkClipOp op, bool do_anti_alias) override;
+ bool quickReject(const SkRect& rect) const override;
+ bool quickReject(const SkPath& path) const override;
+ SkRect getLocalClipBounds() const override;
+ bool getLocalClipBounds(SkRect* bounds) const override;
+ SkIRect getDeviceClipBounds() const override;
+ bool getDeviceClipBounds(SkIRect* bounds) const override;
+ void drawColor(SkColor color, SkBlendMode mode) override;
+ void clear(SkColor color) override;
+
+ void drawLine(SkScalar x0,
+ SkScalar y0,
+ SkScalar x1,
+ SkScalar y1,
+ const PaintFlags& flags) override;
+ void drawRect(const SkRect& rect, const PaintFlags& flags) override;
+ void drawIRect(const SkIRect& rect, const PaintFlags& flags) override;
+ void drawOval(const SkRect& oval, const PaintFlags& flags) override;
+ void drawRRect(const SkRRect& rrect, const PaintFlags& flags) override;
+ void drawDRRect(const SkRRect& outer,
+ const SkRRect& inner,
+ const PaintFlags& flags) override;
+ void drawCircle(SkScalar cx,
+ SkScalar cy,
+ SkScalar radius,
+ const PaintFlags& flags) override;
+ void drawArc(const SkRect& oval,
+ SkScalar start_angle,
+ SkScalar sweep_angle,
+ bool use_center,
+ const PaintFlags& flags) override;
+ void drawRoundRect(const SkRect& rect,
+ SkScalar rx,
+ SkScalar ry,
+ const PaintFlags& flags) override;
+ void drawPath(const SkPath& path, const PaintFlags& flags) override;
+ void drawImage(sk_sp<const SkImage> image,
+ SkScalar left,
+ SkScalar top,
+ const PaintFlags* flags) override;
+ void drawImageRect(sk_sp<const SkImage> image,
+ const SkRect& src,
+ const SkRect& dst,
+ const PaintFlags* flags,
+ SrcRectConstraint constraint) override;
+ void drawBitmap(const SkBitmap& bitmap,
+ SkScalar left,
+ SkScalar top,
+ const PaintFlags* flags) override;
+
+ void drawText(const void* text,
+ size_t byte_length,
+ SkScalar x,
+ SkScalar y,
+ const PaintFlags& flags) override;
+ void drawPosText(const void* text,
+ size_t byte_length,
+ const SkPoint pos[],
+ const PaintFlags& flags) override;
+ void drawTextBlob(sk_sp<SkTextBlob> blob,
+ SkScalar x,
+ SkScalar y,
+ const PaintFlags& flags) override;
+
+ void drawDisplayItemList(
+ scoped_refptr<DisplayItemList> display_item_list) override;
+
+ void drawPicture(sk_sp<const PaintRecord> record) override;
+
+ bool isClipEmpty() const override;
+ bool isClipRect() const override;
+ const SkMatrix& getTotalMatrix() const override;
+
+ void temporary_internal_describeTopLayer(SkMatrix* matrix,
+ SkIRect* clip_bounds) override;
+
+ bool ToPixmap(SkPixmap* output) override;
+ void Annotate(AnnotationType type,
+ const SkRect& rect,
+ sk_sp<SkData> data) override;
+
+ void PlaybackPaintRecord(sk_sp<const PaintRecord> record) override;
+
+ // Don't shadow non-virtual helper functions.
+ using PaintCanvas::clipRect;
+ using PaintCanvas::clipRRect;
+ using PaintCanvas::clipPath;
+ using PaintCanvas::drawBitmap;
+ using PaintCanvas::drawColor;
+ using PaintCanvas::drawImage;
+ using PaintCanvas::drawPicture;
+
+ private:
+ SkCanvas* canvas_;
+ std::unique_ptr<SkCanvas> owned_;
+
+ DISALLOW_COPY_AND_ASSIGN(SkiaPaintCanvas);
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_SKIA_PAINT_CANVAS_H_
diff --git a/chromium/cc/paint/transform_display_item.cc b/chromium/cc/paint/transform_display_item.cc
new file mode 100644
index 00000000000..6cc3e7f2d1e
--- /dev/null
+++ b/chromium/cc/paint/transform_display_item.cc
@@ -0,0 +1,20 @@
+// 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/paint/transform_display_item.h"
+
+namespace cc {
+
+TransformDisplayItem::TransformDisplayItem(const gfx::Transform& transform)
+
+ : DisplayItem(TRANSFORM), transform(transform) {}
+
+TransformDisplayItem::~TransformDisplayItem() = default;
+
+EndTransformDisplayItem::EndTransformDisplayItem()
+ : DisplayItem(END_TRANSFORM) {}
+
+EndTransformDisplayItem::~EndTransformDisplayItem() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/transform_display_item.h b/chromium/cc/paint/transform_display_item.h
new file mode 100644
index 00000000000..a75db2a4d87
--- /dev/null
+++ b/chromium/cc/paint/transform_display_item.h
@@ -0,0 +1,37 @@
+// 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_PAINT_TRANSFORM_DISPLAY_ITEM_H_
+#define CC_PAINT_TRANSFORM_DISPLAY_ITEM_H_
+
+#include <stddef.h>
+
+#include "cc/paint/display_item.h"
+#include "cc/paint/paint_export.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT TransformDisplayItem : public DisplayItem {
+ public:
+ explicit TransformDisplayItem(const gfx::Transform& transform);
+ ~TransformDisplayItem() override;
+
+ size_t ExternalMemoryUsage() const { return 0; }
+ int ApproximateOpCount() const { return 1; }
+
+ const gfx::Transform transform;
+};
+
+class CC_PAINT_EXPORT EndTransformDisplayItem : public DisplayItem {
+ public:
+ EndTransformDisplayItem();
+ ~EndTransformDisplayItem() override;
+
+ int ApproximateOpCount() const { return 0; }
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_TRANSFORM_DISPLAY_ITEM_H_