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 3 // found in the LICENSE file. 4 5 #include "components/favicon_base/select_favicon_frames.h" 6 7 #include <algorithm> 8 #include <cmath> 9 #include <limits> 10 #include <map> 11 #include <set> 12 13 #include "components/favicon_base/favicon_util.h" 14 #include "skia/ext/image_operations.h" 15 #include "third_party/skia/include/core/SkCanvas.h" 16 #include "ui/gfx/image/image.h" 17 #include "ui/gfx/image/image_skia.h" 18 #include "ui/gfx/image/image_skia_source.h" 19 #include "ui/gfx/size.h" 20 21 namespace { 22 23 size_t BiggestCandidate(const std::vector<gfx::Size>& candidate_sizes) { 24 size_t max_index = 0; 25 int max_area = candidate_sizes[0].GetArea(); 26 for (size_t i = 1; i < candidate_sizes.size(); ++i) { 27 int area = candidate_sizes[i].GetArea(); 28 if (area > max_area) { 29 max_area = area; 30 max_index = i; 31 } 32 } 33 return max_index; 34 } 35 36 SkBitmap SampleNearestNeighbor(const SkBitmap& contents, int desired_size) { 37 SkBitmap bitmap; 38 bitmap.allocN32Pixels(desired_size, desired_size); 39 if (!contents.isOpaque()) 40 bitmap.eraseARGB(0, 0, 0, 0); 41 42 { 43 SkCanvas canvas(bitmap); 44 SkRect dest(SkRect::MakeWH(desired_size, desired_size)); 45 canvas.drawBitmapRect(contents, NULL, dest); 46 } 47 48 return bitmap; 49 } 50 51 size_t GetCandidateIndexWithBestScore( 52 const std::vector<gfx::Size>& candidate_sizes, 53 int desired_size, 54 float* score) { 55 DCHECK_NE(desired_size, 0); 56 57 // Try to find an exact match. 58 for (size_t i = 0; i < candidate_sizes.size(); ++i) { 59 if (candidate_sizes[i].width() == desired_size && 60 candidate_sizes[i].height() == desired_size) { 61 *score = 1; 62 return i; 63 } 64 } 65 66 // Huge favicon bitmaps often have a completely different visual style from 67 // smaller favicon bitmaps. Avoid them. 68 const int kHugeEdgeSize = desired_size * 8; 69 70 // Order of preference: 71 // 1) Bitmaps with width and height smaller than |kHugeEdgeSize|. 72 // 2) Bitmaps which need to be scaled down instead of up. 73 // 3) Bitmaps which do not need to be scaled as much. 74 size_t candidate_index = std::numeric_limits<size_t>::max(); 75 float candidate_score = 0; 76 for (size_t i = 0; i < candidate_sizes.size(); ++i) { 77 float average_edge = 78 (candidate_sizes[i].width() + candidate_sizes[i].height()) / 2.0f; 79 80 float score = 0; 81 if (candidate_sizes[i].width() >= kHugeEdgeSize || 82 candidate_sizes[i].height() >= kHugeEdgeSize) { 83 score = std::min(1.0f, desired_size / average_edge) * 0.01f; 84 } else if (candidate_sizes[i].width() >= desired_size && 85 candidate_sizes[i].height() >= desired_size) { 86 score = desired_size / average_edge * 0.01f + 0.15f; 87 } else { 88 score = std::min(1.0f, average_edge / desired_size) * 0.01f + 0.1f; 89 } 90 91 if (candidate_index == std::numeric_limits<size_t>::max() || 92 score > candidate_score) { 93 candidate_index = i; 94 candidate_score = score; 95 } 96 } 97 *score = candidate_score; 98 99 return candidate_index; 100 } 101 102 // Represents the index of the best candidate for |desired_size| from the 103 // |candidate_sizes| passed into GetCandidateIndicesWithBestScores(). 104 struct SelectionResult { 105 // index in |candidate_sizes| of the best candidate. 106 size_t index; 107 108 // The desired size for which |index| is the best candidate. 109 int desired_size; 110 }; 111 112 void GetCandidateIndicesWithBestScores( 113 const std::vector<gfx::Size>& candidate_sizes, 114 const std::vector<int>& desired_sizes, 115 float* match_score, 116 std::vector<SelectionResult>* results) { 117 if (candidate_sizes.empty() || desired_sizes.empty()) { 118 if (match_score) 119 *match_score = 0.0f; 120 return; 121 } 122 123 std::vector<int>::const_iterator zero_size_it = 124 std::find(desired_sizes.begin(), desired_sizes.end(), 0); 125 if (zero_size_it != desired_sizes.end()) { 126 // Just return the biggest image available. 127 SelectionResult result; 128 result.index = BiggestCandidate(candidate_sizes); 129 result.desired_size = 0; 130 results->push_back(result); 131 if (match_score) 132 *match_score = 1.0f; 133 return; 134 } 135 136 float total_score = 0; 137 for (size_t i = 0; i < desired_sizes.size(); ++i) { 138 float score; 139 SelectionResult result; 140 result.desired_size = desired_sizes[i]; 141 result.index = GetCandidateIndexWithBestScore( 142 candidate_sizes, result.desired_size, &score); 143 results->push_back(result); 144 total_score += score; 145 } 146 147 if (match_score) 148 *match_score = total_score / desired_sizes.size(); 149 } 150 151 // Resize |source_bitmap| 152 SkBitmap GetResizedBitmap(const SkBitmap& source_bitmap, 153 gfx::Size original_size, 154 int desired_size_in_pixel) { 155 if (desired_size_in_pixel == 0 || 156 (original_size.width() == desired_size_in_pixel && 157 original_size.height() == desired_size_in_pixel)) { 158 return source_bitmap; 159 } 160 if (desired_size_in_pixel % original_size.width() == 0 && 161 desired_size_in_pixel % original_size.height() == 0) { 162 return SampleNearestNeighbor(source_bitmap, desired_size_in_pixel); 163 } 164 return skia::ImageOperations::Resize(source_bitmap, 165 skia::ImageOperations::RESIZE_LANCZOS3, 166 desired_size_in_pixel, 167 desired_size_in_pixel); 168 } 169 170 class FaviconImageSource : public gfx::ImageSkiaSource { 171 public: 172 FaviconImageSource() {} 173 virtual ~FaviconImageSource() {} 174 175 // gfx::ImageSkiaSource: 176 virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 177 const gfx::ImageSkiaRep* rep = NULL; 178 // gfx::ImageSkia passes one of the resource scale factors. The source 179 // should return: 180 // 1) The ImageSkiaRep with the highest scale if all available 181 // scales are smaller than |scale|. 182 // 2) The ImageSkiaRep with the smallest one that is larger than |scale|. 183 // Note: Keep this logic consistent with the PNGImageSource in 184 // ui/gfx/image.cc. 185 // TODO(oshima): consolidate these logic into one place. 186 for (std::vector<gfx::ImageSkiaRep>::const_iterator iter = 187 image_skia_reps_.begin(); 188 iter != image_skia_reps_.end(); ++iter) { 189 if ((*iter).scale() == scale) 190 return (*iter); 191 if (!rep || rep->scale() < (*iter).scale()) 192 rep = &(*iter); 193 if (rep->scale() >= scale) 194 break; 195 } 196 DCHECK(rep); 197 return rep ? *rep : gfx::ImageSkiaRep(); 198 } 199 200 void AddImageSkiaRep(const gfx::ImageSkiaRep& rep) { 201 image_skia_reps_.push_back(rep); 202 } 203 204 private: 205 std::vector<gfx::ImageSkiaRep> image_skia_reps_; 206 DISALLOW_COPY_AND_ASSIGN(FaviconImageSource); 207 }; 208 209 } // namespace 210 211 const float kSelectFaviconFramesInvalidScore = -1.0f; 212 213 gfx::ImageSkia CreateFaviconImageSkia( 214 const std::vector<SkBitmap>& bitmaps, 215 const std::vector<gfx::Size>& original_sizes, 216 int desired_size_in_dip, 217 float* score) { 218 219 const std::vector<float>& favicon_scales = favicon_base::GetFaviconScales(); 220 std::vector<int> desired_sizes; 221 222 if (desired_size_in_dip == 0) { 223 desired_sizes.push_back(0); 224 } else { 225 for (std::vector<float>::const_iterator iter = favicon_scales.begin(); 226 iter != favicon_scales.end(); ++iter) { 227 desired_sizes.push_back(ceil(desired_size_in_dip * (*iter))); 228 } 229 } 230 231 std::vector<SelectionResult> results; 232 GetCandidateIndicesWithBestScores(original_sizes, 233 desired_sizes, 234 score, 235 &results); 236 if (results.size() == 0) 237 return gfx::ImageSkia(); 238 239 if (desired_size_in_dip == 0) { 240 size_t index = results[0].index; 241 return gfx::ImageSkia(gfx::ImageSkiaRep(bitmaps[index], 1.0f)); 242 } 243 244 FaviconImageSource* image_source = new FaviconImageSource; 245 246 for (size_t i = 0; i < results.size(); ++i) { 247 size_t index = results[i].index; 248 image_source->AddImageSkiaRep( 249 gfx::ImageSkiaRep(GetResizedBitmap(bitmaps[index], 250 original_sizes[index], 251 desired_sizes[i]), 252 favicon_scales[i])); 253 } 254 return gfx::ImageSkia(image_source, 255 gfx::Size(desired_size_in_dip, desired_size_in_dip)); 256 } 257 258 void SelectFaviconFrameIndices(const std::vector<gfx::Size>& frame_pixel_sizes, 259 const std::vector<int>& desired_sizes, 260 std::vector<size_t>* best_indices, 261 float* match_score) { 262 std::vector<SelectionResult> results; 263 GetCandidateIndicesWithBestScores( 264 frame_pixel_sizes, desired_sizes, match_score, &results); 265 266 std::set<size_t> already_added; 267 for (size_t i = 0; i < results.size(); ++i) { 268 size_t index = results[i].index; 269 // GetCandidateIndicesWithBestScores() will return duplicate indices if the 270 // bitmap data with |frame_pixel_sizes[index]| should be used for multiple 271 // scale factors. Remove duplicates here such that |best_indices| contains 272 // no duplicates. 273 if (already_added.find(index) == already_added.end()) { 274 already_added.insert(index); 275 best_indices->push_back(index); 276 } 277 } 278 } 279