picture.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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.
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/values.h"
14#include "cc/base/math_util.h"
15#include "cc/base/util.h"
16#include "cc/debug/rendering_stats_instrumentation.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/lazy_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/SkStream.h"
26#include "third_party/skia/include/utils/SkPictureUtils.h"
27#include "ui/gfx/codec/jpeg_codec.h"
28#include "ui/gfx/codec/png_codec.h"
29#include "ui/gfx/rect_conversions.h"
30#include "ui/gfx/skia_util.h"
31
32namespace cc {
33
34namespace {
35
36SkData* EncodeBitmap(size_t* offset, const SkBitmap& bm) {
37  const int kJpegQuality = 80;
38  std::vector<unsigned char> data;
39
40  // If bitmap is opaque, encode as JPEG.
41  // Otherwise encode as PNG.
42  bool encoding_succeeded = false;
43  if (bm.isOpaque()) {
44    SkAutoLockPixels lock_bitmap(bm);
45    if (bm.empty())
46      return NULL;
47
48    encoding_succeeded = gfx::JPEGCodec::Encode(
49        reinterpret_cast<unsigned char*>(bm.getAddr32(0, 0)),
50        gfx::JPEGCodec::FORMAT_SkBitmap,
51        bm.width(),
52        bm.height(),
53        bm.rowBytes(),
54        kJpegQuality,
55        &data);
56  } else {
57    encoding_succeeded = gfx::PNGCodec::EncodeBGRASkBitmap(bm, false, &data);
58  }
59
60  if (encoding_succeeded) {
61    *offset = 0;
62    return SkData::NewWithCopy(&data.front(), data.size());
63  }
64  return NULL;
65}
66
67bool DecodeBitmap(const void* buffer, size_t size, SkBitmap* bm) {
68  const unsigned char* data = static_cast<const unsigned char *>(buffer);
69
70  // Try PNG first.
71  if (gfx::PNGCodec::Decode(data, size, bm))
72    return true;
73
74  // Try JPEG.
75  scoped_ptr<SkBitmap> decoded_jpeg(gfx::JPEGCodec::Decode(data, size));
76  if (decoded_jpeg) {
77    *bm = *decoded_jpeg;
78    return true;
79  }
80  return false;
81}
82
83}  // namespace
84
85scoped_refptr<Picture> Picture::Create(gfx::Rect layer_rect) {
86  return make_scoped_refptr(new Picture(layer_rect));
87}
88
89scoped_refptr<Picture> Picture::CreateFromValue(const base::Value* value) {
90  bool success;
91  scoped_refptr<Picture> picture =
92    make_scoped_refptr(new Picture(value, &success));
93  if (!success)
94    picture = NULL;
95  return picture;
96}
97
98Picture::Picture(gfx::Rect layer_rect)
99    : layer_rect_(layer_rect) {
100  // Instead of recording a trace event for object creation here, we wait for
101  // the picture to be recorded in Picture::Record.
102}
103
104Picture::Picture(const base::Value* raw_value, bool* success) {
105  const base::DictionaryValue* value = NULL;
106  if (!raw_value->GetAsDictionary(&value)) {
107    *success = false;
108    return;
109  }
110
111  // Decode the picture from base64.
112  std::string encoded;
113  if (!value->GetString("skp64", &encoded)) {
114    *success = false;
115    return;
116  }
117
118  std::string decoded;
119  base::Base64Decode(encoded, &decoded);
120  SkMemoryStream stream(decoded.data(), decoded.size());
121
122  const base::Value* layer_rect = NULL;
123  if (!value->Get("params.layer_rect", &layer_rect)) {
124    *success = false;
125    return;
126  }
127  if (!MathUtil::FromValue(layer_rect, &layer_rect_)) {
128    *success = false;
129    return;
130  }
131
132  const base::Value* opaque_rect = NULL;
133  if (!value->Get("params.opaque_rect", &opaque_rect)) {
134    *success = false;
135    return;
136  }
137  if (!MathUtil::FromValue(opaque_rect, &opaque_rect_)) {
138    *success = false;
139    return;
140  }
141
142  // Read the picture. This creates an empty picture on failure.
143  picture_ = skia::AdoptRef(new SkPicture(&stream, success, &DecodeBitmap));
144}
145
146Picture::Picture(const skia::RefPtr<SkPicture>& picture,
147                 gfx::Rect layer_rect,
148                 gfx::Rect opaque_rect,
149                 const PixelRefMap& pixel_refs) :
150    layer_rect_(layer_rect),
151    opaque_rect_(opaque_rect),
152    picture_(picture),
153    pixel_refs_(pixel_refs) {
154}
155
156Picture::~Picture() {
157  TRACE_EVENT_OBJECT_DELETED_WITH_ID(
158    TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this);
159}
160
161scoped_refptr<Picture> Picture::GetCloneForDrawingOnThread(
162    unsigned thread_index) const {
163  // SkPicture is not thread-safe to rasterize with, this returns a clone
164  // to rasterize with on a specific thread.
165  CHECK_GT(clones_.size(), thread_index);
166  return clones_[thread_index];
167}
168
169void Picture::CloneForDrawing(int num_threads) {
170  TRACE_EVENT1("cc", "Picture::CloneForDrawing", "num_threads", num_threads);
171
172  DCHECK(picture_);
173  scoped_ptr<SkPicture[]> clones(new SkPicture[num_threads]);
174  picture_->clone(&clones[0], num_threads);
175
176  clones_.clear();
177  for (int i = 0; i < num_threads; i++) {
178    scoped_refptr<Picture> clone = make_scoped_refptr(
179        new Picture(skia::AdoptRef(new SkPicture(clones[i])),
180                    layer_rect_,
181                    opaque_rect_,
182                    pixel_refs_));
183    clones_.push_back(clone);
184
185    clone->EmitTraceSnapshot();
186  }
187}
188
189void Picture::Record(ContentLayerClient* painter,
190                     const SkTileGridPicture::TileGridInfo& tile_grid_info,
191                     RenderingStatsInstrumentation* stats_instrumentation) {
192  TRACE_EVENT2("cc", "Picture::Record",
193               "width", layer_rect_.width(),
194               "height", layer_rect_.height());
195
196  DCHECK(!tile_grid_info.fTileInterval.isEmpty());
197  picture_ = skia::AdoptRef(new SkTileGridPicture(
198      layer_rect_.width(), layer_rect_.height(), tile_grid_info));
199
200  SkCanvas* canvas = picture_->beginRecording(
201      layer_rect_.width(),
202      layer_rect_.height(),
203      SkPicture::kUsePathBoundsForClip_RecordingFlag |
204      SkPicture::kOptimizeForClippedPlayback_RecordingFlag);
205
206  canvas->save();
207  canvas->translate(SkFloatToScalar(-layer_rect_.x()),
208                    SkFloatToScalar(-layer_rect_.y()));
209
210  SkRect layer_skrect = SkRect::MakeXYWH(layer_rect_.x(),
211                                         layer_rect_.y(),
212                                         layer_rect_.width(),
213                                         layer_rect_.height());
214  canvas->clipRect(layer_skrect);
215
216  gfx::RectF opaque_layer_rect;
217  base::TimeTicks start_time = stats_instrumentation->StartRecording();
218
219  painter->PaintContents(canvas, layer_rect_, &opaque_layer_rect);
220
221  base::TimeDelta duration = stats_instrumentation->EndRecording(start_time);
222  stats_instrumentation->AddRecord(duration,
223                                   layer_rect_.width() * layer_rect_.height());
224
225  canvas->restore();
226  picture_->endRecording();
227
228  opaque_rect_ = gfx::ToEnclosedRect(opaque_layer_rect);
229
230  EmitTraceSnapshot();
231}
232
233void Picture::GatherPixelRefs(
234    const SkTileGridPicture::TileGridInfo& tile_grid_info,
235    RenderingStatsInstrumentation* stats_instrumentation) {
236  TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
237               "width", layer_rect_.width(),
238               "height", layer_rect_.height());
239
240  DCHECK(picture_);
241  cell_size_ = gfx::Size(
242      tile_grid_info.fTileInterval.width() +
243          2 * tile_grid_info.fMargin.width(),
244      tile_grid_info.fTileInterval.height() +
245          2 * tile_grid_info.fMargin.height());
246  DCHECK_GT(cell_size_.width(), 0);
247  DCHECK_GT(cell_size_.height(), 0);
248
249  int min_x = std::numeric_limits<int>::max();
250  int min_y = std::numeric_limits<int>::max();
251  int max_x = 0;
252  int max_y = 0;
253
254  base::TimeTicks start_time = stats_instrumentation->StartRecording();
255
256  skia::LazyPixelRefList pixel_refs;
257  skia::LazyPixelRefUtils::GatherPixelRefs(picture_.get(), &pixel_refs);
258  for (skia::LazyPixelRefList::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()));
271
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->lazy_pixel_ref);
276      }
277    }
278
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());
283  }
284
285  base::TimeDelta duration = stats_instrumentation->EndRecording(start_time);
286  stats_instrumentation->AddImageGathering(duration);
287
288  min_pixel_cell_ = gfx::Point(min_x, min_y);
289  max_pixel_cell_ = gfx::Point(max_x, max_y);
290}
291
292void Picture::Raster(
293    SkCanvas* canvas,
294    SkDrawPictureCallback* callback,
295    gfx::Rect content_rect,
296    float contents_scale) {
297  TRACE_EVENT_BEGIN1("cc", "Picture::Raster",
298    "data", AsTraceableRasterData(content_rect, contents_scale));
299
300  DCHECK(picture_);
301
302  canvas->save();
303  canvas->clipRect(gfx::RectToSkRect(content_rect));
304  canvas->scale(contents_scale, contents_scale);
305  canvas->translate(layer_rect_.x(), layer_rect_.y());
306  picture_->draw(canvas, callback);
307  SkIRect bounds;
308  canvas->getClipDeviceBounds(&bounds);
309  canvas->restore();
310  TRACE_EVENT_END1("cc", "Picture::Raster",
311                   "num_pixels_rasterized", bounds.width() * bounds.height());
312}
313
314void Picture::Replay(SkCanvas* canvas) {
315  TRACE_EVENT_BEGIN0("cc", "Picture::Replay");
316  DCHECK(picture_);
317
318  picture_->draw(canvas);
319  SkIRect bounds;
320  canvas->getClipDeviceBounds(&bounds);
321  TRACE_EVENT_END1("cc", "Picture::Replay",
322                   "num_pixels_replayed", bounds.width() * bounds.height());
323}
324
325scoped_ptr<base::Value> Picture::AsValue() const {
326  SkDynamicMemoryWStream stream;
327
328  // Serialize the picture.
329  picture_->serialize(&stream, &EncodeBitmap);
330
331  // Encode the picture as base64.
332  scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
333  res->Set("params.layer_rect", MathUtil::AsValue(layer_rect_).release());
334  res->Set("params.opaque_rect", MathUtil::AsValue(opaque_rect_).release());
335
336  size_t serialized_size = stream.bytesWritten();
337  scoped_ptr<char[]> serialized_picture(new char[serialized_size]);
338  stream.copyTo(serialized_picture.get());
339  std::string b64_picture;
340  base::Base64Encode(std::string(serialized_picture.get(), serialized_size),
341                     &b64_picture);
342  res->SetString("skp64", b64_picture);
343  return res.PassAs<base::Value>();
344}
345
346void Picture::EmitTraceSnapshot() {
347  TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
348      "cc::Picture", this, TracedPicture::AsTraceablePicture(this));
349}
350
351base::LazyInstance<Picture::PixelRefs>
352    Picture::PixelRefIterator::empty_pixel_refs_;
353
354Picture::PixelRefIterator::PixelRefIterator()
355    : picture_(NULL),
356      current_pixel_refs_(empty_pixel_refs_.Pointer()),
357      current_index_(0),
358      min_point_(-1, -1),
359      max_point_(-1, -1),
360      current_x_(0),
361      current_y_(0) {
362}
363
364Picture::PixelRefIterator::PixelRefIterator(
365    gfx::Rect query_rect,
366    const Picture* picture)
367    : picture_(picture),
368      current_pixel_refs_(empty_pixel_refs_.Pointer()),
369      current_index_(0) {
370  gfx::Rect layer_rect = picture->layer_rect_;
371  gfx::Size cell_size = picture->cell_size_;
372
373  // Early out if the query rect doesn't intersect this picture
374  if (!query_rect.Intersects(layer_rect)) {
375    min_point_ = gfx::Point(0, 0);
376    max_point_ = gfx::Point(0, 0);
377    current_x_ = 1;
378    current_y_ = 1;
379    return;
380  }
381
382  // First, subtract the layer origin as cells are stored in layer space.
383  query_rect.Offset(-layer_rect.OffsetFromOrigin());
384
385  // We have to find a cell_size aligned point that corresponds to
386  // query_rect. Point is a multiple of cell_size.
387  min_point_ = gfx::Point(
388      RoundDown(query_rect.x(), cell_size.width()),
389      RoundDown(query_rect.y(), cell_size.height()));
390  max_point_ = gfx::Point(
391      RoundDown(query_rect.right() - 1, cell_size.width()),
392      RoundDown(query_rect.bottom() - 1, cell_size.height()));
393
394  // Limit the points to known pixel ref boundaries.
395  min_point_ = gfx::Point(
396      std::max(min_point_.x(), picture->min_pixel_cell_.x()),
397      std::max(min_point_.y(), picture->min_pixel_cell_.y()));
398  max_point_ = gfx::Point(
399      std::min(max_point_.x(), picture->max_pixel_cell_.x()),
400      std::min(max_point_.y(), picture->max_pixel_cell_.y()));
401
402  // Make the current x be cell_size.width() less than min point, so that
403  // the first increment will point at min_point_.
404  current_x_ = min_point_.x() - cell_size.width();
405  current_y_ = min_point_.y();
406  if (current_y_ <= max_point_.y())
407    ++(*this);
408}
409
410Picture::PixelRefIterator::~PixelRefIterator() {
411}
412
413Picture::PixelRefIterator& Picture::PixelRefIterator::operator++() {
414  ++current_index_;
415  // If we're not at the end of the list, then we have the next item.
416  if (current_index_ < current_pixel_refs_->size())
417    return *this;
418
419  DCHECK(current_y_ <= max_point_.y());
420  while (true) {
421    gfx::Size cell_size = picture_->cell_size_;
422
423    // Advance the current grid cell.
424    current_x_ += cell_size.width();
425    if (current_x_ > max_point_.x()) {
426      current_y_ += cell_size.height();
427      current_x_ = min_point_.x();
428      if (current_y_ > max_point_.y()) {
429        current_pixel_refs_ = empty_pixel_refs_.Pointer();
430        current_index_ = 0;
431        break;
432      }
433    }
434
435    // If there are no pixel refs at this grid cell, keep incrementing.
436    PixelRefMapKey key(current_x_, current_y_);
437    PixelRefMap::const_iterator iter = picture_->pixel_refs_.find(key);
438    if (iter == picture_->pixel_refs_.end())
439      continue;
440
441    // We found a non-empty list: store it and get the first pixel ref.
442    current_pixel_refs_ = &iter->second;
443    current_index_ = 0;
444    break;
445  }
446  return *this;
447}
448
449scoped_ptr<base::debug::ConvertableToTraceFormat>
450    Picture::AsTraceableRasterData(gfx::Rect rect, float scale) {
451  scoped_ptr<base::DictionaryValue> raster_data(new base::DictionaryValue());
452  raster_data->Set("picture_id", TracedValue::CreateIDRef(this).release());
453  raster_data->SetDouble("scale", scale);
454  raster_data->SetDouble("rect_x", rect.x());
455  raster_data->SetDouble("rect_y", rect.y());
456  raster_data->SetDouble("rect_width", rect.width());
457  raster_data->SetDouble("rect_height", rect.height());
458  return TracedValue::FromValue(raster_data.release());
459}
460
461}  // namespace cc
462