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

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

Issue 15458003: Plugs in the new thumbnailing algorithm to ThumbnailTabHelper. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed up a compile problem (indpendent change). Created 7 years, 7 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/content_based_thumbnailing_algorithm.cc
diff --git a/chrome/browser/thumbnails/content_based_thumbnailing_algorithm.cc b/chrome/browser/thumbnails/content_based_thumbnailing_algorithm.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7bce10e0b5c7d8ff9a6572da50a0aef7eb9c0f59
--- /dev/null
+++ b/chrome/browser/thumbnails/content_based_thumbnailing_algorithm.cc
@@ -0,0 +1,237 @@
+// 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/content_based_thumbnailing_algorithm.h"
+
+#include "base/metrics/histogram.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/thumbnails/content_analysis.h"
+#include "chrome/browser/thumbnails/simple_thumbnail_crop.h"
+#include "content/public/browser/browser_thread.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/scrollbar_size.h"
+#include "ui/gfx/size_conversions.h"
+#include "ui/gfx/skbitmap_operations.h"
+#include "ui/gfx/skia_util.h"
+
+namespace {
+
+const char kThumbnailHistogramName[] = "Thumbnail.RetargetMS";
+const char kFailureHistogramName[] = "Thumbnail.FailedRetargetMS";
+const float kScoreBoostFromSuccessfulRetargeting = 1.1f;
+
+void CallbackInvocationAdapter(
+ const thumbnails::ThumbnailingAlgorithm::ConsumerCallback& callback,
+ scoped_refptr<thumbnails::ThumbnailingContext> context,
+ const SkBitmap& source_bitmap) {
+ callback.Run(*context, source_bitmap);
+}
+
+} // namespace
+
+namespace thumbnails {
+
+using content::BrowserThread;
+
+ContentBasedThumbnailingAlgorithm::ContentBasedThumbnailingAlgorithm(
+ const gfx::Size& target_size)
+ : target_size_(target_size) {
+ DCHECK(!target_size.IsEmpty());
+}
+
+ClipResult ContentBasedThumbnailingAlgorithm::GetCanvasCopyInfo(
+ const gfx::Size& source_size,
+ ui::ScaleFactor scale_factor,
+ gfx::Rect* clipping_rect,
+ gfx::Size* target_size) const {
+ DCHECK(!source_size.IsEmpty());
+ gfx::Size target_thumbnail_size =
+ SimpleThumbnailCrop::GetCopySizeForThumbnail(scale_factor, target_size_);
+
+ ClipResult clipping_method = thumbnails::CLIP_RESULT_NOT_CLIPPED;
+ *clipping_rect = GetClippingRect(
+ source_size, target_thumbnail_size, target_size, &clipping_method);
+ return clipping_method;
+}
+
+void ContentBasedThumbnailingAlgorithm::ProcessBitmap(
+ scoped_refptr<ThumbnailingContext> context,
+ const ConsumerCallback& callback,
+ const SkBitmap& bitmap) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(context);
+ if (bitmap.isNull() || bitmap.empty())
+ return;
+
+ gfx::Size target_thumbnail_size =
+ SimpleThumbnailCrop::ComputeTargetSizeAtMaximumScale(target_size_);
+
+ SkBitmap source_bitmap = PrepareSourceBitmap(
+ bitmap, target_thumbnail_size, context);
+
+ // If the source is same (or smaller) than the target, just return it as
+ // the final result. Otherwise, send the shrinking task to the blocking
+ // thread pool.
+ if (source_bitmap.width() <= target_thumbnail_size.width() ||
+ source_bitmap.height() <= target_thumbnail_size.height()) {
+ context->score.boring_score =
+ SimpleThumbnailCrop::CalculateBoringScore(source_bitmap);
+ context->score.good_clipping =
+ (context->clip_result == CLIP_RESULT_WIDER_THAN_TALL ||
+ context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE ||
+ context->clip_result == CLIP_RESULT_NOT_CLIPPED ||
+ context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET);
+
+ callback.Run(*context, source_bitmap);
+ return;
+ }
+
+ if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
+ FROM_HERE,
+ base::Bind(&CreateRetargetedThumbnail,
+ source_bitmap,
+ target_thumbnail_size,
+ context,
+ callback),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)) {
+ LOG(WARNING) << "PostSequencedWorkerTask failed. The thumbnail for "
+ << context->url << " will not be created.";
+ }
+}
+
+// static
+SkBitmap ContentBasedThumbnailingAlgorithm::PrepareSourceBitmap(
+ const SkBitmap& received_bitmap,
+ const gfx::Size& thumbnail_size,
+ ThumbnailingContext* context) {
+ gfx::Size resize_target;
+ SkBitmap clipped_bitmap;
+ if (context->clip_result == CLIP_RESULT_UNPROCESSED) {
+ // This case will require extracting a fragment from the retrieved bitmap.
+ int scrollbar_size = gfx::scrollbar_size();
+ gfx::Size scrollbarless(
+ std::max(1, received_bitmap.width() - scrollbar_size),
+ std::max(1, received_bitmap.height() - scrollbar_size));
+
+ gfx::Rect clipping_rect = GetClippingRect(
+ scrollbarless,
+ thumbnail_size,
+ &resize_target,
+ &context->clip_result);
+
+ received_bitmap.extractSubset(&clipped_bitmap,
+ gfx::RectToSkIRect(clipping_rect));
+ } else {
+ // This means that the source bitmap has been requested and at least
+ // clipped. Upstream code in same cases seems opportunistic and it may
+ // not perform actual resizing if copying with resize is not supported.
+ // In this case we will resize to the orignally requested copy size.
+ resize_target = context->requested_copy_size;
+ clipped_bitmap = received_bitmap;
+ }
+
+ SkBitmap result_bitmap = SkBitmapOperations::DownsampleByTwoUntilSize(
+ clipped_bitmap, resize_target.width(), resize_target.height());
+#if !defined(USE_AURA)
+ // If the bitmap has not been indeed resized, it has to be copied. In that
+ // case resampler simply returns a reference to the original bitmap, sitting
+ // in PlatformCanvas. One does not simply assign these 'magic' bitmaps to
+ // SkBitmap. They cannot be refcounted.
+ //
+ // With Aura, this does not happen since PlatformCanvas is platform
+ // idependent.
+ if (clipped_bitmap.width() == result_bitmap.width() &&
+ clipped_bitmap.height() == result_bitmap.height()) {
+ clipped_bitmap.copyTo(&result_bitmap, SkBitmap::kARGB_8888_Config);
+ }
+#endif
+
+ return result_bitmap;
+}
+
+// static
+void ContentBasedThumbnailingAlgorithm::CreateRetargetedThumbnail(
+ const SkBitmap& source_bitmap,
+ const gfx::Size& thumbnail_size,
+ scoped_refptr<ThumbnailingContext> context,
+ const ConsumerCallback& callback) {
+ base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
+ float kernel_sigma =
+ context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET ? 5.0f : 2.5f;
+ SkBitmap thumbnail = thumbnailing_utils::CreateRetargetedThumbnailImage(
+ source_bitmap, thumbnail_size, kernel_sigma);
+ bool processing_failed = thumbnail.empty();
+ if (processing_failed) {
+ // Log and apply the method very much like in SimpleThumbnailCrop (except
+ // that some clipping and copying is not required).
+ LOG(WARNING) << "CreateRetargetedThumbnailImage failed. "
+ << "The thumbnail for " << context->url
+ << " will be created the old-fashioned way.";
+
+ ClipResult clip_result;
+ gfx::Rect clipping_rect = SimpleThumbnailCrop::GetClippingRect(
+ gfx::Size(source_bitmap.width(), source_bitmap.height()),
+ thumbnail_size,
+ &clip_result);
+ source_bitmap.extractSubset(&thumbnail, gfx::RectToSkIRect(clipping_rect));
+ thumbnail = SkBitmapOperations::DownsampleByTwoUntilSize(
+ thumbnail, thumbnail_size.width(), thumbnail_size.height());
+ }
+
+ HISTOGRAM_TIMES(
+ processing_failed ? kFailureHistogramName : kThumbnailHistogramName,
+ base::TimeTicks::Now() - begin_compute_thumbnail);
+ context->score.boring_score =
+ SimpleThumbnailCrop::CalculateBoringScore(source_bitmap);
+ if (!processing_failed)
+ context->score.boring_score *= kScoreBoostFromSuccessfulRetargeting;
+ context->score.good_clipping =
+ (context->clip_result == CLIP_RESULT_WIDER_THAN_TALL ||
+ context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE ||
+ context->clip_result == CLIP_RESULT_NOT_CLIPPED ||
+ context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET);
+ // Post the result (the bitmap) back to the callback.
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&CallbackInvocationAdapter, callback, context, thumbnail));
+}
+
+ContentBasedThumbnailingAlgorithm::~ContentBasedThumbnailingAlgorithm() {
+}
+
+// static
+gfx::Rect ContentBasedThumbnailingAlgorithm::GetClippingRect(
+ const gfx::Size& source_size,
+ const gfx::Size& thumbnail_size,
+ gfx::Size* target_size,
+ ClipResult* clip_result) {
+ // Compute and return the clipping rectagle of the source image and the
+ // size of the target bitmap which will be used for the further processing.
+ // This function in 'general case' is trivial (don't clip, halve the source)
+ // but it is needed for handling edge cases (source smaller than the target
+ // thumbnail size).
+ DCHECK(target_size);
+ DCHECK(clip_result);
+ gfx::Rect clipping_rect;
+ if (source_size.width() < thumbnail_size.width() ||
+ source_size.height() < thumbnail_size.height()) {
+ clipping_rect = gfx::Rect(thumbnail_size);
+ *target_size = thumbnail_size;
+ *clip_result = CLIP_RESULT_SOURCE_IS_SMALLER;
+ } else if (source_size.width() < thumbnail_size.width() * 4 ||
+ source_size.height() < thumbnail_size.height() * 4) {
+ clipping_rect = gfx::Rect(source_size);
+ *target_size = source_size;
+ *clip_result = CLIP_RESULT_SOURCE_SAME_AS_TARGET;
+ } else {
+ clipping_rect = gfx::Rect(source_size);
+ target_size->SetSize(source_size.width() / 2, source_size.height() / 2);
+ *clip_result = CLIP_RESULT_NOT_CLIPPED;
+ }
+
+ return clipping_rect;
+}
+
+} // namespace thumbnails

Powered by Google App Engine
This is Rietveld 408576698