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 "chrome/browser/android/thumbnail/thumbnail_cache.h" | |
6 | |
7 #include <algorithm> | |
8 #include <cmath> | |
9 | |
10 #include "base/file_util.h" | |
11 #include "base/files/file.h" | |
12 #include "base/files/file_enumerator.h" | |
13 #include "base/files/file_path.h" | |
14 #include "base/strings/string_number_conversions.h" | |
15 #include "base/time/time.h" | |
16 #include "content/public/browser/android/ui_resource_provider.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "third_party/android_opengl/etc1/etc1.h" | |
19 #include "third_party/skia/include/core/SkBitmap.h" | |
20 #include "third_party/skia/include/core/SkCanvas.h" | |
21 #include "third_party/skia/include/core/SkData.h" | |
22 #include "third_party/skia/include/core/SkMallocPixelRef.h" | |
23 #include "third_party/skia/include/core/SkPixelRef.h" | |
24 #include "ui/gfx/geometry/size_conversions.h" | |
25 | |
26 namespace { | |
27 | |
28 const float kApproximationScaleFactor = 4.f; | |
29 const base::TimeDelta kCaptureMinRequestTimeMs( | |
30 base::TimeDelta::FromMilliseconds(1000)); | |
31 const Thumbnail kInvalidThumbnail; | |
32 | |
33 const int kCompressedKey = 0xABABABAB; | |
34 const int kDecompressedKey = 0xCDCDCDCD; | |
35 | |
36 // ETC1 texture sizes are multiples of four. | |
37 size_t NextETC1Size(size_t s) { | |
38 return (s / 4 + (s % 4 ? 1 : 0)) * 4; | |
39 } | |
40 | |
41 gfx::Size GetEncodedSize(const gfx::Size& bitmap_size) { | |
42 return gfx::Size(NextETC1Size(bitmap_size.width()), | |
43 NextETC1Size(bitmap_size.height())); | |
44 } | |
45 | |
46 } // anonymous namespace | |
47 | |
48 ThumbnailCache::ThumbnailCache(const std::string& disk_cache_path_str, | |
49 size_t default_cache_size, | |
50 size_t approximation_cache_size, | |
51 size_t compression_queue_max_size, | |
52 size_t write_queue_max_size, | |
53 bool use_approximation_thumbnail) | |
54 : disk_cache_path_(disk_cache_path_str), | |
55 compression_queue_max_size_(compression_queue_max_size), | |
56 write_queue_max_size_(write_queue_max_size), | |
57 use_approximation_thumbnail_(use_approximation_thumbnail), | |
58 compression_tasks_count_(0), | |
59 write_tasks_count_(0), | |
60 cache_(default_cache_size), | |
61 approximation_cache_(approximation_cache_size), | |
62 ui_resource_provider_(NULL), | |
63 compression_thread_("thumbnail_compression"), | |
64 weak_factory_(this) { | |
65 compression_thread_.Start(); | |
66 } | |
67 | |
68 ThumbnailCache::~ThumbnailCache() { | |
69 compression_thread_.Stop(); | |
70 SetUIResourceProvider(NULL); | |
71 } | |
72 | |
73 void ThumbnailCache::SetUIResourceProvider( | |
74 content::UIResourceProvider* ui_resource_provider) { | |
75 if (ui_resource_provider_ == ui_resource_provider) | |
76 return; | |
77 | |
78 approximation_cache_.Clear(); | |
79 cache_.Clear(); | |
80 | |
81 ui_resource_provider_ = ui_resource_provider; | |
82 } | |
83 | |
84 void ThumbnailCache::AddThumbnailCacheObserver( | |
85 ThumbnailCacheObserver* observer) { | |
86 if (!observers_.HasObserver(observer)) | |
87 observers_.AddObserver(observer); | |
88 } | |
89 | |
90 void ThumbnailCache::RemoveThumbnailCacheObserver( | |
91 ThumbnailCacheObserver* observer) { | |
92 if (observers_.HasObserver(observer)) | |
93 observers_.RemoveObserver(observer); | |
94 } | |
95 | |
96 void ThumbnailCache::Put(TabId tab_id, | |
97 const SkBitmap& bitmap, | |
98 float thumbnail_scale) { | |
99 if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0) | |
100 return; | |
101 | |
102 DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end()); | |
103 | |
104 base::Time time_stamp = thumbnail_meta_data_[tab_id].capture_time(); | |
105 | |
106 Thumbnail thumbnail( | |
107 tab_id, time_stamp, thumbnail_scale, ui_resource_provider_, this); | |
108 thumbnail.SetBitmap(bitmap); | |
109 | |
110 RemoveFromReadQueue(tab_id); | |
111 MakeSpaceForNewItemIfNecessary(tab_id); | |
112 cache_.Put(tab_id, thumbnail); | |
David Trainor- moved to gerrit
2014/07/14 20:30:46
I'm not a huge fan of using references instead of
powei
2014/07/15 20:06:09
Done. Note that lru_expiring_cache is now scoped_
| |
113 | |
114 if (use_approximation_thumbnail_) { | |
115 std::pair<SkBitmap, float> approximation = | |
116 CreateApproximation(bitmap, thumbnail_scale); | |
117 Thumbnail approx_thumbnail(tab_id, | |
118 time_stamp, | |
119 approximation.second, | |
120 ui_resource_provider_, | |
121 this); | |
122 approx_thumbnail.SetBitmap(approximation.first); | |
123 approximation_cache_.Put(tab_id, approx_thumbnail); | |
124 } | |
125 CompressThumbnailIfNecessary(tab_id, bitmap, thumbnail_scale, time_stamp); | |
126 } | |
127 | |
128 void ThumbnailCache::Remove(TabId tab_id) { | |
129 cache_.Remove(tab_id); | |
130 approximation_cache_.Remove(tab_id); | |
131 thumbnail_meta_data_.erase(tab_id); | |
132 RemoveFromDisk(tab_id); | |
133 RemoveFromReadQueue(tab_id); | |
134 } | |
135 | |
136 const Thumbnail& ThumbnailCache::Get(TabId tab_id) { | |
David Trainor- moved to gerrit
2014/07/14 20:30:46
This feels a bit heavyweight, as in someone has to
powei
2014/07/15 20:06:09
Done.
| |
137 if (cache_.Contains(tab_id)) { | |
138 Thumbnail& thumbnail = cache_.Get(tab_id); | |
139 thumbnail.CreateUIResource(); | |
140 return thumbnail; | |
141 } | |
142 | |
143 if (written_thumbnails_.find(tab_id) != written_thumbnails_.end() && | |
David Trainor- moved to gerrit
2014/07/14 20:30:46
written_thumbnails_ won't be up to date if we rest
powei
2014/07/14 23:56:25
This use to fallback to just checking the disk, bu
powei
2014/07/15 20:06:09
|written_thumbnails_| is a misnomer. It's more li
David Trainor- moved to gerrit
2014/07/15 20:08:24
What kind of regression? Couldn't the read task j
powei
2014/07/18 00:51:49
Currently we ask for the *static page* (i.e. thumb
| |
144 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) != | |
145 visible_ids_.end() && | |
146 std::find(read_queue_.begin(), read_queue_.end(), tab_id) == | |
147 read_queue_.end()) { | |
148 read_queue_.push_back(tab_id); | |
149 ReadNextThumbnail(); | |
150 } | |
151 | |
152 if (approximation_cache_.Contains(tab_id)) { | |
153 Thumbnail& thumbnail = approximation_cache_.Get(tab_id); | |
154 thumbnail.CreateUIResource(); | |
155 return thumbnail; | |
156 } | |
157 | |
158 return kInvalidThumbnail; | |
159 } | |
160 | |
161 void ThumbnailCache::RemoveFromDiskAtAndAboveId(TabId min_id) { | |
162 base::Closure remove_task = | |
163 base::Bind(&ThumbnailCache::RemoveFromDiskAtAndAboveIdTask, | |
164 disk_cache_path_, | |
165 min_id); | |
166 content::BrowserThread::PostTask( | |
167 content::BrowserThread::FILE, FROM_HERE, remove_task); | |
168 } | |
169 | |
170 void ThumbnailCache::InvalidateThumbnailIfChanged(TabId tab_id, | |
171 const GURL& url) { | |
172 ThumbnailMetaDataMap::iterator meta_data_iter = | |
173 thumbnail_meta_data_.find(tab_id); | |
174 if (meta_data_iter == thumbnail_meta_data_.end()) { | |
175 thumbnail_meta_data_[tab_id] = ThumbnailMetaData(base::Time(), url); | |
176 } else if (meta_data_iter->second.url() != url) { | |
177 Remove(tab_id); | |
178 } | |
179 } | |
180 | |
181 bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(TabId tab_id, | |
182 const GURL& url) { | |
183 base::Time current_time = base::Time::Now(); | |
184 ThumbnailMetaDataMap::iterator meta_data_iter = | |
185 thumbnail_meta_data_.find(tab_id); | |
186 if (meta_data_iter != thumbnail_meta_data_.end() && | |
187 meta_data_iter->second.url() == url && | |
188 (current_time - meta_data_iter->second.capture_time()) < | |
189 kCaptureMinRequestTimeMs) { | |
190 return false; | |
191 } | |
192 | |
193 thumbnail_meta_data_[tab_id] = ThumbnailMetaData(current_time, url); | |
194 return true; | |
195 } | |
196 | |
197 void ThumbnailCache::UpdateVisibleIds(const TabIdList& priority) { | |
198 if (priority.empty()) { | |
199 visible_ids_.clear(); | |
200 return; | |
201 } | |
202 | |
203 size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize()); | |
204 if (visible_ids_.size() == ids_size) { | |
205 // Early out if called with the same input as last time (We only care | |
206 // about the first mCache.MaximumCacheSize() entries). | |
207 bool lists_differ = false; | |
208 TabIdList::const_iterator visible_iter = visible_ids_.begin(); | |
209 TabIdList::const_iterator priority_iter = priority.begin(); | |
210 while (visible_iter != visible_ids_.end() && | |
211 priority_iter != priority.end()) { | |
212 if (*priority_iter != *visible_iter) { | |
213 lists_differ = true; | |
214 break; | |
215 } | |
216 visible_iter++; | |
217 priority_iter++; | |
218 } | |
219 | |
220 if (!lists_differ) | |
221 return; | |
222 } | |
223 | |
224 read_queue_.clear(); | |
225 visible_ids_.clear(); | |
226 size_t count = 0; | |
227 TabIdList::const_iterator iter = priority.begin(); | |
228 while (iter != priority.end() && count < ids_size) { | |
229 TabId tab_id = *iter; | |
230 visible_ids_.push_back(tab_id); | |
231 if (!cache_.Contains(tab_id) && | |
232 std::find(read_queue_.begin(), read_queue_.end(), tab_id) == | |
233 read_queue_.end()) { | |
234 read_queue_.push_back(tab_id); | |
235 } | |
236 iter++; | |
237 count++; | |
238 } | |
239 | |
240 ReadNextThumbnail(); | |
241 } | |
242 | |
243 void ThumbnailCache::RemoveFromDisk(TabId tab_id) { | |
244 if (written_thumbnails_.find(tab_id) == written_thumbnails_.end()) | |
245 return; | |
246 | |
247 written_thumbnails_.erase(tab_id); | |
248 | |
249 base::FilePath file_path = GetFilePath(tab_id); | |
250 base::Closure task = | |
251 base::Bind(&ThumbnailCache::RemoveFromDiskTask, file_path); | |
252 content::BrowserThread::PostTask( | |
253 content::BrowserThread::FILE, FROM_HERE, task); | |
254 } | |
255 | |
256 void ThumbnailCache::RemoveFromDiskTask(const base::FilePath& file_path) { | |
257 DCHECK(base::PathExists(file_path)); | |
258 base::DeleteFile(file_path, false); | |
259 } | |
260 | |
261 void ThumbnailCache::RemoveFromDiskAtAndAboveIdTask( | |
262 const base::FilePath& dir_path, | |
263 TabId min_id) { | |
264 base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES); | |
265 while (true) { | |
266 base::FilePath path = enumerator.Next(); | |
267 if (path.empty()) | |
268 break; | |
269 base::FileEnumerator::FileInfo info = enumerator.GetInfo(); | |
270 TabId tab_id; | |
271 bool success = base::StringToInt(info.GetName().value(), &tab_id); | |
272 if (success && tab_id >= min_id) | |
273 base::DeleteFile(path, false); | |
274 } | |
275 } | |
276 | |
277 void ThumbnailCache::WriteThumbnailIfNecessary( | |
278 TabId tab_id, | |
279 skia::RefPtr<SkPixelRef> compressed_data, | |
280 float scale, | |
281 const gfx::Size& content_size) { | |
282 if (write_tasks_count_ >= write_queue_max_size_) | |
David Trainor- moved to gerrit
2014/07/14 20:30:46
What do we do if we're over this limit? Do we jus
powei
2014/07/14 23:56:25
Yes, it is just never written. Maybe we'll need t
powei
2014/07/15 20:06:09
Actually, I think we can live with not writing it
David Trainor- moved to gerrit
2014/07/15 20:08:23
Do these max write queue/compress queue checks do
powei
2014/07/18 00:51:49
I did some manual testing. We do hit the compress
David Trainor- moved to gerrit
2014/07/18 17:03:59
Oh interesting... if we're peaked out in the compr
| |
283 return; | |
284 | |
285 write_tasks_count_++; | |
286 | |
287 scoped_refptr<WriteTaskResult> result = | |
288 make_scoped_refptr(new WriteTaskResult()); | |
289 base::Closure write_task = base::Bind(&ThumbnailCache::WriteTask, | |
290 GetFilePath(tab_id), | |
291 compressed_data, | |
David Trainor- moved to gerrit
2014/07/14 20:30:46
Huh is skia::RefPtr<> thread safe? I just realize
powei
2014/07/14 23:56:25
SkRefCnt does use atomic operations and barrier fo
David Trainor- moved to gerrit
2014/07/18 17:03:59
w00t
| |
292 scale, | |
293 content_size, | |
294 result); | |
295 base::Closure post_write_task = base::Bind(&ThumbnailCache::PostWriteTask, | |
296 weak_factory_.GetWeakPtr(), | |
297 tab_id, | |
298 result); | |
299 | |
300 content::BrowserThread::PostTaskAndReply( | |
301 content::BrowserThread::FILE, FROM_HERE, write_task, post_write_task); | |
302 } | |
303 | |
304 void ThumbnailCache::CompressThumbnailIfNecessary(TabId tab_id, | |
305 const SkBitmap& bitmap, | |
306 float scale, | |
307 base::Time& time_stamp) { | |
308 if (compression_tasks_count_ >= compression_queue_max_size_) | |
David Trainor- moved to gerrit
2014/07/14 20:30:46
What do we do if we're over this limit? If we jus
powei
2014/07/14 23:56:25
Yeah, maybe we'll need to drop if the approximated
powei
2014/07/15 20:06:09
Done. Added line to remove the cached version.
David Trainor- moved to gerrit
2014/07/15 20:08:23
Yeah that makes sense :)
| |
309 return; | |
310 | |
311 compression_tasks_count_++; | |
312 | |
313 scoped_refptr<CompressedDataTransport> transport = | |
314 make_scoped_refptr(new CompressedDataTransport()); | |
315 base::Closure compression_task = | |
316 base::Bind(&ThumbnailCache::CompressionTask, bitmap, transport); | |
317 base::Closure post_compression_task = | |
318 base::Bind(&ThumbnailCache::PostCompressionTask, | |
319 weak_factory_.GetWeakPtr(), | |
320 tab_id, | |
321 scale, | |
322 time_stamp, | |
323 transport); | |
David Trainor- moved to gerrit
2014/07/14 20:30:46
Is this the only way to do this? Do you need to k
powei
2014/07/14 23:56:25
Closure should have no unbound parameters (https:/
David Trainor- moved to gerrit
2014/07/15 20:08:24
Do you have to use PostTaskAndReply? Would it be
powei
2014/07/18 00:51:49
Done. Pass the Post*Task() as callback params to
| |
324 | |
325 DCHECK(compression_thread_.message_loop_proxy()); | |
326 compression_thread_.message_loop_proxy()->PostTaskAndReply( | |
327 FROM_HERE, compression_task, post_compression_task); | |
328 } | |
329 | |
330 void ThumbnailCache::ReadNextThumbnail() { | |
331 if (read_queue_.empty()) | |
David Trainor- moved to gerrit
2014/07/15 20:08:24
I'm still a bit confused by this queue :(. It mig
powei
2014/07/18 00:51:49
I added a boolean to indicate that a read is curre
| |
332 return; | |
333 | |
334 TabId tab_id = read_queue_.front(); | |
335 | |
336 scoped_refptr<CompressedDataTransport> transport = | |
337 make_scoped_refptr(new CompressedDataTransport()); | |
338 base::FilePath file_path = GetFilePath(tab_id); | |
339 base::Closure read_task = | |
340 base::Bind(&ThumbnailCache::ReadTask, file_path, transport); | |
341 base::Closure post_read_task = base::Bind(&ThumbnailCache::PostReadTask, | |
342 weak_factory_.GetWeakPtr(), | |
343 tab_id, | |
344 transport); | |
345 | |
346 content::BrowserThread::PostTaskAndReply( | |
347 content::BrowserThread::FILE, FROM_HERE, read_task, post_read_task); | |
348 } | |
349 | |
350 void ThumbnailCache::MakeSpaceForNewItemIfNecessary(TabId tab_id) { | |
351 if (cache_.Contains(tab_id) || | |
352 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) == | |
353 visible_ids_.end() || | |
354 cache_.size() < cache_.MaximumCacheSize()) { | |
355 return; | |
356 } | |
357 | |
358 TabId key_to_remove; | |
359 bool found_key_to_remove = false; | |
360 | |
361 // 1. Find a cached item not in this list | |
362 for (ExpiringThumbnailCache::iterator iter = cache_.begin(); | |
363 iter != cache_.end(); | |
364 iter++) { | |
365 if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) == | |
366 visible_ids_.end()) { | |
367 key_to_remove = iter->first; | |
368 found_key_to_remove = true; | |
369 break; | |
370 } | |
371 } | |
372 | |
373 if (!found_key_to_remove) { | |
374 // 2. Find the least important id we can remove. | |
375 for (TabIdList::reverse_iterator riter = visible_ids_.rbegin(); | |
376 riter != visible_ids_.rend(); | |
377 riter++) { | |
378 if (cache_.Contains(*riter)) { | |
379 key_to_remove = *riter; | |
380 break; | |
381 found_key_to_remove = true; | |
382 } | |
383 } | |
384 } | |
385 | |
386 if (found_key_to_remove) | |
387 cache_.Remove(key_to_remove); | |
388 } | |
389 | |
390 void ThumbnailCache::RemoveFromReadQueue(TabId tab_id) { | |
391 TabIdList::iterator read_iter = | |
392 std::find(read_queue_.begin(), read_queue_.end(), tab_id); | |
393 if (read_iter != read_queue_.end()) | |
394 read_queue_.erase(read_iter); | |
395 } | |
396 | |
397 void ThumbnailCache::InvalidateCachedThumbnail(const Thumbnail& thumbnail) { | |
398 TabId tab_id = thumbnail.tab_id(); | |
399 base::Time time_stamp = thumbnail.time_stamp(); | |
400 | |
401 if (cache_.Contains(tab_id) && cache_.Get(tab_id).time_stamp() == time_stamp) | |
402 cache_.Remove(tab_id); | |
403 | |
404 if (approximation_cache_.Contains(tab_id) && | |
405 approximation_cache_.Get(tab_id).time_stamp() == time_stamp) | |
406 approximation_cache_.Remove(tab_id); | |
407 } | |
408 | |
409 base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) const { | |
410 return disk_cache_path_.Append(base::IntToString(tab_id)); | |
411 } | |
412 | |
413 void ThumbnailCache::WriteTask( | |
414 const base::FilePath& file_path, | |
415 skia::RefPtr<SkPixelRef> compressed_data, | |
416 float scale, | |
417 const gfx::Size& content_size, | |
418 scoped_refptr<ThumbnailCache::WriteTaskResult> result) { | |
419 DCHECK(compressed_data); | |
420 DCHECK(result); | |
421 | |
422 base::File file(file_path, | |
423 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | |
424 DCHECK(file.IsValid()); | |
425 | |
426 compressed_data->lockPixels(); | |
427 bool success = true; | |
428 int content_width = content_size.width(); | |
429 int content_height = content_size.height(); | |
430 int data_width = compressed_data->info().width(); | |
431 int data_height = compressed_data->info().height(); | |
432 | |
433 if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey), | |
434 sizeof(int)) < 0 || | |
435 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_width), | |
436 sizeof(int)) < 0 || | |
437 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_height), | |
438 sizeof(int)) < 0 || | |
439 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width), | |
440 sizeof(int)) < 0 || | |
441 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height), | |
442 sizeof(int)) < 0 || | |
443 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&scale), | |
444 sizeof(float)) < 0) { | |
445 success = false; | |
446 } | |
447 | |
448 size_t compressed_bytes = etc1_get_encoded_data_size(data_width, data_height); | |
449 if (file.WriteAtCurrentPos(reinterpret_cast<char*>(compressed_data->pixels()), | |
450 compressed_bytes) < 0) | |
451 success = false; | |
452 | |
453 compressed_data->unlockPixels(); | |
454 | |
455 if (!success) | |
456 base::DeleteFile(file_path, false); | |
457 | |
458 file.Close(); | |
David Trainor- moved to gerrit
2014/07/15 20:08:23
Do you have to close this before you delete the fi
powei
2014/07/18 00:51:49
Done. Moved close to before delete.
| |
459 | |
460 result->SetSuccess(success); | |
461 } | |
462 | |
463 void ThumbnailCache::PostWriteTask( | |
464 TabId tab_id, | |
465 scoped_refptr<ThumbnailCache::WriteTaskResult> result) { | |
466 write_tasks_count_--; | |
467 if (result->success()) | |
468 written_thumbnails_.insert(tab_id); | |
469 } | |
470 | |
471 void ThumbnailCache::CompressionTask( | |
472 SkBitmap raw_data, | |
473 scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) { | |
474 if (raw_data.empty()) | |
475 return; | |
476 | |
477 SkAutoLockPixels raw_data_lock(raw_data); | |
478 gfx::Size raw_data_size(raw_data.width(), raw_data.height()); | |
479 DCHECK_EQ(raw_data.config(), SkBitmap::kARGB_8888_Config); | |
480 size_t pixel_size = 4; // Pixel size is 4 bytes for kARGB_8888_Config. | |
481 size_t stride = pixel_size * raw_data_size.width(); | |
482 | |
483 gfx::Size encoded_size = GetEncodedSize(raw_data_size); | |
484 size_t encoded_bytes = | |
485 etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height()); | |
486 SkImageInfo info = {encoded_size.width(), | |
487 encoded_size.height(), | |
488 kUnknown_SkColorType, | |
489 kUnpremul_SkAlphaType}; | |
490 skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef( | |
491 SkData::NewFromMalloc(new uint8_t[encoded_bytes], encoded_bytes)); | |
492 skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef( | |
493 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); | |
494 | |
495 etc1_pixel_ref->lockPixels(); | |
496 bool success = etc1_encode_image( | |
497 reinterpret_cast<unsigned char*>(raw_data.getPixels()), | |
498 raw_data_size.width(), | |
499 raw_data_size.height(), | |
500 pixel_size, | |
501 stride, | |
502 reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()), | |
503 encoded_size.width(), | |
504 encoded_size.height()); | |
505 etc1_pixel_ref->setImmutable(); | |
506 etc1_pixel_ref->unlockPixels(); | |
507 if (success) { | |
508 transport->compressed_data = etc1_pixel_ref; | |
509 transport->content_size = raw_data_size; | |
510 } | |
511 } | |
512 | |
513 void ThumbnailCache::PostCompressionTask( | |
514 TabId tab_id, | |
515 float scale, | |
516 const base::Time& time_stamp, | |
517 scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) { | |
518 DCHECK(transport); | |
519 compression_tasks_count_--; | |
520 | |
521 if (!transport->compressed_data) | |
522 return; | |
David Trainor- moved to gerrit
2014/07/15 20:08:24
Do we need to clean anything up here?
powei
2014/07/18 00:51:49
Done.
| |
523 | |
524 if (cache_.Contains(tab_id)) { | |
525 Thumbnail& thumbnail = cache_.Get(tab_id); | |
526 if (thumbnail.time_stamp() != time_stamp) | |
527 return; | |
528 thumbnail.SetCompressedBitmap(transport->compressed_data, | |
529 transport->content_size); | |
530 thumbnail.CreateUIResource(); | |
531 } | |
532 WriteThumbnailIfNecessary( | |
533 tab_id, transport->compressed_data, scale, transport->content_size); | |
534 } | |
535 | |
536 void ThumbnailCache::ReadTask( | |
537 const base::FilePath& file_path, | |
538 scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) { | |
539 DCHECK(transport); | |
540 if (!base::PathExists(file_path)) | |
541 return; | |
542 | |
543 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); | |
544 DCHECK(file.IsValid()); | |
545 | |
546 int key; | |
547 bool success = true; | |
548 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 || | |
549 key != kCompressedKey) | |
550 success = false; | |
551 | |
552 int width, height; | |
553 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < 0 || | |
554 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < 0) | |
555 success = false; | |
556 gfx::Size content_size(width, height); | |
557 | |
558 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < 0 || | |
559 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < 0) | |
560 success = false; | |
561 gfx::Size data_size(width, height); | |
562 | |
563 float scale = 0.f; | |
564 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale), sizeof(float)) < 0) | |
565 success = false; | |
566 | |
567 size_t compressed_bytes = | |
568 etc1_get_encoded_data_size(data_size.width(), data_size.height()); | |
569 | |
570 SkImageInfo info = {data_size.width(), | |
571 data_size.height(), | |
572 kUnknown_SkColorType, | |
573 kUnpremul_SkAlphaType}; | |
574 | |
575 scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]); | |
576 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()), | |
577 compressed_bytes) < 0) | |
578 success = false; | |
579 | |
580 skia::RefPtr<SkData> etc1_pixel_data = | |
581 skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); | |
582 skia::RefPtr<SkPixelRef> compressed_data = skia::AdoptRef( | |
583 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); | |
584 | |
585 if (success) { | |
586 transport->compressed_data = compressed_data; | |
587 transport->scale = scale; | |
588 transport->content_size = content_size; | |
589 } else { | |
590 base::DeleteFile(file_path, false); | |
591 } | |
592 | |
593 file.Close(); | |
594 } | |
595 | |
596 void ThumbnailCache::PostReadTask( | |
597 TabId tab_id, | |
598 scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) { | |
599 TabIdList::iterator iter = | |
600 std::find(read_queue_.begin(), read_queue_.end(), tab_id); | |
601 if (iter == read_queue_.end()) | |
602 return; | |
603 | |
604 read_queue_.erase(iter); | |
605 | |
606 if (!cache_.Contains(tab_id) && transport->compressed_data) { | |
607 DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end()); | |
608 | |
609 MakeSpaceForNewItemIfNecessary(tab_id); | |
610 Thumbnail thumbnail(tab_id, | |
611 thumbnail_meta_data_[tab_id].capture_time(), | |
612 transport->scale, | |
613 ui_resource_provider_, | |
614 this); | |
615 thumbnail.SetCompressedBitmap(transport->compressed_data, | |
616 transport->content_size); | |
617 cache_.Put(tab_id, thumbnail); | |
David Trainor- moved to gerrit
2014/07/14 20:30:46
thumbnail.CreateUIResource?
powei
2014/07/14 23:56:25
Just to make sure. This means we prefer cpu mem o
powei
2014/07/15 20:06:09
Done.
David Trainor- moved to gerrit
2014/07/15 20:08:23
Yes. At least when it was in Dalvik that was the
powei
2014/07/22 18:16:19
Done. Forgot the flag in the last patch. Now add
| |
618 NotifyObserversOfThumbnailRead(tab_id); | |
619 } | |
620 | |
621 ReadNextThumbnail(); | |
622 } | |
623 | |
624 void ThumbnailCache::NotifyObserversOfThumbnailRead(TabId tab_id) { | |
625 FOR_EACH_OBSERVER( | |
626 ThumbnailCacheObserver, observers_, OnFinishedThumbnailRead(tab_id)); | |
627 } | |
628 | |
629 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData() { | |
630 } | |
631 | |
632 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData( | |
633 const base::Time& current_time, | |
634 const GURL& url) | |
635 : capture_time_(current_time), url_(url) { | |
636 } | |
637 | |
638 std::pair<SkBitmap, float> ThumbnailCache::CreateApproximation( | |
639 const SkBitmap& bitmap, | |
640 float scale) { | |
641 DCHECK(!bitmap.empty()); | |
642 DCHECK_GT(scale, 0); | |
643 SkAutoLockPixels bitmap_lock(bitmap); | |
644 float new_scale = 1.f / kApproximationScaleFactor; | |
645 | |
646 gfx::Size dst_size = gfx::ToFlooredSize( | |
647 gfx::ScaleSize(gfx::Size(bitmap.width(), bitmap.height()), new_scale)); | |
648 SkBitmap dst_bitmap; | |
649 dst_bitmap.setConfig(bitmap.getConfig(), dst_size.width(), dst_size.height()); | |
650 dst_bitmap.allocPixels(); | |
651 dst_bitmap.eraseColor(0); | |
652 SkAutoLockPixels dst_bitmap_lock(dst_bitmap); | |
653 | |
654 SkCanvas canvas(dst_bitmap); | |
655 canvas.scale(new_scale, new_scale); | |
656 canvas.drawBitmap(bitmap, 0, 0, NULL); | |
657 dst_bitmap.setImmutable(); | |
658 | |
659 return std::make_pair(dst_bitmap, new_scale * scale); | |
660 } | |
OLD | NEW |