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 "ui/gfx/codec/png_codec.h"
12#include "ui/gfx/favicon_size.h"
13#include "ui/gfx/image/image_png_rep.h"
14#include "ui/gfx/image/image_skia.h"
15#include "ui/gfx/size.h"
16
17#if defined(OS_MACOSX) && !defined(OS_IOS)
18#include "base/mac/mac_util.h"
19#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
20
21namespace {
22
23// Creates image reps of DIP size |favicon_size| for the subset of
24// |scale_factors| for which the image reps can be created without resizing
25// or decoding the bitmap data.
26std::vector<gfx::ImagePNGRep> SelectFaviconFramesFromPNGsWithoutResizing(
27    const std::vector<chrome::FaviconBitmapResult>& png_data,
28    const std::vector<ui::ScaleFactor>& scale_factors,
29    int favicon_size) {
30  std::vector<gfx::ImagePNGRep> png_reps;
31  if (png_data.empty())
32    return png_reps;
33
34  // A |favicon_size| of 0 indicates that the largest frame is desired.
35  if (favicon_size == 0 && scale_factors.size() == 1) {
36    int maximum_area = 0;
37    scoped_refptr<base::RefCountedMemory> best_candidate;
38    for (size_t i = 0; i < png_data.size(); ++i) {
39      int area = png_data[i].pixel_size.GetArea();
40      if (area > maximum_area) {
41        maximum_area = area;
42        best_candidate = png_data[i].bitmap_data;
43      }
44    }
45    png_reps.push_back(gfx::ImagePNGRep(best_candidate,
46                                        scale_factors[0]));
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::GetScaleFactorFromScale() 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::GetScaleFactorScale(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(gfx::ImagePNGRep(png_data[i].bitmap_data, it->second));
74  }
75
76  return png_reps;
77}
78
79}  // namespace
80
81// static
82std::vector<ui::ScaleFactor> FaviconUtil::GetFaviconScaleFactors() {
83  const float kScale1x = ui::GetScaleFactorScale(ui::SCALE_FACTOR_100P);
84  std::vector<ui::ScaleFactor> favicon_scale_factors =
85      ui::GetSupportedScaleFactors();
86
87  // The scale factors returned from ui::GetSupportedScaleFactors() are sorted.
88  // Insert the 1x scale factor such that GetFaviconScaleFactors() is sorted as
89  // well.
90  size_t insert_index = favicon_scale_factors.size();
91  for (size_t i = 0; i < favicon_scale_factors.size(); ++i) {
92    float scale = ui::GetScaleFactorScale(favicon_scale_factors[i]);
93    if (scale == kScale1x) {
94      return favicon_scale_factors;
95    } else if (scale > kScale1x) {
96      insert_index = i;
97      break;
98    }
99  }
100  // TODO(ios): 100p should not be necessary on iOS retina devices. However
101  // the sync service only supports syncing 100p favicons. Until sync supports
102  // other scales 100p is needed in the list of scale factors to retrieve and
103  // store the favicons in both 100p for sync and 200p for display. cr/160503.
104  favicon_scale_factors.insert(favicon_scale_factors.begin() + insert_index,
105                               ui::SCALE_FACTOR_100P);
106  return favicon_scale_factors;
107}
108
109// static
110void FaviconUtil::SetFaviconColorSpace(gfx::Image* image) {
111#if defined(OS_MACOSX) && !defined(OS_IOS)
112  image->SetSourceColorSpace(base::mac::GetSystemColorSpace());
113#endif  // defined(OS_MACOSX) && !defined(OS_IOS)
114}
115
116// static
117gfx::Image FaviconUtil::SelectFaviconFramesFromPNGs(
118      const std::vector<chrome::FaviconBitmapResult>& png_data,
119      const std::vector<ui::ScaleFactor>& scale_factors,
120      int favicon_size) {
121  // Create image reps for as many scale factors as possible without resizing
122  // the bitmap data or decoding it. FaviconHandler stores already resized
123  // favicons into history so no additional resizing should be needed in the
124  // common case.
125  // Creating the gfx::Image from |png_data| without resizing or decoding if
126  // possible is important because:
127  // - Sync does a byte-to-byte comparison of gfx::Image::As1xPNGBytes() to
128  //   the data it put into the database in order to determine whether any
129  //   updates should be pushed to sync.
130  // - The decoding occurs on the UI thread and the decoding can be a
131  //   significant performance hit if a user has many bookmarks.
132  // TODO(pkotwicz): Move the decoding off the UI thread.
133  std::vector<gfx::ImagePNGRep> png_reps =
134      SelectFaviconFramesFromPNGsWithoutResizing(png_data, scale_factors,
135          favicon_size);
136
137  std::vector<ui::ScaleFactor> scale_factors_to_generate = scale_factors;
138  for (size_t i = 0; i < png_reps.size(); ++i) {
139    std::vector<ui::ScaleFactor>::iterator it = std::find(
140        scale_factors_to_generate.begin(),
141        scale_factors_to_generate.end(),
142        png_reps[i].scale_factor);
143    CHECK(it != scale_factors_to_generate.end());
144    scale_factors_to_generate.erase(it);
145  }
146
147  if (scale_factors_to_generate.empty())
148    return gfx::Image(png_reps);
149
150  std::vector<SkBitmap> bitmaps;
151  for (size_t i = 0; i < png_data.size(); ++i) {
152    if (!png_data[i].is_valid())
153      continue;
154
155    SkBitmap bitmap;
156    if (gfx::PNGCodec::Decode(png_data[i].bitmap_data->front(),
157                              png_data[i].bitmap_data->size(),
158                              &bitmap)) {
159      bitmaps.push_back(bitmap);
160    }
161  }
162
163  if (bitmaps.empty())
164    return gfx::Image();
165
166  gfx::ImageSkia resized_image_skia = SelectFaviconFrames(bitmaps,
167      scale_factors_to_generate, favicon_size, NULL);
168
169  if (png_reps.empty())
170    return gfx::Image(resized_image_skia);
171
172  std::vector<gfx::ImageSkiaRep> resized_image_skia_reps =
173      resized_image_skia.image_reps();
174  for (size_t i = 0; i < resized_image_skia_reps.size(); ++i) {
175    scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes());
176    if (gfx::PNGCodec::EncodeBGRASkBitmap(
177        resized_image_skia_reps[i].sk_bitmap(), false, &png_bytes->data())) {
178      png_reps.push_back(gfx::ImagePNGRep(png_bytes,
179          resized_image_skia_reps[i].scale_factor()));
180    }
181  }
182
183  return gfx::Image(png_reps);
184}
185
186// static
187size_t FaviconUtil::SelectBestFaviconFromBitmaps(
188    const std::vector<SkBitmap>& bitmaps,
189    const std::vector<ui::ScaleFactor>& scale_factors,
190    int desired_size) {
191  std::vector<gfx::Size> sizes;
192  for (size_t i = 0; i < bitmaps.size(); ++i)
193    sizes.push_back(gfx::Size(bitmaps[i].width(), bitmaps[i].height()));
194  std::vector<size_t> selected_bitmap_indices;
195  SelectFaviconFrameIndices(sizes, scale_factors, desired_size,
196                            &selected_bitmap_indices, NULL);
197  DCHECK_EQ(1u, selected_bitmap_indices.size());
198  return selected_bitmap_indices[0];
199}
200