Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "components/favicon_base/select_favicon_frames.h" | 5 #include "components/favicon_base/select_favicon_frames.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
| 7 #include <limits> | 8 #include <limits> |
| 9 #include <map> | |
| 8 #include <set> | 10 #include <set> |
| 9 | 11 |
| 10 #include "skia/ext/image_operations.h" | 12 #include "skia/ext/image_operations.h" |
| 11 #include "third_party/skia/include/core/SkCanvas.h" | 13 #include "third_party/skia/include/core/SkCanvas.h" |
| 12 #include "ui/gfx/image/image.h" | 14 #include "ui/gfx/image/image.h" |
| 13 #include "ui/gfx/image/image_skia.h" | 15 #include "ui/gfx/image/image_skia.h" |
| 14 #include "ui/gfx/size.h" | 16 #include "ui/gfx/size.h" |
| 15 | 17 |
| 16 namespace { | 18 namespace { |
| 17 | 19 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 40 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); | 42 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); |
| 41 canvas.drawBitmapRect(contents, NULL, dest); | 43 canvas.drawBitmapRect(contents, NULL, dest); |
| 42 } | 44 } |
| 43 | 45 |
| 44 return bitmap; | 46 return bitmap; |
| 45 } | 47 } |
| 46 | 48 |
| 47 enum ResizeMethod { NONE, SAMPLE_NEAREST_NEIGHBOUR, LANCZOS }; | 49 enum ResizeMethod { NONE, SAMPLE_NEAREST_NEIGHBOUR, LANCZOS }; |
| 48 | 50 |
| 49 size_t GetCandidateIndexWithBestScore( | 51 size_t GetCandidateIndexWithBestScore( |
| 50 const std::vector<gfx::Size>& candidate_sizes_in_pixel, | 52 const std::vector<gfx::Size>& candidate_sizes, |
| 51 ui::ScaleFactor scale_factor, | 53 int desired_size, |
| 52 int desired_size_in_dip, | |
| 53 float* score, | 54 float* score, |
| 54 ResizeMethod* resize_method) { | 55 ResizeMethod* resize_method) { |
| 55 DCHECK_NE(desired_size_in_dip, 0); | 56 DCHECK_NE(desired_size, 0); |
| 56 | |
| 57 float scale = ui::GetScaleForScaleFactor(scale_factor); | |
| 58 int desired_size_in_pixel = | |
| 59 static_cast<int>(desired_size_in_dip * scale + 0.5f); | |
| 60 | 57 |
| 61 // Try to find an exact match. | 58 // Try to find an exact match. |
| 62 for (size_t i = 0; i < candidate_sizes_in_pixel.size(); ++i) { | 59 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
| 63 if (candidate_sizes_in_pixel[i].width() == desired_size_in_pixel && | 60 if (candidate_sizes[i].width() == desired_size && |
| 64 candidate_sizes_in_pixel[i].height() == desired_size_in_pixel) { | 61 candidate_sizes[i].height() == desired_size) { |
| 65 *score = 1; | 62 *score = 1; |
| 66 *resize_method = NONE; | 63 *resize_method = NONE; |
| 67 return i; | 64 return i; |
| 68 } | 65 } |
| 69 } | 66 } |
| 70 | 67 |
| 71 // Huge favicon bitmaps often have a completely different visual style from | 68 // Huge favicon bitmaps often have a completely different visual style from |
| 72 // smaller favicon bitmaps. Avoid these favicon bitmaps when a favicon of | 69 // smaller favicon bitmaps. Avoid them. |
| 73 // gfx::kFaviconSize DIP is requested. | 70 const int kHugeEdgeSize = desired_size * 8; |
|
pkotwicz
2014/06/18 04:10:54
The old cold is correct while the old comment is n
| |
| 74 const int kHugeEdgeSizeInPixel = desired_size_in_pixel * 8; | |
| 75 | 71 |
| 76 // Order of preference: | 72 // Order of preference: |
| 77 // 1) Bitmaps with width and height smaller than |kHugeEdgeSizeInPixel|. | 73 // 1) Bitmaps with width and height smaller than |kHugeEdgeSize|. |
| 78 // 2) Bitmaps which need to be scaled down instead of up. | 74 // 2) Bitmaps which need to be scaled down instead of up. |
| 79 // 3) Bitmaps which do not need to be scaled as much. | 75 // 3) Bitmaps which do not need to be scaled as much. |
| 80 size_t candidate_index = std::numeric_limits<size_t>::max(); | 76 size_t candidate_index = std::numeric_limits<size_t>::max(); |
| 81 float candidate_score = 0; | 77 float candidate_score = 0; |
| 82 for (size_t i = 0; i < candidate_sizes_in_pixel.size(); ++i) { | 78 for (size_t i = 0; i < candidate_sizes.size(); ++i) { |
| 83 float average_edge_in_pixel = (candidate_sizes_in_pixel[i].width() + | 79 float average_edge = |
| 84 candidate_sizes_in_pixel[i].height()) / | 80 (candidate_sizes[i].width() + candidate_sizes[i].height()) / 2.0f; |
| 85 2.0f; | |
| 86 | 81 |
| 87 float score = 0; | 82 float score = 0; |
| 88 if (candidate_sizes_in_pixel[i].width() >= kHugeEdgeSizeInPixel || | 83 if (candidate_sizes[i].width() >= kHugeEdgeSize || |
| 89 candidate_sizes_in_pixel[i].height() >= kHugeEdgeSizeInPixel) { | 84 candidate_sizes[i].height() >= kHugeEdgeSize) { |
| 90 score = | 85 score = std::min(1.0f, desired_size / average_edge) * 0.01f; |
| 91 std::min(1.0f, desired_size_in_pixel / average_edge_in_pixel) * 0.01f; | 86 } else if (candidate_sizes[i].width() >= desired_size && |
| 92 } else if (candidate_sizes_in_pixel[i].width() >= desired_size_in_pixel && | 87 candidate_sizes[i].height() >= desired_size) { |
| 93 candidate_sizes_in_pixel[i].height() >= desired_size_in_pixel) { | 88 score = desired_size / average_edge * 0.01f + 0.15f; |
| 94 score = desired_size_in_pixel / average_edge_in_pixel * 0.01f + 0.15f; | |
| 95 } else { | 89 } else { |
| 96 score = std::min(1.0f, average_edge_in_pixel / desired_size_in_pixel) * | 90 score = std::min(1.0f, average_edge / desired_size) * |
| 97 0.01f + | 91 0.01f + |
| 98 0.1f; | 92 0.1f; |
| 99 } | 93 } |
| 100 | 94 |
| 101 if (candidate_index == std::numeric_limits<size_t>::max() || | 95 if (candidate_index == std::numeric_limits<size_t>::max() || |
| 102 score > candidate_score) { | 96 score > candidate_score) { |
| 103 candidate_index = i; | 97 candidate_index = i; |
| 104 candidate_score = score; | 98 candidate_score = score; |
| 105 } | 99 } |
| 106 } | 100 } |
| 107 *score = candidate_score; | 101 *score = candidate_score; |
| 108 | 102 |
| 109 // Integer multiples are built using nearest neighbor sampling. Otherwise, | 103 // Integer multiples are built using nearest neighbor sampling. Otherwise, |
| 110 // Lanczos scaling is used. | 104 // Lanczos scaling is used. |
| 111 const gfx::Size& candidate_size_in_pixel = | 105 const gfx::Size& candidate_size = candidate_sizes[candidate_index]; |
| 112 candidate_sizes_in_pixel[candidate_index]; | 106 if (candidate_size.IsEmpty()) { |
| 113 if (candidate_size_in_pixel.IsEmpty()) { | |
| 114 *resize_method = NONE; | 107 *resize_method = NONE; |
| 115 } else if (desired_size_in_pixel % candidate_size_in_pixel.width() == 0 && | 108 } else if (desired_size % candidate_size.width() == 0 && |
| 116 desired_size_in_pixel % candidate_size_in_pixel.height() == 0) { | 109 desired_size % candidate_size.height() == 0) { |
| 117 *resize_method = SAMPLE_NEAREST_NEIGHBOUR; | 110 *resize_method = SAMPLE_NEAREST_NEIGHBOUR; |
| 118 } else { | 111 } else { |
| 119 *resize_method = LANCZOS; | 112 *resize_method = LANCZOS; |
| 120 } | 113 } |
| 121 return candidate_index; | 114 return candidate_index; |
| 122 } | 115 } |
| 123 | 116 |
| 124 // Represents the index of the best candidate for a |scale_factor| from the | 117 // Represents the index of the best candidate for |desired_size| from the |
| 125 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores(). | 118 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores(). |
| 126 struct SelectionResult { | 119 struct SelectionResult { |
| 127 // index in |candidate_sizes| of the best candidate. | 120 // index in |candidate_sizes| of the best candidate. |
| 128 size_t index; | 121 size_t index; |
| 129 | 122 |
| 130 // The ScaleFactor for which |index| is the best candidate. | 123 // The desired size for which |index| is the best candidate. |
| 131 ui::ScaleFactor scale_factor; | 124 int desired_size; |
| 132 | 125 |
| 133 // How the bitmap data that the bitmap with |candidate_sizes[index]| should | 126 // How the bitmap data that the bitmap with |candidate_sizes[index]| should |
| 134 // be resized for displaying in the UI. | 127 // be resized for displaying in the UI. |
| 135 ResizeMethod resize_method; | 128 ResizeMethod resize_method; |
| 136 }; | 129 }; |
| 137 | 130 |
| 138 void GetCandidateIndicesWithBestScores( | 131 void GetCandidateIndicesWithBestScores( |
| 139 const std::vector<gfx::Size>& candidate_sizes, | 132 const std::vector<gfx::Size>& candidate_sizes, |
| 140 const std::vector<ui::ScaleFactor>& scale_factors, | 133 const std::vector<int>& desired_sizes, |
| 141 int desired_size, | |
| 142 float* match_score, | 134 float* match_score, |
| 143 std::vector<SelectionResult>* results) { | 135 std::vector<SelectionResult>* results) { |
| 144 if (candidate_sizes.empty()) { | 136 if (candidate_sizes.empty() || desired_sizes.empty()) { |
| 145 if (match_score) | 137 if (match_score) |
| 146 *match_score = 0.0f; | 138 *match_score = 0.0f; |
| 147 return; | 139 return; |
| 148 } | 140 } |
| 149 | 141 |
| 150 if (desired_size == 0) { | 142 std::vector<int>::const_iterator zero_size_it = std::find( |
| 143 desired_sizes.begin(), desired_sizes.end(), 0); | |
| 144 if (zero_size_it != desired_sizes.end()) { | |
| 151 // Just return the biggest image available. | 145 // Just return the biggest image available. |
| 152 SelectionResult result; | 146 SelectionResult result; |
| 153 result.index = BiggestCandidate(candidate_sizes); | 147 result.index = BiggestCandidate(candidate_sizes); |
| 154 result.scale_factor = ui::SCALE_FACTOR_100P; | 148 result.desired_size = 0; |
| 155 result.resize_method = NONE; | 149 result.resize_method = NONE; |
| 156 results->push_back(result); | 150 results->push_back(result); |
| 157 if (match_score) | 151 if (match_score) |
| 158 *match_score = 1.0f; | 152 *match_score = 1.0f; |
| 159 return; | 153 return; |
| 160 } | 154 } |
| 161 | 155 |
| 162 float total_score = 0; | 156 float total_score = 0; |
| 163 for (size_t i = 0; i < scale_factors.size(); ++i) { | 157 for (size_t i = 0; i < desired_sizes.size(); ++i) { |
| 164 float score; | 158 float score; |
| 165 SelectionResult result; | 159 SelectionResult result; |
| 166 result.scale_factor = scale_factors[i]; | 160 result.desired_size = desired_sizes[i]; |
| 167 result.index = GetCandidateIndexWithBestScore(candidate_sizes, | 161 result.index = GetCandidateIndexWithBestScore(candidate_sizes, |
| 168 result.scale_factor, | 162 result.desired_size, |
| 169 desired_size, | |
| 170 &score, | 163 &score, |
| 171 &result.resize_method); | 164 &result.resize_method); |
| 172 results->push_back(result); | 165 results->push_back(result); |
| 173 total_score += score; | 166 total_score += score; |
| 174 } | 167 } |
| 175 | 168 |
| 176 if (match_score) | 169 if (match_score) |
| 177 *match_score = total_score / scale_factors.size(); | 170 *match_score = total_score / desired_sizes.size(); |
| 178 } | 171 } |
| 179 | 172 |
| 180 // Resize |source_bitmap| using |resize_method|. | 173 // Resize |source_bitmap| using |resize_method|. |
| 181 SkBitmap GetResizedBitmap(const SkBitmap& source_bitmap, | 174 SkBitmap GetResizedBitmap(const SkBitmap& source_bitmap, |
| 182 int desired_size_in_dip, | 175 int desired_size, |
| 183 ui::ScaleFactor scale_factor, | |
| 184 ResizeMethod resize_method) { | 176 ResizeMethod resize_method) { |
| 185 float scale = ui::GetScaleForScaleFactor(scale_factor); | |
| 186 int desired_size_in_pixel = | |
| 187 static_cast<int>(desired_size_in_dip * scale + 0.5f); | |
| 188 | |
| 189 switch (resize_method) { | 177 switch (resize_method) { |
| 190 case NONE: | 178 case NONE: |
| 191 return source_bitmap; | 179 return source_bitmap; |
| 192 case SAMPLE_NEAREST_NEIGHBOUR: | 180 case SAMPLE_NEAREST_NEIGHBOUR: |
| 193 return SampleNearestNeighbor(source_bitmap, desired_size_in_pixel); | 181 return SampleNearestNeighbor(source_bitmap, desired_size); |
| 194 case LANCZOS: | 182 case LANCZOS: |
| 195 return skia::ImageOperations::Resize( | 183 return skia::ImageOperations::Resize( |
| 196 source_bitmap, | 184 source_bitmap, |
| 197 skia::ImageOperations::RESIZE_LANCZOS3, | 185 skia::ImageOperations::RESIZE_LANCZOS3, |
| 198 desired_size_in_pixel, | 186 desired_size, |
| 199 desired_size_in_pixel); | 187 desired_size); |
| 200 } | 188 } |
| 201 return source_bitmap; | 189 return source_bitmap; |
| 202 } | 190 } |
| 203 | 191 |
| 204 } // namespace | 192 } // namespace |
| 205 | 193 |
| 206 const float kSelectFaviconFramesInvalidScore = -1.0f; | 194 const float kSelectFaviconFramesInvalidScore = -1.0f; |
| 207 | 195 |
| 208 gfx::ImageSkia SelectFaviconFrames( | 196 gfx::ImageSkia SelectFaviconFrames( |
| 209 const std::vector<SkBitmap>& bitmaps, | 197 const std::vector<SkBitmap>& bitmaps, |
| 210 const std::vector<gfx::Size>& original_sizes, | 198 const std::vector<gfx::Size>& original_sizes, |
| 211 const std::vector<ui::ScaleFactor>& scale_factors, | 199 const std::vector<ui::ScaleFactor>& scale_factors, |
| 212 int desired_size, | 200 int desired_size_in_dip, |
| 213 float* match_score) { | 201 float* match_score) { |
| 202 std::vector<int> desired_sizes; | |
| 203 std::map<int, float> scale_map; | |
| 204 if (desired_size_in_dip == 0) { | |
| 205 desired_sizes.push_back(0); | |
| 206 scale_map[0] = 1.0f; | |
| 207 } else { | |
| 208 for (size_t i = 0; i < scale_factors.size(); ++i) { | |
| 209 float scale = ui::GetScaleForScaleFactor(scale_factors[i]); | |
| 210 int desired_size = static_cast<int>(desired_size_in_dip * scale + 0.5f); | |
| 211 desired_sizes.push_back(desired_size); | |
| 212 scale_map[desired_size] = scale; | |
| 213 } | |
| 214 } | |
| 215 | |
| 214 std::vector<SelectionResult> results; | 216 std::vector<SelectionResult> results; |
| 215 GetCandidateIndicesWithBestScores( | 217 GetCandidateIndicesWithBestScores( |
| 216 original_sizes, scale_factors, desired_size, match_score, &results); | 218 original_sizes, desired_sizes, match_score, &results); |
| 217 | 219 |
| 218 gfx::ImageSkia multi_image; | 220 gfx::ImageSkia multi_image; |
| 219 for (size_t i = 0; i < results.size(); ++i) { | 221 for (size_t i = 0; i < results.size(); ++i) { |
| 220 const SelectionResult& result = results[i]; | 222 const SelectionResult& result = results[i]; |
| 221 SkBitmap resized_bitmap = GetResizedBitmap(bitmaps[result.index], | 223 SkBitmap resized_bitmap = GetResizedBitmap(bitmaps[result.index], |
| 222 desired_size, | 224 result.desired_size, |
| 223 result.scale_factor, | |
| 224 result.resize_method); | 225 result.resize_method); |
| 225 multi_image.AddRepresentation(gfx::ImageSkiaRep( | 226 |
| 226 resized_bitmap, ui::GetScaleForScaleFactor(result.scale_factor))); | 227 std::map<int, float>::const_iterator it = scale_map.find( |
| 228 result.desired_size); | |
| 229 DCHECK(it != scale_map.end()); | |
| 230 float scale = it->second; | |
| 231 multi_image.AddRepresentation(gfx::ImageSkiaRep(resized_bitmap, scale)); | |
| 227 } | 232 } |
| 228 return multi_image; | 233 return multi_image; |
| 229 } | 234 } |
| 230 | 235 |
| 231 void SelectFaviconFrameIndices( | 236 void SelectFaviconFrameIndices( |
| 232 const std::vector<gfx::Size>& frame_pixel_sizes, | 237 const std::vector<gfx::Size>& frame_pixel_sizes, |
| 233 const std::vector<ui::ScaleFactor>& scale_factors, | 238 const std::vector<int>& desired_sizes, |
| 234 int desired_size, | |
| 235 std::vector<size_t>* best_indices, | 239 std::vector<size_t>* best_indices, |
| 236 float* match_score) { | 240 float* match_score) { |
| 237 std::vector<SelectionResult> results; | 241 std::vector<SelectionResult> results; |
| 238 GetCandidateIndicesWithBestScores( | 242 GetCandidateIndicesWithBestScores( |
| 239 frame_pixel_sizes, scale_factors, desired_size, match_score, &results); | 243 frame_pixel_sizes, desired_sizes, match_score, &results); |
| 240 | 244 |
| 241 std::set<size_t> already_added; | 245 std::set<size_t> already_added; |
| 242 for (size_t i = 0; i < results.size(); ++i) { | 246 for (size_t i = 0; i < results.size(); ++i) { |
| 243 size_t index = results[i].index; | 247 size_t index = results[i].index; |
| 244 // GetCandidateIndicesWithBestScores() will return duplicate indices if the | 248 // GetCandidateIndicesWithBestScores() will return duplicate indices if the |
| 245 // bitmap data with |frame_pixel_sizes[index]| should be used for multiple | 249 // bitmap data with |frame_pixel_sizes[index]| should be used for multiple |
| 246 // scale factors. Remove duplicates here such that |best_indices| contains | 250 // scale factors. Remove duplicates here such that |best_indices| contains |
| 247 // no duplicates. | 251 // no duplicates. |
| 248 if (already_added.find(index) == already_added.end()) { | 252 if (already_added.find(index) == already_added.end()) { |
| 249 already_added.insert(index); | 253 already_added.insert(index); |
| 250 best_indices->push_back(index); | 254 best_indices->push_back(index); |
| 251 } | 255 } |
| 252 } | 256 } |
| 253 } | 257 } |
| OLD | NEW |