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/debug/trace_event.h"
13 #include "base/debug/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/traced_picture.h"
18 #include "cc/debug/traced_value.h"
19 #include "cc/layers/content_layer_client.h"
20 #include "skia/ext/pixel_ref_utils.h"
21 #include "third_party/skia/include/core/SkCanvas.h"
22 #include "third_party/skia/include/core/SkData.h"
23 #include "third_party/skia/include/core/SkDrawFilter.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/rect_conversions.h"
32 #include "ui/gfx/skia_util.h"
38 SkData
* EncodeBitmap(size_t* offset
, const SkBitmap
& bm
) {
39 const int kJpegQuality
= 80;
40 std::vector
<unsigned char> data
;
42 // If bitmap is opaque, encode as JPEG.
43 // Otherwise encode as PNG.
44 bool encoding_succeeded
= false;
46 SkAutoLockPixels
lock_bitmap(bm
);
50 encoding_succeeded
= gfx::JPEGCodec::Encode(
51 reinterpret_cast<unsigned char*>(bm
.getAddr32(0, 0)),
52 gfx::JPEGCodec::FORMAT_SkBitmap
,
59 encoding_succeeded
= gfx::PNGCodec::EncodeBGRASkBitmap(bm
, false, &data
);
62 if (encoding_succeeded
) {
64 return SkData::NewWithCopy(&data
.front(), data
.size());
69 bool DecodeBitmap(const void* buffer
, size_t size
, SkBitmap
* bm
) {
70 const unsigned char* data
= static_cast<const unsigned char *>(buffer
);
73 if (gfx::PNGCodec::Decode(data
, size
, bm
))
77 scoped_ptr
<SkBitmap
> decoded_jpeg(gfx::JPEGCodec::Decode(data
, size
));
87 scoped_refptr
<Picture
> Picture::Create(
88 const gfx::Rect
& layer_rect
,
89 ContentLayerClient
* client
,
90 const SkTileGridFactory::TileGridInfo
& tile_grid_info
,
91 bool gather_pixel_refs
,
92 RecordingMode recording_mode
) {
93 scoped_refptr
<Picture
> picture
= make_scoped_refptr(new Picture(layer_rect
));
95 picture
->Record(client
, tile_grid_info
, recording_mode
);
96 if (gather_pixel_refs
)
97 picture
->GatherPixelRefs(tile_grid_info
);
102 Picture::Picture(const gfx::Rect
& layer_rect
)
103 : layer_rect_(layer_rect
),
104 cell_size_(layer_rect
.size()) {
105 // Instead of recording a trace event for object creation here, we wait for
106 // the picture to be recorded in Picture::Record.
109 scoped_refptr
<Picture
> Picture::CreateFromSkpValue(const base::Value
* value
) {
110 // Decode the picture from base64.
112 if (!value
->GetAsString(&encoded
))
116 base::Base64Decode(encoded
, &decoded
);
117 SkMemoryStream
stream(decoded
.data(), decoded
.size());
119 // Read the picture. This creates an empty picture on failure.
120 SkPicture
* skpicture
= SkPicture::CreateFromStream(&stream
, &DecodeBitmap
);
121 if (skpicture
== NULL
)
124 gfx::Rect
layer_rect(skpicture
->width(), skpicture
->height());
125 gfx::Rect
opaque_rect(skpicture
->width(), skpicture
->height());
127 return make_scoped_refptr(new Picture(skpicture
, layer_rect
, opaque_rect
));
130 scoped_refptr
<Picture
> Picture::CreateFromValue(const base::Value
* raw_value
) {
131 const base::DictionaryValue
* value
= NULL
;
132 if (!raw_value
->GetAsDictionary(&value
))
135 // Decode the picture from base64.
137 if (!value
->GetString("skp64", &encoded
))
141 base::Base64Decode(encoded
, &decoded
);
142 SkMemoryStream
stream(decoded
.data(), decoded
.size());
144 const base::Value
* layer_rect_value
= NULL
;
145 if (!value
->Get("params.layer_rect", &layer_rect_value
))
148 gfx::Rect layer_rect
;
149 if (!MathUtil::FromValue(layer_rect_value
, &layer_rect
))
152 const base::Value
* opaque_rect_value
= NULL
;
153 if (!value
->Get("params.opaque_rect", &opaque_rect_value
))
156 gfx::Rect opaque_rect
;
157 if (!MathUtil::FromValue(opaque_rect_value
, &opaque_rect
))
160 // Read the picture. This creates an empty picture on failure.
161 SkPicture
* skpicture
= SkPicture::CreateFromStream(&stream
, &DecodeBitmap
);
162 if (skpicture
== NULL
)
165 return make_scoped_refptr(new Picture(skpicture
, layer_rect
, opaque_rect
));
168 Picture::Picture(SkPicture
* picture
,
169 const gfx::Rect
& layer_rect
,
170 const gfx::Rect
& opaque_rect
) :
171 layer_rect_(layer_rect
),
172 opaque_rect_(opaque_rect
),
173 picture_(skia::AdoptRef(picture
)),
174 cell_size_(layer_rect
.size()) {
177 Picture::Picture(const skia::RefPtr
<SkPicture
>& picture
,
178 const gfx::Rect
& layer_rect
,
179 const gfx::Rect
& opaque_rect
,
180 const PixelRefMap
& pixel_refs
) :
181 layer_rect_(layer_rect
),
182 opaque_rect_(opaque_rect
),
184 pixel_refs_(pixel_refs
),
185 cell_size_(layer_rect
.size()) {
188 Picture::~Picture() {
189 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
190 TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this);
193 bool Picture::IsSuitableForGpuRasterization() const {
196 // TODO(alokp): SkPicture::suitableForGpuRasterization needs a GrContext.
197 // Ideally this GrContext should be the same as that for rasterizing this
198 // picture. But we are on the main thread while the rasterization context
199 // may be on the compositor or raster thread.
200 // SkPicture::suitableForGpuRasterization is not implemented yet.
201 // Pass a NULL context for now and discuss with skia folks if the context
203 return picture_
->suitableForGpuRasterization(NULL
);
206 int Picture::ApproximateOpCount() const {
208 return picture_
->approximateOpCount();
211 bool Picture::HasText() const {
213 return picture_
->hasText();
216 void Picture::Record(ContentLayerClient
* painter
,
217 const SkTileGridFactory::TileGridInfo
& tile_grid_info
,
218 RecordingMode recording_mode
) {
222 AsTraceableRecordData(),
227 DCHECK(!tile_grid_info
.fTileInterval
.isEmpty());
229 SkTileGridFactory
factory(tile_grid_info
);
230 SkPictureRecorder recorder
;
232 scoped_ptr
<EXPERIMENTAL::SkRecording
> recording
;
234 skia::RefPtr
<SkCanvas
> canvas
;
235 canvas
= skia::SharePtr(recorder
.beginRecording(
236 layer_rect_
.width(), layer_rect_
.height(), &factory
));
238 ContentLayerClient::GraphicsContextStatus graphics_context_status
=
239 ContentLayerClient::GRAPHICS_CONTEXT_ENABLED
;
241 switch (recording_mode
) {
242 case RECORD_NORMALLY
:
243 // Already setup for normal recording.
245 case RECORD_WITH_SK_NULL_CANVAS
:
246 canvas
= skia::AdoptRef(SkCreateNullCanvas());
248 case RECORD_WITH_PAINTING_DISABLED
:
249 // We pass a disable flag through the paint calls when perfromance
250 // testing (the only time this case should ever arise) when we want to
251 // prevent the Blink GraphicsContext object from consuming any compute
253 canvas
= skia::AdoptRef(SkCreateNullCanvas());
254 graphics_context_status
= ContentLayerClient::GRAPHICS_CONTEXT_DISABLED
;
256 case RECORD_WITH_SKRECORD
:
257 recording
.reset(new EXPERIMENTAL::SkRecording(layer_rect_
.width(),
258 layer_rect_
.height()));
259 canvas
= skia::SharePtr(recording
->canvas());
266 canvas
->translate(SkFloatToScalar(-layer_rect_
.x()),
267 SkFloatToScalar(-layer_rect_
.y()));
269 SkRect layer_skrect
= SkRect::MakeXYWH(layer_rect_
.x(),
272 layer_rect_
.height());
273 canvas
->clipRect(layer_skrect
);
275 gfx::RectF opaque_layer_rect
;
276 painter
->PaintContents(
277 canvas
.get(), layer_rect_
, &opaque_layer_rect
, graphics_context_status
);
280 picture_
= skia::AdoptRef(recorder
.endRecording());
284 // SkRecording requires it's the only one holding onto canvas before we
285 // may call releasePlayback(). (This helps enforce thread-safety.)
287 playback_
.reset(recording
->releasePlayback());
290 opaque_rect_
= gfx::ToEnclosedRect(opaque_layer_rect
);
295 void Picture::GatherPixelRefs(
296 const SkTileGridFactory::TileGridInfo
& tile_grid_info
) {
297 TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
298 "width", layer_rect_
.width(),
299 "height", layer_rect_
.height());
302 DCHECK(pixel_refs_
.empty());
303 if (!WillPlayBackBitmaps())
305 cell_size_
= gfx::Size(
306 tile_grid_info
.fTileInterval
.width() +
307 2 * tile_grid_info
.fMargin
.width(),
308 tile_grid_info
.fTileInterval
.height() +
309 2 * tile_grid_info
.fMargin
.height());
310 DCHECK_GT(cell_size_
.width(), 0);
311 DCHECK_GT(cell_size_
.height(), 0);
313 int min_x
= std::numeric_limits
<int>::max();
314 int min_y
= std::numeric_limits
<int>::max();
318 skia::DiscardablePixelRefList pixel_refs
;
319 skia::PixelRefUtils::GatherDiscardablePixelRefs(picture_
.get(), &pixel_refs
);
320 for (skia::DiscardablePixelRefList::const_iterator it
= pixel_refs
.begin();
321 it
!= pixel_refs
.end();
324 RoundDown(static_cast<int>(it
->pixel_ref_rect
.x()),
326 RoundDown(static_cast<int>(it
->pixel_ref_rect
.y()),
327 cell_size_
.height()));
329 RoundDown(static_cast<int>(std::ceil(it
->pixel_ref_rect
.right())),
331 RoundDown(static_cast<int>(std::ceil(it
->pixel_ref_rect
.bottom())),
332 cell_size_
.height()));
334 for (int y
= min
.y(); y
<= max
.y(); y
+= cell_size_
.height()) {
335 for (int x
= min
.x(); x
<= max
.x(); x
+= cell_size_
.width()) {
336 PixelRefMapKey
key(x
, y
);
337 pixel_refs_
[key
].push_back(it
->pixel_ref
);
341 min_x
= std::min(min_x
, min
.x());
342 min_y
= std::min(min_y
, min
.y());
343 max_x
= std::max(max_x
, max
.x());
344 max_y
= std::max(max_y
, max
.y());
347 min_pixel_cell_
= gfx::Point(min_x
, min_y
);
348 max_pixel_cell_
= gfx::Point(max_x
, max_y
);
351 int Picture::Raster(SkCanvas
* canvas
,
352 SkDrawPictureCallback
* callback
,
353 const Region
& negated_content_region
,
354 float contents_scale
) const {
359 AsTraceableRasterData(contents_scale
));
365 for (Region::Iterator
it(negated_content_region
); it
.has_rect(); it
.next())
366 canvas
->clipRect(gfx::RectToSkRect(it
.rect()), SkRegion::kDifference_Op
);
368 canvas
->scale(contents_scale
, contents_scale
);
369 canvas
->translate(layer_rect_
.x(), layer_rect_
.y());
371 playback_
->draw(canvas
);
373 picture_
->draw(canvas
, callback
);
376 canvas
->getClipDeviceBounds(&bounds
);
379 "cc", "Picture::Raster",
380 "num_pixels_rasterized", bounds
.width() * bounds
.height());
381 return bounds
.width() * bounds
.height();
384 void Picture::Replay(SkCanvas
* canvas
) {
385 TRACE_EVENT_BEGIN0("cc", "Picture::Replay");
389 playback_
->draw(canvas
);
391 picture_
->draw(canvas
);
394 canvas
->getClipDeviceBounds(&bounds
);
395 TRACE_EVENT_END1("cc", "Picture::Replay",
396 "num_pixels_replayed", bounds
.width() * bounds
.height());
399 scoped_ptr
<base::Value
> Picture::AsValue() const {
400 SkDynamicMemoryWStream stream
;
403 // SkPlayback can't serialize itself, so re-record into an SkPicture.
404 SkPictureRecorder recorder
;
405 skia::RefPtr
<SkCanvas
> canvas(skia::SharePtr(recorder
.beginRecording(
407 layer_rect_
.height(),
408 NULL
))); // Default (no) bounding-box hierarchy is fastest.
409 playback_
->draw(canvas
.get());
410 skia::RefPtr
<SkPicture
> picture(skia::AdoptRef(recorder
.endRecording()));
411 picture
->serialize(&stream
, &EncodeBitmap
);
413 // Serialize the picture.
414 picture_
->serialize(&stream
, &EncodeBitmap
);
417 // Encode the picture as base64.
418 scoped_ptr
<base::DictionaryValue
> res(new base::DictionaryValue());
419 res
->Set("params.layer_rect", MathUtil::AsValue(layer_rect_
).release());
420 res
->Set("params.opaque_rect", MathUtil::AsValue(opaque_rect_
).release());
422 size_t serialized_size
= stream
.bytesWritten();
423 scoped_ptr
<char[]> serialized_picture(new char[serialized_size
]);
424 stream
.copyTo(serialized_picture
.get());
425 std::string b64_picture
;
426 base::Base64Encode(std::string(serialized_picture
.get(), serialized_size
),
428 res
->SetString("skp64", b64_picture
);
429 return res
.PassAs
<base::Value
>();
432 void Picture::EmitTraceSnapshot() const {
433 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
434 TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
435 "devtools.timeline.picture"),
438 TracedPicture::AsTraceablePicture(this));
441 void Picture::EmitTraceSnapshotAlias(Picture
* original
) const {
442 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
443 TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
444 "devtools.timeline.picture"),
447 TracedPicture::AsTraceablePictureAlias(original
));
450 base::LazyInstance
<Picture::PixelRefs
>
451 Picture::PixelRefIterator::empty_pixel_refs_
;
453 Picture::PixelRefIterator::PixelRefIterator()
455 current_pixel_refs_(empty_pixel_refs_
.Pointer()),
463 Picture::PixelRefIterator::PixelRefIterator(
464 const gfx::Rect
& rect
,
465 const Picture
* picture
)
467 current_pixel_refs_(empty_pixel_refs_
.Pointer()),
469 gfx::Rect layer_rect
= picture
->layer_rect_
;
470 gfx::Size cell_size
= picture
->cell_size_
;
471 DCHECK(!cell_size
.IsEmpty());
473 gfx::Rect
query_rect(rect
);
474 // Early out if the query rect doesn't intersect this picture.
475 if (!query_rect
.Intersects(layer_rect
)) {
476 min_point_
= gfx::Point(0, 0);
477 max_point_
= gfx::Point(0, 0);
483 // First, subtract the layer origin as cells are stored in layer space.
484 query_rect
.Offset(-layer_rect
.OffsetFromOrigin());
486 // We have to find a cell_size aligned point that corresponds to
487 // query_rect. Point is a multiple of cell_size.
488 min_point_
= gfx::Point(
489 RoundDown(query_rect
.x(), cell_size
.width()),
490 RoundDown(query_rect
.y(), cell_size
.height()));
491 max_point_
= gfx::Point(
492 RoundDown(query_rect
.right() - 1, cell_size
.width()),
493 RoundDown(query_rect
.bottom() - 1, cell_size
.height()));
495 // Limit the points to known pixel ref boundaries.
496 min_point_
= gfx::Point(
497 std::max(min_point_
.x(), picture
->min_pixel_cell_
.x()),
498 std::max(min_point_
.y(), picture
->min_pixel_cell_
.y()));
499 max_point_
= gfx::Point(
500 std::min(max_point_
.x(), picture
->max_pixel_cell_
.x()),
501 std::min(max_point_
.y(), picture
->max_pixel_cell_
.y()));
503 // Make the current x be cell_size.width() less than min point, so that
504 // the first increment will point at min_point_.
505 current_x_
= min_point_
.x() - cell_size
.width();
506 current_y_
= min_point_
.y();
507 if (current_y_
<= max_point_
.y())
511 Picture::PixelRefIterator::~PixelRefIterator() {
514 Picture::PixelRefIterator
& Picture::PixelRefIterator::operator++() {
516 // If we're not at the end of the list, then we have the next item.
517 if (current_index_
< current_pixel_refs_
->size())
520 DCHECK(current_y_
<= max_point_
.y());
522 gfx::Size cell_size
= picture_
->cell_size_
;
524 // Advance the current grid cell.
525 current_x_
+= cell_size
.width();
526 if (current_x_
> max_point_
.x()) {
527 current_y_
+= cell_size
.height();
528 current_x_
= min_point_
.x();
529 if (current_y_
> max_point_
.y()) {
530 current_pixel_refs_
= empty_pixel_refs_
.Pointer();
536 // If there are no pixel refs at this grid cell, keep incrementing.
537 PixelRefMapKey
key(current_x_
, current_y_
);
538 PixelRefMap::const_iterator iter
= picture_
->pixel_refs_
.find(key
);
539 if (iter
== picture_
->pixel_refs_
.end())
542 // We found a non-empty list: store it and get the first pixel ref.
543 current_pixel_refs_
= &iter
->second
;
550 scoped_refptr
<base::debug::ConvertableToTraceFormat
>
551 Picture::AsTraceableRasterData(float scale
) const {
552 scoped_refptr
<base::debug::TracedValue
> raster_data
=
553 new base::debug::TracedValue();
554 TracedValue::SetIDRef(this, raster_data
.get(), "picture_id");
555 raster_data
->SetDouble("scale", scale
);
559 scoped_refptr
<base::debug::ConvertableToTraceFormat
>
560 Picture::AsTraceableRecordData() const {
561 scoped_refptr
<base::debug::TracedValue
> record_data
=
562 new base::debug::TracedValue();
563 TracedValue::SetIDRef(this, record_data
.get(), "picture_id");
564 record_data
->BeginArray("layer_rect");
565 MathUtil::AddToTracedValue(layer_rect_
, record_data
.get());
566 record_data
->EndArray();