• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/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/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/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/rect_conversions.h"
32 #include "ui/gfx/skia_util.h"
33 
34 namespace cc {
35 
36 namespace {
37 
EncodeBitmap(size_t * offset,const SkBitmap & bm)38 SkData* 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 
DecodeBitmap(const void * buffer,size_t size,SkBitmap * bm)69 bool 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 
Create(const gfx::Rect & layer_rect,ContentLayerClient * client,const SkTileGridFactory::TileGridInfo & tile_grid_info,bool gather_pixel_refs,RecordingMode recording_mode)87 scoped_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 
Picture(const gfx::Rect & layer_rect)102 Picture::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 
CreateFromSkpValue(const base::Value * value)109 scoped_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 
CreateFromValue(const base::Value * raw_value)128 scoped_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 
Picture(SkPicture * picture,const gfx::Rect & layer_rect)158 Picture::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 
Picture(const skia::RefPtr<SkPicture> & picture,const gfx::Rect & layer_rect,const PixelRefMap & pixel_refs)164 Picture::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 
~Picture()173 Picture::~Picture() {
174   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
175     TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this);
176 }
177 
IsSuitableForGpuRasterization() const178 bool Picture::IsSuitableForGpuRasterization() const {
179   DCHECK(picture_);
180 
181   // TODO(alokp): SkPicture::suitableForGpuRasterization needs a GrContext.
182   // Ideally this GrContext should be the same as that for rasterizing this
183   // picture. But we are on the main thread while the rasterization context
184   // may be on the compositor or raster thread.
185   // SkPicture::suitableForGpuRasterization is not implemented yet.
186   // Pass a NULL context for now and discuss with skia folks if the context
187   // is really needed.
188   return picture_->suitableForGpuRasterization(NULL);
189 }
190 
ApproximateOpCount() const191 int Picture::ApproximateOpCount() const {
192   DCHECK(picture_);
193   return picture_->approximateOpCount();
194 }
195 
HasText() const196 bool Picture::HasText() const {
197   DCHECK(picture_);
198   return picture_->hasText();
199 }
200 
Record(ContentLayerClient * painter,const SkTileGridFactory::TileGridInfo & tile_grid_info,RecordingMode recording_mode)201 void Picture::Record(ContentLayerClient* painter,
202                      const SkTileGridFactory::TileGridInfo& tile_grid_info,
203                      RecordingMode recording_mode) {
204   TRACE_EVENT2("cc",
205                "Picture::Record",
206                "data",
207                AsTraceableRecordData(),
208                "recording_mode",
209                recording_mode);
210 
211   DCHECK(!picture_);
212   DCHECK(!tile_grid_info.fTileInterval.isEmpty());
213 
214   SkTileGridFactory factory(tile_grid_info);
215   SkPictureRecorder recorder;
216 
217   scoped_ptr<EXPERIMENTAL::SkRecording> recording;
218 
219   skia::RefPtr<SkCanvas> canvas;
220   canvas = skia::SharePtr(recorder.beginRecording(
221       layer_rect_.width(), layer_rect_.height(), &factory));
222 
223   ContentLayerClient::GraphicsContextStatus graphics_context_status =
224       ContentLayerClient::GRAPHICS_CONTEXT_ENABLED;
225 
226   switch (recording_mode) {
227     case RECORD_NORMALLY:
228       // Already setup for normal recording.
229       break;
230     case RECORD_WITH_SK_NULL_CANVAS:
231       canvas = skia::AdoptRef(SkCreateNullCanvas());
232       break;
233     case RECORD_WITH_PAINTING_DISABLED:
234       // We pass a disable flag through the paint calls when perfromance
235       // testing (the only time this case should ever arise) when we want to
236       // prevent the Blink GraphicsContext object from consuming any compute
237       // time.
238       canvas = skia::AdoptRef(SkCreateNullCanvas());
239       graphics_context_status = ContentLayerClient::GRAPHICS_CONTEXT_DISABLED;
240       break;
241     case RECORD_WITH_SKRECORD:
242       recording.reset(new EXPERIMENTAL::SkRecording(layer_rect_.width(),
243                                                     layer_rect_.height()));
244       canvas = skia::SharePtr(recording->canvas());
245       break;
246     default:
247       NOTREACHED();
248   }
249 
250   canvas->save();
251   canvas->translate(SkFloatToScalar(-layer_rect_.x()),
252                     SkFloatToScalar(-layer_rect_.y()));
253 
254   SkRect layer_skrect = SkRect::MakeXYWH(layer_rect_.x(),
255                                          layer_rect_.y(),
256                                          layer_rect_.width(),
257                                          layer_rect_.height());
258   canvas->clipRect(layer_skrect);
259 
260   painter->PaintContents(canvas.get(), layer_rect_, graphics_context_status);
261 
262   canvas->restore();
263   picture_ = skia::AdoptRef(recorder.endRecording());
264   DCHECK(picture_);
265 
266   if (recording) {
267     // SkRecording requires it's the only one holding onto canvas before we
268     // may call releasePlayback().  (This helps enforce thread-safety.)
269     canvas.clear();
270     playback_.reset(recording->releasePlayback());
271   }
272 
273   EmitTraceSnapshot();
274 }
275 
GatherPixelRefs(const SkTileGridFactory::TileGridInfo & tile_grid_info)276 void Picture::GatherPixelRefs(
277     const SkTileGridFactory::TileGridInfo& tile_grid_info) {
278   TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
279                "width", layer_rect_.width(),
280                "height", layer_rect_.height());
281 
282   DCHECK(picture_);
283   DCHECK(pixel_refs_.empty());
284   if (!WillPlayBackBitmaps())
285     return;
286   cell_size_ = gfx::Size(
287       tile_grid_info.fTileInterval.width() +
288           2 * tile_grid_info.fMargin.width(),
289       tile_grid_info.fTileInterval.height() +
290           2 * tile_grid_info.fMargin.height());
291   DCHECK_GT(cell_size_.width(), 0);
292   DCHECK_GT(cell_size_.height(), 0);
293 
294   int min_x = std::numeric_limits<int>::max();
295   int min_y = std::numeric_limits<int>::max();
296   int max_x = 0;
297   int max_y = 0;
298 
299   skia::DiscardablePixelRefList pixel_refs;
300   skia::PixelRefUtils::GatherDiscardablePixelRefs(picture_.get(), &pixel_refs);
301   for (skia::DiscardablePixelRefList::const_iterator it = pixel_refs.begin();
302        it != pixel_refs.end();
303        ++it) {
304     gfx::Point min(
305         RoundDown(static_cast<int>(it->pixel_ref_rect.x()),
306                   cell_size_.width()),
307         RoundDown(static_cast<int>(it->pixel_ref_rect.y()),
308                   cell_size_.height()));
309     gfx::Point max(
310         RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.right())),
311                   cell_size_.width()),
312         RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.bottom())),
313                   cell_size_.height()));
314 
315     for (int y = min.y(); y <= max.y(); y += cell_size_.height()) {
316       for (int x = min.x(); x <= max.x(); x += cell_size_.width()) {
317         PixelRefMapKey key(x, y);
318         pixel_refs_[key].push_back(it->pixel_ref);
319       }
320     }
321 
322     min_x = std::min(min_x, min.x());
323     min_y = std::min(min_y, min.y());
324     max_x = std::max(max_x, max.x());
325     max_y = std::max(max_y, max.y());
326   }
327 
328   min_pixel_cell_ = gfx::Point(min_x, min_y);
329   max_pixel_cell_ = gfx::Point(max_x, max_y);
330 }
331 
Raster(SkCanvas * canvas,SkDrawPictureCallback * callback,const Region & negated_content_region,float contents_scale) const332 int Picture::Raster(SkCanvas* canvas,
333                     SkDrawPictureCallback* callback,
334                     const Region& negated_content_region,
335                     float contents_scale) const {
336   TRACE_EVENT_BEGIN1(
337       "cc",
338       "Picture::Raster",
339       "data",
340       AsTraceableRasterData(contents_scale));
341 
342   DCHECK(picture_);
343 
344   canvas->save();
345 
346   for (Region::Iterator it(negated_content_region); it.has_rect(); it.next())
347     canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
348 
349   canvas->scale(contents_scale, contents_scale);
350   canvas->translate(layer_rect_.x(), layer_rect_.y());
351   if (playback_) {
352     playback_->draw(canvas);
353   } else if (callback) {
354     // If we have a callback, we need to call |draw()|, |drawPicture()| doesn't
355     // take a callback.  This is used by |AnalysisCanvas| to early out.
356     picture_->draw(canvas, callback);
357   } else {
358     // Prefer to call |drawPicture()| on the canvas since it could place the
359     // entire picture on the canvas instead of parsing the skia operations.
360     canvas->drawPicture(picture_.get());
361   }
362   SkIRect bounds;
363   canvas->getClipDeviceBounds(&bounds);
364   canvas->restore();
365   TRACE_EVENT_END1(
366       "cc", "Picture::Raster",
367       "num_pixels_rasterized", bounds.width() * bounds.height());
368   return bounds.width() * bounds.height();
369 }
370 
Replay(SkCanvas * canvas)371 void Picture::Replay(SkCanvas* canvas) {
372   TRACE_EVENT_BEGIN0("cc", "Picture::Replay");
373   DCHECK(picture_);
374 
375   if (playback_) {
376     playback_->draw(canvas);
377   } else {
378     picture_->draw(canvas);
379   }
380   SkIRect bounds;
381   canvas->getClipDeviceBounds(&bounds);
382   TRACE_EVENT_END1("cc", "Picture::Replay",
383                    "num_pixels_replayed", bounds.width() * bounds.height());
384 }
385 
AsValue() const386 scoped_ptr<base::Value> Picture::AsValue() const {
387   SkDynamicMemoryWStream stream;
388 
389   if (playback_) {
390     // SkPlayback can't serialize itself, so re-record into an SkPicture.
391     SkPictureRecorder recorder;
392     skia::RefPtr<SkCanvas> canvas(skia::SharePtr(recorder.beginRecording(
393         layer_rect_.width(),
394         layer_rect_.height(),
395         NULL)));  // Default (no) bounding-box hierarchy is fastest.
396     playback_->draw(canvas.get());
397     skia::RefPtr<SkPicture> picture(skia::AdoptRef(recorder.endRecording()));
398     picture->serialize(&stream, &EncodeBitmap);
399   } else {
400     // Serialize the picture.
401     picture_->serialize(&stream, &EncodeBitmap);
402   }
403 
404   // Encode the picture as base64.
405   scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
406   res->Set("params.layer_rect", MathUtil::AsValue(layer_rect_).release());
407 
408   size_t serialized_size = stream.bytesWritten();
409   scoped_ptr<char[]> serialized_picture(new char[serialized_size]);
410   stream.copyTo(serialized_picture.get());
411   std::string b64_picture;
412   base::Base64Encode(std::string(serialized_picture.get(), serialized_size),
413                      &b64_picture);
414   res->SetString("skp64", b64_picture);
415   return res.PassAs<base::Value>();
416 }
417 
EmitTraceSnapshot() const418 void Picture::EmitTraceSnapshot() const {
419   TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
420       TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
421           "devtools.timeline.picture"),
422       "cc::Picture",
423       this,
424       TracedPicture::AsTraceablePicture(this));
425 }
426 
EmitTraceSnapshotAlias(Picture * original) const427 void Picture::EmitTraceSnapshotAlias(Picture* original) const {
428   TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
429       TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
430           "devtools.timeline.picture"),
431       "cc::Picture",
432       this,
433       TracedPicture::AsTraceablePictureAlias(original));
434 }
435 
436 base::LazyInstance<Picture::PixelRefs>
437     Picture::PixelRefIterator::empty_pixel_refs_;
438 
PixelRefIterator()439 Picture::PixelRefIterator::PixelRefIterator()
440     : picture_(NULL),
441       current_pixel_refs_(empty_pixel_refs_.Pointer()),
442       current_index_(0),
443       min_point_(-1, -1),
444       max_point_(-1, -1),
445       current_x_(0),
446       current_y_(0) {
447 }
448 
PixelRefIterator(const gfx::Rect & rect,const Picture * picture)449 Picture::PixelRefIterator::PixelRefIterator(
450     const gfx::Rect& rect,
451     const Picture* picture)
452     : picture_(picture),
453       current_pixel_refs_(empty_pixel_refs_.Pointer()),
454       current_index_(0) {
455   gfx::Rect layer_rect = picture->layer_rect_;
456   gfx::Size cell_size = picture->cell_size_;
457   DCHECK(!cell_size.IsEmpty());
458 
459   gfx::Rect query_rect(rect);
460   // Early out if the query rect doesn't intersect this picture.
461   if (!query_rect.Intersects(layer_rect)) {
462     min_point_ = gfx::Point(0, 0);
463     max_point_ = gfx::Point(0, 0);
464     current_x_ = 1;
465     current_y_ = 1;
466     return;
467   }
468 
469   // First, subtract the layer origin as cells are stored in layer space.
470   query_rect.Offset(-layer_rect.OffsetFromOrigin());
471 
472   // We have to find a cell_size aligned point that corresponds to
473   // query_rect. Point is a multiple of cell_size.
474   min_point_ = gfx::Point(
475       RoundDown(query_rect.x(), cell_size.width()),
476       RoundDown(query_rect.y(), cell_size.height()));
477   max_point_ = gfx::Point(
478       RoundDown(query_rect.right() - 1, cell_size.width()),
479       RoundDown(query_rect.bottom() - 1, cell_size.height()));
480 
481   // Limit the points to known pixel ref boundaries.
482   min_point_ = gfx::Point(
483       std::max(min_point_.x(), picture->min_pixel_cell_.x()),
484       std::max(min_point_.y(), picture->min_pixel_cell_.y()));
485   max_point_ = gfx::Point(
486       std::min(max_point_.x(), picture->max_pixel_cell_.x()),
487       std::min(max_point_.y(), picture->max_pixel_cell_.y()));
488 
489   // Make the current x be cell_size.width() less than min point, so that
490   // the first increment will point at min_point_.
491   current_x_ = min_point_.x() - cell_size.width();
492   current_y_ = min_point_.y();
493   if (current_y_ <= max_point_.y())
494     ++(*this);
495 }
496 
~PixelRefIterator()497 Picture::PixelRefIterator::~PixelRefIterator() {
498 }
499 
operator ++()500 Picture::PixelRefIterator& Picture::PixelRefIterator::operator++() {
501   ++current_index_;
502   // If we're not at the end of the list, then we have the next item.
503   if (current_index_ < current_pixel_refs_->size())
504     return *this;
505 
506   DCHECK(current_y_ <= max_point_.y());
507   while (true) {
508     gfx::Size cell_size = picture_->cell_size_;
509 
510     // Advance the current grid cell.
511     current_x_ += cell_size.width();
512     if (current_x_ > max_point_.x()) {
513       current_y_ += cell_size.height();
514       current_x_ = min_point_.x();
515       if (current_y_ > max_point_.y()) {
516         current_pixel_refs_ = empty_pixel_refs_.Pointer();
517         current_index_ = 0;
518         break;
519       }
520     }
521 
522     // If there are no pixel refs at this grid cell, keep incrementing.
523     PixelRefMapKey key(current_x_, current_y_);
524     PixelRefMap::const_iterator iter = picture_->pixel_refs_.find(key);
525     if (iter == picture_->pixel_refs_.end())
526       continue;
527 
528     // We found a non-empty list: store it and get the first pixel ref.
529     current_pixel_refs_ = &iter->second;
530     current_index_ = 0;
531     break;
532   }
533   return *this;
534 }
535 
536 scoped_refptr<base::debug::ConvertableToTraceFormat>
AsTraceableRasterData(float scale) const537     Picture::AsTraceableRasterData(float scale) const {
538   scoped_refptr<base::debug::TracedValue> raster_data =
539       new base::debug::TracedValue();
540   TracedValue::SetIDRef(this, raster_data.get(), "picture_id");
541   raster_data->SetDouble("scale", scale);
542   return raster_data;
543 }
544 
545 scoped_refptr<base::debug::ConvertableToTraceFormat>
AsTraceableRecordData() const546     Picture::AsTraceableRecordData() const {
547   scoped_refptr<base::debug::TracedValue> record_data =
548       new base::debug::TracedValue();
549   TracedValue::SetIDRef(this, record_data.get(), "picture_id");
550   record_data->BeginArray("layer_rect");
551   MathUtil::AddToTracedValue(layer_rect_, record_data.get());
552   record_data->EndArray();
553   return record_data;
554 }
555 
556 }  // namespace cc
557