Chromium Code Reviews| Index: chrome/browser/android/thumbnail/thumbnail_cache.cc |
| diff --git a/chrome/browser/android/thumbnail/thumbnail_cache.cc b/chrome/browser/android/thumbnail/thumbnail_cache.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..7156e800bbab4d996567602ce8f38ed720fe55e4 |
| --- /dev/null |
| +++ b/chrome/browser/android/thumbnail/thumbnail_cache.cc |
| @@ -0,0 +1,660 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/android/thumbnail/thumbnail_cache.h" |
| + |
| +#include <algorithm> |
| +#include <cmath> |
| + |
| +#include "base/file_util.h" |
| +#include "base/files/file.h" |
| +#include "base/files/file_enumerator.h" |
| +#include "base/files/file_path.h" |
| +#include "base/strings/string_number_conversions.h" |
| +#include "base/time/time.h" |
| +#include "content/public/browser/android/ui_resource_provider.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "third_party/android_opengl/etc1/etc1.h" |
| +#include "third_party/skia/include/core/SkBitmap.h" |
| +#include "third_party/skia/include/core/SkCanvas.h" |
| +#include "third_party/skia/include/core/SkData.h" |
| +#include "third_party/skia/include/core/SkMallocPixelRef.h" |
| +#include "third_party/skia/include/core/SkPixelRef.h" |
| +#include "ui/gfx/geometry/size_conversions.h" |
| + |
| +namespace { |
| + |
| +const float kApproximationScaleFactor = 4.f; |
| +const base::TimeDelta kCaptureMinRequestTimeMs( |
| + base::TimeDelta::FromMilliseconds(1000)); |
| +const Thumbnail kInvalidThumbnail; |
| + |
| +const int kCompressedKey = 0xABABABAB; |
| +const int kDecompressedKey = 0xCDCDCDCD; |
| + |
| +// ETC1 texture sizes are multiples of four. |
| +size_t NextETC1Size(size_t s) { |
| + return (s / 4 + (s % 4 ? 1 : 0)) * 4; |
| +} |
| + |
| +gfx::Size GetEncodedSize(const gfx::Size& bitmap_size) { |
| + return gfx::Size(NextETC1Size(bitmap_size.width()), |
| + NextETC1Size(bitmap_size.height())); |
| +} |
| + |
| +} // anonymous namespace |
| + |
| +ThumbnailCache::ThumbnailCache(const std::string& disk_cache_path_str, |
| + size_t default_cache_size, |
| + size_t approximation_cache_size, |
| + size_t compression_queue_max_size, |
| + size_t write_queue_max_size, |
| + bool use_approximation_thumbnail) |
| + : disk_cache_path_(disk_cache_path_str), |
| + compression_queue_max_size_(compression_queue_max_size), |
| + write_queue_max_size_(write_queue_max_size), |
| + use_approximation_thumbnail_(use_approximation_thumbnail), |
| + compression_tasks_count_(0), |
| + write_tasks_count_(0), |
| + cache_(default_cache_size), |
| + approximation_cache_(approximation_cache_size), |
| + ui_resource_provider_(NULL), |
| + compression_thread_("thumbnail_compression"), |
| + weak_factory_(this) { |
| + compression_thread_.Start(); |
| +} |
| + |
| +ThumbnailCache::~ThumbnailCache() { |
| + compression_thread_.Stop(); |
| + SetUIResourceProvider(NULL); |
| +} |
| + |
| +void ThumbnailCache::SetUIResourceProvider( |
| + content::UIResourceProvider* ui_resource_provider) { |
| + if (ui_resource_provider_ == ui_resource_provider) |
| + return; |
| + |
| + approximation_cache_.Clear(); |
| + cache_.Clear(); |
| + |
| + ui_resource_provider_ = ui_resource_provider; |
| +} |
| + |
| +void ThumbnailCache::AddThumbnailCacheObserver( |
| + ThumbnailCacheObserver* observer) { |
| + if (!observers_.HasObserver(observer)) |
| + observers_.AddObserver(observer); |
| +} |
| + |
| +void ThumbnailCache::RemoveThumbnailCacheObserver( |
| + ThumbnailCacheObserver* observer) { |
| + if (observers_.HasObserver(observer)) |
| + observers_.RemoveObserver(observer); |
| +} |
| + |
| +void ThumbnailCache::Put(TabId tab_id, |
| + const SkBitmap& bitmap, |
| + float thumbnail_scale) { |
| + if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0) |
| + return; |
| + |
| + DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end()); |
| + |
| + base::Time time_stamp = thumbnail_meta_data_[tab_id].capture_time(); |
| + |
| + Thumbnail thumbnail( |
| + tab_id, time_stamp, thumbnail_scale, ui_resource_provider_, this); |
| + thumbnail.SetBitmap(bitmap); |
| + |
| + RemoveFromReadQueue(tab_id); |
| + MakeSpaceForNewItemIfNecessary(tab_id); |
| + 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_
|
| + |
| + if (use_approximation_thumbnail_) { |
| + std::pair<SkBitmap, float> approximation = |
| + CreateApproximation(bitmap, thumbnail_scale); |
| + Thumbnail approx_thumbnail(tab_id, |
| + time_stamp, |
| + approximation.second, |
| + ui_resource_provider_, |
| + this); |
| + approx_thumbnail.SetBitmap(approximation.first); |
| + approximation_cache_.Put(tab_id, approx_thumbnail); |
| + } |
| + CompressThumbnailIfNecessary(tab_id, bitmap, thumbnail_scale, time_stamp); |
| +} |
| + |
| +void ThumbnailCache::Remove(TabId tab_id) { |
| + cache_.Remove(tab_id); |
| + approximation_cache_.Remove(tab_id); |
| + thumbnail_meta_data_.erase(tab_id); |
| + RemoveFromDisk(tab_id); |
| + RemoveFromReadQueue(tab_id); |
| +} |
| + |
| +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.
|
| + if (cache_.Contains(tab_id)) { |
| + Thumbnail& thumbnail = cache_.Get(tab_id); |
| + thumbnail.CreateUIResource(); |
| + return thumbnail; |
| + } |
| + |
| + 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
|
| + std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) != |
| + visible_ids_.end() && |
| + std::find(read_queue_.begin(), read_queue_.end(), tab_id) == |
| + read_queue_.end()) { |
| + read_queue_.push_back(tab_id); |
| + ReadNextThumbnail(); |
| + } |
| + |
| + if (approximation_cache_.Contains(tab_id)) { |
| + Thumbnail& thumbnail = approximation_cache_.Get(tab_id); |
| + thumbnail.CreateUIResource(); |
| + return thumbnail; |
| + } |
| + |
| + return kInvalidThumbnail; |
| +} |
| + |
| +void ThumbnailCache::RemoveFromDiskAtAndAboveId(TabId min_id) { |
| + base::Closure remove_task = |
| + base::Bind(&ThumbnailCache::RemoveFromDiskAtAndAboveIdTask, |
| + disk_cache_path_, |
| + min_id); |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::FILE, FROM_HERE, remove_task); |
| +} |
| + |
| +void ThumbnailCache::InvalidateThumbnailIfChanged(TabId tab_id, |
| + const GURL& url) { |
| + ThumbnailMetaDataMap::iterator meta_data_iter = |
| + thumbnail_meta_data_.find(tab_id); |
| + if (meta_data_iter == thumbnail_meta_data_.end()) { |
| + thumbnail_meta_data_[tab_id] = ThumbnailMetaData(base::Time(), url); |
| + } else if (meta_data_iter->second.url() != url) { |
| + Remove(tab_id); |
| + } |
| +} |
| + |
| +bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(TabId tab_id, |
| + const GURL& url) { |
| + base::Time current_time = base::Time::Now(); |
| + ThumbnailMetaDataMap::iterator meta_data_iter = |
| + thumbnail_meta_data_.find(tab_id); |
| + if (meta_data_iter != thumbnail_meta_data_.end() && |
| + meta_data_iter->second.url() == url && |
| + (current_time - meta_data_iter->second.capture_time()) < |
| + kCaptureMinRequestTimeMs) { |
| + return false; |
| + } |
| + |
| + thumbnail_meta_data_[tab_id] = ThumbnailMetaData(current_time, url); |
| + return true; |
| +} |
| + |
| +void ThumbnailCache::UpdateVisibleIds(const TabIdList& priority) { |
| + if (priority.empty()) { |
| + visible_ids_.clear(); |
| + return; |
| + } |
| + |
| + size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize()); |
| + if (visible_ids_.size() == ids_size) { |
| + // Early out if called with the same input as last time (We only care |
| + // about the first mCache.MaximumCacheSize() entries). |
| + bool lists_differ = false; |
| + TabIdList::const_iterator visible_iter = visible_ids_.begin(); |
| + TabIdList::const_iterator priority_iter = priority.begin(); |
| + while (visible_iter != visible_ids_.end() && |
| + priority_iter != priority.end()) { |
| + if (*priority_iter != *visible_iter) { |
| + lists_differ = true; |
| + break; |
| + } |
| + visible_iter++; |
| + priority_iter++; |
| + } |
| + |
| + if (!lists_differ) |
| + return; |
| + } |
| + |
| + read_queue_.clear(); |
| + visible_ids_.clear(); |
| + size_t count = 0; |
| + TabIdList::const_iterator iter = priority.begin(); |
| + while (iter != priority.end() && count < ids_size) { |
| + TabId tab_id = *iter; |
| + visible_ids_.push_back(tab_id); |
| + if (!cache_.Contains(tab_id) && |
| + std::find(read_queue_.begin(), read_queue_.end(), tab_id) == |
| + read_queue_.end()) { |
| + read_queue_.push_back(tab_id); |
| + } |
| + iter++; |
| + count++; |
| + } |
| + |
| + ReadNextThumbnail(); |
| +} |
| + |
| +void ThumbnailCache::RemoveFromDisk(TabId tab_id) { |
| + if (written_thumbnails_.find(tab_id) == written_thumbnails_.end()) |
| + return; |
| + |
| + written_thumbnails_.erase(tab_id); |
| + |
| + base::FilePath file_path = GetFilePath(tab_id); |
| + base::Closure task = |
| + base::Bind(&ThumbnailCache::RemoveFromDiskTask, file_path); |
| + content::BrowserThread::PostTask( |
| + content::BrowserThread::FILE, FROM_HERE, task); |
| +} |
| + |
| +void ThumbnailCache::RemoveFromDiskTask(const base::FilePath& file_path) { |
| + DCHECK(base::PathExists(file_path)); |
| + base::DeleteFile(file_path, false); |
| +} |
| + |
| +void ThumbnailCache::RemoveFromDiskAtAndAboveIdTask( |
| + const base::FilePath& dir_path, |
| + TabId min_id) { |
| + base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES); |
| + while (true) { |
| + base::FilePath path = enumerator.Next(); |
| + if (path.empty()) |
| + break; |
| + base::FileEnumerator::FileInfo info = enumerator.GetInfo(); |
| + TabId tab_id; |
| + bool success = base::StringToInt(info.GetName().value(), &tab_id); |
| + if (success && tab_id >= min_id) |
| + base::DeleteFile(path, false); |
| + } |
| +} |
| + |
| +void ThumbnailCache::WriteThumbnailIfNecessary( |
| + TabId tab_id, |
| + skia::RefPtr<SkPixelRef> compressed_data, |
| + float scale, |
| + const gfx::Size& content_size) { |
| + 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
|
| + return; |
| + |
| + write_tasks_count_++; |
| + |
| + scoped_refptr<WriteTaskResult> result = |
| + make_scoped_refptr(new WriteTaskResult()); |
| + base::Closure write_task = base::Bind(&ThumbnailCache::WriteTask, |
| + GetFilePath(tab_id), |
| + 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
|
| + scale, |
| + content_size, |
| + result); |
| + base::Closure post_write_task = base::Bind(&ThumbnailCache::PostWriteTask, |
| + weak_factory_.GetWeakPtr(), |
| + tab_id, |
| + result); |
| + |
| + content::BrowserThread::PostTaskAndReply( |
| + content::BrowserThread::FILE, FROM_HERE, write_task, post_write_task); |
| +} |
| + |
| +void ThumbnailCache::CompressThumbnailIfNecessary(TabId tab_id, |
| + const SkBitmap& bitmap, |
| + float scale, |
| + base::Time& time_stamp) { |
| + 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 :)
|
| + return; |
| + |
| + compression_tasks_count_++; |
| + |
| + scoped_refptr<CompressedDataTransport> transport = |
| + make_scoped_refptr(new CompressedDataTransport()); |
| + base::Closure compression_task = |
| + base::Bind(&ThumbnailCache::CompressionTask, bitmap, transport); |
| + base::Closure post_compression_task = |
| + base::Bind(&ThumbnailCache::PostCompressionTask, |
| + weak_factory_.GetWeakPtr(), |
| + tab_id, |
| + scale, |
| + time_stamp, |
| + 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
|
| + |
| + DCHECK(compression_thread_.message_loop_proxy()); |
| + compression_thread_.message_loop_proxy()->PostTaskAndReply( |
| + FROM_HERE, compression_task, post_compression_task); |
| +} |
| + |
| +void ThumbnailCache::ReadNextThumbnail() { |
| + 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
|
| + return; |
| + |
| + TabId tab_id = read_queue_.front(); |
| + |
| + scoped_refptr<CompressedDataTransport> transport = |
| + make_scoped_refptr(new CompressedDataTransport()); |
| + base::FilePath file_path = GetFilePath(tab_id); |
| + base::Closure read_task = |
| + base::Bind(&ThumbnailCache::ReadTask, file_path, transport); |
| + base::Closure post_read_task = base::Bind(&ThumbnailCache::PostReadTask, |
| + weak_factory_.GetWeakPtr(), |
| + tab_id, |
| + transport); |
| + |
| + content::BrowserThread::PostTaskAndReply( |
| + content::BrowserThread::FILE, FROM_HERE, read_task, post_read_task); |
| +} |
| + |
| +void ThumbnailCache::MakeSpaceForNewItemIfNecessary(TabId tab_id) { |
| + if (cache_.Contains(tab_id) || |
| + std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) == |
| + visible_ids_.end() || |
| + cache_.size() < cache_.MaximumCacheSize()) { |
| + return; |
| + } |
| + |
| + TabId key_to_remove; |
| + bool found_key_to_remove = false; |
| + |
| + // 1. Find a cached item not in this list |
| + for (ExpiringThumbnailCache::iterator iter = cache_.begin(); |
| + iter != cache_.end(); |
| + iter++) { |
| + if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) == |
| + visible_ids_.end()) { |
| + key_to_remove = iter->first; |
| + found_key_to_remove = true; |
| + break; |
| + } |
| + } |
| + |
| + if (!found_key_to_remove) { |
| + // 2. Find the least important id we can remove. |
| + for (TabIdList::reverse_iterator riter = visible_ids_.rbegin(); |
| + riter != visible_ids_.rend(); |
| + riter++) { |
| + if (cache_.Contains(*riter)) { |
| + key_to_remove = *riter; |
| + break; |
| + found_key_to_remove = true; |
| + } |
| + } |
| + } |
| + |
| + if (found_key_to_remove) |
| + cache_.Remove(key_to_remove); |
| +} |
| + |
| +void ThumbnailCache::RemoveFromReadQueue(TabId tab_id) { |
| + TabIdList::iterator read_iter = |
| + std::find(read_queue_.begin(), read_queue_.end(), tab_id); |
| + if (read_iter != read_queue_.end()) |
| + read_queue_.erase(read_iter); |
| +} |
| + |
| +void ThumbnailCache::InvalidateCachedThumbnail(const Thumbnail& thumbnail) { |
| + TabId tab_id = thumbnail.tab_id(); |
| + base::Time time_stamp = thumbnail.time_stamp(); |
| + |
| + if (cache_.Contains(tab_id) && cache_.Get(tab_id).time_stamp() == time_stamp) |
| + cache_.Remove(tab_id); |
| + |
| + if (approximation_cache_.Contains(tab_id) && |
| + approximation_cache_.Get(tab_id).time_stamp() == time_stamp) |
| + approximation_cache_.Remove(tab_id); |
| +} |
| + |
| +base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) const { |
| + return disk_cache_path_.Append(base::IntToString(tab_id)); |
| +} |
| + |
| +void ThumbnailCache::WriteTask( |
| + const base::FilePath& file_path, |
| + skia::RefPtr<SkPixelRef> compressed_data, |
| + float scale, |
| + const gfx::Size& content_size, |
| + scoped_refptr<ThumbnailCache::WriteTaskResult> result) { |
| + DCHECK(compressed_data); |
| + DCHECK(result); |
| + |
| + base::File file(file_path, |
| + base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| + DCHECK(file.IsValid()); |
| + |
| + compressed_data->lockPixels(); |
| + bool success = true; |
| + int content_width = content_size.width(); |
| + int content_height = content_size.height(); |
| + int data_width = compressed_data->info().width(); |
| + int data_height = compressed_data->info().height(); |
| + |
| + if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey), |
| + sizeof(int)) < 0 || |
| + file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_width), |
| + sizeof(int)) < 0 || |
| + file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_height), |
| + sizeof(int)) < 0 || |
| + file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width), |
| + sizeof(int)) < 0 || |
| + file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height), |
| + sizeof(int)) < 0 || |
| + file.WriteAtCurrentPos(reinterpret_cast<const char*>(&scale), |
| + sizeof(float)) < 0) { |
| + success = false; |
| + } |
| + |
| + size_t compressed_bytes = etc1_get_encoded_data_size(data_width, data_height); |
| + if (file.WriteAtCurrentPos(reinterpret_cast<char*>(compressed_data->pixels()), |
| + compressed_bytes) < 0) |
| + success = false; |
| + |
| + compressed_data->unlockPixels(); |
| + |
| + if (!success) |
| + base::DeleteFile(file_path, false); |
| + |
| + 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.
|
| + |
| + result->SetSuccess(success); |
| +} |
| + |
| +void ThumbnailCache::PostWriteTask( |
| + TabId tab_id, |
| + scoped_refptr<ThumbnailCache::WriteTaskResult> result) { |
| + write_tasks_count_--; |
| + if (result->success()) |
| + written_thumbnails_.insert(tab_id); |
| +} |
| + |
| +void ThumbnailCache::CompressionTask( |
| + SkBitmap raw_data, |
| + scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) { |
| + if (raw_data.empty()) |
| + return; |
| + |
| + SkAutoLockPixels raw_data_lock(raw_data); |
| + gfx::Size raw_data_size(raw_data.width(), raw_data.height()); |
| + DCHECK_EQ(raw_data.config(), SkBitmap::kARGB_8888_Config); |
| + size_t pixel_size = 4; // Pixel size is 4 bytes for kARGB_8888_Config. |
| + size_t stride = pixel_size * raw_data_size.width(); |
| + |
| + gfx::Size encoded_size = GetEncodedSize(raw_data_size); |
| + size_t encoded_bytes = |
| + etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height()); |
| + SkImageInfo info = {encoded_size.width(), |
| + encoded_size.height(), |
| + kUnknown_SkColorType, |
| + kUnpremul_SkAlphaType}; |
| + skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef( |
| + SkData::NewFromMalloc(new uint8_t[encoded_bytes], encoded_bytes)); |
| + skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef( |
| + SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
| + |
| + etc1_pixel_ref->lockPixels(); |
| + bool success = etc1_encode_image( |
| + reinterpret_cast<unsigned char*>(raw_data.getPixels()), |
| + raw_data_size.width(), |
| + raw_data_size.height(), |
| + pixel_size, |
| + stride, |
| + reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()), |
| + encoded_size.width(), |
| + encoded_size.height()); |
| + etc1_pixel_ref->setImmutable(); |
| + etc1_pixel_ref->unlockPixels(); |
| + if (success) { |
| + transport->compressed_data = etc1_pixel_ref; |
| + transport->content_size = raw_data_size; |
| + } |
| +} |
| + |
| +void ThumbnailCache::PostCompressionTask( |
| + TabId tab_id, |
| + float scale, |
| + const base::Time& time_stamp, |
| + scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) { |
| + DCHECK(transport); |
| + compression_tasks_count_--; |
| + |
| + if (!transport->compressed_data) |
| + 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.
|
| + |
| + if (cache_.Contains(tab_id)) { |
| + Thumbnail& thumbnail = cache_.Get(tab_id); |
| + if (thumbnail.time_stamp() != time_stamp) |
| + return; |
| + thumbnail.SetCompressedBitmap(transport->compressed_data, |
| + transport->content_size); |
| + thumbnail.CreateUIResource(); |
| + } |
| + WriteThumbnailIfNecessary( |
| + tab_id, transport->compressed_data, scale, transport->content_size); |
| +} |
| + |
| +void ThumbnailCache::ReadTask( |
| + const base::FilePath& file_path, |
| + scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) { |
| + DCHECK(transport); |
| + if (!base::PathExists(file_path)) |
| + return; |
| + |
| + base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| + DCHECK(file.IsValid()); |
| + |
| + int key; |
| + bool success = true; |
| + if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 || |
| + key != kCompressedKey) |
| + success = false; |
| + |
| + int width, height; |
| + if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < 0 || |
| + file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < 0) |
| + success = false; |
| + gfx::Size content_size(width, height); |
| + |
| + if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < 0 || |
| + file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < 0) |
| + success = false; |
| + gfx::Size data_size(width, height); |
| + |
| + float scale = 0.f; |
| + if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale), sizeof(float)) < 0) |
| + success = false; |
| + |
| + size_t compressed_bytes = |
| + etc1_get_encoded_data_size(data_size.width(), data_size.height()); |
| + |
| + SkImageInfo info = {data_size.width(), |
| + data_size.height(), |
| + kUnknown_SkColorType, |
| + kUnpremul_SkAlphaType}; |
| + |
| + scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]); |
| + if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()), |
| + compressed_bytes) < 0) |
| + success = false; |
| + |
| + skia::RefPtr<SkData> etc1_pixel_data = |
| + skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); |
| + skia::RefPtr<SkPixelRef> compressed_data = skia::AdoptRef( |
| + SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
| + |
| + if (success) { |
| + transport->compressed_data = compressed_data; |
| + transport->scale = scale; |
| + transport->content_size = content_size; |
| + } else { |
| + base::DeleteFile(file_path, false); |
| + } |
| + |
| + file.Close(); |
| +} |
| + |
| +void ThumbnailCache::PostReadTask( |
| + TabId tab_id, |
| + scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) { |
| + TabIdList::iterator iter = |
| + std::find(read_queue_.begin(), read_queue_.end(), tab_id); |
| + if (iter == read_queue_.end()) |
| + return; |
| + |
| + read_queue_.erase(iter); |
| + |
| + if (!cache_.Contains(tab_id) && transport->compressed_data) { |
| + DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end()); |
| + |
| + MakeSpaceForNewItemIfNecessary(tab_id); |
| + Thumbnail thumbnail(tab_id, |
| + thumbnail_meta_data_[tab_id].capture_time(), |
| + transport->scale, |
| + ui_resource_provider_, |
| + this); |
| + thumbnail.SetCompressedBitmap(transport->compressed_data, |
| + transport->content_size); |
| + 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
|
| + NotifyObserversOfThumbnailRead(tab_id); |
| + } |
| + |
| + ReadNextThumbnail(); |
| +} |
| + |
| +void ThumbnailCache::NotifyObserversOfThumbnailRead(TabId tab_id) { |
| + FOR_EACH_OBSERVER( |
| + ThumbnailCacheObserver, observers_, OnFinishedThumbnailRead(tab_id)); |
| +} |
| + |
| +ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData() { |
| +} |
| + |
| +ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData( |
| + const base::Time& current_time, |
| + const GURL& url) |
| + : capture_time_(current_time), url_(url) { |
| +} |
| + |
| +std::pair<SkBitmap, float> ThumbnailCache::CreateApproximation( |
| + const SkBitmap& bitmap, |
| + float scale) { |
| + DCHECK(!bitmap.empty()); |
| + DCHECK_GT(scale, 0); |
| + SkAutoLockPixels bitmap_lock(bitmap); |
| + float new_scale = 1.f / kApproximationScaleFactor; |
| + |
| + gfx::Size dst_size = gfx::ToFlooredSize( |
| + gfx::ScaleSize(gfx::Size(bitmap.width(), bitmap.height()), new_scale)); |
| + SkBitmap dst_bitmap; |
| + dst_bitmap.setConfig(bitmap.getConfig(), dst_size.width(), dst_size.height()); |
| + dst_bitmap.allocPixels(); |
| + dst_bitmap.eraseColor(0); |
| + SkAutoLockPixels dst_bitmap_lock(dst_bitmap); |
| + |
| + SkCanvas canvas(dst_bitmap); |
| + canvas.scale(new_scale, new_scale); |
| + canvas.drawBitmap(bitmap, 0, 0, NULL); |
| + dst_bitmap.setImmutable(); |
| + |
| + return std::make_pair(dst_bitmap, new_scale * scale); |
| +} |