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 <algorithm> |
| 8 |
| 9 #include "base/logging.h" |
7 #include "skia/ext/image_operations.h" | 10 #include "skia/ext/image_operations.h" |
8 #include "ui/gfx/image/image.h" | 11 #include "ui/gfx/image/image.h" |
9 #include "ui/gfx/image/image_skia.h" | 12 #include "ui/gfx/image/image_skia.h" |
10 #include "third_party/skia/include/core/SkCanvas.h" | 13 #include "third_party/skia/include/core/SkCanvas.h" |
11 | 14 |
12 namespace { | 15 namespace { |
13 | 16 |
14 size_t BiggestCandidate(const std::vector<SkBitmap>& bitmaps) { | 17 void SizesFromBitmaps(const std::vector<SkBitmap>& bitmaps, |
| 18 std::vector<gfx::Size>* sizes) { |
| 19 DCHECK(sizes); |
| 20 sizes->clear(); |
| 21 for (size_t i = 0; i < bitmaps.size(); ++i) |
| 22 sizes->push_back(gfx::Size(bitmaps[i].width(), bitmaps[i].height())); |
| 23 } |
| 24 |
| 25 void SizesFromFaviconBitmapIDSizeListing( |
| 26 const std::vector<history::FaviconBitmapIDSize> favicon_id_size_listing, |
| 27 std::vector<gfx::Size>* sizes) { |
| 28 DCHECK(sizes); |
| 29 sizes->clear(); |
| 30 for (size_t i = 0; i < favicon_id_size_listing.size(); ++i) |
| 31 sizes->push_back(favicon_id_size_listing[i].pixel_size); |
| 32 } |
| 33 |
| 34 size_t BiggestCandidate(const std::vector<gfx::Size>& candidate_sizes) { |
15 size_t max_index = 0; | 35 size_t max_index = 0; |
16 int max_area = bitmaps[0].width() * bitmaps[0].height(); | 36 int max_area = candidate_sizes[0].GetArea(); |
17 for (size_t i = 1; i < bitmaps.size(); ++i) { | 37 for (size_t i = 1; i < candidate_sizes.size(); ++i) { |
18 int area = bitmaps[i].width() * bitmaps[i].height(); | 38 int area = candidate_sizes[i].GetArea(); |
19 if (area > max_area) { | 39 if (area > max_area) { |
20 max_area = area; | 40 max_area = area; |
21 max_index = i; | 41 max_index = i; |
22 } | 42 } |
23 } | 43 } |
24 return max_index; | 44 return max_index; |
25 } | 45 } |
26 | 46 |
27 SkBitmap PadWithBorder(const SkBitmap& contents, | 47 SkBitmap PadWithBorder(const SkBitmap& contents, |
28 int desired_size, | 48 int desired_size, |
(...skipping 24 matching lines...) Expand all Loading... |
53 | 73 |
54 { | 74 { |
55 SkCanvas canvas(bitmap); | 75 SkCanvas canvas(bitmap); |
56 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); | 76 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); |
57 canvas.drawBitmapRect(contents, NULL, dest); | 77 canvas.drawBitmapRect(contents, NULL, dest); |
58 } | 78 } |
59 | 79 |
60 return bitmap; | 80 return bitmap; |
61 } | 81 } |
62 | 82 |
63 SkBitmap SelectCandidate(const std::vector<SkBitmap>& bitmaps, | 83 enum ResizeMethod { |
64 int desired_size, | 84 NONE, |
65 ui::ScaleFactor scale_factor, | 85 PAD_WITH_BORDER, |
66 float* score) { | 86 SAMPLE_NEAREST_NEIGHBOUR, |
67 float scale = GetScaleFactorScale(scale_factor); | 87 LANCZOS |
| 88 }; |
| 89 |
| 90 size_t GetCandidateIndexWithBestScore( |
| 91 const std::vector<gfx::Size>& candidate_sizes, |
| 92 int desired_size, |
| 93 float scale, |
| 94 float* score, |
| 95 ResizeMethod* resize_method) { |
68 desired_size = static_cast<int>(desired_size * scale + 0.5f); | 96 desired_size = static_cast<int>(desired_size * scale + 0.5f); |
69 | 97 |
70 // Try to find an exact match. | 98 // Try to find an exact match. |
71 for (size_t i = 0; i < bitmaps.size(); ++i) { | 99 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
72 if (bitmaps[i].width() == desired_size && | 100 if (candidate_sizes[i].width() == desired_size && |
73 bitmaps[i].height() == desired_size) { | 101 candidate_sizes[i].height() == desired_size) { |
74 *score = 1; | 102 *score = 1; |
75 return bitmaps[i]; | 103 *resize_method = NONE; |
| 104 return i; |
76 } | 105 } |
77 } | 106 } |
78 | 107 |
79 // If that failed, the following special rules apply: | 108 // If that failed, the following special rules apply: |
80 // 1. 17px-24px images are built from 16px images by adding | 109 // 1. 17px-24px images are built from 16px images by adding |
81 // a transparent border. | 110 // a transparent border. |
82 if (desired_size > 16 * scale && desired_size <= 24 * scale) { | 111 if (desired_size > 16 * scale && desired_size <= 24 * scale) { |
83 int source_size = static_cast<int>(16 * scale + 0.5f); | 112 int source_size = static_cast<int>(16 * scale + 0.5f); |
84 for (size_t i = 0; i < bitmaps.size(); ++i) { | 113 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
85 if (bitmaps[i].width() == source_size && | 114 if (candidate_sizes[i].width() == source_size && |
86 bitmaps[i].height() == source_size) { | 115 candidate_sizes[i].height() == source_size) { |
87 *score = 0.2f; | 116 *score = 0.2f; |
88 return PadWithBorder(bitmaps[i], desired_size, source_size); | 117 *resize_method = PAD_WITH_BORDER; |
| 118 return i; |
89 } | 119 } |
90 } | 120 } |
91 // Try again, with upsizing the base variant. | 121 // Try again, with upsizing the base variant. |
92 for (size_t i = 0; i < bitmaps.size(); ++i) { | 122 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
93 if (bitmaps[i].width() * scale == source_size && | 123 if (candidate_sizes[i].width() * scale == source_size && |
94 bitmaps[i].height() * scale == source_size) { | 124 candidate_sizes[i].height() * scale == source_size) { |
95 *score = 0.15f; | 125 *score = 0.15f; |
96 return PadWithBorder(bitmaps[i], desired_size, source_size); | 126 *resize_method = PAD_WITH_BORDER; |
| 127 return i; |
97 } | 128 } |
98 } | 129 } |
99 } | 130 } |
100 | 131 |
101 // 2. Integer multiples are built using nearest neighbor sampling. | 132 // 2. Integer multiples are built using nearest neighbor sampling. |
102 // 3. Else, use Lancosz scaling: | 133 // 3. Else, use Lancosz scaling: |
103 // b) If available, from the next bigger variant. | 134 // b) If available, from the next bigger variant. |
104 int candidate = -1; | 135 int candidate_index = -1; |
105 int min_area = INT_MAX; | 136 int min_area = INT_MAX; |
106 for (size_t i = 0; i < bitmaps.size(); ++i) { | 137 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
107 int area = bitmaps[i].width() * bitmaps[i].height(); | 138 int area = candidate_sizes[i].GetArea(); |
108 if (bitmaps[i].width() > desired_size && | 139 if (candidate_sizes[i].width() > desired_size && |
109 bitmaps[i].height() > desired_size && | 140 candidate_sizes[i].height() > desired_size && |
110 (candidate == -1 || area < min_area)) { | 141 (candidate_index == -1 || area < min_area)) { |
111 candidate = i; | 142 candidate_index = i; |
112 min_area = area; | 143 min_area = area; |
113 } | 144 } |
114 } | 145 } |
115 *score = 0.1f; | 146 *score = 0.1f; |
116 // c) Else, from the biggest smaller variant. | 147 // c) Else, from the biggest smaller variant. |
117 if (candidate == -1) { | 148 if (candidate_index == -1) { |
118 *score = 0; | 149 *score = 0; |
119 candidate = BiggestCandidate(bitmaps); | 150 candidate_index = BiggestCandidate(candidate_sizes); |
120 } | 151 } |
121 | 152 |
122 const SkBitmap& bitmap = bitmaps[candidate]; | 153 const gfx::Size& candidate_size = candidate_sizes[candidate_index]; |
123 bool is_integer_multiple = desired_size % bitmap.width() == 0 && | 154 bool is_integer_multiple = desired_size % candidate_size.width() == 0 && |
124 desired_size % bitmap.height() == 0; | 155 desired_size % candidate_size.height() == 0; |
125 if (is_integer_multiple) | 156 *resize_method = is_integer_multiple ? SAMPLE_NEAREST_NEIGHBOUR : LANCZOS; |
126 return SampleNearestNeighbor(bitmap, desired_size); | 157 return candidate_index; |
127 return skia::ImageOperations::Resize( | 158 } |
128 bitmap, skia::ImageOperations::RESIZE_LANCZOS3, | 159 |
129 desired_size, desired_size); | 160 SkBitmap SelectCandidate(const std::vector<SkBitmap>& bitmaps, |
| 161 int desired_size, |
| 162 ui::ScaleFactor scale_factor, |
| 163 float* score) { |
| 164 std::vector<gfx::Size> candidate_sizes; |
| 165 SizesFromBitmaps(bitmaps, &candidate_sizes); |
| 166 |
| 167 ResizeMethod resize_method = NONE; |
| 168 float scale = ui::GetScaleFactorScale(scale_factor); |
| 169 size_t best_candidate = GetCandidateIndexWithBestScore(candidate_sizes, |
| 170 desired_size, |
| 171 scale, |
| 172 score, |
| 173 &resize_method); |
| 174 |
| 175 int scaled_desired_size = static_cast<int>(desired_size * scale + 0.5f); |
| 176 SkBitmap best_bitmap = bitmaps[best_candidate]; |
| 177 switch(resize_method) { |
| 178 case NONE: |
| 179 return best_bitmap; |
| 180 case PAD_WITH_BORDER: { |
| 181 int scaled_16 = static_cast<int>(16 * scale + 0.5f); |
| 182 return PadWithBorder(best_bitmap, scaled_desired_size, scaled_16); |
| 183 } |
| 184 case SAMPLE_NEAREST_NEIGHBOUR: |
| 185 return SampleNearestNeighbor(best_bitmap, scaled_desired_size); |
| 186 case LANCZOS: |
| 187 return skia::ImageOperations::Resize( |
| 188 best_bitmap, skia::ImageOperations::RESIZE_LANCZOS3, |
| 189 scaled_desired_size, scaled_desired_size); |
| 190 default: |
| 191 NOTREACHED(); |
| 192 return best_bitmap; |
| 193 } |
130 } | 194 } |
131 | 195 |
132 } // namespace | 196 } // namespace |
133 | 197 |
134 gfx::ImageSkia SelectFaviconFrames( | 198 gfx::ImageSkia SelectFaviconFrames( |
135 const std::vector<SkBitmap>& bitmaps, | 199 const std::vector<SkBitmap>& bitmaps, |
136 const std::vector<ui::ScaleFactor>& scale_factors, | 200 const std::vector<ui::ScaleFactor>& scale_factors, |
137 int desired_size, | 201 int desired_size, |
138 float* match_score) { | 202 float* match_score) { |
139 gfx::ImageSkia multi_image; | 203 gfx::ImageSkia multi_image; |
140 if (bitmaps.empty()) | 204 if (bitmaps.empty()) |
141 return multi_image; | 205 return multi_image; |
142 | 206 |
143 if (desired_size == 0) { | 207 if (desired_size == 0) { |
144 // Just return the biggest image available. | 208 // Just return the biggest image available. |
145 size_t max_index = BiggestCandidate(bitmaps); | 209 std::vector<gfx::Size> candidate_sizes; |
| 210 SizesFromBitmaps(bitmaps, &candidate_sizes); |
| 211 size_t max_index = BiggestCandidate(candidate_sizes); |
146 multi_image.AddRepresentation( | 212 multi_image.AddRepresentation( |
147 gfx::ImageSkiaRep(bitmaps[max_index], ui::SCALE_FACTOR_100P)); | 213 gfx::ImageSkiaRep(bitmaps[max_index], ui::SCALE_FACTOR_100P)); |
148 if (match_score) | 214 if (match_score) |
149 *match_score = 0.8f; | 215 *match_score = 0.8f; |
150 return multi_image; | 216 return multi_image; |
151 } | 217 } |
152 | 218 |
153 float total_score = 0; | 219 float total_score = 0; |
154 for (size_t i = 0; i < scale_factors.size(); ++i) { | 220 for (size_t i = 0; i < scale_factors.size(); ++i) { |
155 float score; | 221 float score; |
156 multi_image.AddRepresentation(gfx::ImageSkiaRep( | 222 multi_image.AddRepresentation(gfx::ImageSkiaRep( |
157 SelectCandidate(bitmaps, desired_size, scale_factors[i], &score), | 223 SelectCandidate(bitmaps, desired_size, scale_factors[i], &score), |
158 scale_factors[i])); | 224 scale_factors[i])); |
159 total_score += score; | 225 total_score += score; |
160 } | 226 } |
161 | 227 |
162 if (match_score) | 228 if (match_score) |
163 *match_score = total_score / scale_factors.size(); | 229 *match_score = total_score / scale_factors.size(); |
164 return multi_image; | 230 return multi_image; |
165 } | 231 } |
| 232 |
| 233 void SelectFaviconBitmapIDs( |
| 234 const std::vector<history::FaviconBitmapIDSize>& bitmap_id_size_list, |
| 235 const std::vector<ui::ScaleFactor>& scale_factors, |
| 236 int desired_size, |
| 237 std::vector<history::FaviconBitmapID>* filtered_favicon_bitmap_ids, |
| 238 float* match_score) { |
| 239 DCHECK(filtered_favicon_bitmap_ids); |
| 240 filtered_favicon_bitmap_ids->clear(); |
| 241 |
| 242 if (bitmap_id_size_list.empty()) |
| 243 return; |
| 244 |
| 245 std::vector<gfx::Size> candidate_sizes; |
| 246 SizesFromFaviconBitmapIDSizeListing(bitmap_id_size_list, |
| 247 &candidate_sizes); |
| 248 |
| 249 if (desired_size == 0) { |
| 250 // Just return the FaviconID with the biggest size available. |
| 251 size_t max_index = BiggestCandidate(candidate_sizes); |
| 252 filtered_favicon_bitmap_ids->push_back( |
| 253 bitmap_id_size_list[max_index].bitmap_id); |
| 254 *match_score = 0.8f; |
| 255 return; |
| 256 } |
| 257 |
| 258 float total_score = 0; |
| 259 for (size_t i = 0; i < scale_factors.size(); ++i) { |
| 260 float score; |
| 261 ResizeMethod resize_method = NONE; |
| 262 float scale = ui::GetScaleFactorScale(scale_factors[i]); |
| 263 size_t best_candidate_index = GetCandidateIndexWithBestScore( |
| 264 candidate_sizes, desired_size, scale, &score, &resize_method); |
| 265 filtered_favicon_bitmap_ids->push_back( |
| 266 bitmap_id_size_list[best_candidate_index].bitmap_id); |
| 267 total_score += score; |
| 268 } |
| 269 |
| 270 // Remove duplicates. |
| 271 std::sort(filtered_favicon_bitmap_ids->begin(), |
| 272 filtered_favicon_bitmap_ids->end()); |
| 273 filtered_favicon_bitmap_ids->erase(std::unique( |
| 274 filtered_favicon_bitmap_ids->begin(), |
| 275 filtered_favicon_bitmap_ids->end()), |
| 276 filtered_favicon_bitmap_ids->end()); |
| 277 |
| 278 if (match_score) |
| 279 *match_score = total_score / scale_factors.size(); |
| 280 } |
OLD | NEW |