OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/favicon_base/select_favicon_frames.h" | 5 #include "components/favicon_base/select_favicon_frames.h" |
6 | 6 |
| 7 #include <algorithm> |
| 8 #include <cmath> |
7 #include <limits> | 9 #include <limits> |
| 10 #include <map> |
8 #include <set> | 11 #include <set> |
9 | 12 |
10 #include "skia/ext/image_operations.h" | 13 #include "skia/ext/image_operations.h" |
11 #include "third_party/skia/include/core/SkCanvas.h" | 14 #include "third_party/skia/include/core/SkCanvas.h" |
12 #include "ui/gfx/image/image.h" | 15 #include "ui/gfx/image/image.h" |
13 #include "ui/gfx/image/image_skia.h" | 16 #include "ui/gfx/image/image_skia.h" |
14 #include "ui/gfx/size.h" | 17 #include "ui/gfx/size.h" |
15 | 18 |
16 namespace { | 19 namespace { |
17 | 20 |
(...skipping 22 matching lines...) Expand all Loading... |
40 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); | 43 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); |
41 canvas.drawBitmapRect(contents, NULL, dest); | 44 canvas.drawBitmapRect(contents, NULL, dest); |
42 } | 45 } |
43 | 46 |
44 return bitmap; | 47 return bitmap; |
45 } | 48 } |
46 | 49 |
47 enum ResizeMethod { NONE, SAMPLE_NEAREST_NEIGHBOUR, LANCZOS }; | 50 enum ResizeMethod { NONE, SAMPLE_NEAREST_NEIGHBOUR, LANCZOS }; |
48 | 51 |
49 size_t GetCandidateIndexWithBestScore( | 52 size_t GetCandidateIndexWithBestScore( |
50 const std::vector<gfx::Size>& candidate_sizes_in_pixel, | 53 const std::vector<gfx::Size>& candidate_sizes, |
51 ui::ScaleFactor scale_factor, | 54 int desired_size, |
52 int desired_size_in_dip, | |
53 float* score, | 55 float* score, |
54 ResizeMethod* resize_method) { | 56 ResizeMethod* resize_method) { |
55 DCHECK_NE(desired_size_in_dip, 0); | 57 DCHECK_NE(desired_size, 0); |
56 | |
57 float scale = ui::GetScaleForScaleFactor(scale_factor); | |
58 int desired_size_in_pixel = | |
59 static_cast<int>(desired_size_in_dip * scale + 0.5f); | |
60 | 58 |
61 // Try to find an exact match. | 59 // Try to find an exact match. |
62 for (size_t i = 0; i < candidate_sizes_in_pixel.size(); ++i) { | 60 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
63 if (candidate_sizes_in_pixel[i].width() == desired_size_in_pixel && | 61 if (candidate_sizes[i].width() == desired_size && |
64 candidate_sizes_in_pixel[i].height() == desired_size_in_pixel) { | 62 candidate_sizes[i].height() == desired_size) { |
65 *score = 1; | 63 *score = 1; |
66 *resize_method = NONE; | 64 *resize_method = NONE; |
67 return i; | 65 return i; |
68 } | 66 } |
69 } | 67 } |
70 | 68 |
71 // Huge favicon bitmaps often have a completely different visual style from | 69 // Huge favicon bitmaps often have a completely different visual style from |
72 // smaller favicon bitmaps. Avoid these favicon bitmaps when a favicon of | 70 // smaller favicon bitmaps. Avoid them. |
73 // gfx::kFaviconSize DIP is requested. | 71 const int kHugeEdgeSize = desired_size * 8; |
74 const int kHugeEdgeSizeInPixel = desired_size_in_pixel * 8; | |
75 | 72 |
76 // Order of preference: | 73 // Order of preference: |
77 // 1) Bitmaps with width and height smaller than |kHugeEdgeSizeInPixel|. | 74 // 1) Bitmaps with width and height smaller than |kHugeEdgeSize|. |
78 // 2) Bitmaps which need to be scaled down instead of up. | 75 // 2) Bitmaps which need to be scaled down instead of up. |
79 // 3) Bitmaps which do not need to be scaled as much. | 76 // 3) Bitmaps which do not need to be scaled as much. |
80 size_t candidate_index = std::numeric_limits<size_t>::max(); | 77 size_t candidate_index = std::numeric_limits<size_t>::max(); |
81 float candidate_score = 0; | 78 float candidate_score = 0; |
82 for (size_t i = 0; i < candidate_sizes_in_pixel.size(); ++i) { | 79 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
83 float average_edge_in_pixel = (candidate_sizes_in_pixel[i].width() + | 80 float average_edge = |
84 candidate_sizes_in_pixel[i].height()) / | 81 (candidate_sizes[i].width() + candidate_sizes[i].height()) / 2.0f; |
85 2.0f; | |
86 | 82 |
87 float score = 0; | 83 float score = 0; |
88 if (candidate_sizes_in_pixel[i].width() >= kHugeEdgeSizeInPixel || | 84 if (candidate_sizes[i].width() >= kHugeEdgeSize || |
89 candidate_sizes_in_pixel[i].height() >= kHugeEdgeSizeInPixel) { | 85 candidate_sizes[i].height() >= kHugeEdgeSize) { |
90 score = | 86 score = std::min(1.0f, desired_size / average_edge) * 0.01f; |
91 std::min(1.0f, desired_size_in_pixel / average_edge_in_pixel) * 0.01f; | 87 } else if (candidate_sizes[i].width() >= desired_size && |
92 } else if (candidate_sizes_in_pixel[i].width() >= desired_size_in_pixel && | 88 candidate_sizes[i].height() >= desired_size) { |
93 candidate_sizes_in_pixel[i].height() >= desired_size_in_pixel) { | 89 score = desired_size / average_edge * 0.01f + 0.15f; |
94 score = desired_size_in_pixel / average_edge_in_pixel * 0.01f + 0.15f; | |
95 } else { | 90 } else { |
96 score = std::min(1.0f, average_edge_in_pixel / desired_size_in_pixel) * | 91 score = std::min(1.0f, average_edge / desired_size) * |
97 0.01f + | 92 0.01f + |
98 0.1f; | 93 0.1f; |
99 } | 94 } |
100 | 95 |
101 if (candidate_index == std::numeric_limits<size_t>::max() || | 96 if (candidate_index == std::numeric_limits<size_t>::max() || |
102 score > candidate_score) { | 97 score > candidate_score) { |
103 candidate_index = i; | 98 candidate_index = i; |
104 candidate_score = score; | 99 candidate_score = score; |
105 } | 100 } |
106 } | 101 } |
107 *score = candidate_score; | 102 *score = candidate_score; |
108 | 103 |
109 // Integer multiples are built using nearest neighbor sampling. Otherwise, | 104 // Integer multiples are built using nearest neighbor sampling. Otherwise, |
110 // Lanczos scaling is used. | 105 // Lanczos scaling is used. |
111 const gfx::Size& candidate_size_in_pixel = | 106 const gfx::Size& candidate_size = candidate_sizes[candidate_index]; |
112 candidate_sizes_in_pixel[candidate_index]; | 107 if (candidate_size.IsEmpty()) { |
113 if (candidate_size_in_pixel.IsEmpty()) { | |
114 *resize_method = NONE; | 108 *resize_method = NONE; |
115 } else if (desired_size_in_pixel % candidate_size_in_pixel.width() == 0 && | 109 } else if (desired_size % candidate_size.width() == 0 && |
116 desired_size_in_pixel % candidate_size_in_pixel.height() == 0) { | 110 desired_size % candidate_size.height() == 0) { |
117 *resize_method = SAMPLE_NEAREST_NEIGHBOUR; | 111 *resize_method = SAMPLE_NEAREST_NEIGHBOUR; |
118 } else { | 112 } else { |
119 *resize_method = LANCZOS; | 113 *resize_method = LANCZOS; |
120 } | 114 } |
121 return candidate_index; | 115 return candidate_index; |
122 } | 116 } |
123 | 117 |
124 // Represents the index of the best candidate for a |scale_factor| from the | 118 // Represents the index of the best candidate for |desired_size| from the |
125 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores(). | 119 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores(). |
126 struct SelectionResult { | 120 struct SelectionResult { |
127 // index in |candidate_sizes| of the best candidate. | 121 // index in |candidate_sizes| of the best candidate. |
128 size_t index; | 122 size_t index; |
129 | 123 |
130 // The ScaleFactor for which |index| is the best candidate. | 124 // The desired size for which |index| is the best candidate. |
131 ui::ScaleFactor scale_factor; | 125 int desired_size; |
132 | 126 |
133 // How the bitmap data that the bitmap with |candidate_sizes[index]| should | 127 // How the bitmap data that the bitmap with |candidate_sizes[index]| should |
134 // be resized for displaying in the UI. | 128 // be resized for displaying in the UI. |
135 ResizeMethod resize_method; | 129 ResizeMethod resize_method; |
136 }; | 130 }; |
137 | 131 |
138 void GetCandidateIndicesWithBestScores( | 132 void GetCandidateIndicesWithBestScores( |
139 const std::vector<gfx::Size>& candidate_sizes, | 133 const std::vector<gfx::Size>& candidate_sizes, |
140 const std::vector<ui::ScaleFactor>& scale_factors, | 134 const std::vector<int>& desired_sizes, |
141 int desired_size, | |
142 float* match_score, | 135 float* match_score, |
143 std::vector<SelectionResult>* results) { | 136 std::vector<SelectionResult>* results) { |
144 if (candidate_sizes.empty()) { | 137 if (candidate_sizes.empty() || desired_sizes.empty()) { |
145 if (match_score) | 138 if (match_score) |
146 *match_score = 0.0f; | 139 *match_score = 0.0f; |
147 return; | 140 return; |
148 } | 141 } |
149 | 142 |
150 if (desired_size == 0) { | 143 std::vector<int>::const_iterator zero_size_it = std::find( |
| 144 desired_sizes.begin(), desired_sizes.end(), 0); |
| 145 if (zero_size_it != desired_sizes.end()) { |
151 // Just return the biggest image available. | 146 // Just return the biggest image available. |
152 SelectionResult result; | 147 SelectionResult result; |
153 result.index = BiggestCandidate(candidate_sizes); | 148 result.index = BiggestCandidate(candidate_sizes); |
154 result.scale_factor = ui::SCALE_FACTOR_100P; | 149 result.desired_size = 0; |
155 result.resize_method = NONE; | 150 result.resize_method = NONE; |
156 results->push_back(result); | 151 results->push_back(result); |
157 if (match_score) | 152 if (match_score) |
158 *match_score = 1.0f; | 153 *match_score = 1.0f; |
159 return; | 154 return; |
160 } | 155 } |
161 | 156 |
162 float total_score = 0; | 157 float total_score = 0; |
163 for (size_t i = 0; i < scale_factors.size(); ++i) { | 158 for (size_t i = 0; i < desired_sizes.size(); ++i) { |
164 float score; | 159 float score; |
165 SelectionResult result; | 160 SelectionResult result; |
166 result.scale_factor = scale_factors[i]; | 161 result.desired_size = desired_sizes[i]; |
167 result.index = GetCandidateIndexWithBestScore(candidate_sizes, | 162 result.index = GetCandidateIndexWithBestScore(candidate_sizes, |
168 result.scale_factor, | 163 result.desired_size, |
169 desired_size, | |
170 &score, | 164 &score, |
171 &result.resize_method); | 165 &result.resize_method); |
172 results->push_back(result); | 166 results->push_back(result); |
173 total_score += score; | 167 total_score += score; |
174 } | 168 } |
175 | 169 |
176 if (match_score) | 170 if (match_score) |
177 *match_score = total_score / scale_factors.size(); | 171 *match_score = total_score / desired_sizes.size(); |
178 } | 172 } |
179 | 173 |
180 // Resize |source_bitmap| using |resize_method|. | 174 // Resize |source_bitmap| using |resize_method|. |
181 SkBitmap GetResizedBitmap(const SkBitmap& source_bitmap, | 175 SkBitmap GetResizedBitmap(const SkBitmap& source_bitmap, |
182 int desired_size_in_dip, | 176 int desired_size, |
183 ui::ScaleFactor scale_factor, | |
184 ResizeMethod resize_method) { | 177 ResizeMethod resize_method) { |
185 float scale = ui::GetScaleForScaleFactor(scale_factor); | |
186 int desired_size_in_pixel = | |
187 static_cast<int>(desired_size_in_dip * scale + 0.5f); | |
188 | |
189 switch (resize_method) { | 178 switch (resize_method) { |
190 case NONE: | 179 case NONE: |
191 return source_bitmap; | 180 return source_bitmap; |
192 case SAMPLE_NEAREST_NEIGHBOUR: | 181 case SAMPLE_NEAREST_NEIGHBOUR: |
193 return SampleNearestNeighbor(source_bitmap, desired_size_in_pixel); | 182 return SampleNearestNeighbor(source_bitmap, desired_size); |
194 case LANCZOS: | 183 case LANCZOS: |
195 return skia::ImageOperations::Resize( | 184 return skia::ImageOperations::Resize( |
196 source_bitmap, | 185 source_bitmap, |
197 skia::ImageOperations::RESIZE_LANCZOS3, | 186 skia::ImageOperations::RESIZE_LANCZOS3, |
198 desired_size_in_pixel, | 187 desired_size, |
199 desired_size_in_pixel); | 188 desired_size); |
200 } | 189 } |
201 return source_bitmap; | 190 return source_bitmap; |
202 } | 191 } |
203 | 192 |
204 } // namespace | 193 } // namespace |
205 | 194 |
206 const float kSelectFaviconFramesInvalidScore = -1.0f; | 195 const float kSelectFaviconFramesInvalidScore = -1.0f; |
207 | 196 |
208 gfx::ImageSkia SelectFaviconFrames( | 197 gfx::ImageSkia SelectFaviconFrames( |
209 const std::vector<SkBitmap>& bitmaps, | 198 const std::vector<SkBitmap>& bitmaps, |
210 const std::vector<gfx::Size>& original_sizes, | 199 const std::vector<gfx::Size>& original_sizes, |
211 const std::vector<ui::ScaleFactor>& scale_factors, | 200 const std::vector<ui::ScaleFactor>& scale_factors, |
212 int desired_size, | 201 int desired_size_in_dip, |
213 float* match_score) { | 202 float* match_score) { |
| 203 std::vector<int> desired_sizes; |
| 204 std::map<int, float> scale_map; |
| 205 if (desired_size_in_dip == 0) { |
| 206 desired_sizes.push_back(0); |
| 207 scale_map[0] = 1.0f; |
| 208 } else { |
| 209 for (size_t i = 0; i < scale_factors.size(); ++i) { |
| 210 float scale = ui::GetScaleForScaleFactor(scale_factors[i]); |
| 211 int desired_size = ceil(desired_size_in_dip * scale); |
| 212 desired_sizes.push_back(desired_size); |
| 213 scale_map[desired_size] = scale; |
| 214 } |
| 215 } |
| 216 |
214 std::vector<SelectionResult> results; | 217 std::vector<SelectionResult> results; |
215 GetCandidateIndicesWithBestScores( | 218 GetCandidateIndicesWithBestScores( |
216 original_sizes, scale_factors, desired_size, match_score, &results); | 219 original_sizes, desired_sizes, match_score, &results); |
217 | 220 |
218 gfx::ImageSkia multi_image; | 221 gfx::ImageSkia multi_image; |
219 for (size_t i = 0; i < results.size(); ++i) { | 222 for (size_t i = 0; i < results.size(); ++i) { |
220 const SelectionResult& result = results[i]; | 223 const SelectionResult& result = results[i]; |
221 SkBitmap resized_bitmap = GetResizedBitmap(bitmaps[result.index], | 224 SkBitmap resized_bitmap = GetResizedBitmap(bitmaps[result.index], |
222 desired_size, | 225 result.desired_size, |
223 result.scale_factor, | |
224 result.resize_method); | 226 result.resize_method); |
225 multi_image.AddRepresentation(gfx::ImageSkiaRep( | 227 |
226 resized_bitmap, ui::GetScaleForScaleFactor(result.scale_factor))); | 228 std::map<int, float>::const_iterator it = scale_map.find( |
| 229 result.desired_size); |
| 230 DCHECK(it != scale_map.end()); |
| 231 float scale = it->second; |
| 232 multi_image.AddRepresentation(gfx::ImageSkiaRep(resized_bitmap, scale)); |
227 } | 233 } |
228 return multi_image; | 234 return multi_image; |
229 } | 235 } |
230 | 236 |
231 void SelectFaviconFrameIndices( | 237 void SelectFaviconFrameIndices( |
232 const std::vector<gfx::Size>& frame_pixel_sizes, | 238 const std::vector<gfx::Size>& frame_pixel_sizes, |
233 const std::vector<ui::ScaleFactor>& scale_factors, | 239 const std::vector<int>& desired_sizes, |
234 int desired_size, | |
235 std::vector<size_t>* best_indices, | 240 std::vector<size_t>* best_indices, |
236 float* match_score) { | 241 float* match_score) { |
237 std::vector<SelectionResult> results; | 242 std::vector<SelectionResult> results; |
238 GetCandidateIndicesWithBestScores( | 243 GetCandidateIndicesWithBestScores( |
239 frame_pixel_sizes, scale_factors, desired_size, match_score, &results); | 244 frame_pixel_sizes, desired_sizes, match_score, &results); |
240 | 245 |
241 std::set<size_t> already_added; | 246 std::set<size_t> already_added; |
242 for (size_t i = 0; i < results.size(); ++i) { | 247 for (size_t i = 0; i < results.size(); ++i) { |
243 size_t index = results[i].index; | 248 size_t index = results[i].index; |
244 // GetCandidateIndicesWithBestScores() will return duplicate indices if the | 249 // GetCandidateIndicesWithBestScores() will return duplicate indices if the |
245 // bitmap data with |frame_pixel_sizes[index]| should be used for multiple | 250 // bitmap data with |frame_pixel_sizes[index]| should be used for multiple |
246 // scale factors. Remove duplicates here such that |best_indices| contains | 251 // scale factors. Remove duplicates here such that |best_indices| contains |
247 // no duplicates. | 252 // no duplicates. |
248 if (already_added.find(index) == already_added.end()) { | 253 if (already_added.find(index) == already_added.end()) { |
249 already_added.insert(index); | 254 already_added.insert(index); |
250 best_indices->push_back(index); | 255 best_indices->push_back(index); |
251 } | 256 } |
252 } | 257 } |
253 } | 258 } |
OLD | NEW |