blob: 6530866d6a03dce9a4d6374a53693fe70f73cdaa [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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.
4
5#include "cc/resources/picture.h"
6
7#include <algorithm>
8#include <limits>
9#include <set>
10
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"
James Robinson30d547e2014-10-23 18:20:06 -070021#include "third_party/skia/include/core/SkBBHFactory.h"
James Robinson646469d2014-10-03 15:33:28 -070022#include "third_party/skia/include/core/SkCanvas.h"
23#include "third_party/skia/include/core/SkData.h"
James Robinson646469d2014-10-03 15:33:28 -070024#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"
James Robinson6a64b812014-12-03 13:38:42 -080028#include "third_party/skia/include/utils/SkPictureUtils.h"
James Robinson646469d2014-10-03 15:33:28 -070029#include "ui/gfx/codec/jpeg_codec.h"
30#include "ui/gfx/codec/png_codec.h"
James Robinson30d547e2014-10-23 18:20:06 -070031#include "ui/gfx/geometry/rect_conversions.h"
James Robinson646469d2014-10-03 15:33:28 -070032#include "ui/gfx/skia_util.h"
33
34namespace cc {
35
36namespace {
37
38SkData* EncodeBitmap(size_t* offset, const SkBitmap& bm) {
39 const int kJpegQuality = 80;
40 std::vector<unsigned char> data;
41
42 // If bitmap is opaque, encode as JPEG.
43 // Otherwise encode as PNG.
44 bool encoding_succeeded = false;
45 if (bm.isOpaque()) {
46 SkAutoLockPixels lock_bitmap(bm);
47 if (bm.empty())
48 return NULL;
49
50 encoding_succeeded = gfx::JPEGCodec::Encode(
51 reinterpret_cast<unsigned char*>(bm.getAddr32(0, 0)),
52 gfx::JPEGCodec::FORMAT_SkBitmap,
53 bm.width(),
54 bm.height(),
55 bm.rowBytes(),
56 kJpegQuality,
57 &data);
58 } else {
59 encoding_succeeded = gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &data);
60 }
61
62 if (encoding_succeeded) {
63 *offset = 0;
64 return SkData::NewWithCopy(&data.front(), data.size());
65 }
66 return NULL;
67}
68
69bool DecodeBitmap(const void* buffer, size_t size, SkBitmap* bm) {
70 const unsigned char* data = static_cast<const unsigned char *>(buffer);
71
72 // Try PNG first.
73 if (gfx::PNGCodec::Decode(data, size, bm))
74 return true;
75
76 // Try JPEG.
77 scoped_ptr<SkBitmap> decoded_jpeg(gfx::JPEGCodec::Decode(data, size));
78 if (decoded_jpeg) {
79 *bm = *decoded_jpeg;
80 return true;
81 }
82 return false;
83}
84
85} // namespace
86
87scoped_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));
94
95 picture->Record(client, tile_grid_info, recording_mode);
96 if (gather_pixel_refs)
97 picture->GatherPixelRefs(tile_grid_info);
98
99 return picture;
100}
101
102Picture::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.
107}
108
109scoped_refptr<Picture> Picture::CreateFromSkpValue(const base::Value* value) {
110 // Decode the picture from base64.
111 std::string encoded;
112 if (!value->GetAsString(&encoded))
113 return NULL;
114
115 std::string decoded;
116 base::Base64Decode(encoded, &decoded);
117 SkMemoryStream stream(decoded.data(), decoded.size());
118
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;
123
124 gfx::Rect layer_rect(skpicture->width(), skpicture->height());
125 return make_scoped_refptr(new Picture(skpicture, layer_rect));
126}
127
128scoped_refptr<Picture> Picture::CreateFromValue(const base::Value* raw_value) {
129 const base::DictionaryValue* value = NULL;
130 if (!raw_value->GetAsDictionary(&value))
131 return NULL;
132
133 // Decode the picture from base64.
134 std::string encoded;
135 if (!value->GetString("skp64", &encoded))
136 return NULL;
137
138 std::string decoded;
139 base::Base64Decode(encoded, &decoded);
140 SkMemoryStream stream(decoded.data(), decoded.size());
141
142 const base::Value* layer_rect_value = NULL;
143 if (!value->Get("params.layer_rect", &layer_rect_value))
144 return NULL;
145
146 gfx::Rect layer_rect;
147 if (!MathUtil::FromValue(layer_rect_value, &layer_rect))
148 return NULL;
149
150 // Read the picture. This creates an empty picture on failure.
151 SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
152 if (skpicture == NULL)
153 return NULL;
154
155 return make_scoped_refptr(new Picture(skpicture, layer_rect));
156}
157
158Picture::Picture(SkPicture* picture, const gfx::Rect& layer_rect)
159 : layer_rect_(layer_rect),
160 picture_(skia::AdoptRef(picture)),
161 cell_size_(layer_rect.size()) {
162}
163
164Picture::Picture(const skia::RefPtr<SkPicture>& picture,
165 const gfx::Rect& layer_rect,
166 const PixelRefMap& pixel_refs) :
167 layer_rect_(layer_rect),
168 picture_(picture),
169 pixel_refs_(pixel_refs),
170 cell_size_(layer_rect.size()) {
171}
172
173Picture::~Picture() {
174 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
175 TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this);
176}
177
James Robinsonb96d28f2014-12-03 16:55:21 -0800178bool Picture::IsSuitableForGpuRasterization(const char** reason) const {
James Robinson646469d2014-10-03 15:33:28 -0700179 DCHECK(picture_);
180
James Robinsonb96d28f2014-12-03 16:55:21 -0800181 // TODO(hendrikw): SkPicture::suitableForGpuRasterization takes a GrContext.
182 // Currently the GrContext isn't used, and should probably be removed from
183 // skia.
184 return picture_->suitableForGpuRasterization(nullptr, reason);
James Robinson646469d2014-10-03 15:33:28 -0700185}
186
187int Picture::ApproximateOpCount() const {
188 DCHECK(picture_);
189 return picture_->approximateOpCount();
190}
191
James Robinson6a64b812014-12-03 13:38:42 -0800192size_t Picture::ApproximateMemoryUsage() const {
193 DCHECK(picture_);
194 return SkPictureUtils::ApproximateBytesUsed(picture_.get());
195}
196
James Robinson646469d2014-10-03 15:33:28 -0700197bool Picture::HasText() const {
198 DCHECK(picture_);
199 return picture_->hasText();
200}
201
202void Picture::Record(ContentLayerClient* painter,
203 const SkTileGridFactory::TileGridInfo& tile_grid_info,
204 RecordingMode recording_mode) {
205 TRACE_EVENT2("cc",
206 "Picture::Record",
207 "data",
208 AsTraceableRecordData(),
209 "recording_mode",
210 recording_mode);
211
212 DCHECK(!picture_);
213 DCHECK(!tile_grid_info.fTileInterval.isEmpty());
214
James Robinson3f862972014-11-18 16:50:38 -0800215 // TODO(mtklein): If SkRTree sticks, clean up tile_grid_info. skbug.com/3085
216 SkRTreeFactory factory;
James Robinson646469d2014-10-03 15:33:28 -0700217 SkPictureRecorder recorder;
218
James Robinson646469d2014-10-03 15:33:28 -0700219 skia::RefPtr<SkCanvas> canvas;
220 canvas = skia::SharePtr(recorder.beginRecording(
James Robinson3f862972014-11-18 16:50:38 -0800221 layer_rect_.width(), layer_rect_.height(), &factory,
222 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag));
James Robinson646469d2014-10-03 15:33:28 -0700223
224 ContentLayerClient::GraphicsContextStatus graphics_context_status =
225 ContentLayerClient::GRAPHICS_CONTEXT_ENABLED;
226
227 switch (recording_mode) {
228 case RECORD_NORMALLY:
229 // Already setup for normal recording.
230 break;
231 case RECORD_WITH_SK_NULL_CANVAS:
232 canvas = skia::AdoptRef(SkCreateNullCanvas());
233 break;
234 case RECORD_WITH_PAINTING_DISABLED:
235 // We pass a disable flag through the paint calls when perfromance
236 // testing (the only time this case should ever arise) when we want to
237 // prevent the Blink GraphicsContext object from consuming any compute
238 // time.
239 canvas = skia::AdoptRef(SkCreateNullCanvas());
240 graphics_context_status = ContentLayerClient::GRAPHICS_CONTEXT_DISABLED;
241 break;
James Robinson646469d2014-10-03 15:33:28 -0700242 default:
243 NOTREACHED();
244 }
245
246 canvas->save();
247 canvas->translate(SkFloatToScalar(-layer_rect_.x()),
248 SkFloatToScalar(-layer_rect_.y()));
249
250 SkRect layer_skrect = SkRect::MakeXYWH(layer_rect_.x(),
251 layer_rect_.y(),
252 layer_rect_.width(),
253 layer_rect_.height());
254 canvas->clipRect(layer_skrect);
255
256 painter->PaintContents(canvas.get(), layer_rect_, graphics_context_status);
257
258 canvas->restore();
259 picture_ = skia::AdoptRef(recorder.endRecording());
260 DCHECK(picture_);
261
James Robinson646469d2014-10-03 15:33:28 -0700262 EmitTraceSnapshot();
263}
264
265void Picture::GatherPixelRefs(
266 const SkTileGridFactory::TileGridInfo& tile_grid_info) {
267 TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
268 "width", layer_rect_.width(),
269 "height", layer_rect_.height());
270
271 DCHECK(picture_);
272 DCHECK(pixel_refs_.empty());
273 if (!WillPlayBackBitmaps())
274 return;
275 cell_size_ = gfx::Size(
276 tile_grid_info.fTileInterval.width() +
277 2 * tile_grid_info.fMargin.width(),
278 tile_grid_info.fTileInterval.height() +
279 2 * tile_grid_info.fMargin.height());
280 DCHECK_GT(cell_size_.width(), 0);
281 DCHECK_GT(cell_size_.height(), 0);
282
283 int min_x = std::numeric_limits<int>::max();
284 int min_y = std::numeric_limits<int>::max();
285 int max_x = 0;
286 int max_y = 0;
287
288 skia::DiscardablePixelRefList pixel_refs;
289 skia::PixelRefUtils::GatherDiscardablePixelRefs(picture_.get(), &pixel_refs);
290 for (skia::DiscardablePixelRefList::const_iterator it = pixel_refs.begin();
291 it != pixel_refs.end();
292 ++it) {
293 gfx::Point min(
294 RoundDown(static_cast<int>(it->pixel_ref_rect.x()),
295 cell_size_.width()),
296 RoundDown(static_cast<int>(it->pixel_ref_rect.y()),
297 cell_size_.height()));
298 gfx::Point max(
299 RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.right())),
300 cell_size_.width()),
301 RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.bottom())),
302 cell_size_.height()));
303
304 for (int y = min.y(); y <= max.y(); y += cell_size_.height()) {
305 for (int x = min.x(); x <= max.x(); x += cell_size_.width()) {
306 PixelRefMapKey key(x, y);
307 pixel_refs_[key].push_back(it->pixel_ref);
308 }
309 }
310
311 min_x = std::min(min_x, min.x());
312 min_y = std::min(min_y, min.y());
313 max_x = std::max(max_x, max.x());
314 max_y = std::max(max_y, max.y());
315 }
316
317 min_pixel_cell_ = gfx::Point(min_x, min_y);
318 max_pixel_cell_ = gfx::Point(max_x, max_y);
319}
320
321int Picture::Raster(SkCanvas* canvas,
322 SkDrawPictureCallback* callback,
323 const Region& negated_content_region,
324 float contents_scale) const {
325 TRACE_EVENT_BEGIN1(
326 "cc",
327 "Picture::Raster",
328 "data",
329 AsTraceableRasterData(contents_scale));
330
331 DCHECK(picture_);
332
333 canvas->save();
334
335 for (Region::Iterator it(negated_content_region); it.has_rect(); it.next())
336 canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
337
338 canvas->scale(contents_scale, contents_scale);
339 canvas->translate(layer_rect_.x(), layer_rect_.y());
James Robinsond4531882014-10-17 16:14:32 -0700340 if (callback) {
James Robinson646469d2014-10-03 15:33:28 -0700341 // If we have a callback, we need to call |draw()|, |drawPicture()| doesn't
342 // take a callback. This is used by |AnalysisCanvas| to early out.
343 picture_->draw(canvas, callback);
344 } else {
345 // Prefer to call |drawPicture()| on the canvas since it could place the
346 // entire picture on the canvas instead of parsing the skia operations.
347 canvas->drawPicture(picture_.get());
348 }
349 SkIRect bounds;
350 canvas->getClipDeviceBounds(&bounds);
351 canvas->restore();
352 TRACE_EVENT_END1(
353 "cc", "Picture::Raster",
354 "num_pixels_rasterized", bounds.width() * bounds.height());
355 return bounds.width() * bounds.height();
356}
357
358void Picture::Replay(SkCanvas* canvas) {
359 TRACE_EVENT_BEGIN0("cc", "Picture::Replay");
360 DCHECK(picture_);
James Robinsond4531882014-10-17 16:14:32 -0700361 picture_->draw(canvas);
James Robinson646469d2014-10-03 15:33:28 -0700362 SkIRect bounds;
363 canvas->getClipDeviceBounds(&bounds);
364 TRACE_EVENT_END1("cc", "Picture::Replay",
365 "num_pixels_replayed", bounds.width() * bounds.height());
366}
367
368scoped_ptr<base::Value> Picture::AsValue() const {
369 SkDynamicMemoryWStream stream;
James Robinsond4531882014-10-17 16:14:32 -0700370 picture_->serialize(&stream, &EncodeBitmap);
James Robinson646469d2014-10-03 15:33:28 -0700371
372 // Encode the picture as base64.
373 scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
374 res->Set("params.layer_rect", MathUtil::AsValue(layer_rect_).release());
375
376 size_t serialized_size = stream.bytesWritten();
377 scoped_ptr<char[]> serialized_picture(new char[serialized_size]);
378 stream.copyTo(serialized_picture.get());
379 std::string b64_picture;
380 base::Base64Encode(std::string(serialized_picture.get(), serialized_size),
381 &b64_picture);
382 res->SetString("skp64", b64_picture);
383 return res.Pass();
384}
385
386void Picture::EmitTraceSnapshot() const {
387 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
388 TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
389 "devtools.timeline.picture"),
390 "cc::Picture",
391 this,
392 TracedPicture::AsTraceablePicture(this));
393}
394
395void Picture::EmitTraceSnapshotAlias(Picture* original) const {
396 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
397 TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
398 "devtools.timeline.picture"),
399 "cc::Picture",
400 this,
401 TracedPicture::AsTraceablePictureAlias(original));
402}
403
404base::LazyInstance<Picture::PixelRefs>
405 Picture::PixelRefIterator::empty_pixel_refs_;
406
407Picture::PixelRefIterator::PixelRefIterator()
408 : picture_(NULL),
409 current_pixel_refs_(empty_pixel_refs_.Pointer()),
410 current_index_(0),
411 min_point_(-1, -1),
412 max_point_(-1, -1),
413 current_x_(0),
414 current_y_(0) {
415}
416
417Picture::PixelRefIterator::PixelRefIterator(
418 const gfx::Rect& rect,
419 const Picture* picture)
420 : picture_(picture),
421 current_pixel_refs_(empty_pixel_refs_.Pointer()),
422 current_index_(0) {
423 gfx::Rect layer_rect = picture->layer_rect_;
424 gfx::Size cell_size = picture->cell_size_;
425 DCHECK(!cell_size.IsEmpty());
426
427 gfx::Rect query_rect(rect);
428 // Early out if the query rect doesn't intersect this picture.
429 if (!query_rect.Intersects(layer_rect)) {
430 min_point_ = gfx::Point(0, 0);
431 max_point_ = gfx::Point(0, 0);
432 current_x_ = 1;
433 current_y_ = 1;
434 return;
435 }
436
437 // First, subtract the layer origin as cells are stored in layer space.
438 query_rect.Offset(-layer_rect.OffsetFromOrigin());
439
440 // We have to find a cell_size aligned point that corresponds to
441 // query_rect. Point is a multiple of cell_size.
442 min_point_ = gfx::Point(
443 RoundDown(query_rect.x(), cell_size.width()),
444 RoundDown(query_rect.y(), cell_size.height()));
445 max_point_ = gfx::Point(
446 RoundDown(query_rect.right() - 1, cell_size.width()),
447 RoundDown(query_rect.bottom() - 1, cell_size.height()));
448
449 // Limit the points to known pixel ref boundaries.
450 min_point_ = gfx::Point(
451 std::max(min_point_.x(), picture->min_pixel_cell_.x()),
452 std::max(min_point_.y(), picture->min_pixel_cell_.y()));
453 max_point_ = gfx::Point(
454 std::min(max_point_.x(), picture->max_pixel_cell_.x()),
455 std::min(max_point_.y(), picture->max_pixel_cell_.y()));
456
457 // Make the current x be cell_size.width() less than min point, so that
458 // the first increment will point at min_point_.
459 current_x_ = min_point_.x() - cell_size.width();
460 current_y_ = min_point_.y();
461 if (current_y_ <= max_point_.y())
462 ++(*this);
463}
464
465Picture::PixelRefIterator::~PixelRefIterator() {
466}
467
468Picture::PixelRefIterator& Picture::PixelRefIterator::operator++() {
469 ++current_index_;
470 // If we're not at the end of the list, then we have the next item.
471 if (current_index_ < current_pixel_refs_->size())
472 return *this;
473
474 DCHECK(current_y_ <= max_point_.y());
475 while (true) {
476 gfx::Size cell_size = picture_->cell_size_;
477
478 // Advance the current grid cell.
479 current_x_ += cell_size.width();
480 if (current_x_ > max_point_.x()) {
481 current_y_ += cell_size.height();
482 current_x_ = min_point_.x();
483 if (current_y_ > max_point_.y()) {
484 current_pixel_refs_ = empty_pixel_refs_.Pointer();
485 current_index_ = 0;
486 break;
487 }
488 }
489
490 // If there are no pixel refs at this grid cell, keep incrementing.
491 PixelRefMapKey key(current_x_, current_y_);
492 PixelRefMap::const_iterator iter = picture_->pixel_refs_.find(key);
493 if (iter == picture_->pixel_refs_.end())
494 continue;
495
496 // We found a non-empty list: store it and get the first pixel ref.
497 current_pixel_refs_ = &iter->second;
498 current_index_ = 0;
499 break;
500 }
501 return *this;
502}
503
504scoped_refptr<base::debug::ConvertableToTraceFormat>
505 Picture::AsTraceableRasterData(float scale) const {
506 scoped_refptr<base::debug::TracedValue> raster_data =
507 new base::debug::TracedValue();
508 TracedValue::SetIDRef(this, raster_data.get(), "picture_id");
509 raster_data->SetDouble("scale", scale);
510 return raster_data;
511}
512
513scoped_refptr<base::debug::ConvertableToTraceFormat>
514 Picture::AsTraceableRecordData() const {
515 scoped_refptr<base::debug::TracedValue> record_data =
516 new base::debug::TracedValue();
517 TracedValue::SetIDRef(this, record_data.get(), "picture_id");
518 record_data->BeginArray("layer_rect");
519 MathUtil::AddToTracedValue(layer_rect_, record_data.get());
520 record_data->EndArray();
521 return record_data;
522}
523
524} // namespace cc