Home | History | Annotate | Download | only in resources
      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/traced_picture.h"
     17 #include "cc/debug/traced_value.h"
     18 #include "cc/layers/content_layer_client.h"
     19 #include "skia/ext/pixel_ref_utils.h"
     20 #include "third_party/skia/include/core/SkCanvas.h"
     21 #include "third_party/skia/include/core/SkData.h"
     22 #include "third_party/skia/include/core/SkDrawFilter.h"
     23 #include "third_party/skia/include/core/SkPaint.h"
     24 #include "third_party/skia/include/core/SkPictureRecorder.h"
     25 #include "third_party/skia/include/core/SkStream.h"
     26 #include "third_party/skia/include/utils/SkNullCanvas.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 
     33 namespace cc {
     34 
     35 namespace {
     36 
     37 SkData* 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 
     68 bool 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 
     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     int num_raster_threads,
     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   picture->CloneForDrawing(num_raster_threads);
     99 
    100   return picture;
    101 }
    102 
    103 Picture::Picture(const gfx::Rect& layer_rect)
    104   : layer_rect_(layer_rect),
    105     cell_size_(layer_rect.size()) {
    106   // Instead of recording a trace event for object creation here, we wait for
    107   // the picture to be recorded in Picture::Record.
    108 }
    109 
    110 scoped_refptr<Picture> Picture::CreateFromSkpValue(const base::Value* value) {
    111   // Decode the picture from base64.
    112   std::string encoded;
    113   if (!value->GetAsString(&encoded))
    114     return NULL;
    115 
    116   std::string decoded;
    117   base::Base64Decode(encoded, &decoded);
    118   SkMemoryStream stream(decoded.data(), decoded.size());
    119 
    120   // Read the picture. This creates an empty picture on failure.
    121   SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
    122   if (skpicture == NULL)
    123     return NULL;
    124 
    125   gfx::Rect layer_rect(skpicture->width(), skpicture->height());
    126   gfx::Rect opaque_rect(skpicture->width(), skpicture->height());
    127 
    128   return make_scoped_refptr(new Picture(skpicture, layer_rect, opaque_rect));
    129 }
    130 
    131 scoped_refptr<Picture> Picture::CreateFromValue(const base::Value* raw_value) {
    132   const base::DictionaryValue* value = NULL;
    133   if (!raw_value->GetAsDictionary(&value))
    134     return NULL;
    135 
    136   // Decode the picture from base64.
    137   std::string encoded;
    138   if (!value->GetString("skp64", &encoded))
    139     return NULL;
    140 
    141   std::string decoded;
    142   base::Base64Decode(encoded, &decoded);
    143   SkMemoryStream stream(decoded.data(), decoded.size());
    144 
    145   const base::Value* layer_rect_value = NULL;
    146   if (!value->Get("params.layer_rect", &layer_rect_value))
    147     return NULL;
    148 
    149   gfx::Rect layer_rect;
    150   if (!MathUtil::FromValue(layer_rect_value, &layer_rect))
    151     return NULL;
    152 
    153   const base::Value* opaque_rect_value = NULL;
    154   if (!value->Get("params.opaque_rect", &opaque_rect_value))
    155     return NULL;
    156 
    157   gfx::Rect opaque_rect;
    158   if (!MathUtil::FromValue(opaque_rect_value, &opaque_rect))
    159     return NULL;
    160 
    161   // Read the picture. This creates an empty picture on failure.
    162   SkPicture* skpicture = SkPicture::CreateFromStream(&stream, &DecodeBitmap);
    163   if (skpicture == NULL)
    164     return NULL;
    165 
    166   return make_scoped_refptr(new Picture(skpicture, layer_rect, opaque_rect));
    167 }
    168 
    169 Picture::Picture(SkPicture* picture,
    170                  const gfx::Rect& layer_rect,
    171                  const gfx::Rect& opaque_rect) :
    172     layer_rect_(layer_rect),
    173     opaque_rect_(opaque_rect),
    174     picture_(skia::AdoptRef(picture)),
    175     cell_size_(layer_rect.size()) {
    176 }
    177 
    178 Picture::Picture(const skia::RefPtr<SkPicture>& picture,
    179                  const gfx::Rect& layer_rect,
    180                  const gfx::Rect& opaque_rect,
    181                  const PixelRefMap& pixel_refs) :
    182     layer_rect_(layer_rect),
    183     opaque_rect_(opaque_rect),
    184     picture_(picture),
    185     pixel_refs_(pixel_refs),
    186     cell_size_(layer_rect.size()) {
    187 }
    188 
    189 Picture::~Picture() {
    190   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
    191     TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this);
    192 }
    193 
    194 Picture* Picture::GetCloneForDrawingOnThread(unsigned thread_index) {
    195   // We don't need clones to draw from multiple threads with SkRecord.
    196   if (playback_) {
    197     return this;
    198   }
    199 
    200   // SkPicture is not thread-safe to rasterize with, this returns a clone
    201   // to rasterize with on a specific thread.
    202   CHECK_GE(clones_.size(), thread_index);
    203   return thread_index == clones_.size() ? this : clones_[thread_index].get();
    204 }
    205 
    206 bool Picture::IsSuitableForGpuRasterization() const {
    207   DCHECK(picture_);
    208 
    209   // TODO(alokp): SkPicture::suitableForGpuRasterization needs a GrContext.
    210   // Ideally this GrContext should be the same as that for rasterizing this
    211   // picture. But we are on the main thread while the rasterization context
    212   // may be on the compositor or raster thread.
    213   // SkPicture::suitableForGpuRasterization is not implemented yet.
    214   // Pass a NULL context for now and discuss with skia folks if the context
    215   // is really needed.
    216   return picture_->suitableForGpuRasterization(NULL);
    217 }
    218 
    219 void Picture::CloneForDrawing(int num_threads) {
    220   TRACE_EVENT1("cc", "Picture::CloneForDrawing", "num_threads", num_threads);
    221 
    222   // We don't need clones to draw from multiple threads with SkRecord.
    223   if (playback_) {
    224     return;
    225   }
    226 
    227   DCHECK(picture_);
    228   DCHECK(clones_.empty());
    229 
    230   // We can re-use this picture for one raster worker thread.
    231   raster_thread_checker_.DetachFromThread();
    232 
    233   if (num_threads > 1) {
    234     scoped_ptr<SkPicture[]> clones(new SkPicture[num_threads - 1]);
    235     picture_->clone(&clones[0], num_threads - 1);
    236 
    237     for (int i = 0; i < num_threads - 1; i++) {
    238       scoped_refptr<Picture> clone = make_scoped_refptr(
    239           new Picture(skia::AdoptRef(new SkPicture(clones[i])),
    240                       layer_rect_,
    241                       opaque_rect_,
    242                       pixel_refs_));
    243       clones_.push_back(clone);
    244 
    245       clone->EmitTraceSnapshotAlias(this);
    246       clone->raster_thread_checker_.DetachFromThread();
    247     }
    248   }
    249 }
    250 
    251 void Picture::Record(ContentLayerClient* painter,
    252                      const SkTileGridFactory::TileGridInfo& tile_grid_info,
    253                      RecordingMode recording_mode) {
    254   TRACE_EVENT2("cc",
    255                "Picture::Record",
    256                "data",
    257                AsTraceableRecordData(),
    258                "recording_mode",
    259                recording_mode);
    260 
    261   DCHECK(!picture_);
    262   DCHECK(!tile_grid_info.fTileInterval.isEmpty());
    263 
    264   SkTileGridFactory factory(tile_grid_info);
    265   SkPictureRecorder recorder;
    266 
    267   scoped_ptr<EXPERIMENTAL::SkRecording> recording;
    268 
    269   skia::RefPtr<SkCanvas> canvas;
    270   canvas = skia::SharePtr(recorder.beginRecording(
    271       layer_rect_.width(), layer_rect_.height(), &factory));
    272 
    273   ContentLayerClient::GraphicsContextStatus graphics_context_status =
    274       ContentLayerClient::GRAPHICS_CONTEXT_ENABLED;
    275 
    276   switch (recording_mode) {
    277     case RECORD_NORMALLY:
    278       // Already setup for normal recording.
    279       break;
    280     case RECORD_WITH_SK_NULL_CANVAS:
    281       canvas = skia::AdoptRef(SkCreateNullCanvas());
    282       break;
    283     case RECORD_WITH_PAINTING_DISABLED:
    284       // We pass a disable flag through the paint calls when perfromance
    285       // testing (the only time this case should ever arise) when we want to
    286       // prevent the Blink GraphicsContext object from consuming any compute
    287       // time.
    288       canvas = skia::AdoptRef(SkCreateNullCanvas());
    289       graphics_context_status = ContentLayerClient::GRAPHICS_CONTEXT_DISABLED;
    290       break;
    291     case RECORD_WITH_SKRECORD:
    292       recording.reset(new EXPERIMENTAL::SkRecording(layer_rect_.width(),
    293                                                     layer_rect_.height()));
    294       canvas = skia::SharePtr(recording->canvas());
    295       break;
    296     default:
    297       NOTREACHED();
    298   }
    299 
    300   canvas->save();
    301   canvas->translate(SkFloatToScalar(-layer_rect_.x()),
    302                     SkFloatToScalar(-layer_rect_.y()));
    303 
    304   SkRect layer_skrect = SkRect::MakeXYWH(layer_rect_.x(),
    305                                          layer_rect_.y(),
    306                                          layer_rect_.width(),
    307                                          layer_rect_.height());
    308   canvas->clipRect(layer_skrect);
    309 
    310   gfx::RectF opaque_layer_rect;
    311   painter->PaintContents(
    312       canvas.get(), layer_rect_, &opaque_layer_rect, graphics_context_status);
    313 
    314   canvas->restore();
    315   picture_ = skia::AdoptRef(recorder.endRecording());
    316   DCHECK(picture_);
    317 
    318   if (recording) {
    319     // SkRecording requires it's the only one holding onto canvas before we
    320     // may call releasePlayback().  (This helps enforce thread-safety.)
    321     canvas.clear();
    322     playback_.reset(recording->releasePlayback());
    323   }
    324 
    325   opaque_rect_ = gfx::ToEnclosedRect(opaque_layer_rect);
    326 
    327   EmitTraceSnapshot();
    328 }
    329 
    330 void Picture::GatherPixelRefs(
    331     const SkTileGridFactory::TileGridInfo& tile_grid_info) {
    332   TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
    333                "width", layer_rect_.width(),
    334                "height", layer_rect_.height());
    335 
    336   DCHECK(picture_);
    337   DCHECK(pixel_refs_.empty());
    338   if (!WillPlayBackBitmaps())
    339     return;
    340   cell_size_ = gfx::Size(
    341       tile_grid_info.fTileInterval.width() +
    342           2 * tile_grid_info.fMargin.width(),
    343       tile_grid_info.fTileInterval.height() +
    344           2 * tile_grid_info.fMargin.height());
    345   DCHECK_GT(cell_size_.width(), 0);
    346   DCHECK_GT(cell_size_.height(), 0);
    347 
    348   int min_x = std::numeric_limits<int>::max();
    349   int min_y = std::numeric_limits<int>::max();
    350   int max_x = 0;
    351   int max_y = 0;
    352 
    353   skia::DiscardablePixelRefList pixel_refs;
    354   skia::PixelRefUtils::GatherDiscardablePixelRefs(picture_.get(), &pixel_refs);
    355   for (skia::DiscardablePixelRefList::const_iterator it = pixel_refs.begin();
    356        it != pixel_refs.end();
    357        ++it) {
    358     gfx::Point min(
    359         RoundDown(static_cast<int>(it->pixel_ref_rect.x()),
    360                   cell_size_.width()),
    361         RoundDown(static_cast<int>(it->pixel_ref_rect.y()),
    362                   cell_size_.height()));
    363     gfx::Point max(
    364         RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.right())),
    365                   cell_size_.width()),
    366         RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.bottom())),
    367                   cell_size_.height()));
    368 
    369     for (int y = min.y(); y <= max.y(); y += cell_size_.height()) {
    370       for (int x = min.x(); x <= max.x(); x += cell_size_.width()) {
    371         PixelRefMapKey key(x, y);
    372         pixel_refs_[key].push_back(it->pixel_ref);
    373       }
    374     }
    375 
    376     min_x = std::min(min_x, min.x());
    377     min_y = std::min(min_y, min.y());
    378     max_x = std::max(max_x, max.x());
    379     max_y = std::max(max_y, max.y());
    380   }
    381 
    382   min_pixel_cell_ = gfx::Point(min_x, min_y);
    383   max_pixel_cell_ = gfx::Point(max_x, max_y);
    384 }
    385 
    386 int Picture::Raster(
    387     SkCanvas* canvas,
    388     SkDrawPictureCallback* callback,
    389     const Region& negated_content_region,
    390     float contents_scale) {
    391   if (!playback_)
    392     DCHECK(raster_thread_checker_.CalledOnValidThread());
    393   TRACE_EVENT_BEGIN1(
    394       "cc",
    395       "Picture::Raster",
    396       "data",
    397       AsTraceableRasterData(contents_scale));
    398 
    399   DCHECK(picture_);
    400 
    401   canvas->save();
    402 
    403   for (Region::Iterator it(negated_content_region); it.has_rect(); it.next())
    404     canvas->clipRect(gfx::RectToSkRect(it.rect()), SkRegion::kDifference_Op);
    405 
    406   canvas->scale(contents_scale, contents_scale);
    407   canvas->translate(layer_rect_.x(), layer_rect_.y());
    408   if (playback_) {
    409     playback_->draw(canvas);
    410   } else {
    411     picture_->draw(canvas, callback);
    412   }
    413   SkIRect bounds;
    414   canvas->getClipDeviceBounds(&bounds);
    415   canvas->restore();
    416   TRACE_EVENT_END1(
    417       "cc", "Picture::Raster",
    418       "num_pixels_rasterized", bounds.width() * bounds.height());
    419   return bounds.width() * bounds.height();
    420 }
    421 
    422 void Picture::Replay(SkCanvas* canvas) {
    423   if (!playback_)
    424     DCHECK(raster_thread_checker_.CalledOnValidThread());
    425   TRACE_EVENT_BEGIN0("cc", "Picture::Replay");
    426   DCHECK(picture_);
    427 
    428   if (playback_) {
    429     playback_->draw(canvas);
    430   } else {
    431     picture_->draw(canvas);
    432   }
    433   SkIRect bounds;
    434   canvas->getClipDeviceBounds(&bounds);
    435   TRACE_EVENT_END1("cc", "Picture::Replay",
    436                    "num_pixels_replayed", bounds.width() * bounds.height());
    437 }
    438 
    439 scoped_ptr<base::Value> Picture::AsValue() const {
    440   SkDynamicMemoryWStream stream;
    441 
    442   if (playback_) {
    443     // SkPlayback can't serialize itself, so re-record into an SkPicture.
    444     SkPictureRecorder recorder;
    445     skia::RefPtr<SkCanvas> canvas(skia::SharePtr(recorder.beginRecording(
    446         layer_rect_.width(),
    447         layer_rect_.height(),
    448         NULL)));  // Default (no) bounding-box hierarchy is fastest.
    449     playback_->draw(canvas.get());
    450     skia::RefPtr<SkPicture> picture(skia::AdoptRef(recorder.endRecording()));
    451     picture->serialize(&stream, &EncodeBitmap);
    452   } else {
    453     // Serialize the picture.
    454     picture_->serialize(&stream, &EncodeBitmap);
    455   }
    456 
    457   // Encode the picture as base64.
    458   scoped_ptr<base::DictionaryValue> res(new base::DictionaryValue());
    459   res->Set("params.layer_rect", MathUtil::AsValue(layer_rect_).release());
    460   res->Set("params.opaque_rect", MathUtil::AsValue(opaque_rect_).release());
    461 
    462   size_t serialized_size = stream.bytesWritten();
    463   scoped_ptr<char[]> serialized_picture(new char[serialized_size]);
    464   stream.copyTo(serialized_picture.get());
    465   std::string b64_picture;
    466   base::Base64Encode(std::string(serialized_picture.get(), serialized_size),
    467                      &b64_picture);
    468   res->SetString("skp64", b64_picture);
    469   return res.PassAs<base::Value>();
    470 }
    471 
    472 void Picture::EmitTraceSnapshot() const {
    473   TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
    474       TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
    475           "devtools.timeline.picture"),
    476       "cc::Picture",
    477       this,
    478       TracedPicture::AsTraceablePicture(this));
    479 }
    480 
    481 void Picture::EmitTraceSnapshotAlias(Picture* original) const {
    482   TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
    483       TRACE_DISABLED_BY_DEFAULT("cc.debug") "," TRACE_DISABLED_BY_DEFAULT(
    484           "devtools.timeline.picture"),
    485       "cc::Picture",
    486       this,
    487       TracedPicture::AsTraceablePictureAlias(original));
    488 }
    489 
    490 base::LazyInstance<Picture::PixelRefs>
    491     Picture::PixelRefIterator::empty_pixel_refs_;
    492 
    493 Picture::PixelRefIterator::PixelRefIterator()
    494     : picture_(NULL),
    495       current_pixel_refs_(empty_pixel_refs_.Pointer()),
    496       current_index_(0),
    497       min_point_(-1, -1),
    498       max_point_(-1, -1),
    499       current_x_(0),
    500       current_y_(0) {
    501 }
    502 
    503 Picture::PixelRefIterator::PixelRefIterator(
    504     const gfx::Rect& rect,
    505     const Picture* picture)
    506     : picture_(picture),
    507       current_pixel_refs_(empty_pixel_refs_.Pointer()),
    508       current_index_(0) {
    509   gfx::Rect layer_rect = picture->layer_rect_;
    510   gfx::Size cell_size = picture->cell_size_;
    511   DCHECK(!cell_size.IsEmpty());
    512 
    513   gfx::Rect query_rect(rect);
    514   // Early out if the query rect doesn't intersect this picture.
    515   if (!query_rect.Intersects(layer_rect)) {
    516     min_point_ = gfx::Point(0, 0);
    517     max_point_ = gfx::Point(0, 0);
    518     current_x_ = 1;
    519     current_y_ = 1;
    520     return;
    521   }
    522 
    523   // First, subtract the layer origin as cells are stored in layer space.
    524   query_rect.Offset(-layer_rect.OffsetFromOrigin());
    525 
    526   // We have to find a cell_size aligned point that corresponds to
    527   // query_rect. Point is a multiple of cell_size.
    528   min_point_ = gfx::Point(
    529       RoundDown(query_rect.x(), cell_size.width()),
    530       RoundDown(query_rect.y(), cell_size.height()));
    531   max_point_ = gfx::Point(
    532       RoundDown(query_rect.right() - 1, cell_size.width()),
    533       RoundDown(query_rect.bottom() - 1, cell_size.height()));
    534 
    535   // Limit the points to known pixel ref boundaries.
    536   min_point_ = gfx::Point(
    537       std::max(min_point_.x(), picture->min_pixel_cell_.x()),
    538       std::max(min_point_.y(), picture->min_pixel_cell_.y()));
    539   max_point_ = gfx::Point(
    540       std::min(max_point_.x(), picture->max_pixel_cell_.x()),
    541       std::min(max_point_.y(), picture->max_pixel_cell_.y()));
    542 
    543   // Make the current x be cell_size.width() less than min point, so that
    544   // the first increment will point at min_point_.
    545   current_x_ = min_point_.x() - cell_size.width();
    546   current_y_ = min_point_.y();
    547   if (current_y_ <= max_point_.y())
    548     ++(*this);
    549 }
    550 
    551 Picture::PixelRefIterator::~PixelRefIterator() {
    552 }
    553 
    554 Picture::PixelRefIterator& Picture::PixelRefIterator::operator++() {
    555   ++current_index_;
    556   // If we're not at the end of the list, then we have the next item.
    557   if (current_index_ < current_pixel_refs_->size())
    558     return *this;
    559 
    560   DCHECK(current_y_ <= max_point_.y());
    561   while (true) {
    562     gfx::Size cell_size = picture_->cell_size_;
    563 
    564     // Advance the current grid cell.
    565     current_x_ += cell_size.width();
    566     if (current_x_ > max_point_.x()) {
    567       current_y_ += cell_size.height();
    568       current_x_ = min_point_.x();
    569       if (current_y_ > max_point_.y()) {
    570         current_pixel_refs_ = empty_pixel_refs_.Pointer();
    571         current_index_ = 0;
    572         break;
    573       }
    574     }
    575 
    576     // If there are no pixel refs at this grid cell, keep incrementing.
    577     PixelRefMapKey key(current_x_, current_y_);
    578     PixelRefMap::const_iterator iter = picture_->pixel_refs_.find(key);
    579     if (iter == picture_->pixel_refs_.end())
    580       continue;
    581 
    582     // We found a non-empty list: store it and get the first pixel ref.
    583     current_pixel_refs_ = &iter->second;
    584     current_index_ = 0;
    585     break;
    586   }
    587   return *this;
    588 }
    589 
    590 scoped_refptr<base::debug::ConvertableToTraceFormat>
    591     Picture::AsTraceableRasterData(float scale) const {
    592   scoped_ptr<base::DictionaryValue> raster_data(new base::DictionaryValue());
    593   raster_data->Set("picture_id", TracedValue::CreateIDRef(this).release());
    594   raster_data->SetDouble("scale", scale);
    595   return TracedValue::FromValue(raster_data.release());
    596 }
    597 
    598 scoped_refptr<base::debug::ConvertableToTraceFormat>
    599     Picture::AsTraceableRecordData() const {
    600   scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue());
    601   record_data->Set("picture_id", TracedValue::CreateIDRef(this).release());
    602   record_data->Set("layer_rect", MathUtil::AsValue(layer_rect_).release());
    603   return TracedValue::FromValue(record_data.release());
    604 }
    605 
    606 }  // namespace cc
    607