| Index: chrome/browser/tab_contents/thumbnail_generator.cc
|
| diff --git a/chrome/browser/tab_contents/thumbnail_generator.cc b/chrome/browser/tab_contents/thumbnail_generator.cc
|
| index a26bca08c39766143ca231983fa211eaaa5bd4ae..82766c3b7a3a36d73e7c3fea91186efb6c240755 100644
|
| --- a/chrome/browser/tab_contents/thumbnail_generator.cc
|
| +++ b/chrome/browser/tab_contents/thumbnail_generator.cc
|
| @@ -17,8 +17,11 @@
|
| #include "chrome/browser/tab_contents/tab_contents.h"
|
| #include "chrome/common/notification_service.h"
|
| #include "chrome/common/property_bag.h"
|
| +#include "gfx/color_utils.h"
|
| #include "gfx/rect.h"
|
| #include "gfx/skbitmap_operations.h"
|
| +#include "skia/ext/bitmap_platform_device.h"
|
| +#include "skia/ext/image_operations.h"
|
| #include "skia/ext/platform_canvas.h"
|
| #include "third_party/skia/include/core/SkBitmap.h"
|
|
|
| @@ -97,11 +100,27 @@ WidgetThumbnail* GetDataForHost(RenderWidgetHost* host) {
|
| return GetThumbnailAccessor()->GetProperty(host->property_bag());
|
| }
|
|
|
| +// Calculates how "boring" a thumbnail is. The boring score is the
|
| +// 0,1 ranged percentage of pixels that are the most common
|
| +// luma. Higher boring scores indicate that a higher percentage of a
|
| +// bitmap are all the same brightness.
|
| +static double CalculateBoringScore(SkBitmap* bitmap) {
|
| + int histogram[256] = {0};
|
| + color_utils::BuildLumaHistogram(bitmap, histogram);
|
| +
|
| + int color_count = *std::max_element(histogram, histogram + 256);
|
| + int pixel_count = bitmap->width() * bitmap->height();
|
| + return static_cast<double>(color_count) / pixel_count;
|
| +}
|
| +
|
| // Creates a downsampled thumbnail for the given backing store. The returned
|
| // bitmap will be isNull if there was an error creating it.
|
| -SkBitmap GetBitmapForBackingStore(BackingStore* backing_store,
|
| - int desired_width,
|
| - int desired_height) {
|
| +SkBitmap GetBitmapForBackingStore(
|
| + BackingStore* backing_store,
|
| + int desired_width,
|
| + int desired_height,
|
| + int options,
|
| + ThumbnailGenerator::ThumbnailResult* thumbnail_result) {
|
| base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
|
|
|
| SkBitmap result;
|
| @@ -115,20 +134,77 @@ SkBitmap GetBitmapForBackingStore(BackingStore* backing_store,
|
| return result;
|
| const SkBitmap& bmp = temp_canvas.getTopPlatformDevice().accessBitmap(false);
|
|
|
| - // Need to resize it to the size we want, so downsample until it's
|
| - // close, and let the caller make it the exact size if desired.
|
| - result = SkBitmapOperations::DownsampleByTwoUntilSize(
|
| - bmp, desired_width, desired_height);
|
| -
|
| - // This is a bit subtle. SkBitmaps are refcounted, but the magic
|
| - // ones in PlatformCanvas can't be assigned to SkBitmap with proper
|
| - // refcounting. If the bitmap doesn't change, then the downsampler
|
| - // will return the input bitmap, which will be the reference to the
|
| - // weird PlatformCanvas one insetad of a regular one. To get a
|
| - // regular refcounted bitmap, we need to copy it.
|
| - if (bmp.width() == result.width() &&
|
| - bmp.height() == result.height())
|
| - bmp.copyTo(&result, SkBitmap::kARGB_8888_Config);
|
| + // Check if a clipped thumbnail is requested.
|
| + if (options & ThumbnailGenerator::kClippedThumbnail) {
|
| + const SkRect dest_rect = { 0, 0,
|
| + SkIntToScalar(desired_width),
|
| + SkIntToScalar(desired_height) };
|
| + const float dest_aspect = dest_rect.width() / dest_rect.height();
|
| +
|
| + // Get the src rect so that we can preserve the aspect ratio while filling
|
| + // the destination.
|
| + SkIRect src_rect;
|
| + if (bmp.width() < dest_rect.width() ||
|
| + bmp.height() < dest_rect.height()) {
|
| + // Source image is smaller: we clip the part of source image within the
|
| + // dest rect, and then stretch it to fill the dest rect. We don't respect
|
| + // the aspect ratio in this case.
|
| + src_rect.set(0, 0, static_cast<S16CPU>(dest_rect.width()),
|
| + static_cast<S16CPU>(dest_rect.height()));
|
| + if (thumbnail_result)
|
| + thumbnail_result->clip_result = ThumbnailGenerator::kSourceIsSmaller;
|
| + } else {
|
| + const float src_aspect =
|
| + static_cast<float>(bmp.width()) / bmp.height();
|
| + if (src_aspect > dest_aspect) {
|
| + // Wider than tall, clip horizontally: we center the smaller
|
| + // thumbnail in the wider screen.
|
| + S16CPU new_width = static_cast<S16CPU>(bmp.height() * dest_aspect);
|
| + S16CPU x_offset = (bmp.width() - new_width) / 2;
|
| + src_rect.set(x_offset, 0, new_width + x_offset, bmp.height());
|
| + if (thumbnail_result)
|
| + thumbnail_result->clip_result = ThumbnailGenerator::kWiderThanTall;
|
| + } else if (src_aspect < dest_aspect) {
|
| + src_rect.set(0, 0, bmp.width(),
|
| + static_cast<S16CPU>(bmp.width() / dest_aspect));
|
| + if (thumbnail_result)
|
| + thumbnail_result->clip_result = ThumbnailGenerator::kTallerThanWide;
|
| + } else {
|
| + src_rect.set(0, 0, bmp.width(), bmp.height());
|
| + if (thumbnail_result)
|
| + thumbnail_result->clip_result = ThumbnailGenerator::kNotClipped;
|
| + }
|
| + }
|
| +
|
| + SkBitmap subset;
|
| + temp_canvas.getTopPlatformDevice().accessBitmap(false).
|
| + extractSubset(&subset, src_rect);
|
| +
|
| + // Need to resize it to the size we want, so downsample until it's
|
| + // close, and let the caller make it the exact size if desired.
|
| + result = SkBitmapOperations::DownsampleByTwoUntilSize(
|
| + subset, desired_width, desired_height);
|
| + } else {
|
| + // Need to resize it to the size we want, so downsample until it's
|
| + // close, and let the caller make it the exact size if desired.
|
| + result = SkBitmapOperations::DownsampleByTwoUntilSize(
|
| + bmp, desired_width, desired_height);
|
| +
|
| + // This is a bit subtle. SkBitmaps are refcounted, but the magic
|
| + // ones in PlatformCanvas can't be assigned to SkBitmap with proper
|
| + // refcounting. If the bitmap doesn't change, then the downsampler
|
| + // will return the input bitmap, which will be the reference to the
|
| + // weird PlatformCanvas one insetad of a regular one. To get a
|
| + // regular refcounted bitmap, we need to copy it.
|
| + if (bmp.width() == result.width() &&
|
| + bmp.height() == result.height())
|
| + bmp.copyTo(&result, SkBitmap::kARGB_8888_Config);
|
| + }
|
| +
|
| + // Calculate the boring score if requested.
|
| + if (options & ThumbnailGenerator::kBoringScore && thumbnail_result) {
|
| + thumbnail_result->boring_score = CalculateBoringScore(&result);
|
| + }
|
|
|
| HISTOGRAM_TIMES(kThumbnailHistogramName,
|
| base::TimeTicks::Now() - begin_compute_thumbnail);
|
| @@ -223,7 +299,9 @@ void ThumbnailGenerator::AskForSnapshot(RenderWidgetHost* renderer,
|
| // we'll go with it.
|
| SkBitmap first_try = GetBitmapForBackingStore(backing_store,
|
| desired_size.width(),
|
| - desired_size.height());
|
| + desired_size.height(),
|
| + kNone,
|
| + NULL);
|
| callback->Run(first_try);
|
|
|
| delete callback;
|
| @@ -275,6 +353,13 @@ void ThumbnailGenerator::AskForSnapshot(RenderWidgetHost* renderer,
|
|
|
| SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(
|
| RenderWidgetHost* renderer) const {
|
| + return GetThumbnailForRendererWithOptions(renderer, kNone, NULL);
|
| +}
|
| +
|
| +SkBitmap ThumbnailGenerator::GetThumbnailForRendererWithOptions(
|
| + RenderWidgetHost* renderer,
|
| + int options,
|
| + ThumbnailResult* result) const {
|
| WidgetThumbnail* wt = GetDataForHost(renderer);
|
|
|
| BackingStore* backing_store = renderer->GetBackingStore(false);
|
| @@ -299,7 +384,9 @@ SkBitmap ThumbnailGenerator::GetThumbnailForRenderer(
|
| // invalidated on the next paint.
|
| wt->thumbnail = GetBitmapForBackingStore(backing_store,
|
| kThumbnailWidth,
|
| - kThumbnailHeight);
|
| + kThumbnailHeight,
|
| + options,
|
| + result);
|
| return wt->thumbnail;
|
| }
|
|
|
| @@ -320,7 +407,9 @@ void ThumbnailGenerator::WidgetWillDestroyBackingStore(
|
| // an existing thumbnail.
|
| SkBitmap new_thumbnail = GetBitmapForBackingStore(backing_store,
|
| kThumbnailWidth,
|
| - kThumbnailHeight);
|
| + kThumbnailHeight,
|
| + kNone,
|
| + NULL);
|
| if (!new_thumbnail.isNull())
|
| wt->thumbnail = new_thumbnail;
|
| }
|
|
|