OLD | NEW |
---|---|
(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/advanced_thumbnail_crop.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 { | |
mazda
2013/05/23 19:13:57
nit: add one empty line
motek.
2013/05/27 16:54:54
Done.
| |
19 static const char kThumbnailHistogramName[] = "Thumbnail.RetargetMS"; | |
20 static const char kFailureHistogramName[] = "Thumbnail.FailedRetargetMS"; | |
21 | |
22 void CallbackInvocationAdapter( | |
23 const thumbnails::ThumbnailingAlgorithm::ConsumerCallback& callback, | |
24 scoped_refptr<thumbnails::ThumbnailingContext> context, | |
25 const SkBitmap& source_bitmap) { | |
26 callback.Run(*context, source_bitmap); | |
27 } | |
28 | |
29 } | |
mazda
2013/05/23 19:13:57
} // namespace
motek.
2013/05/27 16:54:54
Done.
| |
30 | |
31 namespace thumbnails { | |
32 | |
33 using content::BrowserThread; | |
34 | |
35 AdvancedThumbnailCrop::AdvancedThumbnailCrop(const gfx::Size& target_size) | |
36 : target_size_(target_size) { | |
37 DCHECK(!target_size.IsEmpty()); | |
38 } | |
39 | |
40 ClipResult AdvancedThumbnailCrop::GetCanvasCopyInfo( | |
41 const gfx::Size& source_size, | |
42 ui::ScaleFactor scale_factor, | |
43 gfx::Rect* clipping_rect, | |
44 gfx::Size* target_size) const { | |
45 DCHECK(!source_size.IsEmpty()); | |
46 gfx::Size target_thumbnail_size = | |
47 SimpleThumbnailCrop::GetCopySizeForThumbnail(scale_factor, target_size_); | |
48 | |
49 ClipResult clipping_method = thumbnails::CLIP_RESULT_NOT_CLIPPED; | |
50 *clipping_rect = GetClippingRect( | |
51 source_size, target_thumbnail_size, target_size, &clipping_method); | |
52 return clipping_method; | |
53 } | |
54 | |
55 void AdvancedThumbnailCrop::ProcessBitmap( | |
56 scoped_refptr<ThumbnailingContext> context, | |
57 const ConsumerCallback& callback, | |
58 const SkBitmap& bitmap) { | |
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
60 DCHECK(context); | |
61 if (bitmap.isNull() || bitmap.empty()) | |
62 return; | |
63 | |
64 float max_scale_factor = | |
65 ui::GetScaleFactorScale(ui::GetMaxScaleFactor()); | |
66 gfx::Size target_thumbnail_size = gfx::ToFlooredSize( | |
67 gfx::ScaleSize(target_size_, max_scale_factor)); | |
mazda
2013/05/23 19:13:57
Could you share this logic of how the scale factor
motek.
2013/05/27 16:54:54
I did it by applying some refactoring. Effectively
| |
68 | |
69 SkBitmap source_bitmap = PrepareSourceBitmap( | |
70 bitmap, target_thumbnail_size, context); | |
71 | |
72 // If the source is same (or smaller) than the target, just return it as | |
73 // the final result. Otherwise, send the shrinking task to the blocking | |
74 // thread pool. | |
75 if (source_bitmap.width() <= target_thumbnail_size.width() || | |
76 source_bitmap.height() <= target_thumbnail_size.height()) { | |
77 context->score.boring_score = | |
78 SimpleThumbnailCrop::CalculateBoringScore(source_bitmap); | |
79 context->score.good_clipping = | |
80 (context->clip_result == CLIP_RESULT_WIDER_THAN_TALL || | |
81 context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE || | |
82 context->clip_result == CLIP_RESULT_NOT_CLIPPED || | |
83 context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET); | |
84 | |
85 callback.Run(*context, source_bitmap); | |
86 return; | |
87 } | |
88 | |
89 if (!BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( | |
90 FROM_HERE, | |
91 base::Bind(&CreateRetargettedThumbnail, | |
92 source_bitmap, | |
93 target_thumbnail_size, | |
94 context, | |
95 callback), | |
96 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)) { | |
97 LOG(WARNING) << "PostSequencedWorkerTask failed. " << | |
98 "The thumbnail for " << context->url << " will not be created."; | |
mazda
2013/05/23 19:13:57
Move "<<" from the previous line and align it with
motek.
2013/05/27 16:54:54
Done.
| |
99 } | |
100 } | |
101 | |
102 // static | |
103 SkBitmap AdvancedThumbnailCrop::PrepareSourceBitmap( | |
104 const SkBitmap& received_bitmap, | |
105 const gfx::Size& thumbnail_size, | |
106 ThumbnailingContext* context) { | |
107 | |
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 = AdvancedThumbnailCrop::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, SkBitmap::kARGB_8888_Config); | |
147 } | |
148 #endif | |
149 | |
150 return result_bitmap; | |
151 } | |
152 | |
153 // static | |
154 void AdvancedThumbnailCrop::CreateRetargettedThumbnail( | |
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::CreateRetargettedThumbnailImage( | |
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) << "CreateRetargettedThumbnailImage failed. " << | |
169 "The thumbnail for " << context->url << | |
170 " will be created the old-fashioned way."; | |
mazda
2013/05/23 19:13:57
LOG(WARNING) << "CreateRetargettedThumbnailImage f
motek.
2013/05/27 16:54:54
Done.
| |
171 ClipResult clip_result; | |
172 gfx::Rect clipping_rect = SimpleThumbnailCrop::GetClippingRect( | |
173 gfx::Size(source_bitmap.width(), source_bitmap.height()), | |
174 thumbnail_size, | |
175 &clip_result); | |
176 source_bitmap.extractSubset(&thumbnail, gfx::RectToSkIRect(clipping_rect)); | |
177 thumbnail = SkBitmapOperations::DownsampleByTwoUntilSize( | |
178 thumbnail, thumbnail_size.width(), thumbnail_size.height()); | |
179 } | |
180 | |
181 HISTOGRAM_TIMES( | |
182 processing_failed ? kFailureHistogramName : kThumbnailHistogramName, | |
183 base::TimeTicks::Now() - begin_compute_thumbnail); | |
184 context->score.boring_score = | |
185 SimpleThumbnailCrop::CalculateBoringScore(source_bitmap); | |
186 if (!processing_failed) | |
187 context->score.boring_score *= 1.1f; // A bit of a boost for retargetted. | |
mazda
2013/05/23 19:13:57
Please make a constant variable for 1.1f.
motek.
2013/05/27 16:54:54
Done.
| |
188 context->score.good_clipping = | |
189 (context->clip_result == CLIP_RESULT_WIDER_THAN_TALL || | |
190 context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE || | |
191 context->clip_result == CLIP_RESULT_NOT_CLIPPED || | |
192 context->clip_result == CLIP_RESULT_SOURCE_SAME_AS_TARGET); | |
193 // Post the result (the bitmap) back to the callback. | |
194 BrowserThread::PostTask( | |
195 BrowserThread::UI, | |
196 FROM_HERE, | |
197 base::Bind(&CallbackInvocationAdapter, callback, context, thumbnail)); | |
198 } | |
199 | |
200 AdvancedThumbnailCrop::~AdvancedThumbnailCrop() { | |
201 } | |
202 | |
203 // static | |
204 gfx::Rect AdvancedThumbnailCrop::GetClippingRect( | |
205 const gfx::Size& source_size, | |
206 const gfx::Size& thumbnail_size, | |
207 gfx::Size* target_size, | |
208 ClipResult* clip_result) { | |
209 // Compute and return the clipping rectagle of the source image and the | |
210 // size of the target bitmap which will be used for the further processing. | |
211 // This function in 'general case' is trivial (don't clip, halve the source) | |
212 // but it is needed for handling edge cases (source smaller than the target | |
213 // thumbnail size). | |
214 DCHECK(target_size); | |
215 DCHECK(clip_result); | |
216 gfx::Rect clipping_rect; | |
217 if (source_size.width() < thumbnail_size.width() || | |
218 source_size.height() < thumbnail_size.height()) { | |
219 clipping_rect = gfx::Rect(thumbnail_size); | |
220 *target_size = thumbnail_size; | |
221 *clip_result = CLIP_RESULT_SOURCE_IS_SMALLER; | |
222 } else if (source_size.width() < thumbnail_size.width() * 4 || | |
223 source_size.height() < thumbnail_size.height() * 4) { | |
224 clipping_rect = gfx::Rect(source_size); | |
225 *target_size = source_size; | |
226 *clip_result = CLIP_RESULT_SOURCE_SAME_AS_TARGET; | |
227 } else { | |
228 clipping_rect = gfx::Rect(source_size); | |
229 target_size->SetSize(source_size.width() / 2, source_size.height() / 2); | |
230 *clip_result = CLIP_RESULT_NOT_CLIPPED; | |
231 } | |
232 | |
233 return clipping_rect; | |
234 } | |
235 | |
236 | |
mazda
2013/05/23 19:13:57
nit: delete two empty lines
motek.
2013/05/27 16:54:54
Done.
| |
237 | |
238 } // namespace thumbnails | |
OLD | NEW |