| Index: chrome/browser/thumbnails/thumbnail_tab_helper.cc
|
| diff --git a/chrome/browser/thumbnails/thumbnail_tab_helper.cc b/chrome/browser/thumbnails/thumbnail_tab_helper.cc
|
| index 64837767c74ea7a67d31279d301d89bf38a5d5bf..40f8d8e998e92c571f48fbb2104d5a4b62a9b2c8 100644
|
| --- a/chrome/browser/thumbnails/thumbnail_tab_helper.cc
|
| +++ b/chrome/browser/thumbnails/thumbnail_tab_helper.cc
|
| @@ -4,12 +4,13 @@
|
|
|
| #include "chrome/browser/thumbnails/thumbnail_tab_helper.h"
|
|
|
| -#include "base/metrics/histogram.h"
|
| #include "chrome/browser/browser_process.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| #include "chrome/browser/thumbnails/render_widget_snapshot_taker.h"
|
| #include "chrome/browser/thumbnails/thumbnail_service.h"
|
| #include "chrome/browser/thumbnails/thumbnail_service_factory.h"
|
| +#include "chrome/browser/thumbnails/thumbnailing_algorithm.h"
|
| +#include "chrome/browser/thumbnails/thumbnailing_context.h"
|
| #include "content/public/browser/notification_details.h"
|
| #include "content/public/browser/notification_source.h"
|
| #include "content/public/browser/notification_types.h"
|
| @@ -45,191 +46,85 @@ using content::RenderViewHost;
|
| using content::RenderWidgetHost;
|
| using content::WebContents;
|
|
|
| -namespace {
|
| -
|
| -// The thumbnail size in DIP.
|
| -static const int kThumbnailWidth = 212;
|
| -static const int kThumbnailHeight = 132;
|
| +using thumbnails::ClipResult;
|
| +using thumbnails::ThumbnailingContext;
|
| +using thumbnails::ThumbnailingAlgorithm;
|
|
|
| -static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS";
|
| +namespace {
|
|
|
| -// Returns the size used by RenderWidgetHost::CopyFromBackingStore.
|
| -//
|
| -// The size is calculated in such a way that the copied size in pixel becomes
|
| -// equal to (f * kThumbnailWidth, f * kThumbnailHeight), where f is the scale
|
| -// of ui::SCALE_FACTOR_200P. Since RenderWidgetHost::CopyFromBackingStore takes
|
| -// the size in DIP, we need to adjust the size based on |view|'s device scale
|
| -// factor in order to copy the pixels with the size above.
|
| -//
|
| -// The copied size was chosen for the following reasons.
|
| -//
|
| -// 1. When the scale factor of the primary monitor is ui::SCALE_FACTOR_200P, the
|
| -// generated thumbnail size is (f * kThumbnailWidth, f * kThumbnailHeight).
|
| -// In order to avoid degrading the image quality by magnification, the size
|
| -// of the copied pixels should be equal to or larger than this thumbnail size.
|
| -//
|
| -// 2. RenderWidgetHost::CopyFromBackingStore can be costly especially when
|
| -// it is necessary to read back the web contents image data from GPU. As the
|
| -// cost is roughly propotional to the number of the copied pixels, the size of
|
| -// the copied pixels should be as small as possible.
|
| -//
|
| -// When the scale factor of the primary monitor is ui::SCALE_FACTOR_100P,
|
| -// we still copy the pixels with the same size as ui::SCALE_FACTOR_200P because
|
| -// the resampling method used in RenderWidgetHost::CopyFromBackingStore is not
|
| -// good enough for the resampled image to be used directly for the thumbnail
|
| -// (http://crbug.com/141235). We assume this is not an issue in case of
|
| -// ui::SCALE_FACTOR_200P because the high resolution thumbnail on high density
|
| -// display alleviates the aliasing.
|
| -// TODO(mazda): Copy the pixels with the smaller size in the case of
|
| -// ui::SCALE_FACTOR_100P once the resampling method has been improved.
|
| -gfx::Size GetCopySizeForThumbnail(content::RenderWidgetHostView* view) {
|
| - gfx::Size copy_size(kThumbnailWidth, kThumbnailHeight);
|
| - ui::ScaleFactor scale_factor =
|
| - ui::GetScaleFactorForNativeView(view->GetNativeView());
|
| - switch (scale_factor) {
|
| - case ui::SCALE_FACTOR_100P:
|
| - copy_size = gfx::ToFlooredSize(gfx::ScaleSize(
|
| - copy_size, ui::GetScaleFactorScale(ui::SCALE_FACTOR_200P)));
|
| - break;
|
| - case ui::SCALE_FACTOR_200P:
|
| - // Use the size as-is.
|
| - break;
|
| - default:
|
| - DLOG(WARNING) << "Unsupported scale factor. Use the same copy size as "
|
| - << "ui::SCALE_FACTOR_100P";
|
| - copy_size = gfx::ToFlooredSize(gfx::ScaleSize(
|
| - copy_size, ui::GetScaleFactorScale(ui::SCALE_FACTOR_200P)));
|
| - break;
|
| - }
|
| - return copy_size;
|
| +// Feed the constructed thumbnail to the thumbnail service.
|
| +void UpdateThumbnail(const ThumbnailingContext& context,
|
| + const SkBitmap& thumbnail) {
|
| + gfx::Image image(thumbnail);
|
| + context.service->SetPageThumbnail(context, image);
|
| + VLOG(1) << "Thumbnail taken for " << context.url << ": "
|
| + << context.score.ToString();
|
| }
|
|
|
| -// Returns the size of the thumbnail stored in the database in pixel.
|
| -gfx::Size GetThumbnailSizeInPixel() {
|
| - gfx::Size thumbnail_size(kThumbnailWidth, kThumbnailHeight);
|
| - // Determine the resolution of the thumbnail based on the maximum scale
|
| - // factor.
|
| - // TODO(mazda|oshima): Update thumbnail when the max scale factor changes.
|
| - // crbug.com/159157.
|
| - float max_scale_factor =
|
| - ui::GetScaleFactorScale(ui::GetMaxScaleFactor());
|
| - return gfx::ToFlooredSize(gfx::ScaleSize(thumbnail_size, max_scale_factor));
|
| -}
|
| +void ProcessCanvas(ThumbnailingContext* context,
|
| + ThumbnailingAlgorithm* algorithm,
|
| + skia::PlatformBitmap* temp_bitmap,
|
| + bool succeeded) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + if (!succeeded)
|
| + return;
|
|
|
| -// Returns the clipping rectangle that is used for creating a thumbnail with
|
| -// the size of |desired_size| from the bitmap with the size of |source_size|.
|
| -// The type of clipping that needs to be done is assigned to |clip_result|.
|
| -gfx::Rect GetClippingRect(const gfx::Size& source_size,
|
| - const gfx::Size& desired_size,
|
| - ThumbnailTabHelper::ClipResult* clip_result) {
|
| - DCHECK(clip_result);
|
| -
|
| - float desired_aspect =
|
| - static_cast<float>(desired_size.width()) / desired_size.height();
|
| -
|
| - // Get the clipping rect so that we can preserve the aspect ratio while
|
| - // filling the destination.
|
| - gfx::Rect clipping_rect;
|
| - if (source_size.width() < desired_size.width() ||
|
| - source_size.height() < desired_size.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.
|
| - clipping_rect = gfx::Rect(desired_size);
|
| - *clip_result = ThumbnailTabHelper::kSourceIsSmaller;
|
| - } else {
|
| - float src_aspect =
|
| - static_cast<float>(source_size.width()) / source_size.height();
|
| - if (src_aspect > desired_aspect) {
|
| - // Wider than tall, clip horizontally: we center the smaller
|
| - // thumbnail in the wider screen.
|
| - int new_width = static_cast<int>(source_size.height() * desired_aspect);
|
| - int x_offset = (source_size.width() - new_width) / 2;
|
| - clipping_rect.SetRect(x_offset, 0, new_width, source_size.height());
|
| - *clip_result = (src_aspect >= ThumbnailScore::kTooWideAspectRatio) ?
|
| - ThumbnailTabHelper::kTooWiderThanTall :
|
| - ThumbnailTabHelper::kWiderThanTall;
|
| - } else if (src_aspect < desired_aspect) {
|
| - clipping_rect =
|
| - gfx::Rect(source_size.width(), source_size.width() / desired_aspect);
|
| - *clip_result = ThumbnailTabHelper::kTallerThanWide;
|
| - } else {
|
| - clipping_rect = gfx::Rect(source_size);
|
| - *clip_result = ThumbnailTabHelper::kNotClipped;
|
| - }
|
| - }
|
| - return clipping_rect;
|
| + SkBitmap bitmap = temp_bitmap->GetBitmap();
|
| + algorithm->ProcessBitmap(context, base::Bind(&UpdateThumbnail), bitmap);
|
| }
|
|
|
| -// Creates a downsampled thumbnail from the given bitmap.
|
| -// store. The returned bitmap will be isNull if there was an error creating it.
|
| -SkBitmap CreateThumbnail(
|
| - const SkBitmap& bitmap,
|
| - const gfx::Size& desired_size,
|
| - ThumbnailTabHelper::ClipResult* clip_result) {
|
| - base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
|
| -
|
| - SkBitmap clipped_bitmap;
|
| - if (*clip_result == ThumbnailTabHelper::kUnprocessed) {
|
| - // Clip the pixels that will commonly hold a scrollbar, which looks bad in
|
| - // thumbnails.
|
| - int scrollbar_size = gfx::scrollbar_size();
|
| - SkIRect scrollbarless_rect =
|
| - { 0, 0,
|
| - std::max(1, bitmap.width() - scrollbar_size),
|
| - std::max(1, bitmap.height() - scrollbar_size) };
|
| - SkBitmap bmp;
|
| - bitmap.extractSubset(&bmp, scrollbarless_rect);
|
| -
|
| - clipped_bitmap = ThumbnailTabHelper::GetClippedBitmap(
|
| - bmp, desired_size.width(), desired_size.height(), clip_result);
|
| - } else {
|
| - clipped_bitmap = bitmap;
|
| +void AsyncProcessThumbnail(content::WebContents* web_contents,
|
| + scoped_refptr<ThumbnailingContext> context,
|
| + scoped_refptr<ThumbnailingAlgorithm> algorithm) {
|
| + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| + RenderWidgetHost* render_widget_host = web_contents->GetRenderViewHost();
|
| + content::RenderWidgetHostView* view = render_widget_host->GetView();
|
| + if (!view)
|
| + return;
|
| + if (!view->IsSurfaceAvailableForCopy()) {
|
| +#if defined(OS_WIN)
|
| + // On Windows XP, neither the backing store nor the compositing surface is
|
| + // available in the browser when accelerated compositing is active, so ask
|
| + // the renderer to send a snapshot for creating the thumbnail.
|
| + if (base::win::GetVersion() < base::win::VERSION_VISTA) {
|
| + gfx::Size view_size =
|
| + render_widget_host->GetView()->GetViewBounds().size();
|
| + g_browser_process->GetRenderWidgetSnapshotTaker()->AskForSnapshot(
|
| + render_widget_host,
|
| + base::Bind(&ThumbnailingAlgorithm::ProcessBitmap,
|
| + algorithm, context, base::Bind(&UpdateThumbnail)),
|
| + view_size,
|
| + view_size);
|
| + }
|
| +#endif
|
| + return;
|
| }
|
|
|
| - // 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.
|
| - SkBitmap result = SkBitmapOperations::DownsampleByTwoUntilSize(
|
| - clipped_bitmap, desired_size.width(), desired_size.height());
|
| -#if !defined(USE_AURA)
|
| - // 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.
|
| - //
|
| - // On Aura, the PlatformCanvas is platform-independent and does not have
|
| - // any native platform resources that can't be refounted, so this issue does
|
| - // not occur.
|
| - //
|
| - // Note that GetClippedBitmap() does extractSubset() but it won't copy
|
| - // the pixels, hence we check result size == clipped_bitmap size here.
|
| - if (clipped_bitmap.width() == result.width() &&
|
| - clipped_bitmap.height() == result.height())
|
| - clipped_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config);
|
| -#endif
|
| + gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
|
| + // Clip the pixels that will commonly hold a scrollbar, which looks bad in
|
| + // thumbnails.
|
| + int scrollbar_size = gfx::scrollbar_size();
|
| + gfx::Size copy_size;
|
| + copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
|
|
|
| - HISTOGRAM_TIMES(kThumbnailHistogramName,
|
| - base::TimeTicks::Now() - begin_compute_thumbnail);
|
| - return result;
|
| -}
|
| + if (copy_rect.IsEmpty())
|
| + return;
|
|
|
| -} // namespace
|
| + context->clip_result = algorithm->GetCanvasCopyInfo(
|
| + copy_rect.size(),
|
| + ui::GetScaleFactorForNativeView(view->GetNativeView()),
|
| + ©_rect,
|
| + ©_size);
|
|
|
| -ThumbnailTabHelper::ThumbnailingContext::ThumbnailingContext(
|
| - content::WebContents* web_contents,
|
| - bool load_interrupted)
|
| - : browser_context(web_contents->GetBrowserContext()),
|
| - url(web_contents->GetURL()),
|
| - clip_result(kUnprocessed) {
|
| - score.at_top =
|
| - (web_contents->GetRenderViewHost()->GetLastScrollOffset().y() == 0);
|
| - score.load_completed = !web_contents->IsLoading() && !load_interrupted;
|
| + skia::PlatformBitmap* temp_bitmap = new skia::PlatformBitmap;
|
| + render_widget_host->CopyFromBackingStore(
|
| + copy_rect,
|
| + copy_size,
|
| + base::Bind(&ProcessCanvas, context, algorithm, base::Owned(temp_bitmap)),
|
| + temp_bitmap);
|
| }
|
|
|
| -ThumbnailTabHelper::ThumbnailingContext::~ThumbnailingContext() {
|
| -}
|
| +} // namespace
|
|
|
| ThumbnailTabHelper::ThumbnailTabHelper(content::WebContents* contents)
|
| : content::WebContentsObserver(contents),
|
| @@ -310,32 +205,6 @@ void ThumbnailTabHelper::RenderViewHostDeleted(
|
| content::Source<RenderViewHost>(renderer));
|
| }
|
|
|
| -double ThumbnailTabHelper::CalculateBoringScore(const SkBitmap& bitmap) {
|
| - if (bitmap.isNull() || bitmap.empty())
|
| - return 1.0;
|
| - 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;
|
| -}
|
| -
|
| -SkBitmap ThumbnailTabHelper::GetClippedBitmap(const SkBitmap& bitmap,
|
| - int desired_width,
|
| - int desired_height,
|
| - ClipResult* clip_result) {
|
| - gfx::Rect clipping_rect =
|
| - GetClippingRect(gfx::Size(bitmap.width(), bitmap.height()),
|
| - gfx::Size(desired_width, desired_height),
|
| - clip_result);
|
| - SkIRect src_rect = { clipping_rect.x(), clipping_rect.y(),
|
| - clipping_rect.right(), clipping_rect.bottom() };
|
| - SkBitmap clipped_bitmap;
|
| - bitmap.extractSubset(&clipped_bitmap, src_rect);
|
| - return clipped_bitmap;
|
| -}
|
| -
|
| void ThumbnailTabHelper::UpdateThumbnailIfNecessary(
|
| WebContents* web_contents) {
|
| // Destroying a WebContents may trigger it to be hidden, prompting a snapshot
|
| @@ -360,107 +229,14 @@ void ThumbnailTabHelper::UpdateThumbnailIfNecessary(
|
| return;
|
| }
|
|
|
| - AsyncUpdateThumbnail(web_contents);
|
| -}
|
| -
|
| -void ThumbnailTabHelper::UpdateThumbnail(
|
| - ThumbnailingContext* context,
|
| - const SkBitmap& thumbnail) {
|
| - Profile* profile =
|
| - Profile::FromBrowserContext(context->browser_context);
|
| - scoped_refptr<thumbnails::ThumbnailService> thumbnail_service =
|
| - ThumbnailServiceFactory::GetForProfile(profile);
|
| -
|
| - if (!thumbnail_service)
|
| - return;
|
| -
|
| - context->score.boring_score = CalculateBoringScore(thumbnail);
|
| - context->score.good_clipping =
|
| - (context->clip_result == ThumbnailTabHelper::kWiderThanTall ||
|
| - context->clip_result == ThumbnailTabHelper::kTallerThanWide ||
|
| - context->clip_result == ThumbnailTabHelper::kNotClipped);
|
| -
|
| - gfx::Image image(thumbnail);
|
| - thumbnail_service->SetPageThumbnail(context->url, image, context->score);
|
| - VLOG(1) << "Thumbnail taken for " << context->url << ": "
|
| - << context->score.ToString();
|
| -}
|
| -
|
| -void ThumbnailTabHelper::AsyncUpdateThumbnail(
|
| - WebContents* web_contents) {
|
| - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| - RenderWidgetHost* render_widget_host = web_contents->GetRenderViewHost();
|
| - content::RenderWidgetHostView* view = render_widget_host->GetView();
|
| - if (!view)
|
| - return;
|
| - if (!view->IsSurfaceAvailableForCopy()) {
|
| -#if defined(OS_WIN)
|
| - // On Windows XP, neither the backing store nor the compositing surface is
|
| - // available in the browser when accelerated compositing is active, so ask
|
| - // the renderer to send a snapshot for creating the thumbnail.
|
| - if (base::win::GetVersion() < base::win::VERSION_VISTA) {
|
| - scoped_refptr<ThumbnailingContext> context(
|
| - new ThumbnailingContext(web_contents, load_interrupted_));
|
| - gfx::Size view_size =
|
| - render_widget_host->GetView()->GetViewBounds().size();
|
| - g_browser_process->GetRenderWidgetSnapshotTaker()->AskForSnapshot(
|
| - render_widget_host,
|
| - base::Bind(&ThumbnailTabHelper::UpdateThumbnailWithBitmap,
|
| - context),
|
| - view_size,
|
| - view_size);
|
| - }
|
| -#endif
|
| - return;
|
| - }
|
| -
|
| - gfx::Rect copy_rect = gfx::Rect(view->GetViewBounds().size());
|
| - // Clip the pixels that will commonly hold a scrollbar, which looks bad in
|
| - // thumbnails.
|
| - int scrollbar_size = gfx::scrollbar_size();
|
| - copy_rect.Inset(0, 0, scrollbar_size, scrollbar_size);
|
| + scoped_refptr<thumbnails::ThumbnailingAlgorithm> algorithm(
|
| + thumbnail_service->GetThumbnailingAlgorithm());
|
|
|
| scoped_refptr<ThumbnailingContext> context(
|
| - new ThumbnailingContext(web_contents, load_interrupted_));
|
| - copy_rect = GetClippingRect(copy_rect.size(),
|
| - gfx::Size(kThumbnailWidth, kThumbnailHeight),
|
| - &context->clip_result);
|
| -
|
| - gfx::Size copy_size = GetCopySizeForThumbnail(view);
|
| - skia::PlatformBitmap* temp_bitmap = new skia::PlatformBitmap;
|
| - render_widget_host->CopyFromBackingStore(
|
| - copy_rect,
|
| - copy_size,
|
| - base::Bind(&ThumbnailTabHelper::UpdateThumbnailWithCanvas,
|
| - context,
|
| - base::Owned(temp_bitmap)),
|
| - temp_bitmap);
|
| -}
|
| -
|
| -void ThumbnailTabHelper::UpdateThumbnailWithBitmap(
|
| - ThumbnailingContext* context,
|
| - const SkBitmap& bitmap) {
|
| - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| - if (bitmap.isNull() || bitmap.empty())
|
| - return;
|
| -
|
| - SkBitmap thumbnail = CreateThumbnail(bitmap,
|
| - GetThumbnailSizeInPixel(),
|
| - &context->clip_result);
|
| -
|
| - UpdateThumbnail(context, thumbnail);
|
| -}
|
| -
|
| -void ThumbnailTabHelper::UpdateThumbnailWithCanvas(
|
| - ThumbnailingContext* context,
|
| - skia::PlatformBitmap* temp_bitmap,
|
| - bool succeeded) {
|
| - DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
|
| - if (!succeeded)
|
| - return;
|
| -
|
| - SkBitmap bitmap = temp_bitmap->GetBitmap();
|
| - UpdateThumbnailWithBitmap(context, bitmap);
|
| + new ThumbnailingContext(web_contents,
|
| + thumbnail_service,
|
| + load_interrupted_));
|
| + AsyncProcessThumbnail(web_contents, context, algorithm);
|
| }
|
|
|
| void ThumbnailTabHelper::DidStartLoading(
|
|
|