Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / thumbnails / content_based_thumbnailing_algorithm.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/thumbnails/content_based_thumbnailing_algorithm.h"
6
7 #include "base/metrics/histogram.h"
8 #include "base/threading/sequenced_worker_pool.h"
9 #include "chrome/browser/thumbnails/content_analysis.h"
10 #include "chrome/browser/thumbnails/simple_thumbnail_crop.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "third_party/skia/include/core/SkBitmap.h"
13 #include "ui/gfx/scrollbar_size.h"
14 #include "ui/gfx/size_conversions.h"
15 #include "ui/gfx/skbitmap_operations.h"
16 #include "ui/gfx/skia_util.h"
17
18 namespace {
19
20 const char kThumbnailHistogramName[] = "Thumbnail.RetargetMS";
21 const char kFailureHistogramName[] = "Thumbnail.FailedRetargetMS";
22 const float kScoreBoostFromSuccessfulRetargeting = 1.1f;
23
24 void CallbackInvocationAdapter(
25     const thumbnails::ThumbnailingAlgorithm::ConsumerCallback& callback,
26     scoped_refptr<thumbnails::ThumbnailingContext> context,
27     const SkBitmap& source_bitmap) {
28   callback.Run(*context.get(), source_bitmap);
29 }
30
31 }  // namespace
32
33 namespace thumbnails {
34
35 using content::BrowserThread;
36
37 ContentBasedThumbnailingAlgorithm::ContentBasedThumbnailingAlgorithm(
38     const gfx::Size& target_size)
39     : target_size_(target_size) {
40   DCHECK(!target_size.IsEmpty());
41 }
42
43 ClipResult ContentBasedThumbnailingAlgorithm::GetCanvasCopyInfo(
44     const gfx::Size& source_size,
45     ui::ScaleFactor scale_factor,
46     gfx::Rect* clipping_rect,
47     gfx::Size* target_size) const {
48   DCHECK(!source_size.IsEmpty());
49   gfx::Size target_thumbnail_size =
50       SimpleThumbnailCrop::GetCopySizeForThumbnail(scale_factor, target_size_);
51
52   ClipResult clipping_method = thumbnails::CLIP_RESULT_NOT_CLIPPED;
53   *clipping_rect = GetClippingRect(
54       source_size, target_thumbnail_size, target_size, &clipping_method);
55   return clipping_method;
56 }
57
58 void ContentBasedThumbnailingAlgorithm::ProcessBitmap(
59     scoped_refptr<ThumbnailingContext> context,
60     const ConsumerCallback& callback,
61     const SkBitmap& bitmap) {
62   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
63   DCHECK(context.get());
64   if (bitmap.isNull() || bitmap.empty())
65     return;
66
67   gfx::Size target_thumbnail_size =
68       SimpleThumbnailCrop::ComputeTargetSizeAtMaximumScale(target_size_);
69
70   SkBitmap source_bitmap =
71       PrepareSourceBitmap(bitmap, target_thumbnail_size, context.get());
72
73   // If the source is same (or smaller) than the target, just return it as
74   // the final result. Otherwise, send the shrinking task to the blocking
75   // thread pool.
76   if (source_bitmap.width() <= target_thumbnail_size.width() ||
77       source_bitmap.height() <= target_thumbnail_size.height()) {
78     context->score.boring_score =
79         SimpleThumbnailCrop::CalculateBoringScore(source_bitmap);
80     context->score.good_clipping =
81         (context->clip_result == CLIP_RESULT_WIDER_THAN_TALL ||
82          context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE ||
83          context->clip_result == CLIP_RESULT_NOT_CLIPPED ||
84          context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET);
85
86     callback.Run(*context.get(), source_bitmap);
87     return;
88   }
89
90   if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
91           FROM_HERE,
92           base::Bind(&CreateRetargetedThumbnail,
93                      source_bitmap,
94                      target_thumbnail_size,
95                      context,
96                      callback),
97           base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)) {
98     LOG(WARNING) << "PostSequencedWorkerTask failed. The thumbnail for "
99                  << context->url << " will not be created.";
100   }
101 }
102
103 // static
104 SkBitmap ContentBasedThumbnailingAlgorithm::PrepareSourceBitmap(
105     const SkBitmap& received_bitmap,
106     const gfx::Size& thumbnail_size,
107     ThumbnailingContext* context) {
108   gfx::Size resize_target;
109   SkBitmap clipped_bitmap;
110   if (context->clip_result == CLIP_RESULT_UNPROCESSED) {
111     // This case will require extracting a fragment from the retrieved bitmap.
112     int scrollbar_size = gfx::scrollbar_size();
113     gfx::Size scrollbarless(
114         std::max(1, received_bitmap.width() - scrollbar_size),
115         std::max(1, received_bitmap.height() - scrollbar_size));
116
117     gfx::Rect clipping_rect = GetClippingRect(
118         scrollbarless,
119         thumbnail_size,
120         &resize_target,
121         &context->clip_result);
122
123     received_bitmap.extractSubset(&clipped_bitmap,
124                                   gfx::RectToSkIRect(clipping_rect));
125   } else {
126     // This means that the source bitmap has been requested and at least
127     // clipped. Upstream code in same cases seems opportunistic and it may
128     // not perform actual resizing if copying with resize is not supported.
129     // In this case we will resize to the orignally requested copy size.
130     resize_target = context->requested_copy_size;
131     clipped_bitmap = received_bitmap;
132   }
133
134   SkBitmap result_bitmap = SkBitmapOperations::DownsampleByTwoUntilSize(
135       clipped_bitmap, resize_target.width(), resize_target.height());
136 #if !defined(USE_AURA)
137   // If the bitmap has not been indeed resized, it has to be copied. In that
138   // case resampler simply returns a reference to the original bitmap, sitting
139   // in PlatformCanvas. One does not simply assign these 'magic' bitmaps to
140   // SkBitmap. They cannot be refcounted.
141   //
142   // With Aura, this does not happen since PlatformCanvas is platform
143   // idependent.
144   if (clipped_bitmap.width() == result_bitmap.width() &&
145       clipped_bitmap.height() == result_bitmap.height()) {
146     clipped_bitmap.copyTo(&result_bitmap, kN32_SkColorType);
147   }
148 #endif
149
150   return result_bitmap;
151 }
152
153 // static
154 void ContentBasedThumbnailingAlgorithm::CreateRetargetedThumbnail(
155     const SkBitmap& source_bitmap,
156     const gfx::Size& thumbnail_size,
157     scoped_refptr<ThumbnailingContext> context,
158     const ConsumerCallback& callback) {
159   base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
160   float kernel_sigma =
161       context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET ? 5.0f : 2.5f;
162   SkBitmap thumbnail = thumbnailing_utils::CreateRetargetedThumbnailImage(
163       source_bitmap, thumbnail_size, kernel_sigma);
164   bool processing_failed = thumbnail.empty();
165   if (processing_failed) {
166     // Log and apply the method very much like in SimpleThumbnailCrop (except
167     // that some clipping and copying is not required).
168     LOG(WARNING) << "CreateRetargetedThumbnailImage failed. "
169                  << "The thumbnail for " << context->url
170                  << " will be created the old-fashioned way.";
171
172     ClipResult clip_result;
173     gfx::Rect clipping_rect = SimpleThumbnailCrop::GetClippingRect(
174         gfx::Size(source_bitmap.width(), source_bitmap.height()),
175         thumbnail_size,
176         &clip_result);
177     source_bitmap.extractSubset(&thumbnail, gfx::RectToSkIRect(clipping_rect));
178     thumbnail = SkBitmapOperations::DownsampleByTwoUntilSize(
179         thumbnail, thumbnail_size.width(), thumbnail_size.height());
180   }
181
182   HISTOGRAM_TIMES(
183       processing_failed ? kFailureHistogramName : kThumbnailHistogramName,
184       base::TimeTicks::Now() - begin_compute_thumbnail);
185   context->score.boring_score =
186         SimpleThumbnailCrop::CalculateBoringScore(source_bitmap);
187   if (!processing_failed)
188     context->score.boring_score *= kScoreBoostFromSuccessfulRetargeting;
189   context->score.good_clipping =
190       (context->clip_result == CLIP_RESULT_WIDER_THAN_TALL ||
191        context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE ||
192        context->clip_result == CLIP_RESULT_NOT_CLIPPED ||
193        context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET);
194   // Post the result (the bitmap) back to the callback.
195   BrowserThread::PostTask(
196       BrowserThread::UI,
197       FROM_HERE,
198       base::Bind(&CallbackInvocationAdapter, callback, context, thumbnail));
199 }
200
201 ContentBasedThumbnailingAlgorithm::~ContentBasedThumbnailingAlgorithm() {
202 }
203
204 //  static
205 gfx::Rect ContentBasedThumbnailingAlgorithm::GetClippingRect(
206     const gfx::Size& source_size,
207     const gfx::Size& thumbnail_size,
208     gfx::Size* target_size,
209     ClipResult* clip_result) {
210   // Compute and return the clipping rectagle of the source image and the
211   // size of the target bitmap which will be used for the further processing.
212   // This function in 'general case' is trivial (don't clip, halve the source)
213   // but it is needed for handling edge cases (source smaller than the target
214   // thumbnail size).
215   DCHECK(target_size);
216   DCHECK(clip_result);
217   gfx::Rect clipping_rect;
218   if (source_size.width() < thumbnail_size.width() ||
219       source_size.height() < thumbnail_size.height()) {
220     clipping_rect = gfx::Rect(thumbnail_size);
221     *target_size = thumbnail_size;
222     *clip_result = CLIP_RESULT_SOURCE_IS_SMALLER;
223   } else if (source_size.width() < thumbnail_size.width() * 4 ||
224              source_size.height() < thumbnail_size.height() * 4) {
225     clipping_rect = gfx::Rect(source_size);
226     *target_size = source_size;
227     *clip_result = CLIP_RESULT_SOURCE_SAME_AS_TARGET;
228   } else {
229     clipping_rect = gfx::Rect(source_size);
230     target_size->SetSize(source_size.width() / 2, source_size.height() / 2);
231     *clip_result = CLIP_RESULT_NOT_CLIPPED;
232   }
233
234   return clipping_rect;
235 }
236
237 }  // namespace thumbnails