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