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