1 // Copyright 2015 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/core/large_icon_service.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/task_runner.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "components/favicon/core/favicon_service.h"
15 #include "components/favicon_base/fallback_icon_style.h"
16 #include "components/favicon_base/favicon_types.h"
17 #include "skia/ext/image_operations.h"
18 #include "ui/gfx/codec/png_codec.h"
19 #include "ui/gfx/geometry/size.h"
23 // Processes the bitmap data returned from the FaviconService as part of a
24 // LargeIconService request.
25 class LargeIconWorker
: public base::RefCountedThreadSafe
<LargeIconWorker
> {
27 LargeIconWorker(int min_source_size_in_pixel
,
28 int desired_size_in_pixel
,
29 favicon_base::LargeIconCallback callback
,
30 scoped_refptr
<base::TaskRunner
> background_task_runner
,
31 base::CancelableTaskTracker
* tracker
);
33 // Must run on the owner (UI) thread in production.
34 // Intermediate callback for GetLargeIconOrFallbackStyle(). Invokes
35 // ProcessIconOnBackgroundThread() so we do not perform complex image
36 // operations on the UI thread.
37 void OnIconLookupComplete(
38 const favicon_base::FaviconRawBitmapResult
& bitmap_result
);
41 friend class base::RefCountedThreadSafe
<LargeIconWorker
>;
45 // Must run on a background thread in production.
46 // Tries to resize |bitmap_result_| and pass the output to |callback_|. If
47 // that does not work, computes the icon fallback style and uses it to
48 // invoke |callback_|. This must be run on a background thread because image
49 // resizing and dominant color extraction can be expensive.
50 void ProcessIconOnBackgroundThread();
52 // Must run on a background thread in production.
53 // If |bitmap_result_| is square and large enough (>= |min_source_in_pixel_|),
54 // resizes it to |desired_size_in_pixel_| (but if |desired_size_in_pixel_| is
55 // 0 then don't resize). If successful, stores the resulting bitmap data
56 // into |resized_bitmap_result| and returns true.
57 bool ResizeLargeIconOnBackgroundThreadIfValid(
58 favicon_base::FaviconRawBitmapResult
* resized_bitmap_result
);
60 // Must run on the owner (UI) thread in production.
61 // Invoked when ProcessIconOnBackgroundThread() is done.
62 void OnIconProcessingComplete();
64 int min_source_size_in_pixel_
;
65 int desired_size_in_pixel_
;
66 favicon_base::LargeIconCallback callback_
;
67 scoped_refptr
<base::TaskRunner
> background_task_runner_
;
68 base::CancelableTaskTracker
* tracker_
;
69 favicon_base::FaviconRawBitmapResult bitmap_result_
;
70 scoped_ptr
<favicon_base::LargeIconResult
> result_
;
72 DISALLOW_COPY_AND_ASSIGN(LargeIconWorker
);
75 LargeIconWorker::LargeIconWorker(
76 int min_source_size_in_pixel
,
77 int desired_size_in_pixel
,
78 favicon_base::LargeIconCallback callback
,
79 scoped_refptr
<base::TaskRunner
> background_task_runner
,
80 base::CancelableTaskTracker
* tracker
)
81 : min_source_size_in_pixel_(min_source_size_in_pixel
),
82 desired_size_in_pixel_(desired_size_in_pixel
),
84 background_task_runner_(background_task_runner
),
88 LargeIconWorker::~LargeIconWorker() {
91 void LargeIconWorker::OnIconLookupComplete(
92 const favicon_base::FaviconRawBitmapResult
& bitmap_result
) {
93 bitmap_result_
= bitmap_result
;
94 tracker_
->PostTaskAndReply(
95 background_task_runner_
.get(), FROM_HERE
,
96 base::Bind(&LargeIconWorker::ProcessIconOnBackgroundThread
, this),
97 base::Bind(&LargeIconWorker::OnIconProcessingComplete
, this));
100 void LargeIconWorker::ProcessIconOnBackgroundThread() {
101 favicon_base::FaviconRawBitmapResult resized_bitmap_result
;
102 if (ResizeLargeIconOnBackgroundThreadIfValid(&resized_bitmap_result
)) {
104 new favicon_base::LargeIconResult(resized_bitmap_result
));
106 // Failed to resize |bitmap_result_|, so compute fallback icon style.
107 scoped_ptr
<favicon_base::FallbackIconStyle
> fallback_icon_style(
108 new favicon_base::FallbackIconStyle());
109 if (bitmap_result_
.is_valid()) {
110 favicon_base::SetDominantColorAsBackground(
111 bitmap_result_
.bitmap_data
, fallback_icon_style
.get());
114 new favicon_base::LargeIconResult(fallback_icon_style
.release()));
118 bool LargeIconWorker::ResizeLargeIconOnBackgroundThreadIfValid(
119 favicon_base::FaviconRawBitmapResult
* resized_bitmap_result
) {
120 // Require bitmap to be valid and square.
121 if (!bitmap_result_
.is_valid() ||
122 bitmap_result_
.pixel_size
.width() != bitmap_result_
.pixel_size
.height())
125 // Require bitmap to be large enough. It's square, so just check width.
126 if (bitmap_result_
.pixel_size
.width() < min_source_size_in_pixel_
)
129 *resized_bitmap_result
= bitmap_result_
;
131 // Special case: Can use |bitmap_result_| as is.
132 if (desired_size_in_pixel_
== 0 ||
133 bitmap_result_
.pixel_size
.width() == desired_size_in_pixel_
)
136 // Resize bitmap: decode PNG, resize, and re-encode PNG.
137 SkBitmap decoded_bitmap
;
138 if (!gfx::PNGCodec::Decode(bitmap_result_
.bitmap_data
->front(),
139 bitmap_result_
.bitmap_data
->size(), &decoded_bitmap
))
142 SkBitmap resized_bitmap
= skia::ImageOperations::Resize(
143 decoded_bitmap
, skia::ImageOperations::RESIZE_LANCZOS3
,
144 desired_size_in_pixel_
, desired_size_in_pixel_
);
146 std::vector
<unsigned char> bitmap_data
;
147 if (!gfx::PNGCodec::EncodeBGRASkBitmap(resized_bitmap
, false, &bitmap_data
))
150 resized_bitmap_result
->pixel_size
=
151 gfx::Size(desired_size_in_pixel_
, desired_size_in_pixel_
);
152 resized_bitmap_result
->bitmap_data
=
153 base::RefCountedBytes::TakeVector(&bitmap_data
);
157 void LargeIconWorker::OnIconProcessingComplete() {
158 callback_
.Run(*result_
);
165 LargeIconService::LargeIconService(
166 FaviconService
* favicon_service
,
167 const scoped_refptr
<base::TaskRunner
>& background_task_runner
)
168 : favicon_service_(favicon_service
),
169 background_task_runner_(background_task_runner
) {
170 large_icon_types_
.push_back(favicon_base::IconType::FAVICON
);
171 large_icon_types_
.push_back(favicon_base::IconType::TOUCH_ICON
);
172 large_icon_types_
.push_back(favicon_base::IconType::TOUCH_PRECOMPOSED_ICON
);
175 LargeIconService::~LargeIconService() {
178 base::CancelableTaskTracker::TaskId
179 LargeIconService::GetLargeIconOrFallbackStyle(
180 const GURL
& page_url
,
181 int min_source_size_in_pixel
,
182 int desired_size_in_pixel
,
183 const favicon_base::LargeIconCallback
& callback
,
184 base::CancelableTaskTracker
* tracker
) {
185 DCHECK_LE(1, min_source_size_in_pixel
);
186 DCHECK_LE(0, desired_size_in_pixel
);
188 scoped_refptr
<LargeIconWorker
> worker
=
189 new LargeIconWorker(min_source_size_in_pixel
, desired_size_in_pixel
,
190 callback
, background_task_runner_
, tracker
);
192 // TODO(beaudoin): For now this is just a wrapper around
193 // GetLargestRawFaviconForPageURL. Add the logic required to select the best
194 // possible large icon. Also add logic to fetch-on-demand when the URL of
195 // a large icon is known but its bitmap is not available.
196 return favicon_service_
->GetLargestRawFaviconForPageURL(
197 page_url
, large_icon_types_
, min_source_size_in_pixel
,
198 base::Bind(&LargeIconWorker::OnIconLookupComplete
, worker
),
202 } // namespace favicon