summaryrefslogtreecommitdiffstats
path: root/chromium/cc/paint
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-11-20 10:33:36 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-11-22 11:45:12 +0000
commitbe59a35641616a4cf23c4a13fa0632624b021c1b (patch)
tree9da183258bdf9cc413f7562079d25ace6955467f /chromium/cc/paint
parentd702e4b6a64574e97fc7df8fe3238cde70242080 (diff)
BASELINE: Update Chromium to 62.0.3202.101
Change-Id: I2d5eca8117600df6d331f6166ab24d943d9814ac Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/cc/paint')
-rw-r--r--chromium/cc/paint/BUILD.gn23
-rw-r--r--chromium/cc/paint/decoded_draw_image.cc33
-rw-r--r--chromium/cc/paint/decoded_draw_image.h54
-rw-r--r--chromium/cc/paint/discardable_image_map.cc150
-rw-r--r--chromium/cc/paint/discardable_image_map.h4
-rw-r--r--chromium/cc/paint/discardable_image_map_unittest.cc107
-rw-r--r--chromium/cc/paint/display_item_list.cc60
-rw-r--r--chromium/cc/paint/display_item_list.h52
-rw-r--r--chromium/cc/paint/display_item_list_unittest.cc363
-rw-r--r--chromium/cc/paint/draw_image.cc27
-rw-r--r--chromium/cc/paint/draw_image.h34
-rw-r--r--chromium/cc/paint/frame_metadata.h28
-rw-r--r--chromium/cc/paint/image_id.h5
-rw-r--r--chromium/cc/paint/image_provider.cc44
-rw-r--r--chromium/cc/paint/image_provider.h57
-rw-r--r--chromium/cc/paint/paint_canvas.h18
-rw-r--r--chromium/cc/paint/paint_flags.cc31
-rw-r--r--chromium/cc/paint/paint_flags.h73
-rw-r--r--chromium/cc/paint/paint_image.cc211
-rw-r--r--chromium/cc/paint/paint_image.h167
-rw-r--r--chromium/cc/paint/paint_image_builder.cc46
-rw-r--r--chromium/cc/paint/paint_image_builder.h91
-rw-r--r--chromium/cc/paint/paint_image_generator.cc36
-rw-r--r--chromium/cc/paint/paint_image_generator.h90
-rw-r--r--chromium/cc/paint/paint_image_unittest.cc34
-rw-r--r--chromium/cc/paint/paint_op_buffer.cc1173
-rw-r--r--chromium/cc/paint/paint_op_buffer.h808
-rw-r--r--chromium/cc/paint/paint_op_buffer_fuzzer.cc59
-rw-r--r--chromium/cc/paint/paint_op_buffer_unittest.cc1563
-rw-r--r--chromium/cc/paint/paint_op_reader.cc204
-rw-r--r--chromium/cc/paint/paint_op_reader.h19
-rw-r--r--chromium/cc/paint/paint_op_writer.cc98
-rw-r--r--chromium/cc/paint/paint_op_writer.h11
-rw-r--r--chromium/cc/paint/paint_recorder.cc19
-rw-r--r--chromium/cc/paint/paint_recorder.h5
-rw-r--r--chromium/cc/paint/paint_shader.cc114
-rw-r--r--chromium/cc/paint/paint_shader.h70
-rw-r--r--chromium/cc/paint/record_paint_canvas.cc105
-rw-r--r--chromium/cc/paint/record_paint_canvas.h24
-rw-r--r--chromium/cc/paint/skia_paint_canvas.cc39
-rw-r--r--chromium/cc/paint/skia_paint_canvas.h18
-rw-r--r--chromium/cc/paint/skia_paint_image_generator.cc44
-rw-r--r--chromium/cc/paint/skia_paint_image_generator.h46
-rw-r--r--chromium/cc/paint/solid_color_analyzer.cc40
-rw-r--r--chromium/cc/paint/solid_color_analyzer.h2
-rw-r--r--chromium/cc/paint/solid_color_analyzer_unittest.cc52
46 files changed, 4259 insertions, 2092 deletions
diff --git a/chromium/cc/paint/BUILD.gn b/chromium/cc/paint/BUILD.gn
index 591bb6a31f5..a3ef3d029c8 100644
--- a/chromium/cc/paint/BUILD.gn
+++ b/chromium/cc/paint/BUILD.gn
@@ -3,10 +3,13 @@
# found in the LICENSE file.
import("//cc/cc.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
cc_component("paint") {
output_name = "cc_paint"
sources = [
+ "decoded_draw_image.cc",
+ "decoded_draw_image.h",
"discardable_image_map.cc",
"discardable_image_map.h",
"display_item_list.cc",
@@ -14,6 +17,8 @@ cc_component("paint") {
"draw_image.cc",
"draw_image.h",
"image_id.h",
+ "image_provider.cc",
+ "image_provider.h",
"paint_canvas.cc",
"paint_canvas.h",
"paint_export.h",
@@ -21,6 +26,10 @@ cc_component("paint") {
"paint_flags.h",
"paint_image.cc",
"paint_image.h",
+ "paint_image_builder.cc",
+ "paint_image_builder.h",
+ "paint_image_generator.cc",
+ "paint_image_generator.h",
"paint_op_buffer.cc",
"paint_op_buffer.h",
"paint_op_reader.cc",
@@ -37,6 +46,8 @@ cc_component("paint") {
"record_paint_canvas.h",
"skia_paint_canvas.cc",
"skia_paint_canvas.h",
+ "skia_paint_image_generator.cc",
+ "skia_paint_image_generator.h",
"solid_color_analyzer.cc",
"solid_color_analyzer.h",
]
@@ -59,3 +70,15 @@ cc_component("paint") {
"//base",
]
}
+
+fuzzer_test("paint_op_buffer_fuzzer") {
+ sources = [
+ "paint_op_buffer_fuzzer.cc",
+ ]
+
+ libfuzzer_options = [ "max_len=4096" ]
+
+ deps = [
+ "//cc/paint",
+ ]
+}
diff --git a/chromium/cc/paint/decoded_draw_image.cc b/chromium/cc/paint/decoded_draw_image.cc
new file mode 100644
index 00000000000..70bbfce7182
--- /dev/null
+++ b/chromium/cc/paint/decoded_draw_image.cc
@@ -0,0 +1,33 @@
+// 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/decoded_draw_image.h"
+
+namespace cc {
+
+DecodedDrawImage::DecodedDrawImage(sk_sp<const SkImage> image,
+ const SkSize& src_rect_offset,
+ const SkSize& scale_adjustment,
+ SkFilterQuality filter_quality)
+ : image_(std::move(image)),
+ src_rect_offset_(src_rect_offset),
+ scale_adjustment_(scale_adjustment),
+ filter_quality_(filter_quality),
+ at_raster_decode_(false) {}
+
+DecodedDrawImage::DecodedDrawImage()
+ : DecodedDrawImage(nullptr, kNone_SkFilterQuality) {}
+
+DecodedDrawImage::DecodedDrawImage(sk_sp<const SkImage> image,
+ SkFilterQuality filter_quality)
+ : DecodedDrawImage(std::move(image),
+ SkSize::Make(0.f, 0.f),
+ SkSize::Make(1.f, 1.f),
+ filter_quality) {}
+
+DecodedDrawImage::DecodedDrawImage(const DecodedDrawImage& other) = default;
+
+DecodedDrawImage::~DecodedDrawImage() = default;
+
+} // namespace cc
diff --git a/chromium/cc/paint/decoded_draw_image.h b/chromium/cc/paint/decoded_draw_image.h
new file mode 100644
index 00000000000..3e10dc9779b
--- /dev/null
+++ b/chromium/cc/paint/decoded_draw_image.h
@@ -0,0 +1,54 @@
+// 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_DECODED_DRAW_IMAGE_H_
+#define CC_PAINT_DECODED_DRAW_IMAGE_H_
+
+#include <cfloat>
+#include <cmath>
+
+#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/SkRefCnt.h"
+#include "third_party/skia/include/core/SkSize.h"
+
+namespace cc {
+
+class CC_PAINT_EXPORT DecodedDrawImage {
+ public:
+ DecodedDrawImage(sk_sp<const SkImage> image,
+ const SkSize& src_rect_offset,
+ const SkSize& scale_adjustment,
+ SkFilterQuality filter_quality);
+ DecodedDrawImage(sk_sp<const SkImage> image, SkFilterQuality filter_quality);
+ DecodedDrawImage(const DecodedDrawImage& other);
+ DecodedDrawImage();
+ ~DecodedDrawImage();
+
+ const sk_sp<const SkImage>& image() const { return image_; }
+ const SkSize& src_rect_offset() const { return src_rect_offset_; }
+ const SkSize& scale_adjustment() const { return scale_adjustment_; }
+ SkFilterQuality filter_quality() const { return filter_quality_; }
+ bool is_scale_adjustment_identity() const {
+ return std::abs(scale_adjustment_.width() - 1.f) < FLT_EPSILON &&
+ std::abs(scale_adjustment_.height() - 1.f) < FLT_EPSILON;
+ }
+
+ void set_at_raster_decode(bool at_raster_decode) {
+ at_raster_decode_ = at_raster_decode;
+ }
+ bool is_at_raster_decode() const { return at_raster_decode_; }
+
+ private:
+ sk_sp<const SkImage> image_;
+ SkSize src_rect_offset_;
+ SkSize scale_adjustment_;
+ SkFilterQuality filter_quality_;
+ bool at_raster_decode_;
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_DECODED_DRAW_IMAGE_H_
diff --git a/chromium/cc/paint/discardable_image_map.cc b/chromium/cc/paint/discardable_image_map.cc
index ead8b88947e..430c5cf6651 100644
--- a/chromium/cc/paint/discardable_image_map.cc
+++ b/chromium/cc/paint/discardable_image_map.cc
@@ -82,80 +82,40 @@ class DiscardableImageGenerator {
if (!buffer->HasDiscardableImages())
return;
- SkMatrix original = canvas_.getTotalMatrix();
+ PlaybackParams params(nullptr, canvas_.getTotalMatrix());
canvas_.save();
// TODO(khushalsagar): Optimize out save/restore blocks if there are no
// images in the draw ops between them.
for (auto* op : PaintOpBuffer::Iterator(buffer)) {
if (op->IsDrawOp()) {
- switch (op->GetType()) {
- case PaintOpType::DrawArc: {
- auto* arc_op = static_cast<DrawArcOp*>(op);
- AddImageFromFlags(arc_op->oval, arc_op->flags);
- } break;
- case PaintOpType::DrawCircle: {
- auto* circle_op = static_cast<DrawCircleOp*>(op);
- SkRect rect =
- SkRect::MakeXYWH(circle_op->cx - circle_op->radius,
- circle_op->cy - circle_op->radius,
- 2 * circle_op->radius, 2 * circle_op->radius);
- AddImageFromFlags(rect, circle_op->flags);
- } break;
- case PaintOpType::DrawImage: {
- auto* image_op = static_cast<DrawImageOp*>(op);
- const SkImage* sk_image = image_op->image.sk_image().get();
- AddImage(image_op->image,
- SkRect::MakeIWH(sk_image->width(), sk_image->height()),
- SkRect::MakeXYWH(image_op->left, image_op->top,
- sk_image->width(), sk_image->height()),
- nullptr, image_op->flags);
- } break;
- case PaintOpType::DrawImageRect: {
- auto* image_rect_op = static_cast<DrawImageRectOp*>(op);
- SkMatrix matrix;
- matrix.setRectToRect(image_rect_op->src, image_rect_op->dst,
- SkMatrix::kFill_ScaleToFit);
- AddImage(image_rect_op->image, image_rect_op->src,
- image_rect_op->dst, &matrix, image_rect_op->flags);
- } break;
- case PaintOpType::DrawIRect: {
- auto* rect_op = static_cast<DrawIRectOp*>(op);
- AddImageFromFlags(SkRect::Make(rect_op->rect), rect_op->flags);
- } break;
- case PaintOpType::DrawOval: {
- auto* oval_op = static_cast<DrawOvalOp*>(op);
- AddImageFromFlags(oval_op->oval, oval_op->flags);
- } break;
- case PaintOpType::DrawPath: {
- auto* path_op = static_cast<DrawPathOp*>(op);
- AddImageFromFlags(path_op->path.getBounds(), path_op->flags);
- } break;
- case PaintOpType::DrawRecord: {
- auto* record_op = static_cast<DrawRecordOp*>(op);
- GatherDiscardableImages(record_op->record.get());
- } break;
- case PaintOpType::DrawRect: {
- auto* rect_op = static_cast<DrawRectOp*>(op);
- AddImageFromFlags(rect_op->rect, rect_op->flags);
- } break;
- case PaintOpType::DrawRRect: {
- auto* rect_op = static_cast<DrawRRectOp*>(op);
- AddImageFromFlags(rect_op->rrect.rect(), rect_op->flags);
- } break;
- // TODO(khushalsagar): Check if we should be querying images from any
- // of the following ops.
- case PaintOpType::DrawPosText:
- case PaintOpType::DrawLine:
- case PaintOpType::DrawDRRect:
- case PaintOpType::DrawText:
- case PaintOpType::DrawTextBlob:
- case PaintOpType::DrawColor:
- break;
- default:
- NOTREACHED();
+ SkRect op_rect;
+ if (op->IsPaintOpWithFlags() && PaintOp::GetBounds(op, &op_rect)) {
+ AddImageFromFlags(op_rect,
+ static_cast<const PaintOpWithFlags*>(op)->flags);
+ }
+
+ PaintOpType op_type = static_cast<PaintOpType>(op->type);
+ if (op_type == PaintOpType::DrawImage) {
+ auto* image_op = static_cast<DrawImageOp*>(op);
+ auto* sk_image = image_op->image.GetSkImage().get();
+ AddImage(image_op->image,
+ SkRect::MakeIWH(sk_image->width(), sk_image->height()),
+ SkRect::MakeXYWH(image_op->left, image_op->top,
+ sk_image->width(), sk_image->height()),
+ nullptr, image_op->flags);
+ } else if (op_type == PaintOpType::DrawImageRect) {
+ auto* image_rect_op = static_cast<DrawImageRectOp*>(op);
+ SkMatrix matrix;
+ matrix.setRectToRect(image_rect_op->src, image_rect_op->dst,
+ SkMatrix::kFill_ScaleToFit);
+ AddImage(image_rect_op->image, image_rect_op->src, image_rect_op->dst,
+ &matrix, image_rect_op->flags);
+ } else if (op_type == PaintOpType::DrawRecord) {
+ GatherDiscardableImages(
+ static_cast<const DrawRecordOp*>(op)->record.get());
}
} else {
- op->Raster(&canvas_, original);
+ op->Raster(&canvas_, params);
}
}
canvas_.restore();
@@ -189,28 +149,15 @@ class DiscardableImageGenerator {
private:
void AddImageFromFlags(const SkRect& rect, const PaintFlags& flags) {
- SkShader* shader = flags.getSkShader();
- if (shader) {
- SkMatrix matrix;
- SkShader::TileMode xy[2];
- SkImage* image = shader->isAImage(&matrix, xy);
- if (image) {
- // We currently use the wrong id for images that come from shaders. We
- // don't know what the stable id is, but since the completion and
- // animation states are both unknown, this value doesn't matter as it
- // won't be used in checker imaging anyway. Keep this value the same to
- // avoid id churn.
- // TODO(vmpstr): Remove this when we can add paint images into shaders
- // directly.
- PaintImage paint_image(PaintImage::kUnknownStableId, sk_ref_sp(image),
- PaintImage::AnimationType::UNKNOWN,
- PaintImage::CompletionState::UNKNOWN);
- // TODO(ericrk): Handle cases where we only need a sub-rect from the
- // image. crbug.com/671821
- AddImage(std::move(paint_image), SkRect::MakeFromIRect(image->bounds()),
- rect, &matrix, flags);
- }
- }
+ if (!flags.HasShader() ||
+ flags.getShader()->shader_type() != PaintShader::Type::kImage)
+ return;
+
+ const PaintImage& paint_image = flags.getShader()->paint_image();
+ SkMatrix local_matrix = flags.getShader()->GetLocalMatrix();
+ AddImage(paint_image,
+ SkRect::MakeWH(paint_image.width(), paint_image.height()), rect,
+ &local_matrix, flags);
}
void AddImage(PaintImage paint_image,
@@ -218,7 +165,7 @@ class DiscardableImageGenerator {
const SkRect& rect,
const SkMatrix* local_matrix,
const PaintFlags& flags) {
- if (!paint_image.sk_image()->isLazyGenerated())
+ if (!paint_image.IsLazyGenerated())
return;
const SkRect& clip_rect = SkRect::Make(canvas_.getDeviceClipBounds());
@@ -257,7 +204,7 @@ class DiscardableImageGenerator {
// Make a note if any image was originally specified in a non-sRGB color
// space.
- SkColorSpace* source_color_space = paint_image.sk_image()->colorSpace();
+ SkColorSpace* source_color_space = paint_image.color_space();
color_stats_total_pixel_count_ += image_rect.size().GetCheckedArea();
color_stats_total_image_count_++;
if (!source_color_space || source_color_space->isSRGB()) {
@@ -265,18 +212,13 @@ class DiscardableImageGenerator {
color_stats_srgb_image_count_++;
}
- // The true target color space will be assigned when it is known, in
- // GetDiscardableImagesInRect.
- gfx::ColorSpace target_color_space;
-
SkMatrix matrix = ctm;
if (local_matrix)
matrix.postConcat(*local_matrix);
image_id_to_rect_[paint_image.stable_id()].Union(image_rect);
image_set_.emplace_back(
- DrawImage(std::move(paint_image), src_irect, filter_quality, matrix,
- target_color_space),
+ DrawImage(std::move(paint_image), src_irect, filter_quality, matrix),
image_rect);
}
@@ -322,18 +264,8 @@ void DiscardableImageMap::Generate(const PaintOpBuffer* paint_op_buffer,
void DiscardableImageMap::GetDiscardableImagesInRect(
const gfx::Rect& rect,
- float contents_scale,
- const gfx::ColorSpace& target_color_space,
- std::vector<DrawImage>* images) const {
- *images = images_rtree_.Search(rect);
- // TODO(vmpstr): Remove the second pass and do this in TileManager.
- // crbug.com/727772.
- std::transform(
- images->begin(), images->end(), images->begin(),
- [&contents_scale, &target_color_space](const DrawImage& image) {
- return image.ApplyScale(contents_scale)
- .ApplyTargetColorSpace(target_color_space);
- });
+ std::vector<const DrawImage*>* images) const {
+ *images = images_rtree_.SearchRefs(rect);
}
gfx::Rect DiscardableImageMap::GetRectForImage(PaintImage::Id image_id) const {
diff --git a/chromium/cc/paint/discardable_image_map.h b/chromium/cc/paint/discardable_image_map.h
index 4b5c53fa835..1da2a0c06cc 100644
--- a/chromium/cc/paint/discardable_image_map.h
+++ b/chromium/cc/paint/discardable_image_map.h
@@ -34,9 +34,7 @@ class CC_PAINT_EXPORT DiscardableImageMap {
bool empty() const { return image_id_to_rect_.empty(); }
void GetDiscardableImagesInRect(const gfx::Rect& rect,
- float contents_scale,
- const gfx::ColorSpace& target_color_space,
- std::vector<DrawImage>* images) const;
+ std::vector<const DrawImage*>* images) const;
gfx::Rect GetRectForImage(PaintImage::Id image_id) const;
bool all_images_are_srgb() const { return all_images_are_srgb_; }
diff --git a/chromium/cc/paint/discardable_image_map_unittest.cc b/chromium/cc/paint/discardable_image_map_unittest.cc
index 89da8c9eccd..a00aee5205b 100644
--- a/chromium/cc/paint/discardable_image_map_unittest.cc
+++ b/chromium/cc/paint/discardable_image_map_unittest.cc
@@ -29,21 +29,6 @@
namespace cc {
namespace {
-PaintImage CreateDiscardablePaintImageWithColorSpace(
- const gfx::Size& size,
- const gfx::ColorSpace& color_space) {
- sk_sp<SkImage> sk_image = CreateDiscardableImage(size);
- if (color_space.IsValid()) {
- sk_image = sk_image->makeColorSpace(color_space.ToSkColorSpace(),
- SkTransferFunctionBehavior::kIgnore);
- }
- return PaintImage(PaintImage::GetNextId(), sk_image);
-}
-
-PaintImage CreateDiscardablePaintImage(const gfx::Size& size) {
- return CreateDiscardablePaintImageWithColorSpace(size, gfx::ColorSpace());
-}
-
struct PositionScaleDrawImage {
PositionScaleDrawImage(const PaintImage& image,
const gfx::Rect& image_rect,
@@ -68,12 +53,14 @@ class DiscardableImageMapTest : public testing::Test {
std::vector<PositionScaleDrawImage> GetDiscardableImagesInRect(
const DiscardableImageMap& image_map,
const gfx::Rect& rect) {
- std::vector<DrawImage> draw_images;
+ std::vector<const DrawImage*> draw_image_ptrs;
// 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);
+ image_map.GetDiscardableImagesInRect(rect, &draw_image_ptrs);
+ std::vector<DrawImage> draw_images;
+ for (const auto* image : draw_image_ptrs)
+ draw_images.push_back(DrawImage(*image, 1.f, target_color_space));
std::vector<PositionScaleDrawImage> position_draw_images;
for (DrawImage& image : image_map.images_rtree_.Search(rect)) {
@@ -392,11 +379,11 @@ TEST_F(DiscardableImageMapTest, PaintDestroyedWhileImageIsDrawn) {
scoped_refptr<DisplayItemList> display_list = new DisplayItemList;
PaintFlags paint;
- PaintOpBuffer* buffer = display_list->StartPaint();
+ display_list->StartPaint();
SkRect visible_sk_rect(gfx::RectToSkRect(visible_rect));
- buffer->push<SaveLayerOp>(&visible_sk_rect, &paint);
- buffer->push<DrawRecordOp>(std::move(record));
- buffer->push<RestoreOp>();
+ display_list->push<SaveLayerOp>(&visible_sk_rect, &paint);
+ display_list->push<DrawRecordOp>(std::move(record));
+ display_list->push<RestoreOp>();
display_list->EndPaintOfUnpaired(visible_rect);
display_list->Finalize();
@@ -417,11 +404,11 @@ TEST_F(DiscardableImageMapTest, NullPaintOnSaveLayer) {
sk_sp<PaintRecord> record = CreateRecording(discardable_image, visible_rect);
scoped_refptr<DisplayItemList> display_list = new DisplayItemList;
- PaintOpBuffer* buffer = display_list->StartPaint();
+ display_list->StartPaint();
SkRect visible_sk_rect(gfx::RectToSkRect(visible_rect));
- buffer->push<SaveLayerOp>(&visible_sk_rect, nullptr);
- buffer->push<DrawRecordOp>(std::move(record));
- buffer->push<RestoreOp>();
+ display_list->push<SaveLayerOp>(&visible_sk_rect, nullptr);
+ display_list->push<DrawRecordOp>(std::move(record));
+ display_list->push<RestoreOp>();
display_list->EndPaintOfUnpaired(visible_rect);
display_list->Finalize();
@@ -577,7 +564,7 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) {
// |---|---|---|---|
// | x | | x | |
// |---|---|---|---|
- sk_sp<SkImage> discardable_image[4][4];
+ PaintImage 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.
@@ -586,7 +573,12 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) {
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));
+ discardable_image[y][x] =
+ PaintImageBuilder()
+ .set_id(y * 4 + x)
+ .set_paint_image_generator(
+ CreatePaintImageGenerator(gfx::Size(500, 500)))
+ .TakePaintImage();
SkMatrix scale = SkMatrix::MakeScale(std::max(x * 0.5f, kMinScale),
std::max(y * 0.5f, kMinScale));
PaintFlags flags;
@@ -605,20 +597,19 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) {
display_list->GenerateDiscardableImagesMetadata();
const DiscardableImageMap& image_map = display_list->discardable_image_map();
- gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateXYZD50();
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 4; ++x) {
- std::vector<DrawImage> draw_images;
+ std::vector<const DrawImage*> draw_images;
image_map.GetDiscardableImagesInRect(
- gfx::Rect(x * 512, y * 512, 500, 500), 1.f, target_color_space,
- &draw_images);
+ gfx::Rect(x * 512, y * 512, 500, 500), &draw_images);
if ((x + y) & 1) {
EXPECT_EQ(1u, draw_images.size()) << x << " " << y;
- EXPECT_TRUE(draw_images[0].image() == discardable_image[y][x])
+ EXPECT_TRUE(draw_images[0]->paint_image() == discardable_image[y][x])
<< x << " " << y;
- EXPECT_EQ(std::max(x * 0.5f, kMinScale), draw_images[0].scale().fWidth);
+ EXPECT_EQ(std::max(x * 0.5f, kMinScale),
+ draw_images[0]->scale().fWidth);
EXPECT_EQ(std::max(y * 0.5f, kMinScale),
- draw_images[0].scale().fHeight);
+ draw_images[0]->scale().fHeight);
} else {
EXPECT_EQ(0u, draw_images.size()) << x << " " << y;
}
@@ -626,14 +617,14 @@ TEST_F(DiscardableImageMapTest, GetDiscardableImagesInShader) {
}
// Capture 4 pixel refs.
- std::vector<DrawImage> draw_images;
- image_map.GetDiscardableImagesInRect(gfx::Rect(512, 512, 2048, 2048), 1.f,
- target_color_space, &draw_images);
+ std::vector<const DrawImage*> draw_images;
+ image_map.GetDiscardableImagesInRect(gfx::Rect(512, 512, 2048, 2048),
+ &draw_images);
EXPECT_EQ(4u, draw_images.size());
- EXPECT_TRUE(draw_images[0].image() == discardable_image[1][2]);
- EXPECT_TRUE(draw_images[1].image() == discardable_image[2][1]);
- EXPECT_TRUE(draw_images[2].image() == discardable_image[2][3]);
- EXPECT_TRUE(draw_images[3].image() == discardable_image[3][2]);
+ EXPECT_TRUE(draw_images[0]->paint_image() == discardable_image[1][2]);
+ EXPECT_TRUE(draw_images[1]->paint_image() == discardable_image[2][1]);
+ EXPECT_TRUE(draw_images[2]->paint_image() == discardable_image[2][3]);
+ EXPECT_TRUE(draw_images[3]->paint_image() == discardable_image[3][2]);
}
TEST_F(DiscardableImageMapTest, ClipsImageRects) {
@@ -645,10 +636,10 @@ TEST_F(DiscardableImageMapTest, ClipsImageRects) {
scoped_refptr<DisplayItemList> display_list = new DisplayItemList;
- PaintOpBuffer* buffer = display_list->StartPaint();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(gfx::Rect(250, 250)),
- SkClipOp::kIntersect, false);
- buffer->push<DrawRecordOp>(std::move(record));
+ display_list->StartPaint();
+ display_list->push<ClipRectOp>(gfx::RectToSkRect(gfx::Rect(250, 250)),
+ SkClipOp::kIntersect, false);
+ display_list->push<DrawRecordOp>(std::move(record));
display_list->EndPaintOfUnpaired(gfx::Rect(250, 250));
display_list->Finalize();
@@ -676,9 +667,10 @@ TEST_F(DiscardableImageMapTest, GathersDiscardableImagesFromNestedOps) {
PaintImage discardable_image2 =
CreateDiscardablePaintImage(gfx::Size(100, 100));
- scoped_refptr<DisplayItemList> display_list = new DisplayItemList;
- PaintOpBuffer* buffer = display_list->StartPaint();
- buffer->push<DrawImageOp>(discardable_image2, 100.f, 100.f, nullptr);
+ scoped_refptr<DisplayItemList> display_list =
+ new DisplayItemList(DisplayItemList::kToBeReleasedAsPaintOpBuffer);
+ display_list->StartPaint();
+ display_list->push<DrawImageOp>(discardable_image2, 100.f, 100.f, nullptr);
display_list->EndPaintOfUnpaired(gfx::Rect(100, 100, 100, 100));
display_list->Finalize();
@@ -690,18 +682,15 @@ TEST_F(DiscardableImageMapTest, GathersDiscardableImagesFromNestedOps) {
DiscardableImageMap image_map;
image_map.Generate(&root_buffer, gfx::Rect(200, 200));
- gfx::ColorSpace target_color_space;
- std::vector<DrawImage> images;
- image_map.GetDiscardableImagesInRect(gfx::Rect(0, 0, 5, 95), 1.f,
- target_color_space, &images);
+ std::vector<const DrawImage*> images;
+ image_map.GetDiscardableImagesInRect(gfx::Rect(0, 0, 5, 95), &images);
EXPECT_EQ(1u, images.size());
- EXPECT_TRUE(discardable_image == images[0].paint_image());
+ EXPECT_TRUE(discardable_image == images[0]->paint_image());
images.clear();
- image_map.GetDiscardableImagesInRect(gfx::Rect(105, 105, 5, 95), 1.f,
- target_color_space, &images);
+ image_map.GetDiscardableImagesInRect(gfx::Rect(105, 105, 5, 95), &images);
EXPECT_EQ(1u, images.size());
- EXPECT_TRUE(discardable_image2 == images[0].paint_image());
+ EXPECT_TRUE(discardable_image2 == images[0]->paint_image());
}
class DiscardableImageMapColorSpaceTest
@@ -711,8 +700,8 @@ class DiscardableImageMapColorSpaceTest
TEST_P(DiscardableImageMapColorSpaceTest, ColorSpace) {
const gfx::ColorSpace image_color_space = GetParam();
gfx::Rect visible_rect(500, 500);
- PaintImage discardable_image = CreateDiscardablePaintImageWithColorSpace(
- gfx::Size(500, 500), image_color_space);
+ PaintImage discardable_image = CreateDiscardablePaintImage(
+ gfx::Size(500, 500), image_color_space.ToSkColorSpace());
FakeContentLayerClient content_layer_client;
content_layer_client.set_bounds(visible_rect.size());
diff --git a/chromium/cc/paint/display_item_list.cc b/chromium/cc/paint/display_item_list.cc
index 61aa873a869..bf6fbefc145 100644
--- a/chromium/cc/paint/display_item_list.cc
+++ b/chromium/cc/paint/display_item_list.cc
@@ -35,25 +35,32 @@ bool GetCanvasClipBounds(SkCanvas* canvas, gfx::Rect* clip_bounds) {
} // namespace
-DisplayItemList::DisplayItemList() {
- visual_rects_.reserve(1024);
- begin_paired_indices_.reserve(32);
+DisplayItemList::DisplayItemList(UsageHint usage_hint)
+ : usage_hint_(usage_hint) {
+ if (usage_hint_ == kTopLevelDisplayItemList) {
+ visual_rects_.reserve(1024);
+ offsets_.reserve(1024);
+ begin_paired_indices_.reserve(32);
+ }
}
DisplayItemList::~DisplayItemList() = default;
void DisplayItemList::Raster(SkCanvas* canvas,
+ ImageProvider* image_provider,
SkPicture::AbortCallback* callback) const {
+ DCHECK(usage_hint_ == kTopLevelDisplayItemList);
gfx::Rect canvas_playback_rect;
if (!GetCanvasClipBounds(canvas, &canvas_playback_rect))
return;
- std::vector<size_t> indices = rtree_.Search(canvas_playback_rect);
- paint_op_buffer_.Playback(canvas, callback, &indices);
+ std::vector<size_t> offsets = rtree_.Search(canvas_playback_rect);
+ paint_op_buffer_.Playback(canvas, image_provider, callback, &offsets);
}
void DisplayItemList::GrowCurrentBeginItemVisualRect(
const gfx::Rect& visual_rect) {
+ DCHECK(usage_hint_ == kTopLevelDisplayItemList);
if (!begin_paired_indices_.empty())
visual_rects_[begin_paired_indices_.back().first].Union(visual_rect);
}
@@ -65,12 +72,25 @@ void DisplayItemList::Finalize() {
// If this fails we had more calls to EndPaintOfPairedBegin() than
// to EndPaintOfPairedEnd().
DCHECK_EQ(0, in_paired_begin_count_);
-
+ DCHECK_EQ(visual_rects_.size(), offsets_.size());
+
+ if (usage_hint_ == kTopLevelDisplayItemList) {
+ rtree_.Build(visual_rects_,
+ [](const std::vector<gfx::Rect>& rects, size_t index) {
+ return rects[index];
+ },
+ [this](const std::vector<gfx::Rect>& rects, size_t index) {
+ // Ignore the given rects, since the payload comes from
+ // offsets. However, the indices match, so we can just index
+ // into offsets.
+ return offsets_[index];
+ });
+ }
paint_op_buffer_.ShrinkToFit();
- rtree_.Build(visual_rects_);
-
visual_rects_.clear();
visual_rects_.shrink_to_fit();
+ offsets_.clear();
+ offsets_.shrink_to_fit();
begin_paired_indices_.shrink_to_fit();
}
@@ -95,20 +115,20 @@ void DisplayItemList::EmitTraceSnapshot() const {
std::unique_ptr<base::trace_event::TracedValue>
DisplayItemList::CreateTracedValue(bool include_items) const {
- auto state = base::MakeUnique<base::trace_event::TracedValue>();
+ auto state = std::make_unique<base::trace_event::TracedValue>();
state->BeginDictionary("params");
if (include_items) {
state->BeginArray("items");
- for (size_t i = 0; i < paint_op_buffer_.size(); ++i) {
+ PlaybackParams params(nullptr, SkMatrix::I());
+ for (const PaintOp* op : PaintOpBuffer::Iterator(&paint_op_buffer_)) {
state->BeginDictionary();
SkPictureRecorder recorder;
SkCanvas* canvas =
recorder.beginRecording(gfx::RectToSkRect(rtree_.GetBounds()));
- std::vector<size_t> indices{i};
- paint_op_buffer_.Playback(canvas, nullptr, &indices);
+ op->Raster(canvas, params);
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
std::string b64_picture;
@@ -141,6 +161,7 @@ DisplayItemList::CreateTracedValue(bool include_items) const {
}
void DisplayItemList::GenerateDiscardableImagesMetadata() {
+ DCHECK(usage_hint_ == kTopLevelDisplayItemList);
image_map_.Generate(&paint_op_buffer_, rtree_.GetBounds());
}
@@ -152,7 +173,11 @@ void DisplayItemList::Reset() {
image_map_.Reset();
paint_op_buffer_.Reset();
visual_rects_.clear();
+ visual_rects_.shrink_to_fit();
+ offsets_.clear();
+ offsets_.shrink_to_fit();
begin_paired_indices_.clear();
+ begin_paired_indices_.shrink_to_fit();
current_range_start_ = 0;
in_paired_begin_count_ = 0;
in_painting_ = false;
@@ -169,16 +194,17 @@ sk_sp<PaintRecord> DisplayItemList::ReleaseAsRecord() {
bool DisplayItemList::GetColorIfSolidInRect(const gfx::Rect& rect,
SkColor* color,
int max_ops_to_analyze) {
- std::vector<size_t>* indices_to_use = nullptr;
- std::vector<size_t> indices;
+ DCHECK(usage_hint_ == kTopLevelDisplayItemList);
+ std::vector<size_t>* offsets_to_use = nullptr;
+ std::vector<size_t> offsets;
if (!rect.Contains(rtree_.GetBounds())) {
- indices = rtree_.Search(rect);
- indices_to_use = &indices;
+ offsets = rtree_.Search(rect);
+ offsets_to_use = &offsets;
}
base::Optional<SkColor> solid_color =
SolidColorAnalyzer::DetermineIfSolidColor(
- &paint_op_buffer_, rect, max_ops_to_analyze, indices_to_use);
+ &paint_op_buffer_, rect, max_ops_to_analyze, offsets_to_use);
if (solid_color) {
*color = *solid_color;
return true;
diff --git a/chromium/cc/paint/display_item_list.h b/chromium/cc/paint/display_item_list.h
index 989539e4a2e..3f96dc46246 100644
--- a/chromium/cc/paint/display_item_list.h
+++ b/chromium/cc/paint/display_item_list.h
@@ -26,6 +26,12 @@
class SkCanvas;
+namespace gpu {
+namespace gles2 {
+class GLES2Implementation;
+} // namespace gles2
+} // namespace gpu
+
namespace base {
namespace trace_event {
class TracedValue;
@@ -34,22 +40,49 @@ class TracedValue;
namespace cc {
+// DisplayItemList is a container of paint operations. One can populate the list
+// using StartPaint, followed by push{,_with_data,_with_array} functions
+// specialized with ops coming from paint_op_buffer.h. Internally, the
+// DisplayItemList contains a PaintOpBuffer and defers op saving to it.
+// Additionally, it store some meta information about the paint operations.
+// Specifically, it creates an rtree to assist in rasterization: when
+// rasterizing a rect, it queries the rtree to extract only the byte offsets of
+// the ops required and replays those into a canvas.
class CC_PAINT_EXPORT DisplayItemList
: public base::RefCountedThreadSafe<DisplayItemList> {
public:
- DisplayItemList();
+ // TODO(vmpstr): It would be cool if we didn't need this, and instead used
+ // PaintOpBuffer directly when we needed to release this as a paint op buffer.
+ enum UsageHint { kTopLevelDisplayItemList, kToBeReleasedAsPaintOpBuffer };
+
+ explicit DisplayItemList(UsageHint = kTopLevelDisplayItemList);
void Raster(SkCanvas* canvas,
+ ImageProvider* image_provider = nullptr,
SkPicture::AbortCallback* callback = nullptr) const;
- PaintOpBuffer* StartPaint() {
+ // TODO(vmpstr): This is only used to keep track of debugging info, so we can
+ // probably remove it? But it would be nice to delimit painting in a block
+ // somehow (RAII object maybe).
+ void StartPaint() {
DCHECK(!in_painting_);
in_painting_ = true;
- return &paint_op_buffer_;
+ }
+
+ // Push functions construct a new op on the paint op buffer, while maintaining
+ // bookkeeping information. Must be called after invoking StartPaint().
+ template <typename T, typename... Args>
+ void push(Args&&... args) {
+ DCHECK(in_painting_);
+ if (usage_hint_ == kTopLevelDisplayItemList)
+ offsets_.push_back(paint_op_buffer_.next_op_offset());
+ paint_op_buffer_.push<T>(std::forward<Args>(args)...);
}
void EndPaintOfUnpaired(const gfx::Rect& visual_rect) {
in_painting_ = false;
+ if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
+ return;
// Empty paint item.
if (visual_rects_.size() == paint_op_buffer_.size())
@@ -61,6 +94,9 @@ class CC_PAINT_EXPORT DisplayItemList
}
void EndPaintOfPairedBegin(const gfx::Rect& visual_rect = gfx::Rect()) {
+ in_painting_ = false;
+ if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
+ return;
DCHECK_NE(visual_rects_.size(), paint_op_buffer_.size());
size_t count = paint_op_buffer_.size() - visual_rects_.size();
for (size_t i = 0; i < count; ++i)
@@ -68,11 +104,13 @@ class CC_PAINT_EXPORT DisplayItemList
begin_paired_indices_.push_back(
std::make_pair(visual_rects_.size() - 1, count));
- in_painting_ = false;
in_paired_begin_count_++;
}
void EndPaintOfPairedEnd() {
+ in_painting_ = false;
+ if (usage_hint_ == kToBeReleasedAsPaintOpBuffer)
+ return;
DCHECK_NE(current_range_start_, paint_op_buffer_.size());
DCHECK(in_paired_begin_count_);
@@ -100,7 +138,6 @@ class CC_PAINT_EXPORT DisplayItemList
// block.
GrowCurrentBeginItemVisualRect(visual_rect);
- in_painting_ = false;
in_paired_begin_count_--;
}
@@ -137,6 +174,7 @@ class CC_PAINT_EXPORT DisplayItemList
private:
FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, AsValueWithNoOps);
FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, AsValueWithOps);
+ friend gpu::gles2::GLES2Implementation;
~DisplayItemList();
@@ -159,6 +197,8 @@ class CC_PAINT_EXPORT DisplayItemList
// display item list. These rects are intentionally kept separate because they
// are used to decide which ops to walk for raster.
std::vector<gfx::Rect> visual_rects_;
+ // Byte offsets associated with each of the ops.
+ std::vector<size_t> offsets_;
// A stack of pairs of indices and counts. The indices are into the
// |visual_rects_| for each paired begin range that hasn't been closed. The
// counts refer to the number of visual rects in that begin sequence that end
@@ -174,6 +214,8 @@ class CC_PAINT_EXPORT DisplayItemList
// nesting.
bool in_painting_ = false;
+ UsageHint usage_hint_;
+
friend class base::RefCountedThreadSafe<DisplayItemList>;
FRIEND_TEST_ALL_PREFIXES(DisplayItemListTest, BytesUsed);
DISALLOW_COPY_AND_ASSIGN(DisplayItemList);
diff --git a/chromium/cc/paint/display_item_list_unittest.cc b/chromium/cc/paint/display_item_list_unittest.cc
index d5ff4869b7f..5fec9fbff88 100644
--- a/chromium/cc/paint/display_item_list_unittest.cc
+++ b/chromium/cc/paint/display_item_list_unittest.cc
@@ -76,14 +76,13 @@ TEST(DisplayItemListTest, SingleUnpairedRange) {
gfx::Point offset(8, 9);
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<TranslateOp>(static_cast<float>(offset.x()),
- static_cast<float>(offset.y()));
- buffer->push<DrawRectOp>(SkRect::MakeLTRB(0.f, 0.f, 60.f, 60.f), red_paint);
- buffer->push<DrawRectOp>(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f),
- blue_flags);
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<TranslateOp>(static_cast<float>(offset.x()),
+ static_cast<float>(offset.y()));
+ list->push<DrawRectOp>(SkRect::MakeLTRB(0.f, 0.f, 60.f, 60.f), red_paint);
+ list->push<DrawRectOp>(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f), blue_flags);
+ list->push<RestoreOp>();
list->EndPaintOfUnpaired(gfx::Rect(offset, layer_rect.size()));
list->Finalize();
DrawDisplayList(pixels, layer_rect, list);
@@ -119,9 +118,9 @@ TEST(DisplayItemListTest, EmptyUnpairedRangeDoesNotAddVisualRect) {
EXPECT_EQ(0u, list->op_count());
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<RestoreOp>();
list->EndPaintOfUnpaired(layer_rect);
}
// Two ops.
@@ -141,40 +140,40 @@ TEST(DisplayItemListTest, ClipPairedRange) {
gfx::Rect first_recording_rect(first_offset, layer_rect.size());
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<TranslateOp>(static_cast<float>(first_offset.x()),
- static_cast<float>(first_offset.y()));
- buffer->push<DrawRectOp>(SkRect::MakeWH(60, 60), red_paint);
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<TranslateOp>(static_cast<float>(first_offset.x()),
+ static_cast<float>(first_offset.y()));
+ list->push<DrawRectOp>(SkRect::MakeWH(60, 60), red_paint);
+ list->push<RestoreOp>();
list->EndPaintOfUnpaired(first_recording_rect);
}
gfx::Rect clip_rect(60, 60, 10, 10);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_rect), SkClipOp::kIntersect,
- true);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_rect), SkClipOp::kIntersect,
+ true);
list->EndPaintOfPairedBegin();
}
gfx::Point second_offset(2, 3);
gfx::Rect second_recording_rect(second_offset, layer_rect.size());
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<TranslateOp>(static_cast<float>(second_offset.x()),
- static_cast<float>(second_offset.y()));
- buffer->push<DrawRectOp>(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f),
- blue_flags);
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<TranslateOp>(static_cast<float>(second_offset.x()),
+ static_cast<float>(second_offset.y()));
+ list->push<DrawRectOp>(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f),
+ blue_flags);
+ list->push<RestoreOp>();
list->EndPaintOfUnpaired(second_recording_rect);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -214,40 +213,40 @@ TEST(DisplayItemListTest, TransformPairedRange) {
gfx::Point first_offset(8, 9);
gfx::Rect first_recording_rect(first_offset, layer_rect.size());
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<TranslateOp>(static_cast<float>(first_offset.x()),
- static_cast<float>(first_offset.y()));
- buffer->push<DrawRectOp>(SkRect::MakeWH(60, 60), red_paint);
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<TranslateOp>(static_cast<float>(first_offset.x()),
+ static_cast<float>(first_offset.y()));
+ list->push<DrawRectOp>(SkRect::MakeWH(60, 60), red_paint);
+ list->push<RestoreOp>();
list->EndPaintOfUnpaired(first_recording_rect);
}
gfx::Transform transform;
transform.Rotate(45.0);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ConcatOp>(static_cast<SkMatrix>(transform.matrix()));
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ConcatOp>(static_cast<SkMatrix>(transform.matrix()));
list->EndPaintOfPairedBegin();
}
gfx::Point second_offset(2, 3);
gfx::Rect second_recording_rect(second_offset, layer_rect.size());
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<TranslateOp>(static_cast<float>(second_offset.x()),
- static_cast<float>(second_offset.y()));
- buffer->push<DrawRectOp>(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f),
- blue_flags);
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<TranslateOp>(static_cast<float>(second_offset.x()),
+ static_cast<float>(second_offset.y()));
+ list->push<DrawRectOp>(SkRect::MakeLTRB(50.f, 50.f, 75.f, 75.f),
+ blue_flags);
+ list->push<RestoreOp>();
list->EndPaintOfUnpaired(second_recording_rect);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
list->Finalize();
@@ -301,9 +300,9 @@ TEST(DisplayItemListTest, FilterPairedRange) {
filters.Append(FilterOperation::CreateBrightnessFilter(0.5f));
gfx::RectF filter_bounds(10.f, 10.f, 50.f, 50.f);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<TranslateOp>(filter_bounds.x(), filter_bounds.y());
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<TranslateOp>(filter_bounds.x(), filter_bounds.y());
PaintFlags flags;
flags.setImageFilter(
@@ -311,20 +310,20 @@ TEST(DisplayItemListTest, FilterPairedRange) {
SkRect layer_bounds = gfx::RectFToSkRect(filter_bounds);
layer_bounds.offset(-filter_bounds.x(), -filter_bounds.y());
- buffer->push<SaveLayerOp>(&layer_bounds, &flags);
- buffer->push<TranslateOp>(-filter_bounds.x(), -filter_bounds.y());
+ list->push<SaveLayerOp>(&layer_bounds, &flags);
+ list->push<TranslateOp>(-filter_bounds.x(), -filter_bounds.y());
list->EndPaintOfPairedBegin();
}
// Include a rect drawing so that filter is actually applied to something.
{
- PaintOpBuffer* buffer = list->StartPaint();
+ list->StartPaint();
PaintFlags red_flags;
red_flags.setColor(SK_ColorRED);
- buffer->push<DrawRectOp>(
+ list->push<DrawRectOp>(
SkRect::MakeLTRB(filter_bounds.x(), filter_bounds.y(),
filter_bounds.right(), filter_bounds.bottom()),
red_flags);
@@ -333,9 +332,9 @@ TEST(DisplayItemListTest, FilterPairedRange) {
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>(); // For SaveLayerOp.
- buffer->push<RestoreOp>(); // For SaveOp.
+ list->StartPaint();
+ list->push<RestoreOp>(); // For SaveLayerOp.
+ list->push<RestoreOp>(); // For SaveOp.
list->EndPaintOfPairedEnd();
}
@@ -367,9 +366,9 @@ TEST(DisplayItemListTest, BytesUsed) {
blue_flags.setColor(SK_ColorBLUE);
{
- PaintOpBuffer* buffer = list->StartPaint();
+ list->StartPaint();
for (int i = 0; i < kNumPaintOps; i++)
- buffer->push<DrawRectOp>(SkRect::MakeWH(1, 1), blue_flags);
+ list->push<DrawRectOp>(SkRect::MakeWH(1, 1), blue_flags);
list->EndPaintOfUnpaired(layer_rect);
}
@@ -445,31 +444,31 @@ TEST(DisplayItemListTest, AsValueWithOps) {
transform.Translate(6.f, 7.f);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ConcatOp>(static_cast<SkMatrix>(transform.matrix()));
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ConcatOp>(static_cast<SkMatrix>(transform.matrix()));
list->EndPaintOfPairedBegin();
}
gfx::Point offset(2, 3);
gfx::Rect bounds(offset, layer_rect.size());
{
- PaintOpBuffer* buffer = list->StartPaint();
+ list->StartPaint();
PaintFlags red_paint;
red_paint.setColor(SK_ColorRED);
- buffer->push<SaveOp>();
- buffer->push<TranslateOp>(static_cast<float>(offset.x()),
- static_cast<float>(offset.y()));
- buffer->push<DrawRectOp>(SkRect::MakeWH(4, 4), red_paint);
+ list->push<SaveOp>();
+ list->push<TranslateOp>(static_cast<float>(offset.x()),
+ static_cast<float>(offset.y()));
+ list->push<DrawRectOp>(SkRect::MakeWH(4, 4), red_paint);
list->EndPaintOfUnpaired(bounds);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
list->Finalize();
@@ -549,8 +548,8 @@ TEST(DisplayItemListTest, SizeOne) {
auto list = make_scoped_refptr(new DisplayItemList);
gfx::Rect drawing_bounds(5, 6, 1, 1);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_bounds);
}
EXPECT_EQ(1u, list->op_count());
@@ -560,15 +559,15 @@ TEST(DisplayItemListTest, SizeMultiple) {
auto list = make_scoped_refptr(new DisplayItemList);
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
EXPECT_EQ(3u, list->op_count());
@@ -581,8 +580,8 @@ TEST(DisplayItemListTest, AppendVisualRectSimple) {
gfx::Rect drawing_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_bounds);
}
@@ -597,15 +596,15 @@ TEST(DisplayItemListTest, AppendVisualRectEmptyBlock) {
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -622,27 +621,27 @@ TEST(DisplayItemListTest, AppendVisualRectEmptyBlockContainingEmptyBlock) {
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
+ list->StartPaint();
+ list->push<SaveOp>();
list->EndPaintOfPairedBegin();
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -661,23 +660,23 @@ TEST(DisplayItemListTest, AppendVisualRectBlockContainingDrawing) {
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_bounds(5, 6, 1, 1);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_bounds);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -695,23 +694,23 @@ TEST(DisplayItemListTest, AppendVisualRectBlockContainingEscapedDrawing) {
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_bounds(1, 2, 3, 4);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_bounds);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -731,30 +730,30 @@ TEST(DisplayItemListTest,
gfx::Rect drawing_a_bounds(1, 2, 3, 4);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_a_bounds);
}
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_b_bounds(13, 14, 1, 1);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_b_bounds);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -773,44 +772,44 @@ TEST(DisplayItemListTest, AppendVisualRectTwoBlocksTwoDrawings) {
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_a_bounds(5, 6, 1, 1);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_a_bounds);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ConcatOp>(SkMatrix::I());
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ConcatOp>(SkMatrix::I());
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_b_bounds(7, 8, 1, 1);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_b_bounds);
}
// End transform.
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
// End clip.
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -836,44 +835,44 @@ TEST(DisplayItemListTest,
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_a_bounds(5, 6, 1, 1);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_a_bounds);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ConcatOp>(SkMatrix::I());
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ConcatOp>(SkMatrix::I());
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_b_bounds(1, 2, 3, 4);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_b_bounds);
}
// End transform.
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
// End clip.
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -899,44 +898,44 @@ TEST(DisplayItemListTest,
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_a_bounds(1, 2, 3, 4);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_a_bounds);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ConcatOp>(SkMatrix::I());
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ConcatOp>(SkMatrix::I());
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_b_bounds(7, 8, 1, 1);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_b_bounds);
}
// End transform.
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
// End clip.
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
@@ -962,44 +961,44 @@ TEST(DisplayItemListTest,
gfx::Rect clip_bounds(5, 6, 7, 8);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds),
- SkClipOp::kIntersect, false);
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ClipRectOp>(gfx::RectToSkRect(clip_bounds), SkClipOp::kIntersect,
+ false);
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_a_bounds(13, 14, 1, 1);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_a_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_a_bounds);
}
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<SaveOp>();
- buffer->push<ConcatOp>(SkMatrix::I());
+ list->StartPaint();
+ list->push<SaveOp>();
+ list->push<ConcatOp>(SkMatrix::I());
list->EndPaintOfPairedBegin();
}
gfx::Rect drawing_b_bounds(1, 2, 3, 4);
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
+ list->StartPaint();
+ list->push<DrawRectOp>(gfx::RectToSkRect(drawing_b_bounds), PaintFlags());
list->EndPaintOfUnpaired(drawing_b_bounds);
}
// End transform.
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
// End clip.
{
- PaintOpBuffer* buffer = list->StartPaint();
- buffer->push<RestoreOp>();
+ list->StartPaint();
+ list->push<RestoreOp>();
list->EndPaintOfPairedEnd();
}
diff --git a/chromium/cc/paint/draw_image.cc b/chromium/cc/paint/draw_image.cc
index c0505d97c86..7b7314b3cb1 100644
--- a/chromium/cc/paint/draw_image.cc
+++ b/chromium/cc/paint/draw_image.cc
@@ -25,7 +25,6 @@ bool ExtractScale(const SkMatrix& matrix, SkSize* scale) {
DrawImage::DrawImage()
: 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) {}
@@ -33,23 +32,35 @@ DrawImage::DrawImage(PaintImage image,
const SkIRect& src_rect,
SkFilterQuality filter_quality,
const SkMatrix& matrix,
- const gfx::ColorSpace& target_color_space)
+ const base::Optional<gfx::ColorSpace>& color_space)
: paint_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_);
+ target_color_space_(color_space) {
+ matrix_is_decomposable_ = ExtractScale(matrix, &scale_);
}
-DrawImage::DrawImage(const DrawImage& other) = default;
+DrawImage::DrawImage(const DrawImage& other,
+ float scale_adjustment,
+ const gfx::ColorSpace& color_space)
+ : paint_image_(other.paint_image_),
+ src_rect_(other.src_rect_),
+ filter_quality_(other.filter_quality_),
+ scale_(SkSize::Make(other.scale_.width() * scale_adjustment,
+ other.scale_.height() * scale_adjustment)),
+ matrix_is_decomposable_(other.matrix_is_decomposable_),
+ target_color_space_(color_space) {}
+DrawImage::DrawImage(const DrawImage& other) = default;
+DrawImage::DrawImage(DrawImage&& other) = default;
DrawImage::~DrawImage() = default;
+DrawImage& DrawImage::operator=(DrawImage&& other) = default;
+DrawImage& DrawImage::operator=(const DrawImage& other) = default;
+
bool DrawImage::operator==(const DrawImage& other) const {
return paint_image_ == other.paint_image_ && src_rect_ == other.src_rect_ &&
- filter_quality_ == other.filter_quality_ && matrix_ == other.matrix_ &&
- scale_ == other.scale_ &&
+ filter_quality_ == other.filter_quality_ && scale_ == other.scale_ &&
matrix_is_decomposable_ == other.matrix_is_decomposable_ &&
target_color_space_ == other.target_color_space_;
}
diff --git a/chromium/cc/paint/draw_image.h b/chromium/cc/paint/draw_image.h
index 8f202b1d629..58c46d98b58 100644
--- a/chromium/cc/paint/draw_image.h
+++ b/chromium/cc/paint/draw_image.h
@@ -5,6 +5,7 @@
#ifndef CC_PAINT_DRAW_IMAGE_H_
#define CC_PAINT_DRAW_IMAGE_H_
+#include "base/optional.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_image.h"
#include "third_party/skia/include/core/SkFilterQuality.h"
@@ -17,8 +18,6 @@
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();
@@ -26,42 +25,41 @@ class CC_PAINT_EXPORT DrawImage {
const SkIRect& src_rect,
SkFilterQuality filter_quality,
const SkMatrix& matrix,
- const gfx::ColorSpace& target_color_space);
+ const base::Optional<gfx::ColorSpace>& color_space = base::nullopt);
+ // Constructs a DrawImage from |other| by adjusting its scale and setting a
+ // new color_space.
+ DrawImage(const DrawImage& other,
+ float scale_adjustment,
+ const gfx::ColorSpace& color_space);
DrawImage(const DrawImage& other);
+ DrawImage(DrawImage&& other);
~DrawImage();
+ DrawImage& operator=(DrawImage&& other);
+ DrawImage& operator=(const DrawImage& other);
+
bool operator==(const DrawImage& other) const;
const PaintImage& paint_image() const { return paint_image_; }
- const sk_sp<SkImage>& image() const { return paint_image_.sk_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(paint_image_, src_rect_, filter_quality_, scaled_matrix,
- target_color_space_);
+ DCHECK(target_color_space_.has_value());
+ return *target_color_space_;
}
- DrawImage ApplyTargetColorSpace(const gfx::ColorSpace& target_color_space) {
- return DrawImage(paint_image_, src_rect_, filter_quality_, matrix_,
- target_color_space);
+ PaintImage::FrameKey frame_key() const {
+ return paint_image_.GetKeyForFrame(paint_image_.frame_index());
}
private:
PaintImage paint_image_;
SkIRect src_rect_;
SkFilterQuality filter_quality_;
- SkMatrix matrix_;
SkSize scale_;
bool matrix_is_decomposable_;
- gfx::ColorSpace target_color_space_;
+ base::Optional<gfx::ColorSpace> target_color_space_;
};
} // namespace cc
diff --git a/chromium/cc/paint/frame_metadata.h b/chromium/cc/paint/frame_metadata.h
new file mode 100644
index 00000000000..d5efe8ec92c
--- /dev/null
+++ b/chromium/cc/paint/frame_metadata.h
@@ -0,0 +1,28 @@
+// 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_FRAME_METADATA_H_
+#define CC_PAINT_FRAME_METADATA_H_
+
+#include "base/time/time.h"
+#include "cc/paint/paint_export.h"
+
+namespace cc {
+
+// TODO(khushalsagar): Find a better name?
+struct CC_PAINT_EXPORT FrameMetadata {
+ FrameMetadata() {}
+ FrameMetadata(bool complete, base::TimeDelta duration)
+ : complete(complete), duration(duration) {}
+
+ // True if the decoder has all encoded content for this frame.
+ bool complete = true;
+
+ // The duration for which this frame should be displayed.
+ base::TimeDelta duration;
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_FRAME_METADATA_H_
diff --git a/chromium/cc/paint/image_id.h b/chromium/cc/paint/image_id.h
index 015d6616eea..8c4154a69ba 100644
--- a/chromium/cc/paint/image_id.h
+++ b/chromium/cc/paint/image_id.h
@@ -15,11 +15,6 @@ namespace cc {
using PaintImageIdFlatSet = base::flat_set<PaintImage::Id>;
-// TODO(khushalsagar): These are only used by the hijack canvas since it uses
-// an SkCanvas to replace images. Remove once that moves to PaintOpBuffer.
-using SkImageId = uint32_t;
-using SkImageIdFlatSet = base::flat_set<SkImageId>;
-
} // namespace cc
#endif // CC_PAINT_IMAGE_ID_H_
diff --git a/chromium/cc/paint/image_provider.cc b/chromium/cc/paint/image_provider.cc
new file mode 100644
index 00000000000..80cb4bff89c
--- /dev/null
+++ b/chromium/cc/paint/image_provider.cc
@@ -0,0 +1,44 @@
+// 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/image_provider.h"
+
+namespace cc {
+
+ImageProvider::ScopedDecodedDrawImage::ScopedDecodedDrawImage() = default;
+
+ImageProvider::ScopedDecodedDrawImage::ScopedDecodedDrawImage(
+ DecodedDrawImage image)
+ : image_(std::move(image)) {}
+
+ImageProvider::ScopedDecodedDrawImage::ScopedDecodedDrawImage(
+ DecodedDrawImage image,
+ DestructionCallback callback)
+ : image_(std::move(image)), destruction_callback_(std::move(callback)) {}
+
+ImageProvider::ScopedDecodedDrawImage::ScopedDecodedDrawImage(
+ ScopedDecodedDrawImage&& other) {
+ image_ = std::move(other.image_);
+ destruction_callback_ = std::move(other.destruction_callback_);
+}
+
+ImageProvider::ScopedDecodedDrawImage& ImageProvider::ScopedDecodedDrawImage::
+operator=(ScopedDecodedDrawImage&& other) {
+ DestroyDecode();
+
+ image_ = std::move(other.image_);
+ destruction_callback_ = std::move(other.destruction_callback_);
+ return *this;
+}
+
+ImageProvider::ScopedDecodedDrawImage::~ScopedDecodedDrawImage() {
+ DestroyDecode();
+}
+
+void ImageProvider::ScopedDecodedDrawImage::DestroyDecode() {
+ if (!destruction_callback_.is_null())
+ std::move(destruction_callback_).Run(std::move(image_));
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/image_provider.h b/chromium/cc/paint/image_provider.h
new file mode 100644
index 00000000000..f5230e6e7e4
--- /dev/null
+++ b/chromium/cc/paint/image_provider.h
@@ -0,0 +1,57 @@
+// 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_PROVIDER_H_
+#define CC_PAINT_IMAGE_PROVIDER_H_
+
+#include "base/callback.h"
+#include "cc/paint/decoded_draw_image.h"
+#include "cc/paint/paint_export.h"
+
+namespace cc {
+class PaintImage;
+
+// Used to replace lazy generated PaintImages with decoded images for
+// rasterization.
+class CC_PAINT_EXPORT ImageProvider {
+ public:
+ class CC_PAINT_EXPORT ScopedDecodedDrawImage {
+ public:
+ using DestructionCallback = base::OnceCallback<void(DecodedDrawImage)>;
+
+ ScopedDecodedDrawImage();
+ explicit ScopedDecodedDrawImage(DecodedDrawImage image);
+ ScopedDecodedDrawImage(DecodedDrawImage image,
+ DestructionCallback callback);
+ ~ScopedDecodedDrawImage();
+
+ ScopedDecodedDrawImage(ScopedDecodedDrawImage&& other);
+ ScopedDecodedDrawImage& operator=(ScopedDecodedDrawImage&& other);
+
+ operator bool() const { return image_.image(); }
+ const DecodedDrawImage& decoded_image() const { return image_; }
+
+ private:
+ void DestroyDecode();
+
+ DecodedDrawImage image_;
+ DestructionCallback destruction_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDecodedDrawImage);
+ };
+
+ virtual ~ImageProvider() {}
+
+ // Returns the DecodedDrawImage to use for this PaintImage. If no image is
+ // provided, the draw for this image will be skipped during raster.
+ virtual ScopedDecodedDrawImage GetDecodedDrawImage(
+ const PaintImage& paint_image,
+ const SkRect& src_rect,
+ SkFilterQuality filter_quality,
+ const SkMatrix& matrix) = 0;
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_IMAGE_PROVIDER_H_
diff --git a/chromium/cc/paint/paint_canvas.h b/chromium/cc/paint/paint_canvas.h
index 556be6268a2..dd389770670 100644
--- a/chromium/cc/paint/paint_canvas.h
+++ b/chromium/cc/paint/paint_canvas.h
@@ -110,15 +110,6 @@ class CC_PAINT_EXPORT PaintCanvas {
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,
@@ -150,15 +141,6 @@ class CC_PAINT_EXPORT PaintCanvas {
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,
diff --git a/chromium/cc/paint/paint_flags.cc b/chromium/cc/paint/paint_flags.cc
index ce4f0486d2a..3f1027cf1f8 100644
--- a/chromium/cc/paint/paint_flags.cc
+++ b/chromium/cc/paint/paint_flags.cc
@@ -4,6 +4,8 @@
#include "cc/paint/paint_flags.h"
+#include "cc/paint/paint_op_buffer.h"
+
namespace {
static bool affects_alpha(const SkColorFilter* cf) {
@@ -14,15 +16,19 @@ static bool affects_alpha(const SkColorFilter* cf) {
namespace cc {
-// Match SkPaint defaults.
-PaintFlags::PaintFlags()
- : flags_(0u),
- cap_type_(SkPaint::kDefault_Cap),
- join_type_(SkPaint::kDefault_Join),
- style_(SkPaint::kFill_Style),
- text_encoding_(SkPaint::kUTF8_TextEncoding),
- hinting_(SkPaint::kNormal_Hinting),
- filter_quality_(SkFilterQuality::kNone_SkFilterQuality) {}
+PaintFlags::PaintFlags() {
+ // Match SkPaint defaults.
+ bitfields_uint_ = 0u;
+ bitfields_.cap_type_ = SkPaint::kDefault_Cap;
+ bitfields_.join_type_ = SkPaint::kDefault_Join;
+ bitfields_.style_ = SkPaint::kFill_Style;
+ bitfields_.text_encoding_ = SkPaint::kUTF8_TextEncoding;
+ bitfields_.hinting_ = SkPaint::kNormal_Hinting;
+ bitfields_.filter_quality_ = SkFilterQuality::kNone_SkFilterQuality;
+
+ static_assert(sizeof(bitfields_) <= sizeof(bitfields_uint_),
+ "Too many bitfields");
+}
PaintFlags::PaintFlags(const PaintFlags& flags) = default;
@@ -104,12 +110,11 @@ SkPaint PaintFlags::ToSkPaint() const {
paint.setDrawLooper(draw_looper_);
paint.setImageFilter(image_filter_);
paint.setTextSize(text_size_);
- paint.setTextScaleX(text_scale_x_);
paint.setColor(color_);
paint.setStrokeWidth(width_);
paint.setStrokeMiter(miter_limit_);
paint.setBlendMode(getBlendMode());
- paint.setFlags(flags_);
+ paint.setFlags(bitfields_.flags_);
paint.setStrokeCap(static_cast<SkPaint::Cap>(getStrokeCap()));
paint.setStrokeJoin(static_cast<SkPaint::Join>(getStrokeJoin()));
paint.setStyle(static_cast<SkPaint::Style>(getStyle()));
@@ -119,4 +124,8 @@ SkPaint PaintFlags::ToSkPaint() const {
return paint;
}
+bool PaintFlags::IsValid() const {
+ return PaintOp::IsValidPaintFlagsSkBlendMode(getBlendMode());
+}
+
} // namespace cc
diff --git a/chromium/cc/paint/paint_flags.h b/chromium/cc/paint/paint_flags.h
index c61b9fe09f4..f652f90c9b4 100644
--- a/chromium/cc/paint/paint_flags.h
+++ b/chromium/cc/paint/paint_flags.h
@@ -34,8 +34,10 @@ class CC_PAINT_EXPORT PaintFlags {
kStrokeAndFill_Style = SkPaint::kStrokeAndFill_Style,
};
bool nothingToDraw() const;
- ALWAYS_INLINE Style getStyle() const { return static_cast<Style>(style_); }
- ALWAYS_INLINE void setStyle(Style style) { style_ = style; }
+ ALWAYS_INLINE Style getStyle() const {
+ return static_cast<Style>(bitfields_.style_);
+ }
+ ALWAYS_INLINE void setStyle(Style style) { bitfields_.style_ = style; }
ALWAYS_INLINE SkColor getColor() const { return color_; }
ALWAYS_INLINE void setColor(SkColor color) { color_ = color; }
ALWAYS_INLINE uint8_t getAlpha() const { return SkColorGetA(color_); }
@@ -50,25 +52,25 @@ class CC_PAINT_EXPORT PaintFlags {
return static_cast<SkBlendMode>(blend_mode_);
}
ALWAYS_INLINE bool isAntiAlias() const {
- return !!(flags_ & SkPaint::kAntiAlias_Flag);
+ return !!(bitfields_.flags_ & SkPaint::kAntiAlias_Flag);
}
ALWAYS_INLINE void setAntiAlias(bool aa) {
SetInternalFlag(aa, SkPaint::kAntiAlias_Flag);
}
ALWAYS_INLINE bool isVerticalText() const {
- return !!(flags_ & SkPaint::kVerticalText_Flag);
+ return !!(bitfields_.flags_ & SkPaint::kVerticalText_Flag);
}
ALWAYS_INLINE void setVerticalText(bool vertical) {
SetInternalFlag(vertical, SkPaint::kVerticalText_Flag);
}
ALWAYS_INLINE bool isSubpixelText() const {
- return !!(flags_ & SkPaint::kSubpixelText_Flag);
+ return !!(bitfields_.flags_ & SkPaint::kSubpixelText_Flag);
}
ALWAYS_INLINE void setSubpixelText(bool subpixel_text) {
SetInternalFlag(subpixel_text, SkPaint::kSubpixelText_Flag);
}
ALWAYS_INLINE bool isLCDRenderText() const {
- return !!(flags_ & SkPaint::kLCDRenderText_Flag);
+ return !!(bitfields_.flags_ & SkPaint::kLCDRenderText_Flag);
}
ALWAYS_INLINE void setLCDRenderText(bool lcd_text) {
SetInternalFlag(lcd_text, SkPaint::kLCDRenderText_Flag);
@@ -80,17 +82,19 @@ class CC_PAINT_EXPORT PaintFlags {
kFull_Hinting = SkPaint::kFull_Hinting
};
ALWAYS_INLINE Hinting getHinting() const {
- return static_cast<Hinting>(hinting_);
+ return static_cast<Hinting>(bitfields_.hinting_);
+ }
+ ALWAYS_INLINE void setHinting(Hinting hinting) {
+ bitfields_.hinting_ = hinting;
}
- ALWAYS_INLINE void setHinting(Hinting hinting) { hinting_ = hinting; }
ALWAYS_INLINE bool isAutohinted() const {
- return !!(flags_ & SkPaint::kAutoHinting_Flag);
+ return !!(bitfields_.flags_ & SkPaint::kAutoHinting_Flag);
}
ALWAYS_INLINE void setAutohinted(bool use_auto_hinter) {
SetInternalFlag(use_auto_hinter, SkPaint::kAutoHinting_Flag);
}
ALWAYS_INLINE bool isDither() const {
- return !!(flags_ & SkPaint::kDither_Flag);
+ return !!(bitfields_.flags_ & SkPaint::kDither_Flag);
}
ALWAYS_INLINE void setDither(bool dither) {
SetInternalFlag(dither, SkPaint::kDither_Flag);
@@ -102,18 +106,18 @@ class CC_PAINT_EXPORT PaintFlags {
kGlyphID_TextEncoding = SkPaint::kGlyphID_TextEncoding
};
ALWAYS_INLINE TextEncoding getTextEncoding() const {
- return static_cast<TextEncoding>(text_encoding_);
+ return static_cast<TextEncoding>(bitfields_.text_encoding_);
}
ALWAYS_INLINE void setTextEncoding(TextEncoding encoding) {
- text_encoding_ = encoding;
+ bitfields_.text_encoding_ = encoding;
}
ALWAYS_INLINE SkScalar getTextSize() const { return text_size_; }
ALWAYS_INLINE void setTextSize(SkScalar text_size) { text_size_ = text_size; }
ALWAYS_INLINE void setFilterQuality(SkFilterQuality quality) {
- filter_quality_ = quality;
+ bitfields_.filter_quality_ = quality;
}
ALWAYS_INLINE SkFilterQuality getFilterQuality() const {
- return static_cast<SkFilterQuality>(filter_quality_);
+ return static_cast<SkFilterQuality>(bitfields_.filter_quality_);
}
ALWAYS_INLINE SkScalar getStrokeWidth() const { return width_; }
ALWAYS_INLINE void setStrokeWidth(SkScalar width) { width_ = width; }
@@ -128,8 +132,10 @@ class CC_PAINT_EXPORT PaintFlags {
kLast_Cap = kSquare_Cap,
kDefault_Cap = kButt_Cap
};
- ALWAYS_INLINE Cap getStrokeCap() const { return static_cast<Cap>(cap_type_); }
- ALWAYS_INLINE void setStrokeCap(Cap cap) { cap_type_ = cap; }
+ ALWAYS_INLINE Cap getStrokeCap() const {
+ return static_cast<Cap>(bitfields_.cap_type_);
+ }
+ ALWAYS_INLINE void setStrokeCap(Cap cap) { bitfields_.cap_type_ = cap; }
enum Join {
kMiter_Join = SkPaint::kMiter_Join,
kRound_Join = SkPaint::kRound_Join,
@@ -138,9 +144,9 @@ class CC_PAINT_EXPORT PaintFlags {
kDefault_Join = kMiter_Join
};
ALWAYS_INLINE Join getStrokeJoin() const {
- return static_cast<Join>(join_type_);
+ return static_cast<Join>(bitfields_.join_type_);
}
- ALWAYS_INLINE void setStrokeJoin(Join join) { join_type_ = join; }
+ ALWAYS_INLINE void setStrokeJoin(Join join) { bitfields_.join_type_ = join; }
ALWAYS_INLINE const sk_sp<SkTypeface>& getTypeface() const {
return typeface_;
@@ -166,6 +172,8 @@ class CC_PAINT_EXPORT PaintFlags {
return shader_ ? shader_->GetSkShader().get() : nullptr;
}
+ ALWAYS_INLINE const PaintShader* getShader() const { return shader_.get(); }
+
// Returns true if the shader has been set on the flags.
ALWAYS_INLINE bool HasShader() const { return !!shader_; }
@@ -209,15 +217,17 @@ class CC_PAINT_EXPORT PaintFlags {
SkPaint ToSkPaint() const;
+ bool IsValid() const;
+
private:
friend class PaintOpReader;
friend class PaintOpWriter;
ALWAYS_INLINE void SetInternalFlag(bool value, uint32_t mask) {
if (value)
- flags_ |= mask;
+ bitfields_.flags_ |= mask;
else
- flags_ &= ~mask;
+ bitfields_.flags_ &= ~mask;
}
sk_sp<SkTypeface> typeface_;
@@ -231,20 +241,25 @@ class CC_PAINT_EXPORT PaintFlags {
// Match(ish) SkPaint defaults. SkPaintDefaults is not public, so this
// just uses these values and ignores any SkUserConfig overrides.
float text_size_ = 12.f;
- float text_scale_x_ = 1.f;
- float text_skew_x_ = 0.f;
SkColor color_ = SK_ColorBLACK;
float width_ = 0.f;
float miter_limit_ = 4.f;
uint32_t blend_mode_ = static_cast<uint32_t>(SkBlendMode::kSrcOver);
- uint32_t flags_ : 16;
- uint32_t cap_type_ : 2;
- uint32_t join_type_ : 2;
- uint32_t style_ : 2;
- uint32_t text_encoding_ : 2;
- uint32_t hinting_ : 2;
- uint32_t filter_quality_ : 2;
+ struct PaintFlagsBitfields {
+ uint32_t flags_ : 16;
+ uint32_t cap_type_ : 2;
+ uint32_t join_type_ : 2;
+ uint32_t style_ : 2;
+ uint32_t text_encoding_ : 2;
+ uint32_t hinting_ : 2;
+ uint32_t filter_quality_ : 2;
+ };
+
+ union {
+ PaintFlagsBitfields bitfields_;
+ uint32_t bitfields_uint_;
+ };
};
} // namespace cc
diff --git a/chromium/cc/paint/paint_image.cc b/chromium/cc/paint/paint_image.cc
index 493e2a68704..ca109ec220b 100644
--- a/chromium/cc/paint/paint_image.cc
+++ b/chromium/cc/paint/paint_image.cc
@@ -3,26 +3,22 @@
// found in the LICENSE file.
#include "cc/paint/paint_image.h"
+
#include "base/atomic_sequence_num.h"
+#include "base/hash.h"
+#include "base/memory/ptr_util.h"
+#include "cc/paint/paint_image_generator.h"
+#include "cc/paint/paint_record.h"
+#include "cc/paint/skia_paint_image_generator.h"
+#include "ui/gfx/skia_util.h"
namespace cc {
namespace {
base::AtomicSequenceNumber s_next_id_;
-}
+base::AtomicSequenceNumber s_next_content_id_;
+} // namespace
PaintImage::PaintImage() = default;
-PaintImage::PaintImage(Id id,
- sk_sp<SkImage> sk_image,
- AnimationType animation_type,
- CompletionState completion_state,
- size_t frame_count,
- bool is_multipart)
- : id_(id),
- sk_image_(std::move(sk_image)),
- animation_type_(animation_type),
- completion_state_(completion_state),
- frame_count_(frame_count),
- is_multipart_(is_multipart) {}
PaintImage::PaintImage(const PaintImage& other) = default;
PaintImage::PaintImage(PaintImage&& other) = default;
PaintImage::~PaintImage() = default;
@@ -31,15 +27,196 @@ PaintImage& PaintImage::operator=(const PaintImage& other) = default;
PaintImage& PaintImage::operator=(PaintImage&& other) = default;
bool PaintImage::operator==(const PaintImage& other) const {
- return id_ == other.id_ && sk_image_ == other.sk_image_ &&
- animation_type_ == other.animation_type_ &&
+ return sk_image_ == other.sk_image_ && paint_record_ == other.paint_record_ &&
+ paint_record_rect_ == other.paint_record_rect_ &&
+ paint_record_content_id_ == other.paint_record_content_id_ &&
+ paint_image_generator_ == other.paint_image_generator_ &&
+ id_ == other.id_ && animation_type_ == other.animation_type_ &&
completion_state_ == other.completion_state_ &&
- frame_count_ == other.frame_count_ &&
- is_multipart_ == other.is_multipart_;
+ subset_rect_ == other.subset_rect_ &&
+ frame_index_ == other.frame_index_ &&
+ is_multipart_ == other.is_multipart_ &&
+ sk_image_id_ == other.sk_image_id_;
}
PaintImage::Id PaintImage::GetNextId() {
return s_next_id_.GetNext();
}
+PaintImage::ContentId PaintImage::GetNextContentId() {
+ return s_next_content_id_.GetNext();
+}
+
+const sk_sp<SkImage>& PaintImage::GetSkImage() const {
+ if (cached_sk_image_)
+ return cached_sk_image_;
+
+ if (sk_image_) {
+ cached_sk_image_ = sk_image_;
+ } else if (paint_record_) {
+ cached_sk_image_ = SkImage::MakeFromPicture(
+ ToSkPicture(paint_record_, gfx::RectToSkRect(paint_record_rect_)),
+ SkISize::Make(paint_record_rect_.width(), paint_record_rect_.height()),
+ nullptr, nullptr, SkImage::BitDepth::kU8, SkColorSpace::MakeSRGB());
+ } else if (paint_image_generator_) {
+ cached_sk_image_ =
+ SkImage::MakeFromGenerator(base::MakeUnique<SkiaPaintImageGenerator>(
+ paint_image_generator_, frame_index_, sk_image_id_));
+ }
+
+ if (!subset_rect_.IsEmpty() && cached_sk_image_) {
+ cached_sk_image_ =
+ cached_sk_image_->makeSubset(gfx::RectToSkIRect(subset_rect_));
+ }
+ return cached_sk_image_;
+}
+
+PaintImage PaintImage::MakeSubset(const gfx::Rect& subset) const {
+ DCHECK(!subset.IsEmpty());
+
+ // If the subset is the same as the image bounds, we can return the same
+ // image.
+ gfx::Rect bounds(width(), height());
+ if (bounds == subset)
+ return *this;
+
+ DCHECK(bounds.Contains(subset))
+ << "Subset should not be greater than the image bounds";
+ PaintImage result(*this);
+ result.subset_rect_ = subset;
+ // Store the subset from the original image.
+ result.subset_rect_.Offset(subset_rect_.x(), subset_rect_.y());
+
+ // Creating the |cached_sk_image_| is an optimization to allow re-use of the
+ // original decode for image subsets in skia, for cases that rely on skia's
+ // image decode cache.
+ // TODO(khushalsagar): Remove this when we no longer have such cases. See
+ // crbug.com/753639.
+ result.cached_sk_image_ =
+ GetSkImage()->makeSubset(gfx::RectToSkIRect(subset));
+ return result;
+}
+
+SkISize PaintImage::GetSupportedDecodeSize(
+ const SkISize& requested_size) const {
+ // TODO(vmpstr): For now, we ignore the requested size and just return the
+ // available image size.
+ return SkISize::Make(width(), height());
+}
+
+SkImageInfo PaintImage::CreateDecodeImageInfo(const SkISize& size,
+ SkColorType color_type) const {
+ DCHECK(GetSupportedDecodeSize(size) == size);
+ return SkImageInfo::Make(size.width(), size.height(), color_type,
+ kPremul_SkAlphaType);
+}
+
+bool PaintImage::Decode(void* memory,
+ SkImageInfo* info,
+ sk_sp<SkColorSpace> color_space) const {
+ auto image = GetSkImage();
+ DCHECK(image);
+ if (color_space) {
+ image =
+ image->makeColorSpace(color_space, SkTransferFunctionBehavior::kIgnore);
+ if (!image)
+ return false;
+ }
+ // Note that the readPixels has to happen before converting the info to the
+ // given color space, since it can produce incorrect results.
+ bool result = image->readPixels(*info, memory, info->minRowBytes(), 0, 0,
+ SkImage::kDisallow_CachingHint);
+ *info = info->makeColorSpace(color_space);
+ return result;
+}
+
+PaintImage::FrameKey PaintImage::GetKeyForFrame(size_t frame_index) const {
+ DCHECK_LT(frame_index, FrameCount());
+ DCHECK(paint_image_generator_ || paint_record_);
+
+ // Query the content id that uniquely identifies the content for this frame
+ // from the content provider.
+ ContentId content_id = kInvalidContentId;
+ if (paint_image_generator_)
+ content_id = paint_image_generator_->GetContentIdForFrame(frame_index);
+ else
+ content_id = paint_record_content_id_;
+
+ DCHECK_NE(content_id, kInvalidContentId);
+ return FrameKey(id_, content_id, frame_index, subset_rect_);
+}
+
+const std::vector<FrameMetadata>& PaintImage::GetFrameMetadata() const {
+ DCHECK_EQ(animation_type_, AnimationType::ANIMATED);
+ DCHECK(paint_image_generator_);
+
+ return paint_image_generator_->GetFrameMetadata();
+}
+
+size_t PaintImage::FrameCount() const {
+ if (!GetSkImage())
+ return 0u;
+ return paint_image_generator_
+ ? paint_image_generator_->GetFrameMetadata().size()
+ : 1u;
+}
+
+std::string PaintImage::ToString() const {
+ std::ostringstream str;
+ str << "sk_image_: " << sk_image_ << " paint_record_: " << paint_record_
+ << " paint_record_rect_: " << paint_record_rect_.ToString()
+ << " paint_image_generator_: " << paint_image_generator_
+ << " id_: " << id_
+ << " animation_type_: " << static_cast<int>(animation_type_)
+ << " completion_state_: " << static_cast<int>(completion_state_)
+ << " subset_rect_: " << subset_rect_.ToString()
+ << " frame_index_: " << frame_index_
+ << " is_multipart_: " << is_multipart_
+ << " sk_image_id_: " << sk_image_id_;
+ return str.str();
+}
+
+PaintImage::FrameKey::FrameKey(Id paint_image_id,
+ ContentId content_id,
+ size_t frame_index,
+ gfx::Rect subset_rect)
+ : paint_image_id_(paint_image_id),
+ content_id_(content_id),
+ frame_index_(frame_index),
+ subset_rect_(subset_rect) {
+ size_t original_hash = base::HashInts(
+ static_cast<uint64_t>(base::HashInts(paint_image_id_, content_id_)),
+ static_cast<uint64_t>(frame_index_));
+ if (subset_rect_.IsEmpty()) {
+ hash_ = original_hash;
+ } else {
+ size_t subset_hash =
+ base::HashInts(static_cast<uint64_t>(
+ base::HashInts(subset_rect_.x(), subset_rect_.y())),
+ static_cast<uint64_t>(base::HashInts(
+ subset_rect_.width(), subset_rect_.height())));
+ hash_ = base::HashInts(original_hash, subset_hash);
+ }
+}
+
+bool PaintImage::FrameKey::operator==(const FrameKey& other) const {
+ return paint_image_id_ == other.paint_image_id_ &&
+ content_id_ == other.content_id_ &&
+ frame_index_ == other.frame_index_ &&
+ subset_rect_ == other.subset_rect_;
+}
+
+bool PaintImage::FrameKey::operator!=(const FrameKey& other) const {
+ return !(*this == other);
+}
+
+std::string PaintImage::FrameKey::ToString() const {
+ std::ostringstream str;
+ str << "paint_image_id: " << paint_image_id_ << ","
+ << "content_id: " << content_id_ << ","
+ << "frame_index: " << frame_index_ << ","
+ << "subset_rect: " << subset_rect_.ToString();
+ return str.str();
+}
+
} // namespace cc
diff --git a/chromium/cc/paint/paint_image.h b/chromium/cc/paint/paint_image.h
index 40e59889a55..8586e680317 100644
--- a/chromium/cc/paint/paint_image.h
+++ b/chromium/cc/paint/paint_image.h
@@ -5,48 +5,80 @@
#ifndef CC_PAINT_PAINT_IMAGE_H_
#define CC_PAINT_PAINT_IMAGE_H_
+#include <vector>
+
+#include "base/gtest_prod_util.h"
#include "base/logging.h"
+#include "cc/paint/frame_metadata.h"
#include "cc/paint/paint_export.h"
+#include "cc/paint/skia_paint_image_generator.h"
#include "third_party/skia/include/core/SkImage.h"
+#include "ui/gfx/geometry/rect.h"
namespace cc {
-// TODO(vmpstr): Add a persistent id to the paint image.
+class PaintImageGenerator;
+class PaintOpBuffer;
+using PaintRecord = PaintOpBuffer;
+
+// A representation of an image for the compositor.
+// Note that aside from default construction, it can only be constructed using a
+// PaintImageBuilder, or copied/moved into using operator=.
class CC_PAINT_EXPORT PaintImage {
public:
using Id = int;
+ // A ContentId is used to identify the content for which images which can be
+ // lazily generated (generator/record backed images). As opposed to Id, which
+ // stays constant for the same image, the content id can be updated when the
+ // backing encoded data for this image changes. For instance, in the case of
+ // images which can be progressively updated as more encoded data is received.
+ using ContentId = int;
+
// An id that can be used for all non-lazy images. Note that if an image is
// not lazy, it does not mean that this id must be used; one can still use
// GetNextId to generate a stable id for such images.
static const Id kNonLazyStableId = -1;
- // This is the id used in places where we are currently not plumbing the
- // correct image id from blink.
- // TODO(khushalsagar): Eliminate these cases. See crbug.com/722559.
- static const Id kUnknownStableId = -2;
-
- // TODO(vmpstr): Work towards removing "UNKNOWN" value.
- enum class AnimationType { UNKNOWN, ANIMATED, VIDEO, STATIC };
-
- // TODO(vmpstr): Work towards removing "UNKNOWN" value.
- enum class CompletionState { UNKNOWN, DONE, PARTIALLY_DONE };
+ // The default frame index to use if no index is provided. For multi-frame
+ // images, this would imply the first frame of the animation.
+ static const size_t kDefaultFrameIndex = 0;
+
+ class CC_PAINT_EXPORT FrameKey {
+ public:
+ FrameKey(Id paint_image_id,
+ ContentId content_id,
+ size_t frame_index,
+ gfx::Rect subset_rect);
+ bool operator==(const FrameKey& other) const;
+ bool operator!=(const FrameKey& other) const;
+
+ uint64_t hash() const { return hash_; }
+ std::string ToString() const;
+
+ private:
+ Id paint_image_id_;
+ ContentId content_id_;
+ size_t frame_index_;
+ // TODO(khushalsagar): Remove this when callers take care of subsetting.
+ gfx::Rect subset_rect_;
+
+ size_t hash_;
+ };
+
+ struct CC_PAINT_EXPORT FrameKeyHash {
+ size_t operator()(const FrameKey& frame_key) const {
+ return frame_key.hash();
+ }
+ };
+
+ enum class AnimationType { ANIMATED, VIDEO, STATIC };
+ enum class CompletionState { DONE, PARTIALLY_DONE };
static Id GetNextId();
+ static ContentId GetNextContentId();
PaintImage();
- // - id: stable id for this image; can be generated using GetNextId().
- // - sk_image: the underlying skia image that this represents.
- // - animation_type: the animation type of this paint image.
- // - completion_state: indicates whether the image is completed loading.
- // - frame_count: the known number of frames in this image. E.g. number of GIF
- // frames in an animated GIF.
- explicit PaintImage(Id id,
- sk_sp<SkImage> sk_image,
- AnimationType animation_type = AnimationType::STATIC,
- CompletionState completion_state = CompletionState::DONE,
- size_t frame_count = 0,
- bool is_multipart = false);
PaintImage(const PaintImage& other);
PaintImage(PaintImage&& other);
~PaintImage();
@@ -54,28 +86,97 @@ class CC_PAINT_EXPORT PaintImage {
PaintImage& operator=(const PaintImage& other);
PaintImage& operator=(PaintImage&& other);
+ // Makes a new PaintImage representing a subset of the original image. The
+ // subset must be non-empty and lie within the image bounds.
+ PaintImage MakeSubset(const gfx::Rect& subset) const;
+
bool operator==(const PaintImage& other) const;
- explicit operator bool() const { return static_cast<bool>(sk_image_); }
+
+ // Returns the smallest size that is at least as big as the requested_size
+ // such that we can decode to exactly that scale. If the requested size is
+ // larger than the image, this returns the image size. Any returned value is
+ // guaranteed to be stable. That is,
+ // GetSupportedDecodeSize(GetSupportedDecodeSize(size)) is guaranteed to be
+ // GetSupportedDecodeSize(size).
+ SkISize GetSupportedDecodeSize(const SkISize& requested_size) const;
+
+ // Returns SkImageInfo that should be used to decode this image to the given
+ // size and color type. The size must be supported.
+ SkImageInfo CreateDecodeImageInfo(const SkISize& size,
+ SkColorType color_type) const;
+
+ // Decode the image into the given memory for the given SkImageInfo.
+ // - Size in |info| must be supported.
+ // - The amount of memory allocated must be at least
+ // |info|.minRowBytes() * |info|.height().
+ // Returns true on success and false on failure. Updates |info| to match the
+ // requested color space, if provided.
+ // Note that for non-lazy images this will do a copy or readback if the image
+ // is texture backed.
+ bool Decode(void* memory,
+ SkImageInfo* info,
+ sk_sp<SkColorSpace> color_space) const;
Id stable_id() const { return id_; }
- const sk_sp<SkImage>& sk_image() const { return sk_image_; }
+ const sk_sp<SkImage>& GetSkImage() const;
AnimationType animation_type() const { return animation_type_; }
CompletionState completion_state() const { return completion_state_; }
- size_t frame_count() const { return frame_count_; }
bool is_multipart() const { return is_multipart_; }
+ // TODO(vmpstr): Don't get the SkImage here if you don't need to.
+ uint32_t unique_id() const { return GetSkImage()->uniqueID(); }
+ explicit operator bool() const { return !!GetSkImage(); }
+ bool IsLazyGenerated() const { return GetSkImage()->isLazyGenerated(); }
+ int width() const { return GetSkImage()->width(); }
+ int height() const { return GetSkImage()->height(); }
+ SkColorSpace* color_space() const { return GetSkImage()->colorSpace(); }
+ size_t frame_index() const { return frame_index_; }
+
+ // Returns a unique id for the pixel data for the frame at |frame_index|. Used
+ // only for lazy-generated images.
+ FrameKey GetKeyForFrame(size_t frame_index) const;
+
+ // Returns the metadata for each frame of a multi-frame image. Should only be
+ // used with animated images.
+ const std::vector<FrameMetadata>& GetFrameMetadata() const;
+
+ // Returns the total number of frames known to exist in this image.
+ size_t FrameCount() const;
+
+ std::string ToString() const;
+
private:
- Id id_ = kUnknownStableId;
+ static const ContentId kInvalidContentId = -1;
+ friend class PaintImageBuilder;
+ FRIEND_TEST_ALL_PREFIXES(PaintImageTest, Subsetting);
+
sk_sp<SkImage> sk_image_;
- AnimationType animation_type_ = AnimationType::UNKNOWN;
- CompletionState completion_state_ = CompletionState::UNKNOWN;
- // The number of frames known to exist in this image (eg number of GIF frames
- // loaded). 0 indicates either unknown or only a single frame, both of which
- // should be treated similarly.
- size_t frame_count_ = 0;
+
+ sk_sp<PaintRecord> paint_record_;
+ gfx::Rect paint_record_rect_;
+ ContentId paint_record_content_id_ = kInvalidContentId;
+
+ sk_sp<PaintImageGenerator> paint_image_generator_;
+
+ Id id_ = 0;
+ AnimationType animation_type_ = AnimationType::STATIC;
+ CompletionState completion_state_ = CompletionState::DONE;
+
+ // If non-empty, holds the subset of this image relative to the original image
+ // at the origin.
+ gfx::Rect subset_rect_;
+
+ // The frame index to use when rasterizing this image.
+ size_t frame_index_ = kDefaultFrameIndex;
// Whether the data fetched for this image is a part of a multpart response.
bool is_multipart_ = false;
+
+ // |sk_image_id_| is the id used when constructing an SkImage representation
+ // for a generator backed image.
+ // TODO(khushalsagar): Remove the use of this uniqueID. See crbug.com/753639.
+ uint32_t sk_image_id_ = SkiaPaintImageGenerator::kNeedNewImageUniqueID;
+ mutable sk_sp<SkImage> cached_sk_image_;
};
} // namespace cc
diff --git a/chromium/cc/paint/paint_image_builder.cc b/chromium/cc/paint/paint_image_builder.cc
new file mode 100644
index 00000000000..b6fc8eefa82
--- /dev/null
+++ b/chromium/cc/paint/paint_image_builder.cc
@@ -0,0 +1,46 @@
+// 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_image_builder.h"
+
+namespace cc {
+
+PaintImageBuilder::PaintImageBuilder() = default;
+PaintImageBuilder::PaintImageBuilder(PaintImage image)
+ : paint_image_(std::move(image)) {
+#if DCHECK_IS_ON()
+ id_set_ = true;
+#endif
+ paint_image_.cached_sk_image_ = nullptr;
+ paint_image_.sk_image_ = nullptr;
+ paint_image_.paint_record_ = nullptr;
+ paint_image_.paint_record_rect_ = gfx::Rect();
+ paint_image_.paint_image_generator_ = nullptr;
+}
+PaintImageBuilder::~PaintImageBuilder() = default;
+
+PaintImage PaintImageBuilder::TakePaintImage() const {
+#if DCHECK_IS_ON()
+ DCHECK(id_set_);
+ if (paint_image_.sk_image_) {
+ DCHECK(!paint_image_.paint_record_);
+ DCHECK(!paint_image_.paint_image_generator_);
+ DCHECK(!paint_image_.sk_image_->isLazyGenerated());
+ // TODO(khushalsagar): Assert that we don't have an animated image type
+ // here.
+ } else if (paint_image_.paint_record_) {
+ DCHECK(!paint_image_.sk_image_);
+ DCHECK(!paint_image_.paint_image_generator_);
+ // TODO(khushalsagar): Assert that we don't have an animated image type
+ // here.
+ } else if (paint_image_.paint_image_generator_) {
+ DCHECK(!paint_image_.sk_image_);
+ DCHECK(!paint_image_.paint_record_);
+ }
+#endif
+
+ return std::move(paint_image_);
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/paint_image_builder.h b/chromium/cc/paint/paint_image_builder.h
new file mode 100644
index 00000000000..6585b798fc0
--- /dev/null
+++ b/chromium/cc/paint/paint_image_builder.h
@@ -0,0 +1,91 @@
+// 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_IMAGE_BUILDER_H_
+#define CC_PAINT_PAINT_IMAGE_BUILDER_H_
+
+#include "base/memory/ptr_util.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_image.h"
+#include "cc/paint/paint_image_generator.h"
+#include "cc/paint/paint_op_buffer.h"
+#include "cc/paint/skia_paint_image_generator.h"
+#include "third_party/skia/include/core/SkImage.h"
+
+namespace cc {
+
+// Class used to construct a paint image.
+class CC_PAINT_EXPORT PaintImageBuilder {
+ public:
+ PaintImageBuilder();
+ // Starts with the given image's flags. Note that this does _not_ keep the
+ // "contents" of the image. That is, it clears the cached SkImage, the set
+ // SkImage, the set PaintRecord, and any other content type variables.
+ explicit PaintImageBuilder(PaintImage starting_image);
+ ~PaintImageBuilder();
+
+ PaintImageBuilder& set_id(PaintImage::Id id) {
+ paint_image_.id_ = id;
+#if DCHECK_IS_ON()
+ id_set_ = true;
+#endif
+ return *this;
+ }
+
+ PaintImageBuilder& set_image(sk_sp<SkImage> sk_image) {
+ paint_image_.sk_image_ = std::move(sk_image);
+ return *this;
+ }
+ PaintImageBuilder& set_paint_record(sk_sp<PaintRecord> paint_record,
+ const gfx::Rect& rect,
+ PaintImage::ContentId content_id) {
+ DCHECK_NE(content_id, PaintImage::kInvalidContentId);
+
+ paint_image_.paint_record_ = std::move(paint_record);
+ paint_image_.paint_record_rect_ = rect;
+ paint_image_.paint_record_content_id_ = content_id;
+ return *this;
+ }
+ PaintImageBuilder& set_paint_image_generator(
+ sk_sp<PaintImageGenerator> generator) {
+ paint_image_.paint_image_generator_ = std::move(generator);
+ return *this;
+ }
+
+ PaintImageBuilder& set_animation_type(PaintImage::AnimationType type) {
+ paint_image_.animation_type_ = type;
+ return *this;
+ }
+ PaintImageBuilder& set_completion_state(PaintImage::CompletionState state) {
+ paint_image_.completion_state_ = state;
+ return *this;
+ }
+ PaintImageBuilder& set_is_multipart(bool is_multipart) {
+ paint_image_.is_multipart_ = is_multipart;
+ return *this;
+ }
+ PaintImageBuilder& set_frame_index(size_t frame_index) {
+ paint_image_.frame_index_ = frame_index;
+ return *this;
+ }
+
+ PaintImageBuilder& set_sk_image_id(uint32_t sk_image_id) {
+ paint_image_.sk_image_id_ = sk_image_id;
+ return *this;
+ }
+
+ PaintImage TakePaintImage() const;
+
+ private:
+ PaintImage paint_image_;
+#if DCHECK_IS_ON()
+ bool id_set_ = false;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(PaintImageBuilder);
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_PAINT_IMAGE_BUILDER_H_
diff --git a/chromium/cc/paint/paint_image_generator.cc b/chromium/cc/paint/paint_image_generator.cc
new file mode 100644
index 00000000000..bc60848b856
--- /dev/null
+++ b/chromium/cc/paint/paint_image_generator.cc
@@ -0,0 +1,36 @@
+// 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 <vector>
+
+#include "cc/paint/paint_image_generator.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkSize.h"
+
+namespace cc {
+
+PaintImageGenerator::PaintImageGenerator(const SkImageInfo& info,
+ std::vector<FrameMetadata> frames)
+ : info_(info),
+ generator_content_id_(PaintImage::GetNextContentId()),
+ frames_(std::move(frames)) {}
+
+PaintImageGenerator::~PaintImageGenerator() = default;
+
+PaintImage::ContentId PaintImageGenerator::GetContentIdForFrame(
+ size_t frame_index) const {
+ return generator_content_id_;
+}
+
+SkISize PaintImageGenerator::GetSupportedDecodeSize(
+ const SkISize& requested_size) const {
+ // The base class just returns the original size as the only supported decode
+ // size.
+ return info_.dimensions();
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/paint_image_generator.h b/chromium/cc/paint/paint_image_generator.h
new file mode 100644
index 00000000000..88e6368a6da
--- /dev/null
+++ b/chromium/cc/paint/paint_image_generator.h
@@ -0,0 +1,90 @@
+// 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_IMAGE_GENERATOR_H_
+#define CC_PAINT_PAINT_IMAGE_GENERATOR_H_
+
+#include <vector>
+
+#include "base/macros.h"
+#include "cc/paint/frame_metadata.h"
+#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_image.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/core/SkYUVSizeInfo.h"
+
+namespace cc {
+
+// PaintImage Generator is a wrapper to provide a lazily decoded PaintImage to
+// the compositor.
+// Note that the implementation of this class must ensure thread safety, it can
+// be called from any thread.
+class CC_PAINT_EXPORT PaintImageGenerator : public SkRefCnt {
+ public:
+ ~PaintImageGenerator() override;
+
+ // Returns a reference to the encoded content of this image.
+ virtual sk_sp<SkData> GetEncodedData() const = 0;
+
+ // Decode into the given pixels, a block of memory of size at least
+ // (info.fHeight - 1) * rowBytes + (info.fWidth * bytesPerPixel). |info|
+ // represents the desired output format. Returns true on success.
+ //
+ // TODO(khushalsagar): |lazy_pixel_ref| is only present for
+ // DecodingImageGenerator tracing needs. Remove it.
+ virtual bool GetPixels(const SkImageInfo& info,
+ void* pixels,
+ size_t row_bytes,
+ size_t frame_index,
+ uint32_t lazy_pixel_ref) = 0;
+
+ // Returns true if the generator supports YUV decoding, providing the output
+ // information in |info| and |color_space|.
+ virtual bool QueryYUV8(SkYUVSizeInfo* info,
+ SkYUVColorSpace* color_space) const = 0;
+
+ // Decodes to YUV into the provided |planes| for each of the Y, U, and V
+ // planes, and returns true on success. The method should only be used if
+ // QueryYUV8 returns true.
+ // |info| needs to exactly match the values returned by the query, except the
+ // WidthBytes may be larger than the recommendation (but not smaller).
+ //
+ // TODO(khushalsagar): |lazy_pixel_ref| is only present for
+ // DecodingImageGenerator tracing needs. Remove it.
+ virtual bool GetYUV8Planes(const SkYUVSizeInfo& info,
+ void* planes[3],
+ size_t frame_index,
+ uint32_t lazy_pixel_ref) = 0;
+
+ // Returns the smallest size that is at least as big as the requested size,
+ // such that we can decode to exactly that scale.
+ virtual SkISize GetSupportedDecodeSize(const SkISize& requested_size) const;
+
+ // Returns the content id to key the decoded output produced by this
+ // generator for a frame at |frame_index|. The generator promises that
+ // the output for repeated calls to decode a frame will be consistent across
+ // all generators for a PaintImage, if this function returns the same id.
+ virtual PaintImage::ContentId GetContentIdForFrame(size_t frame_index) const;
+
+ const SkImageInfo& GetSkImageInfo() const { return info_; }
+ const std::vector<FrameMetadata>& GetFrameMetadata() const { return frames_; }
+
+ protected:
+ // |info| is the info for this paint image generator.
+ PaintImageGenerator(const SkImageInfo& info,
+ std::vector<FrameMetadata> frames = {FrameMetadata()});
+
+ private:
+ const SkImageInfo info_;
+ const PaintImage::ContentId generator_content_id_;
+ const std::vector<FrameMetadata> frames_;
+
+ DISALLOW_COPY_AND_ASSIGN(PaintImageGenerator);
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_PAINT_IMAGE_GENERATOR_H_
diff --git a/chromium/cc/paint/paint_image_unittest.cc b/chromium/cc/paint/paint_image_unittest.cc
new file mode 100644
index 00000000000..0fe9a4ffda1
--- /dev/null
+++ b/chromium/cc/paint/paint_image_unittest.cc
@@ -0,0 +1,34 @@
+// 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_image.h"
+
+#include "base/test/gtest_util.h"
+#include "cc/test/skia_common.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+
+TEST(PaintImageTest, Subsetting) {
+ PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
+ EXPECT_EQ(image.width(), 100);
+ EXPECT_EQ(image.height(), 100);
+
+ PaintImage subset_rect_1 = image.MakeSubset(gfx::Rect(25, 25, 50, 50));
+ EXPECT_EQ(subset_rect_1.width(), 50);
+ EXPECT_EQ(subset_rect_1.height(), 50);
+ EXPECT_EQ(subset_rect_1.subset_rect_, gfx::Rect(25, 25, 50, 50));
+
+ PaintImage subset_rect_2 =
+ subset_rect_1.MakeSubset(gfx::Rect(25, 25, 25, 25));
+ EXPECT_EQ(subset_rect_2.width(), 25);
+ EXPECT_EQ(subset_rect_2.height(), 25);
+ EXPECT_EQ(subset_rect_2.subset_rect_, gfx::Rect(50, 50, 25, 25));
+
+ EXPECT_EQ(image, image.MakeSubset(gfx::Rect(100, 100)));
+ EXPECT_DCHECK_DEATH(subset_rect_2.MakeSubset(gfx::Rect(10, 10, 25, 25)));
+ EXPECT_DCHECK_DEATH(image.MakeSubset(gfx::Rect()));
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/paint_op_buffer.cc b/chromium/cc/paint/paint_op_buffer.cc
index 763e0cf5396..2cecf129957 100644
--- a/chromium/cc/paint/paint_op_buffer.cc
+++ b/chromium/cc/paint/paint_op_buffer.cc
@@ -5,137 +5,283 @@
#include "cc/paint/paint_op_buffer.h"
#include "base/containers/stack_container.h"
+#include "base/memory/ptr_util.h"
+#include "cc/paint/decoded_draw_image.h"
#include "cc/paint/display_item_list.h"
+#include "cc/paint/image_provider.h"
+#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_op_reader.h"
#include "cc/paint/paint_op_writer.h"
#include "cc/paint/paint_record.h"
#include "third_party/skia/include/core/SkAnnotation.h"
#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkRegion.h"
namespace cc {
+namespace {
-#define TYPES(M) \
- M(AnnotateOp) \
- M(ClipPathOp) \
- M(ClipRectOp) \
- M(ClipRRectOp) \
- M(ConcatOp) \
- M(DrawArcOp) \
- M(DrawCircleOp) \
- M(DrawColorOp) \
- M(DrawDRRectOp) \
- M(DrawImageOp) \
- M(DrawImageRectOp) \
- M(DrawIRectOp) \
- M(DrawLineOp) \
- M(DrawOvalOp) \
- M(DrawPathOp) \
- M(DrawPosTextOp) \
- M(DrawRecordOp) \
- M(DrawRectOp) \
- M(DrawRRectOp) \
- M(DrawTextOp) \
- M(DrawTextBlobOp) \
- M(NoopOp) \
- M(RestoreOp) \
- M(RotateOp) \
- M(SaveOp) \
- M(SaveLayerOp) \
- M(SaveLayerAlphaOp) \
- M(ScaleOp) \
- M(SetMatrixOp) \
- M(TranslateOp)
+bool IsImageShader(const PaintFlags& flags) {
+ return flags.HasShader() &&
+ flags.getShader()->shader_type() == PaintShader::Type::kImage;
+}
-using RasterFunction = void (*)(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm);
-using RasterWithFlagsFunction = void (*)(const PaintOpWithFlags* op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm);
-
-NOINLINE static void RasterWithAlphaInternal(RasterFunction raster_fn,
- const PaintOp* op,
- SkCanvas* canvas,
- const SkRect& bounds,
- uint8_t alpha) {
- bool unset = bounds.x() == PaintOp::kUnsetRect.x();
- canvas->saveLayerAlpha(unset ? nullptr : &bounds, alpha);
- SkMatrix unused_matrix;
- raster_fn(op, canvas, unused_matrix);
- canvas->restore();
+bool IsImageOp(const PaintOp* op) {
+ if (op->GetType() == PaintOpType::DrawImage)
+ return true;
+ else if (op->GetType() == PaintOpType::DrawImageRect)
+ return true;
+ else if (op->IsDrawOp() && op->IsPaintOpWithFlags())
+ return IsImageShader(static_cast<const PaintOpWithFlags*>(op)->flags);
+
+ return false;
+}
+
+bool QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) {
+ DCHECK(op->IsDrawOp());
+
+ SkRect rect;
+ if (!PaintOp::GetBounds(op, &rect))
+ return false;
+
+ if (op->IsPaintOpWithFlags()) {
+ SkPaint paint = static_cast<const PaintOpWithFlags*>(op)->flags.ToSkPaint();
+ if (!paint.canComputeFastBounds())
+ return false;
+ paint.computeFastBounds(rect, &rect);
+ }
+
+ return canvas->quickReject(rect);
}
-// Helper template to share common code for RasterWithAlpha when paint ops
-// have or don't have PaintFlags.
+// Encapsulates a ImageProvider::DecodedImageHolder and a SkPaint. Use of
+// this class ensures that the DecodedImageHolder outlives the dependent
+// SkPaint.
+class ScopedImageFlags {
+ public:
+ ScopedImageFlags(ImageProvider* image_provider,
+ const PaintFlags& flags,
+ const SkMatrix& ctm) {
+ DCHECK(IsImageShader(flags));
+
+ const PaintImage& paint_image = flags.getShader()->paint_image();
+ SkMatrix matrix = flags.getShader()->GetLocalMatrix();
+
+ SkMatrix total_image_matrix = matrix;
+ total_image_matrix.preConcat(ctm);
+ SkRect src_rect =
+ SkRect::MakeIWH(paint_image.width(), paint_image.height());
+ scoped_decoded_draw_image_ = image_provider->GetDecodedDrawImage(
+ paint_image, src_rect, flags.getFilterQuality(), total_image_matrix);
+
+ if (!scoped_decoded_draw_image_)
+ return;
+ const auto& decoded_image = scoped_decoded_draw_image_.decoded_image();
+ DCHECK(decoded_image.image());
+
+ bool need_scale = !decoded_image.is_scale_adjustment_identity();
+ if (need_scale) {
+ matrix.preScale(1.f / decoded_image.scale_adjustment().width(),
+ 1.f / decoded_image.scale_adjustment().height());
+ }
+
+ sk_sp<SkImage> sk_image =
+ sk_ref_sp<SkImage>(const_cast<SkImage*>(decoded_image.image().get()));
+ PaintImage decoded_paint_image = PaintImageBuilder()
+ .set_id(paint_image.stable_id())
+ .set_image(std::move(sk_image))
+ .TakePaintImage();
+ decoded_flags_.emplace(flags);
+ decoded_flags_.value().setFilterQuality(decoded_image.filter_quality());
+ decoded_flags_.value().setShader(
+ PaintShader::MakeImage(decoded_paint_image, flags.getShader()->tx(),
+ flags.getShader()->ty(), &matrix));
+ }
+
+ PaintFlags* decoded_flags() {
+ return decoded_flags_ ? &decoded_flags_.value() : nullptr;
+ }
+
+ ~ScopedImageFlags() = default;
+
+ private:
+ base::Optional<PaintFlags> decoded_flags_;
+ ImageProvider::ScopedDecodedDrawImage scoped_decoded_draw_image_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedImageFlags);
+};
+
+void RasterWithAlpha(const PaintOp* op,
+ SkCanvas* canvas,
+ const PlaybackParams& params,
+ const SkRect& bounds,
+ uint8_t alpha) {
+ DCHECK(op->IsDrawOp());
+ DCHECK_NE(op->GetType(), PaintOpType::DrawRecord);
+
+ // TODO(enne): partially specialize RasterWithAlpha for draw color?
+ if (op->IsPaintOpWithFlags()) {
+ auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
+
+ // Replace the PaintFlags with a copy that holds the decoded image from the
+ // ImageProvider if it consists of an image shader.
+ base::Optional<ScopedImageFlags> scoped_flags;
+ const PaintFlags* decoded_flags = &flags_op->flags;
+ if (params.image_provider && IsImageShader(flags_op->flags)) {
+ scoped_flags.emplace(params.image_provider, flags_op->flags,
+ canvas->getTotalMatrix());
+ decoded_flags = scoped_flags.value().decoded_flags();
+
+ // If we failed to decode the flags, skip the op.
+ if (!decoded_flags)
+ return;
+ }
+
+ if (!decoded_flags->SupportsFoldingAlpha()) {
+ canvas->saveLayerAlpha(PaintOp::IsUnsetRect(bounds) ? nullptr : &bounds,
+ alpha);
+ flags_op->RasterWithFlags(canvas, decoded_flags, params);
+ canvas->restore();
+ } else if (alpha == 255) {
+ flags_op->RasterWithFlags(canvas, decoded_flags, params);
+ } else {
+ if (scoped_flags.has_value()) {
+ // If we already made a copy, just use that to override the alpha
+ // instead of making another copy.
+ PaintFlags* decoded_flags = scoped_flags.value().decoded_flags();
+ decoded_flags->setAlpha(
+ SkMulDiv255Round(decoded_flags->getAlpha(), alpha));
+ flags_op->RasterWithFlags(canvas, decoded_flags, params);
+ } else {
+ PaintFlags alpha_flags = flags_op->flags;
+ alpha_flags.setAlpha(SkMulDiv255Round(alpha_flags.getAlpha(), alpha));
+ flags_op->RasterWithFlags(canvas, &alpha_flags, params);
+ }
+ }
+ } else if (op->GetType() == PaintOpType::DrawColor &&
+ static_cast<const DrawColorOp*>(op)->mode ==
+ SkBlendMode::kSrcOver) {
+ auto* draw_color_op = static_cast<const DrawColorOp*>(op);
+
+ SkColor color = draw_color_op->color;
+ canvas->drawColor(
+ SkColorSetARGB(SkMulDiv255Round(alpha, SkColorGetA(color)),
+ SkColorGetR(color), SkColorGetG(color),
+ SkColorGetB(color)),
+ draw_color_op->mode);
+ } else {
+ canvas->saveLayerAlpha(PaintOp::IsUnsetRect(bounds) ? nullptr : &bounds,
+ alpha);
+ op->Raster(canvas, params);
+ canvas->restore();
+ }
+}
+
+} // namespace
+
+#define TYPES(M) \
+ M(AnnotateOp) \
+ M(ClipPathOp) \
+ M(ClipRectOp) \
+ M(ClipRRectOp) \
+ M(ConcatOp) \
+ M(DrawColorOp) \
+ M(DrawDRRectOp) \
+ M(DrawImageOp) \
+ M(DrawImageRectOp) \
+ M(DrawIRectOp) \
+ M(DrawLineOp) \
+ M(DrawOvalOp) \
+ M(DrawPathOp) \
+ M(DrawRecordOp) \
+ M(DrawRectOp) \
+ M(DrawRRectOp) \
+ M(DrawTextBlobOp) \
+ M(NoopOp) \
+ M(RestoreOp) \
+ M(RotateOp) \
+ M(SaveOp) \
+ M(SaveLayerOp) \
+ M(SaveLayerAlphaOp) \
+ M(ScaleOp) \
+ M(SetMatrixOp) \
+ M(TranslateOp)
+
+static constexpr size_t kNumOpTypes =
+ static_cast<size_t>(PaintOpType::LastPaintOpType) + 1;
+
+// Verify that every op is in the TYPES macro.
+#define M(T) +1
+static_assert(kNumOpTypes == TYPES(M), "Missing op in list");
+#undef M
+
template <typename T, bool HasFlags>
struct Rasterizer {
- static void RasterWithAlpha(const T* op,
+ static void RasterWithFlags(const T* op,
+ const PaintFlags* flags,
SkCanvas* canvas,
- const SkRect& bounds,
- uint8_t alpha) {
+ const PlaybackParams& params) {
static_assert(
!T::kHasPaintFlags,
"This function should not be used for a PaintOp that has PaintFlags");
- DCHECK(T::kIsDrawOp);
- RasterWithAlphaInternal(&T::Raster, op, canvas, bounds, alpha);
+ DCHECK(op->IsValid());
+ NOTREACHED();
}
-};
-
-NOINLINE static void RasterWithAlphaInternalForFlags(
- RasterWithFlagsFunction raster_fn,
- const PaintOpWithFlags* op,
- SkCanvas* canvas,
- const SkRect& bounds,
- uint8_t alpha) {
- SkMatrix unused_matrix;
- if (!op->flags.SupportsFoldingAlpha()) {
- bool unset = bounds.x() == PaintOp::kUnsetRect.x();
- canvas->saveLayerAlpha(unset ? nullptr : &bounds, alpha);
- raster_fn(op, &op->flags, canvas, unused_matrix);
- canvas->restore();
- } else if (alpha == 255) {
- raster_fn(op, &op->flags, canvas, unused_matrix);
- } else {
- PaintFlags flags = op->flags;
- flags.setAlpha(SkMulDiv255Round(flags.getAlpha(), alpha));
- raster_fn(op, &flags, canvas, unused_matrix);
+ static void Raster(const T* op,
+ SkCanvas* canvas,
+ const PlaybackParams& params) {
+ static_assert(
+ !T::kHasPaintFlags,
+ "This function should not be used for a PaintOp that has PaintFlags");
+ DCHECK(op->IsValid());
+ T::Raster(op, canvas, params);
}
-}
+};
template <typename T>
struct Rasterizer<T, true> {
- static void RasterWithAlpha(const T* op,
+ static void RasterWithFlags(const T* op,
+ const PaintFlags* flags,
SkCanvas* canvas,
- const SkRect& bounds,
- uint8_t alpha) {
+ const PlaybackParams& params) {
static_assert(T::kHasPaintFlags,
"This function expects the PaintOp to have PaintFlags");
- DCHECK(T::kIsDrawOp);
- RasterWithAlphaInternalForFlags(&T::RasterWithFlags, op, canvas, bounds,
- alpha);
+ DCHECK(op->IsValid());
+ T::RasterWithFlags(op, flags, canvas, params);
}
-};
-// These should never be used, as we should recurse into them to draw their
-// contained op with alpha instead.
-template <bool HasFlags>
-struct Rasterizer<DrawRecordOp, HasFlags> {
- static void RasterWithAlpha(const DrawRecordOp* op,
- SkCanvas* canvas,
- const SkRect& bounds,
- uint8_t alpha) {
- NOTREACHED();
+ static void Raster(const T* op,
+ SkCanvas* canvas,
+ const PlaybackParams& params) {
+ static_assert(T::kHasPaintFlags,
+ "This function expects the PaintOp to have PaintFlags");
+ DCHECK(op->IsValid());
+ T::RasterWithFlags(op, &op->flags, canvas, params);
}
};
-// TODO(enne): partially specialize RasterWithAlpha for draw color?
-static constexpr size_t kNumOpTypes =
- static_cast<size_t>(PaintOpType::LastPaintOpType) + 1;
+using RasterFunction = void (*)(const PaintOp* op,
+ SkCanvas* canvas,
+ const PlaybackParams& params);
+#define M(T) \
+ [](const PaintOp* op, SkCanvas* canvas, const PlaybackParams& params) { \
+ Rasterizer<T, T::kHasPaintFlags>::Raster(static_cast<const T*>(op), \
+ canvas, params); \
+ },
+static const RasterFunction g_raster_functions[kNumOpTypes] = {TYPES(M)};
+#undef M
-// Verify that every op is in the TYPES macro.
-#define M(T) +1
-static_assert(kNumOpTypes == TYPES(M), "Missing op in list");
+using RasterWithFlagsFunction = void (*)(const PaintOp* op,
+ const PaintFlags* flags,
+ SkCanvas* canvas,
+ const PlaybackParams& params);
+#define M(T) \
+ [](const PaintOp* op, const PaintFlags* flags, SkCanvas* canvas, \
+ const PlaybackParams& params) { \
+ Rasterizer<T, T::kHasPaintFlags>::RasterWithFlags( \
+ static_cast<const T*>(op), flags, canvas, params); \
+ },
+static const RasterWithFlagsFunction
+ g_raster_with_flags_functions[kNumOpTypes] = {TYPES(M)};
#undef M
using SerializeFunction = size_t (*)(const PaintOp* op,
@@ -146,7 +292,7 @@ using SerializeFunction = size_t (*)(const PaintOp* op,
static const SerializeFunction g_serialize_functions[kNumOpTypes] = {TYPES(M)};
#undef M
-using DeserializeFunction = PaintOp* (*)(const void* input,
+using DeserializeFunction = PaintOp* (*)(const volatile void* input,
size_t input_size,
void* output,
size_t output_size);
@@ -156,27 +302,6 @@ static const DeserializeFunction g_deserialize_functions[kNumOpTypes] = {
TYPES(M)};
#undef M
-using RasterFunction = void (*)(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm);
-#define M(T) &T::Raster,
-static const RasterFunction g_raster_functions[kNumOpTypes] = {TYPES(M)};
-#undef M
-
-using RasterAlphaFunction = void (*)(const PaintOp* op,
- SkCanvas* canvas,
- const SkRect& bounds,
- uint8_t alpha);
-#define M(T) \
- T::kIsDrawOp ? [](const PaintOp* op, SkCanvas* canvas, const SkRect& bounds, \
- uint8_t alpha) { \
- Rasterizer<T, T::kHasPaintFlags>::RasterWithAlpha( \
- static_cast<const T*>(op), canvas, bounds, alpha); \
- } : static_cast<RasterAlphaFunction>(nullptr),
-static const RasterAlphaFunction g_raster_alpha_functions[kNumOpTypes] = {
- TYPES(M)};
-#undef M
-
// Most state ops (matrix, clip, save, restore) have a trivial destructor.
// TODO(enne): evaluate if we need the nullptr optimization or if
// we even need to differentiate trivial destructors here.
@@ -192,6 +317,10 @@ static const VoidFunction g_destructor_functions[kNumOpTypes] = {TYPES(M)};
static bool g_is_draw_op[kNumOpTypes] = {TYPES(M)};
#undef M
+#define M(T) T::kHasPaintFlags,
+static bool g_has_paint_flags[kNumOpTypes] = {TYPES(M)};
+#undef M
+
#define M(T) \
static_assert(sizeof(T) <= sizeof(LargestPaintOp), \
#T " must be no bigger than LargestPaintOp");
@@ -206,7 +335,7 @@ TYPES(M);
#undef TYPES
-SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0};
+const SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0};
const size_t PaintOp::kMaxSkip;
std::string PaintOpTypeToString(PaintOpType type) {
@@ -221,10 +350,6 @@ std::string PaintOpTypeToString(PaintOpType type) {
return "ClipRRect";
case PaintOpType::Concat:
return "Concat";
- case PaintOpType::DrawArc:
- return "DrawArc";
- case PaintOpType::DrawCircle:
- return "DrawCircle";
case PaintOpType::DrawColor:
return "DrawColor";
case PaintOpType::DrawDRRect:
@@ -241,16 +366,12 @@ std::string PaintOpTypeToString(PaintOpType type) {
return "DrawOval";
case PaintOpType::DrawPath:
return "DrawPath";
- case PaintOpType::DrawPosText:
- return "DrawPosText";
case PaintOpType::DrawRecord:
return "DrawRecord";
case PaintOpType::DrawRect:
return "DrawRect";
case PaintOpType::DrawRRect:
return "DrawRRect";
- case PaintOpType::DrawText:
- return "DrawText";
case PaintOpType::DrawTextBlob:
return "DrawTextBlob";
case PaintOpType::Noop:
@@ -283,6 +404,10 @@ size_t SimpleSerialize(const PaintOp* op, void* memory, size_t size) {
return sizeof(T);
}
+PlaybackParams::PlaybackParams(ImageProvider* image_provider,
+ const SkMatrix& original_ctm)
+ : image_provider(image_provider), original_ctm(original_ctm) {}
+
size_t AnnotateOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
@@ -328,33 +453,6 @@ size_t ConcatOp::Serialize(const PaintOp* op,
return SimpleSerialize<ConcatOp>(op, memory, size);
}
-size_t DrawArcOp::Serialize(const PaintOp* base_op,
- void* memory,
- size_t size,
- const SerializeOptions& options) {
- auto* op = static_cast<const DrawArcOp*>(base_op);
- PaintOpWriter helper(memory, size);
- helper.Write(op->flags);
- helper.Write(op->oval);
- helper.Write(op->start_angle);
- helper.Write(op->sweep_angle);
- helper.Write(op->use_center);
- return helper.size();
-}
-
-size_t DrawCircleOp::Serialize(const PaintOp* base_op,
- void* memory,
- size_t size,
- const SerializeOptions& options) {
- auto* op = static_cast<const DrawCircleOp*>(base_op);
- PaintOpWriter helper(memory, size);
- helper.Write(op->flags);
- helper.Write(op->cx);
- helper.Write(op->cy);
- helper.Write(op->radius);
- return helper.size();
-}
-
size_t DrawColorOp::Serialize(const PaintOp* op,
void* memory,
size_t size,
@@ -448,20 +546,6 @@ size_t DrawPathOp::Serialize(const PaintOp* base_op,
return helper.size();
}
-size_t DrawPosTextOp::Serialize(const PaintOp* base_op,
- void* memory,
- size_t size,
- const SerializeOptions& options) {
- auto* op = static_cast<const DrawPosTextOp*>(base_op);
- PaintOpWriter helper(memory, size);
- helper.Write(op->flags);
- helper.Write(op->count);
- helper.Write(op->bytes);
- helper.WriteArray(op->count, op->GetArray());
- helper.WriteData(op->bytes, op->GetData());
- return helper.size();
-}
-
size_t DrawRecordOp::Serialize(const PaintOp* op,
void* memory,
size_t size,
@@ -494,20 +578,6 @@ size_t DrawRRectOp::Serialize(const PaintOp* base_op,
return helper.size();
}
-size_t DrawTextOp::Serialize(const PaintOp* base_op,
- void* memory,
- size_t size,
- const SerializeOptions& options) {
- auto* op = static_cast<const DrawTextOp*>(base_op);
- PaintOpWriter helper(memory, size);
- helper.Write(op->flags);
- helper.Write(op->x);
- helper.Write(op->y);
- helper.Write(op->bytes);
- helper.WriteData(op->bytes, op->GetData());
- return helper.size();
-}
-
size_t DrawTextBlobOp::Serialize(const PaintOp* base_op,
void* memory,
size_t size,
@@ -595,33 +665,35 @@ void UpdateTypeAndSkip(T* op) {
}
template <typename T>
-PaintOp* SimpleDeserialize(const void* input,
- size_t input_size,
- void* output,
- size_t output_size) {
+T* SimpleDeserialize(const volatile void* input,
+ size_t input_size,
+ void* output,
+ size_t output_size) {
if (input_size < sizeof(T))
return nullptr;
- memcpy(output, input, sizeof(T));
+ memcpy(output, const_cast<void*>(input), sizeof(T));
T* op = reinterpret_cast<T*>(output);
+ if (!op->IsValid())
+ return nullptr;
// Type and skip were already read once, so could have been changed.
// Don't trust them and clobber them with something valid.
UpdateTypeAndSkip(op);
return op;
}
-PaintOp* AnnotateOp::Deserialize(const void* input,
+PaintOp* AnnotateOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(AnnotateOp));
+ DCHECK_GE(output_size, sizeof(AnnotateOp));
AnnotateOp* op = new (output) AnnotateOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->annotation_type);
helper.Read(&op->rect);
helper.Read(&op->data);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~AnnotateOp();
return nullptr;
}
@@ -630,18 +702,18 @@ PaintOp* AnnotateOp::Deserialize(const void* input,
return op;
}
-PaintOp* ClipPathOp::Deserialize(const void* input,
+PaintOp* ClipPathOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(ClipPathOp));
+ DCHECK_GE(output_size, sizeof(ClipPathOp));
ClipPathOp* op = new (output) ClipPathOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->path);
helper.Read(&op->op);
helper.Read(&op->antialias);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~ClipPathOp();
return nullptr;
}
@@ -650,87 +722,50 @@ PaintOp* ClipPathOp::Deserialize(const void* input,
return op;
}
-PaintOp* ClipRectOp::Deserialize(const void* input,
+PaintOp* ClipRectOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(ClipRectOp));
return SimpleDeserialize<ClipRectOp>(input, input_size, output, output_size);
}
-PaintOp* ClipRRectOp::Deserialize(const void* input,
+PaintOp* ClipRRectOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(ClipRRectOp));
return SimpleDeserialize<ClipRRectOp>(input, input_size, output, output_size);
}
-PaintOp* ConcatOp::Deserialize(const void* input,
+PaintOp* ConcatOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(ConcatOp));
return SimpleDeserialize<ConcatOp>(input, input_size, output, output_size);
}
-PaintOp* DrawArcOp::Deserialize(const void* input,
- size_t input_size,
- void* output,
- size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawArcOp));
- DrawArcOp* op = new (output) DrawArcOp;
-
- PaintOpReader helper(input, input_size);
- helper.Read(&op->flags);
- helper.Read(&op->oval);
- helper.Read(&op->start_angle);
- helper.Read(&op->sweep_angle);
- helper.Read(&op->use_center);
- if (!helper.valid()) {
- op->~DrawArcOp();
- return nullptr;
- }
- UpdateTypeAndSkip(op);
- return op;
-}
-
-PaintOp* DrawCircleOp::Deserialize(const void* input,
- size_t input_size,
- void* output,
- size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawCircleOp));
- DrawCircleOp* op = new (output) DrawCircleOp;
-
- PaintOpReader helper(input, input_size);
- helper.Read(&op->flags);
- helper.Read(&op->cx);
- helper.Read(&op->cy);
- helper.Read(&op->radius);
- if (!helper.valid()) {
- op->~DrawCircleOp();
- return nullptr;
- }
- UpdateTypeAndSkip(op);
- return op;
-}
-
-PaintOp* DrawColorOp::Deserialize(const void* input,
+PaintOp* DrawColorOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(DrawColorOp));
return SimpleDeserialize<DrawColorOp>(input, input_size, output, output_size);
}
-PaintOp* DrawDRRectOp::Deserialize(const void* input,
+PaintOp* DrawDRRectOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawDRRectOp));
+ DCHECK_GE(output_size, sizeof(DrawDRRectOp));
DrawDRRectOp* op = new (output) DrawDRRectOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->flags);
helper.Read(&op->outer);
helper.Read(&op->inner);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawDRRectOp();
return nullptr;
}
@@ -738,11 +773,11 @@ PaintOp* DrawDRRectOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawImageOp::Deserialize(const void* input,
+PaintOp* DrawImageOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawImageOp));
+ DCHECK_GE(output_size, sizeof(DrawImageOp));
DrawImageOp* op = new (output) DrawImageOp;
PaintOpReader helper(input, input_size);
@@ -750,7 +785,7 @@ PaintOp* DrawImageOp::Deserialize(const void* input,
helper.Read(&op->image);
helper.Read(&op->left);
helper.Read(&op->top);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawImageOp();
return nullptr;
}
@@ -758,11 +793,11 @@ PaintOp* DrawImageOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawImageRectOp::Deserialize(const void* input,
+PaintOp* DrawImageRectOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawImageRectOp));
+ DCHECK_GE(output_size, sizeof(DrawImageRectOp));
DrawImageRectOp* op = new (output) DrawImageRectOp;
PaintOpReader helper(input, input_size);
@@ -771,7 +806,7 @@ PaintOp* DrawImageRectOp::Deserialize(const void* input,
helper.Read(&op->src);
helper.Read(&op->dst);
helper.Read(&op->constraint);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawImageRectOp();
return nullptr;
}
@@ -779,17 +814,17 @@ PaintOp* DrawImageRectOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawIRectOp::Deserialize(const void* input,
+PaintOp* DrawIRectOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawIRectOp));
+ DCHECK_GE(output_size, sizeof(DrawIRectOp));
DrawIRectOp* op = new (output) DrawIRectOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->flags);
helper.Read(&op->rect);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawIRectOp();
return nullptr;
}
@@ -797,11 +832,11 @@ PaintOp* DrawIRectOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawLineOp::Deserialize(const void* input,
+PaintOp* DrawLineOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawLineOp));
+ DCHECK_GE(output_size, sizeof(DrawLineOp));
DrawLineOp* op = new (output) DrawLineOp;
PaintOpReader helper(input, input_size);
@@ -810,7 +845,7 @@ PaintOp* DrawLineOp::Deserialize(const void* input,
helper.Read(&op->y0);
helper.Read(&op->x1);
helper.Read(&op->y1);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawLineOp();
return nullptr;
}
@@ -818,17 +853,17 @@ PaintOp* DrawLineOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawOvalOp::Deserialize(const void* input,
+PaintOp* DrawOvalOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawOvalOp));
+ DCHECK_GE(output_size, sizeof(DrawOvalOp));
DrawOvalOp* op = new (output) DrawOvalOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->flags);
helper.Read(&op->oval);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawOvalOp();
return nullptr;
}
@@ -836,17 +871,17 @@ PaintOp* DrawOvalOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawPathOp::Deserialize(const void* input,
+PaintOp* DrawPathOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawPathOp));
+ DCHECK_GE(output_size, sizeof(DrawPathOp));
DrawPathOp* op = new (output) DrawPathOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->flags);
helper.Read(&op->path);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawPathOp();
return nullptr;
}
@@ -854,61 +889,26 @@ PaintOp* DrawPathOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawPosTextOp::Deserialize(const void* input,
- size_t input_size,
- void* output,
- size_t output_size) {
- // TODO(enne): This is a bit of a weird condition, but to avoid the code
- // complexity of every Deserialize function being able to (re)allocate
- // an aligned buffer of the right size, this function asserts that it
- // will have enough size for the extra data. It's guaranteed that any extra
- // memory is at most |input_size| so that plus the op size is an upper bound.
- // The caller has to awkwardly do this allocation though, sorry.
- CHECK_GE(output_size, sizeof(DrawPosTextOp) + input_size);
- DrawPosTextOp* op = new (output) DrawPosTextOp;
-
- PaintOpReader helper(input, input_size);
- helper.Read(&op->flags);
- helper.Read(&op->count);
- helper.Read(&op->bytes);
- if (helper.valid()) {
- helper.ReadArray(op->count, op->GetArray());
- helper.ReadData(op->bytes, op->GetData());
- }
- if (!helper.valid()) {
- op->~DrawPosTextOp();
- return nullptr;
- }
-
- op->type = static_cast<uint8_t>(PaintOpType::DrawPosText);
- op->skip = MathUtil::UncheckedRoundUp(
- sizeof(DrawPosTextOp) + op->bytes + sizeof(SkPoint) * op->count,
- PaintOpBuffer::PaintOpAlign);
-
- return op;
-}
-
-PaintOp* DrawRecordOp::Deserialize(const void* input,
+PaintOp* DrawRecordOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
// TODO(enne): these must be flattened and not sent directly.
// TODO(enne): could also consider caching these service side.
- NOTREACHED();
return nullptr;
}
-PaintOp* DrawRectOp::Deserialize(const void* input,
+PaintOp* DrawRectOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawRectOp));
+ DCHECK_GE(output_size, sizeof(DrawRectOp));
DrawRectOp* op = new (output) DrawRectOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->flags);
helper.Read(&op->rect);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawRectOp();
return nullptr;
}
@@ -916,17 +916,17 @@ PaintOp* DrawRectOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawRRectOp::Deserialize(const void* input,
+PaintOp* DrawRRectOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawRRectOp));
+ DCHECK_GE(output_size, sizeof(DrawRRectOp));
DrawRRectOp* op = new (output) DrawRRectOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->flags);
helper.Read(&op->rrect);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawRRectOp();
return nullptr;
}
@@ -934,36 +934,11 @@ PaintOp* DrawRRectOp::Deserialize(const void* input,
return op;
}
-PaintOp* DrawTextOp::Deserialize(const void* input,
- size_t input_size,
- void* output,
- size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawTextOp) + input_size);
- DrawTextOp* op = new (output) DrawTextOp;
-
- PaintOpReader helper(input, input_size);
- helper.Read(&op->flags);
- helper.Read(&op->x);
- helper.Read(&op->y);
- helper.Read(&op->bytes);
- if (helper.valid())
- helper.ReadData(op->bytes, op->GetData());
- if (!helper.valid()) {
- op->~DrawTextOp();
- return nullptr;
- }
-
- op->type = static_cast<uint8_t>(PaintOpType::DrawText);
- op->skip = MathUtil::UncheckedRoundUp(sizeof(DrawTextOp) + op->bytes,
- PaintOpBuffer::PaintOpAlign);
- return op;
-}
-
-PaintOp* DrawTextBlobOp::Deserialize(const void* input,
+PaintOp* DrawTextBlobOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(DrawTextBlobOp));
+ DCHECK_GE(output_size, sizeof(DrawTextBlobOp));
DrawTextBlobOp* op = new (output) DrawTextBlobOp;
PaintOpReader helper(input, input_size);
@@ -971,7 +946,7 @@ PaintOp* DrawTextBlobOp::Deserialize(const void* input,
helper.Read(&op->x);
helper.Read(&op->y);
helper.Read(&op->blob);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~DrawTextBlobOp();
return nullptr;
}
@@ -979,45 +954,49 @@ PaintOp* DrawTextBlobOp::Deserialize(const void* input,
return op;
}
-PaintOp* NoopOp::Deserialize(const void* input,
+PaintOp* NoopOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(NoopOp));
return SimpleDeserialize<NoopOp>(input, input_size, output, output_size);
}
-PaintOp* RestoreOp::Deserialize(const void* input,
+PaintOp* RestoreOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(RestoreOp));
return SimpleDeserialize<RestoreOp>(input, input_size, output, output_size);
}
-PaintOp* RotateOp::Deserialize(const void* input,
+PaintOp* RotateOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(RotateOp));
return SimpleDeserialize<RotateOp>(input, input_size, output, output_size);
}
-PaintOp* SaveOp::Deserialize(const void* input,
+PaintOp* SaveOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(SaveOp));
return SimpleDeserialize<SaveOp>(input, input_size, output, output_size);
}
-PaintOp* SaveLayerOp::Deserialize(const void* input,
+PaintOp* SaveLayerOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
- CHECK_GE(output_size, sizeof(SaveLayerOp));
+ DCHECK_GE(output_size, sizeof(SaveLayerOp));
SaveLayerOp* op = new (output) SaveLayerOp;
PaintOpReader helper(input, input_size);
helper.Read(&op->flags);
helper.Read(&op->bounds);
- if (!helper.valid()) {
+ if (!helper.valid() || !op->IsValid()) {
op->~SaveLayerOp();
return nullptr;
}
@@ -1025,39 +1004,43 @@ PaintOp* SaveLayerOp::Deserialize(const void* input,
return op;
}
-PaintOp* SaveLayerAlphaOp::Deserialize(const void* input,
+PaintOp* SaveLayerAlphaOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(SaveLayerAlphaOp));
return SimpleDeserialize<SaveLayerAlphaOp>(input, input_size, output,
output_size);
}
-PaintOp* ScaleOp::Deserialize(const void* input,
+PaintOp* ScaleOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(ScaleOp));
+
return SimpleDeserialize<ScaleOp>(input, input_size, output, output_size);
}
-PaintOp* SetMatrixOp::Deserialize(const void* input,
+PaintOp* SetMatrixOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(SetMatrixOp));
return SimpleDeserialize<SetMatrixOp>(input, input_size, output, output_size);
}
-PaintOp* TranslateOp::Deserialize(const void* input,
+PaintOp* TranslateOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
size_t output_size) {
+ DCHECK_GE(output_size, sizeof(TranslateOp));
return SimpleDeserialize<TranslateOp>(input, input_size, output, output_size);
}
-void AnnotateOp::Raster(const PaintOp* base_op,
+void AnnotateOp::Raster(const AnnotateOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const AnnotateOp*>(base_op);
+ const PlaybackParams& params) {
switch (op->annotation_type) {
case PaintCanvas::AnnotationType::URL:
SkAnnotateRectWithURL(canvas, op->rect, op->data.get());
@@ -1073,215 +1056,219 @@ void AnnotateOp::Raster(const PaintOp* base_op,
}
}
-void ClipPathOp::Raster(const PaintOp* base_op,
+void ClipPathOp::Raster(const ClipPathOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const ClipPathOp*>(base_op);
+ const PlaybackParams& params) {
canvas->clipPath(op->path, op->op, op->antialias);
}
-void ClipRectOp::Raster(const PaintOp* base_op,
+void ClipRectOp::Raster(const ClipRectOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const ClipRectOp*>(base_op);
+ const PlaybackParams& params) {
canvas->clipRect(op->rect, op->op, op->antialias);
}
-void ClipRRectOp::Raster(const PaintOp* base_op,
+void ClipRRectOp::Raster(const ClipRRectOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const ClipRRectOp*>(base_op);
+ const PlaybackParams& params) {
canvas->clipRRect(op->rrect, op->op, op->antialias);
}
-void ConcatOp::Raster(const PaintOp* base_op,
+void ConcatOp::Raster(const ConcatOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const ConcatOp*>(base_op);
+ const PlaybackParams& params) {
canvas->concat(op->matrix);
}
-void DrawArcOp::RasterWithFlags(const PaintOpWithFlags* base_op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawArcOp*>(base_op);
- SkPaint paint = flags->ToSkPaint();
- canvas->drawArc(op->oval, op->start_angle, op->sweep_angle, op->use_center,
- paint);
-}
-
-void DrawCircleOp::RasterWithFlags(const PaintOpWithFlags* base_op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawCircleOp*>(base_op);
- SkPaint paint = flags->ToSkPaint();
- canvas->drawCircle(op->cx, op->cy, op->radius, paint);
-}
-
-void DrawColorOp::Raster(const PaintOp* base_op,
+void DrawColorOp::Raster(const DrawColorOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawColorOp*>(base_op);
+ const PlaybackParams& params) {
canvas->drawColor(op->color, op->mode);
}
-void DrawDRRectOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawDRRectOp::RasterWithFlags(const DrawDRRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawDRRectOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
canvas->drawDRRect(op->outer, op->inner, paint);
}
-void DrawImageOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawImageOp::RasterWithFlags(const DrawImageOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawImageOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
- canvas->drawImage(op->image.sk_image().get(), op->left, op->top, &paint);
+
+ if (!params.image_provider) {
+ canvas->drawImage(op->image.GetSkImage().get(), op->left, op->top, &paint);
+ return;
+ }
+
+ SkRect image_rect = SkRect::MakeIWH(op->image.width(), op->image.height());
+ auto scoped_decoded_draw_image = params.image_provider->GetDecodedDrawImage(
+ op->image, image_rect,
+ flags ? flags->getFilterQuality() : kNone_SkFilterQuality,
+ canvas->getTotalMatrix());
+ if (!scoped_decoded_draw_image)
+ return;
+
+ const auto& decoded_image = scoped_decoded_draw_image.decoded_image();
+ DCHECK(decoded_image.image());
+
+ DCHECK_EQ(0, static_cast<int>(decoded_image.src_rect_offset().width()));
+ DCHECK_EQ(0, static_cast<int>(decoded_image.src_rect_offset().height()));
+ bool need_scale = !decoded_image.is_scale_adjustment_identity();
+ if (need_scale) {
+ canvas->save();
+ canvas->scale(1.f / (decoded_image.scale_adjustment().width()),
+ 1.f / (decoded_image.scale_adjustment().height()));
+ }
+
+ paint.setFilterQuality(decoded_image.filter_quality());
+ canvas->drawImage(decoded_image.image().get(), op->left, op->top, &paint);
+ if (need_scale)
+ canvas->restore();
}
-void DrawImageRectOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawImageRectOp::RasterWithFlags(const DrawImageRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawImageRectOp*>(base_op);
+ const PlaybackParams& params) {
// TODO(enne): Probably PaintCanvas should just use the skia enum directly.
SkCanvas::SrcRectConstraint skconstraint =
static_cast<SkCanvas::SrcRectConstraint>(op->constraint);
SkPaint paint = flags->ToSkPaint();
- canvas->drawImageRect(op->image.sk_image().get(), op->src, op->dst, &paint,
- skconstraint);
+
+ if (!params.image_provider) {
+ canvas->drawImageRect(op->image.GetSkImage().get(), op->src, op->dst,
+ &paint, skconstraint);
+ return;
+ }
+
+ SkMatrix matrix;
+ matrix.setRectToRect(op->src, op->dst, SkMatrix::kFill_ScaleToFit);
+ matrix.postConcat(canvas->getTotalMatrix());
+
+ auto scoped_decoded_draw_image = params.image_provider->GetDecodedDrawImage(
+ op->image, op->src,
+ flags ? flags->getFilterQuality() : kNone_SkFilterQuality, matrix);
+ if (!scoped_decoded_draw_image)
+ return;
+
+ const auto& decoded_image = scoped_decoded_draw_image.decoded_image();
+ DCHECK(decoded_image.image());
+
+ SkRect adjusted_src =
+ op->src.makeOffset(decoded_image.src_rect_offset().width(),
+ decoded_image.src_rect_offset().height());
+ if (!decoded_image.is_scale_adjustment_identity()) {
+ float x_scale = decoded_image.scale_adjustment().width();
+ float y_scale = decoded_image.scale_adjustment().height();
+ adjusted_src = SkRect::MakeXYWH(
+ adjusted_src.x() * x_scale, adjusted_src.y() * y_scale,
+ adjusted_src.width() * x_scale, adjusted_src.height() * y_scale);
+ }
+
+ paint.setFilterQuality(decoded_image.filter_quality());
+ canvas->drawImageRect(decoded_image.image().get(), adjusted_src, op->dst,
+ &paint, skconstraint);
}
-void DrawIRectOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawIRectOp::RasterWithFlags(const DrawIRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawIRectOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
canvas->drawIRect(op->rect, paint);
}
-void DrawLineOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawLineOp::RasterWithFlags(const DrawLineOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawLineOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
canvas->drawLine(op->x0, op->y0, op->x1, op->y1, paint);
}
-void DrawOvalOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawOvalOp::RasterWithFlags(const DrawOvalOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawOvalOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
canvas->drawOval(op->oval, paint);
}
-void DrawPathOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawPathOp::RasterWithFlags(const DrawPathOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawPathOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
canvas->drawPath(op->path, paint);
}
-void DrawPosTextOp::RasterWithFlags(const PaintOpWithFlags* base_op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawPosTextOp*>(base_op);
- SkPaint paint = flags->ToSkPaint();
- canvas->drawPosText(op->GetData(), op->bytes, op->GetArray(), paint);
-}
-
-void DrawRecordOp::Raster(const PaintOp* base_op,
+void DrawRecordOp::Raster(const DrawRecordOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
+ const PlaybackParams& params) {
// Don't use drawPicture here, as it adds an implicit clip.
- auto* op = static_cast<const DrawRecordOp*>(base_op);
- op->record->Playback(canvas);
+ op->record->Playback(canvas, params.image_provider);
}
-void DrawRectOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawRectOp::RasterWithFlags(const DrawRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawRectOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
canvas->drawRect(op->rect, paint);
}
-void DrawRRectOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawRRectOp::RasterWithFlags(const DrawRRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawRRectOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
canvas->drawRRect(op->rrect, paint);
}
-void DrawTextOp::RasterWithFlags(const PaintOpWithFlags* base_op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawTextOp*>(base_op);
- SkPaint paint = flags->ToSkPaint();
- canvas->drawText(op->GetData(), op->bytes, op->x, op->y, paint);
-}
-
-void DrawTextBlobOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void DrawTextBlobOp::RasterWithFlags(const DrawTextBlobOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const DrawTextBlobOp*>(base_op);
+ const PlaybackParams& params) {
SkPaint paint = flags->ToSkPaint();
canvas->drawTextBlob(op->blob.get(), op->x, op->y, paint);
}
-void RestoreOp::Raster(const PaintOp* base_op,
+void RestoreOp::Raster(const RestoreOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
+ const PlaybackParams& params) {
canvas->restore();
}
-void RotateOp::Raster(const PaintOp* base_op,
+void RotateOp::Raster(const RotateOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const RotateOp*>(base_op);
+ const PlaybackParams& params) {
canvas->rotate(op->degrees);
}
-void SaveOp::Raster(const PaintOp* base_op,
+void SaveOp::Raster(const SaveOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
+ const PlaybackParams& params) {
canvas->save();
}
-void SaveLayerOp::RasterWithFlags(const PaintOpWithFlags* base_op,
+void SaveLayerOp::RasterWithFlags(const SaveLayerOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const SaveLayerOp*>(base_op);
+ const PlaybackParams& params) {
// See PaintOp::kUnsetRect
- bool unset = op->bounds.left() == SK_ScalarInfinity;
-
SkPaint paint = flags->ToSkPaint();
+ bool unset = op->bounds.left() == SK_ScalarInfinity;
canvas->saveLayer(unset ? nullptr : &op->bounds, &paint);
}
-void SaveLayerAlphaOp::Raster(const PaintOp* base_op,
+void SaveLayerAlphaOp::Raster(const SaveLayerAlphaOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const SaveLayerAlphaOp*>(base_op);
+ const PlaybackParams& params) {
// See PaintOp::kUnsetRect
bool unset = op->bounds.left() == SK_ScalarInfinity;
if (op->preserve_lcd_text_requests) {
@@ -1294,24 +1281,21 @@ void SaveLayerAlphaOp::Raster(const PaintOp* base_op,
}
}
-void ScaleOp::Raster(const PaintOp* base_op,
+void ScaleOp::Raster(const ScaleOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const ScaleOp*>(base_op);
+ const PlaybackParams& params) {
canvas->scale(op->sx, op->sy);
}
-void SetMatrixOp::Raster(const PaintOp* base_op,
+void SetMatrixOp::Raster(const SetMatrixOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const SetMatrixOp*>(base_op);
- canvas->setMatrix(SkMatrix::Concat(original_ctm, op->matrix));
+ const PlaybackParams& params) {
+ canvas->setMatrix(SkMatrix::Concat(params.original_ctm, op->matrix));
}
-void TranslateOp::Raster(const PaintOp* base_op,
+void TranslateOp::Raster(const TranslateOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* op = static_cast<const TranslateOp*>(base_op);
+ const PlaybackParams& params) {
canvas->translate(op->dx, op->dy);
}
@@ -1319,14 +1303,12 @@ bool PaintOp::IsDrawOp() const {
return g_is_draw_op[type];
}
-void PaintOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const {
- g_raster_functions[type](this, canvas, original_ctm);
+bool PaintOp::IsPaintOpWithFlags() const {
+ return g_has_paint_flags[type];
}
-void PaintOp::RasterWithAlpha(SkCanvas* canvas,
- const SkRect& bounds,
- uint8_t alpha) const {
- g_raster_alpha_functions[type](this, canvas, bounds, alpha);
+void PaintOp::Raster(SkCanvas* canvas, const PlaybackParams& params) const {
+ g_raster_functions[type](this, canvas, params);
}
size_t PaintOp::Serialize(void* memory,
@@ -1357,23 +1339,102 @@ size_t PaintOp::Serialize(void* memory,
return skip;
}
-PaintOp* PaintOp::Deserialize(const void* input,
+PaintOp* PaintOp::Deserialize(const volatile void* input,
size_t input_size,
void* output,
- size_t output_size) {
- // TODO(enne): assert that output_size is big enough.
- const PaintOp* serialized = reinterpret_cast<const PaintOp*>(input);
- uint32_t skip = serialized->skip;
+ size_t output_size,
+ size_t* read_bytes) {
+ DCHECK_GE(output_size, sizeof(LargestPaintOp));
+
+ uint32_t first_word = reinterpret_cast<const volatile uint32_t*>(input)[0];
+ uint8_t type = static_cast<uint8_t>(first_word & 0xFF);
+ uint32_t skip = first_word >> 8;
+
if (input_size < skip)
return nullptr;
if (skip % PaintOpBuffer::PaintOpAlign != 0)
return nullptr;
- uint8_t type = serialized->type;
if (type > static_cast<uint8_t>(PaintOpType::LastPaintOpType))
return nullptr;
+ *read_bytes = skip;
+ return g_deserialize_functions[type](input, skip, output, output_size);
+}
+
+// static
+bool PaintOp::GetBounds(const PaintOp* op, SkRect* rect) {
+ DCHECK(op->IsDrawOp());
- return g_deserialize_functions[serialized->type](input, skip, output,
- output_size);
+ switch (op->GetType()) {
+ case PaintOpType::DrawColor:
+ return false;
+ case PaintOpType::DrawDRRect: {
+ auto* rect_op = static_cast<const DrawDRRectOp*>(op);
+ *rect = rect_op->outer.getBounds();
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawImage: {
+ auto* image_op = static_cast<const DrawImageOp*>(op);
+ *rect =
+ SkRect::MakeXYWH(image_op->left, image_op->top,
+ image_op->image.width(), image_op->image.height());
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawImageRect: {
+ auto* image_rect_op = static_cast<const DrawImageRectOp*>(op);
+ *rect = image_rect_op->dst;
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawIRect: {
+ auto* rect_op = static_cast<const DrawIRectOp*>(op);
+ *rect = SkRect::Make(rect_op->rect);
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawLine: {
+ auto* line_op = static_cast<const DrawLineOp*>(op);
+ rect->set(line_op->x0, line_op->y0, line_op->x1, line_op->y1);
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawOval: {
+ auto* oval_op = static_cast<const DrawOvalOp*>(op);
+ *rect = oval_op->oval;
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawPath: {
+ auto* path_op = static_cast<const DrawPathOp*>(op);
+ *rect = path_op->path.getBounds();
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawRect: {
+ auto* rect_op = static_cast<const DrawRectOp*>(op);
+ *rect = rect_op->rect;
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawRRect: {
+ auto* rect_op = static_cast<const DrawRRectOp*>(op);
+ *rect = rect_op->rrect.rect();
+ rect->sort();
+ return true;
+ }
+ case PaintOpType::DrawRecord:
+ return false;
+ case PaintOpType::DrawTextBlob: {
+ auto* text_op = static_cast<const DrawTextBlobOp*>(op);
+ *rect = text_op->blob->bounds().makeOffset(text_op->x, text_op->y);
+ rect->sort();
+ return true;
+ }
+ default:
+ NOTREACHED();
+ }
+ return false;
}
void PaintOp::DestroyThis() {
@@ -1382,6 +1443,12 @@ void PaintOp::DestroyThis() {
func(this);
}
+void PaintOpWithFlags::RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags* flags,
+ const PlaybackParams& params) const {
+ g_raster_with_flags_functions[type](this, flags, canvas, params);
+}
+
int ClipPathOp::CountSlowPaths() const {
return antialias && !path.isConvex() ? 1 : 0;
}
@@ -1450,7 +1517,7 @@ DrawImageOp::DrawImageOp(const PaintImage& image,
bool DrawImageOp::HasDiscardableImages() const {
// TODO(khushalsagar): Callers should not be able to change the lazy generated
// state for a PaintImage.
- return image.sk_image()->isLazyGenerated();
+ return image.IsLazyGenerated();
}
DrawImageOp::~DrawImageOp() = default;
@@ -1469,20 +1536,11 @@ DrawImageRectOp::DrawImageRectOp(const PaintImage& image,
constraint(constraint) {}
bool DrawImageRectOp::HasDiscardableImages() const {
- return image.sk_image()->isLazyGenerated();
+ return image.IsLazyGenerated();
}
DrawImageRectOp::~DrawImageRectOp() = default;
-DrawPosTextOp::DrawPosTextOp() = default;
-
-DrawPosTextOp::DrawPosTextOp(size_t bytes,
- size_t count,
- const PaintFlags& flags)
- : PaintOpWithArray(flags, bytes, count) {}
-
-DrawPosTextOp::~DrawPosTextOp() = default;
-
DrawRecordOp::DrawRecordOp() = default;
DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record)
@@ -1508,6 +1566,21 @@ DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob,
DrawTextBlobOp::~DrawTextBlobOp() = default;
+PaintOpBuffer::CompositeIterator::CompositeIterator(
+ const PaintOpBuffer* buffer,
+ const std::vector<size_t>* offsets)
+ : using_offsets_(!!offsets) {
+ if (using_offsets_)
+ offset_iter_.emplace(buffer, offsets);
+ else
+ iter_.emplace(buffer);
+}
+
+PaintOpBuffer::CompositeIterator::CompositeIterator(
+ const CompositeIterator& other) = default;
+PaintOpBuffer::CompositeIterator::CompositeIterator(CompositeIterator&& other) =
+ default;
+
PaintOpBuffer::PaintOpBuffer()
: has_non_aa_paint_(false), has_discardable_images_(false) {}
@@ -1526,6 +1599,7 @@ void PaintOpBuffer::operator=(PaintOpBuffer&& other) {
op_count_ = other.op_count_;
num_slow_paths_ = other.num_slow_paths_;
subrecord_bytes_used_ = other.subrecord_bytes_used_;
+ has_non_aa_paint_ = other.has_non_aa_paint_;
has_discardable_images_ = other.has_discardable_images_;
// Make sure the other pob can destruct safely.
@@ -1538,10 +1612,14 @@ void PaintOpBuffer::Reset() {
for (auto* op : Iterator(this))
op->DestroyThis();
- // Leave data_ allocated, reserved_ unchanged.
+ // Leave data_ allocated, reserved_ unchanged. ShrinkToFit will take care of
+ // that if called.
used_ = 0;
op_count_ = 0;
num_slow_paths_ = 0;
+ has_non_aa_paint_ = false;
+ subrecord_bytes_used_ = 0;
+ has_discardable_images_ = false;
}
// When |op| is a nested PaintOpBuffer, this returns the PaintOp inside
@@ -1571,11 +1649,18 @@ static const PaintOp* GetNestedSingleDrawingOp(const PaintOp* op) {
}
void PaintOpBuffer::Playback(SkCanvas* canvas,
+ ImageProvider* image_provider,
+ SkPicture::AbortCallback* callback) const {
+ Playback(canvas, image_provider, callback, nullptr);
+}
+
+void PaintOpBuffer::Playback(SkCanvas* canvas,
+ ImageProvider* image_provider,
SkPicture::AbortCallback* callback,
- const std::vector<size_t>* indices) const {
+ const std::vector<size_t>* offsets) const {
if (!op_count_)
return;
- if (indices && indices->empty())
+ if (offsets && offsets->empty())
return;
// Prevent PaintOpBuffers from having side effects back into the canvas.
@@ -1586,11 +1671,11 @@ void PaintOpBuffer::Playback(SkCanvas* canvas,
// translate(x, y), then draw a paint record with a SetMatrix(identity),
// the translation should be preserved instead of clobbering the top level
// transform. This could probably be done more efficiently.
- SkMatrix original = canvas->getTotalMatrix();
+ PlaybackParams params(image_provider, canvas->getTotalMatrix());
// FIFO queue of paint ops that have been peeked at.
base::StackVector<const PaintOp*, 3> stack;
- Iterator iter(this, indices);
+ CompositeIterator iter(this, offsets);
auto next_op = [&stack, &iter]() -> const PaintOp* {
if (stack->size()) {
const PaintOp* op = stack->front();
@@ -1630,10 +1715,30 @@ void PaintOpBuffer::Playback(SkCanvas* canvas,
const PaintOp* draw_op = GetNestedSingleDrawingOp(second);
if (draw_op) {
+ // This is an optimization to replicate the behaviour in SkCanvas
+ // which rejects ops that draw outside the current clip. In the
+ // general case we defer this to the SkCanvas but if we will be
+ // using an ImageProvider for pre-decoding images, we can save
+ // performing an expensive decode that will never be rasterized.
+ const bool skip_op = params.image_provider && IsImageOp(draw_op) &&
+ QuickRejectDraw(draw_op, canvas);
+ if (skip_op) {
+ // Now that we know this op will be skipped, we can push the save
+ // layer op back to the stack and continue iterating .
+ // In the case with the following list of ops:
+ // [SaveLayer, DrawImage, DrawRect, Restore], where draw_op is the
+ // DrawImage op, this starts the iteration again from SaveLayer and
+ // eliminates the DrawImage op.
+ DCHECK(stack->empty());
+ stack->push_back(op);
+ continue;
+ }
+
third = next_op();
if (third && third->GetType() == PaintOpType::Restore) {
auto* save_op = static_cast<const SaveLayerAlphaOp*>(op);
- draw_op->RasterWithAlpha(canvas, save_op->bounds, save_op->alpha);
+ RasterWithAlpha(draw_op, canvas, params, save_op->bounds,
+ save_op->alpha);
continue;
}
}
@@ -1644,12 +1749,32 @@ void PaintOpBuffer::Playback(SkCanvas* canvas,
stack->push_back(third);
}
}
+
+ if (params.image_provider && IsImageOp(op)) {
+ if (QuickRejectDraw(op, canvas))
+ continue;
+
+ auto* flags_op = op->IsPaintOpWithFlags()
+ ? static_cast<const PaintOpWithFlags*>(op)
+ : nullptr;
+ if (flags_op && IsImageShader(flags_op->flags)) {
+ ScopedImageFlags scoped_flags(image_provider, flags_op->flags,
+ canvas->getTotalMatrix());
+
+ // Only rasterize the op if we successfully decoded the image.
+ if (scoped_flags.decoded_flags()) {
+ flags_op->RasterWithFlags(canvas, scoped_flags.decoded_flags(),
+ params);
+ }
+ continue;
+ }
+ }
+
// TODO(enne): skip SaveLayer followed by restore with nothing in
// between, however SaveLayer with image filters on it (or maybe
// other PaintFlags options) are not a noop. Figure out what these
// are so we can skip them correctly.
-
- op->Raster(canvas, original);
+ op->Raster(canvas, params);
}
}
@@ -1657,16 +1782,16 @@ void PaintOpBuffer::ReallocBuffer(size_t new_size) {
DCHECK_GE(new_size, used_);
std::unique_ptr<char, base::AlignedFreeDeleter> new_data(
static_cast<char*>(base::AlignedAlloc(new_size, PaintOpAlign)));
- memcpy(new_data.get(), data_.get(), used_);
+ if (data_)
+ memcpy(new_data.get(), data_.get(), used_);
data_ = std::move(new_data);
reserved_ = new_size;
}
-std::pair<void*, size_t> PaintOpBuffer::AllocatePaintOp(size_t sizeof_op,
- size_t bytes) {
+std::pair<void*, size_t> PaintOpBuffer::AllocatePaintOp(size_t sizeof_op) {
// Compute a skip such that all ops in the buffer are aligned to the
// maximum required alignment of all ops.
- size_t skip = MathUtil::UncheckedRoundUp(sizeof_op + bytes, PaintOpAlign);
+ size_t skip = MathUtil::UncheckedRoundUp(sizeof_op, PaintOpAlign);
DCHECK_LT(skip, PaintOp::kMaxSkip);
if (used_ + skip > reserved_) {
// Start reserved_ at kInitialBufferSize and then double.
@@ -1685,9 +1810,59 @@ std::pair<void*, size_t> PaintOpBuffer::AllocatePaintOp(size_t sizeof_op,
}
void PaintOpBuffer::ShrinkToFit() {
- if (!used_ || used_ == reserved_)
+ if (used_ == reserved_)
return;
- ReallocBuffer(used_);
+ if (!used_) {
+ reserved_ = 0;
+ data_.reset();
+ } else {
+ ReallocBuffer(used_);
+ }
+}
+
+PaintOpBuffer::FlatteningIterator::FlatteningIterator(
+ const PaintOpBuffer* buffer,
+ const std::vector<size_t>* offsets)
+ : top_level_iter_(buffer, offsets) {
+ FlattenCurrentOpIfNeeded();
}
+void PaintOpBuffer::FlatteningIterator::FlattenCurrentOpIfNeeded() {
+ // At the top of the loop, the last nested iterator (or the top if no
+ // nested) is pointing at the current op. Advance through iterators,
+ // flattening draw record ops as we go until this gets to a non
+ // DrawRecordOp (which could be the current op).
+ while (true) {
+ // If there aren't nested iterators and the top level iterator is
+ // at its end, then we're done with all ops.
+ if (nested_iter_.empty() && !top_level_iter_)
+ return;
+
+ // If the nested iterator is not valid, then we've reached the end of
+ // whatever current iterator we're looping through.
+ if (!nested_iter_.empty() && !nested_iter_.back()) {
+ // Pop the current iterator. Now, the last iterator is currently
+ // pointing at whatever DrawRecordOp created the iterator that was
+ // just popped, so increment it to go to the next op.
+ nested_iter_.pop_back();
+ if (nested_iter_.empty())
+ ++top_level_iter_;
+ else
+ ++nested_iter_.back();
+ continue;
+ }
+
+ PaintOp* op = **this;
+ DCHECK(op);
+ if (op->GetType() != PaintOpType::DrawRecord)
+ return;
+ // If the current op is a draw record, then push another iterator for that
+ // record on the stack, and loop again to see what's in this new record.
+ auto* record_op = static_cast<DrawRecordOp*>(op);
+ nested_iter_.push_back(Iterator(record_op->record.get()));
+ }
+}
+
+PaintOpBuffer::FlatteningIterator::~FlatteningIterator() = default;
+
} // namespace cc
diff --git a/chromium/cc/paint/paint_op_buffer.h b/chromium/cc/paint/paint_op_buffer.h
index 4d7560cfa5d..17405ecac86 100644
--- a/chromium/cc/paint/paint_op_buffer.h
+++ b/chromium/cc/paint/paint_op_buffer.h
@@ -8,10 +8,12 @@
#include <stdint.h>
#include <string>
+#include <type_traits>
#include "base/debug/alias.h"
#include "base/logging.h"
#include "base/memory/aligned_memory.h"
+#include "base/optional.h"
#include "cc/base/math_util.h"
#include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_export.h"
@@ -26,8 +28,8 @@
// See: third_party/skia/src/core/SkLiteDL.h.
namespace cc {
-
class ImageDecodeCache;
+class ImageProvider;
class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix {
public:
@@ -47,10 +49,10 @@ class CC_PAINT_EXPORT ThreadsafePath : public SkPath {
// See PaintOp::Serialize/Deserialize for comments. Derived Serialize types
// don't write the 4 byte type/skip header because they don't know how much
// data they will need to write. PaintOp::Serialize itself must update it.
-#define HAS_SERIALIZATION_FUNCTIONS() \
- static size_t Serialize(const PaintOp* op, void* memory, size_t size, \
- const SerializeOptions& options); \
- static PaintOp* Deserialize(const void* input, size_t input_size, \
+#define HAS_SERIALIZATION_FUNCTIONS() \
+ static size_t Serialize(const PaintOp* op, void* memory, size_t size, \
+ const SerializeOptions& options); \
+ static PaintOp* Deserialize(const volatile void* input, size_t input_size, \
void* output, size_t output_size);
enum class PaintOpType : uint8_t {
@@ -59,8 +61,6 @@ enum class PaintOpType : uint8_t {
ClipRect,
ClipRRect,
Concat,
- DrawArc,
- DrawCircle,
DrawColor,
DrawDRRect,
DrawImage,
@@ -69,11 +69,9 @@ enum class PaintOpType : uint8_t {
DrawLine,
DrawOval,
DrawPath,
- DrawPosText,
DrawRecord,
DrawRect,
DrawRRect,
- DrawText,
DrawTextBlob,
Noop,
Restore,
@@ -89,6 +87,12 @@ enum class PaintOpType : uint8_t {
CC_PAINT_EXPORT std::string PaintOpTypeToString(PaintOpType type);
+struct CC_PAINT_EXPORT PlaybackParams {
+ PlaybackParams(ImageProvider* image_provider, const SkMatrix& original_ctm);
+ ImageProvider* image_provider;
+ const SkMatrix original_ctm;
+};
+
class CC_PAINT_EXPORT PaintOp {
public:
uint32_t type : 8;
@@ -100,8 +104,10 @@ class CC_PAINT_EXPORT PaintOp {
// here. The Raster method should take a const PaintOp* parameter. It is
// static with a pointer to the base type so that we can use it as a function
// pointer.
- void Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const;
+ void Raster(SkCanvas* canvas, const PlaybackParams& params) const;
bool IsDrawOp() const;
+ bool IsPaintOpWithFlags() const;
+ bool IsValid() const;
struct SerializeOptions {
ImageDecodeCache* decode_cache = nullptr;
@@ -120,15 +126,16 @@ class CC_PAINT_EXPORT PaintOp {
// if valid. nullptr is returned if the deserialization fails.
// |output_size| must be at least LargestPaintOp + serialized->skip,
// to fit all ops. The caller is responsible for destroying these ops.
- static PaintOp* Deserialize(const void* input,
+ // After reading, it returns the number of bytes read in |read_bytes|.
+ static PaintOp* Deserialize(const volatile void* input,
size_t input_size,
void* output,
- size_t output_size);
+ size_t output_size,
+ size_t* read_bytes);
- // Only valid for draw ops.
- void RasterWithAlpha(SkCanvas* canvas,
- const SkRect& bounds,
- uint8_t alpha) const;
+ // For draw ops, returns true if a conservative bounding rect can be provided
+ // for the op.
+ static bool GetBounds(const PaintOp* op, SkRect* rect);
int CountSlowPaths() const { return 0; }
int CountSlowPathsFromFlags() const { return 0; }
@@ -146,11 +153,39 @@ class CC_PAINT_EXPORT PaintOp {
// memory buffers and so don't have their destructors run automatically.
void DestroyThis();
+ // DrawColor is more restrictive on the blend modes that can be used.
+ static bool IsValidDrawColorSkBlendMode(SkBlendMode mode) {
+ return static_cast<uint32_t>(mode) <=
+ static_cast<uint32_t>(SkBlendMode::kLastCoeffMode);
+ }
+
+ // PaintFlags can have more complex blend modes than DrawColor.
+ static bool IsValidPaintFlagsSkBlendMode(SkBlendMode mode) {
+ return static_cast<uint32_t>(mode) <=
+ static_cast<uint32_t>(SkBlendMode::kLastMode);
+ }
+
+ static bool IsValidSkClipOp(SkClipOp op) {
+ return static_cast<uint32_t>(op) <=
+ static_cast<uint32_t>(SkClipOp::kMax_EnumValue);
+ }
+
+ static bool IsValidPath(const SkPath& path) {
+ return path.isValid() && path.pathRefIsValid();
+ }
+
+ static bool IsUnsetRect(const SkRect& rect) {
+ return rect.fLeft == SK_ScalarInfinity;
+ }
+ static bool IsValidOrUnsetRect(const SkRect& rect) {
+ return IsUnsetRect(rect) || rect.isFinite();
+ }
+
static constexpr bool kIsDrawOp = false;
static constexpr bool kHasPaintFlags = false;
// Since skip and type fit in a uint32_t, this is the max size of skip.
static constexpr size_t kMaxSkip = static_cast<size_t>(1 << 24);
- static SkRect kUnsetRect;
+ static const SkRect kUnsetRect;
};
class CC_PAINT_EXPORT PaintOpWithFlags : public PaintOp {
@@ -169,8 +204,12 @@ class CC_PAINT_EXPORT PaintOpWithFlags : public PaintOp {
return image && image->isLazyGenerated();
}
+ void RasterWithFlags(SkCanvas* canvas,
+ const PaintFlags* flags,
+ const PlaybackParams& params) const;
+
// Subclasses should provide a static RasterWithFlags() method which is called
- // from the Raster() method. The RasterWithFlags() should use the PaintFlags
+ // from the Raster() method. The RasterWithFlags() should use the SkPaint
// passed to it, instead of the |flags| member directly, as some callers may
// provide a modified PaintFlags. The RasterWithFlags() method is static with
// a const PaintOpWithFlags* parameter so that it can be used as a function
@@ -181,112 +220,6 @@ class CC_PAINT_EXPORT PaintOpWithFlags : public PaintOp {
PaintOpWithFlags() = default;
};
-class CC_PAINT_EXPORT PaintOpWithData : public PaintOpWithFlags {
- public:
- // Having data is just a helper for ops that have a varying amount of data and
- // want a way to store that inline. This is for ops that pass in a
- // void* and a length. The void* data is assumed to not have any alignment
- // requirements.
- PaintOpWithData(const PaintFlags& flags, size_t bytes)
- : PaintOpWithFlags(flags), bytes(bytes) {}
-
- // Get data out by calling paint_op_data. This can't be part of the class
- // because it needs to know the size of the derived type.
- size_t bytes;
-
- protected:
- PaintOpWithData() = default;
-
- // For some derived object T, return the internally stored data.
- // This needs the fully derived type to know how much to offset
- // from the start of the top to the data.
- template <typename T>
- const void* GetDataForThis(const T* op) const {
- static_assert(std::is_convertible<T, PaintOpWithData>::value,
- "T is not a PaintOpWithData");
- // Arbitrary data for a PaintOp is stored after the PaintOp itself
- // in the PaintOpBuffer. Therefore, to access this data, it's
- // pointer math to increment past the size of T. Accessing the
- // next op in the buffer is ((char*)op) + op->skip, with the data
- // fitting between.
- return op + 1;
- }
-
- template <typename T>
- void* GetDataForThis(T* op) {
- return const_cast<void*>(
- const_cast<const PaintOpWithData*>(this)->GetDataForThis(
- const_cast<const T*>(op)));
- }
-};
-
-class CC_PAINT_EXPORT PaintOpWithArrayBase : public PaintOpWithFlags {
- public:
- explicit PaintOpWithArrayBase(const PaintFlags& flags)
- : PaintOpWithFlags(flags) {}
-
- protected:
- PaintOpWithArrayBase() = default;
-};
-
-template <typename M>
-class CC_PAINT_EXPORT PaintOpWithArray : public PaintOpWithArrayBase {
- public:
- // Paint op that has a M[count] and a char[bytes].
- // Array data is stored first so that it can be aligned with T's alignment
- // with the arbitrary unaligned char data after it.
- // Memory layout here is: | op | M[count] | char[bytes] | padding | next op |
- // Next op is located at (char*)(op) + op->skip.
- PaintOpWithArray(const PaintFlags& flags, size_t bytes, size_t count)
- : PaintOpWithArrayBase(flags), bytes(bytes), count(count) {}
-
- size_t bytes;
- size_t count;
-
- protected:
- PaintOpWithArray() = default;
-
- template <typename T>
- const void* GetDataForThis(const T* op) const {
- static_assert(std::is_convertible<T, PaintOpWithArrayBase>::value,
- "T is not a PaintOpWithData");
- const char* start_array =
- reinterpret_cast<const char*>(GetArrayForThis(op));
- return start_array + sizeof(M) * count;
- }
-
- template <typename T>
- void* GetDataForThis(T* op) {
- return const_cast<void*>(
- const_cast<const PaintOpWithArray*>(this)->GetDataForThis(
- const_cast<T*>(op)));
- }
-
- template <typename T>
- const M* GetArrayForThis(const T* op) const {
- static_assert(std::is_convertible<T, PaintOpWithArrayBase>::value,
- "T is not a PaintOpWithData");
- // As an optimization to not have to store an additional offset,
- // assert that T has the same or more alignment requirements than M. Thus,
- // if T is aligned, and M's alignment needs are a multiple of T's size, then
- // M will also be aligned when placed immediately after T.
- static_assert(
- sizeof(T) % alignof(M) == 0,
- "T must be padded such that an array of M is aligned after it");
- static_assert(
- alignof(T) >= alignof(M),
- "T must have not have less alignment requirements than the array data");
- return reinterpret_cast<const M*>(op + 1);
- }
-
- template <typename T>
- M* GetArrayForThis(T* op) {
- return const_cast<M*>(
- const_cast<const PaintOpWithArray*>(this)->GetArrayForThis(
- const_cast<T*>(op)));
- }
-};
-
class CC_PAINT_EXPORT AnnotateOp final : public PaintOp {
public:
enum class AnnotationType {
@@ -300,9 +233,10 @@ class CC_PAINT_EXPORT AnnotateOp final : public PaintOp {
const SkRect& rect,
sk_sp<SkData> data);
~AnnotateOp();
- static void Raster(const PaintOp* op,
+ static void Raster(const AnnotateOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return rect.isFinite(); }
HAS_SERIALIZATION_FUNCTIONS();
PaintCanvas::AnnotationType annotation_type;
@@ -318,9 +252,10 @@ class CC_PAINT_EXPORT ClipPathOp final : public PaintOp {
static constexpr PaintOpType kType = PaintOpType::ClipPath;
ClipPathOp(SkPath path, SkClipOp op, bool antialias)
: path(path), op(op), antialias(antialias) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const ClipPathOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return IsValidSkClipOp(op) && IsValidPath(path); }
int CountSlowPaths() const;
bool HasNonAAPaint() const { return !antialias; }
HAS_SERIALIZATION_FUNCTIONS();
@@ -338,9 +273,10 @@ class CC_PAINT_EXPORT ClipRectOp final : public PaintOp {
static constexpr PaintOpType kType = PaintOpType::ClipRect;
ClipRectOp(const SkRect& rect, SkClipOp op, bool antialias)
: rect(rect), op(op), antialias(antialias) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const ClipRectOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return IsValidSkClipOp(op) && rect.isFinite(); }
HAS_SERIALIZATION_FUNCTIONS();
SkRect rect;
@@ -356,9 +292,10 @@ class CC_PAINT_EXPORT ClipRRectOp final : public PaintOp {
static constexpr PaintOpType kType = PaintOpType::ClipRRect;
ClipRRectOp(const SkRRect& rrect, SkClipOp op, bool antialias)
: rrect(rrect), op(op), antialias(antialias) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const ClipRRectOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return IsValidSkClipOp(op) && rrect.isValid(); }
bool HasNonAAPaint() const { return !antialias; }
HAS_SERIALIZATION_FUNCTIONS();
@@ -374,9 +311,10 @@ class CC_PAINT_EXPORT ConcatOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::Concat;
explicit ConcatOp(const SkMatrix& matrix) : matrix(matrix) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const ConcatOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
ThreadsafeMatrix matrix;
@@ -385,78 +323,15 @@ class CC_PAINT_EXPORT ConcatOp final : public PaintOp {
ConcatOp() = default;
};
-class CC_PAINT_EXPORT DrawArcOp final : public PaintOpWithFlags {
- public:
- static constexpr PaintOpType kType = PaintOpType::DrawArc;
- static constexpr bool kIsDrawOp = true;
- DrawArcOp(const SkRect& oval,
- SkScalar start_angle,
- SkScalar sweep_angle,
- bool use_center,
- const PaintFlags& flags)
- : PaintOpWithFlags(flags),
- oval(oval),
- start_angle(start_angle),
- sweep_angle(sweep_angle),
- use_center(use_center) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm);
- HAS_SERIALIZATION_FUNCTIONS();
-
- SkRect oval;
- SkScalar start_angle;
- SkScalar sweep_angle;
- bool use_center;
-
- private:
- DrawArcOp() = default;
-};
-
-class CC_PAINT_EXPORT DrawCircleOp final : public PaintOpWithFlags {
- public:
- static constexpr PaintOpType kType = PaintOpType::DrawCircle;
- static constexpr bool kIsDrawOp = true;
- DrawCircleOp(SkScalar cx,
- SkScalar cy,
- SkScalar radius,
- const PaintFlags& flags)
- : PaintOpWithFlags(flags), cx(cx), cy(cy), radius(radius) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm);
- HAS_SERIALIZATION_FUNCTIONS();
-
- SkScalar cx;
- SkScalar cy;
- SkScalar radius;
-
- private:
- DrawCircleOp() = default;
-};
-
class CC_PAINT_EXPORT DrawColorOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::DrawColor;
static constexpr bool kIsDrawOp = true;
DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const DrawColorOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return IsValidDrawColorSkBlendMode(mode); }
HAS_SERIALIZATION_FUNCTIONS();
SkColor color;
@@ -474,16 +349,13 @@ class CC_PAINT_EXPORT DrawDRRectOp final : public PaintOpWithFlags {
const SkRRect& inner,
const PaintFlags& flags)
: PaintOpWithFlags(flags), outer(outer), inner(inner) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawDRRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const {
+ return flags.IsValid() && outer.isValid() && inner.isValid();
+ }
HAS_SERIALIZATION_FUNCTIONS();
SkRRect outer;
@@ -502,16 +374,11 @@ class CC_PAINT_EXPORT DrawImageOp final : public PaintOpWithFlags {
SkScalar top,
const PaintFlags* flags);
~DrawImageOp();
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawImageOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid(); }
bool HasDiscardableImages() const;
bool HasNonAAPaint() const { return false; }
HAS_SERIALIZATION_FUNCTIONS();
@@ -534,16 +401,13 @@ class CC_PAINT_EXPORT DrawImageRectOp final : public PaintOpWithFlags {
const PaintFlags* flags,
PaintCanvas::SrcRectConstraint constraint);
~DrawImageRectOp();
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawImageRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const {
+ return flags.IsValid() && src.isFinite() && dst.isFinite();
+ }
bool HasDiscardableImages() const;
HAS_SERIALIZATION_FUNCTIONS();
@@ -562,16 +426,11 @@ class CC_PAINT_EXPORT DrawIRectOp final : public PaintOpWithFlags {
static constexpr bool kIsDrawOp = true;
DrawIRectOp(const SkIRect& rect, const PaintFlags& flags)
: PaintOpWithFlags(flags), rect(rect) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawIRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid(); }
bool HasNonAAPaint() const { return false; }
HAS_SERIALIZATION_FUNCTIONS();
@@ -591,16 +450,11 @@ class CC_PAINT_EXPORT DrawLineOp final : public PaintOpWithFlags {
SkScalar y1,
const PaintFlags& flags)
: PaintOpWithFlags(flags), x0(x0), y0(y0), x1(x1), y1(y1) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawLineOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid(); }
HAS_SERIALIZATION_FUNCTIONS();
int CountSlowPaths() const;
@@ -620,16 +474,11 @@ class CC_PAINT_EXPORT DrawOvalOp final : public PaintOpWithFlags {
static constexpr bool kIsDrawOp = true;
DrawOvalOp(const SkRect& oval, const PaintFlags& flags)
: PaintOpWithFlags(flags), oval(oval) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawOvalOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid() && oval.isFinite(); }
HAS_SERIALIZATION_FUNCTIONS();
SkRect oval;
@@ -644,16 +493,11 @@ class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags {
static constexpr bool kIsDrawOp = true;
DrawPathOp(const SkPath& path, const PaintFlags& flags)
: PaintOpWithFlags(flags), path(path) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawPathOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid() && IsValidPath(path); }
int CountSlowPaths() const;
HAS_SERIALIZATION_FUNCTIONS();
@@ -663,42 +507,16 @@ class CC_PAINT_EXPORT DrawPathOp final : public PaintOpWithFlags {
DrawPathOp() = default;
};
-class CC_PAINT_EXPORT DrawPosTextOp final : public PaintOpWithArray<SkPoint> {
- public:
- static constexpr PaintOpType kType = PaintOpType::DrawPosText;
- static constexpr bool kIsDrawOp = true;
- DrawPosTextOp(size_t bytes, size_t count, const PaintFlags& flags);
- ~DrawPosTextOp();
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm);
- HAS_SERIALIZATION_FUNCTIONS();
-
- const void* GetData() const { return GetDataForThis(this); }
- void* GetData() { return GetDataForThis(this); }
- const SkPoint* GetArray() const { return GetArrayForThis(this); }
- SkPoint* GetArray() { return GetArrayForThis(this); }
-
- private:
- DrawPosTextOp();
-};
-
class CC_PAINT_EXPORT DrawRecordOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::DrawRecord;
static constexpr bool kIsDrawOp = true;
explicit DrawRecordOp(sk_sp<const PaintRecord> record);
~DrawRecordOp();
- static void Raster(const PaintOp* op,
+ static void Raster(const DrawRecordOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return true; }
size_t AdditionalBytesUsed() const;
bool HasDiscardableImages() const;
int CountSlowPaths() const;
@@ -717,16 +535,11 @@ class CC_PAINT_EXPORT DrawRectOp final : public PaintOpWithFlags {
static constexpr bool kIsDrawOp = true;
DrawRectOp(const SkRect& rect, const PaintFlags& flags)
: PaintOpWithFlags(flags), rect(rect) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid() && rect.isFinite(); }
HAS_SERIALIZATION_FUNCTIONS();
SkRect rect;
@@ -741,16 +554,11 @@ class CC_PAINT_EXPORT DrawRRectOp final : public PaintOpWithFlags {
static constexpr bool kIsDrawOp = true;
DrawRRectOp(const SkRRect& rrect, const PaintFlags& flags)
: PaintOpWithFlags(flags), rrect(rrect) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawRRectOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid() && rrect.isValid(); }
HAS_SERIALIZATION_FUNCTIONS();
SkRRect rrect;
@@ -759,34 +567,6 @@ class CC_PAINT_EXPORT DrawRRectOp final : public PaintOpWithFlags {
DrawRRectOp() = default;
};
-class CC_PAINT_EXPORT DrawTextOp final : public PaintOpWithData {
- public:
- static constexpr PaintOpType kType = PaintOpType::DrawText;
- static constexpr bool kIsDrawOp = true;
- DrawTextOp(size_t bytes, SkScalar x, SkScalar y, const PaintFlags& flags)
- : PaintOpWithData(flags, bytes), x(x), y(y) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
- const PaintFlags* flags,
- SkCanvas* canvas,
- const SkMatrix& original_ctm);
- HAS_SERIALIZATION_FUNCTIONS();
-
- void* GetData() { return GetDataForThis(this); }
- const void* GetData() const { return GetDataForThis(this); }
-
- SkScalar x;
- SkScalar y;
-
- private:
- DrawTextOp() = default;
-};
-
class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags {
public:
static constexpr PaintOpType kType = PaintOpType::DrawTextBlob;
@@ -796,16 +576,11 @@ class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags {
SkScalar y,
const PaintFlags& flags);
~DrawTextBlobOp();
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const DrawTextBlobOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid(); }
HAS_SERIALIZATION_FUNCTIONS();
sk_sp<SkTextBlob> blob;
@@ -819,18 +594,20 @@ class CC_PAINT_EXPORT DrawTextBlobOp final : public PaintOpWithFlags {
class CC_PAINT_EXPORT NoopOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::Noop;
- static void Raster(const PaintOp* op,
+ static void Raster(const NoopOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm) {}
+ const PlaybackParams& params) {}
+ bool IsValid() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
};
class CC_PAINT_EXPORT RestoreOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::Restore;
- static void Raster(const PaintOp* op,
+ static void Raster(const RestoreOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
};
@@ -838,9 +615,10 @@ class CC_PAINT_EXPORT RotateOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::Rotate;
explicit RotateOp(SkScalar degrees) : degrees(degrees) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const RotateOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
SkScalar degrees;
@@ -852,9 +630,10 @@ class CC_PAINT_EXPORT RotateOp final : public PaintOp {
class CC_PAINT_EXPORT SaveOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::Save;
- static void Raster(const PaintOp* op,
+ static void Raster(const SaveOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
};
@@ -864,16 +643,11 @@ class CC_PAINT_EXPORT SaveLayerOp final : public PaintOpWithFlags {
SaveLayerOp(const SkRect* bounds, const PaintFlags* flags)
: PaintOpWithFlags(flags ? *flags : PaintFlags()),
bounds(bounds ? *bounds : kUnsetRect) {}
- static void Raster(const PaintOp* op,
- SkCanvas* canvas,
- const SkMatrix& original_ctm) {
- auto* flags_op = static_cast<const PaintOpWithFlags*>(op);
- RasterWithFlags(flags_op, &flags_op->flags, canvas, original_ctm);
- }
- static void RasterWithFlags(const PaintOpWithFlags* op,
+ static void RasterWithFlags(const SaveLayerOp* op,
const PaintFlags* flags,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return flags.IsValid() && IsValidOrUnsetRect(bounds); }
bool HasNonAAPaint() const { return false; }
HAS_SERIALIZATION_FUNCTIONS();
@@ -892,9 +666,10 @@ class CC_PAINT_EXPORT SaveLayerAlphaOp final : public PaintOp {
: bounds(bounds ? *bounds : kUnsetRect),
alpha(alpha),
preserve_lcd_text_requests(preserve_lcd_text_requests) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const SaveLayerAlphaOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return IsValidOrUnsetRect(bounds); }
HAS_SERIALIZATION_FUNCTIONS();
SkRect bounds;
@@ -909,9 +684,10 @@ class CC_PAINT_EXPORT ScaleOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::Scale;
ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const ScaleOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
SkScalar sx;
@@ -931,9 +707,10 @@ class CC_PAINT_EXPORT SetMatrixOp final : public PaintOp {
//
// TODO(enne): Find some cleaner way to do this, possibly by making
// all SetMatrix calls Concat??
- static void Raster(const PaintOp* op,
+ static void Raster(const SetMatrixOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
ThreadsafeMatrix matrix;
@@ -946,9 +723,10 @@ class CC_PAINT_EXPORT TranslateOp final : public PaintOp {
public:
static constexpr PaintOpType kType = PaintOpType::Translate;
TranslateOp(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {}
- static void Raster(const PaintOp* op,
+ static void Raster(const TranslateOp* op,
SkCanvas* canvas,
- const SkMatrix& original_ctm);
+ const PlaybackParams& params);
+ bool IsValid() const { return true; }
HAS_SERIALIZATION_FUNCTIONS();
SkScalar dx;
@@ -960,7 +738,11 @@ class CC_PAINT_EXPORT TranslateOp final : public PaintOp {
#undef HAS_SERIALIZATION_FUNCTIONS
-using LargestPaintOp = DrawDRRectOp;
+// TODO(vmpstr): Revisit this when sizes of DrawImageRectOp change.
+using LargestPaintOp =
+ typename std::conditional<(sizeof(DrawImageRectOp) > sizeof(DrawDRRectOp)),
+ DrawImageRectOp,
+ DrawDRRectOp>::type;
class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
public:
@@ -977,12 +759,10 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
void Reset();
- // Replays the paint op buffer into the canvas. If |indices| is specified, it
- // contains indices in an increasing order and only the indices specified in
- // the vector will be replayed.
+ // Replays the paint op buffer into the canvas.
void Playback(SkCanvas* canvas,
- SkPicture::AbortCallback* callback = nullptr,
- const std::vector<size_t>* indices = nullptr) const;
+ ImageProvider* image_provider = nullptr,
+ SkPicture::AbortCallback* callback = nullptr) const;
// Returns the size of the paint op buffer. That is, the number of ops
// contained in it.
@@ -991,6 +771,7 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
size_t bytes_used() const {
return sizeof(*this) + reserved_ + subrecord_bytes_used_;
}
+ size_t next_op_offset() const { return used_; }
int numSlowPaths() const { return num_slow_paths_; }
bool HasNonAAPaint() const { return has_non_aa_paint_; }
bool HasDiscardableImages() const { return has_discardable_images_; }
@@ -1005,159 +786,214 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
template <typename T, typename... Args>
void push(Args&&... args) {
static_assert(std::is_convertible<T, PaintOp>::value, "T not a PaintOp.");
- static_assert(!std::is_convertible<T, PaintOpWithData>::value,
- "Type needs to use push_with_data");
- push_internal<T>(0, std::forward<Args>(args)...);
- }
+ static_assert(alignof(T) <= PaintOpAlign, "");
- template <typename T, typename... Args>
- void push_with_data(const void* data, size_t bytes, Args&&... args) {
- static_assert(std::is_convertible<T, PaintOpWithData>::value,
- "T is not a PaintOpWithData");
- static_assert(!std::is_convertible<T, PaintOpWithArrayBase>::value,
- "Type needs to use push_with_array");
- T* op = push_internal<T>(bytes, bytes, std::forward<Args>(args)...);
- memcpy(op->GetData(), data, bytes);
-
-#if DCHECK_IS_ON()
- // Double check the data fits between op and next op and doesn't clobber.
- char* op_start = reinterpret_cast<char*>(op);
- char* op_end = op_start + sizeof(T);
- char* next_op = op_start + op->skip;
- char* data_start = reinterpret_cast<char*>(op->GetData());
- char* data_end = data_start + bytes;
- DCHECK_GE(data_start, op_end);
- DCHECK_LT(data_start, next_op);
- DCHECK_LE(data_end, next_op);
-#endif
- }
+ auto pair = AllocatePaintOp(sizeof(T));
+ T* op = reinterpret_cast<T*>(pair.first);
+ size_t skip = pair.second;
- template <typename T, typename M, typename... Args>
- void push_with_array(const void* data,
- size_t bytes,
- const M* array,
- size_t count,
- Args&&... args) {
- static_assert(std::is_convertible<T, PaintOpWithArray<M>>::value,
- "T is not a PaintOpWithArray");
- size_t array_size = sizeof(M) * count;
- size_t total_size = bytes + array_size;
- T* op =
- push_internal<T>(total_size, bytes, count, std::forward<Args>(args)...);
- memcpy(op->GetData(), data, bytes);
- memcpy(op->GetArray(), array, array_size);
-
-#if DCHECK_IS_ON()
- // Double check data and array don't clobber op, next op, or each other
- char* op_start = reinterpret_cast<char*>(op);
- char* op_end = op_start + sizeof(T);
- char* array_start = reinterpret_cast<char*>(op->GetArray());
- char* array_end = array_start + array_size;
- char* data_start = reinterpret_cast<char*>(op->GetData());
- char* data_end = data_start + bytes;
- char* next_op = op_start + op->skip;
- DCHECK_GE(array_start, op_end);
- DCHECK_LE(array_start, data_start);
- DCHECK_GE(data_start, array_end);
- DCHECK_LE(data_end, next_op);
-#endif
+ new (op) T{std::forward<Args>(args)...};
+ op->type = static_cast<uint32_t>(T::kType);
+ op->skip = skip;
+ AnalyzeAddedOp(op);
}
- class Iterator {
+ class CC_PAINT_EXPORT Iterator {
public:
- explicit Iterator(const PaintOpBuffer* buffer,
- const std::vector<size_t>* indices = nullptr)
- : buffer_(buffer), ptr_(buffer_->data_.get()), indices_(indices) {
- if (indices) {
- if (indices->empty()) {
- *this = end();
- return;
- }
- target_idx_ = (*indices)[0];
- }
- ++(*this);
- }
+ explicit Iterator(const PaintOpBuffer* buffer)
+ : Iterator(buffer, buffer->data_.get(), 0u) {}
PaintOp* operator->() const { return reinterpret_cast<PaintOp*>(ptr_); }
PaintOp* operator*() const { return operator->(); }
- Iterator begin() { return Iterator(buffer_, indices_); }
+ Iterator begin() { return Iterator(buffer_); }
Iterator end() {
return Iterator(buffer_, buffer_->data_.get() + buffer_->used_,
- buffer_->size());
+ buffer_->used_);
}
bool operator!=(const Iterator& other) {
// Not valid to compare iterators on different buffers.
DCHECK_EQ(other.buffer_, buffer_);
- return other.op_idx_ != op_idx_;
+ return other.op_offset_ != op_offset_;
}
Iterator& operator++() {
- if (target_idx_ == std::numeric_limits<size_t>::max()) {
+ DCHECK(*this);
+ const PaintOp* op = **this;
+ ptr_ += op->skip;
+ op_offset_ += op->skip;
+
+ // Debugging crbug.com/738182.
+ base::debug::Alias(op);
+ CHECK_LE(op_offset_, buffer_->used_);
+ return *this;
+ }
+ operator bool() const { return op_offset_ < buffer_->used_; }
+
+ private:
+ Iterator(const PaintOpBuffer* buffer, char* ptr, size_t op_offset)
+ : buffer_(buffer), ptr_(ptr), op_offset_(op_offset) {}
+
+ const PaintOpBuffer* buffer_ = nullptr;
+ char* ptr_ = nullptr;
+ size_t op_offset_ = 0;
+ };
+
+ class CC_PAINT_EXPORT OffsetIterator {
+ public:
+ // Offsets and paint op buffer must come from the same DisplayItemList.
+ OffsetIterator(const PaintOpBuffer* buffer,
+ const std::vector<size_t>* offsets)
+ : buffer_(buffer), ptr_(buffer_->data_.get()), offsets_(offsets) {
+ if (offsets->empty()) {
*this = end();
- return *this;
+ return;
}
+ op_offset_ = (*offsets)[0];
+ ptr_ += op_offset_;
+ }
- while (*this && target_idx_ != op_idx_) {
- PaintOp* op = **this;
- uint32_t type = op->type;
- uint32_t skip = op->skip;
-
- // Sanity checks.
- base::debug::Alias(&type);
- base::debug::Alias(&skip);
- CHECK_LE(type, static_cast<uint32_t>(PaintOpType::LastPaintOpType));
- // This is here for debugging crbug.com/738182.
- CHECK_LE(static_cast<size_t>(ptr_ - buffer_->data_.get() + skip),
- buffer_->used_);
-
- ptr_ += skip;
- op_idx_++;
+ PaintOp* operator->() const { return reinterpret_cast<PaintOp*>(ptr_); }
+ PaintOp* operator*() const { return operator->(); }
+ OffsetIterator begin() { return OffsetIterator(buffer_, offsets_); }
+ OffsetIterator end() {
+ return OffsetIterator(buffer_, buffer_->data_.get() + buffer_->used_,
+ buffer_->used_, offsets_);
+ }
+ bool operator!=(const OffsetIterator& other) {
+ // Not valid to compare iterators on different buffers.
+ DCHECK_EQ(other.buffer_, buffer_);
+ return other.op_offset_ != op_offset_;
+ }
+ OffsetIterator& operator++() {
+ if (++offsets_index_ >= offsets_->size()) {
+ *this = end();
+ return *this;
}
- if (indices_) {
- if (++indices_index_ >= indices_->size())
- target_idx_ = std::numeric_limits<size_t>::max();
- else
- target_idx_ = (*indices_)[indices_index_];
- } else {
- ++target_idx_;
- }
+ size_t target_offset = (*offsets_)[offsets_index_];
+ // Sanity checks.
+ CHECK_GE(target_offset, op_offset_);
+ // Debugging crbug.com/738182.
+ base::debug::Alias(&target_offset);
+ CHECK_LT(target_offset, buffer_->used_);
+
+ // Advance the iterator to the target offset.
+ ptr_ += (target_offset - op_offset_);
+ op_offset_ = target_offset;
+
+ DCHECK(!*this || (*this)->type <=
+ static_cast<uint32_t>(PaintOpType::LastPaintOpType));
return *this;
}
- operator bool() const { return op_idx_ < buffer_->size(); }
- size_t op_idx() const { return op_idx_; }
+
+ operator bool() const { return op_offset_ < buffer_->used_; }
private:
- Iterator(const PaintOpBuffer* buffer, char* ptr, size_t op_idx)
- : buffer_(buffer), ptr_(ptr), op_idx_(op_idx) {}
+ OffsetIterator(const PaintOpBuffer* buffer,
+ char* ptr,
+ size_t op_offset,
+ const std::vector<size_t>* offsets)
+ : buffer_(buffer),
+ ptr_(ptr),
+ offsets_(offsets),
+ op_offset_(op_offset) {}
const PaintOpBuffer* buffer_ = nullptr;
char* ptr_ = nullptr;
- const std::vector<size_t>* indices_ = nullptr;
- size_t op_idx_ = 0;
- size_t target_idx_ = 0;
- size_t indices_index_ = 0;
+ const std::vector<size_t>* offsets_;
+ size_t op_offset_ = 0;
+ size_t offsets_index_ = 0;
+ };
+
+ class CC_PAINT_EXPORT CompositeIterator {
+ public:
+ // Offsets and paint op buffer must come from the same DisplayItemList.
+ CompositeIterator(const PaintOpBuffer* buffer,
+ const std::vector<size_t>* offsets);
+ CompositeIterator(const CompositeIterator& other);
+ CompositeIterator(CompositeIterator&& other);
+
+ PaintOp* operator->() const {
+ return using_offsets_ ? **offset_iter_ : **iter_;
+ }
+ PaintOp* operator*() const {
+ return using_offsets_ ? **offset_iter_ : **iter_;
+ }
+ bool operator==(const CompositeIterator& other) {
+ if (using_offsets_ != other.using_offsets_)
+ return false;
+ return using_offsets_ ? (*offset_iter_ == *other.offset_iter_)
+ : (*iter_ == *other.iter_);
+ }
+ bool operator!=(const CompositeIterator& other) {
+ return !(*this == other);
+ }
+ CompositeIterator& operator++() {
+ if (using_offsets_)
+ ++*offset_iter_;
+ else
+ ++*iter_;
+ return *this;
+ }
+ operator bool() const {
+ return using_offsets_ ? !!*offset_iter_ : !!*iter_;
+ }
+
+ private:
+ bool using_offsets_ = false;
+ base::Optional<OffsetIterator> offset_iter_;
+ base::Optional<Iterator> iter_;
+ };
+
+ // Returns a stream of non-DrawRecord ops from a top level pob with indices.
+ // Upon encountering DrawRecord ops, it returns ops from inside them
+ // without returning the DrawRecord op itself. It does this recursively.
+ class CC_PAINT_EXPORT FlatteningIterator {
+ public:
+ // Offsets and paint op buffer must come from the same DisplayItemList.
+ FlatteningIterator(const PaintOpBuffer* buffer,
+ const std::vector<size_t>* offsets);
+ ~FlatteningIterator();
+
+ PaintOp* operator->() const {
+ return nested_iter_.empty() ? *top_level_iter_ : *nested_iter_.back();
+ }
+ PaintOp* operator*() const { return operator->(); }
+
+ FlatteningIterator& operator++() {
+ if (nested_iter_.empty())
+ ++top_level_iter_;
+ else
+ ++nested_iter_.back();
+ FlattenCurrentOpIfNeeded();
+ return *this;
+ }
+
+ operator bool() const { return top_level_iter_; }
+
+ private:
+ void FlattenCurrentOpIfNeeded();
+
+ PaintOpBuffer::OffsetIterator top_level_iter_;
+ std::vector<Iterator> nested_iter_;
};
private:
+ friend class DisplayItemList;
+ friend class PaintOpBufferOffsetsTest;
+ friend class SolidColorAnalyzer;
+
+ // Replays the paint op buffer into the canvas. If |indices| is specified, it
+ // contains indices in an increasing order and only the indices specified in
+ // the vector will be replayed.
+ void Playback(SkCanvas* canvas,
+ ImageProvider* image_provider,
+ SkPicture::AbortCallback* callback,
+ const std::vector<size_t>* indices) const;
+
void ReallocBuffer(size_t new_size);
// Returns the allocated op and the number of bytes to skip in |data_| to get
// to the next op.
- std::pair<void*, size_t> AllocatePaintOp(size_t sizeof_op, size_t bytes);
-
- template <typename T, typename... Args>
- T* push_internal(size_t bytes, Args&&... args) {
- static_assert(alignof(T) <= PaintOpAlign, "");
-
- auto pair = AllocatePaintOp(sizeof(T), bytes);
- T* op = reinterpret_cast<T*>(pair.first);
- size_t skip = pair.second;
-
- new (op) T{std::forward<Args>(args)...};
- op->type = static_cast<uint32_t>(T::kType);
- op->skip = skip;
- AnalyzeAddedOp(op);
- return op;
- }
+ std::pair<void*, size_t> AllocatePaintOp(size_t sizeof_op);
template <typename T>
void AnalyzeAddedOp(const T* op) {
diff --git a/chromium/cc/paint/paint_op_buffer_fuzzer.cc b/chromium/cc/paint/paint_op_buffer_fuzzer.cc
new file mode 100644
index 00000000000..6a7cca6b750
--- /dev/null
+++ b/chromium/cc/paint/paint_op_buffer_fuzzer.cc
@@ -0,0 +1,59 @@
+// 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 <stddef.h>
+#include <stdint.h>
+
+#include "cc/paint/paint_op_buffer.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/gl/GrGLInterface.h"
+
+// Deserialize an arbitrary number of cc::PaintOps and raster them
+// using gpu raster into an SkCanvas.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ const size_t kMaxSerializedSize = 1000000;
+ const size_t kRasterDimension = 32;
+
+ SkImageInfo image_info = SkImageInfo::MakeN32(
+ kRasterDimension, kRasterDimension, kOpaque_SkAlphaType);
+ sk_sp<const GrGLInterface> gl_interface(GrGLCreateNullInterface());
+ sk_sp<GrContext> gr_context(GrContext::Create(
+ kOpenGL_GrBackend,
+ reinterpret_cast<GrBackendContext>(gl_interface.get())));
+ sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(
+ gr_context.get(), SkBudgeted::kYes, image_info);
+ SkCanvas* canvas = surface->getCanvas();
+
+ cc::PlaybackParams params(nullptr, canvas->getTotalMatrix());
+
+ // Need 4 bytes to be able to read the type/skip.
+ while (size >= 4) {
+ const cc::PaintOp* serialized = reinterpret_cast<const cc::PaintOp*>(data);
+ if (serialized->skip > kMaxSerializedSize)
+ break;
+
+ std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
+ static_cast<char*>(base::AlignedAlloc(
+ sizeof(cc::LargestPaintOp), cc::PaintOpBuffer::PaintOpAlign)));
+ size_t bytes_read = 0;
+ cc::PaintOp* deserialized_op =
+ cc::PaintOp::Deserialize(data, size, deserialized.get(),
+ sizeof(cc::LargestPaintOp), &bytes_read);
+
+ if (!deserialized_op)
+ break;
+
+ deserialized_op->Raster(canvas, params);
+
+ deserialized_op->DestroyThis();
+
+ if (serialized->skip >= size)
+ break;
+
+ size -= bytes_read;
+ data += bytes_read;
+ }
+ return 0;
+}
diff --git a/chromium/cc/paint/paint_op_buffer_unittest.cc b/chromium/cc/paint/paint_op_buffer_unittest.cc
index 0af57a47653..b9601e4a486 100644
--- a/chromium/cc/paint/paint_op_buffer_unittest.cc
+++ b/chromium/cc/paint/paint_op_buffer_unittest.cc
@@ -5,12 +5,22 @@
#include "cc/paint/paint_op_buffer.h"
#include "base/memory/ptr_util.h"
#include "base/strings/stringprintf.h"
+#include "cc/paint/decoded_draw_image.h"
#include "cc/paint/display_item_list.h"
+#include "cc/paint/image_provider.h"
+#include "cc/paint/paint_image_builder.h"
+#include "cc/paint/paint_op_reader.h"
+#include "cc/paint/paint_op_writer.h"
#include "cc/test/skia_common.h"
#include "cc/test/test_skcanvas.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkFlattenableSerialization.h"
#include "third_party/skia/include/core/SkWriteBuffer.h"
+#include "third_party/skia/include/effects/SkBlurMaskFilter.h"
+#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
#include "third_party/skia/include/effects/SkDashPathEffect.h"
+#include "third_party/skia/include/effects/SkLayerDrawLooper.h"
+#include "third_party/skia/include/effects/SkOffsetImageFilter.h"
using testing::_;
using testing::Property;
@@ -18,22 +28,123 @@ using testing::Mock;
namespace cc {
namespace {
+// An arbitrary size guaranteed to fit the size of any serialized op in this
+// unit test. This can also be used for deserialized op size safely in this
+// unit test suite as generally deserialized ops are smaller.
+static constexpr size_t kBufferBytesPerOp = 1000 + sizeof(LargestPaintOp);
-void Playback(PaintOpBuffer* buffer,
- SkCanvas* canvas,
- const std::vector<size_t>& indices) {
- buffer->Playback(canvas, nullptr, &indices);
-}
-
-void ExpectPaintFlagsEqual(const PaintFlags& expected,
- const PaintFlags& actual) {
- SkPaint expected_paint = expected.ToSkPaint();
- SkPaint actual_paint = actual.ToSkPaint();
- EXPECT_TRUE(expected_paint == actual_paint);
+template <typename T>
+void ValidateOps(PaintOpBuffer* buffer) {
+ // Make sure all test data is valid before serializing it.
+ for (auto* op : PaintOpBuffer::Iterator(buffer))
+ EXPECT_TRUE(static_cast<T*>(op)->IsValid());
}
} // namespace
+class PaintOpSerializationTestUtils {
+ public:
+ static void ExpectFlattenableEqual(SkFlattenable* expected,
+ SkFlattenable* actual) {
+ sk_sp<SkData> expected_data(SkValidatingSerializeFlattenable(expected));
+ sk_sp<SkData> actual_data(SkValidatingSerializeFlattenable(actual));
+ ASSERT_EQ(expected_data->size(), actual_data->size());
+ EXPECT_TRUE(expected_data->equals(actual_data.get()));
+ }
+
+ static void ExpectPaintShadersEqual(const PaintShader* one,
+ const PaintShader* two) {
+ if (!one) {
+ EXPECT_FALSE(two);
+ return;
+ }
+
+ ASSERT_TRUE(one);
+ ASSERT_TRUE(two);
+
+ EXPECT_EQ(one->shader_type_, two->shader_type_);
+ EXPECT_EQ(one->flags_, two->flags_);
+ EXPECT_EQ(one->end_radius_, two->end_radius_);
+ EXPECT_EQ(one->start_radius_, two->start_radius_);
+ EXPECT_EQ(one->tx_, two->tx_);
+ EXPECT_EQ(one->ty_, two->ty_);
+ EXPECT_EQ(one->fallback_color_, two->fallback_color_);
+ EXPECT_EQ(one->scaling_behavior_, two->scaling_behavior_);
+ if (one->local_matrix_) {
+ EXPECT_TRUE(two->local_matrix_.has_value());
+ EXPECT_TRUE(*one->local_matrix_ == *two->local_matrix_);
+ } else {
+ EXPECT_FALSE(two->local_matrix_.has_value());
+ }
+ EXPECT_EQ(one->center_, two->center_);
+ EXPECT_EQ(one->tile_, two->tile_);
+ EXPECT_EQ(one->start_point_, two->start_point_);
+ EXPECT_EQ(one->end_point_, two->end_point_);
+ EXPECT_EQ(one->start_degrees_, two->start_degrees_);
+ EXPECT_EQ(one->end_degrees_, two->end_degrees_);
+ EXPECT_THAT(one->colors_, testing::ElementsAreArray(two->colors_));
+ EXPECT_THAT(one->positions_, testing::ElementsAreArray(two->positions_));
+ }
+
+ static void ExpectPaintFlagsEqual(const PaintFlags& expected,
+ const PaintFlags& actual) {
+ // Can't just ToSkPaint and operator== here as SkPaint does pointer
+ // comparisons on all the ref'd skia objects on the SkPaint, which
+ // is not true after serialization.
+ EXPECT_EQ(expected.getTextSize(), actual.getTextSize());
+ EXPECT_EQ(expected.getColor(), actual.getColor());
+ EXPECT_EQ(expected.getStrokeWidth(), actual.getStrokeWidth());
+ EXPECT_EQ(expected.getStrokeMiter(), actual.getStrokeMiter());
+ EXPECT_EQ(expected.getBlendMode(), actual.getBlendMode());
+ EXPECT_EQ(expected.getStrokeCap(), actual.getStrokeCap());
+ EXPECT_EQ(expected.getStrokeJoin(), actual.getStrokeJoin());
+ EXPECT_EQ(expected.getStyle(), actual.getStyle());
+ EXPECT_EQ(expected.getTextEncoding(), actual.getTextEncoding());
+ EXPECT_EQ(expected.getHinting(), actual.getHinting());
+ EXPECT_EQ(expected.getFilterQuality(), actual.getFilterQuality());
+
+ // TODO(enne): compare typeface too
+ ExpectFlattenableEqual(expected.getPathEffect().get(),
+ actual.getPathEffect().get());
+ ExpectFlattenableEqual(expected.getMaskFilter().get(),
+ actual.getMaskFilter().get());
+ ExpectFlattenableEqual(expected.getColorFilter().get(),
+ actual.getColorFilter().get());
+ ExpectFlattenableEqual(expected.getLooper().get(),
+ actual.getLooper().get());
+ ExpectFlattenableEqual(expected.getImageFilter().get(),
+ actual.getImageFilter().get());
+
+ ExpectPaintShadersEqual(expected.getShader(), actual.getShader());
+ }
+
+ static void FillArbitraryShaderValues(PaintShader* shader, bool use_matrix) {
+ shader->shader_type_ = PaintShader::Type::kTwoPointConicalGradient;
+ shader->flags_ = 12345;
+ shader->end_radius_ = 12.3f;
+ shader->start_radius_ = 13.4f;
+ shader->tx_ = SkShader::kRepeat_TileMode;
+ shader->ty_ = SkShader::kMirror_TileMode;
+ shader->fallback_color_ = SkColorSetARGB(254, 252, 250, 248);
+ shader->scaling_behavior_ = PaintShader::ScalingBehavior::kRasterAtScale;
+ if (use_matrix) {
+ shader->local_matrix_.emplace(SkMatrix::I());
+ shader->local_matrix_->setSkewX(10);
+ shader->local_matrix_->setSkewY(20);
+ }
+ shader->center_ = SkPoint::Make(50, 40);
+ shader->tile_ = SkRect::MakeXYWH(7, 77, 777, 7777);
+ shader->start_point_ = SkPoint::Make(-1, -5);
+ shader->end_point_ = SkPoint::Make(13, -13);
+ shader->start_degrees_ = 123;
+ shader->end_degrees_ = 456;
+ // TODO(vmpstr): Add PaintImage/PaintRecord.
+ shader->colors_ = {SkColorSetARGB(1, 2, 3, 4), SkColorSetARGB(5, 6, 7, 8),
+ SkColorSetARGB(9, 0, 1, 2)};
+ shader->positions_ = {0.f, 0.4f, 1.f};
+ }
+};
+
TEST(PaintOpBufferTest, Empty) {
PaintOpBuffer buffer;
EXPECT_EQ(buffer.size(), 0u);
@@ -77,7 +188,8 @@ class PaintOpAppendTest : public ::testing::Test {
ASSERT_EQ(iter->GetType(), PaintOpType::SaveLayer);
SaveLayerOp* save_op = static_cast<SaveLayerOp*>(*iter);
EXPECT_EQ(save_op->bounds, rect_);
- ExpectPaintFlagsEqual(save_op->flags, flags_);
+ PaintOpSerializationTestUtils::ExpectPaintFlagsEqual(save_op->flags,
+ flags_);
++iter;
ASSERT_EQ(iter->GetType(), PaintOpType::Save);
@@ -164,94 +276,6 @@ TEST_F(PaintOpAppendTest, MoveThenReappendOperatorEq) {
VerifyOps(&original);
}
-// Verify that PaintOps with data are stored properly.
-TEST(PaintOpBufferTest, PaintOpData) {
- PaintOpBuffer buffer;
-
- buffer.push<SaveOp>();
- PaintFlags flags;
- char text1[] = "asdfasdf";
- buffer.push_with_data<DrawTextOp>(text1, arraysize(text1), 0.f, 0.f, flags);
-
- char text2[] = "qwerty";
- buffer.push_with_data<DrawTextOp>(text2, arraysize(text2), 0.f, 0.f, flags);
-
- ASSERT_EQ(buffer.size(), 3u);
-
- // Verify iteration behavior and brief smoke test of op state.
- PaintOpBuffer::Iterator iter(&buffer);
- PaintOp* save_op = *iter;
- EXPECT_EQ(save_op->GetType(), PaintOpType::Save);
- ++iter;
-
- PaintOp* op1 = *iter;
- ASSERT_EQ(op1->GetType(), PaintOpType::DrawText);
- DrawTextOp* draw_text_op1 = static_cast<DrawTextOp*>(op1);
- EXPECT_EQ(draw_text_op1->bytes, arraysize(text1));
- const void* data1 = draw_text_op1->GetData();
- EXPECT_EQ(memcmp(data1, text1, arraysize(text1)), 0);
- ++iter;
-
- PaintOp* op2 = *iter;
- ASSERT_EQ(op2->GetType(), PaintOpType::DrawText);
- DrawTextOp* draw_text_op2 = static_cast<DrawTextOp*>(op2);
- EXPECT_EQ(draw_text_op2->bytes, arraysize(text2));
- const void* data2 = draw_text_op2->GetData();
- EXPECT_EQ(memcmp(data2, text2, arraysize(text2)), 0);
- ++iter;
-
- EXPECT_FALSE(iter);
-}
-
-// Verify that PaintOps with arrays are stored properly.
-TEST(PaintOpBufferTest, PaintOpArray) {
- PaintOpBuffer buffer;
- buffer.push<SaveOp>();
-
- // arbitrary data
- std::string texts[] = {"xyz", "abcdefg", "thingerdoo"};
- SkPoint point1[] = {SkPoint::Make(1, 2), SkPoint::Make(2, 3),
- SkPoint::Make(3, 4)};
- SkPoint point2[] = {SkPoint::Make(8, -12)};
- SkPoint point3[] = {SkPoint::Make(0, 0), SkPoint::Make(5, 6),
- SkPoint::Make(-1, -1), SkPoint::Make(9, 9),
- SkPoint::Make(50, 50), SkPoint::Make(100, 100)};
- SkPoint* points[] = {point1, point2, point3};
- size_t counts[] = {arraysize(point1), arraysize(point2), arraysize(point3)};
-
- for (size_t i = 0; i < arraysize(texts); ++i) {
- PaintFlags flags;
- flags.setAlpha(i);
- buffer.push_with_array<DrawPosTextOp>(texts[i].c_str(), texts[i].length(),
- points[i], counts[i], flags);
- }
-
- PaintOpBuffer::Iterator iter(&buffer);
- PaintOp* save_op = *iter;
- EXPECT_EQ(save_op->GetType(), PaintOpType::Save);
- ++iter;
-
- for (size_t i = 0; i < arraysize(texts); ++i) {
- ASSERT_EQ(iter->GetType(), PaintOpType::DrawPosText);
- DrawPosTextOp* op = static_cast<DrawPosTextOp*>(*iter);
-
- EXPECT_EQ(op->flags.getAlpha(), i);
-
- EXPECT_EQ(op->bytes, texts[i].length());
- const void* data = op->GetData();
- EXPECT_EQ(memcmp(data, texts[i].c_str(), op->bytes), 0);
-
- EXPECT_EQ(op->count, counts[i]);
- const SkPoint* op_points = op->GetArray();
- for (size_t k = 0; k < op->count; ++k)
- EXPECT_EQ(op_points[k], points[i][k]);
-
- ++iter;
- }
-
- EXPECT_FALSE(iter);
-}
-
// Verify that a SaveLayerAlpha / Draw / Restore can be optimized to just
// a draw with opacity.
TEST(PaintOpBufferTest, SaveDrawRestore) {
@@ -434,6 +458,24 @@ TEST(PaintOpBufferTest, SaveDrawRestore_SingleOpRecordWithSingleNonDrawOp) {
EXPECT_EQ(1, canvas.restore_count_);
}
+TEST(PaintOpBufferTest, SaveLayerRestore_DrawColor) {
+ PaintOpBuffer buffer;
+ uint8_t alpha = 100;
+ SkColor original = SkColorSetA(50, SK_ColorRED);
+
+ buffer.push<SaveLayerAlphaOp>(nullptr, alpha, false);
+ buffer.push<DrawColorOp>(original, SkBlendMode::kSrcOver);
+ buffer.push<RestoreOp>();
+
+ SaveCountingCanvas canvas;
+ buffer.Playback(&canvas);
+ EXPECT_EQ(canvas.save_count_, 0);
+ EXPECT_EQ(canvas.restore_count_, 0);
+
+ uint8_t expected_alpha = SkMulDiv255Round(alpha, SkColorGetA(original));
+ EXPECT_EQ(canvas.paint_.getColor(), SkColorSetA(original, expected_alpha));
+}
+
TEST(PaintOpBufferTest, DiscardableImagesTracking_EmptyBuffer) {
PaintOpBuffer buffer;
EXPECT_FALSE(buffer.HasDiscardableImages());
@@ -448,16 +490,14 @@ TEST(PaintOpBufferTest, DiscardableImagesTracking_NoImageOp) {
TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImage) {
PaintOpBuffer buffer;
- PaintImage image = PaintImage(PaintImage::GetNextId(),
- CreateDiscardableImage(gfx::Size(100, 100)));
+ PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
buffer.push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0), nullptr);
EXPECT_TRUE(buffer.HasDiscardableImages());
}
TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) {
PaintOpBuffer buffer;
- PaintImage image = PaintImage(PaintImage::GetNextId(),
- CreateDiscardableImage(gfx::Size(100, 100)));
+ PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
buffer.push<DrawImageRectOp>(
image, SkRect::MakeWH(100, 100), SkRect::MakeWH(100, 100), nullptr,
PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
@@ -467,7 +507,7 @@ TEST(PaintOpBufferTest, DiscardableImagesTracking_DrawImageRect) {
TEST(PaintOpBufferTest, DiscardableImagesTracking_OpWithFlags) {
PaintOpBuffer buffer;
PaintFlags flags;
- sk_sp<SkImage> image = CreateDiscardableImage(gfx::Size(100, 100));
+ auto image = CreateDiscardablePaintImage(gfx::Size(100, 100));
flags.setShader(PaintShader::MakeImage(std::move(image),
SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode, nullptr));
@@ -604,8 +644,7 @@ TEST(PaintOpBufferTest, NonAAPaint) {
auto buffer = sk_make_sp<PaintOpBuffer>();
EXPECT_FALSE(buffer->HasNonAAPaint());
- PaintImage image = PaintImage(PaintImage::GetNextId(),
- CreateDiscardableImage(gfx::Size(100, 100)));
+ PaintImage image = CreateDiscardablePaintImage(gfx::Size(100, 100));
PaintFlags non_aa_flags;
non_aa_flags.setAntiAlias(true);
buffer->push<DrawImageOp>(image, SkIntToScalar(0), SkIntToScalar(0),
@@ -640,15 +679,45 @@ TEST(PaintOpBufferTest, NonAAPaint) {
}
}
-TEST(PaintOpBufferTest, ContiguousIndices) {
- PaintOpBuffer buffer;
+class PaintOpBufferOffsetsTest : public ::testing::Test {
+ public:
+ void SetUp() override {}
+ void TearDown() override {
+ offsets_.clear();
+ buffer_.Reset();
+ }
+
+ template <typename T, typename... Args>
+ void push_op(Args&&... args) {
+ offsets_.push_back(buffer_.next_op_offset());
+ buffer_.push<T>(std::forward<Args>(args)...);
+ }
+
+ // Returns a subset of offsets_ by selecting only the specified indices.
+ std::vector<size_t> Select(const std::vector<size_t>& indices) {
+ std::vector<size_t> result;
+ for (size_t i : indices)
+ result.push_back(offsets_[i]);
+ return result;
+ }
+
+ void Playback(SkCanvas* canvas, const std::vector<size_t>& offsets) {
+ buffer_.Playback(canvas, nullptr, nullptr, &offsets);
+ }
+
+ protected:
+ std::vector<size_t> offsets_;
+ PaintOpBuffer buffer_;
+};
+
+TEST_F(PaintOpBufferOffsetsTest, ContiguousIndices) {
MockCanvas canvas;
- buffer.push<DrawColorOp>(0u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(1u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(2u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(3u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(4u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(0u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(1u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(2u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(3u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(4u, SkBlendMode::kClear);
// Plays all items.
testing::Sequence s;
@@ -657,18 +726,17 @@ TEST(PaintOpBufferTest, ContiguousIndices) {
EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 2, 3, 4});
+ Playback(&canvas, Select({0, 1, 2, 3, 4}));
}
-TEST(PaintOpBufferTest, NonContiguousIndices) {
- PaintOpBuffer buffer;
+TEST_F(PaintOpBufferOffsetsTest, NonContiguousIndices) {
MockCanvas canvas;
- buffer.push<DrawColorOp>(0u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(1u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(2u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(3u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(4u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(0u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(1u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(2u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(3u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(4u, SkBlendMode::kClear);
// Plays 0, 1, 3, 4 indices.
testing::Sequence s;
@@ -676,71 +744,67 @@ TEST(PaintOpBufferTest, NonContiguousIndices) {
EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 3, 4});
+ Playback(&canvas, Select({0, 1, 3, 4}));
}
-TEST(PaintOpBufferTest, FirstTwoIndices) {
- PaintOpBuffer buffer;
+TEST_F(PaintOpBufferOffsetsTest, FirstTwoIndices) {
MockCanvas canvas;
- buffer.push<DrawColorOp>(0u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(1u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(2u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(3u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(4u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(0u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(1u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(2u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(3u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(4u, SkBlendMode::kClear);
// Plays first two indices.
testing::Sequence s;
EXPECT_CALL(canvas, OnDrawPaintWithColor(0u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1});
+ Playback(&canvas, Select({0, 1}));
}
-TEST(PaintOpBufferTest, MiddleIndex) {
- PaintOpBuffer buffer;
+TEST_F(PaintOpBufferOffsetsTest, MiddleIndex) {
MockCanvas canvas;
- buffer.push<DrawColorOp>(0u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(1u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(2u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(3u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(4u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(0u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(1u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(2u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(3u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(4u, SkBlendMode::kClear);
// Plays index 2.
testing::Sequence s;
EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s);
- Playback(&buffer, &canvas, {2});
+ Playback(&canvas, Select({2}));
}
-TEST(PaintOpBufferTest, LastTwoElements) {
- PaintOpBuffer buffer;
+TEST_F(PaintOpBufferOffsetsTest, LastTwoElements) {
MockCanvas canvas;
- buffer.push<DrawColorOp>(0u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(1u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(2u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(3u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(4u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(0u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(1u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(2u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(3u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(4u, SkBlendMode::kClear);
// Plays last two elements.
testing::Sequence s;
EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&buffer, &canvas, {3, 4});
+ Playback(&canvas, Select({3, 4}));
}
-TEST(PaintOpBufferTest, ContiguousIndicesWithSaveLayerAlphaRestore) {
- PaintOpBuffer buffer;
+TEST_F(PaintOpBufferOffsetsTest, ContiguousIndicesWithSaveLayerAlphaRestore) {
MockCanvas canvas;
- buffer.push<DrawColorOp>(0u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(1u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(0u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(1u, SkBlendMode::kClear);
uint8_t alpha = 100;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha, true);
- buffer.push<RestoreOp>();
- buffer.push<DrawColorOp>(2u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(3u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(4u, SkBlendMode::kClear);
+ push_op<SaveLayerAlphaOp>(nullptr, alpha, true);
+ push_op<RestoreOp>();
+ push_op<DrawColorOp>(2u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(3u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(4u, SkBlendMode::kClear);
// Items are {0, 1, save, restore, 2, 3, 4}.
@@ -751,22 +815,22 @@ TEST(PaintOpBufferTest, ContiguousIndicesWithSaveLayerAlphaRestore) {
EXPECT_CALL(canvas, OnDrawPaintWithColor(2u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 2, 3, 4, 5, 6});
+ Playback(&canvas, Select({0, 1, 2, 3, 4, 5, 6}));
Mock::VerifyAndClearExpectations(&canvas);
}
-TEST(PaintOpBufferTest, NonContiguousIndicesWithSaveLayerAlphaRestore) {
- PaintOpBuffer buffer;
+TEST_F(PaintOpBufferOffsetsTest,
+ NonContiguousIndicesWithSaveLayerAlphaRestore) {
MockCanvas canvas;
- buffer.push<DrawColorOp>(0u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(1u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(0u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(1u, SkBlendMode::kClear);
uint8_t alpha = 100;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha, true);
- buffer.push<DrawColorOp>(2u, SkBlendMode::kClear);
- buffer.push<DrawColorOp>(3u, SkBlendMode::kClear);
- buffer.push<RestoreOp>();
- buffer.push<DrawColorOp>(4u, SkBlendMode::kClear);
+ push_op<SaveLayerAlphaOp>(nullptr, alpha, true);
+ push_op<DrawColorOp>(2u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(3u, SkBlendMode::kClear);
+ push_op<RestoreOp>();
+ push_op<DrawColorOp>(4u, SkBlendMode::kClear);
// Items are {0, 1, save, 2, 3, restore, 4}.
@@ -783,7 +847,7 @@ TEST(PaintOpBufferTest, NonContiguousIndicesWithSaveLayerAlphaRestore) {
EXPECT_CALL(canvas, OnDrawPaintWithColor(3u)).InSequence(s);
EXPECT_CALL(canvas, willRestore()).InSequence(s);
EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 2, 3, 4, 5, 6});
+ Playback(&canvas, Select({0, 1, 2, 3, 4, 5, 6}));
}
Mock::VerifyAndClearExpectations(&canvas);
@@ -794,29 +858,29 @@ TEST(PaintOpBufferTest, NonContiguousIndicesWithSaveLayerAlphaRestore) {
EXPECT_CALL(canvas, OnDrawPaintWithColor(1u)).InSequence(s);
// The now-empty SaveLayerAlpha/Restore is dropped
EXPECT_CALL(canvas, OnDrawPaintWithColor(4u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 2, 5, 6});
+ Playback(&canvas, Select({0, 1, 2, 5, 6}));
}
Mock::VerifyAndClearExpectations(&canvas);
}
-TEST(PaintOpBufferTest, ContiguousIndicesWithSaveLayerAlphaDrawRestore) {
- PaintOpBuffer buffer;
+TEST_F(PaintOpBufferOffsetsTest,
+ ContiguousIndicesWithSaveLayerAlphaDrawRestore) {
MockCanvas canvas;
- auto add_draw_rect = [](PaintOpBuffer* buffer, SkColor c) {
+ auto add_draw_rect = [this](SkColor c) {
PaintFlags flags;
flags.setColor(c);
- buffer->push<DrawRectOp>(SkRect::MakeWH(1, 1), flags);
+ push_op<DrawRectOp>(SkRect::MakeWH(1, 1), flags);
};
- add_draw_rect(&buffer, 0u);
- add_draw_rect(&buffer, 1u);
+ add_draw_rect(0u);
+ add_draw_rect(1u);
uint8_t alpha = 100;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha, true);
- add_draw_rect(&buffer, 2u);
- buffer.push<RestoreOp>();
- add_draw_rect(&buffer, 3u);
- add_draw_rect(&buffer, 4u);
+ push_op<SaveLayerAlphaOp>(nullptr, alpha, true);
+ add_draw_rect(2u);
+ push_op<RestoreOp>();
+ add_draw_rect(3u);
+ add_draw_rect(4u);
// Items are {0, 1, save, 2, restore, 3, 4}.
@@ -828,28 +892,28 @@ TEST(PaintOpBufferTest, ContiguousIndicesWithSaveLayerAlphaDrawRestore) {
EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 2, 3, 4, 5, 6});
+ Playback(&canvas, Select({0, 1, 2, 3, 4, 5, 6}));
Mock::VerifyAndClearExpectations(&canvas);
}
-TEST(PaintOpBufferTest, NonContiguousIndicesWithSaveLayerAlphaDrawRestore) {
- PaintOpBuffer buffer;
+TEST_F(PaintOpBufferOffsetsTest,
+ NonContiguousIndicesWithSaveLayerAlphaDrawRestore) {
MockCanvas canvas;
- auto add_draw_rect = [](PaintOpBuffer* buffer, SkColor c) {
+ auto add_draw_rect = [this](SkColor c) {
PaintFlags flags;
flags.setColor(c);
- buffer->push<DrawRectOp>(SkRect::MakeWH(1, 1), flags);
+ push_op<DrawRectOp>(SkRect::MakeWH(1, 1), flags);
};
- add_draw_rect(&buffer, 0u);
- add_draw_rect(&buffer, 1u);
+ add_draw_rect(0u);
+ add_draw_rect(1u);
uint8_t alpha = 100;
- buffer.push<SaveLayerAlphaOp>(nullptr, alpha, true);
- add_draw_rect(&buffer, 2u);
- add_draw_rect(&buffer, 3u);
- add_draw_rect(&buffer, 4u);
- buffer.push<RestoreOp>();
+ push_op<SaveLayerAlphaOp>(nullptr, alpha, true);
+ add_draw_rect(2u);
+ add_draw_rect(3u);
+ add_draw_rect(4u);
+ push_op<RestoreOp>();
// Items are are {0, 1, save, 2, 3, 4, restore}.
@@ -864,7 +928,7 @@ TEST(PaintOpBufferTest, NonContiguousIndicesWithSaveLayerAlphaDrawRestore) {
EXPECT_CALL(canvas, OnDrawRectWithColor(3u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s);
EXPECT_CALL(canvas, willRestore()).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 2, 3, 4, 5, 6});
+ Playback(&canvas, Select({0, 1, 2, 3, 4, 5, 6}));
}
Mock::VerifyAndClearExpectations(&canvas);
@@ -875,7 +939,7 @@ TEST(PaintOpBufferTest, NonContiguousIndicesWithSaveLayerAlphaDrawRestore) {
EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawRectWithColor(4u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 2, 5, 6});
+ Playback(&canvas, Select({0, 1, 2, 5, 6}));
}
Mock::VerifyAndClearExpectations(&canvas);
@@ -886,7 +950,254 @@ TEST(PaintOpBufferTest, NonContiguousIndicesWithSaveLayerAlphaDrawRestore) {
EXPECT_CALL(canvas, OnDrawRectWithColor(0u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawRectWithColor(1u)).InSequence(s);
EXPECT_CALL(canvas, OnDrawRectWithColor(2u)).InSequence(s);
- Playback(&buffer, &canvas, {0, 1, 2, 3, 6});
+ Playback(&canvas, Select({0, 1, 2, 3, 6}));
+ }
+}
+
+TEST_F(PaintOpBufferOffsetsTest, FlatteningIteratorEmptyList) {
+ std::vector<size_t> offsets = Select({});
+ PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets);
+ EXPECT_FALSE(iter);
+}
+
+TEST_F(PaintOpBufferOffsetsTest, FlatteningIteratorNoRecords) {
+ push_op<DrawColorOp>(0u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(1u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(2u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(3u, SkBlendMode::kClear);
+ push_op<DrawColorOp>(4u, SkBlendMode::kClear);
+
+ // all ops
+ {
+ size_t op_count = 0;
+ std::vector<size_t> offsets = Select({0, 1, 2, 3, 4});
+ for (PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets); iter;
+ ++iter) {
+ PaintOp* base_op = *iter;
+ ASSERT_EQ(base_op->GetType(), PaintOpType::DrawColor);
+ DrawColorOp* op = static_cast<DrawColorOp*>(base_op);
+ EXPECT_EQ(op_count, op->color);
+ ++op_count;
+ }
+ EXPECT_EQ(5u, op_count);
+ }
+
+ // some ops
+ {
+ size_t op_count = 0;
+ size_t expected_color = 0;
+ std::vector<size_t> offsets = Select({0, 2, 4});
+ for (PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets); iter;
+ ++iter) {
+ PaintOp* base_op = *iter;
+ ASSERT_EQ(base_op->GetType(), PaintOpType::DrawColor);
+ DrawColorOp* op = static_cast<DrawColorOp*>(base_op);
+ EXPECT_EQ(expected_color, op->color);
+ expected_color += 2;
+ ++op_count;
+ }
+ EXPECT_EQ(3u, op_count);
+ }
+
+ // no ops
+ {
+ std::vector<size_t> offsets = Select({});
+ PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets);
+ EXPECT_FALSE(iter);
+ }
+}
+
+TEST_F(PaintOpBufferOffsetsTest, FlatteningIteratorMultipleUnnestedRecords) {
+ static constexpr SkBlendMode mode = SkBlendMode::kClear;
+
+ // Deliberately start with a draw record.
+ auto internal0 = sk_make_sp<PaintOpBuffer>();
+ internal0->push<DrawColorOp>(0u, mode);
+ push_op<DrawRecordOp>(internal0);
+ push_op<DrawColorOp>(1u, mode);
+ auto internal1 = sk_make_sp<PaintOpBuffer>();
+ internal1->push<DrawColorOp>(2u, mode);
+ internal1->push<DrawColorOp>(3u, mode);
+ internal1->push<DrawColorOp>(4u, mode);
+ push_op<DrawRecordOp>(internal1);
+ // Two draw records back to back.
+ auto internal2 = sk_make_sp<PaintOpBuffer>();
+ internal2->push<DrawColorOp>(5u, mode);
+ internal2->push<DrawColorOp>(6u, mode);
+ push_op<DrawRecordOp>(internal2);
+ push_op<DrawColorOp>(7u, SkBlendMode::kClear);
+ // Test empty draw records.
+ auto internal3 = sk_make_sp<PaintOpBuffer>();
+ push_op<DrawRecordOp>(internal3);
+ auto internal4 = sk_make_sp<PaintOpBuffer>();
+ push_op<DrawRecordOp>(internal4);
+ push_op<DrawColorOp>(8u, SkBlendMode::kClear);
+ auto internal5 = sk_make_sp<PaintOpBuffer>();
+ internal5->push<DrawColorOp>(9u, mode);
+ internal5->push<DrawColorOp>(10u, mode);
+ internal5->push<DrawColorOp>(11u, mode);
+ internal5->push<DrawColorOp>(12u, mode);
+ internal5->push<DrawColorOp>(13u, mode);
+ // Deliberately end with a draw record.
+ push_op<DrawRecordOp>(internal5);
+
+ EXPECT_EQ(9u, buffer_.size());
+
+ // all ops
+ {
+ size_t op_count = 0;
+ std::vector<size_t> offsets = Select({0, 1, 2, 3, 4, 5, 6, 7, 8});
+ for (PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets); iter;
+ ++iter) {
+ PaintOp* base_op = *iter;
+ ASSERT_EQ(base_op->GetType(), PaintOpType::DrawColor);
+ DrawColorOp* op = static_cast<DrawColorOp*>(base_op);
+ EXPECT_EQ(op_count, op->color);
+ ++op_count;
+ }
+ EXPECT_EQ(14u, op_count);
+ }
+
+ // some ops
+ {
+ size_t op_count = 0;
+ std::vector<size_t> offsets = Select({2, 4, 5, 6, 8});
+ std::vector<size_t> expected_colors = {2, 3, 4, 7, 9, 10, 11, 12, 13};
+ for (PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets); iter;
+ ++iter) {
+ PaintOp* base_op = *iter;
+ ASSERT_EQ(base_op->GetType(), PaintOpType::DrawColor);
+ DrawColorOp* op = static_cast<DrawColorOp*>(base_op);
+ ASSERT_LT(op_count, expected_colors.size());
+ EXPECT_EQ(expected_colors[op_count], op->color);
+ ++op_count;
+ }
+ EXPECT_EQ(expected_colors.size(), op_count);
+ }
+
+ // no ops
+ {
+ std::vector<size_t> offsets = Select({});
+ PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets);
+ EXPECT_FALSE(iter);
+ }
+}
+
+TEST_F(PaintOpBufferOffsetsTest, FlatteningIteratorManyNestedRecordsAllEmpty) {
+ auto empty_record = sk_make_sp<PaintOpBuffer>();
+
+ auto outer_record = sk_make_sp<PaintOpBuffer>();
+ auto record_chain = sk_make_sp<PaintOpBuffer>();
+ for (size_t i = 0; i < 5; ++i) {
+ outer_record->push<DrawRecordOp>(empty_record);
+ record_chain->push<DrawRecordOp>(empty_record);
+ }
+ for (size_t i = 0; i < 5; ++i) {
+ auto next_parent = sk_make_sp<PaintOpBuffer>();
+ next_parent->push<DrawRecordOp>(record_chain);
+ record_chain = next_parent;
+ }
+ outer_record->push<DrawRecordOp>(record_chain);
+ push_op<DrawRecordOp>(outer_record);
+
+ // Test a single top level draw record op.
+ {
+ std::vector<size_t> offsets = Select({0});
+ PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets);
+ EXPECT_FALSE(iter);
+ }
+
+ // Test multiple top level draw record ops.
+ push_op<DrawRecordOp>(outer_record);
+ push_op<DrawRecordOp>(outer_record);
+ {
+ std::vector<size_t> offsets = Select({0, 1, 2});
+ PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets);
+ EXPECT_FALSE(iter);
+ }
+
+ // Test multiple top level draw record ops, but only some offsets.
+ {
+ std::vector<size_t> offsets = Select({1});
+ PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets);
+ EXPECT_FALSE(iter);
+ }
+
+ // No offsets.
+ {
+ std::vector<size_t> offsets = Select({});
+ PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets);
+ EXPECT_FALSE(iter);
+ }
+}
+
+TEST_F(PaintOpBufferOffsetsTest, FlatteningIteratorManyNestedRecords) {
+ static constexpr SkBlendMode mode = SkBlendMode::kClear;
+
+ push_op<DrawColorOp>(0u, mode);
+ auto internal0 = sk_make_sp<PaintOpBuffer>();
+ auto internal00 = sk_make_sp<PaintOpBuffer>();
+ auto internal000 = sk_make_sp<PaintOpBuffer>();
+ internal000->push<DrawColorOp>(1u, mode);
+ internal00->push<DrawRecordOp>(internal000);
+ auto internal001 = sk_make_sp<PaintOpBuffer>();
+ internal001->push<DrawColorOp>(2u, mode);
+ internal001->push<DrawColorOp>(3u, mode);
+ internal00->push<DrawRecordOp>(internal001);
+ internal0->push<DrawRecordOp>(internal00);
+ push_op<DrawRecordOp>(internal0);
+ push_op<DrawColorOp>(4u, mode);
+
+ auto chain = sk_make_sp<PaintOpBuffer>();
+ chain->push<DrawColorOp>(5u, mode);
+ chain->push<DrawColorOp>(6u, mode);
+ chain->push<DrawColorOp>(7u, mode);
+ for (size_t i = 0; i < 5; ++i) {
+ auto next_parent = sk_make_sp<PaintOpBuffer>();
+ next_parent->push<DrawRecordOp>(chain);
+ chain = next_parent;
+ }
+ push_op<DrawRecordOp>(chain);
+
+ EXPECT_EQ(4u, buffer_.size());
+
+ // all ops
+ {
+ size_t op_count = 0;
+ std::vector<size_t> offsets = Select({0, 1, 2, 3});
+ for (PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets); iter;
+ ++iter) {
+ PaintOp* base_op = *iter;
+ ASSERT_EQ(base_op->GetType(), PaintOpType::DrawColor);
+ DrawColorOp* op = static_cast<DrawColorOp*>(base_op);
+ EXPECT_EQ(op_count, op->color);
+ ++op_count;
+ }
+ EXPECT_EQ(8u, op_count);
+ }
+
+ // some ops
+ {
+ size_t op_count = 0;
+ std::vector<size_t> offsets = Select({1, 3});
+ std::vector<size_t> expected_colors = {1, 2, 3, 5, 6, 7};
+ for (PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets); iter;
+ ++iter) {
+ PaintOp* base_op = *iter;
+ ASSERT_EQ(base_op->GetType(), PaintOpType::DrawColor);
+ DrawColorOp* op = static_cast<DrawColorOp*>(base_op);
+ ASSERT_LT(op_count, expected_colors.size());
+ EXPECT_EQ(expected_colors[op_count], op->color);
+ ++op_count;
+ }
+ EXPECT_EQ(expected_colors.size(), op_count);
+ }
+
+ // no ops
+ {
+ std::vector<size_t> offsets = Select({});
+ PaintOpBuffer::FlatteningIterator iter(&buffer_, &offsets);
+ EXPECT_FALSE(iter);
}
}
@@ -1023,9 +1334,96 @@ std::vector<SkPath> test_paths = {
SkPath(),
};
-// TODO(enne): make this more real.
std::vector<PaintFlags> test_flags = {
- PaintFlags(), PaintFlags(), PaintFlags(), PaintFlags(), PaintFlags(),
+ PaintFlags(),
+ [] {
+ PaintFlags flags;
+ flags.setTextSize(82.7f);
+ flags.setColor(SK_ColorMAGENTA);
+ flags.setStrokeWidth(4.2f);
+ flags.setStrokeMiter(5.91f);
+ flags.setBlendMode(SkBlendMode::kDst);
+ flags.setStrokeCap(PaintFlags::kSquare_Cap);
+ flags.setStrokeJoin(PaintFlags::kBevel_Join);
+ flags.setStyle(PaintFlags::kStrokeAndFill_Style);
+ flags.setTextEncoding(PaintFlags::kGlyphID_TextEncoding);
+ flags.setHinting(PaintFlags::kNormal_Hinting);
+ flags.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality);
+ flags.setShader(PaintShader::MakeColor(SkColorSetARGB(1, 2, 3, 4)));
+ return flags;
+ }(),
+ [] {
+ PaintFlags flags;
+ flags.setTextSize(0.0f);
+ flags.setColor(SK_ColorCYAN);
+ flags.setAlpha(103);
+ flags.setStrokeWidth(0.32f);
+ flags.setStrokeMiter(7.98f);
+ flags.setBlendMode(SkBlendMode::kSrcOut);
+ flags.setStrokeCap(PaintFlags::kRound_Cap);
+ flags.setStrokeJoin(PaintFlags::kRound_Join);
+ flags.setStyle(PaintFlags::kFill_Style);
+ flags.setTextEncoding(PaintFlags::kUTF32_TextEncoding);
+ flags.setHinting(PaintFlags::kSlight_Hinting);
+ flags.setFilterQuality(SkFilterQuality::kHigh_SkFilterQuality);
+
+ SkScalar intervals[] = {1.f, 1.f};
+ flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
+ flags.setMaskFilter(
+ SkBlurMaskFilter::Make(SkBlurStyle::kOuter_SkBlurStyle, 4.3f,
+ test_rects[0], kHigh_SkBlurQuality));
+ flags.setColorFilter(SkColorMatrixFilter::MakeLightingFilter(
+ SK_ColorYELLOW, SK_ColorGREEN));
+
+ SkLayerDrawLooper::Builder looper_builder;
+ looper_builder.addLayer();
+ looper_builder.addLayer(2.3f, 4.5f);
+ SkLayerDrawLooper::LayerInfo layer_info;
+ layer_info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit;
+ layer_info.fColorMode = SkBlendMode::kDst;
+ layer_info.fOffset.set(-1.f, 5.2f);
+ looper_builder.addLayer(layer_info);
+ flags.setLooper(looper_builder.detach());
+
+ flags.setImageFilter(SkOffsetImageFilter::Make(10, 11, nullptr));
+
+ sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT);
+ PaintOpSerializationTestUtils::FillArbitraryShaderValues(shader.get(),
+ true);
+ flags.setShader(std::move(shader));
+
+ return flags;
+ }(),
+ [] {
+ PaintFlags flags;
+ flags.setShader(PaintShader::MakeColor(SkColorSetARGB(12, 34, 56, 78)));
+
+ return flags;
+ }(),
+ [] {
+ PaintFlags flags;
+ sk_sp<PaintShader> shader = PaintShader::MakeColor(SK_ColorTRANSPARENT);
+ PaintOpSerializationTestUtils::FillArbitraryShaderValues(shader.get(),
+ false);
+ flags.setShader(std::move(shader));
+
+ return flags;
+ }(),
+ [] {
+ PaintFlags flags;
+ SkPoint points[2] = {SkPoint::Make(1, 2), SkPoint::Make(3, 4)};
+ SkColor colors[3] = {SkColorSetARGB(1, 2, 3, 4),
+ SkColorSetARGB(4, 3, 2, 1),
+ SkColorSetARGB(0, 10, 20, 30)};
+ SkScalar positions[3] = {0.f, 0.3f, 1.f};
+ flags.setShader(PaintShader::MakeLinearGradient(
+ points, colors, positions, 3, SkShader::kMirror_TileMode));
+
+ return flags;
+ }(),
+ PaintFlags(),
+ PaintFlags(),
+ PaintFlags(),
};
std::vector<SkColor> test_colors = {
@@ -1075,12 +1473,9 @@ std::vector<sk_sp<SkTextBlob>> test_blobs = {
// ahead of time and not be bitmaps. These paint images should be fake
// gpu resource paint images.
std::vector<PaintImage> test_images = {
- PaintImage(PaintImage::GetNextId(),
- CreateDiscardableImage(gfx::Size(5, 10))),
- PaintImage(PaintImage::GetNextId(),
- CreateDiscardableImage(gfx::Size(1, 1))),
- PaintImage(PaintImage::GetNextId(),
- CreateDiscardableImage(gfx::Size(50, 50))),
+ CreateDiscardablePaintImage(gfx::Size(5, 10)),
+ CreateDiscardablePaintImage(gfx::Size(1, 1)),
+ CreateDiscardablePaintImage(gfx::Size(50, 50)),
};
// Writes as many ops in |buffer| as can fit in |output_size| to |output|.
@@ -1157,11 +1552,9 @@ class DeserializerIterator {
input_size_ != other.input_size_ || remaining_ != other.remaining_;
}
DeserializerIterator& operator++() {
- const PaintOp* serialized = reinterpret_cast<const PaintOp*>(current_);
-
- CHECK_GE(remaining_, serialized->skip);
- current_ += serialized->skip;
- remaining_ -= serialized->skip;
+ CHECK_GE(remaining_, last_bytes_read_);
+ current_ += last_bytes_read_;
+ remaining_ -= last_bytes_read_;
if (remaining_ > 0)
CHECK_GE(remaining_, 4u);
@@ -1184,6 +1577,8 @@ class DeserializerIterator {
current_(current),
input_size_(input_size),
remaining_(remaining) {
+ data_.reset(static_cast<char*>(base::AlignedAlloc(
+ sizeof(LargestPaintOp), PaintOpBuffer::PaintOpAlign)));
DeserializeCurrentOp();
}
@@ -1199,25 +1594,17 @@ class DeserializerIterator {
if (!remaining_)
return;
-
- const PaintOp* serialized = reinterpret_cast<const PaintOp*>(current_);
- size_t required = sizeof(LargestPaintOp) + serialized->skip;
-
- if (data_size_ < required) {
- data_.reset(static_cast<char*>(
- base::AlignedAlloc(required, PaintOpBuffer::PaintOpAlign)));
- data_size_ = required;
- }
deserialized_op_ =
- PaintOp::Deserialize(current_, remaining_, data_.get(), data_size_);
+ PaintOp::Deserialize(current_, remaining_, data_.get(),
+ sizeof(LargestPaintOp), &last_bytes_read_);
}
const void* input_ = nullptr;
const char* current_ = nullptr;
size_t input_size_ = 0u;
size_t remaining_ = 0u;
+ size_t last_bytes_read_ = 0u;
std::unique_ptr<char, base::AlignedFreeDeleter> data_;
- size_t data_size_ = 0u;
PaintOp* deserialized_op_ = nullptr;
};
@@ -1229,6 +1616,7 @@ void PushAnnotateOps(PaintOpBuffer* buffer) {
test_rects[1], nullptr);
buffer->push<AnnotateOp>(PaintCanvas::AnnotationType::NAMED_DESTINATION,
test_rects[2], SkData::MakeEmpty());
+ ValidateOps<AnnotateOp>(buffer);
}
void PushClipPathOps(PaintOpBuffer* buffer) {
@@ -1236,6 +1624,7 @@ void PushClipPathOps(PaintOpBuffer* buffer) {
SkClipOp op = i % 3 ? SkClipOp::kDifference : SkClipOp::kIntersect;
buffer->push<ClipPathOp>(test_paths[i], op, !!(i % 2));
}
+ ValidateOps<ClipPathOp>(buffer);
}
void PushClipRectOps(PaintOpBuffer* buffer) {
@@ -1244,6 +1633,7 @@ void PushClipRectOps(PaintOpBuffer* buffer) {
bool antialias = !!(i % 3);
buffer->push<ClipRectOp>(test_rects[i], op, antialias);
}
+ ValidateOps<ClipRectOp>(buffer);
}
void PushClipRRectOps(PaintOpBuffer* buffer) {
@@ -1252,35 +1642,20 @@ void PushClipRRectOps(PaintOpBuffer* buffer) {
bool antialias = !!(i % 3);
buffer->push<ClipRRectOp>(test_rrects[i], op, antialias);
}
+ ValidateOps<ClipRRectOp>(buffer);
}
void PushConcatOps(PaintOpBuffer* buffer) {
for (size_t i = 0; i < test_matrices.size(); ++i)
buffer->push<ConcatOp>(test_matrices[i]);
-}
-
-void PushDrawArcOps(PaintOpBuffer* buffer) {
- size_t len = std::min(std::min(test_floats.size() - 1, test_flags.size()),
- test_rects.size());
- for (size_t i = 0; i < len; ++i) {
- bool use_center = !!(i % 2);
- buffer->push<DrawArcOp>(test_rects[i], test_floats[i], test_floats[i + 1],
- use_center, test_flags[i]);
- }
-}
-
-void PushDrawCircleOps(PaintOpBuffer* buffer) {
- size_t len = std::min(test_floats.size() - 2, test_flags.size());
- for (size_t i = 0; i < len; ++i) {
- buffer->push<DrawCircleOp>(test_floats[i], test_floats[i + 1],
- test_floats[i + 2], test_flags[i]);
- }
+ ValidateOps<ConcatOp>(buffer);
}
void PushDrawColorOps(PaintOpBuffer* buffer) {
for (size_t i = 0; i < test_colors.size(); ++i) {
buffer->push<DrawColorOp>(test_colors[i], static_cast<SkBlendMode>(i));
}
+ ValidateOps<DrawColorOp>(buffer);
}
void PushDrawDRRectOps(PaintOpBuffer* buffer) {
@@ -1289,6 +1664,7 @@ void PushDrawDRRectOps(PaintOpBuffer* buffer) {
buffer->push<DrawDRRectOp>(test_rrects[i], test_rrects[i + 1],
test_flags[i]);
}
+ ValidateOps<DrawDRRectOp>(buffer);
}
void PushDrawImageOps(PaintOpBuffer* buffer) {
@@ -1303,6 +1679,7 @@ void PushDrawImageOps(PaintOpBuffer* buffer) {
// TODO(enne): maybe all these optional ops should not be optional.
buffer->push<DrawImageOp>(test_images[0], test_floats[0], test_floats[1],
nullptr);
+ ValidateOps<DrawImageOp>(buffer);
}
void PushDrawImageRectOps(PaintOpBuffer* buffer) {
@@ -1321,12 +1698,14 @@ void PushDrawImageRectOps(PaintOpBuffer* buffer) {
buffer->push<DrawImageRectOp>(test_images[0], test_rects[0], test_rects[1],
nullptr,
PaintCanvas::kStrict_SrcRectConstraint);
+ ValidateOps<DrawImageRectOp>(buffer);
}
void PushDrawIRectOps(PaintOpBuffer* buffer) {
size_t len = std::min(test_irects.size(), test_flags.size());
for (size_t i = 0; i < len; ++i)
buffer->push<DrawIRectOp>(test_irects[i], test_flags[i]);
+ ValidateOps<DrawIRectOp>(buffer);
}
void PushDrawLineOps(PaintOpBuffer* buffer) {
@@ -1336,53 +1715,35 @@ void PushDrawLineOps(PaintOpBuffer* buffer) {
test_floats[i + 2], test_floats[i + 3],
test_flags[i]);
}
+ ValidateOps<DrawLineOp>(buffer);
}
void PushDrawOvalOps(PaintOpBuffer* buffer) {
size_t len = std::min(test_paths.size(), test_flags.size());
for (size_t i = 0; i < len; ++i)
buffer->push<DrawOvalOp>(test_rects[i], test_flags[i]);
+ ValidateOps<DrawOvalOp>(buffer);
}
void PushDrawPathOps(PaintOpBuffer* buffer) {
size_t len = std::min(test_paths.size(), test_flags.size());
for (size_t i = 0; i < len; ++i)
buffer->push<DrawPathOp>(test_paths[i], test_flags[i]);
-}
-
-void PushDrawPosTextOps(PaintOpBuffer* buffer) {
- size_t len = std::min(std::min(test_flags.size(), test_strings.size()),
- test_point_arrays.size());
- for (size_t i = 0; i < len; ++i) {
- // Make sure empty array works fine.
- SkPoint* array =
- test_point_arrays[i].size() > 0 ? &test_point_arrays[i][0] : nullptr;
- buffer->push_with_array<DrawPosTextOp>(
- test_strings[i].c_str(), test_strings[i].size() + 1, array,
- test_point_arrays[i].size(), test_flags[i]);
- }
+ ValidateOps<DrawPathOp>(buffer);
}
void PushDrawRectOps(PaintOpBuffer* buffer) {
size_t len = std::min(test_rects.size(), test_flags.size());
for (size_t i = 0; i < len; ++i)
buffer->push<DrawRectOp>(test_rects[i], test_flags[i]);
+ ValidateOps<DrawRectOp>(buffer);
}
void PushDrawRRectOps(PaintOpBuffer* buffer) {
size_t len = std::min(test_rrects.size(), test_flags.size());
for (size_t i = 0; i < len; ++i)
buffer->push<DrawRRectOp>(test_rrects[i], test_flags[i]);
-}
-
-void PushDrawTextOps(PaintOpBuffer* buffer) {
- size_t len = std::min(std::min(test_strings.size(), test_flags.size()),
- test_floats.size() - 1);
- for (size_t i = 0; i < len; ++i) {
- buffer->push_with_data<DrawTextOp>(
- test_strings[i].c_str(), test_strings[i].size() + 1, test_floats[i],
- test_floats[i + 1], test_flags[i]);
- }
+ ValidateOps<DrawRRectOp>(buffer);
}
void PushDrawTextBlobOps(PaintOpBuffer* buffer) {
@@ -1392,6 +1753,7 @@ void PushDrawTextBlobOps(PaintOpBuffer* buffer) {
buffer->push<DrawTextBlobOp>(test_blobs[i], test_floats[i],
test_floats[i + 1], test_flags[i]);
}
+ ValidateOps<DrawTextBlobOp>(buffer);
}
void PushNoopOps(PaintOpBuffer* buffer) {
@@ -1399,6 +1761,7 @@ void PushNoopOps(PaintOpBuffer* buffer) {
buffer->push<NoopOp>();
buffer->push<NoopOp>();
buffer->push<NoopOp>();
+ ValidateOps<NoopOp>(buffer);
}
void PushRestoreOps(PaintOpBuffer* buffer) {
@@ -1406,11 +1769,13 @@ void PushRestoreOps(PaintOpBuffer* buffer) {
buffer->push<RestoreOp>();
buffer->push<RestoreOp>();
buffer->push<RestoreOp>();
+ ValidateOps<RestoreOp>(buffer);
}
void PushRotateOps(PaintOpBuffer* buffer) {
for (size_t i = 0; i < test_floats.size(); ++i)
buffer->push<RotateOp>(test_floats[i]);
+ ValidateOps<RotateOp>(buffer);
}
void PushSaveOps(PaintOpBuffer* buffer) {
@@ -1418,6 +1783,7 @@ void PushSaveOps(PaintOpBuffer* buffer) {
buffer->push<SaveOp>();
buffer->push<SaveOp>();
buffer->push<SaveOp>();
+ ValidateOps<SaveOp>(buffer);
}
void PushSaveLayerOps(PaintOpBuffer* buffer) {
@@ -1429,6 +1795,7 @@ void PushSaveLayerOps(PaintOpBuffer* buffer) {
buffer->push<SaveLayerOp>(nullptr, &test_flags[0]);
buffer->push<SaveLayerOp>(&test_rects[0], nullptr);
buffer->push<SaveLayerOp>(nullptr, nullptr);
+ ValidateOps<SaveLayerOp>(buffer);
}
void PushSaveLayerAlphaOps(PaintOpBuffer* buffer) {
@@ -1438,28 +1805,36 @@ void PushSaveLayerAlphaOps(PaintOpBuffer* buffer) {
// Test optional args.
buffer->push<SaveLayerAlphaOp>(nullptr, test_uint8s[0], false);
+ ValidateOps<SaveLayerAlphaOp>(buffer);
}
void PushScaleOps(PaintOpBuffer* buffer) {
for (size_t i = 0; i < test_floats.size(); i += 2)
buffer->push<ScaleOp>(test_floats[i], test_floats[i + 1]);
+ ValidateOps<ScaleOp>(buffer);
}
void PushSetMatrixOps(PaintOpBuffer* buffer) {
for (size_t i = 0; i < test_matrices.size(); ++i)
buffer->push<SetMatrixOp>(test_matrices[i]);
+ ValidateOps<SetMatrixOp>(buffer);
}
void PushTranslateOps(PaintOpBuffer* buffer) {
for (size_t i = 0; i < test_floats.size(); i += 2)
buffer->push<TranslateOp>(test_floats[i], test_floats[i + 1]);
+ ValidateOps<TranslateOp>(buffer);
}
-void CompareFlags(const PaintFlags& original, const PaintFlags& written) {}
+void CompareFlags(const PaintFlags& original, const PaintFlags& written) {
+ PaintOpSerializationTestUtils::ExpectPaintFlagsEqual(original, written);
+}
void CompareImages(const PaintImage& original, const PaintImage& written) {}
void CompareAnnotateOp(const AnnotateOp* original, const AnnotateOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->annotation_type, written->annotation_type);
EXPECT_EQ(original->rect, written->rect);
EXPECT_EQ(!!original->data, !!written->data);
@@ -1471,12 +1846,16 @@ void CompareAnnotateOp(const AnnotateOp* original, const AnnotateOp* written) {
}
void CompareClipPathOp(const ClipPathOp* original, const ClipPathOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_TRUE(original->path == written->path);
EXPECT_EQ(original->op, written->op);
EXPECT_EQ(original->antialias, written->antialias);
}
void CompareClipRectOp(const ClipRectOp* original, const ClipRectOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->rect, written->rect);
EXPECT_EQ(original->op, written->op);
EXPECT_EQ(original->antialias, written->antialias);
@@ -1484,39 +1863,31 @@ void CompareClipRectOp(const ClipRectOp* original, const ClipRectOp* written) {
void CompareClipRRectOp(const ClipRRectOp* original,
const ClipRRectOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->rrect, written->rrect);
EXPECT_EQ(original->op, written->op);
EXPECT_EQ(original->antialias, written->antialias);
}
void CompareConcatOp(const ConcatOp* original, const ConcatOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->matrix, written->matrix);
EXPECT_EQ(original->matrix.getType(), written->matrix.getType());
}
-void CompareDrawArcOp(const DrawArcOp* original, const DrawArcOp* written) {
- CompareFlags(original->flags, written->flags);
- EXPECT_EQ(original->oval, written->oval);
- EXPECT_EQ(original->start_angle, written->start_angle);
- EXPECT_EQ(original->sweep_angle, written->sweep_angle);
- EXPECT_EQ(original->use_center, written->use_center);
-}
-
-void CompareDrawCircleOp(const DrawCircleOp* original,
- const DrawCircleOp* written) {
- CompareFlags(original->flags, written->flags);
- EXPECT_EQ(original->cx, written->cx);
- EXPECT_EQ(original->cy, written->cy);
- EXPECT_EQ(original->radius, written->radius);
-}
-
void CompareDrawColorOp(const DrawColorOp* original,
const DrawColorOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->color, written->color);
}
void CompareDrawDRRectOp(const DrawDRRectOp* original,
const DrawDRRectOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_EQ(original->outer, written->outer);
EXPECT_EQ(original->inner, written->inner);
@@ -1524,6 +1895,8 @@ void CompareDrawDRRectOp(const DrawDRRectOp* original,
void CompareDrawImageOp(const DrawImageOp* original,
const DrawImageOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
CompareImages(original->image, written->image);
EXPECT_EQ(original->left, written->left);
@@ -1532,6 +1905,8 @@ void CompareDrawImageOp(const DrawImageOp* original,
void CompareDrawImageRectOp(const DrawImageRectOp* original,
const DrawImageRectOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
CompareImages(original->image, written->image);
EXPECT_EQ(original->src, written->src);
@@ -1540,11 +1915,15 @@ void CompareDrawImageRectOp(const DrawImageRectOp* original,
void CompareDrawIRectOp(const DrawIRectOp* original,
const DrawIRectOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_EQ(original->rect, written->rect);
}
void CompareDrawLineOp(const DrawLineOp* original, const DrawLineOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_EQ(original->x0, written->x0);
EXPECT_EQ(original->y0, written->y0);
@@ -1553,48 +1932,38 @@ void CompareDrawLineOp(const DrawLineOp* original, const DrawLineOp* written) {
}
void CompareDrawOvalOp(const DrawOvalOp* original, const DrawOvalOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_EQ(original->oval, written->oval);
}
void CompareDrawPathOp(const DrawPathOp* original, const DrawPathOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_TRUE(original->path == written->path);
}
-void CompareDrawPosTextOp(const DrawPosTextOp* original,
- const DrawPosTextOp* written) {
- CompareFlags(original->flags, written->flags);
- ASSERT_EQ(original->bytes, written->bytes);
- EXPECT_EQ(std::string(static_cast<const char*>(original->GetData())),
- std::string(static_cast<const char*>(written->GetData())));
- ASSERT_EQ(original->count, written->count);
- for (size_t i = 0; i < original->count; ++i)
- EXPECT_EQ(original->GetArray()[i], written->GetArray()[i]);
-}
-
void CompareDrawRectOp(const DrawRectOp* original, const DrawRectOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_EQ(original->rect, written->rect);
}
void CompareDrawRRectOp(const DrawRRectOp* original,
const DrawRRectOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_EQ(original->rrect, written->rrect);
}
-void CompareDrawTextOp(const DrawTextOp* original, const DrawTextOp* written) {
- CompareFlags(original->flags, written->flags);
- EXPECT_EQ(original->x, written->x);
- EXPECT_EQ(original->y, written->y);
- ASSERT_EQ(original->bytes, written->bytes);
- EXPECT_EQ(std::string(static_cast<const char*>(original->GetData())),
- std::string(static_cast<const char*>(written->GetData())));
-}
-
void CompareDrawTextBlobOp(const DrawTextBlobOp* original,
const DrawTextBlobOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_EQ(original->x, written->x);
EXPECT_EQ(original->y, written->y);
@@ -1627,28 +1996,40 @@ void CompareDrawTextBlobOp(const DrawTextBlobOp* original,
void CompareNoopOp(const NoopOp* original, const NoopOp* written) {
// Nothing to compare.
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
}
void CompareRestoreOp(const RestoreOp* original, const RestoreOp* written) {
// Nothing to compare.
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
}
void CompareRotateOp(const RotateOp* original, const RotateOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->degrees, written->degrees);
}
void CompareSaveOp(const SaveOp* original, const SaveOp* written) {
// Nothing to compare.
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
}
void CompareSaveLayerOp(const SaveLayerOp* original,
const SaveLayerOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
CompareFlags(original->flags, written->flags);
EXPECT_EQ(original->bounds, written->bounds);
}
void CompareSaveLayerAlphaOp(const SaveLayerAlphaOp* original,
const SaveLayerAlphaOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->bounds, written->bounds);
EXPECT_EQ(original->alpha, written->alpha);
EXPECT_EQ(original->preserve_lcd_text_requests,
@@ -1656,17 +2037,23 @@ void CompareSaveLayerAlphaOp(const SaveLayerAlphaOp* original,
}
void CompareScaleOp(const ScaleOp* original, const ScaleOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->sx, written->sx);
EXPECT_EQ(original->sy, written->sy);
}
void CompareSetMatrixOp(const SetMatrixOp* original,
const SetMatrixOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->matrix, written->matrix);
}
void CompareTranslateOp(const TranslateOp* original,
const TranslateOp* written) {
+ EXPECT_TRUE(original->IsValid());
+ EXPECT_TRUE(written->IsValid());
EXPECT_EQ(original->dx, written->dx);
EXPECT_EQ(original->dy, written->dy);
}
@@ -1694,12 +2081,6 @@ class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
case PaintOpType::Concat:
PushConcatOps(&buffer_);
break;
- case PaintOpType::DrawArc:
- PushDrawArcOps(&buffer_);
- break;
- case PaintOpType::DrawCircle:
- PushDrawCircleOps(&buffer_);
- break;
case PaintOpType::DrawColor:
PushDrawColorOps(&buffer_);
break;
@@ -1724,9 +2105,6 @@ class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
case PaintOpType::DrawPath:
PushDrawPathOps(&buffer_);
break;
- case PaintOpType::DrawPosText:
- PushDrawPosTextOps(&buffer_);
- break;
case PaintOpType::DrawRecord:
// Not supported.
break;
@@ -1736,9 +2114,6 @@ class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
case PaintOpType::DrawRRect:
PushDrawRRectOps(&buffer_);
break;
- case PaintOpType::DrawText:
- PushDrawTextOps(&buffer_);
- break;
case PaintOpType::DrawTextBlob:
PushDrawTextBlobOps(&buffer_);
break;
@@ -1799,14 +2174,6 @@ class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
CompareConcatOp(static_cast<const ConcatOp*>(original),
static_cast<const ConcatOp*>(written));
break;
- case PaintOpType::DrawArc:
- CompareDrawArcOp(static_cast<const DrawArcOp*>(original),
- static_cast<const DrawArcOp*>(written));
- break;
- case PaintOpType::DrawCircle:
- CompareDrawCircleOp(static_cast<const DrawCircleOp*>(original),
- static_cast<const DrawCircleOp*>(written));
- break;
case PaintOpType::DrawColor:
CompareDrawColorOp(static_cast<const DrawColorOp*>(original),
static_cast<const DrawColorOp*>(written));
@@ -1839,10 +2206,6 @@ class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
CompareDrawPathOp(static_cast<const DrawPathOp*>(original),
static_cast<const DrawPathOp*>(written));
break;
- case PaintOpType::DrawPosText:
- CompareDrawPosTextOp(static_cast<const DrawPosTextOp*>(original),
- static_cast<const DrawPosTextOp*>(written));
- break;
case PaintOpType::DrawRecord:
// Not supported.
break;
@@ -1854,10 +2217,6 @@ class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
CompareDrawRRectOp(static_cast<const DrawRRectOp*>(original),
static_cast<const DrawRRectOp*>(written));
break;
- case PaintOpType::DrawText:
- CompareDrawTextOp(static_cast<const DrawTextOp*>(original),
- static_cast<const DrawTextOp*>(written));
- break;
case PaintOpType::DrawTextBlob:
CompareDrawTextBlobOp(static_cast<const DrawTextBlobOp*>(original),
static_cast<const DrawTextBlobOp*>(written));
@@ -1904,7 +2263,7 @@ class PaintOpSerializationTest : public ::testing::TestWithParam<uint8_t> {
void ResizeOutputBuffer() {
// An arbitrary deserialization buffer size that should fit all the ops
// in the buffer_.
- output_size_ = (100 + sizeof(LargestPaintOp)) * buffer_.size();
+ output_size_ = kBufferBytesPerOp * buffer_.size();
output_.reset(static_cast<char*>(
base::AlignedAlloc(output_size_, PaintOpBuffer::PaintOpAlign)));
}
@@ -1948,15 +2307,17 @@ TEST_P(PaintOpSerializationTest, SmokeTest) {
}
PaintOpBuffer::Iterator iter(&buffer_);
+ size_t i = 0;
for (auto* base_written :
DeserializerIterator(output_.get(), serializer.TotalBytesWritten())) {
SCOPED_TRACE(base::StringPrintf(
- "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), iter.op_idx()));
+ "%s #%zu", PaintOpTypeToString(GetParamType()).c_str(), i));
ExpectOpsEqual(*iter, base_written);
++iter;
+ ++i;
}
- EXPECT_EQ(buffer_.size(), iter.op_idx());
+ EXPECT_EQ(buffer_.size(), i);
}
// Verify for all test ops that serializing into a smaller size aborts
@@ -1975,15 +2336,16 @@ TEST_P(PaintOpSerializationTest, SerializationFailures) {
PaintOp::SerializeOptions options;
- for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter) {
+ size_t op_idx = 0;
+ for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter, ++op_idx) {
SCOPED_TRACE(base::StringPrintf(
- "%s #%zd", PaintOpTypeToString(GetParamType()).c_str(), iter.op_idx()));
- size_t expected_bytes = bytes_written[iter.op_idx()];
+ "%s #%zu", PaintOpTypeToString(GetParamType()).c_str(), op_idx));
+ size_t expected_bytes = bytes_written[op_idx];
EXPECT_GT(expected_bytes, 0u);
// Attempt to write op into a buffer of size |i|, and only expect
// it to succeed if the buffer is large enough.
- for (size_t i = 0; i < bytes_written[iter.op_idx()] + 2; ++i) {
+ for (size_t i = 0; i < bytes_written[op_idx] + 2; ++i) {
size_t written_bytes = iter->Serialize(output_.get(), i, options);
if (i >= expected_bytes) {
EXPECT_EQ(expected_bytes, written_bytes) << "i: " << i;
@@ -2010,11 +2372,12 @@ TEST_P(PaintOpSerializationTest, DeserializationFailures) {
char* current = static_cast<char*>(output_.get());
static constexpr size_t kAlign = PaintOpBuffer::PaintOpAlign;
- static constexpr size_t kOutputOpSize = sizeof(LargestPaintOp) + 100;
+ static constexpr size_t kOutputOpSize = kBufferBytesPerOp;
std::unique_ptr<char, base::AlignedFreeDeleter> deserialize_buffer_(
static_cast<char*>(base::AlignedAlloc(kOutputOpSize, kAlign)));
- for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter) {
+ size_t op_idx = 0;
+ for (PaintOpBuffer::Iterator iter(&buffer_); iter; ++iter, ++op_idx) {
PaintOp* serialized = reinterpret_cast<PaintOp*>(current);
uint32_t skip = serialized->skip;
@@ -2023,23 +2386,25 @@ TEST_P(PaintOpSerializationTest, DeserializationFailures) {
// deserialization failure to return nullptr. Also test a few valid sizes
// larger than read size.
for (size_t read_size = 0; read_size < skip + kAlign * 2 + 2; ++read_size) {
- SCOPED_TRACE(
- base::StringPrintf("%s #%zd, read_size: %zd",
- PaintOpTypeToString(GetParamType()).c_str(),
- iter.op_idx(), read_size));
+ SCOPED_TRACE(base::StringPrintf(
+ "%s #%zd, read_size: %zu",
+ PaintOpTypeToString(GetParamType()).c_str(), op_idx, read_size));
// Because PaintOp::Deserialize early outs when the input size is < skip
// deliberately lie about the skip. This op tooooootally fits.
// This will verify that individual op deserializing code behaves
// properly when presented with invalid offsets.
serialized->skip = read_size;
- PaintOp* written = PaintOp::Deserialize(
- current, read_size, deserialize_buffer_.get(), kOutputOpSize);
+ size_t bytes_read = 0;
+ PaintOp* written =
+ PaintOp::Deserialize(current, read_size, deserialize_buffer_.get(),
+ kOutputOpSize, &bytes_read);
// Skips are only valid if they are aligned.
if (read_size >= skip && read_size % kAlign == 0) {
ASSERT_NE(nullptr, written);
ASSERT_LE(written->skip, kOutputOpSize);
EXPECT_EQ(GetParamType(), written->GetType());
+ EXPECT_EQ(serialized->skip, bytes_read);
} else {
EXPECT_EQ(nullptr, written);
}
@@ -2073,28 +2438,534 @@ TEST(PaintOpBufferTest, PaintOpDeserialize) {
ASSERT_GT(bytes_written, 0u);
// can deserialize from exactly the right size
- PaintOp* success =
- PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize);
+ size_t bytes_read = 0;
+ PaintOp* success = PaintOp::Deserialize(input_.get(), bytes_written,
+ output_.get(), kSize, &bytes_read);
ASSERT_TRUE(success);
+ EXPECT_EQ(bytes_written, bytes_read);
success->DestroyThis();
// fail to deserialize if skip goes past input size
// (the DeserializationFailures test above tests if the skip is lying)
for (size_t i = 0; i < bytes_written - 1; ++i)
- EXPECT_FALSE(PaintOp::Deserialize(input_.get(), i, output_.get(), kSize));
+ EXPECT_FALSE(PaintOp::Deserialize(input_.get(), i, output_.get(), kSize,
+ &bytes_read));
// unaligned skips fail to deserialize
PaintOp* serialized = reinterpret_cast<PaintOp*>(input_.get());
EXPECT_EQ(0u, serialized->skip % kAlign);
serialized->skip -= 1;
- EXPECT_FALSE(
- PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize));
+ EXPECT_FALSE(PaintOp::Deserialize(input_.get(), bytes_written, output_.get(),
+ kSize, &bytes_read));
serialized->skip += 1;
// bogus types fail to deserialize
serialized->type = static_cast<uint8_t>(PaintOpType::LastPaintOpType) + 1;
- EXPECT_FALSE(
- PaintOp::Deserialize(input_.get(), bytes_written, output_.get(), kSize));
+ EXPECT_FALSE(PaintOp::Deserialize(input_.get(), bytes_written, output_.get(),
+ kSize, &bytes_read));
+}
+
+// Test that deserializing invalid SkClipOp enums fails silently.
+// Skia release asserts on this in several places so these are not safe
+// to pass through to the SkCanvas API.
+TEST(PaintOpBufferTest, ValidateSkClip) {
+ size_t buffer_size = kBufferBytesPerOp;
+ std::unique_ptr<char, base::AlignedFreeDeleter> serialized(static_cast<char*>(
+ base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
+ std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
+ static_cast<char*>(
+ base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
+
+ PaintOpBuffer buffer;
+
+ // Successful first op.
+ SkPath path;
+ buffer.push<ClipPathOp>(path, SkClipOp::kMax_EnumValue, true);
+
+ // Bad other ops.
+ SkClipOp bad_clip = static_cast<SkClipOp>(
+ static_cast<uint32_t>(SkClipOp::kMax_EnumValue) + 1);
+
+ buffer.push<ClipPathOp>(path, bad_clip, true);
+ buffer.push<ClipRectOp>(test_rects[0], bad_clip, true);
+ buffer.push<ClipRRectOp>(test_rrects[0], bad_clip, false);
+
+ SkClipOp bad_clip_max = static_cast<SkClipOp>(~static_cast<uint32_t>(0));
+ buffer.push<ClipRectOp>(test_rects[1], bad_clip_max, false);
+
+ PaintOp::SerializeOptions options;
+
+ int op_idx = 0;
+ for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
+ const PaintOp* op = *iter;
+ size_t bytes_written =
+ op->Serialize(serialized.get(), buffer_size, options);
+ ASSERT_GT(bytes_written, 0u);
+ size_t bytes_read = 0;
+ PaintOp* written =
+ PaintOp::Deserialize(serialized.get(), bytes_written,
+ deserialized.get(), buffer_size, &bytes_read);
+ // First op should succeed. Other ops with bad enums should
+ // serialize correctly but fail to deserialize due to the bad
+ // SkClipOp enum.
+ if (!op_idx) {
+ EXPECT_TRUE(written) << "op: " << op_idx;
+ EXPECT_EQ(bytes_written, bytes_read);
+ written->DestroyThis();
+ } else {
+ EXPECT_FALSE(written) << "op: " << op_idx;
+ }
+
+ ++op_idx;
+ }
+}
+
+TEST(PaintOpBufferTest, ValidateSkBlendMode) {
+ size_t buffer_size = kBufferBytesPerOp;
+ std::unique_ptr<char, base::AlignedFreeDeleter> serialized(static_cast<char*>(
+ base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
+ std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
+ static_cast<char*>(
+ base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
+
+ PaintOpBuffer buffer;
+
+ // Successful first two ops.
+ buffer.push<DrawColorOp>(SK_ColorMAGENTA, SkBlendMode::kDstIn);
+ PaintFlags good_flags = test_flags[0];
+ good_flags.setBlendMode(SkBlendMode::kColorBurn);
+ buffer.push<DrawRectOp>(test_rects[0], good_flags);
+
+ // Modes that are not supported by drawColor or SkPaint.
+ SkBlendMode bad_modes_for_draw_color[] = {
+ SkBlendMode::kOverlay,
+ SkBlendMode::kDarken,
+ SkBlendMode::kLighten,
+ SkBlendMode::kColorDodge,
+ SkBlendMode::kColorBurn,
+ SkBlendMode::kHardLight,
+ SkBlendMode::kSoftLight,
+ SkBlendMode::kDifference,
+ SkBlendMode::kExclusion,
+ SkBlendMode::kMultiply,
+ SkBlendMode::kHue,
+ SkBlendMode::kSaturation,
+ SkBlendMode::kColor,
+ SkBlendMode::kLuminosity,
+ static_cast<SkBlendMode>(static_cast<uint32_t>(SkBlendMode::kLastMode) +
+ 1),
+ static_cast<SkBlendMode>(static_cast<uint32_t>(~0)),
+ };
+
+ SkBlendMode bad_modes_for_flags[] = {
+ static_cast<SkBlendMode>(static_cast<uint32_t>(SkBlendMode::kLastMode) +
+ 1),
+ static_cast<SkBlendMode>(static_cast<uint32_t>(~0)),
+ };
+
+ for (size_t i = 0; i < arraysize(bad_modes_for_draw_color); ++i) {
+ buffer.push<DrawColorOp>(SK_ColorMAGENTA, bad_modes_for_draw_color[i]);
+ }
+
+ for (size_t i = 0; i < arraysize(bad_modes_for_flags); ++i) {
+ PaintFlags flags = test_flags[i % test_flags.size()];
+ flags.setBlendMode(bad_modes_for_flags[i]);
+ buffer.push<DrawRectOp>(test_rects[i % test_rects.size()], flags);
+ }
+
+ PaintOp::SerializeOptions options;
+
+ int op_idx = 0;
+ for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
+ const PaintOp* op = *iter;
+ size_t bytes_written =
+ op->Serialize(serialized.get(), buffer_size, options);
+ ASSERT_GT(bytes_written, 0u);
+ size_t bytes_read = 0;
+ PaintOp* written =
+ PaintOp::Deserialize(serialized.get(), bytes_written,
+ deserialized.get(), buffer_size, &bytes_read);
+ // First two ops should succeed. Other ops with bad enums should
+ // serialize correctly but fail to deserialize due to the bad
+ // SkBlendMode enum.
+ if (op_idx < 2) {
+ EXPECT_TRUE(written) << "op: " << op_idx;
+ EXPECT_EQ(bytes_written, bytes_read);
+ written->DestroyThis();
+ } else {
+ EXPECT_FALSE(written) << "op: " << op_idx;
+ }
+
+ ++op_idx;
+ }
+}
+
+TEST(PaintOpBufferTest, ValidateRects) {
+ size_t buffer_size = kBufferBytesPerOp;
+ std::unique_ptr<char, base::AlignedFreeDeleter> serialized(static_cast<char*>(
+ base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
+ std::unique_ptr<char, base::AlignedFreeDeleter> deserialized(
+ static_cast<char*>(
+ base::AlignedAlloc(buffer_size, PaintOpBuffer::PaintOpAlign)));
+
+ SkRect bad_rect = SkRect::MakeEmpty();
+ bad_rect.fBottom = std::numeric_limits<float>::quiet_NaN();
+ EXPECT_FALSE(bad_rect.isFinite());
+
+ // Push all op variations that take rects.
+ PaintOpBuffer buffer;
+ buffer.push<AnnotateOp>(PaintCanvas::AnnotationType::URL, bad_rect,
+ SkData::MakeWithCString("test1"));
+ buffer.push<ClipRectOp>(bad_rect, SkClipOp::kDifference, true);
+
+ buffer.push<DrawImageRectOp>(test_images[0], bad_rect, test_rects[1], nullptr,
+ PaintCanvas::kStrict_SrcRectConstraint);
+ buffer.push<DrawImageRectOp>(test_images[0], test_rects[0], bad_rect, nullptr,
+ PaintCanvas::kStrict_SrcRectConstraint);
+ buffer.push<DrawOvalOp>(bad_rect, test_flags[0]);
+ buffer.push<DrawRectOp>(bad_rect, test_flags[0]);
+ buffer.push<SaveLayerOp>(&bad_rect, nullptr);
+ buffer.push<SaveLayerOp>(&bad_rect, &test_flags[0]);
+ buffer.push<SaveLayerAlphaOp>(&bad_rect, test_uint8s[0], true);
+
+ PaintOp::SerializeOptions options;
+
+ // Every op should serialize but fail to deserialize due to the bad rect.
+ int op_idx = 0;
+ for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) {
+ const PaintOp* op = *iter;
+ size_t bytes_written =
+ op->Serialize(serialized.get(), buffer_size, options);
+ ASSERT_GT(bytes_written, 0u);
+ size_t bytes_read = 0;
+ PaintOp* written =
+ PaintOp::Deserialize(serialized.get(), bytes_written,
+ deserialized.get(), buffer_size, &bytes_read);
+ EXPECT_FALSE(written) << "op: " << op_idx;
+ ++op_idx;
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawImageOp) {
+ PaintOpBuffer buffer;
+ PushDrawImageOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawImageOp*>(base_op);
+
+ SkRect image_rect =
+ SkRect::MakeXYWH(op->left, op->top, op->image.GetSkImage()->width(),
+ op->image.GetSkImage()->height());
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, image_rect.makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawImageRectOp) {
+ PaintOpBuffer buffer;
+ PushDrawImageRectOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawImageRectOp*>(base_op);
+
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, op->dst.makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawIRectOp) {
+ PaintOpBuffer buffer;
+ PushDrawIRectOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawIRectOp*>(base_op);
+
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, SkRect::Make(op->rect).makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawOvalOp) {
+ PaintOpBuffer buffer;
+ PushDrawOvalOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawOvalOp*>(base_op);
+
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, op->oval.makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawPathOp) {
+ PaintOpBuffer buffer;
+ PushDrawPathOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawPathOp*>(base_op);
+
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, op->path.getBounds().makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawRectOp) {
+ PaintOpBuffer buffer;
+ PushDrawRectOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawRectOp*>(base_op);
+
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, op->rect.makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawRRectOp) {
+ PaintOpBuffer buffer;
+ PushDrawRRectOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawRRectOp*>(base_op);
+
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, op->rrect.rect().makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawLineOp) {
+ PaintOpBuffer buffer;
+ PushDrawLineOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawLineOp*>(base_op);
+
+ SkRect line_rect;
+ line_rect.fLeft = op->x0;
+ line_rect.fTop = op->y0;
+ line_rect.fRight = op->x1;
+ line_rect.fBottom = op->y1;
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, line_rect.makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawDRRectOp) {
+ PaintOpBuffer buffer;
+ PushDrawDRRectOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawDRRectOp*>(base_op);
+
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, op->outer.getBounds().makeSorted());
+ }
+}
+
+TEST(PaintOpBufferTest, BoundingRect_DrawTextBlobOp) {
+ PaintOpBuffer buffer;
+ PushDrawTextBlobOps(&buffer);
+
+ SkRect rect;
+ for (auto* base_op : PaintOpBuffer::Iterator(&buffer)) {
+ auto* op = static_cast<DrawTextBlobOp*>(base_op);
+
+ ASSERT_TRUE(PaintOp::GetBounds(op, &rect));
+ EXPECT_EQ(rect, op->blob->bounds().makeOffset(op->x, op->y).makeSorted());
+ }
+}
+
+class MockImageProvider : public ImageProvider {
+ public:
+ MockImageProvider() = default;
+ explicit MockImageProvider(bool fail_all_decodes)
+ : fail_all_decodes_(fail_all_decodes) {}
+ MockImageProvider(std::vector<SkSize> src_rect_offset,
+ std::vector<SkSize> scale,
+ std::vector<SkFilterQuality> quality)
+ : src_rect_offset_(src_rect_offset), scale_(scale), quality_(quality) {}
+
+ ~MockImageProvider() override = default;
+
+ ScopedDecodedDrawImage GetDecodedDrawImage(const PaintImage& paint_image,
+ const SkRect& src_rect,
+ SkFilterQuality filter_quality,
+ const SkMatrix& matrix) override {
+ if (fail_all_decodes_)
+ return ScopedDecodedDrawImage();
+
+ SkBitmap bitmap;
+ bitmap.allocN32Pixels(10, 10);
+ sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
+ size_t i = index_++;
+ return ScopedDecodedDrawImage(
+ DecodedDrawImage(image, src_rect_offset_[i], scale_[i], quality_[i]));
+ }
+
+ private:
+ std::vector<SkSize> src_rect_offset_;
+ std::vector<SkSize> scale_;
+ std::vector<SkFilterQuality> quality_;
+ size_t index_ = 0;
+ bool fail_all_decodes_ = false;
+};
+
+TEST(PaintOpBufferTest, SkipsOpsOutsideClip) {
+ // All ops with images draw outside the clip and should be skipped. If any
+ // call is made to the ImageProvider, it should crash.
+ MockImageProvider image_provider;
+ PaintOpBuffer buffer;
+
+ // Apply a clip outside the region for images.
+ buffer.push<ClipRectOp>(SkRect::MakeXYWH(0, 0, 100, 100),
+ SkClipOp::kIntersect, false);
+
+ SkRect rect = SkRect::MakeXYWH(0, 0, 100, 100);
+ uint8_t alpha = 220;
+ buffer.push<SaveLayerAlphaOp>(&rect, alpha, false);
+
+ PaintFlags flags;
+ PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
+ buffer.push<DrawImageOp>(paint_image, 105.0f, 105.0f, &flags);
+ PaintFlags image_flags;
+ image_flags.setShader(
+ PaintShader::MakeImage(paint_image, SkShader::TileMode::kRepeat_TileMode,
+ SkShader::TileMode::kRepeat_TileMode, nullptr));
+ buffer.push<DrawRectOp>(SkRect::MakeXYWH(110, 110, 100, 100), image_flags);
+
+ buffer.push<DrawRectOp>(rect, PaintFlags());
+ buffer.push<RestoreOp>();
+
+ // Using a strict mock to ensure that skipping image ops optimizes the
+ // save/restore sequences. The single save/restore call is from the
+ // PaintOpBuffer's use of SkAutoRestoreCanvas.
+ testing::StrictMock<MockCanvas> canvas;
+ testing::Sequence s;
+ EXPECT_CALL(canvas, willSave()).InSequence(s);
+ EXPECT_CALL(canvas, OnDrawRectWithColor(_)).InSequence(s);
+ EXPECT_CALL(canvas, willRestore()).InSequence(s);
+ buffer.Playback(&canvas, &image_provider);
+}
+
+TEST(PaintOpBufferTest, SkipsOpsWithFailedDecodes) {
+ MockImageProvider image_provider(true);
+ PaintOpBuffer buffer;
+
+ PaintFlags flags;
+ PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
+ buffer.push<DrawImageOp>(paint_image, 105.0f, 105.0f, &flags);
+ PaintFlags image_flags;
+ image_flags.setShader(
+ PaintShader::MakeImage(paint_image, SkShader::TileMode::kRepeat_TileMode,
+ SkShader::TileMode::kRepeat_TileMode, nullptr));
+ buffer.push<DrawRectOp>(SkRect::MakeXYWH(110, 110, 100, 100), image_flags);
+ buffer.push<DrawColorOp>(SK_ColorRED, SkBlendMode::kSrcOver);
+
+ testing::StrictMock<MockCanvas> canvas;
+ testing::Sequence s;
+ EXPECT_CALL(canvas, OnDrawPaintWithColor(_)).InSequence(s);
+ buffer.Playback(&canvas, &image_provider);
+}
+
+MATCHER(NonLazyImage, "") {
+ return !arg->isLazyGenerated();
+}
+
+MATCHER_P(MatchesInvScale, expected, "") {
+ SkSize scale;
+ arg.decomposeScale(&scale, nullptr);
+ SkSize inv = SkSize::Make(1.0f / scale.width(), 1.0f / scale.height());
+ return inv == expected;
+}
+
+MATCHER_P2(MatchesRect, rect, scale, "") {
+ EXPECT_EQ(arg->x(), rect.x() * scale.width());
+ EXPECT_EQ(arg->y(), rect.y() * scale.height());
+ EXPECT_EQ(arg->width(), rect.width() * scale.width());
+ EXPECT_EQ(arg->height(), rect.height() * scale.height());
+ return true;
+}
+
+MATCHER_P(MatchesQuality, quality, "") {
+ return quality == arg->getFilterQuality();
+}
+
+MATCHER_P2(MatchesShader, flags, scale, "") {
+ SkMatrix matrix;
+ SkShader::TileMode xy[2];
+ SkImage* image = arg.getShader()->isAImage(&matrix, xy);
+
+ EXPECT_FALSE(image->isLazyGenerated());
+
+ SkSize local_scale;
+ matrix.decomposeScale(&local_scale, nullptr);
+ EXPECT_EQ(local_scale.width(), 1.0f / scale.width());
+ EXPECT_EQ(local_scale.height(), 1.0f / scale.height());
+
+ EXPECT_EQ(flags.getShader()->tx(), xy[0]);
+ EXPECT_EQ(flags.getShader()->ty(), xy[1]);
+
+ return true;
+};
+
+TEST(PaintOpBufferTest, ReplacesImagesFromProvider) {
+ std::vector<SkSize> src_rect_offset = {
+ SkSize::MakeEmpty(), SkSize::Make(2.0f, 2.0f), SkSize::Make(3.0f, 3.0f)};
+ std::vector<SkSize> scale_adjustment = {SkSize::Make(0.2f, 0.2f),
+ SkSize::Make(0.3f, 0.3f),
+ SkSize::Make(0.4f, 0.4f)};
+ std::vector<SkFilterQuality> quality = {
+ kHigh_SkFilterQuality, kMedium_SkFilterQuality, kHigh_SkFilterQuality};
+
+ MockImageProvider image_provider(src_rect_offset, scale_adjustment, quality);
+ PaintOpBuffer buffer;
+
+ SkRect rect = SkRect::MakeWH(10, 10);
+ PaintFlags flags;
+ flags.setFilterQuality(kLow_SkFilterQuality);
+ PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(10, 10));
+ buffer.push<DrawImageOp>(paint_image, 0.0f, 0.0f, &flags);
+ buffer.push<DrawImageRectOp>(
+ paint_image, rect, rect, &flags,
+ PaintCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ flags.setShader(
+ PaintShader::MakeImage(paint_image, SkShader::TileMode::kRepeat_TileMode,
+ SkShader::TileMode::kRepeat_TileMode, nullptr));
+ buffer.push<DrawOvalOp>(SkRect::MakeWH(10, 10), flags);
+
+ testing::StrictMock<MockCanvas> canvas;
+ testing::Sequence s;
+
+ // Save/scale/image/restore from DrawImageop.
+ EXPECT_CALL(canvas, willSave()).InSequence(s);
+ EXPECT_CALL(canvas, didConcat(MatchesInvScale(scale_adjustment[0])));
+ EXPECT_CALL(canvas, onDrawImage(NonLazyImage(), 0.0f, 0.0f,
+ MatchesQuality(quality[0])));
+ EXPECT_CALL(canvas, willRestore()).InSequence(s);
+
+ // DrawImageRectop.
+ SkRect src_rect =
+ rect.makeOffset(src_rect_offset[1].width(), src_rect_offset[1].height());
+ EXPECT_CALL(canvas,
+ onDrawImageRect(
+ NonLazyImage(), MatchesRect(src_rect, scale_adjustment[1]),
+ SkRect::MakeWH(10, 10), MatchesQuality(quality[1]),
+ SkCanvas::kFast_SrcRectConstraint));
+
+ // DrawOvalop.
+ EXPECT_CALL(canvas, onDrawOval(SkRect::MakeWH(10, 10),
+ MatchesShader(flags, scale_adjustment[2])));
+
+ buffer.Playback(&canvas, &image_provider);
}
} // namespace cc
diff --git a/chromium/cc/paint/paint_op_reader.cc b/chromium/cc/paint/paint_op_reader.cc
index 318974b0413..692c5ac934b 100644
--- a/chromium/cc/paint/paint_op_reader.cc
+++ b/chromium/cc/paint/paint_op_reader.cc
@@ -7,25 +7,80 @@
#include <stddef.h>
#include "cc/paint/paint_flags.h"
+#include "cc/paint/paint_shader.h"
+#include "third_party/skia/include/core/SkFlattenableSerialization.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkTextBlob.h"
namespace cc {
+namespace {
+
+// If we have more than this many colors, abort deserialization.
+const size_t kMaxShaderColorsSupported = 10000;
+
+bool IsValidPaintShaderType(PaintShader::Type type) {
+ return static_cast<uint8_t>(type) <
+ static_cast<uint8_t>(PaintShader::Type::kShaderCount);
+}
+
+bool IsValidSkShaderTileMode(SkShader::TileMode mode) {
+ return mode < SkShader::kTileModeCount;
+}
+
+bool IsValidPaintShaderScalingBehavior(PaintShader::ScalingBehavior behavior) {
+ return behavior == PaintShader::ScalingBehavior::kRasterAtScale ||
+ behavior == PaintShader::ScalingBehavior::kFixedScale;
+}
+
+} // namespace
template <typename T>
void PaintOpReader::ReadSimple(T* val) {
+ static_assert(base::is_trivially_copyable<T>::value,
+ "Not trivially copyable");
+ if (!AlignMemory(alignof(T)))
+ valid_ = false;
if (remaining_bytes_ < sizeof(T))
valid_ = false;
if (!valid_)
return;
- *val = reinterpret_cast<const T*>(memory_)[0];
+ // Most of the time this is used for primitives, but this function is also
+ // used for SkRect/SkIRect/SkMatrix whose implicit operator= can't use a
+ // volatile. TOCTOU violations don't matter for these simple types so
+ // use assignment.
+ *val = *reinterpret_cast<const T*>(const_cast<const char*>(memory_));
memory_ += sizeof(T);
remaining_bytes_ -= sizeof(T);
}
+template <typename T>
+void PaintOpReader::ReadFlattenable(sk_sp<T>* val) {
+ size_t bytes = 0;
+ ReadSimple(&bytes);
+ if (remaining_bytes_ < bytes)
+ valid_ = false;
+ if (!SkIsAlign4(reinterpret_cast<uintptr_t>(memory_)))
+ valid_ = false;
+ if (!valid_)
+ return;
+ if (bytes == 0)
+ return;
+
+ // This is assumed safe from TOCTOU violations as the flattenable
+ // deserializing function uses an SkReadBuffer which reads each piece of
+ // memory once much like PaintOpReader does.
+ val->reset(static_cast<T*>(SkValidatingDeserializeFlattenable(
+ const_cast<const char*>(memory_), bytes, T::GetFlattenableType())));
+ if (!val)
+ valid_ = false;
+
+ memory_ += bytes;
+ remaining_bytes_ -= bytes;
+}
+
void PaintOpReader::ReadData(size_t bytes, void* data) {
if (remaining_bytes_ < bytes)
valid_ = false;
@@ -34,7 +89,7 @@ void PaintOpReader::ReadData(size_t bytes, void* data) {
if (bytes == 0)
return;
- memcpy(data, memory_, bytes);
+ memcpy(data, const_cast<const char*>(memory_), bytes);
memory_ += bytes;
remaining_bytes_ -= bytes;
}
@@ -51,7 +106,7 @@ void PaintOpReader::ReadArray(size_t count, SkPoint* array) {
if (count == 0)
return;
- memcpy(array, memory_, bytes);
+ memcpy(array, const_cast<const char*>(memory_), bytes);
memory_ += bytes;
remaining_bytes_ -= bytes;
}
@@ -84,8 +139,12 @@ void PaintOpReader::Read(SkPath* path) {
if (!valid_)
return;
- // TODO(enne): Should the writer write how many bytes it expects as well?
- size_t read_bytes = path->readFromMemory(memory_, remaining_bytes_);
+ // This is assumed safe from TOCTOU violations as the SkPath deserializing
+ // function uses an SkRBuffer which reads each piece of memory once much
+ // like PaintOpReader does. Additionally, paths are later validated in
+ // PaintOpBuffer.
+ size_t read_bytes =
+ path->readFromMemory(const_cast<const char*>(memory_), remaining_bytes_);
if (!read_bytes)
valid_ = false;
@@ -94,7 +153,24 @@ void PaintOpReader::Read(SkPath* path) {
}
void PaintOpReader::Read(PaintFlags* flags) {
- // TODO(enne): implement PaintFlags serialization: http://crbug.com/737629
+ Read(&flags->text_size_);
+ ReadSimple(&flags->color_);
+ Read(&flags->width_);
+ Read(&flags->miter_limit_);
+ ReadSimple(&flags->blend_mode_);
+ ReadSimple(&flags->bitfields_uint_);
+
+ // TODO(enne): ReadTypeface, http://crbug.com/737629
+
+ // Flattenables must be read at 4-byte boundary, which should be the case
+ // here.
+ ReadFlattenable(&flags->path_effect_);
+ ReadFlattenable(&flags->mask_filter_);
+ ReadFlattenable(&flags->color_filter_);
+ ReadFlattenable(&flags->draw_looper_);
+ ReadFlattenable(&flags->image_filter_);
+
+ Read(&flags->shader_);
}
void PaintOpReader::Read(PaintImage* image) {
@@ -102,7 +178,7 @@ void PaintOpReader::Read(PaintImage* image) {
}
void PaintOpReader::Read(sk_sp<SkData>* data) {
- size_t bytes;
+ size_t bytes = 0;
ReadSimple(&bytes);
if (remaining_bytes_ < bytes)
valid_ = false;
@@ -117,7 +193,9 @@ void PaintOpReader::Read(sk_sp<SkData>* data) {
*data = SkData::MakeEmpty();
return;
}
- *data = SkData::MakeWithCopy(memory_, bytes);
+
+ // This is safe to cast away the volatile as it is just a memcpy internally.
+ *data = SkData::MakeWithCopy(const_cast<const char*>(memory_), bytes);
memory_ += bytes;
remaining_bytes_ -= bytes;
@@ -127,4 +205,114 @@ void PaintOpReader::Read(sk_sp<SkTextBlob>* blob) {
// TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629
}
+void PaintOpReader::Read(sk_sp<PaintShader>* shader) {
+ bool has_shader = false;
+ ReadSimple(&has_shader);
+ if (!has_shader) {
+ *shader = nullptr;
+ return;
+ }
+ PaintShader::Type shader_type;
+ ReadSimple(&shader_type);
+ // Avoid creating a shader if something is invalid.
+ if (!valid_ || !IsValidPaintShaderType(shader_type)) {
+ valid_ = false;
+ return;
+ }
+
+ *shader = sk_sp<PaintShader>(new PaintShader(shader_type));
+ PaintShader& ref = **shader;
+ ReadSimple(&ref.flags_);
+ ReadSimple(&ref.end_radius_);
+ ReadSimple(&ref.start_radius_);
+ ReadSimple(&ref.tx_);
+ ReadSimple(&ref.ty_);
+ if (!IsValidSkShaderTileMode(ref.tx_) || !IsValidSkShaderTileMode(ref.ty_))
+ valid_ = false;
+ ReadSimple(&ref.fallback_color_);
+ ReadSimple(&ref.scaling_behavior_);
+ if (!IsValidPaintShaderScalingBehavior(ref.scaling_behavior_))
+ valid_ = false;
+ bool has_local_matrix = false;
+ ReadSimple(&has_local_matrix);
+ if (has_local_matrix) {
+ ref.local_matrix_.emplace();
+ ReadSimple(&*ref.local_matrix_);
+ }
+ ReadSimple(&ref.center_);
+ ReadSimple(&ref.tile_);
+ ReadSimple(&ref.start_point_);
+ ReadSimple(&ref.end_point_);
+ ReadSimple(&ref.start_degrees_);
+ ReadSimple(&ref.end_degrees_);
+ // TODO(vmpstr): Read PaintImage image_. http://crbug.com/737629
+ // TODO(vmpstr): Read sk_sp<PaintRecord> record_. http://crbug.com/737629
+ decltype(ref.colors_)::size_type colors_size = 0;
+ ReadSimple(&colors_size);
+
+ // If there are too many colors, abort.
+ if (colors_size > kMaxShaderColorsSupported) {
+ valid_ = false;
+ return;
+ }
+ size_t colors_bytes = colors_size * sizeof(SkColor);
+ if (colors_bytes > remaining_bytes_) {
+ valid_ = false;
+ return;
+ }
+ ref.colors_.resize(colors_size);
+ ReadData(colors_bytes, ref.colors_.data());
+
+ decltype(ref.positions_)::size_type positions_size = 0;
+ ReadSimple(&positions_size);
+ // TODO(enne): positions and colors have to have the same count, so maybe
+ // don't serialize this either?
+ if (positions_size != colors_size) {
+ valid_ = false;
+ return;
+ }
+ size_t positions_bytes = positions_size * sizeof(SkScalar);
+ if (positions_bytes > remaining_bytes_) {
+ valid_ = false;
+ return;
+ }
+ ref.positions_.resize(positions_size);
+ ReadData(positions_size * sizeof(SkScalar), ref.positions_.data());
+
+ // We don't write the cached shader, so don't attempt to read it either.
+
+ // TODO(vmpstr): add serialization of these shader types. For the moment
+ // pretend that these shaders don't exist and that the serialization is
+ // successful. Valid ops pre-serialization should not cause deserialization
+ // failures.
+ if (shader_type == PaintShader::Type::kPaintRecord ||
+ shader_type == PaintShader::Type::kImage) {
+ *shader = nullptr;
+ return;
+ }
+
+ if (!(*shader)->IsValid()) {
+ valid_ = false;
+ }
+}
+
+bool PaintOpReader::AlignMemory(size_t alignment) {
+ // Due to the math below, alignment must be a power of two.
+ DCHECK_GT(alignment, 0u);
+ DCHECK_EQ(alignment & (alignment - 1), 0u);
+
+ uintptr_t memory = reinterpret_cast<uintptr_t>(memory_);
+ // The following is equivalent to:
+ // padding = (alignment - memory % alignment) % alignment;
+ // because alignment is a power of two. This doesn't use modulo operator
+ // however, since it can be slow.
+ size_t padding = ((memory + alignment - 1) & ~(alignment - 1)) - memory;
+ if (padding > remaining_bytes_)
+ return false;
+
+ memory_ += padding;
+ remaining_bytes_ -= padding;
+ return true;
+}
+
} // namespace cc
diff --git a/chromium/cc/paint/paint_op_reader.h b/chromium/cc/paint/paint_op_reader.h
index 0252178e0ae..8f1e9ac8228 100644
--- a/chromium/cc/paint/paint_op_reader.h
+++ b/chromium/cc/paint/paint_op_reader.h
@@ -7,16 +7,19 @@
#include <vector>
+#include "cc/paint/paint_export.h"
#include "cc/paint/paint_op_writer.h"
namespace cc {
+class PaintShader;
+
// PaintOpReader takes garbage |memory| and clobbers it with successive
// read functions.
-class PaintOpReader {
+class CC_PAINT_EXPORT PaintOpReader {
public:
- PaintOpReader(const void* memory, size_t size)
- : memory_(static_cast<const char*>(memory) +
+ PaintOpReader(const volatile void* memory, size_t size)
+ : memory_(static_cast<const volatile char*>(memory) +
PaintOpWriter::HeaderBytes()),
remaining_bytes_(size - PaintOpWriter::HeaderBytes()) {
if (size < PaintOpWriter::HeaderBytes())
@@ -40,6 +43,7 @@ class PaintOpReader {
void Read(PaintImage* image);
void Read(sk_sp<SkData>* data);
void Read(sk_sp<SkTextBlob>* blob);
+ void Read(sk_sp<PaintShader>* shader);
void Read(SkClipOp* op) {
uint8_t value = 0u;
@@ -66,7 +70,14 @@ class PaintOpReader {
template <typename T>
void ReadSimple(T* val);
- const char* memory_ = nullptr;
+ template <typename T>
+ void ReadFlattenable(sk_sp<T>* val);
+
+ // Attempts to align the memory to the given alignment. Returns false if there
+ // is unsufficient bytes remaining to do this padding.
+ bool AlignMemory(size_t alignment);
+
+ const volatile char* memory_ = nullptr;
size_t remaining_bytes_ = 0u;
bool valid_ = true;
};
diff --git a/chromium/cc/paint/paint_op_writer.cc b/chromium/cc/paint/paint_op_writer.cc
index 1248e17ba03..37336326c4e 100644
--- a/chromium/cc/paint/paint_op_writer.cc
+++ b/chromium/cc/paint/paint_op_writer.cc
@@ -5,6 +5,8 @@
#include "cc/paint/paint_op_writer.h"
#include "cc/paint/paint_flags.h"
+#include "cc/paint/paint_shader.h"
+#include "third_party/skia/include/core/SkFlattenableSerialization.h"
#include "third_party/skia/include/core/SkTextBlob.h"
namespace cc {
@@ -12,7 +14,9 @@ namespace cc {
template <typename T>
void PaintOpWriter::WriteSimple(const T& val) {
static_assert(base::is_trivially_copyable<T>::value, "");
- if (sizeof(T) > remaining_bytes_)
+ if (!AlignMemory(alignof(T)))
+ valid_ = false;
+ if (remaining_bytes_ < sizeof(T))
valid_ = false;
if (!valid_)
return;
@@ -23,6 +27,18 @@ void PaintOpWriter::WriteSimple(const T& val) {
remaining_bytes_ -= sizeof(T);
}
+void PaintOpWriter::WriteFlattenable(const SkFlattenable* val) {
+ DCHECK(SkIsAlign4(reinterpret_cast<uintptr_t>(memory_)))
+ << "Flattenable must start writing at 4 byte alignment.";
+ // TODO(enne): change skia API to make this a const parameter.
+ sk_sp<SkData> data(
+ SkValidatingSerializeFlattenable(const_cast<SkFlattenable*>(val)));
+
+ Write(data->size());
+ if (!data->isEmpty())
+ WriteData(data->size(), data->data());
+}
+
void PaintOpWriter::Write(size_t data) {
WriteSimple(data);
}
@@ -60,7 +76,24 @@ void PaintOpWriter::Write(const SkPath& path) {
}
void PaintOpWriter::Write(const PaintFlags& flags) {
- // TODO(enne): implement PaintFlags serialization: http://crbug.com/737629
+ Write(flags.text_size_);
+ WriteSimple(flags.color_);
+ Write(flags.width_);
+ Write(flags.miter_limit_);
+ WriteSimple(flags.blend_mode_);
+ WriteSimple(flags.bitfields_uint_);
+
+ // TODO(enne): WriteTypeface, http://crbug.com/737629
+
+ // Flattenables must be written starting at a 4 byte boundary, which should be
+ // the case here.
+ WriteFlattenable(flags.path_effect_.get());
+ WriteFlattenable(flags.mask_filter_.get());
+ WriteFlattenable(flags.color_filter_.get());
+ WriteFlattenable(flags.draw_looper_.get());
+ WriteFlattenable(flags.image_filter_.get());
+
+ Write(flags.shader_.get());
}
void PaintOpWriter::Write(const PaintImage& image, ImageDecodeCache* cache) {
@@ -83,6 +116,48 @@ void PaintOpWriter::Write(const sk_sp<SkTextBlob>& blob) {
// TODO(enne): implement SkTextBlob serialization: http://crbug.com/737629
}
+void PaintOpWriter::Write(const PaintShader* shader) {
+ if (!shader) {
+ WriteSimple(false);
+ return;
+ }
+
+ // TODO(vmpstr): This could be optimized to only serialize fields relevant to
+ // the specific shader type. If done, then corresponding reading and tests
+ // would have to also be updated.
+ WriteSimple(true);
+ WriteSimple(shader->shader_type_);
+ WriteSimple(shader->flags_);
+ WriteSimple(shader->end_radius_);
+ WriteSimple(shader->start_radius_);
+ WriteSimple(shader->tx_);
+ WriteSimple(shader->ty_);
+ WriteSimple(shader->fallback_color_);
+ WriteSimple(shader->scaling_behavior_);
+ if (shader->local_matrix_) {
+ Write(true);
+ WriteSimple(*shader->local_matrix_);
+ } else {
+ Write(false);
+ }
+ WriteSimple(shader->center_);
+ WriteSimple(shader->tile_);
+ WriteSimple(shader->start_point_);
+ WriteSimple(shader->end_point_);
+ WriteSimple(shader->start_degrees_);
+ WriteSimple(shader->end_degrees_);
+ // TODO(vmpstr): Write PaintImage image_. http://crbug.com/737629
+ // TODO(vmpstr): Write sk_sp<PaintRecord> record_. http://crbug.com/737629
+ WriteSimple(shader->colors_.size());
+ WriteData(shader->colors_.size() * sizeof(SkColor), shader->colors_.data());
+
+ WriteSimple(shader->positions_.size());
+ WriteData(shader->positions_.size() * sizeof(SkScalar),
+ shader->positions_.data());
+ // Explicitly don't write the cached_shader_ because that can be regenerated
+ // using other fields.
+}
+
void PaintOpWriter::WriteData(size_t bytes, const void* input) {
if (bytes > remaining_bytes_)
valid_ = false;
@@ -101,4 +176,23 @@ void PaintOpWriter::WriteArray(size_t count, const SkPoint* input) {
WriteData(bytes, input);
}
+bool PaintOpWriter::AlignMemory(size_t alignment) {
+ // Due to the math below, alignment must be a power of two.
+ DCHECK_GT(alignment, 0u);
+ DCHECK_EQ(alignment & (alignment - 1), 0u);
+
+ uintptr_t memory = reinterpret_cast<uintptr_t>(memory_);
+ // The following is equivalent to:
+ // padding = (alignment - memory % alignment) % alignment;
+ // because alignment is a power of two. This doesn't use modulo operator
+ // however, since it can be slow.
+ size_t padding = ((memory + alignment - 1) & ~(alignment - 1)) - memory;
+ if (padding > remaining_bytes_)
+ return false;
+
+ memory_ += padding;
+ remaining_bytes_ -= padding;
+ return true;
+}
+
} // namespace cc
diff --git a/chromium/cc/paint/paint_op_writer.h b/chromium/cc/paint/paint_op_writer.h
index 0163067f179..277f0a4b4ea 100644
--- a/chromium/cc/paint/paint_op_writer.h
+++ b/chromium/cc/paint/paint_op_writer.h
@@ -6,6 +6,7 @@
#define CC_PAINT_PAINT_OP_WRITER_H_
#include "cc/paint/paint_canvas.h"
+#include "cc/paint/paint_export.h"
struct SkRect;
struct SkIRect;
@@ -14,8 +15,9 @@ class SkRRect;
namespace cc {
class ImageDecodeCache;
+class PaintShader;
-class PaintOpWriter {
+class CC_PAINT_EXPORT PaintOpWriter {
public:
PaintOpWriter(void* memory, size_t size)
: memory_(static_cast<char*>(memory) + HeaderBytes()),
@@ -46,6 +48,7 @@ class PaintOpWriter {
void Write(const PaintImage& image, ImageDecodeCache* cache);
void Write(const sk_sp<SkData>& data);
void Write(const sk_sp<SkTextBlob>& blob);
+ void Write(const PaintShader* shader);
void Write(SkClipOp op) { Write(static_cast<uint8_t>(op)); }
void Write(PaintCanvas::AnnotationType type) {
@@ -60,6 +63,12 @@ class PaintOpWriter {
template <typename T>
void WriteSimple(const T& val);
+ void WriteFlattenable(const SkFlattenable* val);
+
+ // Attempts to align the memory to the given alignment. Returns false if there
+ // is unsufficient bytes remaining to do this padding.
+ bool AlignMemory(size_t alignment);
+
char* memory_ = nullptr;
size_t size_ = 0u;
size_t remaining_bytes_ = 0u;
diff --git a/chromium/cc/paint/paint_recorder.cc b/chromium/cc/paint/paint_recorder.cc
index 20f5cacd2d6..f584bff451b 100644
--- a/chromium/cc/paint/paint_recorder.cc
+++ b/chromium/cc/paint/paint_recorder.cc
@@ -4,17 +4,20 @@
#include "cc/paint/paint_recorder.h"
-#include "cc/paint/paint_op_buffer.h"
+#include "cc/paint/display_item_list.h"
+#include "ui/gfx/skia_util.h"
namespace cc {
-PaintRecorder::PaintRecorder() = default;
-
+PaintRecorder::PaintRecorder() {
+ display_item_list_ = base::MakeRefCounted<DisplayItemList>(
+ DisplayItemList::kToBeReleasedAsPaintOpBuffer);
+}
PaintRecorder::~PaintRecorder() = default;
PaintCanvas* PaintRecorder::beginRecording(const SkRect& bounds) {
- buffer_ = sk_make_sp<PaintOpBuffer>();
- canvas_.emplace(buffer_.get(), bounds);
+ display_item_list_->StartPaint();
+ canvas_.emplace(display_item_list_.get(), bounds);
return getRecordingCanvas();
}
@@ -32,8 +35,10 @@ sk_sp<PaintRecord> PaintRecorder::finishRecordingAsPicture() {
// to know if recording is finished, so reset it here.
canvas_.reset();
- buffer_->ShrinkToFit();
- return std::move(buffer_);
+ // The rect doesn't matter, since we just release the record.
+ display_item_list_->EndPaintOfUnpaired(gfx::Rect());
+ display_item_list_->Finalize();
+ return display_item_list_->ReleaseAsRecord();
}
} // namespace cc
diff --git a/chromium/cc/paint/paint_recorder.h b/chromium/cc/paint/paint_recorder.h
index 7f582b85191..1b0bf24d01d 100644
--- a/chromium/cc/paint/paint_recorder.h
+++ b/chromium/cc/paint/paint_recorder.h
@@ -14,7 +14,7 @@
namespace cc {
-class PaintOpBuffer;
+class DisplayItemList;
class CC_PAINT_EXPORT PaintRecorder {
public:
@@ -37,9 +37,8 @@ class CC_PAINT_EXPORT PaintRecorder {
sk_sp<PaintRecord> finishRecordingAsPicture();
private:
- sk_sp<PaintOpBuffer> buffer_;
+ scoped_refptr<DisplayItemList> display_item_list_;
base::Optional<RecordPaintCanvas> canvas_;
-
DISALLOW_COPY_AND_ASSIGN(PaintRecorder);
};
diff --git a/chromium/cc/paint/paint_shader.cc b/chromium/cc/paint/paint_shader.cc
index e6c9450ffac..2257e6af4f3 100644
--- a/chromium/cc/paint/paint_shader.cc
+++ b/chromium/cc/paint/paint_shader.cc
@@ -11,7 +11,7 @@
namespace cc {
sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) {
- sk_sp<PaintShader> shader(new PaintShader(kColor));
+ sk_sp<PaintShader> shader(new PaintShader(Type::kColor));
// Just one color. Store it in the fallback color. Easy.
shader->fallback_color_ = color;
@@ -27,7 +27,7 @@ sk_sp<PaintShader> PaintShader::MakeLinearGradient(const SkPoint points[],
uint32_t flags,
const SkMatrix* local_matrix,
SkColor fallback_color) {
- sk_sp<PaintShader> shader(new PaintShader(kLinearGradient));
+ sk_sp<PaintShader> shader(new PaintShader(Type::kLinearGradient));
// There are always two points, the start and the end.
shader->start_point_ = points[0];
@@ -48,7 +48,7 @@ sk_sp<PaintShader> PaintShader::MakeRadialGradient(const SkPoint& center,
uint32_t flags,
const SkMatrix* local_matrix,
SkColor fallback_color) {
- sk_sp<PaintShader> shader(new PaintShader(kRadialGradient));
+ sk_sp<PaintShader> shader(new PaintShader(Type::kRadialGradient));
shader->center_ = center;
shader->start_radius_ = shader->end_radius_ = radius;
@@ -71,7 +71,7 @@ sk_sp<PaintShader> PaintShader::MakeTwoPointConicalGradient(
uint32_t flags,
const SkMatrix* local_matrix,
SkColor fallback_color) {
- sk_sp<PaintShader> shader(new PaintShader(kTwoPointConicalGradient));
+ sk_sp<PaintShader> shader(new PaintShader(Type::kTwoPointConicalGradient));
shader->start_point_ = start;
shader->end_point_ = end;
@@ -89,41 +89,48 @@ sk_sp<PaintShader> PaintShader::MakeSweepGradient(SkScalar cx,
const SkColor colors[],
const SkScalar pos[],
int color_count,
+ SkShader::TileMode mode,
+ SkScalar start_degrees,
+ SkScalar end_degrees,
uint32_t flags,
const SkMatrix* local_matrix,
SkColor fallback_color) {
- sk_sp<PaintShader> shader(new PaintShader(kSweepGradient));
+ sk_sp<PaintShader> shader(new PaintShader(Type::kSweepGradient));
shader->center_ = SkPoint::Make(cx, cy);
+ shader->start_degrees_ = start_degrees;
+ shader->end_degrees_ = end_degrees;
shader->SetColorsAndPositions(colors, pos, color_count);
- shader->SetMatrixAndTiling(local_matrix, SkShader::kClamp_TileMode,
- SkShader::kClamp_TileMode);
+ shader->SetMatrixAndTiling(local_matrix, mode, mode);
shader->SetFlagsAndFallback(flags, fallback_color);
return shader;
}
-sk_sp<PaintShader> PaintShader::MakeImage(sk_sp<const SkImage> image,
+sk_sp<PaintShader> PaintShader::MakeImage(const PaintImage& image,
SkShader::TileMode tx,
SkShader::TileMode ty,
const SkMatrix* local_matrix) {
- sk_sp<PaintShader> shader(new PaintShader(kImage));
+ sk_sp<PaintShader> shader(new PaintShader(Type::kImage));
- shader->image_ = std::move(image);
+ shader->image_ = image;
shader->SetMatrixAndTiling(local_matrix, tx, ty);
return shader;
}
-sk_sp<PaintShader> PaintShader::MakePaintRecord(sk_sp<PaintRecord> record,
- const SkRect& tile,
- SkShader::TileMode tx,
- SkShader::TileMode ty,
- const SkMatrix* local_matrix) {
- sk_sp<PaintShader> shader(new PaintShader(kPaintRecord));
+sk_sp<PaintShader> PaintShader::MakePaintRecord(
+ sk_sp<PaintRecord> record,
+ const SkRect& tile,
+ SkShader::TileMode tx,
+ SkShader::TileMode ty,
+ const SkMatrix* local_matrix,
+ ScalingBehavior scaling_behavior) {
+ sk_sp<PaintShader> shader(new PaintShader(Type::kPaintRecord));
shader->record_ = std::move(record);
shader->tile_ = tile;
+ shader->scaling_behavior_ = scaling_behavior;
shader->SetMatrixAndTiling(local_matrix, tx, ty);
return shader;
@@ -137,10 +144,10 @@ sk_sp<SkShader> PaintShader::GetSkShader() const {
return cached_shader_;
switch (shader_type_) {
- case kColor:
+ case Type::kColor:
// This will be handled by the fallback check below.
break;
- case kLinearGradient: {
+ case Type::kLinearGradient: {
SkPoint points[2] = {start_point_, end_point_};
cached_shader_ = SkGradientShader::MakeLinear(
points, colors_.data(),
@@ -149,37 +156,56 @@ sk_sp<SkShader> PaintShader::GetSkShader() const {
local_matrix_ ? &*local_matrix_ : nullptr);
break;
}
- case kRadialGradient:
+ case Type::kRadialGradient:
cached_shader_ = SkGradientShader::MakeRadial(
center_, start_radius_, colors_.data(),
positions_.empty() ? nullptr : positions_.data(),
static_cast<int>(colors_.size()), tx_, flags_,
local_matrix_ ? &*local_matrix_ : nullptr);
break;
- case kTwoPointConicalGradient:
+ case Type::kTwoPointConicalGradient:
cached_shader_ = SkGradientShader::MakeTwoPointConical(
start_point_, start_radius_, end_point_, end_radius_, colors_.data(),
positions_.empty() ? nullptr : positions_.data(),
static_cast<int>(colors_.size()), tx_, flags_,
local_matrix_ ? &*local_matrix_ : nullptr);
break;
- case kSweepGradient:
+ case Type::kSweepGradient:
cached_shader_ = SkGradientShader::MakeSweep(
center_.x(), center_.y(), colors_.data(),
positions_.empty() ? nullptr : positions_.data(),
- static_cast<int>(colors_.size()), flags_,
- local_matrix_ ? &*local_matrix_ : nullptr);
+ static_cast<int>(colors_.size()), tx_, start_degrees_, end_degrees_,
+ flags_, local_matrix_ ? &*local_matrix_ : nullptr);
break;
- case kImage:
- cached_shader_ = image_->makeShader(
+ case Type::kImage:
+ cached_shader_ = image_.GetSkImage()->makeShader(
tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr);
break;
- case kPaintRecord:
- cached_shader_ = SkShader::MakePictureShader(
- ToSkPicture(record_, tile_), tx_, ty_,
- local_matrix_ ? &*local_matrix_ : nullptr, nullptr);
+ case Type::kPaintRecord: {
+ auto picture = ToSkPicture(record_, tile_);
+
+ switch (scaling_behavior_) {
+ // For raster scale, we create a picture shader directly.
+ case ScalingBehavior::kRasterAtScale:
+ cached_shader_ = SkShader::MakePictureShader(
+ std::move(picture), tx_, ty_,
+ local_matrix_ ? &*local_matrix_ : nullptr, nullptr);
+ break;
+ // For fixed scale, we create an image shader with and image backed by
+ // the picture.
+ case ScalingBehavior::kFixedScale: {
+ auto image = SkImage::MakeFromPicture(
+ std::move(picture), SkISize::Make(tile_.width(), tile_.height()),
+ nullptr, nullptr, SkImage::BitDepth::kU8,
+ SkColorSpace::MakeSRGB());
+ cached_shader_ = image->makeShader(
+ tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr);
+ break;
+ }
+ }
break;
- case kShaderCount:
+ }
+ case Type::kShaderCount:
NOTREACHED();
break;
}
@@ -194,7 +220,11 @@ sk_sp<SkShader> PaintShader::GetSkShader() const {
void PaintShader::SetColorsAndPositions(const SkColor* colors,
const SkScalar* positions,
int count) {
+#if DCHECK_IS_ON()
+ static const int kMaxShaderColorsSupported = 10000;
DCHECK_GE(count, 2);
+ DCHECK_LE(count, kMaxShaderColorsSupported);
+#endif
colors_.assign(colors, colors + count);
if (positions)
positions_.assign(positions, positions + count);
@@ -219,4 +249,28 @@ bool PaintShader::IsOpaque() const {
return GetSkShader()->isOpaque();
}
+bool PaintShader::IsValid() const {
+ // If we managed to create a shader already, then we should be valid.
+ if (cached_shader_)
+ return true;
+
+ switch (shader_type_) {
+ case Type::kColor:
+ return true;
+ case Type::kLinearGradient:
+ case Type::kRadialGradient:
+ case Type::kTwoPointConicalGradient:
+ case Type::kSweepGradient:
+ return colors_.size() >= 2 &&
+ (positions_.empty() || positions_.size() == colors_.size());
+ case Type::kImage:
+ return !!image_;
+ case Type::kPaintRecord:
+ return !!record_;
+ case Type::kShaderCount:
+ return false;
+ }
+ return false;
+}
+
} // namespace cc
diff --git a/chromium/cc/paint/paint_shader.h b/chromium/cc/paint/paint_shader.h
index e025387c6b5..8a133709b2c 100644
--- a/chromium/cc/paint/paint_shader.h
+++ b/chromium/cc/paint/paint_shader.h
@@ -10,6 +10,7 @@
#include "base/optional.h"
#include "cc/paint/paint_export.h"
+#include "cc/paint/paint_image.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "third_party/skia/include/core/SkShader.h"
@@ -21,6 +22,22 @@ using PaintRecord = PaintOpBuffer;
class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
public:
+ enum class Type : uint8_t {
+ kColor,
+ kLinearGradient,
+ kRadialGradient,
+ kTwoPointConicalGradient,
+ kSweepGradient,
+ kImage,
+ kPaintRecord,
+ kShaderCount
+ };
+
+ // Scaling behavior dictates how a PaintRecord shader will behave. Use
+ // RasterAtScale to create a picture shader. Use FixedScale to create an image
+ // shader that is backed by the paint record.
+ enum class ScalingBehavior : uint8_t { kRasterAtScale, kFixedScale };
+
static sk_sp<PaintShader> MakeColor(SkColor color);
static sk_sp<PaintShader> MakeLinearGradient(
@@ -63,42 +80,53 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
const SkColor colors[],
const SkScalar pos[],
int color_count,
+ SkShader::TileMode mode,
+ SkScalar start_degrees,
+ SkScalar end_degrees,
uint32_t flags = 0,
const SkMatrix* local_matrix = nullptr,
SkColor fallback_color = SK_ColorTRANSPARENT);
- static sk_sp<PaintShader> MakeImage(sk_sp<const SkImage> image,
+ static sk_sp<PaintShader> MakeImage(const PaintImage& image,
SkShader::TileMode tx,
SkShader::TileMode ty,
const SkMatrix* local_matrix);
- static sk_sp<PaintShader> MakePaintRecord(sk_sp<PaintRecord> record,
- const SkRect& tile,
- SkShader::TileMode tx,
- SkShader::TileMode ty,
- const SkMatrix* local_matrix);
+ static sk_sp<PaintShader> MakePaintRecord(
+ sk_sp<PaintRecord> record,
+ const SkRect& tile,
+ SkShader::TileMode tx,
+ SkShader::TileMode ty,
+ const SkMatrix* local_matrix,
+ ScalingBehavior scaling_behavior = ScalingBehavior::kRasterAtScale);
~PaintShader() override;
SkMatrix GetLocalMatrix() const {
return local_matrix_ ? *local_matrix_ : SkMatrix::I();
}
+ Type shader_type() const { return shader_type_; }
+ const PaintImage& paint_image() const {
+ DCHECK_EQ(Type::kImage, shader_type_);
+ return image_;
+ }
+
+ SkShader::TileMode tx() const { return tx_; }
+ SkShader::TileMode ty() const { return ty_; }
bool IsOpaque() const;
+ // Returns true if the shader looks like it is valid (ie the members required
+ // for this shader type all look reasonable. Returns false otherwise. Note
+ // that this is a best effort function since truly validating whether the
+ // shader is correct is hard.
+ bool IsValid() const;
+
private:
friend class PaintFlags;
-
- enum Type {
- kColor,
- kLinearGradient,
- kRadialGradient,
- kTwoPointConicalGradient,
- kSweepGradient,
- kImage,
- kPaintRecord,
- kShaderCount
- };
+ friend class PaintOpReader;
+ friend class PaintOpSerializationTestUtils;
+ friend class PaintOpWriter;
explicit PaintShader(Type type);
@@ -112,7 +140,7 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
SkShader::TileMode ty);
void SetFlagsAndFallback(uint32_t flags, SkColor fallback_color);
- Type shader_type_ = kShaderCount;
+ Type shader_type_ = Type::kShaderCount;
uint32_t flags_ = 0;
SkScalar end_radius_ = 0;
@@ -120,6 +148,7 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
SkShader::TileMode tx_ = SkShader::kClamp_TileMode;
SkShader::TileMode ty_ = SkShader::kClamp_TileMode;
SkColor fallback_color_ = SK_ColorTRANSPARENT;
+ ScalingBehavior scaling_behavior_ = ScalingBehavior::kRasterAtScale;
base::Optional<SkMatrix> local_matrix_;
SkPoint center_ = SkPoint::Make(0, 0);
@@ -128,7 +157,10 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
SkPoint start_point_ = SkPoint::Make(0, 0);
SkPoint end_point_ = SkPoint::Make(0, 0);
- sk_sp<const SkImage> image_;
+ SkScalar start_degrees_ = 0;
+ SkScalar end_degrees_ = 0;
+
+ PaintImage image_;
sk_sp<PaintRecord> record_;
std::vector<SkColor> colors_;
diff --git a/chromium/cc/paint/record_paint_canvas.cc b/chromium/cc/paint/record_paint_canvas.cc
index 3ff82e88dfd..4fd83234b7e 100644
--- a/chromium/cc/paint/record_paint_canvas.cc
+++ b/chromium/cc/paint/record_paint_canvas.cc
@@ -6,7 +6,7 @@
#include "base/memory/ptr_util.h"
#include "cc/paint/display_item_list.h"
-#include "cc/paint/paint_op_buffer.h"
+#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_record.h"
#include "cc/paint/paint_recorder.h"
#include "third_party/skia/include/core/SkAnnotation.h"
@@ -15,10 +15,10 @@
namespace cc {
-RecordPaintCanvas::RecordPaintCanvas(PaintOpBuffer* buffer,
+RecordPaintCanvas::RecordPaintCanvas(DisplayItemList* list,
const SkRect& bounds)
- : buffer_(buffer), recording_bounds_(bounds) {
- DCHECK(buffer_);
+ : list_(list), recording_bounds_(bounds) {
+ DCHECK(list_);
}
RecordPaintCanvas::~RecordPaintCanvas() = default;
@@ -38,7 +38,7 @@ void RecordPaintCanvas::flush() {
}
int RecordPaintCanvas::save() {
- buffer_->push<SaveOp>();
+ list_->push<SaveOp>();
return GetCanvas()->save();
}
@@ -55,23 +55,23 @@ int RecordPaintCanvas::saveLayer(const SkRect* bounds,
// TODO(enne): it appears that image filters affect matrices and color
// matrices affect transparent flags on SkCanvas layers, but it's not clear
// whether those are actually needed and we could just skip ToSkPaint here.
- buffer_->push<SaveLayerOp>(bounds, flags);
+ list_->push<SaveLayerOp>(bounds, flags);
SkPaint paint = flags->ToSkPaint();
return GetCanvas()->saveLayer(bounds, &paint);
}
- buffer_->push<SaveLayerOp>(bounds, flags);
+ list_->push<SaveLayerOp>(bounds, flags);
return GetCanvas()->saveLayer(bounds, nullptr);
}
int RecordPaintCanvas::saveLayerAlpha(const SkRect* bounds,
uint8_t alpha,
bool preserve_lcd_text_requests) {
- buffer_->push<SaveLayerAlphaOp>(bounds, alpha, preserve_lcd_text_requests);
+ list_->push<SaveLayerAlphaOp>(bounds, alpha, preserve_lcd_text_requests);
return GetCanvas()->saveLayerAlpha(bounds, alpha);
}
void RecordPaintCanvas::restore() {
- buffer_->push<RestoreOp>();
+ list_->push<RestoreOp>();
GetCanvas()->restore();
}
@@ -93,34 +93,34 @@ void RecordPaintCanvas::restoreToCount(int save_count) {
}
void RecordPaintCanvas::translate(SkScalar dx, SkScalar dy) {
- buffer_->push<TranslateOp>(dx, dy);
+ list_->push<TranslateOp>(dx, dy);
GetCanvas()->translate(dx, dy);
}
void RecordPaintCanvas::scale(SkScalar sx, SkScalar sy) {
- buffer_->push<ScaleOp>(sx, sy);
+ list_->push<ScaleOp>(sx, sy);
GetCanvas()->scale(sx, sy);
}
void RecordPaintCanvas::rotate(SkScalar degrees) {
- buffer_->push<RotateOp>(degrees);
+ list_->push<RotateOp>(degrees);
GetCanvas()->rotate(degrees);
}
void RecordPaintCanvas::concat(const SkMatrix& matrix) {
- buffer_->push<ConcatOp>(matrix);
+ list_->push<ConcatOp>(matrix);
GetCanvas()->concat(matrix);
}
void RecordPaintCanvas::setMatrix(const SkMatrix& matrix) {
- buffer_->push<SetMatrixOp>(matrix);
+ list_->push<SetMatrixOp>(matrix);
GetCanvas()->setMatrix(matrix);
}
void RecordPaintCanvas::clipRect(const SkRect& rect,
SkClipOp op,
bool antialias) {
- buffer_->push<ClipRectOp>(rect, op, antialias);
+ list_->push<ClipRectOp>(rect, op, antialias);
GetCanvas()->clipRect(rect, op, antialias);
}
@@ -132,7 +132,7 @@ void RecordPaintCanvas::clipRRect(const SkRRect& rrect,
clipRect(rrect.getBounds(), op, antialias);
return;
}
- buffer_->push<ClipRRectOp>(rrect, op, antialias);
+ list_->push<ClipRRectOp>(rrect, op, antialias);
GetCanvas()->clipRRect(rrect, op, antialias);
}
@@ -160,7 +160,7 @@ void RecordPaintCanvas::clipPath(const SkPath& path,
}
}
- buffer_->push<ClipPathOp>(path, op, antialias);
+ list_->push<ClipPathOp>(path, op, antialias);
GetCanvas()->clipPath(path, op, antialias);
return;
}
@@ -190,11 +190,11 @@ bool RecordPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const {
}
void RecordPaintCanvas::drawColor(SkColor color, SkBlendMode mode) {
- buffer_->push<DrawColorOp>(color, mode);
+ list_->push<DrawColorOp>(color, mode);
}
void RecordPaintCanvas::clear(SkColor color) {
- buffer_->push<DrawColorOp>(color, SkBlendMode::kSrc);
+ list_->push<DrawColorOp>(color, SkBlendMode::kSrc);
}
void RecordPaintCanvas::drawLine(SkScalar x0,
@@ -202,25 +202,25 @@ void RecordPaintCanvas::drawLine(SkScalar x0,
SkScalar x1,
SkScalar y1,
const PaintFlags& flags) {
- buffer_->push<DrawLineOp>(x0, y0, x1, y1, flags);
+ list_->push<DrawLineOp>(x0, y0, x1, y1, flags);
}
void RecordPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) {
- buffer_->push<DrawRectOp>(rect, flags);
+ list_->push<DrawRectOp>(rect, flags);
}
void RecordPaintCanvas::drawIRect(const SkIRect& rect,
const PaintFlags& flags) {
- buffer_->push<DrawIRectOp>(rect, flags);
+ list_->push<DrawIRectOp>(rect, flags);
}
void RecordPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) {
- buffer_->push<DrawOvalOp>(oval, flags);
+ list_->push<DrawOvalOp>(oval, flags);
}
void RecordPaintCanvas::drawRRect(const SkRRect& rrect,
const PaintFlags& flags) {
- buffer_->push<DrawRRectOp>(rrect, flags);
+ list_->push<DrawRRectOp>(rrect, flags);
}
void RecordPaintCanvas::drawDRRect(const SkRRect& outer,
@@ -232,22 +232,7 @@ void RecordPaintCanvas::drawDRRect(const SkRRect& outer,
drawRRect(outer, flags);
return;
}
- buffer_->push<DrawDRRectOp>(outer, inner, flags);
-}
-
-void RecordPaintCanvas::drawCircle(SkScalar cx,
- SkScalar cy,
- SkScalar radius,
- const PaintFlags& flags) {
- buffer_->push<DrawCircleOp>(cx, cy, radius, flags);
-}
-
-void RecordPaintCanvas::drawArc(const SkRect& oval,
- SkScalar start_angle,
- SkScalar sweep_angle,
- bool use_center,
- const PaintFlags& flags) {
- buffer_->push<DrawArcOp>(oval, start_angle, sweep_angle, use_center, flags);
+ list_->push<DrawDRRectOp>(outer, inner, flags);
}
void RecordPaintCanvas::drawRoundRect(const SkRect& rect,
@@ -265,14 +250,14 @@ void RecordPaintCanvas::drawRoundRect(const SkRect& rect,
}
void RecordPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) {
- buffer_->push<DrawPathOp>(path, flags);
+ list_->push<DrawPathOp>(path, flags);
}
void RecordPaintCanvas::drawImage(const PaintImage& image,
SkScalar left,
SkScalar top,
const PaintFlags* flags) {
- buffer_->push<DrawImageOp>(image, left, top, flags);
+ list_->push<DrawImageOp>(image, left, top, flags);
}
void RecordPaintCanvas::drawImageRect(const PaintImage& image,
@@ -280,7 +265,7 @@ void RecordPaintCanvas::drawImageRect(const PaintImage& image,
const SkRect& dst,
const PaintFlags* flags,
SrcRectConstraint constraint) {
- buffer_->push<DrawImageRectOp>(image, src, dst, flags, constraint);
+ list_->push<DrawImageRectOp>(image, src, dst, flags, constraint);
}
void RecordPaintCanvas::drawBitmap(const SkBitmap& bitmap,
@@ -290,41 +275,23 @@ void RecordPaintCanvas::drawBitmap(const SkBitmap& bitmap,
// TODO(enne): Move into base class?
if (bitmap.drawsNothing())
return;
- drawImage(
- PaintImage(PaintImage::kNonLazyStableId, SkImage::MakeFromBitmap(bitmap),
- PaintImage::AnimationType::UNKNOWN,
- PaintImage::CompletionState::UNKNOWN),
- left, top, flags);
-}
-
-void RecordPaintCanvas::drawText(const void* text,
- size_t byte_length,
- SkScalar x,
- SkScalar y,
- const PaintFlags& flags) {
- buffer_->push_with_data<DrawTextOp>(text, byte_length, x, y, flags);
-}
-
-void RecordPaintCanvas::drawPosText(const void* text,
- size_t byte_length,
- const SkPoint pos[],
- const PaintFlags& flags) {
- // TODO(enne): implement countText in PaintFlags??
- SkPaint paint = flags.ToSkPaint();
- size_t count = paint.countText(text, byte_length);
- buffer_->push_with_array<DrawPosTextOp>(text, byte_length, pos, count, flags);
+ drawImage(PaintImageBuilder()
+ .set_id(PaintImage::kNonLazyStableId)
+ .set_image(SkImage::MakeFromBitmap(bitmap))
+ .TakePaintImage(),
+ left, top, flags);
}
void RecordPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y,
const PaintFlags& flags) {
- buffer_->push<DrawTextBlobOp>(blob, x, y, flags);
+ list_->push<DrawTextBlobOp>(blob, x, y, flags);
}
void RecordPaintCanvas::drawPicture(sk_sp<const PaintRecord> record) {
// TODO(enne): If this is small, maybe flatten it?
- buffer_->push<DrawRecordOp>(record);
+ list_->push<DrawRecordOp>(record);
}
bool RecordPaintCanvas::isClipEmpty() const {
@@ -342,7 +309,7 @@ const SkMatrix& RecordPaintCanvas::getTotalMatrix() const {
void RecordPaintCanvas::Annotate(AnnotationType type,
const SkRect& rect,
sk_sp<SkData> data) {
- buffer_->push<AnnotateOp>(type, rect, data);
+ list_->push<AnnotateOp>(type, rect, data);
}
const SkNoDrawCanvas* RecordPaintCanvas::GetCanvas() const {
diff --git a/chromium/cc/paint/record_paint_canvas.h b/chromium/cc/paint/record_paint_canvas.h
index d5b6d898a97..b54c9efc6bf 100644
--- a/chromium/cc/paint/record_paint_canvas.h
+++ b/chromium/cc/paint/record_paint_canvas.h
@@ -19,12 +19,12 @@
namespace cc {
-class PaintOpBuffer;
+class DisplayItemList;
class PaintFlags;
class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas {
public:
- explicit RecordPaintCanvas(PaintOpBuffer* buffer, const SkRect& bounds);
+ RecordPaintCanvas(DisplayItemList* list, const SkRect& bounds);
~RecordPaintCanvas() override;
SkMetaData& getMetaData() override;
@@ -71,15 +71,6 @@ class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas {
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,
@@ -99,15 +90,6 @@ class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas {
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,
@@ -136,7 +118,7 @@ class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas {
const SkNoDrawCanvas* GetCanvas() const;
SkNoDrawCanvas* GetCanvas();
- PaintOpBuffer* buffer_;
+ DisplayItemList* list_;
// TODO(enne): Although RecordPaintCanvas is mostly a write-only interface
// where paint commands are stored, occasionally users of PaintCanvas want
diff --git a/chromium/cc/paint/skia_paint_canvas.cc b/chromium/cc/paint/skia_paint_canvas.cc
index a28798ff3df..c3a6e415630 100644
--- a/chromium/cc/paint/skia_paint_canvas.cc
+++ b/chromium/cc/paint/skia_paint_canvas.cc
@@ -10,6 +10,7 @@
#include "third_party/skia/include/core/SkAnnotation.h"
#include "third_party/skia/include/core/SkColorSpaceXformCanvas.h"
#include "third_party/skia/include/core/SkMetaData.h"
+#include "third_party/skia/include/core/SkRegion.h"
#include "third_party/skia/include/utils/SkNWayCanvas.h"
namespace cc {
@@ -192,23 +193,6 @@ void SkiaPaintCanvas::drawDRRect(const SkRRect& outer,
canvas_->drawDRRect(outer, inner, paint);
}
-void SkiaPaintCanvas::drawCircle(SkScalar cx,
- SkScalar cy,
- SkScalar radius,
- const PaintFlags& flags) {
- SkPaint paint = flags.ToSkPaint();
- canvas_->drawCircle(cx, cy, radius, paint);
-}
-
-void SkiaPaintCanvas::drawArc(const SkRect& oval,
- SkScalar start_angle,
- SkScalar sweep_angle,
- bool use_center,
- const PaintFlags& flags) {
- SkPaint paint = flags.ToSkPaint();
- canvas_->drawArc(oval, start_angle, sweep_angle, use_center, paint);
-}
-
void SkiaPaintCanvas::drawRoundRect(const SkRect& rect,
SkScalar rx,
SkScalar ry,
@@ -229,7 +213,7 @@ void SkiaPaintCanvas::drawImage(const PaintImage& image,
SkPaint paint;
if (flags)
paint = flags->ToSkPaint();
- canvas_->drawImage(image.sk_image().get(), left, top,
+ canvas_->drawImage(image.GetSkImage().get(), left, top,
flags ? &paint : nullptr);
}
@@ -241,7 +225,7 @@ void SkiaPaintCanvas::drawImageRect(const PaintImage& image,
SkPaint paint;
if (flags)
paint = flags->ToSkPaint();
- canvas_->drawImageRect(image.sk_image().get(), src, dst,
+ canvas_->drawImageRect(image.GetSkImage().get(), src, dst,
flags ? &paint : nullptr,
static_cast<SkCanvas::SrcRectConstraint>(constraint));
}
@@ -258,23 +242,6 @@ void SkiaPaintCanvas::drawBitmap(const SkBitmap& bitmap,
}
}
-void SkiaPaintCanvas::drawText(const void* text,
- size_t byte_length,
- SkScalar x,
- SkScalar y,
- const PaintFlags& flags) {
- SkPaint paint = flags.ToSkPaint();
- canvas_->drawText(text, byte_length, x, y, paint);
-}
-
-void SkiaPaintCanvas::drawPosText(const void* text,
- size_t byte_length,
- const SkPoint pos[],
- const PaintFlags& flags) {
- SkPaint paint = flags.ToSkPaint();
- canvas_->drawPosText(text, byte_length, pos, paint);
-}
-
void SkiaPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y,
diff --git a/chromium/cc/paint/skia_paint_canvas.h b/chromium/cc/paint/skia_paint_canvas.h
index f3df7538d72..36547517619 100644
--- a/chromium/cc/paint/skia_paint_canvas.h
+++ b/chromium/cc/paint/skia_paint_canvas.h
@@ -79,15 +79,6 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas {
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,
@@ -107,15 +98,6 @@ class CC_PAINT_EXPORT SkiaPaintCanvas final : public PaintCanvas {
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,
diff --git a/chromium/cc/paint/skia_paint_image_generator.cc b/chromium/cc/paint/skia_paint_image_generator.cc
new file mode 100644
index 00000000000..8d8479b7d42
--- /dev/null
+++ b/chromium/cc/paint/skia_paint_image_generator.cc
@@ -0,0 +1,44 @@
+// 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/skia_paint_image_generator.h"
+
+#include "cc/paint/paint_image_generator.h"
+
+namespace cc {
+
+SkiaPaintImageGenerator::SkiaPaintImageGenerator(
+ sk_sp<PaintImageGenerator> paint_image_generator,
+ size_t frame_index,
+ uint32_t unique_id)
+ : SkImageGenerator(paint_image_generator->GetSkImageInfo(), unique_id),
+ paint_image_generator_(std::move(paint_image_generator)),
+ frame_index_(frame_index) {}
+
+SkiaPaintImageGenerator::~SkiaPaintImageGenerator() = default;
+
+SkData* SkiaPaintImageGenerator::onRefEncodedData() {
+ return paint_image_generator_->GetEncodedData().release();
+}
+
+bool SkiaPaintImageGenerator::onGetPixels(const SkImageInfo& info,
+ void* pixels,
+ size_t row_bytes,
+ const Options& options) {
+ return paint_image_generator_->GetPixels(info, pixels, row_bytes,
+ frame_index_, uniqueID());
+}
+
+bool SkiaPaintImageGenerator::onQueryYUV8(SkYUVSizeInfo* size_info,
+ SkYUVColorSpace* color_space) const {
+ return paint_image_generator_->QueryYUV8(size_info, color_space);
+}
+
+bool SkiaPaintImageGenerator::onGetYUV8Planes(const SkYUVSizeInfo& size_info,
+ void* planes[3]) {
+ return paint_image_generator_->GetYUV8Planes(size_info, planes, frame_index_,
+ uniqueID());
+}
+
+} // namespace cc
diff --git a/chromium/cc/paint/skia_paint_image_generator.h b/chromium/cc/paint/skia_paint_image_generator.h
new file mode 100644
index 00000000000..7c51bd8d37e
--- /dev/null
+++ b/chromium/cc/paint/skia_paint_image_generator.h
@@ -0,0 +1,46 @@
+// 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_IMAGE_GENERATOR_H_
+#define CC_PAINT_SKIA_PAINT_IMAGE_GENERATOR_H_
+
+#include "base/macros.h"
+#include "cc/paint/paint_export.h"
+#include "third_party/skia/include/core/SkImageGenerator.h"
+
+namespace cc {
+class PaintImageGenerator;
+
+class CC_PAINT_EXPORT SkiaPaintImageGenerator final : public SkImageGenerator {
+ public:
+ // This is required only by the DecodingImageGenerator in Blink for caching
+ // of animated image frames.
+ // TODO(khushalsagar): Remove the use of this uniqueID. See crbug.com/753639.
+ enum { kNeedNewImageUniqueID = SkImageGenerator::kNeedNewImageUniqueID };
+
+ SkiaPaintImageGenerator(sk_sp<PaintImageGenerator> paint_image_generator,
+ size_t frame_index,
+ uint32_t unique_id = kNeedNewImageUniqueID);
+ ~SkiaPaintImageGenerator() override;
+
+ SkData* onRefEncodedData() override;
+ bool onGetPixels(const SkImageInfo&,
+ void* pixels,
+ size_t row_bytes,
+ const Options& options) override;
+ bool onQueryYUV8(SkYUVSizeInfo* size_info,
+ SkYUVColorSpace* color_space) const override;
+ bool onGetYUV8Planes(const SkYUVSizeInfo& size_info,
+ void* planes[3]) override;
+
+ private:
+ sk_sp<PaintImageGenerator> paint_image_generator_;
+ const size_t frame_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(SkiaPaintImageGenerator);
+};
+
+} // namespace cc
+
+#endif // CC_PAINT_SKIA_PAINT_IMAGE_GENERATOR_H_
diff --git a/chromium/cc/paint/solid_color_analyzer.cc b/chromium/cc/paint/solid_color_analyzer.cc
index 89c3bafc055..fe341d2ef04 100644
--- a/chromium/cc/paint/solid_color_analyzer.cc
+++ b/chromium/cc/paint/solid_color_analyzer.cc
@@ -48,9 +48,6 @@ bool IsSolidColorPaint(const PaintFlags& flags) {
// Returns true if the specified drawn_rect will cover the entire canvas, and
// that the canvas is not clipped (i.e. it covers ALL of the canvas).
bool IsFullQuad(const SkCanvas& canvas, const SkRect& drawn_rect) {
- if (!canvas.isClipRect())
- return false;
-
SkIRect clip_irect;
if (!canvas.getDeviceClipBounds(&clip_irect))
return false;
@@ -132,8 +129,8 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
const PaintOpBuffer* buffer,
const gfx::Rect& rect,
int max_ops_to_analyze,
- const std::vector<size_t>* indices) {
- if (buffer->size() == 0 || (indices && indices->empty()))
+ const std::vector<size_t>* offsets) {
+ if (buffer->size() == 0 || (offsets && offsets->empty()))
return SK_ColorTRANSPARENT;
bool is_solid = false;
@@ -142,12 +139,12 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
struct Frame {
Frame() = default;
- Frame(PaintOpBuffer::Iterator iter,
+ Frame(PaintOpBuffer::CompositeIterator iter,
const SkMatrix& original_ctm,
int save_count)
: iter(iter), original_ctm(original_ctm), save_count(save_count) {}
- PaintOpBuffer::Iterator iter;
+ PaintOpBuffer::CompositeIterator iter;
const SkMatrix original_ctm;
int save_count = 0;
};
@@ -160,7 +157,7 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
// We expect to see at least one DrawRecordOp because of the way items are
// constructed. Reserve this to 2, and go from there.
stack.reserve(2);
- stack.emplace_back(PaintOpBuffer::Iterator(buffer, indices),
+ stack.emplace_back(PaintOpBuffer::CompositeIterator(buffer, offsets),
canvas.getTotalMatrix(), canvas.getSaveCount());
int num_ops = 0;
@@ -175,18 +172,17 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
}
const PaintOp* op = *frame.iter;
- const SkMatrix& original_ctm = frame.original_ctm;
+ PlaybackParams params(nullptr, frame.original_ctm);
switch (op->GetType()) {
case PaintOpType::DrawRecord: {
const DrawRecordOp* record_op = static_cast<const DrawRecordOp*>(op);
- stack.emplace_back(PaintOpBuffer::Iterator(record_op->record.get()),
- canvas.getTotalMatrix(), canvas.getSaveCount());
+ stack.emplace_back(
+ PaintOpBuffer::CompositeIterator(record_op->record.get(), nullptr),
+ canvas.getTotalMatrix(), canvas.getSaveCount());
continue;
}
// Any of the following ops result in non solid content.
- case PaintOpType::DrawArc:
- case PaintOpType::DrawCircle:
case PaintOpType::DrawDRRect:
case PaintOpType::DrawImage:
case PaintOpType::DrawImageRect:
@@ -194,9 +190,8 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
case PaintOpType::DrawLine:
case PaintOpType::DrawOval:
case PaintOpType::DrawPath:
- case PaintOpType::DrawPosText:
+ return base::nullopt;
case PaintOpType::DrawRRect:
- case PaintOpType::DrawText:
case PaintOpType::DrawTextBlob:
// Anything that has to do a save layer is probably not solid. As it will
// likely need more than one draw op.
@@ -209,7 +204,6 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
case PaintOpType::ClipPath:
case PaintOpType::ClipRRect:
return base::nullopt;
-
case PaintOpType::DrawRect: {
if (++num_ops > max_ops_to_analyze)
return base::nullopt;
@@ -226,10 +220,20 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
&is_transparent, &color);
break;
}
+ case PaintOpType::ClipRect: {
+ // SolidColorAnalyzer uses an SkNoDrawCanvas which uses an
+ // SkNoPixelsDevice which says (without looking) that the canvas's
+ // clip is always a rect. So, if this clip could result in not
+ // a rect, this is no longer solid color.
+ const ClipRectOp* clip_op = static_cast<const ClipRectOp*>(op);
+ if (clip_op->op == SkClipOp::kDifference)
+ return base::nullopt;
+ op->Raster(&canvas, params);
+ break;
+ }
// The rest of the ops should only affect our state canvas.
case PaintOpType::Annotate:
- case PaintOpType::ClipRect:
case PaintOpType::Concat:
case PaintOpType::Scale:
case PaintOpType::SetMatrix:
@@ -238,7 +242,7 @@ base::Optional<SkColor> SolidColorAnalyzer::DetermineIfSolidColor(
case PaintOpType::Save:
case PaintOpType::Translate:
case PaintOpType::Noop:
- op->Raster(&canvas, original_ctm);
+ op->Raster(&canvas, params);
break;
}
++frame.iter;
diff --git a/chromium/cc/paint/solid_color_analyzer.h b/chromium/cc/paint/solid_color_analyzer.h
index 2c77c14849b..aa75d8a982b 100644
--- a/chromium/cc/paint/solid_color_analyzer.h
+++ b/chromium/cc/paint/solid_color_analyzer.h
@@ -23,7 +23,7 @@ class CC_PAINT_EXPORT SolidColorAnalyzer {
const PaintOpBuffer* buffer,
const gfx::Rect& rect,
int max_ops_to_analyze,
- const std::vector<size_t>* indices = nullptr);
+ const std::vector<size_t>* offsets = nullptr);
};
} // namespace cc
diff --git a/chromium/cc/paint/solid_color_analyzer_unittest.cc b/chromium/cc/paint/solid_color_analyzer_unittest.cc
index ce5d112cfb1..a944cc6faed 100644
--- a/chromium/cc/paint/solid_color_analyzer_unittest.cc
+++ b/chromium/cc/paint/solid_color_analyzer_unittest.cc
@@ -3,7 +3,9 @@
// found in the LICENSE file.
#include "cc/paint/solid_color_analyzer.h"
+#include "base/memory/ref_counted.h"
#include "base/optional.h"
+#include "cc/paint/display_item_list.h"
#include "cc/paint/record_paint_canvas.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/effects/SkOffsetImageFilter.h"
@@ -14,36 +16,52 @@ namespace {
class SolidColorAnalyzerTest : public testing::Test {
public:
- void SetUp() override {}
+ void SetUp() override {
+ display_item_list_ = base::MakeRefCounted<DisplayItemList>(
+ DisplayItemList::kToBeReleasedAsPaintOpBuffer);
+ display_item_list_->StartPaint();
+ }
void TearDown() override {
+ Finalize();
canvas_.reset();
- buffer_.Reset();
+ display_item_list_ = nullptr;
+ buffer_ = nullptr;
}
void Initialize(const gfx::Rect& rect = gfx::Rect(0, 0, 100, 100)) {
- canvas_.emplace(&buffer_, gfx::RectToSkRect(rect));
+ canvas_.emplace(display_item_list_.get(), gfx::RectToSkRect(rect));
rect_ = rect;
}
RecordPaintCanvas* canvas() { return &*canvas_; }
- PaintOpBuffer* paint_op_buffer() { return &buffer_; }
bool IsSolidColor() {
- auto color =
- SolidColorAnalyzer::DetermineIfSolidColor(&buffer_, rect_, 1, nullptr);
+ Finalize();
+ auto color = SolidColorAnalyzer::DetermineIfSolidColor(buffer_.get(), rect_,
+ 1, nullptr);
return !!color;
}
- SkColor GetColor() const {
- auto color =
- SolidColorAnalyzer::DetermineIfSolidColor(&buffer_, rect_, 1, nullptr);
+ SkColor GetColor() {
+ Finalize();
+ auto color = SolidColorAnalyzer::DetermineIfSolidColor(buffer_.get(), rect_,
+ 1, nullptr);
EXPECT_TRUE(color);
return color ? *color : SK_ColorTRANSPARENT;
}
private:
+ void Finalize() {
+ if (buffer_)
+ return;
+ display_item_list_->EndPaintOfUnpaired(gfx::Rect());
+ display_item_list_->Finalize();
+ buffer_ = display_item_list_->ReleaseAsRecord();
+ }
+
gfx::Rect rect_;
- PaintOpBuffer buffer_;
+ scoped_refptr<DisplayItemList> display_item_list_;
+ sk_sp<PaintOpBuffer> buffer_;
base::Optional<RecordPaintCanvas> canvas_;
base::Optional<SolidColorAnalyzer> analyzer_;
};
@@ -120,6 +138,20 @@ TEST_F(SolidColorAnalyzerTest, DrawRectClipped) {
EXPECT_FALSE(IsSolidColor());
}
+TEST_F(SolidColorAnalyzerTest, DrawRectClippedDifference) {
+ Initialize();
+ PaintFlags flags;
+ SkColor color = SkColorSetARGB(255, 11, 22, 33);
+ flags.setColor(color);
+ SkRect drawRect = SkRect::MakeWH(200, 200);
+ canvas()->clipRect(drawRect, SkClipOp::kIntersect, false);
+ SkRect differenceRect = SkRect::MakeXYWH(50, 50, 200, 200);
+ // Using difference should always make this fail.
+ canvas()->clipRect(differenceRect, SkClipOp::kDifference, false);
+ canvas()->drawRect(drawRect, flags);
+ EXPECT_FALSE(IsSolidColor());
+}
+
TEST_F(SolidColorAnalyzerTest, DrawRectWithTranslateNotSolid) {
Initialize();
PaintFlags flags;