1 // Copyright 2014 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 "components/favicon_base/select_favicon_frames.h"
13 #include "components/favicon_base/favicon_util.h"
14 #include "skia/ext/image_operations.h"
15 #include "third_party/skia/include/core/SkCanvas.h"
16 #include "ui/gfx/geometry/size.h"
17 #include "ui/gfx/image/image.h"
18 #include "ui/gfx/image/image_skia.h"
19 #include "ui/gfx/image/image_skia_source.h"
23 size_t BiggestCandidate(const std::vector
<gfx::Size
>& candidate_sizes
) {
25 int max_area
= candidate_sizes
[0].GetArea();
26 for (size_t i
= 1; i
< candidate_sizes
.size(); ++i
) {
27 int area
= candidate_sizes
[i
].GetArea();
28 if (area
> max_area
) {
36 SkBitmap
SampleNearestNeighbor(const SkBitmap
& contents
, int desired_size
) {
38 bitmap
.allocN32Pixels(desired_size
, desired_size
);
39 if (!contents
.isOpaque())
40 bitmap
.eraseARGB(0, 0, 0, 0);
43 SkCanvas
canvas(bitmap
);
44 canvas
.drawBitmapRect(
46 SkRect::MakeFromIRect(SkIRect::MakeWH(desired_size
, desired_size
)));
52 size_t GetCandidateIndexWithBestScore(
53 const std::vector
<gfx::Size
>& candidate_sizes
,
56 DCHECK_NE(desired_size
, 0);
58 // Try to find an exact match.
59 for (size_t i
= 0; i
< candidate_sizes
.size(); ++i
) {
60 if (candidate_sizes
[i
].width() == desired_size
&&
61 candidate_sizes
[i
].height() == desired_size
) {
67 // Huge favicon bitmaps often have a completely different visual style from
68 // smaller favicon bitmaps. Avoid them.
69 const int kHugeEdgeSize
= desired_size
* 8;
71 // Order of preference:
72 // 1) Bitmaps with width and height smaller than |kHugeEdgeSize|.
73 // 2) Bitmaps which need to be scaled down instead of up.
74 // 3) Bitmaps which do not need to be scaled as much.
75 size_t candidate_index
= std::numeric_limits
<size_t>::max();
76 float candidate_score
= 0;
77 for (size_t i
= 0; i
< candidate_sizes
.size(); ++i
) {
79 (candidate_sizes
[i
].width() + candidate_sizes
[i
].height()) / 2.0f
;
82 if (candidate_sizes
[i
].width() >= kHugeEdgeSize
||
83 candidate_sizes
[i
].height() >= kHugeEdgeSize
) {
84 score
= std::min(1.0f
, desired_size
/ average_edge
) * 0.01f
;
85 } else if (candidate_sizes
[i
].width() >= desired_size
&&
86 candidate_sizes
[i
].height() >= desired_size
) {
87 score
= desired_size
/ average_edge
* 0.01f
+ 0.15f
;
89 score
= std::min(1.0f
, average_edge
/ desired_size
) * 0.01f
+ 0.1f
;
92 if (candidate_index
== std::numeric_limits
<size_t>::max() ||
93 score
> candidate_score
) {
95 candidate_score
= score
;
98 *score
= candidate_score
;
100 return candidate_index
;
103 // Represents the index of the best candidate for |desired_size| from the
104 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores().
105 struct SelectionResult
{
106 // index in |candidate_sizes| of the best candidate.
109 // The desired size for which |index| is the best candidate.
113 void GetCandidateIndicesWithBestScores(
114 const std::vector
<gfx::Size
>& candidate_sizes
,
115 const std::vector
<int>& desired_sizes
,
117 std::vector
<SelectionResult
>* results
) {
118 if (candidate_sizes
.empty() || desired_sizes
.empty()) {
124 std::vector
<int>::const_iterator zero_size_it
=
125 std::find(desired_sizes
.begin(), desired_sizes
.end(), 0);
126 if (zero_size_it
!= desired_sizes
.end()) {
127 // Just return the biggest image available.
128 SelectionResult result
;
129 result
.index
= BiggestCandidate(candidate_sizes
);
130 result
.desired_size
= 0;
131 results
->push_back(result
);
137 float total_score
= 0;
138 for (size_t i
= 0; i
< desired_sizes
.size(); ++i
) {
140 SelectionResult result
;
141 result
.desired_size
= desired_sizes
[i
];
142 result
.index
= GetCandidateIndexWithBestScore(
143 candidate_sizes
, result
.desired_size
, &score
);
144 results
->push_back(result
);
145 total_score
+= score
;
149 *match_score
= total_score
/ desired_sizes
.size();
152 // Resize |source_bitmap|
153 SkBitmap
GetResizedBitmap(const SkBitmap
& source_bitmap
,
154 gfx::Size original_size
,
155 int desired_size_in_pixel
) {
156 if (desired_size_in_pixel
== 0 ||
157 (original_size
.width() == desired_size_in_pixel
&&
158 original_size
.height() == desired_size_in_pixel
)) {
159 return source_bitmap
;
161 if (desired_size_in_pixel
% original_size
.width() == 0 &&
162 desired_size_in_pixel
% original_size
.height() == 0) {
163 return SampleNearestNeighbor(source_bitmap
, desired_size_in_pixel
);
165 return skia::ImageOperations::Resize(source_bitmap
,
166 skia::ImageOperations::RESIZE_LANCZOS3
,
167 desired_size_in_pixel
,
168 desired_size_in_pixel
);
171 class FaviconImageSource
: public gfx::ImageSkiaSource
{
173 FaviconImageSource() {}
174 ~FaviconImageSource() override
{}
176 // gfx::ImageSkiaSource:
177 gfx::ImageSkiaRep
GetImageForScale(float scale
) override
{
178 const gfx::ImageSkiaRep
* rep
= NULL
;
179 // gfx::ImageSkia passes one of the resource scale factors. The source
181 // 1) The ImageSkiaRep with the highest scale if all available
182 // scales are smaller than |scale|.
183 // 2) The ImageSkiaRep with the smallest one that is larger than |scale|.
184 // Note: Keep this logic consistent with the PNGImageSource in
186 // TODO(oshima): consolidate these logic into one place.
187 for (std::vector
<gfx::ImageSkiaRep
>::const_iterator iter
=
188 image_skia_reps_
.begin();
189 iter
!= image_skia_reps_
.end(); ++iter
) {
190 if ((*iter
).scale() == scale
)
192 if (!rep
|| rep
->scale() < (*iter
).scale())
194 if (rep
->scale() >= scale
)
198 return rep
? *rep
: gfx::ImageSkiaRep();
201 void AddImageSkiaRep(const gfx::ImageSkiaRep
& rep
) {
202 image_skia_reps_
.push_back(rep
);
206 std::vector
<gfx::ImageSkiaRep
> image_skia_reps_
;
207 DISALLOW_COPY_AND_ASSIGN(FaviconImageSource
);
212 const float kSelectFaviconFramesInvalidScore
= -1.0f
;
214 gfx::ImageSkia
CreateFaviconImageSkia(
215 const std::vector
<SkBitmap
>& bitmaps
,
216 const std::vector
<gfx::Size
>& original_sizes
,
217 int desired_size_in_dip
,
220 const std::vector
<float>& favicon_scales
= favicon_base::GetFaviconScales();
221 std::vector
<int> desired_sizes
;
223 if (desired_size_in_dip
== 0) {
224 desired_sizes
.push_back(0);
226 for (std::vector
<float>::const_iterator iter
= favicon_scales
.begin();
227 iter
!= favicon_scales
.end(); ++iter
) {
228 desired_sizes
.push_back(
229 static_cast<int>(ceil(desired_size_in_dip
* (*iter
))));
233 std::vector
<SelectionResult
> results
;
234 GetCandidateIndicesWithBestScores(original_sizes
,
238 if (results
.size() == 0)
239 return gfx::ImageSkia();
241 if (desired_size_in_dip
== 0) {
242 size_t index
= results
[0].index
;
243 return gfx::ImageSkia(gfx::ImageSkiaRep(bitmaps
[index
], 1.0f
));
246 FaviconImageSource
* image_source
= new FaviconImageSource
;
248 for (size_t i
= 0; i
< results
.size(); ++i
) {
249 size_t index
= results
[i
].index
;
250 image_source
->AddImageSkiaRep(
251 gfx::ImageSkiaRep(GetResizedBitmap(bitmaps
[index
],
252 original_sizes
[index
],
256 return gfx::ImageSkia(image_source
,
257 gfx::Size(desired_size_in_dip
, desired_size_in_dip
));
260 void SelectFaviconFrameIndices(const std::vector
<gfx::Size
>& frame_pixel_sizes
,
261 const std::vector
<int>& desired_sizes
,
262 std::vector
<size_t>* best_indices
,
263 float* match_score
) {
264 std::vector
<SelectionResult
> results
;
265 GetCandidateIndicesWithBestScores(
266 frame_pixel_sizes
, desired_sizes
, match_score
, &results
);
268 std::set
<size_t> already_added
;
269 for (size_t i
= 0; i
< results
.size(); ++i
) {
270 size_t index
= results
[i
].index
;
271 // GetCandidateIndicesWithBestScores() will return duplicate indices if the
272 // bitmap data with |frame_pixel_sizes[index]| should be used for multiple
273 // scale factors. Remove duplicates here such that |best_indices| contains
275 if (already_added
.find(index
) == already_added
.end()) {
276 already_added
.insert(index
);
277 best_indices
->push_back(index
);