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/suggestions/image_manager.h"
8 #include "components/suggestions/image_fetcher.h"
9 #include "ui/gfx/codec/jpeg_codec.h"
11 using leveldb_proto::ProtoDatabase
;
15 // From JPEG-encoded bytes to SkBitmap.
16 SkBitmap
* DecodeImage(const std::vector
<unsigned char>& encoded_data
) {
17 return gfx::JPEGCodec::Decode(&encoded_data
[0], encoded_data
.size());
22 namespace suggestions
{
24 ImageManager::ImageManager() : weak_ptr_factory_(this) {}
26 ImageManager::ImageManager(scoped_ptr
<ImageFetcher
> image_fetcher
,
27 scoped_ptr
<ProtoDatabase
<ImageData
> > database
,
28 const base::FilePath
& database_dir
)
29 : image_fetcher_(image_fetcher
.Pass()),
30 database_(database
.Pass()),
31 database_ready_(false),
32 weak_ptr_factory_(this) {
33 image_fetcher_
->SetImageFetcherDelegate(this);
34 database_
->Init(database_dir
, base::Bind(&ImageManager::OnDatabaseInit
,
35 weak_ptr_factory_
.GetWeakPtr()));
38 ImageManager::~ImageManager() {}
40 ImageManager::ImageCacheRequest::ImageCacheRequest() {}
42 ImageManager::ImageCacheRequest::~ImageCacheRequest() {}
44 void ImageManager::Initialize(const SuggestionsProfile
& suggestions
) {
45 image_url_map_
.clear();
46 for (int i
= 0; i
< suggestions
.suggestions_size(); ++i
) {
47 const ChromeSuggestion
& suggestion
= suggestions
.suggestions(i
);
48 if (suggestion
.has_thumbnail()) {
49 image_url_map_
[GURL(suggestion
.url())] = GURL(suggestion
.thumbnail());
54 void ImageManager::GetImageForURL(
56 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
57 DCHECK(thread_checker_
.CalledOnValidThread());
58 // If |url| is not found in |image_url_map_|, then invoke |callback| with
59 // NULL since there is no associated image for this |url|.
61 if (!GetImageURL(url
, &image_url
)) {
62 callback
.Run(url
, NULL
);
66 // |database_| can be NULL if something went wrong in initialization.
67 if (database_
.get() && !database_ready_
) {
68 // Once database is initialized, it will serve pending requests from either
70 QueueCacheRequest(url
, image_url
, callback
);
74 ServeFromCacheOrNetwork(url
, image_url
, callback
);
77 void ImageManager::OnImageFetched(const GURL
& url
, const SkBitmap
* bitmap
) {
78 SaveImage(url
, *bitmap
);
81 bool ImageManager::GetImageURL(const GURL
& url
, GURL
* image_url
) {
82 std::map
<GURL
, GURL
>::iterator it
= image_url_map_
.find(url
);
83 if (it
== image_url_map_
.end()) return false; // Not found.
84 *image_url
= it
->second
;
88 void ImageManager::QueueCacheRequest(
89 const GURL
& url
, const GURL
& image_url
,
90 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
91 // To be served when the database has loaded.
92 ImageCacheRequestMap::iterator it
= pending_cache_requests_
.find(url
);
93 if (it
== pending_cache_requests_
.end()) {
94 ImageCacheRequest request
;
96 request
.image_url
= image_url
;
97 request
.callbacks
.push_back(callback
);
98 pending_cache_requests_
[url
] = request
;
100 // Request already queued for this url.
101 it
->second
.callbacks
.push_back(callback
);
105 void ImageManager::ServeFromCacheOrNetwork(
106 const GURL
& url
, const GURL
& image_url
,
107 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
108 // If there is a image available in memory, return it.
109 if (!ServeFromCache(url
, callback
)) {
110 image_fetcher_
->StartOrQueueNetworkRequest(url
, image_url
, callback
);
114 bool ImageManager::ServeFromCache(
116 base::Callback
<void(const GURL
&, const SkBitmap
*)> callback
) {
117 SkBitmap
* bitmap
= GetBitmapFromCache(url
);
119 callback
.Run(url
, bitmap
);
125 SkBitmap
* ImageManager::GetBitmapFromCache(const GURL
& url
) {
126 ImageMap::iterator image_iter
= image_map_
.find(url
.spec());
127 if (image_iter
!= image_map_
.end()) {
128 return &image_iter
->second
;
133 void ImageManager::SaveImage(const GURL
& url
, const SkBitmap
& bitmap
) {
134 // Update the image map.
135 image_map_
.insert(std::make_pair(url
.spec(), bitmap
));
137 if (!database_ready_
) return;
139 // Attempt to save a JPEG representation to the database. If not successful,
140 // the fetched bitmap will still be inserted in the cache, above.
141 std::vector
<unsigned char> encoded_data
;
142 if (EncodeImage(bitmap
, &encoded_data
)) {
143 // Save the resulting bitmap to the database.
145 data
.set_url(url
.spec());
146 data
.set_data(std::string(encoded_data
.begin(), encoded_data
.end()));
147 scoped_ptr
<ProtoDatabase
<ImageData
>::KeyEntryVector
> entries_to_save(
148 new ProtoDatabase
<ImageData
>::KeyEntryVector());
149 scoped_ptr
<std::vector
<std::string
> > keys_to_remove(
150 new std::vector
<std::string
>());
151 entries_to_save
->push_back(std::make_pair(data
.url(), data
));
152 database_
->UpdateEntries(entries_to_save
.Pass(), keys_to_remove
.Pass(),
153 base::Bind(&ImageManager::OnDatabaseSave
,
154 weak_ptr_factory_
.GetWeakPtr()));
158 void ImageManager::OnDatabaseInit(bool success
) {
160 DVLOG(1) << "Image database init failed.";
162 ServePendingCacheRequests();
165 database_
->LoadEntries(base::Bind(&ImageManager::OnDatabaseLoad
,
166 weak_ptr_factory_
.GetWeakPtr()));
169 void ImageManager::OnDatabaseLoad(bool success
,
170 scoped_ptr
<ImageDataVector
> entries
) {
172 DVLOG(1) << "Image database load failed.";
174 ServePendingCacheRequests();
177 database_ready_
= true;
179 LoadEntriesInCache(entries
.Pass());
180 ServePendingCacheRequests();
183 void ImageManager::OnDatabaseSave(bool success
) {
185 DVLOG(1) << "Image database save failed.";
187 database_ready_
= false;
191 void ImageManager::LoadEntriesInCache(scoped_ptr
<ImageDataVector
> entries
) {
192 for (ImageDataVector::iterator it
= entries
->begin(); it
!= entries
->end();
194 std::vector
<unsigned char> encoded_data(it
->data().begin(),
197 scoped_ptr
<SkBitmap
> bitmap(DecodeImage(encoded_data
));
199 image_map_
.insert(std::make_pair(it
->url(), *bitmap
));
204 void ImageManager::ServePendingCacheRequests() {
205 for (ImageCacheRequestMap::iterator it
= pending_cache_requests_
.begin();
206 it
!= pending_cache_requests_
.end(); ++it
) {
207 const ImageCacheRequest
& request
= it
->second
;
208 for (CallbackVector::const_iterator callback_it
= request
.callbacks
.begin();
209 callback_it
!= request
.callbacks
.end(); ++callback_it
) {
210 ServeFromCacheOrNetwork(request
.url
, request
.image_url
, *callback_it
);
216 bool ImageManager::EncodeImage(const SkBitmap
& bitmap
,
217 std::vector
<unsigned char>* dest
) {
218 SkAutoLockPixels
bitmap_lock(bitmap
);
219 if (!bitmap
.readyToDraw() || bitmap
.isNull()) {
222 return gfx::JPEGCodec::Encode(
223 reinterpret_cast<unsigned char*>(bitmap
.getAddr32(0, 0)),
224 gfx::JPEGCodec::FORMAT_SkBitmap
, bitmap
.width(), bitmap
.height(),
225 bitmap
.rowBytes(), 100, dest
);
228 } // namespace suggestions