Enable right clicking on the applist doodle web contents and log the data.
[chromium-blink-merge.git] / cc / resources / picture.cc
blobcf5e59b0bababbb0eb557f5e5a1967b6329b9040
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"
7 #include <algorithm>
8 #include <limits>
9 #include <set>
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"
34 namespace cc {
36 namespace {
38 bool DecodeBitmap(const void* buffer, size_t size, SkBitmap* bm) {
39 const unsigned char* data = static_cast<const unsigned char *>(buffer);
41 // Try PNG first.
42 if (gfx::PNGCodec::Decode(data, size, bm))
43 return true;
45 // Try JPEG.
46 scoped_ptr<SkBitmap> decoded_jpeg(gfx::JPEGCodec::Decode(data, size));
47 if (decoded_jpeg) {
48 *bm = *decoded_jpeg;
49 return true;
51 return false;
54 } // namespace
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);
68 return picture;
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.
80 std::string encoded;
81 if (!value->GetAsString(&encoded))
82 return NULL;
84 std::string decoded;
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)
91 return 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))
100 return NULL;
102 // Decode the picture from base64.
103 std::string encoded;
104 if (!value->GetString("skp64", &encoded))
105 return NULL;
107 std::string decoded;
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))
113 return NULL;
115 gfx::Rect layer_rect;
116 if (!MathUtil::FromValue(layer_rect_value, &layer_rect))
117 return NULL;
119 // Read the picture. This creates an empty picture on failure.
120 SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
121 if (skpicture == NULL)
122 return 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),
137 picture_(picture),
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 {
148 DCHECK(picture_);
150 // TODO(hendrikw): SkPicture::suitableForGpuRasterization takes a GrContext.
151 // Currently the GrContext isn't used, and should probably be removed from
152 // skia.
153 return picture_->suitableForGpuRasterization(nullptr, reason);
156 int Picture::ApproximateOpCount() const {
157 DCHECK(picture_);
158 return picture_->approximateOpCount();
161 size_t Picture::ApproximateMemoryUsage() const {
162 DCHECK(picture_);
163 return SkPictureUtils::ApproximateBytesUsed(picture_.get());
166 bool Picture::HasText() const {
167 DCHECK(picture_);
168 return picture_->hasText();
171 void Picture::Record(ContentLayerClient* painter,
172 const gfx::Size& tile_grid_size,
173 RecordingSource::RecordingMode recording_mode) {
174 TRACE_EVENT2("cc",
175 "Picture::Record",
176 "data",
177 AsTraceableRecordData(),
178 "recording_mode",
179 recording_mode);
181 DCHECK(!picture_);
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.
199 break;
200 case RecordingSource::RECORD_WITH_SK_NULL_CANVAS:
201 canvas = skia::AdoptRef(SkCreateNullCanvas());
202 break;
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
207 // time.
208 canvas = skia::AdoptRef(SkCreateNullCanvas());
209 painting_control = ContentLayerClient::DISPLAY_LIST_CONSTRUCTION_DISABLED;
210 break;
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;
214 break;
215 default:
216 NOTREACHED();
219 canvas->save();
220 canvas->translate(SkFloatToScalar(-layer_rect_.x()),
221 SkFloatToScalar(-layer_rect_.y()));
223 SkRect layer_skrect = SkRect::MakeXYWH(layer_rect_.x(),
224 layer_rect_.y(),
225 layer_rect_.width(),
226 layer_rect_.height());
227 canvas->clipRect(layer_skrect);
229 painter->PaintContents(canvas.get(), layer_rect_, painting_control);
231 canvas->restore();
232 picture_ = skia::AdoptRef(recorder.endRecording());
233 DCHECK(picture_);
235 EmitTraceSnapshot();
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());
243 DCHECK(picture_);
244 DCHECK(pixel_refs_.empty());
245 if (!WillPlayBackBitmaps())
246 return;
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();
253 int max_x = 0;
254 int max_y = 0;
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();
260 ++it) {
261 gfx::Point min(
262 RoundDown(static_cast<int>(it->pixel_ref_rect.x()),
263 cell_size_.width()),
264 RoundDown(static_cast<int>(it->pixel_ref_rect.y()),
265 cell_size_.height()));
266 gfx::Point max(
267 RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.right())),
268 cell_size_.width()),
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 {
293 TRACE_EVENT_BEGIN1(
294 "cc",
295 "Picture::Raster",
296 "data",
297 AsTraceableRasterData(contents_scale));
299 DCHECK(picture_);
301 canvas->save();
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());
308 if (callback) {
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);
312 } else {
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());
317 SkIRect bounds;
318 canvas->getClipDeviceBounds(&bounds);
319 canvas->restore();
320 TRACE_EVENT_END1(
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");
328 DCHECK(picture_);
329 picture_->playback(canvas);
330 SkIRect bounds;
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);
343 return res.Pass();
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"),
350 "cc::Picture",
351 this,
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"),
359 "cc::Picture",
360 this,
361 TracedPicture::AsTraceablePictureAlias(original));
364 base::LazyInstance<Picture::PixelRefs>
365 Picture::PixelRefIterator::empty_pixel_refs_;
367 Picture::PixelRefIterator::PixelRefIterator()
368 : picture_(NULL),
369 current_pixel_refs_(empty_pixel_refs_.Pointer()),
370 current_index_(0),
371 min_point_(-1, -1),
372 max_point_(-1, -1),
373 current_x_(0),
374 current_y_(0) {
377 Picture::PixelRefIterator::PixelRefIterator(
378 const gfx::Rect& rect,
379 const Picture* picture)
380 : picture_(picture),
381 current_pixel_refs_(empty_pixel_refs_.Pointer()),
382 current_index_(0) {
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);
392 current_x_ = 1;
393 current_y_ = 1;
394 return;
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())
422 ++(*this);
425 Picture::PixelRefIterator::~PixelRefIterator() {
428 Picture::PixelRefIterator& Picture::PixelRefIterator::operator++() {
429 ++current_index_;
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())
432 return *this;
434 DCHECK(current_y_ <= max_point_.y());
435 while (true) {
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();
445 current_index_ = 0;
446 break;
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())
454 continue;
456 // We found a non-empty list: store it and get the first pixel ref.
457 current_pixel_refs_ = &iter->second;
458 current_index_ = 0;
459 break;
461 return *this;
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);
470 return raster_data;
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());
479 return record_data;
482 } // namespace cc