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.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