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

Side by Side 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: Fixing up details to make clang happy. 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 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 static const char kThumbnailHistogramName[] = "Thumbnail.RetargetMS";
21 static const char kFailureHistogramName[] = "Thumbnail.FailedRetargetMS";
22 static const float kScoreBoostFromSuccessfulRetargetting = 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, 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);
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 = PrepareSourceBitmap(
71 bitmap, target_thumbnail_size, context);
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, source_bitmap);
87 return;
88 }
89
90 if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
91 FROM_HERE,
92 base::Bind(&CreateRetargettedThumbnail,
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
mazda 2013/05/29 21:50:27 nit: remove the empty line
motek. 2013/05/30 15:00:03 Done.
109 gfx::Size resize_target;
110 SkBitmap clipped_bitmap;
111 if (context->clip_result == CLIP_RESULT_UNPROCESSED) {
112 // This case will require extracting a fragment from the retrieved bitmap.
113 int scrollbar_size = gfx::scrollbar_size();
114 gfx::Size scrollbarless(
115 std::max(1, received_bitmap.width() - scrollbar_size),
116 std::max(1, received_bitmap.height() - scrollbar_size));
117
118 gfx::Rect clipping_rect = GetClippingRect(
119 scrollbarless,
120 thumbnail_size,
121 &resize_target,
122 &context->clip_result);
123
124 received_bitmap.extractSubset(&clipped_bitmap,
125 gfx::RectToSkIRect(clipping_rect));
126 } else {
127 // This means that the source bitmap has been requested and at least
128 // clipped. Upstream code in same cases seems opportunistic and it may
129 // not perform actual resizing if copying with resize is not supported.
130 // In this case we will resize to the orignally requested copy size.
131 resize_target = context->requested_copy_size;
132 clipped_bitmap = received_bitmap;
133 }
134
135 SkBitmap result_bitmap = SkBitmapOperations::DownsampleByTwoUntilSize(
136 clipped_bitmap, resize_target.width(), resize_target.height());
137 #if !defined(USE_AURA)
138 // If the bitmap has not been indeed resized, it has to be copied. In that
139 // case resampler simply returns a reference to the original bitmap, sitting
140 // in PlatformCanvas. One does not simply assign these 'magic' bitmaps to
141 // SkBitmap. They cannot be refcounted.
142 //
143 // With Aura, this does not happen since PlatformCanvas is platform
144 // idependent.
145 if (clipped_bitmap.width() == result_bitmap.width() &&
146 clipped_bitmap.height() == result_bitmap.height()) {
147 clipped_bitmap.copyTo(&result_bitmap, SkBitmap::kARGB_8888_Config);
148 }
149 #endif
150
151 return result_bitmap;
152 }
153
154 // static
155 void ContentBasedThumbnailingAlgorithm::CreateRetargettedThumbnail(
156 const SkBitmap& source_bitmap,
157 const gfx::Size& thumbnail_size,
158 scoped_refptr<ThumbnailingContext> context,
159 const ConsumerCallback& callback) {
160 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
161 float kernel_sigma =
162 context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET ? 5.0f : 2.5f;
163 SkBitmap thumbnail = thumbnailing_utils::CreateRetargettedThumbnailImage(
164 source_bitmap, thumbnail_size, kernel_sigma);
165 bool processing_failed = thumbnail.empty();
166 if (processing_failed) {
167 // Log and apply the method very much like in SimpleThumbnailCrop (except
168 // that some clipping and copying is not required).
169 LOG(WARNING) << "CreateRetargettedThumbnailImage failed. "
170 << "The thumbnail for " << context->url
171 << " will be created the old-fashioned way.";
172
173 ClipResult clip_result;
174 gfx::Rect clipping_rect = SimpleThumbnailCrop::GetClippingRect(
175 gfx::Size(source_bitmap.width(), source_bitmap.height()),
176 thumbnail_size,
177 &clip_result);
178 source_bitmap.extractSubset(&thumbnail, gfx::RectToSkIRect(clipping_rect));
179 thumbnail = SkBitmapOperations::DownsampleByTwoUntilSize(
180 thumbnail, thumbnail_size.width(), thumbnail_size.height());
181 }
182
183 HISTOGRAM_TIMES(
184 processing_failed ? kFailureHistogramName : kThumbnailHistogramName,
185 base::TimeTicks::Now() - begin_compute_thumbnail);
186 context->score.boring_score =
187 SimpleThumbnailCrop::CalculateBoringScore(source_bitmap);
188 if (!processing_failed)
189 context->score.boring_score *= kScoreBoostFromSuccessfulRetargetting;
190 context->score.good_clipping =
191 (context->clip_result == CLIP_RESULT_WIDER_THAN_TALL ||
192 context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE ||
193 context->clip_result == CLIP_RESULT_NOT_CLIPPED ||
194 context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET);
195 // Post the result (the bitmap) back to the callback.
196 BrowserThread::PostTask(
197 BrowserThread::UI,
198 FROM_HERE,
199 base::Bind(&CallbackInvocationAdapter, callback, context, thumbnail));
200 }
201
202 ContentBasedThumbnailingAlgorithm::~ContentBasedThumbnailingAlgorithm() {
203 }
204
205 // static
206 gfx::Rect ContentBasedThumbnailingAlgorithm::GetClippingRect(
207 const gfx::Size& source_size,
208 const gfx::Size& thumbnail_size,
209 gfx::Size* target_size,
210 ClipResult* clip_result) {
211 // Compute and return the clipping rectagle of the source image and the
212 // size of the target bitmap which will be used for the further processing.
213 // This function in 'general case' is trivial (don't clip, halve the source)
214 // but it is needed for handling edge cases (source smaller than the target
215 // thumbnail size).
216 DCHECK(target_size);
217 DCHECK(clip_result);
218 gfx::Rect clipping_rect;
219 if (source_size.width() < thumbnail_size.width() ||
220 source_size.height() < thumbnail_size.height()) {
221 clipping_rect = gfx::Rect(thumbnail_size);
222 *target_size = thumbnail_size;
223 *clip_result = CLIP_RESULT_SOURCE_IS_SMALLER;
224 } else if (source_size.width() < thumbnail_size.width() * 4 ||
225 source_size.height() < thumbnail_size.height() * 4) {
226 clipping_rect = gfx::Rect(source_size);
227 *target_size = source_size;
228 *clip_result = CLIP_RESULT_SOURCE_SAME_AS_TARGET;
229 } else {
230 clipping_rect = gfx::Rect(source_size);
231 target_size->SetSize(source_size.width() / 2, source_size.height() / 2);
232 *clip_result = CLIP_RESULT_NOT_CLIPPED;
233 }
234
235 return clipping_rect;
236 }
237
238 } // namespace thumbnails
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698