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 c37071918801f96ded0fa3312f8f7e3d5b66f200..0a78b73fc6cd1384c034a3b54c6fd9921bbe2425 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" |
@@ -99,9 +102,12 @@ WidgetThumbnail* GetDataForHost(RenderWidgetHost* host) { |
// 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::ClipResult* clip_result) { |
base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now(); |
SkBitmap result; |
@@ -115,20 +121,31 @@ 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) { |
+ SkBitmap clipped_bitmap = ThumbnailGenerator::GetClippedBitmap( |
+ bmp, desired_width, desired_height, clip_result); |
+ |
+ // 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( |
+ clipped_bitmap, 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); |
+ } |
HISTOGRAM_TIMES(kThumbnailHistogramName, |
base::TimeTicks::Now() - begin_compute_thumbnail); |
@@ -223,7 +240,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 +294,13 @@ void ThumbnailGenerator::AskForSnapshot(RenderWidgetHost* renderer, |
SkBitmap ThumbnailGenerator::GetThumbnailForRenderer( |
RenderWidgetHost* renderer) const { |
+ return GetThumbnailForRendererWithOptions(renderer, kNone, NULL); |
+} |
+ |
+SkBitmap ThumbnailGenerator::GetThumbnailForRendererWithOptions( |
+ RenderWidgetHost* renderer, |
+ int options, |
+ ClipResult* clip_result) const { |
WidgetThumbnail* wt = GetDataForHost(renderer); |
BackingStore* backing_store = renderer->GetBackingStore(false); |
@@ -299,7 +325,9 @@ SkBitmap ThumbnailGenerator::GetThumbnailForRenderer( |
// invalidated on the next paint. |
wt->thumbnail = GetBitmapForBackingStore(backing_store, |
kThumbnailWidth, |
- kThumbnailHeight); |
+ kThumbnailHeight, |
+ options, |
+ clip_result); |
return wt->thumbnail; |
} |
@@ -320,7 +348,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; |
} |
@@ -534,3 +564,64 @@ void ThumbnailGenerator::EraseHostFromShownList(RenderWidgetHost* widget) { |
if (found != shown_hosts_.end()) |
shown_hosts_.erase(found); |
} |
+ |
+double ThumbnailGenerator::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(); |
+ if (pixel_count == 0) { |
+ return 1.0; |
+ } |
+ return static_cast<double>(color_count) / pixel_count; |
+} |
+ |
+SkBitmap ThumbnailGenerator::GetClippedBitmap(const SkBitmap& bitmap, |
+ int desired_width, |
+ int desired_height, |
+ ClipResult* clip_result) { |
+ 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 (bitmap.width() < dest_rect.width() || |
+ bitmap.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 (clip_result) |
+ *clip_result = ThumbnailGenerator::kSourceIsSmaller; |
+ } else { |
+ const float src_aspect = |
+ static_cast<float>(bitmap.width()) / bitmap.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>(bitmap.height() * dest_aspect); |
+ S16CPU x_offset = (bitmap.width() - new_width) / 2; |
+ src_rect.set(x_offset, 0, new_width + x_offset, bitmap.height()); |
+ if (clip_result) |
+ *clip_result = ThumbnailGenerator::kWiderThanTall; |
+ } else if (src_aspect < dest_aspect) { |
+ src_rect.set(0, 0, bitmap.width(), |
+ static_cast<S16CPU>(bitmap.width() / dest_aspect)); |
+ if (clip_result) |
+ *clip_result = ThumbnailGenerator::kTallerThanWide; |
+ } else { |
+ src_rect.set(0, 0, bitmap.width(), bitmap.height()); |
+ if (clip_result) |
+ *clip_result = ThumbnailGenerator::kNotClipped; |
+ } |
+ } |
+ |
+ SkBitmap clipped_bitmap; |
+ bitmap.extractSubset(&clipped_bitmap, src_rect); |
+ return clipped_bitmap; |
+} |