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.allocN32Pixels(desired_size_in_pixel, desired_size_in_pixel);
120 if (!best_bitmap.isOpaque())
121 bitmap.eraseARGB(0, 0, 0, 0);
122
123 SkCanvas canvas(bitmap);
124 SkRect dest(SkRect::MakeWH(desired_size_in_pixel, desired_size_in_pixel));
125 canvas.drawBitmapRect(best_bitmap, NULL, dest);
126 return bitmap;
127 }
128 return skia::ImageOperations::Resize(best_bitmap,
129 skia::ImageOperations::RESIZE_LANCZOS3,
130 desired_size_in_pixel,
131 desired_size_in_pixel);
132 }
133
134 } // namespace
135
GetFaviconScales()136 std::vector<float> GetFaviconScales() {
137 const float kScale1x = 1.0f;
138 std::vector<ui::ScaleFactor> resource_scale_factors =
139 ui::GetSupportedScaleFactors();
140
141 // TODO(ios): 1.0f should not be necessary on iOS retina devices. However
142 // the sync service only supports syncing 100p favicons. Until sync supports
143 // other scales 100p is needed in the list of scales to retrieve and
144 // store the favicons in both 100p for sync and 200p for display. cr/160503.
145 std::vector<float> favicon_scales(1, kScale1x);
146 for (size_t i = 0; i < resource_scale_factors.size(); ++i) {
147 if (resource_scale_factors[i] != ui::SCALE_FACTOR_100P)
148 favicon_scales.push_back(
149 ui::GetScaleForScaleFactor(resource_scale_factors[i]));
150 }
151 return favicon_scales;
152 }
153
SetFaviconColorSpace(gfx::Image * image)154 void SetFaviconColorSpace(gfx::Image* image) {
155 #if defined(OS_MACOSX) && !defined(OS_IOS)
156 image->SetSourceColorSpace(base::mac::GetSystemColorSpace());
157 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
158 }
159
SelectFaviconFramesFromPNGs(const std::vector<favicon_base::FaviconRawBitmapResult> & png_data,const std::vector<float> & favicon_scales,int favicon_size)160 gfx::Image SelectFaviconFramesFromPNGs(
161 const std::vector<favicon_base::FaviconRawBitmapResult>& png_data,
162 const std::vector<float>& favicon_scales,
163 int favicon_size) {
164 // Create image reps for as many scales as possible without resizing
165 // the bitmap data or decoding it. FaviconHandler stores already resized
166 // favicons into history so no additional resizing should be needed in the
167 // common case.
168 // Creating the gfx::Image from |png_data| without resizing or decoding if
169 // possible is important because:
170 // - Sync does a byte-to-byte comparison of gfx::Image::As1xPNGBytes() to
171 // the data it put into the database in order to determine whether any
172 // updates should be pushed to sync.
173 // - The decoding occurs on the UI thread and the decoding can be a
174 // significant performance hit if a user has many bookmarks.
175 // TODO(pkotwicz): Move the decoding off the UI thread.
176 std::vector<gfx::ImagePNGRep> png_reps =
177 SelectFaviconFramesFromPNGsWithoutResizing(
178 png_data, favicon_scales, favicon_size);
179
180 // SelectFaviconFramesFromPNGsWithoutResizing() should have selected the
181 // largest favicon if |favicon_size| == 0.
182 if (favicon_size == 0)
183 return gfx::Image(png_reps);
184
185 std::vector<float> favicon_scales_to_generate = favicon_scales;
186 for (size_t i = 0; i < png_reps.size(); ++i) {
187 std::vector<float>::iterator iter = std::find(
188 favicon_scales_to_generate.begin(),
189 favicon_scales_to_generate.end(),
190 png_reps[i].scale);
191 if (iter != favicon_scales_to_generate.end())
192 favicon_scales_to_generate.erase(iter);
193 }
194
195 if (favicon_scales_to_generate.empty())
196 return gfx::Image(png_reps);
197
198 std::vector<SkBitmap> bitmaps;
199 for (size_t i = 0; i < png_data.size(); ++i) {
200 if (!png_data[i].is_valid())
201 continue;
202
203 SkBitmap bitmap;
204 if (gfx::PNGCodec::Decode(png_data[i].bitmap_data->front(),
205 png_data[i].bitmap_data->size(),
206 &bitmap)) {
207 bitmaps.push_back(bitmap);
208 }
209 }
210
211 if (bitmaps.empty())
212 return gfx::Image();
213
214 gfx::ImageSkia resized_image_skia;
215 for (size_t i = 0; i < favicon_scales_to_generate.size(); ++i) {
216 float scale = favicon_scales_to_generate[i];
217 int desired_size_in_pixel = std::ceil(favicon_size * scale);
218 SkBitmap bitmap =
219 ResizeBitmapByDownsamplingIfPossible(bitmaps, desired_size_in_pixel);
220 resized_image_skia.AddRepresentation(gfx::ImageSkiaRep(bitmap, scale));
221 }
222
223 if (png_reps.empty())
224 return gfx::Image(resized_image_skia);
225
226 std::vector<gfx::ImageSkiaRep> resized_image_skia_reps =
227 resized_image_skia.image_reps();
228 for (size_t i = 0; i < resized_image_skia_reps.size(); ++i) {
229 scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes());
230 if (gfx::PNGCodec::EncodeBGRASkBitmap(
231 resized_image_skia_reps[i].sk_bitmap(),
232 false,
233 &png_bytes->data())) {
234 png_reps.push_back(
235 gfx::ImagePNGRep(png_bytes, resized_image_skia_reps[i].scale()));
236 }
237 }
238
239 return gfx::Image(png_reps);
240 }
241
242 } // namespace favicon_base
243