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