diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-20 10:33:36 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-11-22 11:45:12 +0000 |
commit | be59a35641616a4cf23c4a13fa0632624b021c1b (patch) | |
tree | 9da183258bdf9cc413f7562079d25ace6955467f /chromium/cc/paint | |
parent | d702e4b6a64574e97fc7df8fe3238cde70242080 (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')
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; |