Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1040)

Side by Side Diff: chrome/browser/android/thumbnail_cache.cc

Issue 262543002: android: add ThumbnailCache (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@codec
Patch Set: renamed ThumbnailCacheCore to ThumbnailCache Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_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 "chrome/browser/android/tab_thumbnail_provider.h"
17 #include "content/public/browser/android/content_view_core.h"
18 #include "content/public/browser/android/ui_resource_provider.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/render_widget_host_view.h"
22 #include "content/public/browser/web_contents.h"
23 #include "skia/ext/refptr.h"
24 #include "third_party/android_opengl/etc1/etc1.h"
25 #include "third_party/skia/include/core/SkBitmap.h"
26 #include "third_party/skia/include/core/SkCanvas.h"
27 #include "third_party/skia/include/core/SkData.h"
28 #include "third_party/skia/include/core/SkMallocPixelRef.h"
29 #include "third_party/skia/include/core/SkPixelRef.h"
30 #include "ui/gfx/geometry/size_conversions.h"
31
32 namespace {
33
34 const size_t kMaxReadbacks = 1;
35 const bool kDropCachedNTPOnLowMemory = true;
36 const float kApproximationScaleFactor = 4.f;
37 const bool kEnableCompression = true;
38 const base::TimeDelta kCaptureMinRequestTimeMs(
39 base::TimeDelta::FromMilliseconds(1000));
40 const int kCompressedKey = 0xABABABAB;
41 const int kDecompressedKey = 0xCDCDCDCD;
42
43 // ETC1 texture sizes are multiples of four.
44 size_t NextETC1Size(size_t s) {
45 return (s / 4 + (s % 4 ? 1 : 0)) * 4;
46 }
47
48 gfx::Size GetEncodedSize(gfx::Size bitmap_size) {
49 return gfx::Size(NextETC1Size(bitmap_size.width()),
50 NextETC1Size(bitmap_size.height()));
51 }
52
53 } // anonymous namespace
54
55 ThumbnailCache::ThumbnailCache(const std::string& disk_cache_path_str,
56 size_t default_cache_size,
57 size_t approximation_cache_size,
58 size_t compression_queue_max_size,
59 size_t write_queue_max_size,
60 bool use_approximation_thumbnails,
61 float thumbnail_scale)
62 : ui_resource_provider_(NULL),
63 disk_cache_path_(disk_cache_path_str),
64 compression_queue_max_size_(compression_queue_max_size),
65 write_queue_max_size_(write_queue_max_size),
66 cache_(default_cache_size),
67 approximation_cache_(approximation_cache_size),
68 use_approximation_thumbnails_(use_approximation_thumbnails),
69 thumbnail_scale_(thumbnail_scale),
70 compression_thread_("thumbnail_compression"),
71 weak_factory_(this) {
72 compression_thread_.Start();
73 }
74
75 ThumbnailCache::~ThumbnailCache() {
76 compression_thread_.Stop();
77 SetUIResourceProvider(NULL);
78 }
79
80 void ThumbnailCache::SetUIResourceProvider(
81 content::UIResourceProvider* ui_resource_provider) {
82 if (ui_resource_provider_ == ui_resource_provider)
83 return;
84
85 // Clean up the UI resources.
86 for (ExpiringThumbnailCache::iterator iter = approximation_cache_.begin();
87 iter != approximation_cache_.end();
88 iter++) {
89 scoped_refptr<Thumbnail> thumbnail = iter->second;
90 thumbnail->CleanupThumbnail();
91 }
92
93 for (ExpiringThumbnailCache::iterator iter = cache_.begin();
94 iter != cache_.end();
95 iter++) {
96 scoped_refptr<Thumbnail> thumbnail = iter->second;
97 thumbnail->CleanupThumbnail();
98 }
99
100 if (ui_resource_provider_)
101 ui_resource_provider_->RemoveListener(this);
102
103 ui_resource_provider_ = ui_resource_provider;
104
105 if (ui_resource_provider_)
106 ui_resource_provider_->AddListener(this);
107 }
108
109 void ThumbnailCache::CacheInTab(TabId tab_id) {
110 GetThumbnail(tab_id, true);
111 }
112
113 void ThumbnailCache::AddThumbnailChangeListener(
114 ThumbnailChangeListener* listener) {
115 if (thumbnail_change_listeners_.find(listener) ==
116 thumbnail_change_listeners_.end()) {
117 thumbnail_change_listeners_.insert(listener);
118 }
119 }
120
121 void ThumbnailCache::RemoveThumbnailChangeListener(
122 ThumbnailChangeListener* listener) {
123 ThumbnailChangeListenerSet::iterator iter =
124 thumbnail_change_listeners_.find(listener);
125 if (iter != thumbnail_change_listeners_.end())
126 thumbnail_change_listeners_.erase(iter);
127 }
128
129 scoped_refptr<ThumbnailCache::Thumbnail> ThumbnailCache::GetThumbnail(
130 TabId id) {
131 return GetThumbnail(id, false);
132 }
133
134 scoped_refptr<ThumbnailCache::Thumbnail> ThumbnailCache::GetThumbnail(
135 TabId id,
136 bool cache_in_if_missing) {
137 scoped_refptr<Thumbnail> thumbnail = cache_.Get(id);
138
139 if (!thumbnail) {
140 if (cache_in_if_missing) {
141 AddIdToVisibleIds(id);
142 ReadNextThumbnail(id);
143 }
144 thumbnail = approximation_cache_.Get(id);
145 }
146
147 return thumbnail;
148 }
149
150 bool ThumbnailCache::CanReadTabContent(const TabThumbnailProvider* tab) {
151 content::ContentViewCore* view = tab->GetContentViewCore();
152 return view &&
153 view->GetWebContents()->GetRenderViewHost()->CanCopyFromBackingStore();
154 }
155
156 void ThumbnailCache::CacheTabThumbnail(const TabThumbnailProvider* tab) {
157 TabId tab_id = tab->GetAndroidId();
158 if (pending_thumbnail_readbacks_.find(tab_id) !=
159 pending_thumbnail_readbacks_.end() ||
160 pending_thumbnail_readbacks_.size() >= kMaxReadbacks) {
161 return;
162 }
163
164 InvalidateIfChanged(tab);
165 if (CheckAndUpdateThumbnailMetaData(tab, false)) {
166 base::Callback<void(int, const ThumbnailBitmap&)> end_callback = base::Bind(
167 &ThumbnailCache::EndCacheTabThumbnail, weak_factory_.GetWeakPtr());
168 scoped_refptr<TabReadbackRequest> readback =
169 make_scoped_refptr(new TabReadbackRequest(
170 tab_id, tab->GetContentViewCore(), thumbnail_scale_, end_callback));
171 pending_thumbnail_readbacks_[tab_id] = readback;
172 readback->Run();
173 }
174 }
175
176 void ThumbnailCache::CacheTabThumbnailWithBitmap(
177 const TabThumbnailProvider* tab,
178 const SkBitmap& bitmap,
179 float thumbnail_scale) {
180 bool is_native_page = true;
181 TabId tab_id = tab->GetAndroidId();
182 InvalidateIfChanged(tab);
183 if (CheckAndUpdateThumbnailMetaData(tab, is_native_page))
184 PutThumbnail(tab_id, ThumbnailBitmap(bitmap, thumbnail_scale));
185 }
186
187 void ThumbnailCache::EndCacheTabThumbnail(TabId tab_id,
188 const ThumbnailBitmap& bitmap) {
189 bool is_native_page = false;
190 TabReadbackRequestMap::iterator readback_iter =
191 pending_thumbnail_readbacks_.find(tab_id);
192 if (readback_iter != pending_thumbnail_readbacks_.end()) {
193 scoped_refptr<TabReadbackRequest> readback = readback_iter->second;
194 pending_thumbnail_readbacks_.erase(tab_id);
195 if (!readback->drop_after_readback() && bitmap.IsValid()) {
196 PutThumbnail(tab_id, bitmap);
197 }
198 NotifyListenersOfThumbnailChange(tab_id, is_native_page);
199 }
200 }
201
202 void ThumbnailCache::InvalidateIfChanged(const TabThumbnailProvider* tab) {
203 TabId tab_id = tab->GetAndroidId();
204 ThumbnailMetaDataMap::iterator meta_data_iter =
205 thumbnail_meta_data_.find(tab_id);
206 if (meta_data_iter == thumbnail_meta_data_.end()) {
207 // We need this case so we have metadata when loading the thumbnail from
208 // disk. Note that the time stamp is 0 to force invalidation when a new
209 // thumbnail is requested to be captured.
210 thumbnail_meta_data_[tab_id] =
211 ThumbnailMetaData(base::Time(), tab->GetURL());
212 } else if (meta_data_iter->second.url() != tab->GetURL()) {
213 Remove(tab_id);
214 }
215 }
216
217 void ThumbnailCache::PutThumbnail(TabId tab_id,
218 const ThumbnailBitmap& thumbnail_bitmap) {
219 if (!ui_resource_provider_ || !thumbnail_bitmap.IsValid())
220 return;
221
222 scoped_refptr<Thumbnail> thumbnail =
223 make_scoped_refptr(new Thumbnail(tab_id,
224 thumbnail_bitmap.bitmap(),
225 thumbnail_bitmap.scale(),
226 ui_resource_provider_));
227 PutThumbnail(tab_id, thumbnail_bitmap, thumbnail);
228 }
229
230 void ThumbnailCache::PutThumbnail(TabId tab_id,
231 const ThumbnailBitmap& thumbnail_bitmap,
232 scoped_refptr<Thumbnail> thumbnail) {
233 if (!ui_resource_provider_ || !thumbnail || !thumbnail_bitmap.IsValid())
234 return;
235
236 scoped_refptr<Thumbnail> approx_thumbnail = NULL;
237 ThumbnailBitmap approx_thumbnail_bitmap;
238 if (use_approximation_thumbnails_)
239 approx_thumbnail_bitmap = thumbnail_bitmap.CreateApproximation();
240
241 if (approx_thumbnail_bitmap.IsValid()) {
242 approx_thumbnail =
243 make_scoped_refptr(new ApproxThumbnail(tab_id,
244 approx_thumbnail_bitmap.bitmap(),
245 approx_thumbnail_bitmap.scale(),
246 ui_resource_provider_));
247 }
248
249 AddIdToVisibleIds(tab_id);
250 RemoveFromQueues(tab_id);
251 MakeSpaceForNewItemIfNecessary(tab_id);
252 CleanupThumbnail(cache_.Get(tab_id));
253 cache_.Put(tab_id, thumbnail);
254
255 if (approx_thumbnail) {
256 CleanupThumbnail(approximation_cache_.Get(tab_id));
257 approximation_cache_.Put(tab_id, approx_thumbnail);
258 }
259
260 CompressThumbnailIfNecessary(thumbnail);
261 WriteThumbnailIfNecessary(thumbnail);
262 NotifyListenersOfThumbnailChange(tab_id, true);
263 }
264
265 bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(
266 const TabThumbnailProvider* tab,
267 bool is_native_page) {
268 if (!is_native_page && !CanReadTabContent(tab))
269 return false;
270
271 // TODO(tedchoc): Add check to see if the tab has actually drawn content.
272 // Then add a draw counter to see if the content has actually changed since
273 // last thumbnail capture.
274 TabId tab_id = tab->GetAndroidId();
275 ThumbnailMetaDataMap::iterator meta_data_iter =
276 thumbnail_meta_data_.find(tab_id);
277 if (meta_data_iter != thumbnail_meta_data_.end() &&
278 meta_data_iter->second.url() == tab->GetURL() &&
279 (CurrentTime() - meta_data_iter->second.capture_time()) <
280 kCaptureMinRequestTimeMs) {
281 return false;
282 }
283
284 thumbnail_meta_data_[tab_id] =
285 ThumbnailMetaData(CurrentTime(), tab->GetURL());
286 return true;
287 }
288
289 void ThumbnailCache::RemoveFromCache(ExpiringThumbnailCache& cache,
290 const TabIdList& tab_ids) {
291 for (TabIdList::const_iterator iter = tab_ids.begin(); iter != tab_ids.end();
292 iter++) {
293 cache.Remove(*iter);
294 }
295 }
296
297 void ThumbnailCache::RemoveThumbnailsAndScheduleReload() {
298 TabIdList modified_entries;
299 TabIdList remove_from_cache;
300 for (ExpiringThumbnailCache::iterator iter = approximation_cache_.begin();
301 iter != approximation_cache_.end();
302 iter++) {
303 TabId tab_id = iter->first;
304 scoped_refptr<Thumbnail> thumbnail = iter->second;
305 thumbnail->CleanupThumbnail();
306
307 if (!thumbnail->AttemptToScheduleRebuildFromData())
308 remove_from_cache.push_back(tab_id);
309 modified_entries.push_back(tab_id);
310 }
311
312 read_queue_.clear();
313 RemoveFromCache(approximation_cache_, remove_from_cache);
314 remove_from_cache.clear();
315
316 for (ExpiringThumbnailCache::iterator iter = cache_.begin();
317 iter != cache_.end();
318 iter++) {
319 TabId tab_id = iter->first;
320 scoped_refptr<Thumbnail> thumbnail = iter->second;
321 thumbnail->CleanupThumbnail();
322
323 if (!thumbnail->AttemptToScheduleRebuildFromData())
324 remove_from_cache.push_back(tab_id);
325 modified_entries.push_back(tab_id);
326 }
327
328 RemoveFromCache(cache_, remove_from_cache);
329 remove_from_cache.clear();
330
331 for (TabIdList::iterator iter = modified_entries.begin();
332 iter != modified_entries.end();
333 iter++) {
334 NotifyListenersOfThumbnailChange(*iter, false);
335 }
336
337 ReadNextThumbnail();
338
339 TabIdList last_visible_ids = last_visible_ids_;
340 last_visible_ids_.clear();
341 UpdateVisibleIds(last_visible_ids);
342 }
343
344 void ThumbnailCache::UpdateVisibleIds(const TabIdList& priority) {
345 if (priority.empty()) {
346 last_visible_ids_.clear();
347 return;
348 }
349
350 size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize());
351 if (last_visible_ids_.size() == ids_size) {
352 // Early out if called with the same input as last time (We only care
353 // about the first mCache.MaximumCacheSize() entries).
354 bool lists_differ = false;
355 for (TabIdList::const_iterator visible_iter = last_visible_ids_.begin(),
356 priority_iter = priority.begin();
357 visible_iter != last_visible_ids_.end() &&
358 priority_iter != priority.end();
359 visible_iter++, priority_iter++) {
360 if (*priority_iter != *visible_iter) {
361 lists_differ = true;
362 break;
363 }
364 }
365 if (!lists_differ)
366 return;
367 }
368
369 last_visible_ids_.clear();
370 visible_ids_.clear();
371 size_t count = 0;
372 for (TabIdList::const_iterator iter = priority.begin();
373 iter != priority.end() && count < ids_size;
374 iter++, count++) {
375 TabId tab_id = *iter;
376 last_visible_ids_.push_back(tab_id);
377 visible_ids_.push_back(tab_id);
378 }
379
380 read_queue_.clear();
381
382 count = 0;
383 for (TabIdList::const_iterator iter = priority.begin();
384 iter != priority.end() && count < ids_size;
385 iter++, count++) {
386 TabId tab_id = *iter;
387 if (!cache_.Get(tab_id) &&
388 std::find(read_queue_.begin(), read_queue_.end(), tab_id) ==
389 read_queue_.end()) {
390 read_queue_.push_back(tab_id);
391 }
392 }
393
394 ReadNextThumbnail();
395 }
396
397 void ThumbnailCache::Remove(TabId tab_id) {
398 TabReadbackRequestMap::iterator readback_iter =
399 pending_thumbnail_readbacks_.find(tab_id);
400 if (readback_iter != pending_thumbnail_readbacks_.end())
401 readback_iter->second->SetDropAfterReadback(true);
402
403 scoped_refptr<Thumbnail> thumbnail = cache_.Remove(tab_id);
404 CleanupThumbnail(thumbnail);
405
406 scoped_refptr<Thumbnail> approx_thumbnail =
407 approximation_cache_.Remove(tab_id);
408 CleanupThumbnail(approx_thumbnail);
409
410 thumbnail_meta_data_.erase(tab_id);
411 RemoveFromDisk(tab_id);
412 RemoveFromQueues(tab_id);
413
414 if (thumbnail || approx_thumbnail)
415 NotifyListenersOfThumbnailChange(tab_id, false);
416 }
417
418 void ThumbnailCache::HandleLowMemory(bool consider_gpu_memory) {
419 HandleLowMemoryOnQueue(compression_queue_, consider_gpu_memory);
420 HandleLowMemoryOnQueue(write_queue_, consider_gpu_memory);
421
422 read_queue_.clear();
423
424 size_t cached_tabs_free_count = 0;
425 if (cache_.size() > 0) {
426 cached_tabs_free_count = std::max(cache_.size() / 2, 1u);
427 for (size_t i = 0; i < cached_tabs_free_count; ++i) {
428 RemoveThumbnailFromCache();
429 }
430 }
431 }
432
433 size_t ThumbnailCache::HandleLowMemoryOnQueue(ThumbnailQueue& queue,
434 bool consider_gpu_memory) {
435 size_t thumbnails_dropped = 0;
436 ThumbnailQueue next_queue;
437 for (ThumbnailQueue::iterator iter = queue.begin(); iter != queue.end();
438 iter++) {
439 scoped_refptr<Thumbnail> thumbnail = *iter;
440 TabId tab_id = thumbnail->tab_id();
441 if (!consider_gpu_memory && thumbnail.get() == cache_.Get(tab_id).get())
442 RebuildThumbnail(thumbnail, true);
443
444 CleanupThumbnail(thumbnail);
445 ++thumbnails_dropped;
446 }
447
448 queue = next_queue;
449
450 return thumbnails_dropped;
451 }
452
453 void ThumbnailCache::RemoveFromQueues(TabId tab_id) {
454 TabIdList::iterator read_iter =
455 std::find(read_queue_.begin(), read_queue_.end(), tab_id);
456 if (read_iter != read_queue_.end())
457 read_queue_.erase(read_iter);
458
459 ThumbnailQueue new_write_queue;
460 for (ThumbnailQueue::iterator iter = write_queue_.begin();
461 iter != write_queue_.end();
462 iter++) {
463 scoped_refptr<Thumbnail> write_thumbnail = *iter;
464 if (write_thumbnail->tab_id() == tab_id) {
465 write_thumbnail->CleanupCPUData(true);
466 CleanupThumbnail(write_thumbnail);
467 } else {
468 new_write_queue.push_back(write_thumbnail);
469 }
470 }
471 write_queue_ = new_write_queue;
472
473 ThumbnailQueue new_compression_queue;
474 for (ThumbnailQueue::iterator iter = compression_queue_.begin();
475 iter != compression_queue_.end();
476 iter++) {
477 scoped_refptr<Thumbnail> compress_thumbnail = *iter;
478 if (compress_thumbnail->tab_id() == tab_id) {
479 compress_thumbnail->CleanupCPUData(true);
480 CleanupThumbnail(compress_thumbnail);
481 } else {
482 new_compression_queue.push_back(compress_thumbnail);
483 }
484 }
485 compression_queue_ = new_compression_queue;
486 }
487
488 bool ThumbnailCache::WriteThumbnailIfNecessary(
489 scoped_refptr<Thumbnail> thumbnail) {
490 if (!thumbnail->DataWriteRequired())
491 return false;
492
493 if (RemoveDuplicateIdsFromQueueHelper(write_queue_, thumbnail))
494 return true;
495
496 if (write_queue_.size() > write_queue_max_size_) {
497 CleanupThumbnail(write_queue_.front());
498 write_queue_.pop_front();
499 }
500
501 write_queue_.push_back(thumbnail);
502 WriteNextThumbnail();
503 return true;
504 }
505
506 bool ThumbnailCache::CompressThumbnailIfNecessary(
507 scoped_refptr<Thumbnail> thumbnail) {
508 if (!thumbnail->CompressionRequired())
509 return false;
510
511 if (RemoveDuplicateIdsFromQueueHelper(compression_queue_, thumbnail))
512 return true;
513
514 if (compression_queue_.size() > compression_queue_max_size_) {
515 CleanupThumbnail(compression_queue_.front());
516 compression_queue_.pop_front();
517 }
518
519 compression_queue_.push_back(thumbnail);
520 CompressNextThumbnail();
521 return true;
522 }
523
524 bool ThumbnailCache::RemoveDuplicateIdsFromQueueHelper(
525 ThumbnailQueue& queue,
526 scoped_refptr<Thumbnail> thumbnail) {
527 bool found = false;
528 ThumbnailQueue remove_queue;
529 for (ThumbnailQueue::iterator iter = queue.begin(); iter != queue.end();
530 iter++) {
531 if (iter->get() == thumbnail.get())
532 found = true;
533 if ((*iter)->tab_id() == thumbnail->tab_id()) {
534 remove_queue.push_back(thumbnail);
535 CleanupThumbnail(thumbnail);
536 }
537 }
538 return found;
539 }
540
541 void ThumbnailCache::WriteNextThumbnail() {
542 if (!write_queue_.empty()) {
543 scoped_refptr<Thumbnail> thumbnail = write_queue_.front();
544 write_queue_.pop_front();
545
546 if (!thumbnail->IsValid())
547 return;
548
549 // Make a copy of the thumbnail for write to disk. This copy increases the
550 // ref-count of the pixels that the thumbnail is holding on to. We do this
551 // to avoid concurrent access issues.
552 scoped_refptr<Thumbnail> task_thumbnail =
553 make_scoped_refptr(new Thumbnail(*thumbnail));
554
555 base::Closure write_task = base::Bind(&ThumbnailCache::WriteTask,
556 GetFilePath(task_thumbnail->tab_id()),
557 task_thumbnail);
558 base::Closure post_write_task = base::Bind(&ThumbnailCache::PostWriteTask,
559 weak_factory_.GetWeakPtr(),
560 task_thumbnail);
561 content::BrowserThread::PostTaskAndReply(
562 content::BrowserThread::FILE, FROM_HERE, write_task, post_write_task);
563 }
564 }
565
566 void ThumbnailCache::WriteTask(const base::FilePath& file_path,
567 scoped_refptr<Thumbnail> thumbnail) {
568 DCHECK(thumbnail);
569 DCHECK(thumbnail->IsValid());
570 base::File file(file_path,
571 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
572 thumbnail->WriteThumbnailToFile(file);
573 file.Close();
574 }
575
576 void ThumbnailCache::PostWriteTask(scoped_refptr<Thumbnail> thumbnail) {
577 DCHECK(thumbnail);
578 // If the write has failed, then this thumbnail is invalidated, we delete the
579 // associated file. The tab Id should still be valid.
580 TabId tab_id = thumbnail->tab_id();
581 if (!thumbnail->IsValid())
582 RemoveCorruptThumbnailSource(tab_id);
583 CleanupThumbnail(GetThumbnail(tab_id));
584 WriteNextThumbnail();
585 }
586
587 void ThumbnailCache::ReadNextThumbnail(TabId id) {
588 read_queue_.clear();
589 if (std::find(read_queue_.begin(), read_queue_.end(), id) ==
590 read_queue_.end())
591 read_queue_.push_back(id);
592 ReadNextThumbnail();
593 }
594
595 void ThumbnailCache::ReadNextThumbnail() {
596 if (!read_queue_.empty()) {
597 TabId tab_id = read_queue_.front();
598 // Create a thumbnail to hold the content of the read.
599 scoped_refptr<Thumbnail> thumbnail =
600 make_scoped_refptr(new Thumbnail(tab_id, ui_resource_provider_));
601
602 base::Closure read_task = base::Bind(
603 &ThumbnailCache::ReadTask, GetFilePath(thumbnail->tab_id()), thumbnail);
604 base::Closure post_read_task = base::Bind(
605 &ThumbnailCache::PostReadTask, weak_factory_.GetWeakPtr(), thumbnail);
606 content::BrowserThread::PostTaskAndReply(
607 content::BrowserThread::FILE, FROM_HERE, read_task, post_read_task);
608 }
609 }
610
611 void ThumbnailCache::ReadTask(const base::FilePath& file_path,
612 scoped_refptr<Thumbnail> thumbnail) {
613 DCHECK(thumbnail);
614 if (!base::PathExists(file_path))
615 return;
616
617 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
618 DCHECK(file.IsValid());
619 thumbnail->ReadThumbnailFromFile(file);
620 file.Close();
621 }
622
623 void ThumbnailCache::PostReadTask(scoped_refptr<Thumbnail> thumbnail) {
624 DCHECK(thumbnail);
625 TabIdList::iterator iter =
626 std::find(read_queue_.begin(), read_queue_.end(), thumbnail->tab_id());
627 if (iter == read_queue_.end())
628 return;
629
630 read_queue_.erase(iter);
631
632 // If the read has failed, then this thumbnail is invalidated, we delete the
633 // associated file. The tab Id should still be valid.
634 TabId tab_id = thumbnail->tab_id();
635 if (!thumbnail->IsValid()) {
636 RemoveCorruptThumbnailSource(tab_id);
637 ReadNextThumbnail();
638 return;
639 }
640
641 // TODO(powei): We should be able to remove this. All on-disk thumbnails
642 // are compressed and so are not amenable to approximation.
643 scoped_refptr<Thumbnail> approx_thumbnail;
644 if (!approximation_cache_.Contains(tab_id)) {
645 SkBitmap raw_data = thumbnail->raw_data();
646 if (!raw_data.empty() && use_approximation_thumbnails_) {
647 ThumbnailBitmap thumbnail_bitmap(raw_data, thumbnail->scale());
648 ThumbnailBitmap approx_thumbnail_bitmap =
649 thumbnail_bitmap.CreateApproximation();
650 if (approx_thumbnail_bitmap.IsValid()) {
651 approx_thumbnail = make_scoped_refptr(
652 new ApproxThumbnail(tab_id,
653 approx_thumbnail_bitmap.bitmap(),
654 approx_thumbnail_bitmap.scale(),
655 ui_resource_provider_));
656 }
657 }
658 }
659 MakeSpaceForNewItemIfNecessary(tab_id);
660 scoped_refptr<Thumbnail> old_thumbnail = cache_.Remove(tab_id);
661 CleanupThumbnail(old_thumbnail);
662 cache_.Put(tab_id, thumbnail);
663 if (approx_thumbnail) {
664 scoped_refptr<Thumbnail> old_approx_thumbnail =
665 approximation_cache_.Remove(tab_id);
666 CleanupThumbnail(old_approx_thumbnail);
667 approximation_cache_.Put(tab_id, approx_thumbnail);
668 }
669
670 RebuildThumbnail(thumbnail, false);
671 NotifyListenersOfThumbnailChange(tab_id, true);
672
673 // TODO(powei): We should be able to remove this. All on-disk thumbnails are
674 // compressed.
675 if (thumbnail->CompressionRequired()) {
676 compression_queue_.push_back(thumbnail);
677 CompressNextThumbnail();
678 }
679
680 ReadNextThumbnail();
681 }
682
683 void ThumbnailCache::CompressNextThumbnail() {
684 if (!compression_queue_.empty()) {
685 scoped_refptr<Thumbnail> thumbnail = compression_queue_.front();
686 compression_queue_.pop_front();
687
688 // Make a copy of the thumbnail for compression. This copy increases the
689 // ref-count of the pixels that the thumbnail is holding on to. We do this
690 // to avoid concurrent access issues.
691 scoped_refptr<Thumbnail> task_thumbnail =
692 make_scoped_refptr(new Thumbnail(*thumbnail));
693
694 base::Closure compression_task =
695 base::Bind(&ThumbnailCache::CompressionTask, task_thumbnail);
696 base::Closure post_compression_task =
697 base::Bind(&ThumbnailCache::PostCompressionTask,
698 weak_factory_.GetWeakPtr(),
699 task_thumbnail);
700
701 DCHECK(compression_thread_.message_loop_proxy());
702 compression_thread_.message_loop_proxy()->PostTaskAndReply(
703 FROM_HERE, compression_task, post_compression_task);
704 }
705 }
706
707 void ThumbnailCache::CompressionTask(scoped_refptr<Thumbnail> thumbnail) {
708 thumbnail->Compress();
709 }
710
711 void ThumbnailCache::PostCompressionTask(
712 scoped_refptr<Thumbnail> compressed_thumbnail) {
713 DCHECK(compressed_thumbnail);
714 TabId tab_id = compressed_thumbnail->tab_id();
715 if (!compressed_thumbnail->HasCompressedRawData()) {
716 // Compression has failed and we clean up.
717 CleanupThumbnail(cache_.Remove(tab_id));
718 CleanupThumbnail(compressed_thumbnail);
719 NotifyListenersOfThumbnailChange(tab_id, false);
720 } else {
721 // The thumbnail in |cache_| is not the same reference as
722 // |compressed_thumbnail|. Replace the one in |cache_|.
723 bool rebuild_thumbnail = false;
724 if (cache_.Get(tab_id)) {
725 cache_.Put(tab_id, compressed_thumbnail);
726 rebuild_thumbnail = true;
727 }
728
729 if (rebuild_thumbnail)
730 RebuildThumbnail(compressed_thumbnail, false);
731
732 WriteThumbnailIfNecessary(compressed_thumbnail);
733 CleanupThumbnail(compressed_thumbnail);
734 NotifyListenersOfThumbnailChange(tab_id, true);
735 }
736 CompressNextThumbnail();
737 }
738
739 void ThumbnailCache::RebuildThumbnail(scoped_refptr<Thumbnail> thumbnail,
740 bool force_cleanup_cpu_data) {
741 if (!thumbnail)
742 return;
743
744 thumbnail->RebuildThumbnail();
745 thumbnail->CleanupCPUData(force_cleanup_cpu_data);
746 }
747
748 bool ThumbnailCache::ShouldCleanupThumbnail(
749 scoped_refptr<Thumbnail> thumbnail,
750 const ExpiringThumbnailCache* forced_source) {
751 scoped_refptr<Thumbnail> cache_entry = cache_.Get(thumbnail->tab_id());
752 scoped_refptr<Thumbnail> approx_entry =
753 approximation_cache_.Get(thumbnail->tab_id());
754 return thumbnail->is_approximation() ||
755 ((cache_entry.get() != thumbnail.get() || forced_source == &cache_) &&
756 (approx_entry.get() != thumbnail.get() ||
757 forced_source == &approximation_cache_) &&
758 std::find(write_queue_.begin(), write_queue_.end(), thumbnail) ==
759 write_queue_.end() &&
760 std::find(compression_queue_.begin(),
761 compression_queue_.end(),
762 thumbnail) == compression_queue_.end());
763 }
764
765 void ThumbnailCache::CleanupThumbnail(scoped_refptr<Thumbnail> thumbnail) {
766 CleanupThumbnail(thumbnail, NULL);
767 }
768
769 void ThumbnailCache::CleanupThumbnail(
770 scoped_refptr<Thumbnail> thumbnail,
771 const ExpiringThumbnailCache* forced_source) {
772 if (!thumbnail)
773 return;
774
775 if (ShouldCleanupThumbnail(thumbnail, forced_source)) {
776 thumbnail->CleanupThumbnail();
777 thumbnail->CleanupCPUData(false);
778 }
779 }
780
781 void ThumbnailCache::NotifyListenersOfThumbnailChange(TabId tab_id,
782 bool upgrade) {
783 NotifyListenersOfUIResourceUpdate(tab_id);
784
785 for (ThumbnailChangeListenerSet::iterator iter =
786 thumbnail_change_listeners_.begin();
787 iter != thumbnail_change_listeners_.end();
788 iter++) {
789 (*iter)->OnThumbnailChanged(tab_id, upgrade);
790 }
791 }
792
793 void ThumbnailCache::NotifyListenersOfUIResourceUpdate(TabId tab_id) {
794 scoped_refptr<Thumbnail> thumbnail = GetThumbnail(tab_id);
795 for (ThumbnailChangeListenerSet::iterator iter =
796 thumbnail_change_listeners_.begin();
797 iter != thumbnail_change_listeners_.end();
798 iter++) {
799 if (thumbnail) {
800 (*iter)->OnUIResourceUpdated(
801 tab_id,
802 thumbnail->ui_resource_id(),
803 thumbnail->GetContentSize(),
804 gfx::ScaleSize(thumbnail->data_size(), 1.f / thumbnail->scale()));
805 } else {
806 (*iter)->OnUIResourceUpdated(tab_id, 0, gfx::Size(), gfx::SizeF());
807 }
808 }
809 }
810
811 base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) const {
812 return disk_cache_path_.Append(base::IntToString(tab_id));
813 }
814
815 bool ThumbnailCache::ThumbnailInputFileExists(TabId tab_id) const {
816 return base::PathExists(GetFilePath(tab_id));
817 }
818
819 void ThumbnailCache::RemoveCorruptThumbnailSource(TabId tab_id) {
820 RemoveFromDisk(tab_id);
821 }
822
823 void ThumbnailCache::MakeSpaceForNewItemIfNecessary(TabId tab_id) {
824 if (cache_.Get(tab_id) ||
825 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) ==
826 visible_ids_.end() ||
827 cache_.size() < cache_.MaximumCacheSize()) {
828 return;
829 }
830
831 RemoveThumbnailFromCache();
832 }
833
834 void ThumbnailCache::RemoveThumbnailFromCache() {
835 TabId key_to_remove;
836 bool found_key_to_remove = false;
837
838 // 1. Find a cached item not in this list
839 for (ExpiringThumbnailCache::iterator iter = cache_.begin();
840 iter != cache_.end();
841 iter++) {
842 if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) ==
843 visible_ids_.end()) {
844 key_to_remove = iter->first;
845 found_key_to_remove = true;
846 break;
847 }
848 }
849
850 if (!found_key_to_remove) {
851 // 2. Find the least important id we can remove.
852 for (TabIdList::reverse_iterator riter = visible_ids_.rbegin();
853 riter != visible_ids_.rend();
854 riter++) {
855 if (cache_.Get(*riter)) {
856 key_to_remove = *riter;
857 break;
858 found_key_to_remove = true;
859 }
860 }
861 }
862
863 if (found_key_to_remove) {
864 CleanupThumbnail(cache_.Remove(key_to_remove));
865 NotifyListenersOfThumbnailChange(key_to_remove, false);
866 }
867 }
868
869 void ThumbnailCache::AddIdToVisibleIds(TabId tab_id) {
870 if (std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) !=
871 visible_ids_.end())
872 return;
873
874 if (visible_ids_.size() > cache_.MaximumCacheSize() - 1)
875 visible_ids_.pop_back();
876
877 visible_ids_.push_front(tab_id);
878 }
879
880 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData() {
881 }
882
883 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData(
884 const base::Time& current_time,
885 const GURL& url)
886 : capture_time_(current_time), url_(url) {
887 }
888
889 void ThumbnailCache::RemoveFromDiskAtAndAboveId(TabId min_id) {
890 base::Closure remove_task =
891 base::Bind(&ThumbnailCache::RemoveFromDiskAtAndAboveIdTask,
892 disk_cache_path_,
893 min_id);
894 content::BrowserThread::PostTask(
895 content::BrowserThread::FILE, FROM_HERE, remove_task);
896 }
897
898 void ThumbnailCache::RemoveFromDisk(TabId tab_id) {
899 base::Closure remove_task =
900 base::Bind(&ThumbnailCache::RemoveFromDiskTask, GetFilePath(tab_id));
901 content::BrowserThread::PostTask(
902 content::BrowserThread::FILE, FROM_HERE, remove_task);
903 }
904
905 void ThumbnailCache::RemoveFromDiskTask(const base::FilePath& file_path) {
906 base::DeleteFile(file_path, false);
907 }
908
909 void ThumbnailCache::RemoveFromDiskAtAndAboveIdTask(
910 const base::FilePath& dir_path,
911 TabId min_id) {
912 base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES);
913 while (true) {
914 base::FilePath path = enumerator.Next();
915 if (path.empty())
916 break;
917 base::FileEnumerator::FileInfo info = enumerator.GetInfo();
918 TabId tab_id;
919 bool success = base::StringToInt(info.GetName().value(), &tab_id);
920 if (success && tab_id >= min_id)
921 base::DeleteFile(path, false);
922 }
923 }
924
925 void ThumbnailCache::ThumbnailRequested(TabId tab_id) {
926 scoped_refptr<Thumbnail> thumbnail = GetThumbnail(tab_id);
927 if (thumbnail) {
928 if (!thumbnail->RebuildThumbnail())
929 return;
930 cc::UIResourceId ui_resource_id = thumbnail->ui_resource_id();
931 if (!ui_resource_id)
932 return;
933 NotifyListenersOfUIResourceUpdate(tab_id);
934 }
935 }
936
937 void ThumbnailCache::OnUIResourcesAreInvalid() {
938 RemoveThumbnailsAndScheduleReload();
939 }
940
941 void ThumbnailCache::OnRecreateUIResources() {
942 RemoveThumbnailsAndScheduleReload();
943 }
944
945 base::Time ThumbnailCache::CurrentTime() {
946 return base::Time::Now();
947 }
948
949 ThumbnailCache::Thumbnail::Thumbnail(
950 TabId tab_id,
951 content::UIResourceProvider* ui_resource_provider)
952 : tab_id_(tab_id),
953 ui_resource_provider_(ui_resource_provider),
954 ui_resource_id_(0),
955 format_(DECOMPRESSED),
956 rebuild_pending_(false),
957 data_write_pending_(false),
958 from_stream_(false) {
959 DCHECK(ui_resource_provider);
960 }
961
962 ThumbnailCache::Thumbnail::Thumbnail(
963 TabId tab_id,
964 SkBitmap bitmap,
965 float scale,
966 content::UIResourceProvider* ui_resource_provider)
967 : tab_id_(tab_id),
968 raw_data_(bitmap),
969 scale_(scale),
970 ui_resource_provider_(ui_resource_provider),
971 ui_resource_id_(0),
972 format_(DECOMPRESSED),
973 rebuild_pending_(false),
974 data_write_pending_(false),
975 from_stream_(false) {
976 DCHECK(ui_resource_provider);
977 SetRawThumbnailData(bitmap);
978 }
979
980 ThumbnailCache::Thumbnail::Thumbnail(const Thumbnail& thumbnail)
981 : tab_id_(thumbnail.tab_id_),
982 raw_data_(thumbnail.raw_data_),
983 scale_(thumbnail.scale_),
984 ui_resource_provider_(thumbnail.ui_resource_provider_),
985 ui_resource_id_(thumbnail.ui_resource_id_),
986 compressed_data_(thumbnail.compressed_data_),
987 data_size_(thumbnail.data_size_),
988 scaled_content_size_(thumbnail.scaled_content_size_),
989 format_(thumbnail.format_),
990 rebuild_pending_(thumbnail.rebuild_pending_),
991 data_write_pending_(thumbnail.data_write_pending_),
992 from_stream_(thumbnail.from_stream_) {
993 }
994
995 ThumbnailCache::Thumbnail::~Thumbnail() {
996 }
997
998 ThumbnailCache::ApproxThumbnail::ApproxThumbnail(
999 TabId tab_id,
1000 SkBitmap bitmap,
1001 float scale,
1002 content::UIResourceProvider* ui_resource_provider)
1003 : Thumbnail(tab_id, bitmap, scale, ui_resource_provider) {
1004 }
1005
1006 void ThumbnailCache::Thumbnail::CleanupThumbnail() {
1007 if (ui_resource_id_) {
1008 ui_resource_provider_->DeleteUIResource(ui_resource_id_);
1009 ui_resource_id_ = 0;
1010 }
1011 }
1012
1013 bool ThumbnailCache::Thumbnail::AttemptToScheduleRebuildFromData() {
1014 if (!raw_data_.empty() ||
1015 (compressed_data_ && !compressed_data_->info().isEmpty())) {
1016 rebuild_pending_ = true;
1017 return true;
1018 }
1019 return false;
1020 }
1021
1022 void ThumbnailCache::Thumbnail::CleanupCPUData(bool force) {
1023 if (!CleanupRequired() && !force)
1024 return;
1025 raw_data_ = SkBitmap();
1026 compressed_data_.clear();
1027 }
1028
1029 bool ThumbnailCache::Thumbnail::RebuildThumbnail() {
1030 if (!rebuild_pending_)
1031 return false;
1032 CleanupThumbnail();
1033 if ((raw_data_.empty() && format_ == DECOMPRESSED) ||
1034 (!compressed_data_ && format_ == COMPRESSED))
1035 return false;
1036 bool is_bitmap_transient = true;
1037 if (format_ == COMPRESSED) {
1038 ui_resource_id_ = ui_resource_provider_->GenerateCompressedUIResource(
1039 compressed_data_, is_bitmap_transient);
1040 } else {
1041 ui_resource_id_ = ui_resource_provider_->GenerateUIResource(
1042 raw_data_, is_bitmap_transient);
1043 }
1044
1045 rebuild_pending_ = false;
1046 CleanupCPUData(false);
1047 return true;
1048 }
1049
1050 bool ThumbnailCache::Thumbnail::CleanupRequired() {
1051 return !(CompressionRequired() || DataWriteRequired() || rebuild_pending_ ||
1052 (raw_data_.empty() && !compressed_data_));
1053 }
1054
1055 bool ThumbnailCache::Thumbnail::CompressionRequired() const {
1056 return kEnableCompression && !compressed_data_ && !raw_data_.empty();
1057 }
1058
1059 bool ThumbnailCache::Thumbnail::DataWriteRequired() const {
1060 return data_write_pending_ && !from_stream_;
1061 }
1062
1063 bool ThumbnailCache::Thumbnail::HasCompressedRawData() const {
1064 return compressed_data_;
1065 }
1066
1067 bool ThumbnailCache::Thumbnail::IsValid() const {
1068 return ui_resource_provider_ && scale_ > 0 && !data_size_.IsEmpty() &&
1069 !scaled_content_size_.IsEmpty() &&
1070 (ui_resource_id_ || !raw_data_.empty() || compressed_data_);
1071 }
1072
1073 gfx::Size ThumbnailCache::Thumbnail::GetContentSize() const {
1074 return gfx::ToRoundedSize(gfx::ScaleSize(scaled_content_size_, 1.f / scale_));
1075 }
1076
1077 void ThumbnailCache::Thumbnail::Compress() {
1078 SkBitmap raw_data = raw_data_;
1079 if (CompressionRequired() && format_ == DECOMPRESSED && !raw_data.empty()) {
1080 SkAutoLockPixels raw_data_lock(raw_data);
1081 gfx::Size raw_data_size(raw_data.width(), raw_data.height());
1082 DCHECK_EQ(raw_data.config(), SkBitmap::kARGB_8888_Config);
1083 size_t pixel_size = 4; // Pixel size is 4 bytes for kARGB_8888_Config.
1084 size_t stride = pixel_size * raw_data_size.width();
1085
1086 gfx::Size encoded_size = GetEncodedSize(raw_data_size);
1087 size_t encoded_bytes =
1088 etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height());
1089 SkImageInfo info = {encoded_size.width(),
1090 encoded_size.height(),
1091 kUnknown_SkColorType,
1092 kUnpremul_SkAlphaType};
1093 skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef(
1094 SkData::NewFromMalloc(new uint8_t[encoded_bytes], encoded_bytes));
1095 skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef(
1096 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get()));
1097
1098 etc1_pixel_ref->lockPixels();
1099 bool success = etc1_encode_image(
1100 reinterpret_cast<unsigned char*>(raw_data.getPixels()),
1101 raw_data_size.width(),
1102 raw_data_size.height(),
1103 pixel_size,
1104 stride,
1105 reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()),
1106 encoded_size.width(),
1107 encoded_size.height());
1108 etc1_pixel_ref->unlockPixels();
1109 if (success) {
1110 etc1_pixel_ref->setImmutable();
1111 SetCompressedThumbnailData(etc1_pixel_ref, raw_data_size);
1112 } else {
1113 Invalidate();
1114 }
1115 }
1116 }
1117
1118 bool ThumbnailCache::Thumbnail::WriteThumbnailToFile(base::File& file) {
1119 // We assume caller has already initialized the file and will also close it.
1120 DCHECK(file.IsValid());
1121
1122 if (DataWriteRequired() && format_ == COMPRESSED && compressed_data_) {
1123 if (kEnableCompression) {
1124 compressed_data_->lockPixels();
1125 bool success = true;
1126 int scaled_content_width = scaled_content_size_.width();
1127 int scaled_content_height = scaled_content_size_.height();
1128 int data_width = data_size_.width();
1129 int data_height = data_size_.height();
1130
1131 if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey),
1132 sizeof(int)) < 0 ||
1133 file.WriteAtCurrentPos(
1134 reinterpret_cast<const char*>(&scaled_content_width),
1135 sizeof(int)) < 0 ||
1136 file.WriteAtCurrentPos(
1137 reinterpret_cast<const char*>(&scaled_content_height),
1138 sizeof(int)) < 0 ||
1139 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width),
1140 sizeof(int)) < 0 ||
1141 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height),
1142 sizeof(int)) < 0 ||
1143 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&scale_),
1144 sizeof(float)) < 0) {
1145 success = false;
1146 }
1147
1148 size_t compressed_bytes =
1149 etc1_get_encoded_data_size(data_size_.width(), data_size_.height());
1150
1151 if (file.WriteAtCurrentPos(
1152 reinterpret_cast<char*>(compressed_data_->pixels()),
1153 compressed_bytes) < 0)
1154 success = false;
1155
1156 compressed_data_->unlockPixels();
1157
1158 if (!success)
1159 Invalidate();
1160
1161 return success;
1162 } else {
1163 // In the original Java implementation, we store a jpg version when the
1164 // |kEnableCompression| flag is off. Keep as NOTIMPLEMENTED for now.
1165 NOTIMPLEMENTED();
1166 }
1167 }
1168 return false;
1169 }
1170
1171 bool ThumbnailCache::Thumbnail::ReadThumbnailFromFile(base::File& file) {
1172 // We assume caller has already initialized the file and will also close it.
1173 DCHECK(file.IsValid());
1174
1175 if (kEnableCompression) {
1176 int key;
1177 bool success = true;
1178 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 ||
1179 key != kCompressedKey)
1180 success = false;
1181
1182 int width, height;
1183 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) <
1184 0 ||
1185 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) <
1186 0)
1187 success = false;
1188 scaled_content_size_ = gfx::Size(width, height);
1189
1190 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) <
1191 0 ||
1192 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) <
1193 0)
1194 success = false;
1195 data_size_ = gfx::Size(width, height);
1196
1197 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale_), sizeof(float)) <
1198 0)
1199 success = false;
1200
1201 size_t compressed_bytes =
1202 etc1_get_encoded_data_size(data_size_.width(), data_size_.height());
1203
1204 SkImageInfo info = {data_size_.width(),
1205 data_size_.height(),
1206 kUnknown_SkColorType,
1207 kUnpremul_SkAlphaType};
1208
1209 scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]);
1210 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()),
1211 compressed_bytes) < 0)
1212 success = false;
1213
1214 skia::RefPtr<SkData> etc1_pixel_data =
1215 skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes));
1216 compressed_data_ = skia::AdoptRef(
1217 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get()));
1218
1219 format_ = COMPRESSED;
1220 from_stream_ = true;
1221 rebuild_pending_ = true;
1222 data_write_pending_ = false;
1223
1224 if (!success)
1225 Invalidate();
1226
1227 return success;
1228 } else {
1229 NOTIMPLEMENTED();
1230 }
1231
1232 return false;
1233 }
1234
1235 void ThumbnailCache::Thumbnail::Invalidate() {
1236 CleanupCPUData(true);
1237 ui_resource_id_ = 0;
1238 data_size_ = gfx::Size();
1239 scaled_content_size_ = gfx::Size();
1240 }
1241
1242 void ThumbnailCache::Thumbnail::SetCompressedThumbnailData(
1243 skia::RefPtr<SkPixelRef> compressed_data,
1244 gfx::Size scaled_content_size) {
1245 compressed_data_ = compressed_data;
1246 raw_data_ = SkBitmap();
1247 if (compressed_data) {
1248 data_size_ = gfx::Size(compressed_data_->info().width(),
1249 compressed_data_->info().height());
1250 scaled_content_size_ = scaled_content_size;
1251 format_ = COMPRESSED;
1252 rebuild_pending_ = true;
1253 data_write_pending_ = true;
1254 } else {
1255 data_size_ = gfx::Size();
1256 scaled_content_size_ = gfx::Size();
1257 rebuild_pending_ = false;
1258 data_write_pending_ = false;
1259 }
1260 }
1261
1262 void ThumbnailCache::Thumbnail::SetRawThumbnailData(SkBitmap bitmap) {
1263 raw_data_ = bitmap;
1264 compressed_data_.clear();
1265 data_size_ = gfx::Size(bitmap.width(), bitmap.height());
1266 scaled_content_size_ = data_size_;
1267 format_ = DECOMPRESSED;
1268 rebuild_pending_ = true;
1269 data_write_pending_ = !kEnableCompression;
1270 }
1271
1272 ThumbnailCache::ThumbnailBitmap::ThumbnailBitmap() : scale_(0.f) {
1273 }
1274
1275 ThumbnailCache::ThumbnailBitmap::ThumbnailBitmap(SkBitmap bitmap, float scale)
1276 : bitmap_(bitmap), scale_(scale) {
1277 }
1278
1279 bool ThumbnailCache::ThumbnailBitmap::IsValid() const {
1280 return !bitmap_.empty() && scale_ > 0;
1281 }
1282
1283 ThumbnailCache::ThumbnailBitmap
1284 ThumbnailCache::ThumbnailBitmap::CreateApproximation() const {
1285 SkAutoLockPixels bitmap_lock(bitmap_);
1286 float new_scale = 1.f / kApproximationScaleFactor;
1287
1288 gfx::Size dst_size = gfx::ToFlooredSize(
1289 gfx::ScaleSize(gfx::Size(bitmap_.width(), bitmap_.height()), new_scale));
1290 SkBitmap dst_bitmap;
1291 dst_bitmap.setConfig(
1292 bitmap_.getConfig(), dst_size.width(), dst_size.height());
1293 dst_bitmap.allocPixels();
1294 dst_bitmap.eraseColor(0);
1295 SkAutoLockPixels dst_bitmap_lock(dst_bitmap);
1296
1297 SkCanvas canvas(dst_bitmap);
1298 canvas.scale(new_scale, new_scale);
1299 canvas.drawBitmap(bitmap_, 0, 0, NULL);
1300 dst_bitmap.setImmutable();
1301
1302 return ThumbnailBitmap(dst_bitmap, new_scale * scale_);
1303 }
1304
1305 ThumbnailCache::TabReadbackRequest::TabReadbackRequest(
1306 TabId tab_id,
1307 content::ContentViewCore* content_view_core,
1308 float thumbnail_scale,
1309 base::Callback<void(int, const ThumbnailBitmap&)> end_callback)
1310 : tab_id_(tab_id),
1311 content_view_core_(content_view_core),
1312 thumbnail_scale_(thumbnail_scale),
1313 end_callback_(end_callback),
1314 drop_after_readback_(false),
1315 weak_factory_(this) {
1316 }
1317
1318 void ThumbnailCache::TabReadbackRequest::Run() {
1319 base::Callback<void(bool, const SkBitmap&)> result_callback =
1320 base::Bind(&TabReadbackRequest::OnFinishGetTabThumbnailBitmap,
1321 weak_factory_.GetWeakPtr());
1322 if (content_view_core_) {
1323 DCHECK(content_view_core_->GetWebContents());
1324 content_view_core_->GetWebContents()
1325 ->GetRenderViewHost()
1326 ->LockBackingStore();
1327
1328 // Calling this method with an empty rect will return a bitmap of the size
1329 // of the content.
1330 content_view_core_->GetScaledContentBitmap(thumbnail_scale_,
1331 SkBitmap::kARGB_8888_Config,
1332 gfx::Rect(),
1333 result_callback);
1334 } else {
1335 result_callback.Run(false, SkBitmap());
1336 }
1337 }
1338
1339 // This callback should be called on the UI thread. So it should be safe to
1340 // call the thumbnail cache.
1341 void ThumbnailCache::TabReadbackRequest::OnFinishGetTabThumbnailBitmap(
1342 bool success,
1343 const SkBitmap& bitmap) {
1344 if (content_view_core_) {
1345 DCHECK(content_view_core_->GetWebContents());
1346 content_view_core_->GetWebContents()
1347 ->GetRenderViewHost()
1348 ->UnlockBackingStore();
1349 }
1350 if (success) {
1351 SkBitmap local_bitmap = bitmap;
1352 local_bitmap.setImmutable();
1353 end_callback_.Run(tab_id_, ThumbnailBitmap(local_bitmap, thumbnail_scale_));
1354 } else {
1355 end_callback_.Run(tab_id_, ThumbnailBitmap());
1356 }
1357 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698