• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
SelectFaviconFramesFromPNGsWithoutResizing(const std::vector<favicon_base::FaviconRawBitmapResult> & png_data,const std::vector<float> & favicon_scales,int favicon_size)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().
ResizeBitmapByDownsamplingIfPossible(const std::vector<SkBitmap> & input_bitmaps,int desired_size_in_pixel)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 
GetFaviconScales()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 
SetFaviconColorSpace(gfx::Image * image)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 
SelectFaviconFramesFromPNGs(const std::vector<favicon_base::FaviconRawBitmapResult> & png_data,const std::vector<float> & favicon_scales,int favicon_size)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