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

Side by Side 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 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/simple_thumbnail_crop.h"
6
7 #include "base/metrics/histogram.h"
8 #include "content/public/browser/browser_thread.h"
9 #include "skia/ext/platform_canvas.h"
10 #include "ui/gfx/color_utils.h"
11 #include "ui/gfx/screen.h"
12 #include "ui/gfx/scrollbar_size.h"
13 #include "ui/gfx/size_conversions.h"
14 #include "ui/gfx/skbitmap_operations.h"
15
16 namespace {
17 static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS";
18 }
19
20 namespace thumbnails {
21
22 SimpleThumbnailCrop::SimpleThumbnailCrop(const gfx::Size& target_size)
23 : target_size_(target_size) {
24 }
sky 2013/01/17 20:18:35 DCHECK size not empty
motek. 2013/01/18 18:38:41 Done.
25
26 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.
27 }
28
29 void SimpleThumbnailCrop::ProcessBitmap(ThumbnailingContext* context,
30 const SkBitmap& bitmap,
31 const ConsumerCallback& callback) {
32 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
33 if (bitmap.isNull() || bitmap.empty())
34 return;
35
36 SkBitmap thumbnail = CreateThumbnail(bitmap,
37 GetThumbnailSizeInPixel(),
38 &context->clip_result);
39
40 context->score.boring_score = CalculateBoringScore(thumbnail);
41 context->score.good_clipping =
42 (context->clip_result == kWiderThanTall ||
43 context->clip_result == kTallerThanWide ||
44 context->clip_result == kNotClipped);
45
46 callback.Run(context, thumbnail);
47 }
48
49 ClipResult SimpleThumbnailCrop::GetCanvasCopyInfo(
50 const gfx::Size& source_size,
51 ui::ScaleFactor scale_factor,
52 gfx::Rect* clipping_rect,
53 gfx::Size* target_size) const {
54 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,
55 *clipping_rect = GetClippingRect(source_size, target_size_, &clip_result);
56 *target_size = GetCopySizeForThumbnail(scale_factor, target_size_);
57 return clip_result;
58 }
59
60 gfx::Rect SimpleThumbnailCrop::GetClippingRect(const gfx::Size& source_size,
61 const gfx::Size& desired_size,
62 ClipResult* clip_result) {
63 DCHECK(clip_result);
64
65 float desired_aspect =
66 static_cast<float>(desired_size.width()) / desired_size.height();
67
68 // Get the clipping rect so that we can preserve the aspect ratio while
69 // filling the destination.
70 gfx::Rect clipping_rect;
71 if (source_size.width() < desired_size.width() ||
72 source_size.height() < desired_size.height()) {
73 // Source image is smaller: we clip the part of source image within the
74 // dest rect, and then stretch it to fill the dest rect. We don't respect
75 // the aspect ratio in this case.
76 clipping_rect = gfx::Rect(desired_size);
77 *clip_result = thumbnails::kSourceIsSmaller;
78 } else {
79 float src_aspect =
80 static_cast<float>(source_size.width()) / source_size.height();
81 if (src_aspect > desired_aspect) {
82 // Wider than tall, clip horizontally: we center the smaller
83 // thumbnail in the wider screen.
84 int new_width = static_cast<int>(source_size.height() * desired_aspect);
85 int x_offset = (source_size.width() - new_width) / 2;
86 clipping_rect.SetRect(x_offset, 0, new_width, source_size.height());
87 *clip_result = (src_aspect >= ThumbnailScore::kTooWideAspectRatio) ?
88 thumbnails::kTooWiderThanTall :
89 thumbnails::kWiderThanTall;
90 } else if (src_aspect < desired_aspect) {
91 clipping_rect =
92 gfx::Rect(source_size.width(), source_size.width() / desired_aspect);
93 *clip_result = thumbnails::kTallerThanWide;
94 } else {
95 clipping_rect = gfx::Rect(source_size);
96 *clip_result = thumbnails::kNotClipped;
97 }
98 }
99 return clipping_rect;
100 }
101
102 // Returns the size used by RenderWidgetHost::CopyFromBackingStore.
103 //
104 // The size is calculated in such a way that the copied size in pixel becomes
105 // equal to (f * kThumbnailWidth, f * kThumbnailHeight), where f is the scale
106 // of ui::SCALE_FACTOR_200P. Since RenderWidgetHost::CopyFromBackingStore takes
107 // the size in DIP, we need to adjust the size based on |view|'s device scale
108 // factor in order to copy the pixels with the size above.
109 //
110 // The copied size was chosen for the following reasons.
111 //
112 // 1. When the scale factor of the primary monitor is ui::SCALE_FACTOR_200P, the
113 // generated thumbnail size is (f * kThumbnailWidth, f * kThumbnailHeight).
114 // In order to avoid degrading the image quality by magnification, the size
115 // of the copied pixels should be equal to or larger than this thumbnail size.
116 //
117 // 2. RenderWidgetHost::CopyFromBackingStore can be costly especially when
118 // it is necessary to read back the web contents image data from GPU. As the
119 // cost is roughly propotional to the number of the copied pixels, the size of
120 // the copied pixels should be as small as possible.
121 //
122 // When the scale factor of the primary monitor is ui::SCALE_FACTOR_100P,
123 // we still copy the pixels with the same size as ui::SCALE_FACTOR_200P because
124 // the resampling method used in RenderWidgetHost::CopyFromBackingStore is not
125 // good enough for the resampled image to be used directly for the thumbnail
126 // (http://crbug.com/141235). We assume this is not an issue in case of
127 // ui::SCALE_FACTOR_200P because the high resolution thumbnail on high density
128 // display alleviates the aliasing.
129 // TODO(mazda): Copy the pixels with the smaller size in the case of
130 // ui::SCALE_FACTOR_100P once the resampling method has been improved.
131 // static
132 gfx::Size SimpleThumbnailCrop::GetCopySizeForThumbnail(
133 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.
134 gfx::Size copy_size(thumbnail_size);
135 switch (scale_factor) {
136 case ui::SCALE_FACTOR_100P:
137 copy_size = gfx::ToFlooredSize(gfx::ScaleSize(
138 copy_size, ui::GetScaleFactorScale(ui::SCALE_FACTOR_200P)));
139 break;
140 case ui::SCALE_FACTOR_200P:
141 // Use the size as-is.
142 break;
143 default:
144 DLOG(WARNING) << "Unsupported scale factor. Use the same copy size as "
145 << "ui::SCALE_FACTOR_100P";
146 copy_size = gfx::ToFlooredSize(gfx::ScaleSize(
147 copy_size, ui::GetScaleFactorScale(ui::SCALE_FACTOR_200P)));
148 break;
149 }
150 return copy_size;
151 }
152
153 // Returns the size of the thumbnail stored in the database in pixel.
154 gfx::Size SimpleThumbnailCrop::GetThumbnailSizeInPixel() const {
155 // Determine the resolution of the thumbnail based on the maximum scale
156 // factor.
157 // TODO(mazda|oshima): Update thumbnail when the max scale factor changes.
158 // crbug.com/159157.
159 float max_scale_factor =
160 ui::GetScaleFactorScale(ui::GetMaxScaleFactor());
161 return gfx::ToFlooredSize(gfx::ScaleSize(target_size_, max_scale_factor));
162 }
163
164 SkBitmap SimpleThumbnailCrop::GetClippedBitmap(const SkBitmap& bitmap,
165 int desired_width,
166 int desired_height,
167 ClipResult* clip_result) {
168 gfx::Rect clipping_rect =
169 GetClippingRect(gfx::Size(bitmap.width(), bitmap.height()),
170 gfx::Size(desired_width, desired_height),
171 clip_result);
172 SkIRect src_rect = { clipping_rect.x(), clipping_rect.y(),
173 clipping_rect.right(), clipping_rect.bottom() };
174 SkBitmap clipped_bitmap;
175 bitmap.extractSubset(&clipped_bitmap, src_rect);
176 return clipped_bitmap;
177 }
178
179 // Creates a downsampled thumbnail from the given bitmap.
180 // store. The returned bitmap will be isNull if there was an error creating it.
181 SkBitmap SimpleThumbnailCrop::CreateThumbnail(const SkBitmap& bitmap,
182 const gfx::Size& desired_size,
183 ClipResult* clip_result) {
184 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
185
186 SkBitmap clipped_bitmap;
187 if (*clip_result == thumbnails::kUnprocessed) {
188 // Clip the pixels that will commonly hold a scrollbar, which looks bad in
189 // thumbnails.
190 int scrollbar_size = gfx::scrollbar_size();
191 SkIRect scrollbarless_rect =
192 { 0, 0,
193 std::max(1, bitmap.width() - scrollbar_size),
194 std::max(1, bitmap.height() - scrollbar_size) };
195 SkBitmap bmp;
196 bitmap.extractSubset(&bmp, scrollbarless_rect);
197
198 clipped_bitmap = GetClippedBitmap(
199 bmp, desired_size.width(), desired_size.height(), clip_result);
200 } else {
201 clipped_bitmap = bitmap;
202 }
203
204 // Need to resize it to the size we want, so downsample until it's
205 // close, and let the caller make it the exact size if desired.
206 SkBitmap result = SkBitmapOperations::DownsampleByTwoUntilSize(
207 clipped_bitmap, desired_size.width(), desired_size.height());
208 #if !defined(USE_AURA)
209 // This is a bit subtle. SkBitmaps are refcounted, but the magic
210 // ones in PlatformCanvas can't be assigned to SkBitmap with proper
211 // refcounting. If the bitmap doesn't change, then the downsampler
212 // will return the input bitmap, which will be the reference to the
213 // weird PlatformCanvas one insetad of a regular one. To get a
214 // regular refcounted bitmap, we need to copy it.
215 //
216 // On Aura, the PlatformCanvas is platform-independent and does not have
217 // any native platform resources that can't be refounted, so this issue does
218 // not occur.
219 //
220 // Note that GetClippedBitmap() does extractSubset() but it won't copy
221 // the pixels, hence we check result size == clipped_bitmap size here.
222 if (clipped_bitmap.width() == result.width() &&
223 clipped_bitmap.height() == result.height())
224 clipped_bitmap.copyTo(&result, SkBitmap::kARGB_8888_Config);
225 #endif
226
227 HISTOGRAM_TIMES(kThumbnailHistogramName,
228 base::TimeTicks::Now() - begin_compute_thumbnail);
229 return result;
230 }
231
232 double SimpleThumbnailCrop::CalculateBoringScore(const SkBitmap& bitmap) {
233 if (bitmap.isNull() || bitmap.empty())
234 return 1.0;
235 int histogram[256] = {0};
236 color_utils::BuildLumaHistogram(bitmap, histogram);
237
238 int color_count = *std::max_element(histogram, histogram + 256);
239 int pixel_count = bitmap.width() * bitmap.height();
240 return static_cast<double>(color_count) / pixel_count;
241 }
242
243 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698