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