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/favicon_util.h"
      6 
      7 #include <cmath>
      8 
      9 #include "components/favicon_base/favicon_types.h"
     10 #include "components/favicon_base/select_favicon_frames.h"
     11 #include "skia/ext/image_operations.h"
     12 #include "third_party/skia/include/core/SkBitmap.h"
     13 #include "third_party/skia/include/core/SkCanvas.h"
     14 #include "ui/base/layout.h"
     15 #include "ui/gfx/codec/png_codec.h"
     16 #include "ui/gfx/favicon_size.h"
     17 #include "ui/gfx/image/image_png_rep.h"
     18 #include "ui/gfx/image/image_skia.h"
     19 #include "ui/gfx/size.h"
     20 
     21 #if defined(OS_MACOSX) && !defined(OS_IOS)
     22 #include "base/mac/mac_util.h"
     23 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
     24 
     25 namespace favicon_base {
     26 namespace {
     27 
     28 // Creates image reps of DIP size |favicon_size| for the subset of
     29 // |favicon_scales| for which the image reps can be created without resizing
     30 // or decoding the bitmap data.
     31 std::vector<gfx::ImagePNGRep> SelectFaviconFramesFromPNGsWithoutResizing(
     32     const std::vector<favicon_base::FaviconRawBitmapResult>& png_data,
     33     const std::vector<float>& favicon_scales,
     34     int favicon_size) {
     35   std::vector<gfx::ImagePNGRep> png_reps;
     36   if (png_data.empty())
     37     return png_reps;
     38 
     39   // A |favicon_size| of 0 indicates that the largest frame is desired.
     40   if (favicon_size == 0) {
     41     int maximum_area = 0;
     42     scoped_refptr<base::RefCountedMemory> best_candidate;
     43     for (size_t i = 0; i < png_data.size(); ++i) {
     44       int area = png_data[i].pixel_size.GetArea();
     45       if (area > maximum_area) {
     46         maximum_area = area;
     47         best_candidate = png_data[i].bitmap_data;
     48       }
     49     }
     50     png_reps.push_back(gfx::ImagePNGRep(best_candidate, 1.0f));
     51     return png_reps;
     52   }
     53 
     54   // Build a map which will be used to determine the scale used to
     55   // create a bitmap with given pixel size.
     56   std::map<int, float> desired_pixel_sizes;
     57   for (size_t i = 0; i < favicon_scales.size(); ++i) {
     58     int pixel_size = std::ceil(favicon_size * favicon_scales[i]);
     59     desired_pixel_sizes[pixel_size] = favicon_scales[i];
     60   }
     61 
     62   for (size_t i = 0; i < png_data.size(); ++i) {
     63     if (!png_data[i].is_valid())
     64       continue;
     65 
     66     const gfx::Size& pixel_size = png_data[i].pixel_size;
     67     if (pixel_size.width() != pixel_size.height())
     68       continue;
     69 
     70     std::map<int, float>::iterator it =
     71         desired_pixel_sizes.find(pixel_size.width());
     72     if (it == desired_pixel_sizes.end())
     73       continue;
     74 
     75     png_reps.push_back(gfx::ImagePNGRep(png_data[i].bitmap_data, 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 std::vector<float> GetFaviconScales() {
    140   const float kScale1x = 1.0f;
    141   std::vector<ui::ScaleFactor> resource_scale_factors =
    142       ui::GetSupportedScaleFactors();
    143 
    144   // TODO(ios): 1.0f should not be necessary on iOS retina devices. However
    145   // the sync service only supports syncing 100p favicons. Until sync supports
    146   // other scales 100p is needed in the list of scales to retrieve and
    147   // store the favicons in both 100p for sync and 200p for display. cr/160503.
    148   std::vector<float> favicon_scales(1, kScale1x);
    149   for (size_t i = 0; i < resource_scale_factors.size(); ++i) {
    150     if (resource_scale_factors[i] != ui::SCALE_FACTOR_100P)
    151       favicon_scales.push_back(
    152           ui::GetScaleForScaleFactor(resource_scale_factors[i]));
    153   }
    154   return favicon_scales;
    155 }
    156 
    157 void SetFaviconColorSpace(gfx::Image* image) {
    158 #if defined(OS_MACOSX) && !defined(OS_IOS)
    159   image->SetSourceColorSpace(base::mac::GetSystemColorSpace());
    160 #endif  // defined(OS_MACOSX) && !defined(OS_IOS)
    161 }
    162 
    163 gfx::Image SelectFaviconFramesFromPNGs(
    164     const std::vector<favicon_base::FaviconRawBitmapResult>& png_data,
    165     const std::vector<float>& favicon_scales,
    166     int favicon_size) {
    167   // Create image reps for as many scales as possible without resizing
    168   // the bitmap data or decoding it. FaviconHandler stores already resized
    169   // favicons into history so no additional resizing should be needed in the
    170   // common case.
    171   // Creating the gfx::Image from |png_data| without resizing or decoding if
    172   // possible is important because:
    173   // - Sync does a byte-to-byte comparison of gfx::Image::As1xPNGBytes() to
    174   //   the data it put into the database in order to determine whether any
    175   //   updates should be pushed to sync.
    176   // - The decoding occurs on the UI thread and the decoding can be a
    177   //   significant performance hit if a user has many bookmarks.
    178   // TODO(pkotwicz): Move the decoding off the UI thread.
    179   std::vector<gfx::ImagePNGRep> png_reps =
    180       SelectFaviconFramesFromPNGsWithoutResizing(
    181           png_data, favicon_scales, favicon_size);
    182 
    183   // SelectFaviconFramesFromPNGsWithoutResizing() should have selected the
    184   // largest favicon if |favicon_size| == 0.
    185   if (favicon_size == 0)
    186     return gfx::Image(png_reps);
    187 
    188   std::vector<float> favicon_scales_to_generate = favicon_scales;
    189   for (size_t i = 0; i < png_reps.size(); ++i) {
    190     for (int j = static_cast<int>(favicon_scales_to_generate.size()) - 1;
    191          j >= 0;
    192          --j) {
    193       if (png_reps[i].scale == favicon_scales_to_generate[j]) {
    194         favicon_scales_to_generate.erase(favicon_scales_to_generate.begin() +
    195                                          j);
    196       }
    197     }
    198   }
    199 
    200   if (favicon_scales_to_generate.empty())
    201     return gfx::Image(png_reps);
    202 
    203   std::vector<SkBitmap> bitmaps;
    204   for (size_t i = 0; i < png_data.size(); ++i) {
    205     if (!png_data[i].is_valid())
    206       continue;
    207 
    208     SkBitmap bitmap;
    209     if (gfx::PNGCodec::Decode(png_data[i].bitmap_data->front(),
    210                               png_data[i].bitmap_data->size(),
    211                               &bitmap)) {
    212       bitmaps.push_back(bitmap);
    213     }
    214   }
    215 
    216   if (bitmaps.empty())
    217     return gfx::Image();
    218 
    219   gfx::ImageSkia resized_image_skia;
    220   for (size_t i = 0; i < favicon_scales_to_generate.size(); ++i) {
    221     float scale = favicon_scales_to_generate[i];
    222     int desired_size_in_pixel = std::ceil(favicon_size * scale);
    223     SkBitmap bitmap =
    224         ResizeBitmapByDownsamplingIfPossible(bitmaps, desired_size_in_pixel);
    225     resized_image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
    226   }
    227 
    228   if (png_reps.empty())
    229     return gfx::Image(resized_image_skia);
    230 
    231   std::vector<gfx::ImageSkiaRep> resized_image_skia_reps =
    232       resized_image_skia.image_reps();
    233   for (size_t i = 0; i < resized_image_skia_reps.size(); ++i) {
    234     scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes());
    235     if (gfx::PNGCodec::EncodeBGRASkBitmap(
    236             resized_image_skia_reps[i].sk_bitmap(),
    237             false,
    238             &png_bytes->data())) {
    239       png_reps.push_back(
    240           gfx::ImagePNGRep(png_bytes, resized_image_skia_reps[i].scale()));
    241     }
    242   }
    243 
    244   return gfx::Image(png_reps);
    245 }
    246 
    247 }  // namespace favicon_base
    248