Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / cc / resources / picture.cc
blob9a45ad19a67b666a4ce8923aa7bdf41a71546fff
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/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/SkBBHFactory.h"
22 #include "third_party/skia/include/core/SkCanvas.h"
23 #include "third_party/skia/include/core/SkData.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 "ui/gfx/codec/jpeg_codec.h"
29 #include "ui/gfx/codec/png_codec.h"
30 #include "ui/gfx/geometry/rect_conversions.h"
31 #include "ui/gfx/skia_util.h"
33 namespace cc {
35 namespace {
37 SkData* EncodeBitmap(size_t* offset, const SkBitmap& bm) {
38 const int kJpegQuality = 80;
39 std::vector<unsigned char> data;
41 // If bitmap is opaque, encode as JPEG.
42 // Otherwise encode as PNG.
43 bool encoding_succeeded = false;
44 if (bm.isOpaque()) {
45 SkAutoLockPixels lock_bitmap(bm);
46 if (bm.empty())
47 return NULL;
49 encoding_succeeded = gfx::JPEGCodec::Encode(
50 reinterpret_cast<unsigned char*>(bm.getAddr32(0, 0)),
51 gfx::JPEGCodec::FORMAT_SkBitmap,
52 bm.width(),
53 bm.height(),
54 bm.rowBytes(),
55 kJpegQuality,
56 &data);
57 } else {
58 encoding_succeeded = gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &data);
61 if (encoding_succeeded) {
62 *offset = 0;
63 return SkData::NewWithCopy(&data.front(), data.size());
65 return NULL;
68 bool DecodeBitmap(const void* buffer, size_t size, SkBitmap* bm) {
69 const unsigned char* data = static_cast<const unsigned char *>(buffer);
71 // Try PNG first.
72 if (gfx::PNGCodec::Decode(data, size, bm))
73 return true;
75 // Try JPEG.
76 scoped_ptr<SkBitmap> decoded_jpeg(gfx::JPEGCodec::Decode(data, size));
77 if (decoded_jpeg) {
78 *bm = *decoded_jpeg;
79 return true;
81 return false;
84 } // namespace
86 scoped_refptr<Picture> Picture::Create(
87 const gfx::Rect& layer_rect,
88 ContentLayerClient* client,
89 const SkTileGridFactory::TileGridInfo& tile_grid_info,
90 bool gather_pixel_refs,
91 RecordingMode recording_mode) {
92 scoped_refptr<Picture> picture = make_scoped_refptr(new Picture(layer_rect));
94 picture->Record(client, tile_grid_info, recording_mode);
95 if (gather_pixel_refs)
96 picture->GatherPixelRefs(tile_grid_info);
98 return picture;
101 Picture::Picture(const gfx::Rect& layer_rect)
102 : layer_rect_(layer_rect),
103 cell_size_(layer_rect.size()) {
104 // Instead of recording a trace event for object creation here, we wait for
105 // the picture to be recorded in Picture::Record.
108 scoped_refptr<Picture> Picture::CreateFromSkpValue(const base::Value* value) {
109 // Decode the picture from base64.
110 std::string encoded;
111 if (!value->GetAsString(&encoded))
112 return NULL;
114 std::string decoded;
115 base::Base64Decode(encoded, &decoded);
116 SkMemoryStream stream(decoded.data(), decoded.size());
118 // Read the picture. This creates an empty picture on failure.
119 SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
120 if (skpicture == NULL)
121 return NULL;
123 gfx::Rect layer_rect(skpicture->width(), skpicture->height());
124 return make_scoped_refptr(new Picture(skpicture, layer_rect));
127 scoped_refptr<Picture> Picture::CreateFromValue(const base::Value* raw_value) {
128 const base::DictionaryValue* value = NULL;
129 if (!raw_value->GetAsDictionary(&value))
130 return NULL;
132 // Decode the picture from base64.
133 std::string encoded;
134 if (!value->GetString("skp64", &encoded))
135 return NULL;
137 std::string decoded;
138 base::Base64Decode(encoded, &decoded);
139 SkMemoryStream stream(decoded.data(), decoded.size());
141 const base::Value* layer_rect_value = NULL;
142 if (!value->Get("params.layer_rect", &layer_rect_value))
143 return NULL;
145 gfx::Rect layer_rect;
146 if (!MathUtil::FromValue(layer_rect_value, &layer_rect))
147 return NULL;
149 // Read the picture. This creates an empty picture on failure.
150 SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
151 if (skpicture == NULL)
152 return NULL;
154 return make_scoped_refptr(new Picture(skpicture, layer_rect));
157 Picture::Picture(SkPicture* picture, const gfx::Rect& layer_rect)
158 : layer_rect_(layer_rect),
159 picture_(skia::AdoptRef(picture)),
160 cell_size_(layer_rect.size()) {
163 Picture::Picture(const skia::RefPtr<SkPicture>& picture,
164 const gfx::Rect& layer_rect,
165 const PixelRefMap& pixel_refs) :
166 layer_rect_(layer_rect),
167 picture_(picture),
168 pixel_refs_(pixel_refs),
169 cell_size_(layer_rect.size()) {
172 Picture::~Picture() {
173 TRACE_EVENT_OBJECT_DELETED_WITH_ID(
174 TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this);
177 bool Picture::IsSuitableForGpuRasterization() const {
178 DCHECK(picture_);
180 // TODO(alokp): SkPicture::suitableForGpuRasterization needs a GrContext.
181 // Ideally this GrContext should be the same as that for rasterizing this
182 // picture. But we are on the main thread while the rasterization context
183 // may be on the compositor or raster thread.
184 // SkPicture::suitableForGpuRasterization is not implemented yet.
185 // Pass a NULL context for now and discuss with skia folks if the context
186 // is really needed.
187 return picture_->suitableForGpuRasterization(NULL);
190 int Picture::ApproximateOpCount() const {
191 DCHECK(picture_);
192 return picture_->approximateOpCount();
195 bool Picture::HasText() const {
196 DCHECK(picture_);
197 return picture_->hasText();
200 void Picture::Record(ContentLayerClient* painter,
201 const SkTileGridFactory::TileGridInfo& tile_grid_info,
202 RecordingMode recording_mode) {
203 TRACE_EVENT2("cc",
204 "Picture::Record",
205 "data",
206 AsTraceableRecordData(),
207 "recording_mode",
208 recording_mode);
210 DCHECK(!picture_);
211 DCHECK(!tile_grid_info.fTileInterval.isEmpty());
213 // TODO(mtklein): If SkRTree sticks, clean up tile_grid_info. skbug.com/3085
214 SkRTreeFactory factory;
215 SkPictureRecorder recorder;
217 skia::RefPtr<SkCanvas> canvas;
218 canvas = skia::SharePtr(recorder.beginRecording(
219 layer_rect_.width(), layer_rect_.height(), &factory,
220 SkPictureRecorder::kComputeSaveLayerInfo_RecordFlag));
222 ContentLayerClient::GraphicsContextStatus graphics_context_status =
223 ContentLayerClient::GRAPHICS_CONTEXT_ENABLED;
225 switch (recording_mode) {
226 case RECORD_NORMALLY:
227 // Already setup for normal recording.
228 break;
229 case RECORD_WITH_SK_NULL_CANVAS:
230 canvas = skia::AdoptRef(SkCreateNullCanvas());
231 break;
232 case RECORD_WITH_PAINTING_DISABLED:
233 // We pass a disable flag through the paint calls when perfromance
234 // testing (the only time this case should ever arise) when we want to
235 // prevent the Blink GraphicsContext object from consuming any compute
236 // time.
237 canvas = skia::AdoptRef(SkCreateNullCanvas());
238 graphics_context_status = ContentLayerClient::GRAPHICS_CONTEXT_DISABLED;
239 break;
240 default:
241 NOTREACHED();
244 canvas->save();
245 canvas->translate(SkFloatToScalar(-layer_rect_.x()),
246 SkFloatToScalar(-layer_rect_.y()));
248 SkRect layer_skrect = SkRect::MakeXYWH(layer_rect_.x(),
249 layer_rect_.y(),
250 layer_rect_.width(),
251 layer_rect_.height());
252 canvas->clipRect(layer_skrect);
254 painter->PaintContents(canvas.get(), layer_rect_, graphics_context_status);
256 canvas->restore();
257 picture_ = skia::AdoptRef(recorder.endRecording());
258 DCHECK(picture_);
260 EmitTraceSnapshot();
263 void Picture::GatherPixelRefs(
264 const SkTileGridFactory::TileGridInfo& tile_grid_info) {
265 TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
266 "width", layer_rect_.width(),
267 "height", layer_rect_.height());
269 DCHECK(picture_);
270 DCHECK(pixel_refs_.empty());
271 if (!WillPlayBackBitmaps())
272 return;
273 cell_size_ = gfx::Size(
274 tile_grid_info.fTileInterval.width() +
275 2 * tile_grid_info.fMargin.width(),
276 tile_grid_info.fTileInterval.height() +
277 2 * tile_grid_info.fMargin.height());
278 DCHECK_GT(cell_size_.width(), 0);
279 DCHECK_GT(cell_size_.height(), 0);
281 int min_x = std::numeric_limits<int>::max();
282 int min_y = std::numeric_limits<int>::max();
283 int max_x = 0;
284 int max_y = 0;
286 skia::DiscardablePixelRefList pixel_refs;
287 skia::PixelRefUtils::GatherDiscardablePixelRefs(picture_.get(), &pixel_refs);
288 for (skia::DiscardablePixelRefList::const_iterator it = pixel_refs.begin();
289 it != pixel_refs.end();
290 ++it) {
291 gfx::Point min(
292 RoundDown(static_cast<int>(it->pixel_ref_rect.x()),
293 cell_size_.width()),
294 RoundDown(static_cast<int>(it->pixel_ref_rect.y()),
295 cell_size_.height()));
296 gfx::Point max(
297 RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.right())),
298 cell_size_.width()),
299 RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.bottom())),
300 cell_size_.height()));
302 for (int y = min.y(); y <= max.y(); y += cell_size_.height()) {
303 for (int x = min.x(); x <= max.x(); x += cell_size_.width()) {
304 PixelRefMapKey key(x, y);
305 pixel_refs_[key].push_back(it->pixel_ref);
309 min_x = std::min(min_x, min.x());
310 min_y = std::min(min_y, min.y());
311 max_x = std::max(max_x, max.x());
312 max_y = std::max(max_y, max.y());
315 min_pixel_cell_ = gfx::Point(min_x, min_y);
316 max_pixel_cell_ = gfx::Point(max_x, max_y);
319 int Picture::Raster(SkCanvas* canvas,
320 SkDrawPictureCallback* callback,
321 const Region& negated_content_region,
322 float contents_scale) const {
323 TRACE_EVENT_BEGIN1(
324 "cc",
325 "Picture::Raster",
326 "data",
327 AsTraceableRasterData(contents_scale));
329 DCHECK(picture_);
331 canvas->save();
333 for (Region::Iterator it(negated_content_region); it.has_rect(); it.next())
334 canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
336 canvas->scale(contents_scale, contents_scale);
337 canvas->translate(layer_rect_.x(), layer_rect_.y());
338 if (callback) {
339 // If we have a callback, we need to call |draw()|, |drawPicture()| doesn't
340 // take a callback. This is used by |AnalysisCanvas| to early out.
341 picture_->draw(canvas, callback);
342 } else {
343 // Prefer to call |drawPicture()| on the canvas since it could place the
344 // entire picture on the canvas instead of parsing the skia operations.
345 canvas->drawPicture(picture_.get());
347 SkIRect bounds;
348 canvas->getClipDeviceBounds(&bounds);
349 canvas->restore();
350 TRACE_EVENT_END1(
351 "cc", "Picture::Raster",
352 "num_pixels_rasterized", bounds.width() * bounds.height());
353 return bounds.width() * bounds.height();
356 void Picture::Replay(SkCanvas* canvas) {
357 TRACE_EVENT_BEGIN0("cc", "Picture::Replay");
358 DCHECK(picture_);
359 picture_->draw(canvas);
360 SkIRect bounds;
361 canvas->getClipDeviceBounds(&bounds);
362 TRACE_EVENT_END1("cc", "Picture::Replay",
363 "num_pixels_replayed", bounds.width() * bounds.height());
366 scoped_ptr<base::Value> Picture::AsValue() const {
367 SkDynamicMemoryWStream stream;
368 picture_->serialize(&stream, &EncodeBitmap);
370 // Encode the picture as base64.
371 scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
372 res->Set("params.layer_rect", MathUtil::AsValue(layer_rect_).release());
374 size_t serialized_size = stream.bytesWritten();
375 scoped_ptr<char[]> serialized_picture(new char[serialized_size]);
376 stream.copyTo(serialized_picture.get());
377 std::string b64_picture;
378 base::Base64Encode(std::string(serialized_picture.get(), serialized_size),
379 &b64_picture);
380 res->SetString("skp64", b64_picture);
381 return res.Pass();
384 void Picture::EmitTraceSnapshot() const {
385 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
386 TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
387 "devtools.timeline.picture"),
388 "cc::Picture",
389 this,
390 TracedPicture::AsTraceablePicture(this));
393 void Picture::EmitTraceSnapshotAlias(Picture* original) const {
394 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
395 TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
396 "devtools.timeline.picture"),
397 "cc::Picture",
398 this,
399 TracedPicture::AsTraceablePictureAlias(original));
402 base::LazyInstance<Picture::PixelRefs>
403 Picture::PixelRefIterator::empty_pixel_refs_;
405 Picture::PixelRefIterator::PixelRefIterator()
406 : picture_(NULL),
407 current_pixel_refs_(empty_pixel_refs_.Pointer()),
408 current_index_(0),
409 min_point_(-1, -1),
410 max_point_(-1, -1),
411 current_x_(0),
412 current_y_(0) {
415 Picture::PixelRefIterator::PixelRefIterator(
416 const gfx::Rect& rect,
417 const Picture* picture)
418 : picture_(picture),
419 current_pixel_refs_(empty_pixel_refs_.Pointer()),
420 current_index_(0) {
421 gfx::Rect layer_rect = picture->layer_rect_;
422 gfx::Size cell_size = picture->cell_size_;
423 DCHECK(!cell_size.IsEmpty());
425 gfx::Rect query_rect(rect);
426 // Early out if the query rect doesn't intersect this picture.
427 if (!query_rect.Intersects(layer_rect)) {
428 min_point_ = gfx::Point(0, 0);
429 max_point_ = gfx::Point(0, 0);
430 current_x_ = 1;
431 current_y_ = 1;
432 return;
435 // First, subtract the layer origin as cells are stored in layer space.
436 query_rect.Offset(-layer_rect.OffsetFromOrigin());
438 // We have to find a cell_size aligned point that corresponds to
439 // query_rect. Point is a multiple of cell_size.
440 min_point_ = gfx::Point(
441 RoundDown(query_rect.x(), cell_size.width()),
442 RoundDown(query_rect.y(), cell_size.height()));
443 max_point_ = gfx::Point(
444 RoundDown(query_rect.right() - 1, cell_size.width()),
445 RoundDown(query_rect.bottom() - 1, cell_size.height()));
447 // Limit the points to known pixel ref boundaries.
448 min_point_ = gfx::Point(
449 std::max(min_point_.x(), picture->min_pixel_cell_.x()),
450 std::max(min_point_.y(), picture->min_pixel_cell_.y()));
451 max_point_ = gfx::Point(
452 std::min(max_point_.x(), picture->max_pixel_cell_.x()),
453 std::min(max_point_.y(), picture->max_pixel_cell_.y()));
455 // Make the current x be cell_size.width() less than min point, so that
456 // the first increment will point at min_point_.
457 current_x_ = min_point_.x() - cell_size.width();
458 current_y_ = min_point_.y();
459 if (current_y_ <= max_point_.y())
460 ++(*this);
463 Picture::PixelRefIterator::~PixelRefIterator() {
466 Picture::PixelRefIterator& Picture::PixelRefIterator::operator++() {
467 ++current_index_;
468 // If we're not at the end of the list, then we have the next item.
469 if (current_index_ < current_pixel_refs_->size())
470 return *this;
472 DCHECK(current_y_ <= max_point_.y());
473 while (true) {
474 gfx::Size cell_size = picture_->cell_size_;
476 // Advance the current grid cell.
477 current_x_ += cell_size.width();
478 if (current_x_ > max_point_.x()) {
479 current_y_ += cell_size.height();
480 current_x_ = min_point_.x();
481 if (current_y_ > max_point_.y()) {
482 current_pixel_refs_ = empty_pixel_refs_.Pointer();
483 current_index_ = 0;
484 break;
488 // If there are no pixel refs at this grid cell, keep incrementing.
489 PixelRefMapKey key(current_x_, current_y_);
490 PixelRefMap::const_iterator iter = picture_->pixel_refs_.find(key);
491 if (iter == picture_->pixel_refs_.end())
492 continue;
494 // We found a non-empty list: store it and get the first pixel ref.
495 current_pixel_refs_ = &iter->second;
496 current_index_ = 0;
497 break;
499 return *this;
502 scoped_refptr<base::debug::ConvertableToTraceFormat>
503 Picture::AsTraceableRasterData(float scale) const {
504 scoped_refptr<base::debug::TracedValue> raster_data =
505 new base::debug::TracedValue();
506 TracedValue::SetIDRef(this, raster_data.get(), "picture_id");
507 raster_data->SetDouble("scale", scale);
508 return raster_data;
511 scoped_refptr<base::debug::ConvertableToTraceFormat>
512 Picture::AsTraceableRecordData() const {
513 scoped_refptr<base::debug::TracedValue> record_data =
514 new base::debug::TracedValue();
515 TracedValue::SetIDRef(this, record_data.get(), "picture_id");
516 record_data->BeginArray("layer_rect");
517 MathUtil::AddToTracedValue(layer_rect_, record_data.get());
518 record_data->EndArray();
519 return record_data;
522 } // namespace cc