Chromium Code Reviews| 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 |