Home | History | Annotate | Download | only in favicon
      1 // Copyright (c) 2012 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 "chrome/browser/favicon/favicon_util.h"
      6 
      7 #include "chrome/browser/history/select_favicon_frames.h"
      8 #include "chrome/common/favicon/favicon_types.h"
      9 #include "skia/ext/image_operations.h"
     10 #include "third_party/skia/include/core/SkBitmap.h"
     11 #include "third_party/skia/include/core/SkCanvas.h"
     12 #include "ui/gfx/codec/png_codec.h"
     13 #include "ui/gfx/favicon_size.h"
     14 #include "ui/gfx/image/image_png_rep.h"
     15 #include "ui/gfx/image/image_skia.h"
     16 #include "ui/gfx/size.h"
     17 
     18 #if defined(OS_MACOSX) && !defined(OS_IOS)
     19 #include "base/mac/mac_util.h"
     20 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
     21 
     22 namespace {
     23 
     24 // Creates image reps of DIP size |favicon_size| for the subset of
     25 // |scale_factors| for which the image reps can be created without resizing
     26 // or decoding the bitmap data.
     27 std::vector<gfx::ImagePNGRep> SelectFaviconFramesFromPNGsWithoutResizing(
     28     const std::vector<chrome::FaviconBitmapResult>& png_data,
     29     const std::vector<ui::ScaleFactor>& scale_factors,
     30     int favicon_size) {
     31   std::vector<gfx::ImagePNGRep> png_reps;
     32   if (png_data.empty())
     33     return png_reps;
     34 
     35   // A |favicon_size| of 0 indicates that the largest frame is desired.
     36   if (favicon_size == 0) {
     37     int maximum_area = 0;
     38     scoped_refptr<base::RefCountedMemory> best_candidate;
     39     for (size_t i = 0; i < png_data.size(); ++i) {
     40       int area = png_data[i].pixel_size.GetArea();
     41       if (area > maximum_area) {
     42         maximum_area = area;
     43         best_candidate = png_data[i].bitmap_data;
     44       }
     45     }
     46     png_reps.push_back(gfx::ImagePNGRep(best_candidate, 1.0f));
     47     return png_reps;
     48   }
     49 
     50   // Cache the scale factor for each pixel size as |scale_factors| may contain
     51   // any of GetFaviconScaleFactors() which may include scale factors not
     52   // supported by the platform. (ui::GetSupportedScaleFactor() cannot be used.)
     53   std::map<int, ui::ScaleFactor> desired_pixel_sizes;
     54   for (size_t i = 0; i < scale_factors.size(); ++i) {
     55     int pixel_size = floor(favicon_size *
     56         ui::GetImageScale(scale_factors[i]));
     57     desired_pixel_sizes[pixel_size] = scale_factors[i];
     58   }
     59 
     60   for (size_t i = 0; i < png_data.size(); ++i) {
     61     if (!png_data[i].is_valid())
     62       continue;
     63 
     64     const gfx::Size& pixel_size = png_data[i].pixel_size;
     65     if (pixel_size.width() != pixel_size.height())
     66       continue;
     67 
     68     std::map<int, ui::ScaleFactor>::iterator it = desired_pixel_sizes.find(
     69         pixel_size.width());
     70     if (it == desired_pixel_sizes.end())
     71       continue;
     72 
     73     png_reps.push_back(
     74         gfx::ImagePNGRep(png_data[i].bitmap_data,
     75                          ui::GetImageScale(it->second)));
     76   }
     77 
     78   return png_reps;
     79 }
     80 
     81 // Returns a resampled bitmap of
     82 // |desired_size_in_pixel| x |desired_size_in_pixel| by resampling the best
     83 // bitmap out of |input_bitmaps|. ResizeBitmapByDownsamplingIfPossible() is
     84 // similar to SelectFaviconFrames() but it operates on bitmaps which have
     85 // already been resampled via SelectFaviconFrames().
     86 SkBitmap ResizeBitmapByDownsamplingIfPossible(
     87     const std::vector<SkBitmap>& input_bitmaps,
     88     int desired_size_in_pixel) {
     89   DCHECK(!input_bitmaps.empty());
     90   DCHECK_NE(desired_size_in_pixel, 0);
     91 
     92   SkBitmap best_bitmap;
     93   for (size_t i = 0; i < input_bitmaps.size(); ++i) {
     94     const SkBitmap& input_bitmap = input_bitmaps[i];
     95     if (input_bitmap.width() == desired_size_in_pixel &&
     96         input_bitmap.height() == desired_size_in_pixel) {
     97       return input_bitmap;
     98     } else if (best_bitmap.isNull()) {
     99       best_bitmap = input_bitmap;
    100     } else if (input_bitmap.width() >= best_bitmap.width() &&
    101                input_bitmap.height() >= best_bitmap.height()) {
    102       if (best_bitmap.width() < desired_size_in_pixel ||
    103           best_bitmap.height() < desired_size_in_pixel) {
    104         best_bitmap = input_bitmap;
    105       }
    106     } else {
    107       if (input_bitmap.width() >= desired_size_in_pixel &&
    108           input_bitmap.height() >= desired_size_in_pixel) {
    109         best_bitmap = input_bitmap;
    110       }
    111     }
    112   }
    113 
    114   if (desired_size_in_pixel % best_bitmap.width() == 0 &&
    115       desired_size_in_pixel % best_bitmap.height() == 0) {
    116     // Use nearest neighbour resampling if upsampling by an integer. This
    117     // makes the result look similar to the result of SelectFaviconFrames().
    118     SkBitmap bitmap;
    119     bitmap.setConfig(SkBitmap::kARGB_8888_Config,
    120                      desired_size_in_pixel,
    121                      desired_size_in_pixel);
    122     bitmap.allocPixels();
    123     if (!best_bitmap.isOpaque())
    124       bitmap.eraseARGB(0, 0, 0, 0);
    125 
    126     SkCanvas canvas(bitmap);
    127     SkRect dest(SkRect::MakeWH(desired_size_in_pixel, desired_size_in_pixel));
    128     canvas.drawBitmapRect(best_bitmap, NULL, dest);
    129     return bitmap;
    130   }
    131   return skia::ImageOperations::Resize(best_bitmap,
    132                                        skia::ImageOperations::RESIZE_LANCZOS3,
    133                                        desired_size_in_pixel,
    134                                        desired_size_in_pixel);
    135 }
    136 
    137 }  // namespace
    138 
    139 // static
    140 std::vector<ui::ScaleFactor> FaviconUtil::GetFaviconScaleFactors() {
    141   const float kScale1x = ui::GetImageScale(ui::SCALE_FACTOR_100P);
    142   std::vector<ui::ScaleFactor> favicon_scale_factors =
    143       ui::GetSupportedScaleFactors();
    144 
    145   // The scale factors returned from ui::GetSupportedScaleFactors() are sorted.
    146   // Insert the 1x scale factor such that GetFaviconScaleFactors() is sorted as
    147   // well.
    148   size_t insert_index = favicon_scale_factors.size();
    149   for (size_t i = 0; i < favicon_scale_factors.size(); ++i) {
    150     float scale = ui::GetImageScale(favicon_scale_factors[i]);
    151     if (scale == kScale1x) {
    152       return favicon_scale_factors;
    153     } else if (scale > kScale1x) {
    154       insert_index = i;
    155       break;
    156     }
    157   }
    158   // TODO(ios): 100p should not be necessary on iOS retina devices. However
    159   // the sync service only supports syncing 100p favicons. Until sync supports
    160   // other scales 100p is needed in the list of scale factors to retrieve and
    161   // store the favicons in both 100p for sync and 200p for display. cr/160503.
    162   favicon_scale_factors.insert(favicon_scale_factors.begin() + insert_index,
    163                                ui::SCALE_FACTOR_100P);
    164   return favicon_scale_factors;
    165 }
    166 
    167 // static
    168 void FaviconUtil::SetFaviconColorSpace(gfx::Image* image) {
    169 #if defined(OS_MACOSX) && !defined(OS_IOS)
    170   image->SetSourceColorSpace(base::mac::GetSystemColorSpace());
    171 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
    172 }
    173 
    174 // static
    175 gfx::Image FaviconUtil::SelectFaviconFramesFromPNGs(
    176       const std::vector<chrome::FaviconBitmapResult>& png_data,
    177       const std::vector<ui::ScaleFactor>& scale_factors,
    178       int favicon_size) {
    179   // Create image reps for as many scale factors as possible without resizing
    180   // the bitmap data or decoding it. FaviconHandler stores already resized
    181   // favicons into history so no additional resizing should be needed in the
    182   // common case.
    183   // Creating the gfx::Image from |png_data| without resizing or decoding if
    184   // possible is important because:
    185   // - Sync does a byte-to-byte comparison of gfx::Image::As1xPNGBytes() to
    186   //   the data it put into the database in order to determine whether any
    187   //   updates should be pushed to sync.
    188   // - The decoding occurs on the UI thread and the decoding can be a
    189   //   significant performance hit if a user has many bookmarks.
    190   // TODO(pkotwicz): Move the decoding off the UI thread.
    191   std::vector<gfx::ImagePNGRep> png_reps =
    192       SelectFaviconFramesFromPNGsWithoutResizing(png_data, scale_factors,
    193           favicon_size);
    194 
    195   // SelectFaviconFramesFromPNGsWithoutResizing() should have selected the
    196   // largest favicon if |favicon_size| == 0.
    197   if (favicon_size == 0)
    198     return gfx::Image(png_reps);
    199 
    200   std::vector<ui::ScaleFactor> scale_factors_to_generate = scale_factors;
    201   for (size_t i = 0; i < png_reps.size(); ++i) {
    202     std::vector<ui::ScaleFactor>::iterator it = std::find(
    203         scale_factors_to_generate.begin(),
    204         scale_factors_to_generate.end(),
    205         ui::GetSupportedScaleFactor(png_reps[i].scale));
    206     CHECK(it != scale_factors_to_generate.end());
    207     scale_factors_to_generate.erase(it);
    208   }
    209 
    210   if (scale_factors_to_generate.empty())
    211     return gfx::Image(png_reps);
    212 
    213   std::vector<SkBitmap> bitmaps;
    214   for (size_t i = 0; i < png_data.size(); ++i) {
    215     if (!png_data[i].is_valid())
    216       continue;
    217 
    218     SkBitmap bitmap;
    219     if (gfx::PNGCodec::Decode(png_data[i].bitmap_data->front(),
    220                               png_data[i].bitmap_data->size(),
    221                               &bitmap)) {
    222       bitmaps.push_back(bitmap);
    223     }
    224   }
    225 
    226   if (bitmaps.empty())
    227     return gfx::Image();
    228 
    229   gfx::ImageSkia resized_image_skia;
    230   for (size_t i = 0; i < scale_factors_to_generate.size(); ++i) {
    231     ui::ScaleFactor scale_factor = scale_factors_to_generate[i];
    232     int desired_size_in_pixel =
    233         ceil(favicon_size * ui::GetImageScale(scale_factor));
    234     SkBitmap bitmap = ResizeBitmapByDownsamplingIfPossible(
    235         bitmaps, desired_size_in_pixel);
    236     resized_image_skia.AddRepresentation(
    237         gfx::ImageSkiaRep(bitmap, scale_factor));
    238   }
    239 
    240   if (png_reps.empty())
    241     return gfx::Image(resized_image_skia);
    242 
    243   std::vector<gfx::ImageSkiaRep> resized_image_skia_reps =
    244       resized_image_skia.image_reps();
    245   for (size_t i = 0; i < resized_image_skia_reps.size(); ++i) {
    246     scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes());
    247     if (gfx::PNGCodec::EncodeBGRASkBitmap(
    248         resized_image_skia_reps[i].sk_bitmap(), false, &png_bytes->data())) {
    249       png_reps.push_back(gfx::ImagePNGRep(png_bytes,
    250           resized_image_skia_reps[i].scale()));
    251     }
    252   }
    253 
    254   return gfx::Image(png_reps);
    255 }
    256