• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BiggestCandidate(const std::vector<gfx::Size> & candidate_sizes)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 
SampleNearestNeighbor(const SkBitmap & contents,int desired_size)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 
GetCandidateIndexWithBestScore(const std::vector<gfx::Size> & candidate_sizes,int desired_size,float * score)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 
GetCandidateIndicesWithBestScores(const std::vector<gfx::Size> & candidate_sizes,const std::vector<int> & desired_sizes,float * match_score,std::vector<SelectionResult> * results)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|
GetResizedBitmap(const SkBitmap & source_bitmap,gfx::Size original_size,int desired_size_in_pixel)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:
FaviconImageSource()172   FaviconImageSource() {}
~FaviconImageSource()173   virtual ~FaviconImageSource() {}
174 
175   // gfx::ImageSkiaSource:
GetImageForScale(float scale)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 
AddImageSkiaRep(const gfx::ImageSkiaRep & rep)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 
CreateFaviconImageSkia(const std::vector<SkBitmap> & bitmaps,const std::vector<gfx::Size> & original_sizes,int desired_size_in_dip,float * score)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 
SelectFaviconFrameIndices(const std::vector<gfx::Size> & frame_pixel_sizes,const std::vector<int> & desired_sizes,std::vector<size_t> * best_indices,float * match_score)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