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