Roll src/third_party/WebKit f298044:aa8346d (svn 202628:202629)
[chromium-blink-merge.git] / chrome / browser / thumbnails / simple_thumbnail_crop.cc
blob102fd61b826a7d46114093d23b09b40f385e9e2a
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.
5 #include "chrome/browser/thumbnails/simple_thumbnail_crop.h"
7 #include "base/metrics/histogram.h"
8 #include "content/public/browser/browser_thread.h"
9 #include "skia/ext/platform_canvas.h"
10 #include "ui/base/layout.h"
11 #include "ui/gfx/color_utils.h"
12 #include "ui/gfx/geometry/size_conversions.h"
13 #include "ui/gfx/image/image_skia.h"
14 #include "ui/gfx/screen.h"
15 #include "ui/gfx/scrollbar_size.h"
16 #include "ui/gfx/skbitmap_operations.h"
18 namespace {
19 static const char kThumbnailHistogramName[] = "Thumbnail.ComputeMS";
22 namespace thumbnails {
24 SimpleThumbnailCrop::SimpleThumbnailCrop(const gfx::Size& target_size)
25 : target_size_(target_size) {
26 DCHECK(!target_size.IsEmpty());
29 ClipResult SimpleThumbnailCrop::GetCanvasCopyInfo(const gfx::Size& source_size,
30 ui::ScaleFactor scale_factor,
31 gfx::Rect* clipping_rect,
32 gfx::Size* copy_size) const {
33 DCHECK(!source_size.IsEmpty());
34 ClipResult clip_result = thumbnails::CLIP_RESULT_NOT_CLIPPED;
35 *clipping_rect = GetClippingRect(source_size, target_size_, &clip_result);
36 *copy_size = GetCopySizeForThumbnail(scale_factor, target_size_);
37 return clip_result;
40 void SimpleThumbnailCrop::ProcessBitmap(
41 scoped_refptr<ThumbnailingContext> context,
42 const ConsumerCallback& callback,
43 const SkBitmap& bitmap) {
44 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
45 if (bitmap.isNull() || bitmap.empty())
46 return;
48 SkBitmap thumbnail = CreateThumbnail(
49 bitmap,
50 ComputeTargetSizeAtMaximumScale(target_size_),
51 &context->clip_result);
53 context->score.boring_score = color_utils::CalculateBoringScore(thumbnail);
54 context->score.good_clipping =
55 (context->clip_result == CLIP_RESULT_WIDER_THAN_TALL ||
56 context->clip_result == CLIP_RESULT_TALLER_THAN_WIDE ||
57 context->clip_result == CLIP_RESULT_NOT_CLIPPED);
59 callback.Run(*context.get(), thumbnail);
62 SkBitmap SimpleThumbnailCrop::GetClippedBitmap(const SkBitmap& bitmap,
63 int desired_width,
64 int desired_height,
65 ClipResult* clip_result) {
66 gfx::Rect clipping_rect =
67 GetClippingRect(gfx::Size(bitmap.width(), bitmap.height()),
68 gfx::Size(desired_width, desired_height),
69 clip_result);
70 SkIRect src_rect = { clipping_rect.x(), clipping_rect.y(),
71 clipping_rect.right(), clipping_rect.bottom() };
72 SkBitmap clipped_bitmap;
73 bitmap.extractSubset(&clipped_bitmap, src_rect);
74 return clipped_bitmap;
77 // RenderWidgetHost::CopyFromBackingStore can be costly especially when it is
78 // necessary to read back the web contents image data from GPU. As the cost is
79 // roughly proportional to the number of the copied pixels, the size of the
80 // copied pixels should be as small as possible.
81 // static
82 gfx::Size SimpleThumbnailCrop::GetCopySizeForThumbnail(
83 ui::ScaleFactor scale_factor,
84 const gfx::Size& thumbnail_size) {
85 // The copy size returned is the pixel equivalent of |thumbnail_size|, which
86 // is in DIPs.
87 if (scale_factor == ui::SCALE_FACTOR_100P) {
88 // In the case of 1x devices, we get a thumbnail twice as big and reduce
89 // it at serve time to improve quality.
90 scale_factor = ui::SCALE_FACTOR_200P;
92 float scale = GetScaleForScaleFactor(scale_factor);
93 return gfx::ToFlooredSize(gfx::ScaleSize(thumbnail_size, scale));
96 gfx::Rect SimpleThumbnailCrop::GetClippingRect(const gfx::Size& source_size,
97 const gfx::Size& desired_size,
98 ClipResult* clip_result) {
99 DCHECK(clip_result);
101 float desired_aspect =
102 static_cast<float>(desired_size.width()) / desired_size.height();
104 // Get the clipping rect so that we can preserve the aspect ratio while
105 // filling the destination.
106 gfx::Rect clipping_rect;
107 if (source_size.width() < desired_size.width() ||
108 source_size.height() < desired_size.height()) {
109 // Source image is smaller: we clip the part of source image within the
110 // dest rect, and then stretch it to fill the dest rect. We don't respect
111 // the aspect ratio in this case.
112 clipping_rect = gfx::Rect(desired_size);
113 *clip_result = thumbnails::CLIP_RESULT_SOURCE_IS_SMALLER;
114 } else {
115 float src_aspect =
116 static_cast<float>(source_size.width()) / source_size.height();
117 if (src_aspect > desired_aspect) {
118 // Wider than tall, clip horizontally: we center the smaller
119 // thumbnail in the wider screen.
120 int new_width = static_cast<int>(source_size.height() * desired_aspect);
121 int x_offset = (source_size.width() - new_width) / 2;
122 clipping_rect.SetRect(x_offset, 0, new_width, source_size.height());
123 *clip_result = (src_aspect >= ThumbnailScore::kTooWideAspectRatio) ?
124 thumbnails::CLIP_RESULT_MUCH_WIDER_THAN_TALL :
125 thumbnails::CLIP_RESULT_WIDER_THAN_TALL;
126 } else if (src_aspect < desired_aspect) {
127 clipping_rect =
128 gfx::Rect(source_size.width(), source_size.width() / desired_aspect);
129 *clip_result = thumbnails::CLIP_RESULT_TALLER_THAN_WIDE;
130 } else {
131 clipping_rect = gfx::Rect(source_size);
132 *clip_result = thumbnails::CLIP_RESULT_NOT_CLIPPED;
135 return clipping_rect;
138 // static
139 gfx::Size SimpleThumbnailCrop::ComputeTargetSizeAtMaximumScale(
140 const gfx::Size& given_size) {
141 // TODO(mazda|oshima): Update thumbnail when the max scale factor changes.
142 // crbug.com/159157.
143 float max_scale_factor = gfx::ImageSkia::GetMaxSupportedScale();
144 return gfx::ToFlooredSize(gfx::ScaleSize(given_size, max_scale_factor));
147 SimpleThumbnailCrop::~SimpleThumbnailCrop() {
150 // Creates a downsampled thumbnail from the given bitmap.
151 // store. The returned bitmap will be isNull if there was an error creating it.
152 SkBitmap SimpleThumbnailCrop::CreateThumbnail(const SkBitmap& bitmap,
153 const gfx::Size& desired_size,
154 ClipResult* clip_result) {
155 base::TimeTicks begin_compute_thumbnail = base::TimeTicks::Now();
157 SkBitmap clipped_bitmap;
158 if (*clip_result == thumbnails::CLIP_RESULT_UNPROCESSED) {
159 // Clip the pixels that will commonly hold a scrollbar, which looks bad in
160 // thumbnails.
161 int scrollbar_size = gfx::scrollbar_size();
162 SkIRect scrollbarless_rect =
163 { 0, 0,
164 std::max(1, bitmap.width() - scrollbar_size),
165 std::max(1, bitmap.height() - scrollbar_size) };
166 SkBitmap bmp;
167 bitmap.extractSubset(&bmp, scrollbarless_rect);
169 clipped_bitmap = GetClippedBitmap(
170 bmp, desired_size.width(), desired_size.height(), clip_result);
171 } else {
172 clipped_bitmap = bitmap;
175 // Need to resize it to the size we want, so downsample until it's
176 // close, and let the caller make it the exact size if desired.
177 SkBitmap result = SkBitmapOperations::DownsampleByTwoUntilSize(
178 clipped_bitmap, desired_size.width(), desired_size.height());
179 #if !defined(USE_AURA)
180 // This is a bit subtle. SkBitmaps are refcounted, but the magic
181 // ones in PlatformCanvas can't be assigned to SkBitmap with proper
182 // refcounting. If the bitmap doesn't change, then the downsampler
183 // will return the input bitmap, which will be the reference to the
184 // weird PlatformCanvas one insetad of a regular one. To get a
185 // regular refcounted bitmap, we need to copy it.
187 // On Aura, the PlatformCanvas is platform-independent and does not have
188 // any native platform resources that can't be refounted, so this issue does
189 // not occur.
191 // Note that GetClippedBitmap() does extractSubset() but it won't copy
192 // the pixels, hence we check result size == clipped_bitmap size here.
193 if (clipped_bitmap.width() == result.width() &&
194 clipped_bitmap.height() == result.height())
195 clipped_bitmap.copyTo(&result, kN32_SkColorType);
196 #endif
198 LOCAL_HISTOGRAM_TIMES(kThumbnailHistogramName,
199 base::TimeTicks::Now() - begin_compute_thumbnail);
200 return result;
203 } // namespace thumbnails