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/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 
     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(gfx::Rect layer_rect) {
     87   return make_scoped_refptr(new Picture(layer_rect));
     88 }
     89 
     90 Picture::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 
     96 scoped_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 
    134 Picture::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 
    142 Picture::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 
    152 Picture::~Picture() {
    153   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
    154     TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this);
    155 }
    156 
    157 scoped_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 
    165 void 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 
    185 void Picture::Record(ContentLayerClient* painter,
    186                      const SkTileGridPicture::TileGridInfo& tile_grid_info,
    187                      RenderingStatsInstrumentation* stats_instrumentation) {
    188   TRACE_EVENT1(benchmark_instrumentation::kCategory,
    189                benchmark_instrumentation::kPictureRecord,
    190                benchmark_instrumentation::kData, AsTraceableRecordData());
    191 
    192   DCHECK(!tile_grid_info.fTileInterval.isEmpty());
    193   picture_ = skia::AdoptRef(new SkTileGridPicture(
    194       layer_rect_.width(), layer_rect_.height(), tile_grid_info));
    195 
    196   SkCanvas* canvas = picture_->beginRecording(
    197       layer_rect_.width(),
    198       layer_rect_.height(),
    199       SkPicture::kUsePathBoundsForClip_RecordingFlag |
    200       SkPicture::kOptimizeForClippedPlayback_RecordingFlag);
    201 
    202   canvas->save();
    203   canvas->translate(SkFloatToScalar(-layer_rect_.x()),
    204                     SkFloatToScalar(-layer_rect_.y()));
    205 
    206   SkRect layer_skrect = SkRect::MakeXYWH(layer_rect_.x(),
    207                                          layer_rect_.y(),
    208                                          layer_rect_.width(),
    209                                          layer_rect_.height());
    210   canvas->clipRect(layer_skrect);
    211 
    212   gfx::RectF opaque_layer_rect;
    213   base::TimeTicks start_time = stats_instrumentation->StartRecording();
    214 
    215   painter->PaintContents(canvas, layer_rect_, &opaque_layer_rect);
    216 
    217   base::TimeDelta duration = stats_instrumentation->EndRecording(start_time);
    218   stats_instrumentation->AddRecord(duration,
    219                                    layer_rect_.width() * layer_rect_.height());
    220 
    221   canvas->restore();
    222   picture_->endRecording();
    223 
    224   opaque_rect_ = gfx::ToEnclosedRect(opaque_layer_rect);
    225 
    226   EmitTraceSnapshot();
    227 }
    228 
    229 void Picture::GatherPixelRefs(
    230     const SkTileGridPicture::TileGridInfo& tile_grid_info,
    231     RenderingStatsInstrumentation* stats_instrumentation) {
    232   TRACE_EVENT2("cc", "Picture::GatherPixelRefs",
    233                "width", layer_rect_.width(),
    234                "height", layer_rect_.height());
    235 
    236   DCHECK(picture_);
    237   cell_size_ = gfx::Size(
    238       tile_grid_info.fTileInterval.width() +
    239           2 * tile_grid_info.fMargin.width(),
    240       tile_grid_info.fTileInterval.height() +
    241           2 * tile_grid_info.fMargin.height());
    242   DCHECK_GT(cell_size_.width(), 0);
    243   DCHECK_GT(cell_size_.height(), 0);
    244 
    245   int min_x = std::numeric_limits<int>::max();
    246   int min_y = std::numeric_limits<int>::max();
    247   int max_x = 0;
    248   int max_y = 0;
    249 
    250   base::TimeTicks start_time = stats_instrumentation->StartRecording();
    251 
    252   skia::LazyPixelRefList pixel_refs;
    253   skia::LazyPixelRefUtils::GatherPixelRefs(picture_.get(), &pixel_refs);
    254   for (skia::LazyPixelRefList::const_iterator it = pixel_refs.begin();
    255        it != pixel_refs.end();
    256        ++it) {
    257     gfx::Point min(
    258         RoundDown(static_cast<int>(it->pixel_ref_rect.x()),
    259                   cell_size_.width()),
    260         RoundDown(static_cast<int>(it->pixel_ref_rect.y()),
    261                   cell_size_.height()));
    262     gfx::Point max(
    263         RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.right())),
    264                   cell_size_.width()),
    265         RoundDown(static_cast<int>(std::ceil(it->pixel_ref_rect.bottom())),
    266                   cell_size_.height()));
    267 
    268     for (int y = min.y(); y <= max.y(); y += cell_size_.height()) {
    269       for (int x = min.x(); x <= max.x(); x += cell_size_.width()) {
    270         PixelRefMapKey key(x, y);
    271         pixel_refs_[key].push_back(it->lazy_pixel_ref);
    272       }
    273     }
    274 
    275     min_x = std::min(min_x, min.x());
    276     min_y = std::min(min_y, min.y());
    277     max_x = std::max(max_x, max.x());
    278     max_y = std::max(max_y, max.y());
    279   }
    280 
    281   base::TimeDelta duration = stats_instrumentation->EndRecording(start_time);
    282   stats_instrumentation->AddImageGathering(duration);
    283 
    284   min_pixel_cell_ = gfx::Point(min_x, min_y);
    285   max_pixel_cell_ = gfx::Point(max_x, max_y);
    286 }
    287 
    288 void Picture::Raster(
    289     SkCanvas* canvas,
    290     SkDrawPictureCallback* callback,
    291     gfx::Rect content_rect,
    292     float contents_scale) {
    293   TRACE_EVENT_BEGIN1(benchmark_instrumentation::kCategory,
    294                      benchmark_instrumentation::kPictureRaster,
    295                      "data",
    296                      AsTraceableRasterData(content_rect, contents_scale));
    297 
    298   DCHECK(picture_);
    299 
    300   canvas->save();
    301   canvas->clipRect(gfx::RectToSkRect(content_rect));
    302   canvas->scale(contents_scale, contents_scale);
    303   canvas->translate(layer_rect_.x(), layer_rect_.y());
    304   picture_->draw(canvas, callback);
    305   SkIRect bounds;
    306   canvas->getClipDeviceBounds(&bounds);
    307   canvas->restore();
    308   TRACE_EVENT_END1(benchmark_instrumentation::kCategory,
    309                    benchmark_instrumentation::kPictureRaster,
    310                    benchmark_instrumentation::kNumPixelsRasterized,
    311                    bounds.width() * bounds.height());
    312 }
    313 
    314 void 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 
    325 scoped_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 
    346 void Picture::EmitTraceSnapshot() {
    347   TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
    348       "cc::Picture", this, TracedPicture::AsTraceablePicture(this));
    349 }
    350 
    351 base::LazyInstance<Picture::PixelRefs>
    352     Picture::PixelRefIterator::empty_pixel_refs_;
    353 
    354 Picture::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 
    364 Picture::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 
    410 Picture::PixelRefIterator::~PixelRefIterator() {
    411 }
    412 
    413 Picture::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 
    449 scoped_ptr<base::debug::ConvertableToTraceFormat>
    450     Picture::AsTraceableRasterData(gfx::Rect rect, float scale) const {
    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 scoped_ptr<base::debug::ConvertableToTraceFormat>
    462     Picture::AsTraceableRecordData() const {
    463   scoped_ptr<base::DictionaryValue> record_data(new base::DictionaryValue());
    464   record_data->Set("picture_id", TracedValue::CreateIDRef(this).release());
    465   record_data->SetInteger(benchmark_instrumentation::kWidth,
    466                           layer_rect_.width());
    467   record_data->SetInteger(benchmark_instrumentation::kHeight,
    468                           layer_rect_.height());
    469   return TracedValue::FromValue(record_data.release());
    470 }
    471 
    472 }  // namespace cc
    473