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(
45 contents
, SkRect::MakeIWH(desired_size
, desired_size
), NULL
);
51 size_t GetCandidateIndexWithBestScore(
52 const std::vector
<gfx::Size
>& candidate_sizes
,
55 DCHECK_NE(desired_size
, 0);
57 // Try to find an exact match.
58 for (size_t i
= 0; i
< candidate_sizes
.size(); ++i
) {
59 if (candidate_sizes
[i
].width() == desired_size
&&
60 candidate_sizes
[i
].height() == desired_size
) {
66 // Huge favicon bitmaps often have a completely different visual style from
67 // smaller favicon bitmaps. Avoid them.
68 const int kHugeEdgeSize
= desired_size
* 8;
70 // Order of preference:
71 // 1) Bitmaps with width and height smaller than |kHugeEdgeSize|.
72 // 2) Bitmaps which need to be scaled down instead of up.
73 // 3) Bitmaps which do not need to be scaled as much.
74 size_t candidate_index
= std::numeric_limits
<size_t>::max();
75 float candidate_score
= 0;
76 for (size_t i
= 0; i
< candidate_sizes
.size(); ++i
) {
78 (candidate_sizes
[i
].width() + candidate_sizes
[i
].height()) / 2.0f
;
81 if (candidate_sizes
[i
].width() >= kHugeEdgeSize
||
82 candidate_sizes
[i
].height() >= kHugeEdgeSize
) {
83 score
= std::min(1.0f
, desired_size
/ average_edge
) * 0.01f
;
84 } else if (candidate_sizes
[i
].width() >= desired_size
&&
85 candidate_sizes
[i
].height() >= desired_size
) {
86 score
= desired_size
/ average_edge
* 0.01f
+ 0.15f
;
88 score
= std::min(1.0f
, average_edge
/ desired_size
) * 0.01f
+ 0.1f
;
91 if (candidate_index
== std::numeric_limits
<size_t>::max() ||
92 score
> candidate_score
) {
94 candidate_score
= score
;
97 *score
= candidate_score
;
99 return candidate_index
;
102 // Represents the index of the best candidate for |desired_size| from the
103 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores().
104 struct SelectionResult
{
105 // index in |candidate_sizes| of the best candidate.
108 // The desired size for which |index| is the best candidate.
112 void GetCandidateIndicesWithBestScores(
113 const std::vector
<gfx::Size
>& candidate_sizes
,
114 const std::vector
<int>& desired_sizes
,
116 std::vector
<SelectionResult
>* results
) {
117 if (candidate_sizes
.empty() || desired_sizes
.empty()) {
123 std::vector
<int>::const_iterator zero_size_it
=
124 std::find(desired_sizes
.begin(), desired_sizes
.end(), 0);
125 if (zero_size_it
!= desired_sizes
.end()) {
126 // Just return the biggest image available.
127 SelectionResult result
;
128 result
.index
= BiggestCandidate(candidate_sizes
);
129 result
.desired_size
= 0;
130 results
->push_back(result
);
136 float total_score
= 0;
137 for (size_t i
= 0; i
< desired_sizes
.size(); ++i
) {
139 SelectionResult result
;
140 result
.desired_size
= desired_sizes
[i
];
141 result
.index
= GetCandidateIndexWithBestScore(
142 candidate_sizes
, result
.desired_size
, &score
);
143 results
->push_back(result
);
144 total_score
+= score
;
148 *match_score
= total_score
/ desired_sizes
.size();
151 // Resize |source_bitmap|
152 SkBitmap
GetResizedBitmap(const SkBitmap
& source_bitmap
,
153 gfx::Size original_size
,
154 int desired_size_in_pixel
) {
155 if (desired_size_in_pixel
== 0 ||
156 (original_size
.width() == desired_size_in_pixel
&&
157 original_size
.height() == desired_size_in_pixel
)) {
158 return source_bitmap
;
160 if (desired_size_in_pixel
% original_size
.width() == 0 &&
161 desired_size_in_pixel
% original_size
.height() == 0) {
162 return SampleNearestNeighbor(source_bitmap
, desired_size_in_pixel
);
164 return skia::ImageOperations::Resize(source_bitmap
,
165 skia::ImageOperations::RESIZE_LANCZOS3
,
166 desired_size_in_pixel
,
167 desired_size_in_pixel
);
170 class FaviconImageSource
: public gfx::ImageSkiaSource
{
172 FaviconImageSource() {}
173 ~FaviconImageSource() override
{}
175 // gfx::ImageSkiaSource:
176 gfx::ImageSkiaRep
GetImageForScale(float scale
) override
{
177 const gfx::ImageSkiaRep
* rep
= NULL
;
178 // gfx::ImageSkia passes one of the resource scale factors. The source
180 // 1) The ImageSkiaRep with the highest scale if all available
181 // scales are smaller than |scale|.
182 // 2) The ImageSkiaRep with the smallest one that is larger than |scale|.
183 // Note: Keep this logic consistent with the PNGImageSource in
185 // TODO(oshima): consolidate these logic into one place.
186 for (std::vector
<gfx::ImageSkiaRep
>::const_iterator iter
=
187 image_skia_reps_
.begin();
188 iter
!= image_skia_reps_
.end(); ++iter
) {
189 if ((*iter
).scale() == scale
)
191 if (!rep
|| rep
->scale() < (*iter
).scale())
193 if (rep
->scale() >= scale
)
197 return rep
? *rep
: gfx::ImageSkiaRep();
200 void AddImageSkiaRep(const gfx::ImageSkiaRep
& rep
) {
201 image_skia_reps_
.push_back(rep
);
205 std::vector
<gfx::ImageSkiaRep
> image_skia_reps_
;
206 DISALLOW_COPY_AND_ASSIGN(FaviconImageSource
);
211 const float kSelectFaviconFramesInvalidScore
= -1.0f
;
213 gfx::ImageSkia
CreateFaviconImageSkia(
214 const std::vector
<SkBitmap
>& bitmaps
,
215 const std::vector
<gfx::Size
>& original_sizes
,
216 int desired_size_in_dip
,
219 const std::vector
<float>& favicon_scales
= favicon_base::GetFaviconScales();
220 std::vector
<int> desired_sizes
;
222 if (desired_size_in_dip
== 0) {
223 desired_sizes
.push_back(0);
225 for (std::vector
<float>::const_iterator iter
= favicon_scales
.begin();
226 iter
!= favicon_scales
.end(); ++iter
) {
227 desired_sizes
.push_back(
228 static_cast<int>(ceil(desired_size_in_dip
* (*iter
))));
232 std::vector
<SelectionResult
> results
;
233 GetCandidateIndicesWithBestScores(original_sizes
,
237 if (results
.size() == 0)
238 return gfx::ImageSkia();
240 if (desired_size_in_dip
== 0) {
241 size_t index
= results
[0].index
;
242 return gfx::ImageSkia(gfx::ImageSkiaRep(bitmaps
[index
], 1.0f
));
245 FaviconImageSource
* image_source
= new FaviconImageSource
;
247 for (size_t i
= 0; i
< results
.size(); ++i
) {
248 size_t index
= results
[i
].index
;
249 image_source
->AddImageSkiaRep(
250 gfx::ImageSkiaRep(GetResizedBitmap(bitmaps
[index
],
251 original_sizes
[index
],
255 return gfx::ImageSkia(image_source
,
256 gfx::Size(desired_size_in_dip
, desired_size_in_dip
));
259 void SelectFaviconFrameIndices(const std::vector
<gfx::Size
>& frame_pixel_sizes
,
260 const std::vector
<int>& desired_sizes
,
261 std::vector
<size_t>* best_indices
,
262 float* match_score
) {
263 std::vector
<SelectionResult
> results
;
264 GetCandidateIndicesWithBestScores(
265 frame_pixel_sizes
, desired_sizes
, match_score
, &results
);
267 std::set
<size_t> already_added
;
268 for (size_t i
= 0; i
< results
.size(); ++i
) {
269 size_t index
= results
[i
].index
;
270 // GetCandidateIndicesWithBestScores() will return duplicate indices if the
271 // bitmap data with |frame_pixel_sizes[index]| should be used for multiple
272 // scale factors. Remove duplicates here such that |best_indices| contains
274 if (already_added
.find(index
) == already_added
.end()) {
275 already_added
.insert(index
);
276 best_indices
->push_back(index
);