| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/tab_contents/thumbnail_generator.h" | 5 #include "chrome/browser/tab_contents/thumbnail_generator.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <map> | 8 #include <map> |
| 9 | 9 |
| 10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
| 11 #include "base/scoped_ptr.h" | 11 #include "base/scoped_ptr.h" |
| 12 #include "base/time.h" | 12 #include "base/time.h" |
| 13 #include "build/build_config.h" | 13 #include "build/build_config.h" |
| 14 #include "chrome/browser/renderer_host/backing_store.h" | 14 #include "chrome/browser/renderer_host/backing_store.h" |
| 15 #include "chrome/browser/renderer_host/render_process_host.h" | 15 #include "chrome/browser/renderer_host/render_process_host.h" |
| 16 #include "chrome/browser/renderer_host/render_view_host.h" | 16 #include "chrome/browser/renderer_host/render_view_host.h" |
| 17 #include "chrome/browser/tab_contents/tab_contents.h" | 17 #include "chrome/browser/tab_contents/tab_contents.h" |
| 18 #include "chrome/common/notification_service.h" | 18 #include "chrome/common/notification_service.h" |
| 19 #include "chrome/common/property_bag.h" | 19 #include "chrome/common/property_bag.h" |
| 20 #include "gfx/color_utils.h" |
| 20 #include "gfx/rect.h" | 21 #include "gfx/rect.h" |
| 21 #include "gfx/skbitmap_operations.h" | 22 #include "gfx/skbitmap_operations.h" |
| 23 #include "skia/ext/bitmap_platform_device.h" |
| 24 #include "skia/ext/image_operations.h" |
| 22 #include "skia/ext/platform_canvas.h" | 25 #include "skia/ext/platform_canvas.h" |
| 23 #include "third_party/skia/include/core/SkBitmap.h" | 26 #include "third_party/skia/include/core/SkBitmap.h" |
| 24 | 27 |
| 25 #if defined(OS_WIN) | 28 #if defined(OS_WIN) |
| 26 #include "app/win/win_util.h" | 29 #include "app/win/win_util.h" |
| 27 #endif | 30 #endif |
| 28 | 31 |
| 29 // Overview | 32 // Overview |
| 30 // -------- | 33 // -------- |
| 31 // This class provides current thumbnails for tabs. The simplest operation is | 34 // This class provides current thumbnails for tabs. The simplest operation is |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 92 if (wt) | 95 if (wt) |
| 93 return wt; | 96 return wt; |
| 94 | 97 |
| 95 GetThumbnailAccessor()->SetProperty(host->property_bag(), | 98 GetThumbnailAccessor()->SetProperty(host->property_bag(), |
| 96 WidgetThumbnail()); | 99 WidgetThumbnail()); |
| 97 return GetThumbnailAccessor()->GetProperty(host->property_bag()); | 100 return GetThumbnailAccessor()->GetProperty(host->property_bag()); |
| 98 } | 101 } |
| 99 | 102 |
| 100 // Creates a downsampled thumbnail for the given backing store. The returned | 103 // Creates a downsampled thumbnail for the given backing store. The returned |
| 101 // bitmap will be isNull if there was an error creating it. | 104 // bitmap will be isNull if there was an error creating it. |
| 102 SkBitmap GetBitmapForBackingStore(BackingStore* backing_store, | 105 SkBitmap GetBitmapForBackingStore( |
| 103 int desired_width, | 106 BackingStore* backing_store, |
| 104 int desired_height) { | 107 int desired_width, |
| 108 int desired_height, |
| 109 int options, |
| 110 ThumbnailGenerator::ClipResult* clip_result) { |
| 105 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now(); | 111 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now(); |
| 106 | 112 |
| 107 SkBitmap result; | 113 SkBitmap result; |
| 108 | 114 |
| 109 // Get the bitmap as a Skia object so we can resample it. This is a large | 115 // Get the bitmap as a Skia object so we can resample it. This is a large |
| 110 // allocation and we can tolerate failure here, so give up if the allocation | 116 // allocation and we can tolerate failure here, so give up if the allocation |
| 111 // fails. | 117 // fails. |
| 112 skia::PlatformCanvas temp_canvas; | 118 skia::PlatformCanvas temp_canvas; |
| 113 if (!backing_store->CopyFromBackingStore(gfx::Rect(backing_store->size()), | 119 if (!backing_store->CopyFromBackingStore(gfx::Rect(backing_store->size()), |
| 114 &temp_canvas)) | 120 &temp_canvas)) |
| 115 return result; | 121 return result; |
| 116 const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false); | 122 const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false); |
| 117 | 123 |
| 118 // Need to resize it to the size we want, so downsample until it's | 124 // Check if a clipped thumbnail is requested. |
| 119 // close, and let the caller make it the exact size if desired. | 125 if (options & ThumbnailGenerator::kClippedThumbnail) { |
| 120 result = SkBitmapOperations::DownsampleByTwoUntilSize( | 126 SkBitmap clipped_bitmap = ThumbnailGenerator::GetClippedBitmap( |
| 121 bmp, desired_width, desired_height); | 127 bmp, desired_width, desired_height, clip_result); |
| 122 | 128 |
| 123 // This is a bit subtle. SkBitmaps are refcounted, but the magic | 129 // Need to resize it to the size we want, so downsample until it's |
| 124 // ones in PlatformCanvas can't be assigned to SkBitmap with proper | 130 // close, and let the caller make it the exact size if desired. |
| 125 // refcounting. If the bitmap doesn't change, then the downsampler | 131 result = SkBitmapOperations::DownsampleByTwoUntilSize( |
| 126 // will return the input bitmap, which will be the reference to the | 132 clipped_bitmap, desired_width, desired_height); |
| 127 // weird PlatformCanvas one insetad of a regular one. To get a | 133 } else { |
| 128 // regular refcounted bitmap, we need to copy it. | 134 // Need to resize it to the size we want, so downsample until it's |
| 129 if (bmp.width() == result.width() && | 135 // close, and let the caller make it the exact size if desired. |
| 130 bmp.height() == result.height()) | 136 result = SkBitmapOperations::DownsampleByTwoUntilSize( |
| 131 bmp.copyTo(&result, SkBitmap::kARGB_8888_Config); | 137 bmp, desired_width, desired_height); |
| 138 |
| 139 // This is a bit subtle. SkBitmaps are refcounted, but the magic |
| 140 // ones in PlatformCanvas can't be assigned to SkBitmap with proper |
| 141 // refcounting. If the bitmap doesn't change, then the downsampler |
| 142 // will return the input bitmap, which will be the reference to the |
| 143 // weird PlatformCanvas one insetad of a regular one. To get a |
| 144 // regular refcounted bitmap, we need to copy it. |
| 145 if (bmp.width() == result.width() && |
| 146 bmp.height() == result.height()) |
| 147 bmp.copyTo(&result, SkBitmap::kARGB_8888_Config); |
| 148 } |
| 132 | 149 |
| 133 HISTOGRAM_TIMES(kThumbnailHistogramName, | 150 HISTOGRAM_TIMES(kThumbnailHistogramName, |
| 134 base::TimeTicks::Now() - begin_compute_thumbnail); | 151 base::TimeTicks::Now() - begin_compute_thumbnail); |
| 135 return result; | 152 return result; |
| 136 } | 153 } |
| 137 | 154 |
| 138 } // namespace | 155 } // namespace |
| 139 | 156 |
| 140 struct ThumbnailGenerator::AsyncRequestInfo { | 157 struct ThumbnailGenerator::AsyncRequestInfo { |
| 141 scoped_ptr<ThumbnailReadyCallback> callback; | 158 scoped_ptr<ThumbnailReadyCallback> callback; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 ThumbnailReadyCallback* callback, | 233 ThumbnailReadyCallback* callback, |
| 217 gfx::Size page_size, | 234 gfx::Size page_size, |
| 218 gfx::Size desired_size) { | 235 gfx::Size desired_size) { |
| 219 if (prefer_backing_store) { | 236 if (prefer_backing_store) { |
| 220 BackingStore* backing_store = renderer->GetBackingStore(false); | 237 BackingStore* backing_store = renderer->GetBackingStore(false); |
| 221 if (backing_store) { | 238 if (backing_store) { |
| 222 // We were able to find a non-null backing store for this renderer, so | 239 // We were able to find a non-null backing store for this renderer, so |
| 223 // we'll go with it. | 240 // we'll go with it. |
| 224 SkBitmap first_try = GetBitmapForBackingStore(backing_store, | 241 SkBitmap first_try = GetBitmapForBackingStore(backing_store, |
| 225 desired_size.width(), | 242 desired_size.width(), |
| 226 desired_size.height()); | 243 desired_size.height(), |
| 244 kNone, |
| 245 NULL); |
| 227 callback->Run(first_try); | 246 callback->Run(first_try); |
| 228 | 247 |
| 229 delete callback; | 248 delete callback; |
| 230 return; | 249 return; |
| 231 } | 250 } |
| 232 // Now, if the backing store didn't exist, we will still try and | 251 // Now, if the backing store didn't exist, we will still try and |
| 233 // render asynchronously. | 252 // render asynchronously. |
| 234 } | 253 } |
| 235 | 254 |
| 236 // We are going to render the thumbnail asynchronously now, so keep | 255 // We are going to render the thumbnail asynchronously now, so keep |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 268 NOTREACHED() << "Callback already registered?"; | 287 NOTREACHED() << "Callback already registered?"; |
| 269 return; | 288 return; |
| 270 } | 289 } |
| 271 | 290 |
| 272 renderer->PaintAtSize( | 291 renderer->PaintAtSize( |
| 273 renderer_dib_handle, sequence_num, page_size, desired_size); | 292 renderer_dib_handle, sequence_num, page_size, desired_size); |
| 274 } | 293 } |
| 275 | 294 |
| 276 SkBitmap ThumbnailGenerator::GetThumbnailForRenderer( | 295 SkBitmap ThumbnailGenerator::GetThumbnailForRenderer( |
| 277 RenderWidgetHost* renderer) const { | 296 RenderWidgetHost* renderer) const { |
| 297 return GetThumbnailForRendererWithOptions(renderer, kNone, NULL); |
| 298 } |
| 299 |
| 300 SkBitmap ThumbnailGenerator::GetThumbnailForRendererWithOptions( |
| 301 RenderWidgetHost* renderer, |
| 302 int options, |
| 303 ClipResult* clip_result) const { |
| 278 WidgetThumbnail* wt = GetDataForHost(renderer); | 304 WidgetThumbnail* wt = GetDataForHost(renderer); |
| 279 | 305 |
| 280 BackingStore* backing_store = renderer->GetBackingStore(false); | 306 BackingStore* backing_store = renderer->GetBackingStore(false); |
| 281 if (!backing_store) { | 307 if (!backing_store) { |
| 282 // When we have no backing store, there's no choice in what to use. We | 308 // When we have no backing store, there's no choice in what to use. We |
| 283 // have to return either the existing thumbnail or the empty one if there | 309 // have to return either the existing thumbnail or the empty one if there |
| 284 // isn't a saved one. | 310 // isn't a saved one. |
| 285 return wt->thumbnail; | 311 return wt->thumbnail; |
| 286 } | 312 } |
| 287 | 313 |
| 288 // Now that we have a backing store, we have a choice to use it to make | 314 // Now that we have a backing store, we have a choice to use it to make |
| 289 // a new thumbnail, or use a previously stashed one if we have it. | 315 // a new thumbnail, or use a previously stashed one if we have it. |
| 290 // | 316 // |
| 291 // Return the previously-computed one if we have it and it hasn't expired. | 317 // Return the previously-computed one if we have it and it hasn't expired. |
| 292 if (!wt->thumbnail.isNull() && | 318 if (!wt->thumbnail.isNull() && |
| 293 (no_timeout_ || | 319 (no_timeout_ || |
| 294 base::TimeTicks::Now() - | 320 base::TimeTicks::Now() - |
| 295 base::TimeDelta::FromMilliseconds(kVisibilitySlopMS) < wt->last_shown)) | 321 base::TimeDelta::FromMilliseconds(kVisibilitySlopMS) < wt->last_shown)) |
| 296 return wt->thumbnail; | 322 return wt->thumbnail; |
| 297 | 323 |
| 298 // Save this thumbnail in case we need to use it again soon. It will be | 324 // Save this thumbnail in case we need to use it again soon. It will be |
| 299 // invalidated on the next paint. | 325 // invalidated on the next paint. |
| 300 wt->thumbnail = GetBitmapForBackingStore(backing_store, | 326 wt->thumbnail = GetBitmapForBackingStore(backing_store, |
| 301 kThumbnailWidth, | 327 kThumbnailWidth, |
| 302 kThumbnailHeight); | 328 kThumbnailHeight, |
| 329 options, |
| 330 clip_result); |
| 303 return wt->thumbnail; | 331 return wt->thumbnail; |
| 304 } | 332 } |
| 305 | 333 |
| 306 void ThumbnailGenerator::WidgetWillDestroyBackingStore( | 334 void ThumbnailGenerator::WidgetWillDestroyBackingStore( |
| 307 RenderWidgetHost* widget, | 335 RenderWidgetHost* widget, |
| 308 BackingStore* backing_store) { | 336 BackingStore* backing_store) { |
| 309 // Since the backing store is going away, we need to save it as a thumbnail. | 337 // Since the backing store is going away, we need to save it as a thumbnail. |
| 310 WidgetThumbnail* wt = GetDataForHost(widget); | 338 WidgetThumbnail* wt = GetDataForHost(widget); |
| 311 | 339 |
| 312 // If there is already a thumbnail on the RWH that's visible, it means that | 340 // If there is already a thumbnail on the RWH that's visible, it means that |
| 313 // not enough time has elapsed since being shown, and we can ignore generating | 341 // not enough time has elapsed since being shown, and we can ignore generating |
| 314 // a new one. | 342 // a new one. |
| 315 if (!wt->thumbnail.isNull()) | 343 if (!wt->thumbnail.isNull()) |
| 316 return; | 344 return; |
| 317 | 345 |
| 318 // Save a scaled-down image of the page in case we're asked for the thumbnail | 346 // Save a scaled-down image of the page in case we're asked for the thumbnail |
| 319 // when there is no RenderViewHost. If this fails, we don't want to overwrite | 347 // when there is no RenderViewHost. If this fails, we don't want to overwrite |
| 320 // an existing thumbnail. | 348 // an existing thumbnail. |
| 321 SkBitmap new_thumbnail = GetBitmapForBackingStore(backing_store, | 349 SkBitmap new_thumbnail = GetBitmapForBackingStore(backing_store, |
| 322 kThumbnailWidth, | 350 kThumbnailWidth, |
| 323 kThumbnailHeight); | 351 kThumbnailHeight, |
| 352 kNone, |
| 353 NULL); |
| 324 if (!new_thumbnail.isNull()) | 354 if (!new_thumbnail.isNull()) |
| 325 wt->thumbnail = new_thumbnail; | 355 wt->thumbnail = new_thumbnail; |
| 326 } | 356 } |
| 327 | 357 |
| 328 void ThumbnailGenerator::WidgetDidUpdateBackingStore(RenderWidgetHost* widget) { | 358 void ThumbnailGenerator::WidgetDidUpdateBackingStore(RenderWidgetHost* widget) { |
| 329 // Notify interested parties that they might want to update their | 359 // Notify interested parties that they might want to update their |
| 330 // snapshots. | 360 // snapshots. |
| 331 NotificationService::current()->Notify( | 361 NotificationService::current()->Notify( |
| 332 NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED, | 362 NotificationType::THUMBNAIL_GENERATOR_SNAPSHOT_CHANGED, |
| 333 Source<ThumbnailGenerator>(this), | 363 Source<ThumbnailGenerator>(this), |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 &ThumbnailGenerator::ShownDelayHandler); | 557 &ThumbnailGenerator::ShownDelayHandler); |
| 528 } | 558 } |
| 529 } | 559 } |
| 530 | 560 |
| 531 void ThumbnailGenerator::EraseHostFromShownList(RenderWidgetHost* widget) { | 561 void ThumbnailGenerator::EraseHostFromShownList(RenderWidgetHost* widget) { |
| 532 std::vector<RenderWidgetHost*>::iterator found = | 562 std::vector<RenderWidgetHost*>::iterator found = |
| 533 std::find(shown_hosts_.begin(), shown_hosts_.end(), widget); | 563 std::find(shown_hosts_.begin(), shown_hosts_.end(), widget); |
| 534 if (found != shown_hosts_.end()) | 564 if (found != shown_hosts_.end()) |
| 535 shown_hosts_.erase(found); | 565 shown_hosts_.erase(found); |
| 536 } | 566 } |
| 567 |
| 568 double ThumbnailGenerator::CalculateBoringScore(SkBitmap* bitmap) { |
| 569 int histogram[256] = {0}; |
| 570 color_utils::BuildLumaHistogram(bitmap, histogram); |
| 571 |
| 572 int color_count = *std::max_element(histogram, histogram + 256); |
| 573 int pixel_count = bitmap->width() * bitmap->height(); |
| 574 if (pixel_count == 0) { |
| 575 return 1.0; |
| 576 } |
| 577 return static_cast<double>(color_count) / pixel_count; |
| 578 } |
| 579 |
| 580 SkBitmap ThumbnailGenerator::GetClippedBitmap(const SkBitmap& bitmap, |
| 581 int desired_width, |
| 582 int desired_height, |
| 583 ClipResult* clip_result) { |
| 584 const SkRect dest_rect = { 0, 0, |
| 585 SkIntToScalar(desired_width), |
| 586 SkIntToScalar(desired_height) }; |
| 587 const float dest_aspect = dest_rect.width() / dest_rect.height(); |
| 588 |
| 589 // Get the src rect so that we can preserve the aspect ratio while filling |
| 590 // the destination. |
| 591 SkIRect src_rect; |
| 592 if (bitmap.width() < dest_rect.width() || |
| 593 bitmap.height() < dest_rect.height()) { |
| 594 // Source image is smaller: we clip the part of source image within the |
| 595 // dest rect, and then stretch it to fill the dest rect. We don't respect |
| 596 // the aspect ratio in this case. |
| 597 src_rect.set(0, 0, static_cast<S16CPU>(dest_rect.width()), |
| 598 static_cast<S16CPU>(dest_rect.height())); |
| 599 if (clip_result) |
| 600 *clip_result = ThumbnailGenerator::kSourceIsSmaller; |
| 601 } else { |
| 602 const float src_aspect = |
| 603 static_cast<float>(bitmap.width()) / bitmap.height(); |
| 604 if (src_aspect > dest_aspect) { |
| 605 // Wider than tall, clip horizontally: we center the smaller |
| 606 // thumbnail in the wider screen. |
| 607 S16CPU new_width = static_cast<S16CPU>(bitmap.height() * dest_aspect); |
| 608 S16CPU x_offset = (bitmap.width() - new_width) / 2; |
| 609 src_rect.set(x_offset, 0, new_width + x_offset, bitmap.height()); |
| 610 if (clip_result) |
| 611 *clip_result = ThumbnailGenerator::kWiderThanTall; |
| 612 } else if (src_aspect < dest_aspect) { |
| 613 src_rect.set(0, 0, bitmap.width(), |
| 614 static_cast<S16CPU>(bitmap.width() / dest_aspect)); |
| 615 if (clip_result) |
| 616 *clip_result = ThumbnailGenerator::kTallerThanWide; |
| 617 } else { |
| 618 src_rect.set(0, 0, bitmap.width(), bitmap.height()); |
| 619 if (clip_result) |
| 620 *clip_result = ThumbnailGenerator::kNotClipped; |
| 621 } |
| 622 } |
| 623 |
| 624 SkBitmap clipped_bitmap; |
| 625 bitmap.extractSubset(&clipped_bitmap, src_rect); |
| 626 return clipped_bitmap; |
| 627 } |
| OLD | NEW |