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 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( 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 103Picture::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 110scoped_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 131scoped_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 169Picture::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 178Picture::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 189Picture::~Picture() { 190 TRACE_EVENT_OBJECT_DELETED_WITH_ID( 191 TRACE_DISABLED_BY_DEFAULT("cc.debug"), "cc::Picture", this); 192} 193 194Picture* 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 206bool 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 219void 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 251void 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 330void 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 386int 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 422void 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 439scoped_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 472void 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 481void 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 490base::LazyInstance<Picture::PixelRefs> 491 Picture::PixelRefIterator::empty_pixel_refs_; 492 493Picture::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 503Picture::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 551Picture::PixelRefIterator::~PixelRefIterator() { 552} 553 554Picture::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 590scoped_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 598scoped_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