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.
5 #include "chrome/browser/favicon/favicon_util.h"
7 #include "components/favicon_base/favicon_types.h"
8 #include "components/favicon_base/select_favicon_frames.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"
18 #if defined(OS_MACOSX) && !defined(OS_IOS)
19 #include "base/mac/mac_util.h"
20 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
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
<favicon_base::FaviconBitmapResult
>& png_data
,
29 const std::vector
<ui::ScaleFactor
>& scale_factors
,
31 std::vector
<gfx::ImagePNGRep
> png_reps
;
35 // A |favicon_size| of 0 indicates that the largest frame is desired.
36 if (favicon_size
== 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
) {
43 best_candidate
= png_data
[i
].bitmap_data
;
46 png_reps
.push_back(gfx::ImagePNGRep(best_candidate
, 1.0f
));
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
];
60 for (size_t i
= 0; i
< png_data
.size(); ++i
) {
61 if (!png_data
[i
].is_valid())
64 const gfx::Size
& pixel_size
= png_data
[i
].pixel_size
;
65 if (pixel_size
.width() != pixel_size
.height())
68 std::map
<int, ui::ScaleFactor
>::iterator it
= desired_pixel_sizes
.find(
70 if (it
== desired_pixel_sizes
.end())
74 gfx::ImagePNGRep(png_data
[i
].bitmap_data
,
75 ui::GetImageScale(it
->second
)));
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);
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
) {
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
;
107 if (input_bitmap
.width() >= desired_size_in_pixel
&&
108 input_bitmap
.height() >= desired_size_in_pixel
) {
109 best_bitmap
= input_bitmap
;
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().
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);
126 SkCanvas
canvas(bitmap
);
127 SkRect
dest(SkRect::MakeWH(desired_size_in_pixel
, desired_size_in_pixel
));
128 canvas
.drawBitmapRect(best_bitmap
, NULL
, dest
);
131 return skia::ImageOperations::Resize(best_bitmap
,
132 skia::ImageOperations::RESIZE_LANCZOS3
,
133 desired_size_in_pixel
,
134 desired_size_in_pixel
);
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();
145 // The scale factors returned from ui::GetSupportedScaleFactors() are sorted.
146 // Insert the 1x scale factor such that GetFaviconScaleFactors() is sorted as
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
) {
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
;
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)
175 gfx::Image
FaviconUtil::SelectFaviconFramesFromPNGs(
176 const std::vector
<favicon_base::FaviconBitmapResult
>& png_data
,
177 const std::vector
<ui::ScaleFactor
>& scale_factors
,
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
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
,
195 // SelectFaviconFramesFromPNGsWithoutResizing() should have selected the
196 // largest favicon if |favicon_size| == 0.
197 if (favicon_size
== 0)
198 return gfx::Image(png_reps
);
200 std::vector
<ui::ScaleFactor
> scale_factors_to_generate
= scale_factors
;
201 for (size_t i
= 0; i
< png_reps
.size(); ++i
) {
202 for (int j
= static_cast<int>(scale_factors_to_generate
.size()) - 1;
204 if (png_reps
[i
].scale
== ui::GetImageScale(scale_factors_to_generate
[j
]))
205 scale_factors_to_generate
.erase(scale_factors_to_generate
.begin() + j
);
209 if (scale_factors_to_generate
.empty())
210 return gfx::Image(png_reps
);
212 std::vector
<SkBitmap
> bitmaps
;
213 for (size_t i
= 0; i
< png_data
.size(); ++i
) {
214 if (!png_data
[i
].is_valid())
218 if (gfx::PNGCodec::Decode(png_data
[i
].bitmap_data
->front(),
219 png_data
[i
].bitmap_data
->size(),
221 bitmaps
.push_back(bitmap
);
228 gfx::ImageSkia resized_image_skia
;
229 for (size_t i
= 0; i
< scale_factors_to_generate
.size(); ++i
) {
230 float scale
= ui::GetImageScale(scale_factors_to_generate
[i
]);
231 int desired_size_in_pixel
= ceil(favicon_size
* scale
);
232 SkBitmap bitmap
= ResizeBitmapByDownsamplingIfPossible(
233 bitmaps
, desired_size_in_pixel
);
234 resized_image_skia
.AddRepresentation(gfx::ImageSkiaRep(bitmap
, scale
));
237 if (png_reps
.empty())
238 return gfx::Image(resized_image_skia
);
240 std::vector
<gfx::ImageSkiaRep
> resized_image_skia_reps
=
241 resized_image_skia
.image_reps();
242 for (size_t i
= 0; i
< resized_image_skia_reps
.size(); ++i
) {
243 scoped_refptr
<base::RefCountedBytes
> png_bytes(new base::RefCountedBytes());
244 if (gfx::PNGCodec::EncodeBGRASkBitmap(
245 resized_image_skia_reps
[i
].sk_bitmap(), false, &png_bytes
->data())) {
246 png_reps
.push_back(gfx::ImagePNGRep(png_bytes
,
247 resized_image_skia_reps
[i
].scale()));
251 return gfx::Image(png_reps
);