OLD | NEW |
| (Empty) |
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/suggestions/image_manager.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "components/suggestions/image_fetcher.h" | |
9 #include "ui/gfx/codec/jpeg_codec.h" | |
10 | |
11 using leveldb_proto::ProtoDatabase; | |
12 | |
13 namespace { | |
14 | |
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()); | |
18 } | |
19 | |
20 } // namespace | |
21 | |
22 namespace suggestions { | |
23 | |
24 ImageManager::ImageManager() : weak_ptr_factory_(this) {} | |
25 | |
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())); | |
36 } | |
37 | |
38 ImageManager::~ImageManager() {} | |
39 | |
40 ImageManager::ImageCacheRequest::ImageCacheRequest() {} | |
41 | |
42 ImageManager::ImageCacheRequest::~ImageCacheRequest() {} | |
43 | |
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()); | |
50 } | |
51 } | |
52 } | |
53 | |
54 void ImageManager::GetImageForURL( | |
55 const GURL& url, | |
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|. | |
60 GURL image_url; | |
61 if (!GetImageURL(url, &image_url)) { | |
62 callback.Run(url, NULL); | |
63 return; | |
64 } | |
65 | |
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 | |
69 // cache or network. | |
70 QueueCacheRequest(url, image_url, callback); | |
71 return; | |
72 } | |
73 | |
74 ServeFromCacheOrNetwork(url, image_url, callback); | |
75 } | |
76 | |
77 void ImageManager::OnImageFetched(const GURL& url, const SkBitmap* bitmap) { | |
78 SaveImage(url, *bitmap); | |
79 } | |
80 | |
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; | |
85 return true; | |
86 } | |
87 | |
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; | |
95 request.url = url; | |
96 request.image_url = image_url; | |
97 request.callbacks.push_back(callback); | |
98 pending_cache_requests_[url] = request; | |
99 } else { | |
100 // Request already queued for this url. | |
101 it->second.callbacks.push_back(callback); | |
102 } | |
103 } | |
104 | |
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); | |
111 } | |
112 } | |
113 | |
114 bool ImageManager::ServeFromCache( | |
115 const GURL& url, | |
116 base::Callback<void(const GURL&, const SkBitmap*)> callback) { | |
117 SkBitmap* bitmap = GetBitmapFromCache(url); | |
118 if (bitmap) { | |
119 callback.Run(url, bitmap); | |
120 return true; | |
121 } | |
122 return false; | |
123 } | |
124 | |
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; | |
129 } | |
130 return NULL; | |
131 } | |
132 | |
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)); | |
136 | |
137 if (!database_ready_) return; | |
138 | |
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. | |
144 ImageData data; | |
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())); | |
155 } | |
156 } | |
157 | |
158 void ImageManager::OnDatabaseInit(bool success) { | |
159 if (!success) { | |
160 DVLOG(1) << "Image database init failed."; | |
161 database_.reset(); | |
162 ServePendingCacheRequests(); | |
163 return; | |
164 } | |
165 database_->LoadEntries(base::Bind(&ImageManager::OnDatabaseLoad, | |
166 weak_ptr_factory_.GetWeakPtr())); | |
167 } | |
168 | |
169 void ImageManager::OnDatabaseLoad(bool success, | |
170 scoped_ptr<ImageDataVector> entries) { | |
171 if (!success) { | |
172 DVLOG(1) << "Image database load failed."; | |
173 database_.reset(); | |
174 ServePendingCacheRequests(); | |
175 return; | |
176 } | |
177 database_ready_ = true; | |
178 | |
179 LoadEntriesInCache(entries.Pass()); | |
180 ServePendingCacheRequests(); | |
181 } | |
182 | |
183 void ImageManager::OnDatabaseSave(bool success) { | |
184 if (!success) { | |
185 DVLOG(1) << "Image database save failed."; | |
186 database_.reset(); | |
187 database_ready_ = false; | |
188 } | |
189 } | |
190 | |
191 void ImageManager::LoadEntriesInCache(scoped_ptr<ImageDataVector> entries) { | |
192 for (ImageDataVector::iterator it = entries->begin(); it != entries->end(); | |
193 ++it) { | |
194 std::vector<unsigned char> encoded_data(it->data().begin(), | |
195 it->data().end()); | |
196 | |
197 scoped_ptr<SkBitmap> bitmap(DecodeImage(encoded_data)); | |
198 if (bitmap.get()) { | |
199 image_map_.insert(std::make_pair(it->url(), *bitmap)); | |
200 } | |
201 } | |
202 } | |
203 | |
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); | |
211 } | |
212 } | |
213 } | |
214 | |
215 // static | |
216 bool ImageManager::EncodeImage(const SkBitmap& bitmap, | |
217 std::vector<unsigned char>* dest) { | |
218 SkAutoLockPixels bitmap_lock(bitmap); | |
219 if (!bitmap.readyToDraw() || bitmap.isNull()) { | |
220 return false; | |
221 } | |
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.width() * bitmap.bytesPerPixel(), 100, dest); | |
226 } | |
227 | |
228 } // namespace suggestions | |
OLD | NEW |