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/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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    173 Picture::~Picture() {
    174   TRACE_EVENT_OBJECT_DELETED_WITH_ID(
    175     TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this);
    176 }
    177 
    178 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 
    191 int Picture::ApproximateOpCount() const {
    192   DCHECK(picture_);
    193   return picture_->approximateOpCount();
    194 }
    195 
    196 bool Picture::HasText() const {
    197   DCHECK(picture_);
    198   return picture_->hasText();
    199 }
    200 
    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 
    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 
    332 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 
    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 
    386 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 
    418 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 
    427 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 
    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 
    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 
    497 Picture::PixelRefIterator::~PixelRefIterator() {
    498 }
    499 
    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>
    537     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>
    546     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