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.
5 #include "components/favicon_base/favicon_util.h"
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/geometry/size.h"
18 #include "ui/gfx/image/image_png_rep.h"
19 #include "ui/gfx/image/image_skia.h"
21 #if defined(OS_MACOSX) && !defined(OS_IOS)
22 #include "base/mac/mac_util.h"
23 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
25 namespace favicon_base
{
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
,
35 std::vector
<gfx::ImagePNGRep
> png_reps
;
39 // A |favicon_size| of 0 indicates that the largest frame is desired.
40 if (favicon_size
== 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
) {
47 best_candidate
= png_data
[i
].bitmap_data
;
50 png_reps
.push_back(gfx::ImagePNGRep(best_candidate
, 1.0f
));
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
) {
59 static_cast<int>(std::ceil(favicon_size
* favicon_scales
[i
]));
60 desired_pixel_sizes
[pixel_size
] = favicon_scales
[i
];
63 for (size_t i
= 0; i
< png_data
.size(); ++i
) {
64 if (!png_data
[i
].is_valid())
67 const gfx::Size
& pixel_size
= png_data
[i
].pixel_size
;
68 if (pixel_size
.width() != pixel_size
.height())
71 std::map
<int, float>::iterator it
=
72 desired_pixel_sizes
.find(pixel_size
.width());
73 if (it
== desired_pixel_sizes
.end())
76 png_reps
.push_back(gfx::ImagePNGRep(png_data
[i
].bitmap_data
, it
->second
));
82 // Returns a resampled bitmap of |desired_size| x |desired_size| by resampling
83 // the best bitmap out of |input_bitmaps|.
84 // ResizeBitmapByDownsamplingIfPossible() is similar to SelectFaviconFrames()
85 // but it operates on bitmaps which have already been resampled via
86 // SelectFaviconFrames().
87 SkBitmap
ResizeBitmapByDownsamplingIfPossible(
88 const std::vector
<SkBitmap
>& input_bitmaps
,
90 DCHECK(!input_bitmaps
.empty());
91 DCHECK_NE(0, desired_size
);
94 for (size_t i
= 0; i
< input_bitmaps
.size(); ++i
) {
95 const SkBitmap
& input_bitmap
= input_bitmaps
[i
];
96 if (input_bitmap
.width() == desired_size
&&
97 input_bitmap
.height() == desired_size
) {
99 } else if (best_bitmap
.isNull()) {
100 best_bitmap
= input_bitmap
;
101 } else if (input_bitmap
.width() >= best_bitmap
.width() &&
102 input_bitmap
.height() >= best_bitmap
.height()) {
103 if (best_bitmap
.width() < desired_size
||
104 best_bitmap
.height() < desired_size
) {
105 best_bitmap
= input_bitmap
;
108 if (input_bitmap
.width() >= desired_size
&&
109 input_bitmap
.height() >= desired_size
) {
110 best_bitmap
= input_bitmap
;
115 if (desired_size
% best_bitmap
.width() == 0 &&
116 desired_size
% best_bitmap
.height() == 0) {
117 // Use nearest neighbour resampling if upsampling by an integer. This
118 // makes the result look similar to the result of SelectFaviconFrames().
120 bitmap
.allocN32Pixels(desired_size
, desired_size
);
121 if (!best_bitmap
.isOpaque())
122 bitmap
.eraseARGB(0, 0, 0, 0);
124 SkCanvas
canvas(bitmap
);
125 canvas
.drawBitmapRect(
127 SkRect::MakeFromIRect(SkIRect::MakeWH(desired_size
, desired_size
)));
130 return skia::ImageOperations::Resize(best_bitmap
,
131 skia::ImageOperations::RESIZE_LANCZOS3
,
138 std::vector
<float> GetFaviconScales() {
139 const float kScale1x
= 1.0f
;
140 std::vector
<ui::ScaleFactor
> resource_scale_factors
=
141 ui::GetSupportedScaleFactors();
143 // TODO(ios): 1.0f should not be necessary on iOS retina devices. However
144 // the sync service only supports syncing 100p favicons. Until sync supports
145 // other scales 100p is needed in the list of scales to retrieve and
146 // store the favicons in both 100p for sync and 200p for display. cr/160503.
147 std::vector
<float> favicon_scales(1, kScale1x
);
148 for (size_t i
= 0; i
< resource_scale_factors
.size(); ++i
) {
149 if (resource_scale_factors
[i
] != ui::SCALE_FACTOR_100P
)
150 favicon_scales
.push_back(
151 ui::GetScaleForScaleFactor(resource_scale_factors
[i
]));
153 return favicon_scales
;
156 void SetFaviconColorSpace(gfx::Image
* image
) {
157 #if defined(OS_MACOSX) && !defined(OS_IOS)
158 image
->SetSourceColorSpace(base::mac::GetSystemColorSpace());
159 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
162 gfx::Image
SelectFaviconFramesFromPNGs(
163 const std::vector
<favicon_base::FaviconRawBitmapResult
>& png_data
,
164 const std::vector
<float>& favicon_scales
,
166 // Create image reps for as many scales as possible without resizing
167 // the bitmap data or decoding it. FaviconHandler stores already resized
168 // favicons into history so no additional resizing should be needed in the
170 // Creating the gfx::Image from |png_data| without resizing or decoding if
171 // possible is important because:
172 // - Sync does a byte-to-byte comparison of gfx::Image::As1xPNGBytes() to
173 // the data it put into the database in order to determine whether any
174 // updates should be pushed to sync.
175 // - The decoding occurs on the UI thread and the decoding can be a
176 // significant performance hit if a user has many bookmarks.
177 // TODO(pkotwicz): Move the decoding off the UI thread.
178 std::vector
<gfx::ImagePNGRep
> png_reps
=
179 SelectFaviconFramesFromPNGsWithoutResizing(
180 png_data
, favicon_scales
, favicon_size
);
182 // SelectFaviconFramesFromPNGsWithoutResizing() should have selected the
183 // largest favicon if |favicon_size| == 0.
184 if (favicon_size
== 0)
185 return gfx::Image(png_reps
);
187 std::vector
<float> favicon_scales_to_generate
= favicon_scales
;
188 for (size_t i
= 0; i
< png_reps
.size(); ++i
) {
189 std::vector
<float>::iterator iter
= std::find(
190 favicon_scales_to_generate
.begin(),
191 favicon_scales_to_generate
.end(),
193 if (iter
!= favicon_scales_to_generate
.end())
194 favicon_scales_to_generate
.erase(iter
);
197 if (favicon_scales_to_generate
.empty())
198 return gfx::Image(png_reps
);
200 std::vector
<SkBitmap
> bitmaps
;
201 for (size_t i
= 0; i
< png_data
.size(); ++i
) {
202 if (!png_data
[i
].is_valid())
206 if (gfx::PNGCodec::Decode(png_data
[i
].bitmap_data
->front(),
207 png_data
[i
].bitmap_data
->size(),
209 bitmaps
.push_back(bitmap
);
216 gfx::ImageSkia resized_image_skia
;
217 for (size_t i
= 0; i
< favicon_scales_to_generate
.size(); ++i
) {
218 float scale
= favicon_scales_to_generate
[i
];
219 int desired_size_in_pixel
=
220 static_cast<int>(std::ceil(favicon_size
* scale
));
222 ResizeBitmapByDownsamplingIfPossible(bitmaps
, desired_size_in_pixel
);
223 resized_image_skia
.AddRepresentation(gfx::ImageSkiaRep(bitmap
, scale
));
226 if (png_reps
.empty())
227 return gfx::Image(resized_image_skia
);
229 std::vector
<gfx::ImageSkiaRep
> resized_image_skia_reps
=
230 resized_image_skia
.image_reps();
231 for (size_t i
= 0; i
< resized_image_skia_reps
.size(); ++i
) {
232 scoped_refptr
<base::RefCountedBytes
> png_bytes(new base::RefCountedBytes());
233 if (gfx::PNGCodec::EncodeBGRASkBitmap(
234 resized_image_skia_reps
[i
].sk_bitmap(),
236 &png_bytes
->data())) {
238 gfx::ImagePNGRep(png_bytes
, resized_image_skia_reps
[i
].scale()));
242 return gfx::Image(png_reps
);
245 } // namespace favicon_base