Home | History | Annotate | Download | only in favicon_base
      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