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; |
} |