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 "chrome/browser/android/thumbnail/thumbnail_store.h"
10 #include "base/big_endian.h"
11 #include "base/files/file.h"
12 #include "base/files/file_enumerator.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/threading/worker_pool.h"
17 #include "base/time/time.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "third_party/android_opengl/etc1/etc1.h"
20 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "third_party/skia/include/core/SkCanvas.h"
22 #include "third_party/skia/include/core/SkData.h"
23 #include "third_party/skia/include/core/SkMallocPixelRef.h"
24 #include "third_party/skia/include/core/SkPixelRef.h"
25 #include "ui/android/resources/ui_resource_provider.h"
26 #include "ui/gfx/android/device_display_info.h"
27 #include "ui/gfx/geometry/size_conversions.h"
31 const float kApproximationScaleFactor
= 4.f
;
32 const base::TimeDelta
kCaptureMinRequestTimeMs(
33 base::TimeDelta::FromMilliseconds(1000));
35 const int kCompressedKey
= 0xABABABAB;
36 const int kCurrentExtraVersion
= 1;
38 // Indicates whether we prefer to have more free CPU memory over GPU memory.
39 const bool kPreferCPUMemory
= true;
41 size_t NextPowerOfTwo(size_t x
) {
51 size_t RoundUpMod4(size_t x
) {
55 gfx::Size
GetEncodedSize(const gfx::Size
& bitmap_size
, bool supports_npot
) {
56 DCHECK(!bitmap_size
.IsEmpty());
58 return gfx::Size(NextPowerOfTwo(bitmap_size
.width()),
59 NextPowerOfTwo(bitmap_size
.height()));
61 return gfx::Size(RoundUpMod4(bitmap_size
.width()),
62 RoundUpMod4(bitmap_size
.height()));
66 bool ReadBigEndianFromFile(base::File
& file
, T
* out
) {
67 char buffer
[sizeof(T
)];
68 if (file
.ReadAtCurrentPos(buffer
, sizeof(T
)) != sizeof(T
))
70 base::ReadBigEndian(buffer
, out
);
75 bool WriteBigEndianToFile(base::File
& file
, T val
) {
76 char buffer
[sizeof(T
)];
77 base::WriteBigEndian(buffer
, val
);
78 return file
.WriteAtCurrentPos(buffer
, sizeof(T
)) == sizeof(T
);
81 bool ReadBigEndianFloatFromFile(base::File
& file
, float* out
) {
82 char buffer
[sizeof(float)];
83 if (file
.ReadAtCurrentPos(buffer
, sizeof(buffer
)) != sizeof(buffer
))
86 #if defined(ARCH_CPU_LITTLE_ENDIAN)
87 for (size_t i
= 0; i
< sizeof(float) / 2; i
++) {
89 buffer
[i
] = buffer
[sizeof(float) - 1 - i
];
90 buffer
[sizeof(float) - 1 - i
] = tmp
;
93 memcpy(out
, buffer
, sizeof(buffer
));
98 bool WriteBigEndianFloatToFile(base::File
& file
, float val
) {
99 char buffer
[sizeof(float)];
100 memcpy(buffer
, &val
, sizeof(buffer
));
102 #if defined(ARCH_CPU_LITTLE_ENDIAN)
103 for (size_t i
= 0; i
< sizeof(float) / 2; i
++) {
104 char tmp
= buffer
[i
];
105 buffer
[i
] = buffer
[sizeof(float) - 1 - i
];
106 buffer
[sizeof(float) - 1 - i
] = tmp
;
109 return file
.WriteAtCurrentPos(buffer
, sizeof(buffer
)) == sizeof(buffer
);
112 } // anonymous namespace
114 ThumbnailStore::ThumbnailStore(const std::string
& disk_cache_path_str
,
115 size_t default_cache_size
,
116 size_t approximation_cache_size
,
117 size_t compression_queue_max_size
,
118 size_t write_queue_max_size
,
119 bool use_approximation_thumbnail
)
120 : disk_cache_path_(disk_cache_path_str
),
121 compression_queue_max_size_(compression_queue_max_size
),
122 write_queue_max_size_(write_queue_max_size
),
123 use_approximation_thumbnail_(use_approximation_thumbnail
),
124 compression_tasks_count_(0),
125 write_tasks_count_(0),
126 read_in_progress_(false),
127 cache_(default_cache_size
),
128 approximation_cache_(approximation_cache_size
),
129 ui_resource_provider_(NULL
),
130 weak_factory_(this) {
131 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI
));
134 ThumbnailStore::~ThumbnailStore() {
135 SetUIResourceProvider(NULL
);
138 void ThumbnailStore::SetUIResourceProvider(
139 ui::UIResourceProvider
* ui_resource_provider
) {
140 if (ui_resource_provider_
== ui_resource_provider
)
143 approximation_cache_
.Clear();
146 ui_resource_provider_
= ui_resource_provider
;
149 void ThumbnailStore::AddThumbnailStoreObserver(
150 ThumbnailStoreObserver
* observer
) {
151 if (!observers_
.HasObserver(observer
))
152 observers_
.AddObserver(observer
);
155 void ThumbnailStore::RemoveThumbnailStoreObserver(
156 ThumbnailStoreObserver
* observer
) {
157 if (observers_
.HasObserver(observer
))
158 observers_
.RemoveObserver(observer
);
161 void ThumbnailStore::Put(TabId tab_id
,
162 const SkBitmap
& bitmap
,
163 float thumbnail_scale
) {
164 if (!ui_resource_provider_
|| bitmap
.empty() || thumbnail_scale
<= 0)
167 DCHECK(thumbnail_meta_data_
.find(tab_id
) != thumbnail_meta_data_
.end());
169 base::Time time_stamp
= thumbnail_meta_data_
[tab_id
].capture_time();
170 scoped_ptr
<Thumbnail
> thumbnail
= Thumbnail::Create(
171 tab_id
, time_stamp
, thumbnail_scale
, ui_resource_provider_
, this);
172 thumbnail
->SetBitmap(bitmap
);
174 RemoveFromReadQueue(tab_id
);
175 MakeSpaceForNewItemIfNecessary(tab_id
);
176 cache_
.Put(tab_id
, thumbnail
.Pass());
178 if (use_approximation_thumbnail_
) {
179 std::pair
<SkBitmap
, float> approximation
=
180 CreateApproximation(bitmap
, thumbnail_scale
);
181 scoped_ptr
<Thumbnail
> approx_thumbnail
= Thumbnail::Create(
182 tab_id
, time_stamp
, approximation
.second
, ui_resource_provider_
, this);
183 approx_thumbnail
->SetBitmap(approximation
.first
);
184 approximation_cache_
.Put(tab_id
, approx_thumbnail
.Pass());
186 CompressThumbnailIfNecessary(tab_id
, time_stamp
, bitmap
, thumbnail_scale
);
189 void ThumbnailStore::Remove(TabId tab_id
) {
190 cache_
.Remove(tab_id
);
191 approximation_cache_
.Remove(tab_id
);
192 thumbnail_meta_data_
.erase(tab_id
);
193 RemoveFromDisk(tab_id
);
194 RemoveFromReadQueue(tab_id
);
197 Thumbnail
* ThumbnailStore::Get(TabId tab_id
,
198 bool force_disk_read
,
199 bool allow_approximation
) {
200 Thumbnail
* thumbnail
= cache_
.Get(tab_id
);
202 thumbnail
->CreateUIResource();
206 if (force_disk_read
&&
207 std::find(visible_ids_
.begin(), visible_ids_
.end(), tab_id
) !=
208 visible_ids_
.end() &&
209 std::find(read_queue_
.begin(), read_queue_
.end(), tab_id
) ==
211 read_queue_
.push_back(tab_id
);
215 if (allow_approximation
) {
216 thumbnail
= approximation_cache_
.Get(tab_id
);
218 thumbnail
->CreateUIResource();
226 void ThumbnailStore::RemoveFromDiskAtAndAboveId(TabId min_id
) {
227 base::Closure remove_task
=
228 base::Bind(&ThumbnailStore::RemoveFromDiskAtAndAboveIdTask
,
231 content::BrowserThread::PostTask(
232 content::BrowserThread::FILE, FROM_HERE
, remove_task
);
235 void ThumbnailStore::InvalidateThumbnailIfChanged(TabId tab_id
,
237 ThumbnailMetaDataMap::iterator meta_data_iter
=
238 thumbnail_meta_data_
.find(tab_id
);
239 if (meta_data_iter
== thumbnail_meta_data_
.end()) {
240 thumbnail_meta_data_
[tab_id
] = ThumbnailMetaData(base::Time(), url
);
241 } else if (meta_data_iter
->second
.url() != url
) {
246 bool ThumbnailStore::CheckAndUpdateThumbnailMetaData(TabId tab_id
,
248 base::Time current_time
= base::Time::Now();
249 ThumbnailMetaDataMap::iterator meta_data_iter
=
250 thumbnail_meta_data_
.find(tab_id
);
251 if (meta_data_iter
!= thumbnail_meta_data_
.end() &&
252 meta_data_iter
->second
.url() == url
&&
253 (current_time
- meta_data_iter
->second
.capture_time()) <
254 kCaptureMinRequestTimeMs
) {
258 thumbnail_meta_data_
[tab_id
] = ThumbnailMetaData(current_time
, url
);
262 void ThumbnailStore::UpdateVisibleIds(const TabIdList
& priority
) {
263 if (priority
.empty()) {
264 visible_ids_
.clear();
268 size_t ids_size
= std::min(priority
.size(), cache_
.MaximumCacheSize());
269 if (visible_ids_
.size() == ids_size
) {
270 // Early out if called with the same input as last time (We only care
271 // about the first mCache.MaximumCacheSize() entries).
272 bool needs_update
= false;
273 TabIdList::const_iterator visible_iter
= visible_ids_
.begin();
274 TabIdList::const_iterator priority_iter
= priority
.begin();
275 while (visible_iter
!= visible_ids_
.end() &&
276 priority_iter
!= priority
.end()) {
277 if (*priority_iter
!= *visible_iter
|| !cache_
.Get(*priority_iter
)) {
290 visible_ids_
.clear();
292 TabIdList::const_iterator iter
= priority
.begin();
293 while (iter
!= priority
.end() && count
< ids_size
) {
294 TabId tab_id
= *iter
;
295 visible_ids_
.push_back(tab_id
);
296 if (!cache_
.Get(tab_id
) &&
297 std::find(read_queue_
.begin(), read_queue_
.end(), tab_id
) ==
299 read_queue_
.push_back(tab_id
);
308 void ThumbnailStore::DecompressThumbnailFromFile(
310 const base::Callback
<void(bool, SkBitmap
)>&
311 post_decompress_callback
) {
312 base::FilePath file_path
= GetFilePath(tab_id
);
314 base::Callback
<void(skia::RefPtr
<SkPixelRef
>, float, const gfx::Size
&)>
315 decompress_task
= base::Bind(
316 &ThumbnailStore::DecompressionTask
, post_decompress_callback
);
318 content::BrowserThread::PostTask(
319 content::BrowserThread::FILE,
321 base::Bind(&ThumbnailStore::ReadTask
, true, file_path
, decompress_task
));
324 void ThumbnailStore::RemoveFromDisk(TabId tab_id
) {
325 base::FilePath file_path
= GetFilePath(tab_id
);
327 base::Bind(&ThumbnailStore::RemoveFromDiskTask
, file_path
);
328 content::BrowserThread::PostTask(
329 content::BrowserThread::FILE, FROM_HERE
, task
);
332 void ThumbnailStore::RemoveFromDiskTask(const base::FilePath
& file_path
) {
333 if (base::PathExists(file_path
))
334 base::DeleteFile(file_path
, false);
337 void ThumbnailStore::RemoveFromDiskAtAndAboveIdTask(
338 const base::FilePath
& dir_path
,
340 base::FileEnumerator
enumerator(dir_path
, false, base::FileEnumerator::FILES
);
342 base::FilePath path
= enumerator
.Next();
345 base::FileEnumerator::FileInfo info
= enumerator
.GetInfo();
347 bool success
= base::StringToInt(info
.GetName().value(), &tab_id
);
348 if (success
&& tab_id
>= min_id
)
349 base::DeleteFile(path
, false);
353 void ThumbnailStore::WriteThumbnailIfNecessary(
355 skia::RefPtr
<SkPixelRef
> compressed_data
,
357 const gfx::Size
& content_size
) {
358 if (write_tasks_count_
>= write_queue_max_size_
)
361 write_tasks_count_
++;
363 base::Callback
<void()> post_write_task
=
364 base::Bind(&ThumbnailStore::PostWriteTask
, weak_factory_
.GetWeakPtr());
365 content::BrowserThread::PostTask(content::BrowserThread::FILE,
367 base::Bind(&ThumbnailStore::WriteTask
,
375 void ThumbnailStore::CompressThumbnailIfNecessary(
377 const base::Time
& time_stamp
,
378 const SkBitmap
& bitmap
,
380 if (compression_tasks_count_
>= compression_queue_max_size_
) {
381 RemoveOnMatchedTimeStamp(tab_id
, time_stamp
);
385 compression_tasks_count_
++;
387 base::Callback
<void(skia::RefPtr
<SkPixelRef
>, const gfx::Size
&)>
388 post_compression_task
= base::Bind(&ThumbnailStore::PostCompressionTask
,
389 weak_factory_
.GetWeakPtr(),
394 gfx::Size
raw_data_size(bitmap
.width(), bitmap
.height());
395 gfx::Size encoded_size
= GetEncodedSize(
396 raw_data_size
, ui_resource_provider_
->SupportsETC1NonPowerOfTwo());
398 base::WorkerPool::PostTask(FROM_HERE
,
399 base::Bind(&ThumbnailStore::CompressionTask
,
402 post_compression_task
),
406 void ThumbnailStore::ReadNextThumbnail() {
407 if (read_queue_
.empty() || read_in_progress_
)
410 TabId tab_id
= read_queue_
.front();
411 read_in_progress_
= true;
413 base::FilePath file_path
= GetFilePath(tab_id
);
415 base::Callback
<void(skia::RefPtr
<SkPixelRef
>, float, const gfx::Size
&)>
416 post_read_task
= base::Bind(
417 &ThumbnailStore::PostReadTask
, weak_factory_
.GetWeakPtr(), tab_id
);
419 content::BrowserThread::PostTask(
420 content::BrowserThread::FILE,
422 base::Bind(&ThumbnailStore::ReadTask
, false, file_path
, post_read_task
));
425 void ThumbnailStore::MakeSpaceForNewItemIfNecessary(TabId tab_id
) {
426 if (cache_
.Get(tab_id
) ||
427 std::find(visible_ids_
.begin(), visible_ids_
.end(), tab_id
) ==
428 visible_ids_
.end() ||
429 cache_
.size() < cache_
.MaximumCacheSize()) {
434 bool found_key_to_remove
= false;
436 // 1. Find a cached item not in this list
437 for (ExpiringThumbnailCache::iterator iter
= cache_
.begin();
438 iter
!= cache_
.end();
440 if (std::find(visible_ids_
.begin(), visible_ids_
.end(), iter
->first
) ==
441 visible_ids_
.end()) {
442 key_to_remove
= iter
->first
;
443 found_key_to_remove
= true;
448 if (!found_key_to_remove
) {
449 // 2. Find the least important id we can remove.
450 for (TabIdList::reverse_iterator riter
= visible_ids_
.rbegin();
451 riter
!= visible_ids_
.rend();
453 if (cache_
.Get(*riter
)) {
454 key_to_remove
= *riter
;
456 found_key_to_remove
= true;
461 if (found_key_to_remove
)
462 cache_
.Remove(key_to_remove
);
465 void ThumbnailStore::RemoveFromReadQueue(TabId tab_id
) {
466 TabIdList::iterator read_iter
=
467 std::find(read_queue_
.begin(), read_queue_
.end(), tab_id
);
468 if (read_iter
!= read_queue_
.end())
469 read_queue_
.erase(read_iter
);
472 void ThumbnailStore::InvalidateCachedThumbnail(Thumbnail
* thumbnail
) {
474 TabId tab_id
= thumbnail
->tab_id();
475 cc::UIResourceId uid
= thumbnail
->ui_resource_id();
477 Thumbnail
* cached_thumbnail
= cache_
.Get(tab_id
);
478 if (cached_thumbnail
&& cached_thumbnail
->ui_resource_id() == uid
)
479 cache_
.Remove(tab_id
);
481 cached_thumbnail
= approximation_cache_
.Get(tab_id
);
482 if (cached_thumbnail
&& cached_thumbnail
->ui_resource_id() == uid
)
483 approximation_cache_
.Remove(tab_id
);
486 base::FilePath
ThumbnailStore::GetFilePath(TabId tab_id
) const {
487 return disk_cache_path_
.Append(base::IntToString(tab_id
));
492 bool WriteToFile(base::File
& file
,
493 const gfx::Size
& content_size
,
495 skia::RefPtr
<SkPixelRef
> compressed_data
) {
499 if (!WriteBigEndianToFile(file
, kCompressedKey
))
502 if (!WriteBigEndianToFile(file
, content_size
.width()))
505 if (!WriteBigEndianToFile(file
, content_size
.height()))
508 // Write ETC1 header.
509 compressed_data
->lockPixels();
511 unsigned char etc1_buffer
[ETC_PKM_HEADER_SIZE
];
512 etc1_pkm_format_header(etc1_buffer
,
513 compressed_data
->info().width(),
514 compressed_data
->info().height());
516 int header_bytes_written
= file
.WriteAtCurrentPos(
517 reinterpret_cast<char*>(etc1_buffer
), ETC_PKM_HEADER_SIZE
);
518 if (header_bytes_written
!= ETC_PKM_HEADER_SIZE
)
521 int data_size
= etc1_get_encoded_data_size(
522 compressed_data
->info().width(),
523 compressed_data
->info().height());
524 int pixel_bytes_written
= file
.WriteAtCurrentPos(
525 reinterpret_cast<char*>(compressed_data
->pixels()),
527 if (pixel_bytes_written
!= data_size
)
530 compressed_data
->unlockPixels();
532 if (!WriteBigEndianToFile(file
, kCurrentExtraVersion
))
535 if (!WriteBigEndianFloatToFile(file
, 1.f
/ scale
))
541 } // anonymous namespace
543 void ThumbnailStore::WriteTask(const base::FilePath
& file_path
,
544 skia::RefPtr
<SkPixelRef
> compressed_data
,
546 const gfx::Size
& content_size
,
547 const base::Callback
<void()>& post_write_task
) {
548 DCHECK(compressed_data
);
550 base::File
file(file_path
,
551 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
553 bool success
= WriteToFile(file
,
561 base::DeleteFile(file_path
, false);
563 content::BrowserThread::PostTask(
564 content::BrowserThread::UI
, FROM_HERE
, post_write_task
);
567 void ThumbnailStore::PostWriteTask() {
568 write_tasks_count_
--;
571 void ThumbnailStore::CompressionTask(
573 gfx::Size encoded_size
,
574 const base::Callback
<void(skia::RefPtr
<SkPixelRef
>, const gfx::Size
&)>&
575 post_compression_task
) {
576 skia::RefPtr
<SkPixelRef
> compressed_data
;
577 gfx::Size content_size
;
579 if (!raw_data
.empty()) {
580 SkAutoLockPixels
raw_data_lock(raw_data
);
581 gfx::Size
raw_data_size(raw_data
.width(), raw_data
.height());
582 size_t pixel_size
= 4; // Pixel size is 4 bytes for kARGB_8888_Config.
583 size_t stride
= pixel_size
* raw_data_size
.width();
585 size_t encoded_bytes
=
586 etc1_get_encoded_data_size(encoded_size
.width(), encoded_size
.height());
587 SkImageInfo info
= SkImageInfo::Make(encoded_size
.width(),
588 encoded_size
.height(),
589 kUnknown_SkColorType
,
590 kUnpremul_SkAlphaType
);
591 skia::RefPtr
<SkData
> etc1_pixel_data
= skia::AdoptRef(
592 SkData::NewUninitialized(encoded_bytes
));
593 skia::RefPtr
<SkMallocPixelRef
> etc1_pixel_ref
= skia::AdoptRef(
594 SkMallocPixelRef::NewWithData(info
, 0, NULL
, etc1_pixel_data
.get()));
596 etc1_pixel_ref
->lockPixels();
597 bool success
= etc1_encode_image(
598 reinterpret_cast<unsigned char*>(raw_data
.getPixels()),
599 raw_data_size
.width(),
600 raw_data_size
.height(),
603 reinterpret_cast<unsigned char*>(etc1_pixel_ref
->pixels()),
604 encoded_size
.width(),
605 encoded_size
.height());
606 etc1_pixel_ref
->setImmutable();
607 etc1_pixel_ref
->unlockPixels();
610 compressed_data
= etc1_pixel_ref
;
611 content_size
= raw_data_size
;
615 content::BrowserThread::PostTask(
616 content::BrowserThread::UI
,
618 base::Bind(post_compression_task
, compressed_data
, content_size
));
621 void ThumbnailStore::PostCompressionTask(
623 const base::Time
& time_stamp
,
625 skia::RefPtr
<SkPixelRef
> compressed_data
,
626 const gfx::Size
& content_size
) {
627 compression_tasks_count_
--;
628 if (!compressed_data
) {
629 RemoveOnMatchedTimeStamp(tab_id
, time_stamp
);
633 Thumbnail
* thumbnail
= cache_
.Get(tab_id
);
635 if (thumbnail
->time_stamp() != time_stamp
)
637 thumbnail
->SetCompressedBitmap(compressed_data
, content_size
);
638 thumbnail
->CreateUIResource();
640 WriteThumbnailIfNecessary(tab_id
, compressed_data
, scale
, content_size
);
645 bool ReadFromFile(base::File
& file
,
646 gfx::Size
* out_content_size
,
648 skia::RefPtr
<SkPixelRef
>* out_pixels
) {
653 if (!ReadBigEndianFromFile(file
, &key
))
656 if (key
!= kCompressedKey
)
659 int content_width
= 0;
660 if (!ReadBigEndianFromFile(file
, &content_width
) || content_width
<= 0)
663 int content_height
= 0;
664 if (!ReadBigEndianFromFile(file
, &content_height
) || content_height
<= 0)
667 out_content_size
->SetSize(content_width
, content_height
);
670 int header_bytes_read
= 0;
671 unsigned char etc1_buffer
[ETC_PKM_HEADER_SIZE
];
672 header_bytes_read
= file
.ReadAtCurrentPos(
673 reinterpret_cast<char*>(etc1_buffer
),
674 ETC_PKM_HEADER_SIZE
);
675 if (header_bytes_read
!= ETC_PKM_HEADER_SIZE
)
678 if (!etc1_pkm_is_valid(etc1_buffer
))
682 raw_width
= etc1_pkm_get_width(etc1_buffer
);
687 raw_height
= etc1_pkm_get_height(etc1_buffer
);
691 // Do some simple sanity check validation. We can't have thumbnails larger
692 // than the max display size of the screen. We also can't have etc1 texture
693 // data larger than the next power of 2 up from that.
694 gfx::DeviceDisplayInfo display_info
;
695 int max_dimension
= std::max(display_info
.GetDisplayWidth(),
696 display_info
.GetDisplayHeight());
698 if (content_width
> max_dimension
699 || content_height
> max_dimension
700 || static_cast<size_t>(raw_width
) > NextPowerOfTwo(max_dimension
)
701 || static_cast<size_t>(raw_height
) > NextPowerOfTwo(max_dimension
)) {
705 int data_size
= etc1_get_encoded_data_size(raw_width
, raw_height
);
706 skia::RefPtr
<SkData
> etc1_pixel_data
=
707 skia::AdoptRef(SkData::NewUninitialized(data_size
));
709 int pixel_bytes_read
= file
.ReadAtCurrentPos(
710 reinterpret_cast<char*>(etc1_pixel_data
->writable_data()),
713 if (pixel_bytes_read
!= data_size
)
716 SkImageInfo info
= SkImageInfo::Make(raw_width
,
718 kUnknown_SkColorType
,
719 kUnpremul_SkAlphaType
);
721 *out_pixels
= skia::AdoptRef(
722 SkMallocPixelRef::NewWithData(info
,
725 etc1_pixel_data
.get()));
727 int extra_data_version
= 0;
728 if (!ReadBigEndianFromFile(file
, &extra_data_version
))
732 if (extra_data_version
== 1) {
733 if (!ReadBigEndianFloatFromFile(file
, out_scale
))
736 if (*out_scale
== 0.f
)
739 *out_scale
= 1.f
/ *out_scale
;
745 }// anonymous namespace
747 void ThumbnailStore::ReadTask(
749 const base::FilePath
& file_path
,
750 const base::Callback
<
751 void(skia::RefPtr
<SkPixelRef
>, float, const gfx::Size
&)>&
753 gfx::Size content_size
;
755 skia::RefPtr
<SkPixelRef
> compressed_data
;
757 if (base::PathExists(file_path
)) {
758 base::File
file(file_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
761 bool valid_contents
= ReadFromFile(file
,
767 if (!valid_contents
) {
768 content_size
.SetSize(0, 0);
770 compressed_data
.clear();
771 base::DeleteFile(file_path
, false);
776 base::WorkerPool::PostTask(
778 base::Bind(post_read_task
, compressed_data
, scale
, content_size
),
781 content::BrowserThread::PostTask(
782 content::BrowserThread::UI
,
784 base::Bind(post_read_task
, compressed_data
, scale
, content_size
));
788 void ThumbnailStore::PostReadTask(TabId tab_id
,
789 skia::RefPtr
<SkPixelRef
> compressed_data
,
791 const gfx::Size
& content_size
) {
792 read_in_progress_
= false;
794 TabIdList::iterator iter
=
795 std::find(read_queue_
.begin(), read_queue_
.end(), tab_id
);
796 if (iter
== read_queue_
.end()) {
801 read_queue_
.erase(iter
);
803 if (!cache_
.Get(tab_id
) && compressed_data
) {
804 ThumbnailMetaDataMap::iterator meta_iter
=
805 thumbnail_meta_data_
.find(tab_id
);
806 base::Time time_stamp
= base::Time::Now();
807 if (meta_iter
!= thumbnail_meta_data_
.end())
808 time_stamp
= meta_iter
->second
.capture_time();
810 MakeSpaceForNewItemIfNecessary(tab_id
);
811 scoped_ptr
<Thumbnail
> thumbnail
= Thumbnail::Create(
812 tab_id
, time_stamp
, scale
, ui_resource_provider_
, this);
813 thumbnail
->SetCompressedBitmap(compressed_data
,
815 if (kPreferCPUMemory
)
816 thumbnail
->CreateUIResource();
818 cache_
.Put(tab_id
, thumbnail
.Pass());
819 NotifyObserversOfThumbnailRead(tab_id
);
825 void ThumbnailStore::NotifyObserversOfThumbnailRead(TabId tab_id
) {
827 ThumbnailStoreObserver
, observers_
, OnFinishedThumbnailRead(tab_id
));
830 void ThumbnailStore::RemoveOnMatchedTimeStamp(TabId tab_id
,
831 const base::Time
& time_stamp
) {
832 // We remove the cached version if it matches the tab_id and the time_stamp.
833 Thumbnail
* thumbnail
= cache_
.Get(tab_id
);
834 Thumbnail
* approx_thumbnail
= approximation_cache_
.Get(tab_id
);
835 if ((thumbnail
&& thumbnail
->time_stamp() == time_stamp
) ||
836 (approx_thumbnail
&& approx_thumbnail
->time_stamp() == time_stamp
)) {
842 void ThumbnailStore::DecompressionTask(
843 const base::Callback
<void(bool, SkBitmap
)>&
844 post_decompression_callback
,
845 skia::RefPtr
<SkPixelRef
> compressed_data
,
847 const gfx::Size
& content_size
) {
848 SkBitmap raw_data_small
;
849 bool success
= false;
851 if (compressed_data
.get()) {
852 gfx::Size buffer_size
= gfx::Size(compressed_data
->info().width(),
853 compressed_data
->info().height());
856 raw_data
.allocPixels(SkImageInfo::Make(buffer_size
.width(),
857 buffer_size
.height(),
858 kRGBA_8888_SkColorType
,
859 kOpaque_SkAlphaType
));
860 SkAutoLockPixels
raw_data_lock(raw_data
);
861 compressed_data
->lockPixels();
862 success
= etc1_decode_image(
863 reinterpret_cast<unsigned char*>(compressed_data
->pixels()),
864 reinterpret_cast<unsigned char*>(raw_data
.getPixels()),
866 buffer_size
.height(),
867 raw_data
.bytesPerPixel(),
868 raw_data
.rowBytes());
869 compressed_data
->unlockPixels();
870 raw_data
.setImmutable();
873 // Leave raw_data_small empty for consistency with other failure modes.
874 } else if (content_size
== buffer_size
) {
875 // Shallow copy the pixel reference.
876 raw_data_small
= raw_data
;
878 // The content size is smaller than the buffer size (likely because of
879 // a power-of-two rounding), so deep copy the bitmap.
880 raw_data_small
.allocPixels(SkImageInfo::Make(content_size
.width(),
881 content_size
.height(),
882 kRGBA_8888_SkColorType
,
883 kOpaque_SkAlphaType
));
884 SkAutoLockPixels
raw_data_small_lock(raw_data_small
);
885 SkCanvas
small_canvas(raw_data_small
);
886 small_canvas
.drawBitmap(raw_data
, 0, 0);
887 raw_data_small
.setImmutable();
891 content::BrowserThread::PostTask(
892 content::BrowserThread::UI
,
894 base::Bind(post_decompression_callback
, success
, raw_data_small
));
897 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData() {
900 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData(
901 const base::Time
& current_time
,
903 : capture_time_(current_time
), url_(url
) {
906 std::pair
<SkBitmap
, float> ThumbnailStore::CreateApproximation(
907 const SkBitmap
& bitmap
,
909 DCHECK(!bitmap
.empty());
911 SkAutoLockPixels
bitmap_lock(bitmap
);
912 float new_scale
= 1.f
/ kApproximationScaleFactor
;
914 gfx::Size dst_size
= gfx::ToFlooredSize(
915 gfx::ScaleSize(gfx::Size(bitmap
.width(), bitmap
.height()), new_scale
));
917 dst_bitmap
.allocPixels(SkImageInfo::Make(dst_size
.width(),
919 bitmap
.info().fColorType
,
920 bitmap
.info().fAlphaType
));
921 dst_bitmap
.eraseColor(0);
922 SkAutoLockPixels
dst_bitmap_lock(dst_bitmap
);
924 SkCanvas
canvas(dst_bitmap
);
925 canvas
.scale(new_scale
, new_scale
);
926 canvas
.drawBitmap(bitmap
, 0, 0, NULL
);
927 dst_bitmap
.setImmutable();
929 return std::make_pair(dst_bitmap
, new_scale
* scale
);