Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6474)

Unified Diff: chrome/browser/thumbnails/simple_thumbnail_crop.cc

Issue 11985003: Refactored-out the code of thumbnaling algorithm from thumbnail_tab_helper. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/thumbnails/simple_thumbnail_crop.cc
diff --git a/chrome/browser/thumbnails/simple_thumbnail_crop.cc b/chrome/browser/thumbnails/simple_thumbnail_crop.cc
new file mode 100644
index 0000000000000000000000000000000000000000..24572c418605dc7f283b76d697d250e92a5d3992
--- /dev/null
+++ b/chrome/browser/thumbnails/simple_thumbnail_crop.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/thumbnails/simple_thumbnail_crop.h"
+
+#include "base/metrics/histogram.h"
+#include "content/public/browser/browser_thread.h"
+#include "skia/ext/platform_canvas.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/screen.h"
+#include "ui/gfx/scrollbar_size.h"
+#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/skbitmap_operations.h"
+
+namespace {
+static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS";
+}
+
+namespace thumbnails {
+
+SimpleThumbnailCrop::SimpleThumbnailCrop(const gfx::Size& target_size)
+ : target_size_(target_size) {
+}
sky 2013/01/17 20:18:35 DCHECK size not empty
motek. 2013/01/18 18:38:41 Done.
+
+SimpleThumbnailCrop::~SimpleThumbnailCrop() {
sky 2013/01/17 20:18:35 Order of methods in .cc should match header.
motek. 2013/01/18 18:38:41 Done.
+}
+
+void SimpleThumbnailCrop::ProcessBitmap(ThumbnailingContext* context,
+ const SkBitmap& bitmap,
+ const ConsumerCallback& callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ if (bitmap.isNull() || bitmap.empty())
+ return;
+
+ SkBitmap thumbnail = CreateThumbnail(bitmap,
+ GetThumbnailSizeInPixel(),
+ &context->clip_result);
+
+ context->score.boring_score = CalculateBoringScore(thumbnail);
+ context->score.good_clipping =
+ (context->clip_result == kWiderThanTall ||
+ context->clip_result == kTallerThanWide ||
+ context->clip_result == kNotClipped);
+
+ callback.Run(context, thumbnail);
+}
+
+ClipResult SimpleThumbnailCrop::GetCanvasCopyInfo(
+ const gfx::Size& source_size,
+ ui::ScaleFactor scale_factor,
+ gfx::Rect* clipping_rect,
+ gfx::Size* target_size) const {
+ ClipResult clip_result = thumbnails::kNotClipped;
sky 2013/01/17 20:18:36 Are you guaranteed source_size is not empty? If so
motek. 2013/01/18 18:38:41 This is a good question. This is refactored code,
+ *clipping_rect = GetClippingRect(source_size, target_size_, &clip_result);
+ *target_size = GetCopySizeForThumbnail(scale_factor, target_size_);
+ return clip_result;
+}
+
+gfx::Rect SimpleThumbnailCrop::GetClippingRect(const gfx::Size& source_size,
+ const gfx::Size& desired_size,
+ 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 = thumbnails::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) ?
+ thumbnails::kTooWiderThanTall :
+ thumbnails::kWiderThanTall;
+ } else if (src_aspect < desired_aspect) {
+ clipping_rect =
+ gfx::Rect(source_size.width(), source_size.width() / desired_aspect);
+ *clip_result = thumbnails::kTallerThanWide;
+ } else {
+ clipping_rect = gfx::Rect(source_size);
+ *clip_result = thumbnails::kNotClipped;
+ }
+ }
+ return clipping_rect;
+}
+
+// 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.
+// static
+gfx::Size SimpleThumbnailCrop::GetCopySizeForThumbnail(
+ ui::ScaleFactor scale_factor, const gfx::Size& thumbnail_size) {
sky 2013/01/17 20:18:36 wrap each param to its own line.
motek. 2013/01/18 18:38:41 Done.
+ gfx::Size copy_size(thumbnail_size);
+ 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;
+}
+
+// Returns the size of the thumbnail stored in the database in pixel.
+gfx::Size SimpleThumbnailCrop::GetThumbnailSizeInPixel() const {
+ // 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(target_size_, max_scale_factor));
+}
+
+SkBitmap SimpleThumbnailCrop::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;
+}
+
+// Creates a downsampled thumbnail from the given bitmap.
+// store. The returned bitmap will be isNull if there was an error creating it.
+SkBitmap SimpleThumbnailCrop::CreateThumbnail(const SkBitmap& bitmap,
+ const gfx::Size& desired_size,
+ ClipResult* clip_result) {
+ base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
+
+ SkBitmap clipped_bitmap;
+ if (*clip_result == thumbnails::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 = GetClippedBitmap(
+ bmp, desired_size.width(), desired_size.height(), clip_result);
+ } else {
+ clipped_bitmap = bitmap;
+ }
+
+ // 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
+
+ HISTOGRAM_TIMES(kThumbnailHistogramName,
+ base::TimeTicks::Now() - begin_compute_thumbnail);
+ return result;
+}
+
+double SimpleThumbnailCrop::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;
+}
+
+}

Powered by Google App Engine
This is Rietveld 408576698