Commit 53b8ed64 authored by Khushal's avatar Khushal Committed by Commit Bot

Reland cc: Decode discardable images in PaintRecord backed shaders.

This reverts commit fe24d7f3. Since we
rasterize the SkPicture with decoded images in the test, we need to
initialize its pixels. Fix that.

R=enne@chromium.org
TBR=chrishtr@chromium.org

Bug: 735741,728359
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I10989e2bc28594c9fa8e291559ae2753e65de29d
Reviewed-on: https://chromium-review.googlesource.com/695449Reviewed-by: default avatarenne <enne@chromium.org>
Commit-Queue: Khushal <khushalsagar@chromium.org>
Cr-Commit-Position: refs/heads/master@{#505885}
parent 4fc238b5
...@@ -606,6 +606,8 @@ cc_test("cc_unittests") { ...@@ -606,6 +606,8 @@ cc_test("cc_unittests") {
"paint/display_item_list_unittest.cc", "paint/display_item_list_unittest.cc",
"paint/paint_image_unittest.cc", "paint/paint_image_unittest.cc",
"paint/paint_op_buffer_unittest.cc", "paint/paint_op_buffer_unittest.cc",
"paint/paint_shader_unittest.cc",
"paint/scoped_image_flags_unittest.cc",
"paint/solid_color_analyzer_unittest.cc", "paint/solid_color_analyzer_unittest.cc",
"raster/playback_image_provider_unittest.cc", "raster/playback_image_provider_unittest.cc",
"raster/raster_buffer_provider_unittest.cc", "raster/raster_buffer_provider_unittest.cc",
......
...@@ -45,6 +45,8 @@ cc_component("paint") { ...@@ -45,6 +45,8 @@ cc_component("paint") {
"paint_shader.h", "paint_shader.h",
"record_paint_canvas.cc", "record_paint_canvas.cc",
"record_paint_canvas.h", "record_paint_canvas.h",
"scoped_image_flags.cc",
"scoped_image_flags.h",
"skia_paint_canvas.cc", "skia_paint_canvas.cc",
"skia_paint_canvas.h", "skia_paint_canvas.h",
"skia_paint_image_generator.cc", "skia_paint_image_generator.cc",
......
This diff is collapsed.
...@@ -747,6 +747,43 @@ TEST_F(DiscardableImageMapTest, GathersAnimatedImages) { ...@@ -747,6 +747,43 @@ TEST_F(DiscardableImageMapTest, GathersAnimatedImages) {
EXPECT_DCHECK_DEATH(images[2]->frame_index()); EXPECT_DCHECK_DEATH(images[2]->frame_index());
} }
TEST_F(DiscardableImageMapTest, CapturesImagesInPaintRecordShaders) {
// Create the record to use in the shader.
auto shader_record = sk_make_sp<PaintOpBuffer>();
shader_record->push<ScaleOp>(2.0f, 2.0f);
PaintImage paint_image = CreateDiscardablePaintImage(gfx::Size(100, 100));
shader_record->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr);
gfx::Rect visible_rect(500, 500);
scoped_refptr<DisplayItemList> display_list = new DisplayItemList();
display_list->StartPaint();
display_list->push<ScaleOp>(2.0f, 2.0f);
PaintFlags flags;
SkRect tile = SkRect::MakeWH(100, 100);
flags.setShader(PaintShader::MakePaintRecord(
shader_record, tile, SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode, nullptr));
display_list->push<DrawRectOp>(SkRect::MakeWH(200, 200), flags);
display_list->EndPaintOfUnpaired(visible_rect);
display_list->Finalize();
display_list->GenerateDiscardableImagesMetadata();
const auto& image_map = display_list->discardable_image_map();
// The image rect is set to the rect for the DrawRectOp.
std::vector<PositionScaleDrawImage> draw_images =
GetDiscardableImagesInRect(image_map, visible_rect);
std::vector<gfx::Rect> inset_rects = InsetImageRects(draw_images);
ASSERT_EQ(draw_images.size(), 1u);
EXPECT_EQ(draw_images[0].image, paint_image);
// The position of the image is the position of the DrawRectOp that uses the
// shader.
EXPECT_EQ(gfx::Rect(400, 400), inset_rects[0]);
// The scale of the image includes the scale at which the shader record is
// rasterized.
EXPECT_EQ(SkSize::Make(4.f, 4.f), draw_images[0].scale);
}
class DiscardableImageMapColorSpaceTest class DiscardableImageMapColorSpaceTest
: public DiscardableImageMapTest, : public DiscardableImageMapTest,
public testing::WithParamInterface<gfx::ColorSpace> {}; public testing::WithParamInterface<gfx::ColorSpace> {};
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "cc/paint/paint_op_reader.h" #include "cc/paint/paint_op_reader.h"
#include "cc/paint/paint_op_writer.h" #include "cc/paint/paint_op_writer.h"
#include "cc/paint/paint_record.h" #include "cc/paint/paint_record.h"
#include "cc/paint/scoped_image_flags.h"
#include "third_party/skia/include/core/SkAnnotation.h" #include "third_party/skia/include/core/SkAnnotation.h"
#include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkRegion.h" #include "third_party/skia/include/core/SkRegion.h"
...@@ -25,22 +26,6 @@ SkIRect RoundOutRect(const SkRect& rect) { ...@@ -25,22 +26,6 @@ SkIRect RoundOutRect(const SkRect& rect) {
return result; return result;
} }
bool IsImageShader(const PaintFlags& flags) {
return flags.HasShader() &&
flags.getShader()->shader_type() == PaintShader::Type::kImage;
}
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) { bool QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) {
DCHECK(op->IsDrawOp()); DCHECK(op->IsDrawOp());
...@@ -58,65 +43,6 @@ bool QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) { ...@@ -58,65 +43,6 @@ bool QuickRejectDraw(const PaintOp* op, const SkCanvas* canvas) {
return canvas->quickReject(rect); return canvas->quickReject(rect);
} }
// 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());
DrawImage draw_image(paint_image, RoundOutRect(src_rect),
flags.getFilterQuality(), total_image_matrix);
scoped_decoded_draw_image_ =
image_provider->GetDecodedDrawImage(draw_image);
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, void RasterWithAlpha(const PaintOp* op,
SkCanvas* canvas, SkCanvas* canvas,
const PlaybackParams& params, const PlaybackParams& params,
...@@ -133,7 +59,7 @@ void RasterWithAlpha(const PaintOp* op, ...@@ -133,7 +59,7 @@ void RasterWithAlpha(const PaintOp* op,
// ImageProvider if it consists of an image shader. // ImageProvider if it consists of an image shader.
base::Optional<ScopedImageFlags> scoped_flags; base::Optional<ScopedImageFlags> scoped_flags;
const PaintFlags* decoded_flags = &flags_op->flags; const PaintFlags* decoded_flags = &flags_op->flags;
if (params.image_provider && IsImageShader(flags_op->flags)) { if (params.image_provider && flags_op->HasDiscardableImagesFromFlags()) {
scoped_flags.emplace(params.image_provider, flags_op->flags, scoped_flags.emplace(params.image_provider, flags_op->flags,
canvas->getTotalMatrix()); canvas->getTotalMatrix());
decoded_flags = scoped_flags.value().decoded_flags(); decoded_flags = scoped_flags.value().decoded_flags();
...@@ -1457,12 +1383,47 @@ bool PaintOp::GetBounds(const PaintOp* op, SkRect* rect) { ...@@ -1457,12 +1383,47 @@ bool PaintOp::GetBounds(const PaintOp* op, SkRect* rect) {
return false; return false;
} }
// static
bool PaintOp::OpHasDiscardableImages(const PaintOp* op) {
if (op->IsPaintOpWithFlags() && static_cast<const PaintOpWithFlags*>(op)
->HasDiscardableImagesFromFlags()) {
return true;
}
if (op->GetType() == PaintOpType::DrawImage &&
static_cast<const DrawImageOp*>(op)->HasDiscardableImages()) {
return true;
} else if (op->GetType() == PaintOpType::DrawImageRect &&
static_cast<const DrawImageRectOp*>(op)->HasDiscardableImages()) {
return true;
} else if (op->GetType() == PaintOpType::DrawRecord &&
static_cast<const DrawRecordOp*>(op)->HasDiscardableImages()) {
return true;
}
return false;
}
void PaintOp::DestroyThis() { void PaintOp::DestroyThis() {
auto func = g_destructor_functions[type]; auto func = g_destructor_functions[type];
if (func) if (func)
func(this); func(this);
} }
bool PaintOpWithFlags::HasDiscardableImagesFromFlags() const {
if (!IsDrawOp())
return false;
if (!flags.HasShader())
return false;
else if (flags.getShader()->shader_type() == PaintShader::Type::kImage)
return flags.getShader()->paint_image().IsLazyGenerated();
else if (flags.getShader()->shader_type() == PaintShader::Type::kPaintRecord)
return flags.getShader()->paint_record()->HasDiscardableImages();
return false;
}
void PaintOpWithFlags::RasterWithFlags(SkCanvas* canvas, void PaintOpWithFlags::RasterWithFlags(SkCanvas* canvas,
const PaintFlags* flags, const PaintFlags* flags,
const PlaybackParams& params) const { const PlaybackParams& params) const {
...@@ -1740,7 +1701,8 @@ void PaintOpBuffer::Playback(SkCanvas* canvas, ...@@ -1740,7 +1701,8 @@ void PaintOpBuffer::Playback(SkCanvas* canvas,
// general case we defer this to the SkCanvas but if we will be // general case we defer this to the SkCanvas but if we will be
// using an ImageProvider for pre-decoding images, we can save // using an ImageProvider for pre-decoding images, we can save
// performing an expensive decode that will never be rasterized. // performing an expensive decode that will never be rasterized.
const bool skip_op = params.image_provider && IsImageOp(draw_op) && const bool skip_op = params.image_provider &&
PaintOp::OpHasDiscardableImages(draw_op) &&
QuickRejectDraw(draw_op, canvas); QuickRejectDraw(draw_op, canvas);
if (skip_op) { if (skip_op) {
// Now that we know this op will be skipped, we can push the save // Now that we know this op will be skipped, we can push the save
...@@ -1770,15 +1732,15 @@ void PaintOpBuffer::Playback(SkCanvas* canvas, ...@@ -1770,15 +1732,15 @@ void PaintOpBuffer::Playback(SkCanvas* canvas,
} }
} }
if (params.image_provider && IsImageOp(op)) { if (params.image_provider && PaintOp::OpHasDiscardableImages(op)) {
if (QuickRejectDraw(op, canvas)) if (QuickRejectDraw(op, canvas))
continue; continue;
auto* flags_op = op->IsPaintOpWithFlags() auto* flags_op = op->IsPaintOpWithFlags()
? static_cast<const PaintOpWithFlags*>(op) ? static_cast<const PaintOpWithFlags*>(op)
: nullptr; : nullptr;
if (flags_op && IsImageShader(flags_op->flags)) { if (flags_op && flags_op->HasDiscardableImagesFromFlags()) {
ScopedImageFlags scoped_flags(image_provider, flags_op->flags, ScopedImageFlags scoped_flags(params.image_provider, flags_op->flags,
canvas->getTotalMatrix()); canvas->getTotalMatrix());
// Only rasterize the op if we successfully decoded the image. // Only rasterize the op if we successfully decoded the image.
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/memory/aligned_memory.h" #include "base/memory/aligned_memory.h"
#include "base/optional.h" #include "base/optional.h"
#include "cc/base/math_util.h" #include "cc/base/math_util.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/paint_canvas.h" #include "cc/paint/paint_canvas.h"
#include "cc/paint/paint_export.h" #include "cc/paint/paint_export.h"
#include "cc/paint/paint_flags.h" #include "cc/paint/paint_flags.h"
...@@ -29,7 +30,6 @@ ...@@ -29,7 +30,6 @@
namespace cc { namespace cc {
class ImageDecodeCache; class ImageDecodeCache;
class ImageProvider;
class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix { class CC_PAINT_EXPORT ThreadsafeMatrix : public SkMatrix {
public: public:
...@@ -137,6 +137,10 @@ class CC_PAINT_EXPORT PaintOp { ...@@ -137,6 +137,10 @@ class CC_PAINT_EXPORT PaintOp {
// for the op. // for the op.
static bool GetBounds(const PaintOp* op, SkRect* rect); static bool GetBounds(const PaintOp* op, SkRect* rect);
// Returns true if executing this op will require decoding of any lazy
// generated images.
static bool OpHasDiscardableImages(const PaintOp* op);
int CountSlowPaths() const { return 0; } int CountSlowPaths() const { return 0; }
int CountSlowPathsFromFlags() const { return 0; } int CountSlowPathsFromFlags() const { return 0; }
...@@ -195,14 +199,7 @@ class CC_PAINT_EXPORT PaintOpWithFlags : public PaintOp { ...@@ -195,14 +199,7 @@ class CC_PAINT_EXPORT PaintOpWithFlags : public PaintOp {
int CountSlowPathsFromFlags() const { return flags.getPathEffect() ? 1 : 0; } int CountSlowPathsFromFlags() const { return flags.getPathEffect() ? 1 : 0; }
bool HasNonAAPaint() const { return !flags.isAntiAlias(); } bool HasNonAAPaint() const { return !flags.isAntiAlias(); }
bool HasDiscardableImagesFromFlags() const { bool HasDiscardableImagesFromFlags() const;
if (!IsDrawOp())
return false;
SkShader* shader = flags.getSkShader();
SkImage* image = shader ? shader->isAImage(nullptr, nullptr) : nullptr;
return image && image->isLazyGenerated();
}
void RasterWithFlags(SkCanvas* canvas, void RasterWithFlags(SkCanvas* canvas,
const PaintFlags* flags, const PaintFlags* flags,
...@@ -981,6 +978,7 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { ...@@ -981,6 +978,7 @@ class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt {
friend class DisplayItemList; friend class DisplayItemList;
friend class PaintOpBufferOffsetsTest; friend class PaintOpBufferOffsetsTest;
friend class SolidColorAnalyzer; friend class SolidColorAnalyzer;
friend class ScopedImageFlags;
// Replays the paint op buffer into the canvas. If |indices| is specified, it // 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 // contains indices in an increasing order and only the indices specified in
......
...@@ -6,9 +6,25 @@ ...@@ -6,9 +6,25 @@
#include "base/memory/ptr_util.h" #include "base/memory/ptr_util.h"
#include "cc/paint/paint_record.h" #include "cc/paint/paint_record.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkGradientShader.h"
namespace cc { namespace cc {
namespace {
sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record,
const SkRect& bounds,
const SkMatrix* matrix,
ImageProvider* image_provider) {
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(bounds);
if (matrix)
canvas->setMatrix(*matrix);
record->Playback(canvas, image_provider);
return recorder.finishRecordingAsPicture();
}
} // namespace
sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) { sk_sp<PaintShader> PaintShader::MakeColor(SkColor color) {
sk_sp<PaintShader> shader(new PaintShader(Type::kColor)); sk_sp<PaintShader> shader(new PaintShader(Type::kColor));
...@@ -139,6 +155,96 @@ sk_sp<PaintShader> PaintShader::MakePaintRecord( ...@@ -139,6 +155,96 @@ sk_sp<PaintShader> PaintShader::MakePaintRecord(
PaintShader::PaintShader(Type type) : shader_type_(type) {} PaintShader::PaintShader(Type type) : shader_type_(type) {}
PaintShader::~PaintShader() = default; PaintShader::~PaintShader() = default;
bool PaintShader::GetRasterizationTileRect(const SkMatrix& ctm,
SkRect* tile_rect) const {
DCHECK_EQ(shader_type_, Type::kPaintRecord);
// If we are using a fixed scale, the record is rasterized with the original
// tile size and scaling is applied to the generated output.
if (scaling_behavior_ == ScalingBehavior::kFixedScale) {
*tile_rect = tile_;
return true;
}
SkMatrix matrix = ctm;
if (local_matrix_.has_value())
matrix.preConcat(local_matrix_.value());
SkSize scale;
if (!matrix.decomposeScale(&scale)) {
// Decomposition failed, use an approximation.
scale.set(SkScalarSqrt(matrix.getScaleX() * matrix.getScaleX() +
matrix.getSkewX() * matrix.getSkewX()),
SkScalarSqrt(matrix.getScaleY() * matrix.getScaleY() +
matrix.getSkewY() * matrix.getSkewY()));
}
SkSize scaled_size =
SkSize::Make(SkScalarAbs(scale.width() * tile_.width()),
SkScalarAbs(scale.height() * tile_.height()));
// Clamp the tile size to about 4M pixels.
// TODO(khushalsagar): We need to consider the max texture size as well.
static const SkScalar kMaxTileArea = 2048 * 2048;
SkScalar tile_area = scaled_size.width() * scaled_size.height();
if (tile_area > kMaxTileArea) {
SkScalar clamp_scale = SkScalarSqrt(kMaxTileArea / tile_area);
scaled_size.set(scaled_size.width() * clamp_scale,
scaled_size.height() * clamp_scale);
}
scaled_size = scaled_size.toCeil();
if (scaled_size.isEmpty())
return false;
*tile_rect = SkRect::MakeWH(scaled_size.width(), scaled_size.height());
return true;
}
sk_sp<PaintShader> PaintShader::CreateDecodedPaintRecord(
const SkMatrix& ctm,
ImageProvider* image_provider) const {
DCHECK_EQ(shader_type_, Type::kPaintRecord);
// For creating a decoded PaintRecord shader, we need to do the following:
// 1) Figure out the scale at which the record should be rasterization given
// the ctm and local_matrix on the shader.
// 2) Transform this record to an SkPicture with this scale and replace
// encoded images in this record with decodes from the ImageProvider. This
// is done by setting the rasterization_matrix_ for this shader to be used
// in GetSkShader.
// 3) Since the SkShader will use a scaled SkPicture, we use a kFixedScale for
// the decoded shader which creates an SkPicture backed SkImage for
// creating the decoded SkShader.
// Note that the scaling logic here is replicated from
// SkPictureShader::refBitmapShader.
SkRect tile_rect;
if (!GetRasterizationTileRect(ctm, &tile_rect))
return nullptr;
sk_sp<PaintShader> shader(new PaintShader(Type::kPaintRecord));
shader->record_ = record_;
shader->tile_ = tile_rect;
// Use a fixed scale since we have already scaled the tile rect and fixed the
// raster scale.
shader->scaling_behavior_ = ScalingBehavior::kFixedScale;
shader->rasterization_matrix_.emplace();
shader->rasterization_matrix_.value().setRectToRect(
tile_, tile_rect, SkMatrix::kFill_ScaleToFit);
shader->tx_ = tx_;
shader->ty_ = ty_;
const SkSize tile_scale =
SkSize::Make(SkIntToScalar(tile_rect.width()) / tile_.width(),
SkIntToScalar(tile_rect.height()) / tile_.height());
shader->local_matrix_ = GetLocalMatrix();
shader->local_matrix_->preScale(1 / tile_scale.width(),
1 / tile_scale.height());
shader->image_provider_ = image_provider;
return shader;
}
sk_sp<SkShader> PaintShader::GetSkShader() const { sk_sp<SkShader> PaintShader::GetSkShader() const {
if (cached_shader_) if (cached_shader_)
return cached_shader_; return cached_shader_;
...@@ -182,7 +288,12 @@ sk_sp<SkShader> PaintShader::GetSkShader() const { ...@@ -182,7 +288,12 @@ sk_sp<SkShader> PaintShader::GetSkShader() const {
tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr); tx_, ty_, local_matrix_ ? &*local_matrix_ : nullptr);
break; break;
case Type::kPaintRecord: { case Type::kPaintRecord: {
auto picture = ToSkPicture(record_, tile_); // Create a recording at the desired scale if this record has images which
// have been decoded before raster.
auto picture = ToSkPicture(
record_, tile_,
rasterization_matrix_ ? &rasterization_matrix_.value() : nullptr,
image_provider_);
switch (scaling_behavior_) { switch (scaling_behavior_) {
// For raster scale, we create a picture shader directly. // For raster scale, we create a picture shader directly.
...@@ -191,7 +302,7 @@ sk_sp<SkShader> PaintShader::GetSkShader() const { ...@@ -191,7 +302,7 @@ sk_sp<SkShader> PaintShader::GetSkShader() const {
std::move(picture), tx_, ty_, std::move(picture), tx_, ty_,
local_matrix_ ? &*local_matrix_ : nullptr, nullptr); local_matrix_ ? &*local_matrix_ : nullptr, nullptr);
break; break;
// For fixed scale, we create an image shader with and image backed by // For fixed scale, we create an image shader with an image backed by
// the picture. // the picture.
case ScalingBehavior::kFixedScale: { case ScalingBehavior::kFixedScale: {
auto image = SkImage::MakeFromPicture( auto image = SkImage::MakeFromPicture(
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/core/SkShader.h"
namespace cc { namespace cc {
class ImageProvider;
class PaintOpBuffer; class PaintOpBuffer;
using PaintRecord = PaintOpBuffer; using PaintRecord = PaintOpBuffer;
...@@ -111,8 +111,12 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { ...@@ -111,8 +111,12 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
return image_; return image_;
} }
const sk_sp<PaintRecord>& paint_record() const { return record_; }
bool GetRasterizationTileRect(const SkMatrix& ctm, SkRect* tile_rect) const;
SkShader::TileMode tx() const { return tx_; } SkShader::TileMode tx() const { return tx_; }
SkShader::TileMode ty() const { return ty_; } SkShader::TileMode ty() const { return ty_; }
SkRect tile() const { return tile_; }
bool IsOpaque() const; bool IsOpaque() const;
...@@ -127,11 +131,17 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { ...@@ -127,11 +131,17 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
friend class PaintOpReader; friend class PaintOpReader;
friend class PaintOpSerializationTestUtils; friend class PaintOpSerializationTestUtils;
friend class PaintOpWriter; friend class PaintOpWriter;
friend class ScopedImageFlags;
FRIEND_TEST_ALL_PREFIXES(PaintShaderTest, DecodePaintRecord);
explicit PaintShader(Type type); explicit PaintShader(Type type);
sk_sp<SkShader> GetSkShader() const; sk_sp<SkShader> GetSkShader() const;
sk_sp<PaintShader> CreateDecodedPaintRecord(
const SkMatrix& ctm,
ImageProvider* image_provider) const;
void SetColorsAndPositions(const SkColor* colors, void SetColorsAndPositions(const SkColor* colors,
const SkScalar* positions, const SkScalar* positions,
int count); int count);
...@@ -166,6 +176,12 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt { ...@@ -166,6 +176,12 @@ class CC_PAINT_EXPORT PaintShader : public SkRefCnt {
std::vector<SkColor> colors_; std::vector<SkColor> colors_;
std::vector<SkScalar> positions_; std::vector<SkScalar> positions_;
// The following are only used during raster to replace the decoded images in
// the record for this shader. The |image_provider_| and
// |decoded_image_stash_| must outlive this shader.
ImageProvider* image_provider_ = nullptr;
base::Optional<SkMatrix> rasterization_matrix_;
mutable sk_sp<SkShader> cached_shader_; mutable sk_sp<SkShader> cached_shader_;
DISALLOW_COPY_AND_ASSIGN(PaintShader); DISALLOW_COPY_AND_ASSIGN(PaintShader);
......
// 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/paint_shader.h"
#include "cc/paint/draw_image.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/paint/paint_op_buffer.h"
#include "cc/test/fake_paint_image_generator.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/SkSurface.h"
namespace cc {
namespace {
class MockImageGenerator : public FakePaintImageGenerator {
public:
explicit MockImageGenerator(const gfx::Size& size)
: FakePaintImageGenerator(
SkImageInfo::MakeN32Premul(size.width(), size.height())) {}
MOCK_METHOD5(GetPixels,
bool(const SkImageInfo&, void*, size_t, size_t, uint32_t));
};
class MockImageProvider : public ImageProvider {
public:
MockImageProvider() = default;
~MockImageProvider() override = default;
ScopedDecodedDrawImage GetDecodedDrawImage(
const DrawImage& draw_image) override {
draw_image_ = draw_image;
SkBitmap bitmap;
bitmap.allocN32Pixels(10, 10);
bitmap.eraseColor(SK_ColorBLACK);
sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
return ScopedDecodedDrawImage(
DecodedDrawImage(image, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f),
draw_image.filter_quality()));
}
const DrawImage& draw_image() const { return draw_image_; }
private:
DrawImage draw_image_;
};
} // namespace
TEST(PaintShaderTest, RasterizationRectForRecordShaders) {
SkMatrix local_matrix = SkMatrix::MakeScale(0.5f, 0.5f);
auto record_shader = PaintShader::MakePaintRecord(
sk_make_sp<PaintOpBuffer>(), SkRect::MakeWH(100, 100),
SkShader::TileMode::kClamp_TileMode, SkShader::TileMode::kClamp_TileMode,
&local_matrix);
SkRect tile_rect;
SkMatrix ctm = SkMatrix::MakeScale(0.5f, 0.5f);
EXPECT_TRUE(record_shader->GetRasterizationTileRect(ctm, &tile_rect));
EXPECT_EQ(tile_rect, SkRect::MakeWH(25, 25));
}
TEST(PaintShaderTest, DecodePaintRecord) {
auto record = sk_make_sp<PaintOpBuffer>();
// Use a strict mock for the generator. It should never be used when
// rasterizing this shader, since the decode should be done by the
// ImageProvider.
auto generator =
sk_make_sp<testing::StrictMock<MockImageGenerator>>(gfx::Size(100, 100));
PaintImage paint_image = PaintImageBuilder()
.set_id(PaintImage::GetNextId())
.set_paint_image_generator(generator)
.TakePaintImage();
record->push<DrawImageOp>(paint_image, 0.f, 0.f, nullptr);
SkMatrix local_matrix = SkMatrix::MakeScale(0.5f, 0.5f);
auto record_shader = PaintShader::MakePaintRecord(
record, SkRect::MakeWH(100, 100), SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode, &local_matrix);
PaintOpBuffer buffer;
PaintFlags flags;
flags.setShader(record_shader);
buffer.push<ScaleOp>(0.5f, 0.5f);
buffer.push<DrawRectOp>(SkRect::MakeWH(100, 100), flags);
MockImageProvider image_provider;
SaveCountingCanvas canvas;
buffer.Playback(&canvas, &image_provider);
EXPECT_EQ(canvas.draw_rect_, SkRect::MakeWH(100, 100));
SkShader* shader = canvas.paint_.getShader();
ASSERT_TRUE(shader);
SkMatrix decoded_local_matrix;
SkShader::TileMode xy[2];
SkImage* skia_image = shader->isAImage(&decoded_local_matrix, xy);
ASSERT_TRUE(skia_image);
EXPECT_TRUE(skia_image->isLazyGenerated());
EXPECT_EQ(xy[0], record_shader->tx());
EXPECT_EQ(xy[1], record_shader->ty());
EXPECT_EQ(decoded_local_matrix, SkMatrix::MakeScale(2.f, 2.f));
// The rasterization of the shader is internal to skia, so use a raster canvas
// to verify that the decoded paint does not have the encoded image.
auto surface = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(100, 100));
surface->getCanvas()->drawPaint(canvas.paint_);
// Using the shader requests decode for images at the correct scale.
EXPECT_EQ(image_provider.draw_image().paint_image(), paint_image);
EXPECT_EQ(image_provider.draw_image().scale(), SkSize::Make(0.25f, 0.25f));
}
} // namespace cc
// 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/scoped_image_flags.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/paint_image_builder.h"
namespace cc {
namespace {
SkIRect RoundOutRect(const SkRect& rect) {
SkIRect result;
rect.roundOut(&result);
return result;
}
} // namespace
ScopedImageFlags::DecodeStashingImageProvider::DecodeStashingImageProvider(
ImageProvider* source_provider)
: source_provider_(source_provider) {}
ScopedImageFlags::DecodeStashingImageProvider::~DecodeStashingImageProvider() =
default;
ImageProvider::ScopedDecodedDrawImage
ScopedImageFlags::DecodeStashingImageProvider::GetDecodedDrawImage(
const DrawImage& draw_image) {
auto decode = source_provider_->GetDecodedDrawImage(draw_image);
if (!decode)
return ScopedDecodedDrawImage();
// No need to add any destruction callback to the returned image. The images
// decoded here match the lifetime of this provider.
auto image_to_return = ScopedDecodedDrawImage(decode.decoded_image());
decoded_images_.push_back(std::move(decode));
return image_to_return;
}
ScopedImageFlags::ScopedImageFlags(ImageProvider* image_provider,
const PaintFlags& flags,
const SkMatrix& ctm)
: decode_stashing_image_provider_(image_provider) {
if (flags.getShader()->shader_type() == PaintShader::Type::kImage) {
DecodeImageShader(flags, ctm);
} else {
DCHECK_EQ(flags.getShader()->shader_type(),
PaintShader::Type::kPaintRecord);
DecodeRecordShader(flags, ctm);
}
}
ScopedImageFlags::~ScopedImageFlags() = default;
void ScopedImageFlags::DecodeImageShader(const PaintFlags& flags,
const SkMatrix& ctm) {
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());
DrawImage draw_image(paint_image, RoundOutRect(src_rect),
flags.getFilterQuality(), total_image_matrix);
auto decoded_draw_image =
decode_stashing_image_provider_.GetDecodedDrawImage(draw_image);
if (!decoded_draw_image)
return;
const auto& decoded_image = 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));
}
void ScopedImageFlags::DecodeRecordShader(const PaintFlags& flags,
const SkMatrix& ctm) {
auto decoded_shader = flags.getShader()->CreateDecodedPaintRecord(
ctm, &decode_stashing_image_provider_);
if (!decoded_shader)
return;
decoded_flags_.emplace(flags);
decoded_flags_.value().setShader(std::move(decoded_shader));
}
} // namespace cc
// 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_SCOPED_IMAGE_FLAGS_H_
#define CC_PAINT_SCOPED_IMAGE_FLAGS_H_
#include "base/macros.h"
#include "cc/paint/paint_export.h"
#include "cc/paint/paint_op_buffer.h"
namespace cc {
class ImageProvider;
// A helper class to decode images inside the provided |flags| and provide a
// PaintFlags with the decoded images that can directly be used for
// rasterization.
// This class should only be used if |flags| has any discardable images.
class CC_PAINT_EXPORT ScopedImageFlags {
public:
// |image_provider| must outlive this class.
ScopedImageFlags(ImageProvider* image_provider,
const PaintFlags& flags,
const SkMatrix& ctm);
~ScopedImageFlags();
// The usage of these flags should not extend beyond the lifetime of this
// object.
PaintFlags* decoded_flags() {
return decoded_flags_ ? &decoded_flags_.value() : nullptr;
}
private:
// An ImageProvider that passes decode requests through to the
// |source_provider| but keeps the decode cached throughtout its lifetime,
// instead of passing the ref to the caller.
class DecodeStashingImageProvider : public ImageProvider {
public:
// |source_provider| must outlive this class.
explicit DecodeStashingImageProvider(ImageProvider* source_provider);
~DecodeStashingImageProvider() override;
// ImageProvider implementation.
ScopedDecodedDrawImage GetDecodedDrawImage(
const DrawImage& draw_image) override;
private:
ImageProvider* source_provider_;
std::vector<ScopedDecodedDrawImage> decoded_images_;
DISALLOW_COPY_AND_ASSIGN(DecodeStashingImageProvider);
};
void DecodeImageShader(const PaintFlags& flags, const SkMatrix& ctm);
void DecodeRecordShader(const PaintFlags& flags, const SkMatrix& ctm);
base::Optional<PaintFlags> decoded_flags_;
DecodeStashingImageProvider decode_stashing_image_provider_;
DISALLOW_COPY_AND_ASSIGN(ScopedImageFlags);
};
} // namespace cc
#endif // CC_PAINT_SCOPED_IMAGE_FLAGS_H_
// 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/scoped_image_flags.h"
#include "base/bind.h"
#include "base/callback.h"
#include "cc/test/skia_common.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
namespace {
class MockImageProvider : public ImageProvider {
public:
MockImageProvider() = default;
~MockImageProvider() override { EXPECT_EQ(ref_count_, 0); }
ScopedDecodedDrawImage GetDecodedDrawImage(
const DrawImage& draw_image) override {
ref_count_++;
SkBitmap bitmap;
bitmap.allocN32Pixels(10, 10);
sk_sp<SkImage> image = SkImage::MakeFromBitmap(bitmap);
return ScopedDecodedDrawImage(
DecodedDrawImage(image, SkSize::MakeEmpty(), SkSize::Make(1.0f, 1.0f),
draw_image.filter_quality()),
base::BindOnce(&MockImageProvider::UnrefImage, base::Unretained(this)));
}
void UnrefImage(DecodedDrawImage decoded_image) {
ref_count_--;
CHECK_GE(ref_count_, 0);
}
int ref_count() const { return ref_count_; }
private:
int ref_count_ = 0;
};
} // namespace
TEST(ScopedImageFlagsTest, KeepsDecodesAlive) {
auto record = sk_make_sp<PaintOpBuffer>();
record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f,
0.f, nullptr);
record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f,
0.f, nullptr);
record->push<DrawImageOp>(CreateDiscardablePaintImage(gfx::Size(10, 10)), 0.f,
0.f, nullptr);
auto record_shader = PaintShader::MakePaintRecord(
record, SkRect::MakeWH(100, 100), SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode, &SkMatrix::I());
MockImageProvider provider;
PaintFlags flags;
flags.setShader(record_shader);
{
ScopedImageFlags scoped_flags(&provider, flags, SkMatrix::I());
ASSERT_TRUE(scoped_flags.decoded_flags());
SkPaint paint = scoped_flags.decoded_flags()->ToSkPaint();
ASSERT_TRUE(paint.getShader());
EXPECT_EQ(provider.ref_count(), 3);
}
EXPECT_EQ(provider.ref_count(), 0);
}
} // namespace cc
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment