diff options
Diffstat (limited to 'chromium/cc/paint')
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_ |