1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/resources/picture.h"
11 #include "base/base64.h"
12 #include "base/trace_event/trace_event.h"
13 #include "base/trace_event/trace_event_argument.h"
14 #include "base/values.h"
15 #include "cc/base/math_util.h"
16 #include "cc/base/util.h"
17 #include "cc/debug/picture_debug_util.h"
18 #include "cc/debug/traced_picture.h"
19 #include "cc/debug/traced_value.h"
20 #include "cc/layers/content_layer_client.h"
21 #include "skia/ext/pixel_ref_utils.h"
22 #include "third_party/skia/include/core/SkCanvas.h"
23 #include "third_party/skia/include/core/SkDrawPictureCallback.h"
24 #include "third_party/skia/include/core/SkPaint.h"
25 #include "third_party/skia/include/core/SkPictureRecorder.h"
26 #include "third_party/skia/include/core/SkStream.h"
27 #include "third_party/skia/include/utils/SkNullCanvas.h"
28 #include "third_party/skia/include/utils/SkPictureUtils.h"
29 #include "ui/gfx/codec/jpeg_codec.h"
30 #include "ui/gfx/codec/png_codec.h"
31 #include "ui/gfx/geometry/rect_conversions.h"
32 #include "ui/gfx/skia_util.h"
38 bool DecodeBitmap(const void* buffer
, size_t size
, SkBitmap
* bm
) {
39 const unsigned char* data
= static_cast<const unsigned char *>(buffer
);
42 if (gfx::PNGCodec::Decode(data
, size
, bm
))
46 scoped_ptr
<SkBitmap
> decoded_jpeg(gfx::JPEGCodec::Decode(data
, size
));
56 scoped_refptr
<Picture
> Picture::Create(
57 const gfx::Rect
& layer_rect
,
58 ContentLayerClient
* client
,
59 const gfx::Size
& tile_grid_size
,
60 bool gather_pixel_refs
,
61 RecordingSource::RecordingMode recording_mode
) {
62 scoped_refptr
<Picture
> picture
= make_scoped_refptr(new Picture(layer_rect
));
64 picture
->Record(client
, tile_grid_size
, recording_mode
);
65 if (gather_pixel_refs
)
66 picture
->GatherPixelRefs(tile_grid_size
);
71 Picture::Picture(const gfx::Rect
& layer_rect
)
72 : layer_rect_(layer_rect
),
73 cell_size_(layer_rect
.size()) {
74 // Instead of recording a trace event for object creation here, we wait for
75 // the picture to be recorded in Picture::Record.
78 scoped_refptr
<Picture
> Picture::CreateFromSkpValue(const base::Value
* value
) {
79 // Decode the picture from base64.
81 if (!value
->GetAsString(&encoded
))
85 base::Base64Decode(encoded
, &decoded
);
86 SkMemoryStream
stream(decoded
.data(), decoded
.size());
88 // Read the picture. This creates an empty picture on failure.
89 SkPicture
* skpicture
= SkPicture::CreateFromStream(&stream
, &DecodeBitmap
);
90 if (skpicture
== NULL
)
93 gfx::Rect
layer_rect(gfx::SkIRectToRect(skpicture
->cullRect().roundOut()));
94 return make_scoped_refptr(new Picture(skpicture
, layer_rect
));
97 scoped_refptr
<Picture
> Picture::CreateFromValue(const base::Value
* raw_value
) {
98 const base::DictionaryValue
* value
= NULL
;
99 if (!raw_value
->GetAsDictionary(&value
))
102 // Decode the picture from base64.
104 if (!value
->GetString("skp64", &encoded
))
108 base::Base64Decode(encoded
, &decoded
);
109 SkMemoryStream
stream(decoded
.data(), decoded
.size());
111 const base::Value
* layer_rect_value
= NULL
;
112 if (!value
->Get("params.layer_rect", &layer_rect_value
))
115 gfx::Rect layer_rect
;
116 if (!MathUtil::FromValue(layer_rect_value
, &layer_rect
))
119 // Read the picture. This creates an empty picture on failure.
120 SkPicture
* skpicture
= SkPicture::CreateFromStream(&stream
, &DecodeBitmap
);
121 if (skpicture
== NULL
)
124 return make_scoped_refptr(new Picture(skpicture
, layer_rect
));
127 Picture::Picture(SkPicture
* picture
, const gfx::Rect
& layer_rect
)
128 : layer_rect_(layer_rect
),
129 picture_(skia::AdoptRef(picture
)),
130 cell_size_(layer_rect
.size()) {
133 Picture::Picture(const skia::RefPtr
<SkPicture
>& picture
,
134 const gfx::Rect
& layer_rect
,
135 const PixelRefMap
& pixel_refs
) :
136 layer_rect_(layer_rect
),
138 pixel_refs_(pixel_refs
),
139 cell_size_(layer_rect
.size()) {
142 Picture::~Picture() {
143 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
144 TRACE_DISABLED_BY_DEFAULT("cc.debug.picture"), "cc::Picture", this);
147 bool Picture::IsSuitableForGpuRasterization(const char** reason
) const {
150 // TODO(hendrikw): SkPicture::suitableForGpuRasterization takes a GrContext.
151 // Currently the GrContext isn't used, and should probably be removed from
153 return picture_
->suitableForGpuRasterization(nullptr, reason
);
156 int Picture::ApproximateOpCount() const {
158 return picture_
->approximateOpCount();
161 size_t Picture::ApproximateMemoryUsage() const {
163 return SkPictureUtils::ApproximateBytesUsed(picture_
.get());
166 bool Picture::HasText() const {
168 return picture_
->hasText();
171 void Picture::Record(ContentLayerClient
* painter
,
172 const gfx::Size
& tile_grid_size
,
173 RecordingSource::RecordingMode recording_mode
) {
177 AsTraceableRecordData(),
182 DCHECK(!tile_grid_size
.IsEmpty());
184 // TODO(mtklein): If SkRTree sticks, clean up tile_grid_info. skbug.com/3085
185 SkRTreeFactory factory
;
186 SkPictureRecorder recorder
;
188 skia::RefPtr
<SkCanvas
> canvas
;
189 canvas
= skia::SharePtr(recorder
.beginRecording(
190 layer_rect_
.width(), layer_rect_
.height(), &factory
,
191 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag
));
193 ContentLayerClient::PaintingControlSetting painting_control
=
194 ContentLayerClient::PAINTING_BEHAVIOR_NORMAL
;
196 switch (recording_mode
) {
197 case RecordingSource::RECORD_NORMALLY
:
198 // Already setup for normal recording.
200 case RecordingSource::RECORD_WITH_SK_NULL_CANVAS
:
201 canvas
= skia::AdoptRef(SkCreateNullCanvas());
203 case RecordingSource::RECORD_WITH_PAINTING_DISABLED
:
204 // We pass a disable flag through the paint calls when perfromance
205 // testing (the only time this case should ever arise) when we want to
206 // prevent the Blink GraphicsContext object from consuming any compute
208 canvas
= skia::AdoptRef(SkCreateNullCanvas());
209 painting_control
= ContentLayerClient::DISPLAY_LIST_CONSTRUCTION_DISABLED
;
211 case RecordingSource::RECORD_WITH_CACHING_DISABLED
:
212 // This mode should give the same results as RECORD_NORMALLY.
213 painting_control
= ContentLayerClient::DISPLAY_LIST_CACHING_DISABLED
;
220 canvas
->translate(SkFloatToScalar(-layer_rect_
.x()),
221 SkFloatToScalar(-layer_rect_
.y()));
223 SkRect layer_skrect
= SkRect::MakeXYWH(layer_rect_
.x(),
226 layer_rect_
.height());
227 canvas
->clipRect(layer_skrect
);
229 painter
->PaintContents(canvas
.get(), layer_rect_
, painting_control
);
232 picture_
= skia::AdoptRef(recorder
.endRecording());
238 void Picture::GatherPixelRefs(const gfx::Size
& tile_grid_size
) {
239 TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
240 "width", layer_rect_
.width(),
241 "height", layer_rect_
.height());
244 DCHECK(pixel_refs_
.empty());
245 if (!WillPlayBackBitmaps())
247 cell_size_
= tile_grid_size
;
248 DCHECK_GT(cell_size_
.width(), 0);
249 DCHECK_GT(cell_size_
.height(), 0);
251 int min_x
= std::numeric_limits
<int>::max();
252 int min_y
= std::numeric_limits
<int>::max();
256 skia::DiscardablePixelRefList pixel_refs
;
257 skia::PixelRefUtils::GatherDiscardablePixelRefs(picture_
.get(), &pixel_refs
);
258 for (skia::DiscardablePixelRefList::const_iterator it
= pixel_refs
.begin();
259 it
!= pixel_refs
.end();
262 RoundDown(static_cast<int>(it
->pixel_ref_rect
.x()),
264 RoundDown(static_cast<int>(it
->pixel_ref_rect
.y()),
265 cell_size_
.height()));
267 RoundDown(static_cast<int>(std::ceil(it
->pixel_ref_rect
.right())),
269 RoundDown(static_cast<int>(std::ceil(it
->pixel_ref_rect
.bottom())),
270 cell_size_
.height()));
272 for (int y
= min
.y(); y
<= max
.y(); y
+= cell_size_
.height()) {
273 for (int x
= min
.x(); x
<= max
.x(); x
+= cell_size_
.width()) {
274 PixelRefMapKey
key(x
, y
);
275 pixel_refs_
[key
].push_back(it
->pixel_ref
);
279 min_x
= std::min(min_x
, min
.x());
280 min_y
= std::min(min_y
, min
.y());
281 max_x
= std::max(max_x
, max
.x());
282 max_y
= std::max(max_y
, max
.y());
285 min_pixel_cell_
= gfx::Point(min_x
, min_y
);
286 max_pixel_cell_
= gfx::Point(max_x
, max_y
);
289 int Picture::Raster(SkCanvas
* canvas
,
290 SkDrawPictureCallback
* callback
,
291 const Region
& negated_content_region
,
292 float contents_scale
) const {
297 AsTraceableRasterData(contents_scale
));
303 for (Region::Iterator
it(negated_content_region
); it
.has_rect(); it
.next())
304 canvas
->clipRect(gfx::RectToSkRect(it
.rect()), SkRegion::kDifference_Op
);
306 canvas
->scale(contents_scale
, contents_scale
);
307 canvas
->translate(layer_rect_
.x(), layer_rect_
.y());
309 // If we have a callback, we need to call |draw()|, |drawPicture()| doesn't
310 // take a callback. This is used by |AnalysisCanvas| to early out.
311 picture_
->playback(canvas
, callback
);
313 // Prefer to call |drawPicture()| on the canvas since it could place the
314 // entire picture on the canvas instead of parsing the skia operations.
315 canvas
->drawPicture(picture_
.get());
318 canvas
->getClipDeviceBounds(&bounds
);
321 "cc", "Picture::Raster",
322 "num_pixels_rasterized", bounds
.width() * bounds
.height());
323 return bounds
.width() * bounds
.height();
326 void Picture::Replay(SkCanvas
* canvas
) {
327 TRACE_EVENT_BEGIN0("cc", "Picture::Replay");
329 picture_
->playback(canvas
);
331 canvas
->getClipDeviceBounds(&bounds
);
332 TRACE_EVENT_END1("cc", "Picture::Replay",
333 "num_pixels_replayed", bounds
.width() * bounds
.height());
336 scoped_ptr
<base::Value
> Picture::AsValue() const {
337 // Encode the picture as base64.
338 scoped_ptr
<base::DictionaryValue
> res(new base::DictionaryValue());
339 res
->Set("params.layer_rect", MathUtil::AsValue(layer_rect_
).release());
340 std::string b64_picture
;
341 PictureDebugUtil::SerializeAsBase64(picture_
.get(), &b64_picture
);
342 res
->SetString("skp64", b64_picture
);
346 void Picture::EmitTraceSnapshot() const {
347 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
348 TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
349 TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
352 TracedPicture::AsTraceablePicture(this));
355 void Picture::EmitTraceSnapshotAlias(Picture
* original
) const {
356 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
357 TRACE_DISABLED_BY_DEFAULT("cc.debug.picture") ","
358 TRACE_DISABLED_BY_DEFAULT("devtools.timeline.picture"),
361 TracedPicture::AsTraceablePictureAlias(original
));
364 base::LazyInstance
<Picture::PixelRefs
>
365 Picture::PixelRefIterator::empty_pixel_refs_
;
367 Picture::PixelRefIterator::PixelRefIterator()
369 current_pixel_refs_(empty_pixel_refs_
.Pointer()),
377 Picture::PixelRefIterator::PixelRefIterator(
378 const gfx::Rect
& rect
,
379 const Picture
* picture
)
381 current_pixel_refs_(empty_pixel_refs_
.Pointer()),
383 gfx::Rect layer_rect
= picture
->layer_rect_
;
384 gfx::Size cell_size
= picture
->cell_size_
;
385 DCHECK(!cell_size
.IsEmpty());
387 gfx::Rect
query_rect(rect
);
388 // Early out if the query rect doesn't intersect this picture.
389 if (!query_rect
.Intersects(layer_rect
)) {
390 min_point_
= gfx::Point(0, 0);
391 max_point_
= gfx::Point(0, 0);
397 // First, subtract the layer origin as cells are stored in layer space.
398 query_rect
.Offset(-layer_rect
.OffsetFromOrigin());
400 // We have to find a cell_size aligned point that corresponds to
401 // query_rect. Point is a multiple of cell_size.
402 min_point_
= gfx::Point(
403 RoundDown(query_rect
.x(), cell_size
.width()),
404 RoundDown(query_rect
.y(), cell_size
.height()));
405 max_point_
= gfx::Point(
406 RoundDown(query_rect
.right() - 1, cell_size
.width()),
407 RoundDown(query_rect
.bottom() - 1, cell_size
.height()));
409 // Limit the points to known pixel ref boundaries.
410 min_point_
= gfx::Point(
411 std::max(min_point_
.x(), picture
->min_pixel_cell_
.x()),
412 std::max(min_point_
.y(), picture
->min_pixel_cell_
.y()));
413 max_point_
= gfx::Point(
414 std::min(max_point_
.x(), picture
->max_pixel_cell_
.x()),
415 std::min(max_point_
.y(), picture
->max_pixel_cell_
.y()));
417 // Make the current x be cell_size.width() less than min point, so that
418 // the first increment will point at min_point_.
419 current_x_
= min_point_
.x() - cell_size
.width();
420 current_y_
= min_point_
.y();
421 if (current_y_
<= max_point_
.y())
425 Picture::PixelRefIterator::~PixelRefIterator() {
428 Picture::PixelRefIterator
& Picture::PixelRefIterator::operator++() {
430 // If we're not at the end of the list, then we have the next item.
431 if (current_index_
< current_pixel_refs_
->size())
434 DCHECK(current_y_
<= max_point_
.y());
436 gfx::Size cell_size
= picture_
->cell_size_
;
438 // Advance the current grid cell.
439 current_x_
+= cell_size
.width();
440 if (current_x_
> max_point_
.x()) {
441 current_y_
+= cell_size
.height();
442 current_x_
= min_point_
.x();
443 if (current_y_
> max_point_
.y()) {
444 current_pixel_refs_
= empty_pixel_refs_
.Pointer();
450 // If there are no pixel refs at this grid cell, keep incrementing.
451 PixelRefMapKey
key(current_x_
, current_y_
);
452 PixelRefMap::const_iterator iter
= picture_
->pixel_refs_
.find(key
);
453 if (iter
== picture_
->pixel_refs_
.end())
456 // We found a non-empty list: store it and get the first pixel ref.
457 current_pixel_refs_
= &iter
->second
;
464 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
465 Picture::AsTraceableRasterData(float scale
) const {
466 scoped_refptr
<base::trace_event::TracedValue
> raster_data
=
467 new base::trace_event::TracedValue();
468 TracedValue::SetIDRef(this, raster_data
.get(), "picture_id");
469 raster_data
->SetDouble("scale", scale
);
473 scoped_refptr
<base::trace_event::ConvertableToTraceFormat
>
474 Picture::AsTraceableRecordData() const {
475 scoped_refptr
<base::trace_event::TracedValue
> record_data
=
476 new base::trace_event::TracedValue();
477 TracedValue::SetIDRef(this, record_data
.get(), "picture_id");
478 MathUtil::AddToTracedValue("layer_rect", layer_rect_
, record_data
.get());