OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2017 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 "components/favicon/core/favicon_selector.h" | |
6 | |
7 #include <algorithm> | |
8 #include <functional> | |
9 #include <utility> | |
10 | |
11 #include "components/favicon_base/favicon_util.h" | |
12 #include "components/favicon_base/select_favicon_frames.h" | |
13 #include "ui/gfx/favicon_size.h" | |
14 | |
15 namespace favicon { | |
16 namespace { | |
17 | |
18 // Size (along each axis) of a touch icon. | |
19 #if defined(OS_IOS) | |
20 // This currently corresponds to the apple touch icon for iPad. | |
21 const int kLargestIconSizeInPixels = 144; | |
22 #else | |
23 const int kLargestIconSizeInPixels = 192; | |
24 #endif | |
25 | |
26 // Compare function used for std::stable_sort to sort as descend. | |
27 bool CompareScore(const FaviconSelector::Candidate& lhs, | |
28 const FaviconSelector::Candidate& rhs) { | |
29 return lhs.score > rhs.score; | |
30 } | |
31 | |
32 bool ComparePixelSize(const FaviconSelector::TargetSizeSpec& lhs, | |
33 const FaviconSelector::TargetSizeSpec& rhs) { | |
34 return lhs.pixel_size() > rhs.pixel_size(); | |
35 } | |
36 | |
37 // Return true if |bitmap_result| is valid. | |
38 bool IsValid(const favicon_base::FaviconRawBitmapResult& bitmap_result) { | |
39 return bitmap_result.is_valid(); | |
40 } | |
41 | |
42 // Returns true if at least one of |bitmap_results| is valid. | |
43 bool HasValidResult( | |
44 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) { | |
45 return std::find_if(bitmap_results.begin(), bitmap_results.end(), IsValid) != | |
46 bitmap_results.end(); | |
47 } | |
48 | |
49 } // namespace | |
50 | |
51 // static | |
52 FaviconSelector::TargetSizeSpec FaviconSelector::TargetSizeSpec::ForLargest() { | |
53 FaviconSelector::TargetSizeSpec target_size_spec; | |
54 target_size_spec.pixel_size_ = kLargestIconSizeInPixels; | |
55 // 0 is special-cased in CreateFaviconImageSkiaWithScaleFactors(). | |
56 target_size_spec.scale_factor_ = 0; | |
57 target_size_spec.ensure_exact_size_ = false; | |
58 return target_size_spec; | |
59 } | |
60 | |
61 // static | |
62 std::vector<FaviconSelector::TargetSizeSpec> | |
63 FaviconSelector::TargetSizeSpec::For16x16Dips() { | |
64 std::vector<FaviconSelector::TargetSizeSpec> target_size_specs; | |
65 std::vector<float> scale_factors = favicon_base::GetFaviconScales(); | |
66 std::sort(scale_factors.begin(), scale_factors.end(), std::greater<float>()); | |
67 for (float scale : scale_factors) { | |
68 FaviconSelector::TargetSizeSpec target_size_spec; | |
69 target_size_spec.pixel_size_ = std::ceil(scale * gfx::kFaviconSize); | |
70 target_size_spec.scale_factor_ = scale; | |
71 target_size_spec.ensure_exact_size_ = true; | |
72 target_size_specs.push_back(target_size_spec); | |
73 } | |
74 return target_size_specs; | |
75 } | |
76 | |
77 FaviconSelector::TargetSizeSpec::TargetSizeSpec(const TargetSizeSpec&) = | |
78 default; | |
79 | |
80 FaviconSelector::TargetSizeSpec::~TargetSizeSpec() = default; | |
81 | |
82 FaviconSelector::TargetSizeSpec::TargetSizeSpec(TargetSizeSpec&&) = default; | |
83 | |
84 FaviconSelector::TargetSizeSpec& FaviconSelector::TargetSizeSpec::operator=( | |
85 FaviconSelector::TargetSizeSpec&&) = default; | |
86 | |
87 bool FaviconSelector::TargetSizeSpec::WantsBestBitmapOnly() const { | |
88 return !ensure_exact_size_; | |
89 } | |
90 | |
91 bool FaviconSelector::TargetSizeSpec::HasSatisfyingResult( | |
92 const std::vector<favicon_base::FaviconRawBitmapResult>& bitmap_results) | |
93 const { | |
94 if (WantsBestBitmapOnly()) | |
95 return HasValidResult(bitmap_results); | |
96 | |
97 const gfx::Size desired_size(pixel_size_, pixel_size_); | |
98 for (const auto& bitmap_result : bitmap_results) { | |
99 if (IsValid(bitmap_result) && bitmap_result.pixel_size == desired_size) | |
100 return true; | |
101 } | |
102 return false; | |
103 } | |
104 | |
105 std::list<FaviconSelector::Candidate> | |
106 FaviconSelector::TargetSizeSpec::SortAndPruneCandidates( | |
107 const std::vector<favicon::FaviconURL>& favicon_urls) const { | |
108 std::vector<Candidate> candidates; | |
109 for (const favicon::FaviconURL& favicon_url : favicon_urls) | |
110 candidates.push_back(Candidate::FromFaviconURL(favicon_url, pixel_size_)); | |
111 | |
112 std::stable_sort(candidates.begin(), candidates.end(), CompareScore); | |
113 std::list<Candidate> result; | |
114 std::move(candidates.begin(), candidates.end(), std::back_inserter(result)); | |
115 return result; | |
116 } | |
117 | |
118 FaviconSelector::TargetSizeSpec::TargetSizeSpec() = default; | |
119 | |
120 //////////////////////////////////////////////////////////////////////////////// | |
121 | |
122 // static | |
123 FaviconSelector::Candidate FaviconSelector::Candidate::FromFaviconURL( | |
124 const favicon::FaviconURL& favicon_url, | |
125 int target_pixel_size) { | |
126 FaviconSelector::Candidate candidate; | |
127 candidate.icon_url = favicon_url.icon_url; | |
128 candidate.icon_type = favicon_url.icon_type; | |
129 candidate.score = 0; | |
130 for (const gfx::Size& favicon_size : favicon_url.icon_sizes) { | |
pkotwicz
2017/03/20 03:25:16
If you passed in a vector of target sizes (std::ve
mastiz
2017/03/20 08:07:28
This deserves some discussion, because my proposal
| |
131 candidate.score = | |
132 std::max(candidate.score, | |
133 GetFaviconCandidateScore(favicon_size, target_pixel_size)); | |
134 } | |
135 return candidate; | |
136 } | |
137 | |
138 //////////////////////////////////////////////////////////////////////////////// | |
139 | |
140 // static | |
141 std::vector<FaviconSelector> FaviconSelector::BuildMultiple( | |
142 const std::vector<TargetSizeSpec>& target_size_specs, | |
143 const std::vector<favicon::FaviconURL>& candidates) { | |
144 DCHECK(std::is_sorted(target_size_specs.begin(), target_size_specs.end(), | |
145 &ComparePixelSize)); | |
146 std::vector<FaviconSelector> selectors; | |
147 for (const TargetSizeSpec& target_size_spec : target_size_specs) | |
148 selectors.emplace_back(target_size_spec, candidates); | |
149 return selectors; | |
150 } | |
151 | |
152 FaviconSelector::FaviconSelector( | |
153 const FaviconSelector::TargetSizeSpec& target_size_spec, | |
154 const std::vector<favicon::FaviconURL>& candidates) | |
155 : target_size_spec_(target_size_spec), | |
156 pending_candidates_(target_size_spec.SortAndPruneCandidates(candidates)) { | |
157 DCHECK(!pending_candidates_.empty()); | |
158 } | |
159 | |
160 FaviconSelector::~FaviconSelector() = default; | |
161 | |
162 FaviconSelector::FaviconSelector(FaviconSelector&&) = default; | |
163 | |
164 FaviconSelector& FaviconSelector::operator=(FaviconSelector&&) = default; | |
165 | |
166 bool FaviconSelector::IsSatisfied() const { | |
167 if (!best_candidate_) | |
168 return false; | |
169 | |
170 if (target_size_spec_.WantsBestBitmapOnly()) { | |
171 // The next candidate must have size attributes that are more promising than | |
172 // the best candidate so far. Otherwise, all favicon without sizes attribute | |
173 // are downloaded. | |
174 // TODO(mastiz): This seems useful for sites that have reported the wrong | |
175 // size. Consider simplifying and returning true here. | |
176 return pending_candidates_.empty() || pending_candidates_.front().score <= | |
177 best_candidate_->candidate.score; | |
178 } else { | |
179 // For exact sizes (i.e. not best only), we download all candidates until an | |
180 // exact match is found. | |
181 return best_candidate_->original_size.width() == | |
182 target_size_spec_.pixel_size() && | |
183 best_candidate_->original_size.height() == | |
184 target_size_spec_.pixel_size(); | |
185 } | |
186 } | |
187 | |
188 // Returns the next candidate to be processed by populating |*candidate|. | |
189 // Returns false if the queue is empty. | |
190 base::Optional<FaviconSelector::Candidate> FaviconSelector::DequeueCandidate() { | |
191 if (pending_candidates_.empty() || IsSatisfied()) | |
192 return base::Optional<Candidate>(); | |
193 | |
194 base::Optional<Candidate> result = std::move(pending_candidates_.front()); | |
195 pending_candidates_.pop_front(); | |
196 return result; | |
197 } | |
198 | |
199 const FaviconSelector::Candidate* FaviconSelector::CurrentCandidate() const { | |
200 if (pending_candidates_.empty() || IsSatisfied()) | |
201 return nullptr; | |
202 | |
203 return &pending_candidates_.front(); | |
204 } | |
205 | |
206 bool FaviconSelector::ProcessDownloadedImage( | |
207 const GURL& icon_url, | |
208 favicon_base::IconType icon_type, | |
209 const std::vector<SkBitmap>& bitmaps, | |
210 const std::vector<gfx::Size>& original_sizes) { | |
211 // Remove potentially queued candidates for |icon_url| (this happens if the | |
212 // candidate was dequeued from another queue). | |
213 pending_candidates_.remove_if([&icon_url](const Candidate& candidate) { | |
214 return candidate.icon_url == icon_url; | |
215 }); | |
216 | |
217 // Select the best bitmap, which to be relevant needs to be better than the | |
218 // best known bitmap until now. | |
219 float best_score = best_candidate_ ? best_candidate_->candidate.score : -1; | |
220 size_t best_bitmap_index = bitmaps.size(); | |
221 for (size_t i = 0; i < bitmaps.size(); ++i) { | |
222 const SkBitmap& bitmap = bitmaps[i]; | |
223 const float bitmap_score = | |
224 GetFaviconCandidateScore(gfx::Size(bitmap.width(), bitmap.height()), | |
225 target_size_spec_.pixel_size()); | |
226 if (bitmap_score > best_score) { | |
227 best_bitmap_index = i; | |
228 best_score = bitmap_score; | |
229 } | |
230 } | |
231 | |
232 // No interesting bitmap, nothing to be done (|icon_url| has already been | |
233 // removed from the queue). | |
234 if (best_bitmap_index == bitmaps.size()) | |
235 return false; | |
236 | |
237 Candidate candidate{icon_url, icon_type, best_score}; | |
238 best_candidate_.emplace(BestCandidate{std::move(candidate), | |
239 original_sizes[best_bitmap_index], | |
240 bitmaps[best_bitmap_index]}); | |
241 return true; | |
242 } | |
243 | |
244 } // namespace favicon | |
OLD | NEW |