OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/browser/favicon/select_favicon_frames.h" | 5 #include "chrome/browser/favicon/select_favicon_frames.h" |
6 | 6 |
7 #include "base/logging.h" | |
7 #include "skia/ext/image_operations.h" | 8 #include "skia/ext/image_operations.h" |
9 #include "third_party/skia/include/core/SkCanvas.h" | |
8 #include "ui/gfx/image/image.h" | 10 #include "ui/gfx/image/image.h" |
9 #include "ui/gfx/image/image_skia.h" | 11 #include "ui/gfx/image/image_skia.h" |
10 #include "third_party/skia/include/core/SkCanvas.h" | 12 #include "ui/gfx/size.h" |
11 | 13 |
12 namespace { | 14 namespace { |
13 | 15 |
14 size_t BiggestCandidate(const std::vector<SkBitmap>& bitmaps) { | 16 void SizesFromBitmaps(const std::vector<SkBitmap>& bitmaps, |
17 std::vector<gfx::Size>* sizes) { | |
18 DCHECK(sizes); | |
Nico
2012/08/27 19:35:44
No need, the clear() / push_back() will blow up im
| |
19 sizes->clear(); | |
Nico
2012/08/27 19:35:44
nit: I'd omit this too. I find it clearer and more
| |
20 for (size_t i = 0; i < bitmaps.size(); ++i) | |
21 sizes->push_back(gfx::Size(bitmaps[i].width(), bitmaps[i].height())); | |
22 } | |
23 | |
24 size_t BiggestCandidate(const std::vector<gfx::Size>& candidate_sizes) { | |
15 size_t max_index = 0; | 25 size_t max_index = 0; |
16 int max_area = bitmaps[0].width() * bitmaps[0].height(); | 26 int max_area = candidate_sizes[0].GetArea(); |
17 for (size_t i = 1; i < bitmaps.size(); ++i) { | 27 for (size_t i = 1; i < candidate_sizes.size(); ++i) { |
18 int area = bitmaps[i].width() * bitmaps[i].height(); | 28 int area = candidate_sizes[i].GetArea(); |
19 if (area > max_area) { | 29 if (area > max_area) { |
20 max_area = area; | 30 max_area = area; |
21 max_index = i; | 31 max_index = i; |
22 } | 32 } |
23 } | 33 } |
24 return max_index; | 34 return max_index; |
25 } | 35 } |
26 | 36 |
27 SkBitmap PadWithBorder(const SkBitmap& contents, | 37 SkBitmap PadWithBorder(const SkBitmap& contents, |
28 int desired_size, | 38 int desired_size, |
(...skipping 24 matching lines...) Expand all Loading... | |
53 | 63 |
54 { | 64 { |
55 SkCanvas canvas(bitmap); | 65 SkCanvas canvas(bitmap); |
56 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); | 66 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); |
57 canvas.drawBitmapRect(contents, NULL, dest); | 67 canvas.drawBitmapRect(contents, NULL, dest); |
58 } | 68 } |
59 | 69 |
60 return bitmap; | 70 return bitmap; |
61 } | 71 } |
62 | 72 |
63 SkBitmap SelectCandidate(const std::vector<SkBitmap>& bitmaps, | 73 enum ResizeMethod { |
64 int desired_size, | 74 NONE, |
65 ui::ScaleFactor scale_factor, | 75 PAD_WITH_BORDER, |
66 float* score) { | 76 SAMPLE_NEAREST_NEIGHBOUR, |
67 float scale = GetScaleFactorScale(scale_factor); | 77 LANCZOS |
78 }; | |
79 | |
80 size_t GetCandidateIndexWithBestScore( | |
81 const std::vector<gfx::Size>& candidate_sizes, | |
82 ui::ScaleFactor scale_factor, | |
83 int desired_size, | |
84 float* score, | |
85 ResizeMethod* resize_method) { | |
86 float scale = ui::GetScaleFactorScale(scale_factor); | |
68 desired_size = static_cast<int>(desired_size * scale + 0.5f); | 87 desired_size = static_cast<int>(desired_size * scale + 0.5f); |
69 | 88 |
70 // Try to find an exact match. | 89 // Try to find an exact match. |
71 for (size_t i = 0; i < bitmaps.size(); ++i) { | 90 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
72 if (bitmaps[i].width() == desired_size && | 91 if (candidate_sizes[i].width() == desired_size && |
73 bitmaps[i].height() == desired_size) { | 92 candidate_sizes[i].height() == desired_size) { |
74 *score = 1; | 93 *score = 1; |
75 return bitmaps[i]; | 94 *resize_method = NONE; |
95 return i; | |
76 } | 96 } |
77 } | 97 } |
78 | 98 |
79 // If that failed, the following special rules apply: | 99 // If that failed, the following special rules apply: |
80 // 1. 17px-24px images are built from 16px images by adding | 100 // 1. 17px-24px images are built from 16px images by adding |
81 // a transparent border. | 101 // a transparent border. |
82 if (desired_size > 16 * scale && desired_size <= 24 * scale) { | 102 if (desired_size > 16 * scale && desired_size <= 24 * scale) { |
83 int source_size = static_cast<int>(16 * scale + 0.5f); | 103 int source_size = static_cast<int>(16 * scale + 0.5f); |
84 for (size_t i = 0; i < bitmaps.size(); ++i) { | 104 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
85 if (bitmaps[i].width() == source_size && | 105 if (candidate_sizes[i].width() == source_size && |
86 bitmaps[i].height() == source_size) { | 106 candidate_sizes[i].height() == source_size) { |
87 *score = 0.2f; | 107 *score = 0.2f; |
88 return PadWithBorder(bitmaps[i], desired_size, source_size); | 108 *resize_method = PAD_WITH_BORDER; |
109 return i; | |
89 } | 110 } |
90 } | 111 } |
91 // Try again, with upsizing the base variant. | 112 // Try again, with upsizing the base variant. |
92 for (size_t i = 0; i < bitmaps.size(); ++i) { | 113 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
93 if (bitmaps[i].width() * scale == source_size && | 114 if (candidate_sizes[i].width() * scale == source_size && |
94 bitmaps[i].height() * scale == source_size) { | 115 candidate_sizes[i].height() * scale == source_size) { |
95 *score = 0.15f; | 116 *score = 0.15f; |
96 return PadWithBorder(bitmaps[i], desired_size, source_size); | 117 *resize_method = PAD_WITH_BORDER; |
118 return i; | |
97 } | 119 } |
98 } | 120 } |
99 } | 121 } |
100 | 122 |
101 // 2. Integer multiples are built using nearest neighbor sampling. | 123 // 2. Integer multiples are built using nearest neighbor sampling. |
102 // 3. Else, use Lancosz scaling: | 124 // 3. Else, use Lancosz scaling: |
103 // b) If available, from the next bigger variant. | 125 // b) If available, from the next bigger variant. |
104 int candidate = -1; | 126 int candidate_index = -1; |
105 int min_area = INT_MAX; | 127 int min_area = INT_MAX; |
106 for (size_t i = 0; i < bitmaps.size(); ++i) { | 128 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
107 int area = bitmaps[i].width() * bitmaps[i].height(); | 129 int area = candidate_sizes[i].GetArea(); |
108 if (bitmaps[i].width() > desired_size && | 130 if (candidate_sizes[i].width() > desired_size && |
109 bitmaps[i].height() > desired_size && | 131 candidate_sizes[i].height() > desired_size && |
110 (candidate == -1 || area < min_area)) { | 132 (candidate_index == -1 || area < min_area)) { |
111 candidate = i; | 133 candidate_index = i; |
112 min_area = area; | 134 min_area = area; |
113 } | 135 } |
114 } | 136 } |
115 *score = 0.1f; | 137 *score = 0.1f; |
116 // c) Else, from the biggest smaller variant. | 138 // c) Else, from the biggest smaller variant. |
117 if (candidate == -1) { | 139 if (candidate_index == -1) { |
118 *score = 0; | 140 *score = 0; |
119 candidate = BiggestCandidate(bitmaps); | 141 candidate_index = BiggestCandidate(candidate_sizes); |
120 } | 142 } |
121 | 143 |
122 const SkBitmap& bitmap = bitmaps[candidate]; | 144 const gfx::Size& candidate_size = candidate_sizes[candidate_index]; |
123 bool is_integer_multiple = desired_size % bitmap.width() == 0 && | 145 bool is_integer_multiple = desired_size % candidate_size.width() == 0 && |
124 desired_size % bitmap.height() == 0; | 146 desired_size % candidate_size.height() == 0; |
125 if (is_integer_multiple) | 147 *resize_method = is_integer_multiple ? SAMPLE_NEAREST_NEIGHBOUR : LANCZOS; |
126 return SampleNearestNeighbor(bitmap, desired_size); | 148 return candidate_index; |
127 return skia::ImageOperations::Resize( | 149 } |
128 bitmap, skia::ImageOperations::RESIZE_LANCZOS3, | 150 |
129 desired_size, desired_size); | 151 // Represents the index of the best candidate for a |scale_factor| from the |
152 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores(). | |
153 struct SelectionResult { | |
154 // index in |candidate_sizes| of the best candidate. | |
155 size_t index; | |
156 | |
157 // The ScaleFactor for which |index| is the best candidate. | |
158 ui::ScaleFactor scale_factor; | |
159 | |
160 // How the bitmap data that the bitmap with |candidate_sizes[index]| should | |
161 // be resized for displaying in the UI. | |
162 ResizeMethod resize_method; | |
163 }; | |
164 | |
165 void GetCandidateIndicesWithBestScores( | |
166 const std::vector<gfx::Size>& candidate_sizes, | |
167 const std::vector<ui::ScaleFactor>& scale_factors, | |
168 int desired_size, | |
169 float* match_score, | |
170 std::vector<SelectionResult>* results) { | |
Nico
2012/08/27 19:35:44
(You don't DCHECK / clear |results| either)
| |
171 if (candidate_sizes.empty()) | |
172 return; | |
173 | |
174 if (desired_size == 0) { | |
175 // Just return the biggest image available. | |
176 SelectionResult result; | |
177 result.index = BiggestCandidate(candidate_sizes); | |
178 result.scale_factor = ui::SCALE_FACTOR_100P; | |
179 result.resize_method = NONE; | |
180 results->push_back(result); | |
181 if (match_score) | |
182 *match_score = 0.8f; | |
183 return; | |
184 } | |
185 | |
186 float total_score = 0; | |
187 for (size_t i = 0; i < scale_factors.size(); ++i) { | |
188 float score; | |
189 SelectionResult result; | |
190 result.scale_factor = scale_factors[i]; | |
191 result.index = GetCandidateIndexWithBestScore(candidate_sizes, | |
192 result.scale_factor, desired_size, &score, &result.resize_method); | |
193 results->push_back(result); | |
194 total_score += score; | |
195 } | |
196 | |
197 if (match_score) | |
198 *match_score = total_score / scale_factors.size(); | |
130 } | 199 } |
131 | 200 |
132 } // namespace | 201 } // namespace |
133 | 202 |
134 gfx::ImageSkia SelectFaviconFrames( | 203 gfx::ImageSkia SelectFaviconFrames( |
135 const std::vector<SkBitmap>& bitmaps, | 204 const std::vector<SkBitmap>& bitmaps, |
136 const std::vector<ui::ScaleFactor>& scale_factors, | 205 const std::vector<ui::ScaleFactor>& scale_factors, |
137 int desired_size, | 206 int desired_size, |
138 float* match_score) { | 207 float* match_score) { |
208 std::vector<gfx::Size> candidate_sizes; | |
209 SizesFromBitmaps(bitmaps, &candidate_sizes); | |
210 | |
211 std::vector<SelectionResult> results; | |
212 GetCandidateIndicesWithBestScores(candidate_sizes, scale_factors, | |
213 desired_size, match_score, &results); | |
214 | |
139 gfx::ImageSkia multi_image; | 215 gfx::ImageSkia multi_image; |
140 if (bitmaps.empty()) | 216 for (size_t i = 0; i < results.size(); ++i) { |
Nico
2012/08/27 19:35:44
style nit: It helps readability if every action in
| |
141 return multi_image; | 217 const SelectionResult& result = results[i]; |
218 float scale = ui::GetScaleFactorScale(result.scale_factor); | |
219 int scaled_desired_size = static_cast<int>(desired_size * scale + 0.5f); | |
220 SkBitmap best_bitmap = bitmaps[result.index]; | |
142 | 221 |
143 if (desired_size == 0) { | 222 switch(result.resize_method) { |
144 // Just return the biggest image available. | 223 case PAD_WITH_BORDER: { |
145 size_t max_index = BiggestCandidate(bitmaps); | 224 int scaled_16 = static_cast<int>(16 * scale + 0.5f); |
225 best_bitmap = PadWithBorder(best_bitmap, scaled_desired_size, | |
226 scaled_16); | |
227 break; | |
228 } | |
229 case SAMPLE_NEAREST_NEIGHBOUR: | |
230 best_bitmap = SampleNearestNeighbor(best_bitmap, scaled_desired_size); | |
231 break; | |
232 case LANCZOS: | |
233 best_bitmap = skia::ImageOperations::Resize( | |
234 best_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, | |
235 scaled_desired_size, scaled_desired_size); | |
236 break; | |
237 default: | |
Nico
2012/08/27 19:35:44
Say `case NONE:` here, then compilers will warn if
| |
238 break; // NONE | |
239 } | |
146 multi_image.AddRepresentation( | 240 multi_image.AddRepresentation( |
147 gfx::ImageSkiaRep(bitmaps[max_index], ui::SCALE_FACTOR_100P)); | 241 gfx::ImageSkiaRep(best_bitmap, result.scale_factor)); |
148 if (match_score) | |
149 *match_score = 0.8f; | |
150 return multi_image; | |
151 } | 242 } |
152 | |
153 float total_score = 0; | |
154 for (size_t i = 0; i < scale_factors.size(); ++i) { | |
155 float score; | |
156 multi_image.AddRepresentation(gfx::ImageSkiaRep( | |
157 SelectCandidate(bitmaps, desired_size, scale_factors[i], &score), | |
158 scale_factors[i])); | |
159 total_score += score; | |
160 } | |
161 | |
162 if (match_score) | |
163 *match_score = total_score / scale_factors.size(); | |
164 return multi_image; | 243 return multi_image; |
165 } | 244 } |
OLD | NEW |