Index: chrome/browser/android/thumbnail/thumbnail_store.cc |
diff --git a/chrome/browser/android/thumbnail/thumbnail_store.cc b/chrome/browser/android/thumbnail/thumbnail_store.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c4b215960839a3f5ec22ce310f53f35a7a7e1c02 |
--- /dev/null |
+++ b/chrome/browser/android/thumbnail/thumbnail_store.cc |
@@ -0,0 +1,687 @@ |
+// 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_store.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 int kCompressedKey = 0xABABABAB; |
+ |
+// Indicates whether we prefer to have more free CPU memory over GPU memory. |
+const bool kPreferCPUMemory = true; |
+ |
+// 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 |
+ |
+ThumbnailStore::ThumbnailStore(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), |
+ read_in_progress_(false), |
+ cache_(default_cache_size), |
+ approximation_cache_(approximation_cache_size), |
+ ui_resource_provider_(NULL), |
+ compression_thread_("thumbnail_compression"), |
+ weak_factory_(this) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ compression_thread_.Start(); |
+} |
+ |
+ThumbnailStore::~ThumbnailStore() { |
+ compression_thread_.Stop(); |
+ SetUIResourceProvider(NULL); |
+} |
+ |
+void ThumbnailStore::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 ThumbnailStore::AddThumbnailStoreObserver( |
+ ThumbnailStoreObserver* observer) { |
+ if (!observers_.HasObserver(observer)) |
+ observers_.AddObserver(observer); |
+} |
+ |
+void ThumbnailStore::RemoveThumbnailStoreObserver( |
+ ThumbnailStoreObserver* observer) { |
+ if (observers_.HasObserver(observer)) |
+ observers_.RemoveObserver(observer); |
+} |
+ |
+void ThumbnailStore::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(); |
+ scoped_ptr<Thumbnail> thumbnail = Thumbnail::Create( |
+ tab_id, time_stamp, thumbnail_scale, ui_resource_provider_, this); |
+ thumbnail->SetBitmap(bitmap); |
+ |
+ RemoveFromReadQueue(tab_id); |
+ MakeSpaceForNewItemIfNecessary(tab_id); |
+ cache_.Put(tab_id, thumbnail.Pass()); |
+ |
+ if (use_approximation_thumbnail_) { |
+ std::pair<SkBitmap, float> approximation = |
+ CreateApproximation(bitmap, thumbnail_scale); |
+ scoped_ptr<Thumbnail> approx_thumbnail = Thumbnail::Create( |
+ tab_id, time_stamp, approximation.second, ui_resource_provider_, this); |
+ approx_thumbnail->SetBitmap(approximation.first); |
+ approximation_cache_.Put(tab_id, approx_thumbnail.Pass()); |
+ } |
+ CompressThumbnailIfNecessary(tab_id, time_stamp, bitmap, thumbnail_scale); |
+} |
+ |
+void ThumbnailStore::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); |
+} |
+ |
+Thumbnail* ThumbnailStore::Get(TabId tab_id, bool force_disk_read) { |
+ Thumbnail* thumbnail = cache_.Get(tab_id); |
+ if (thumbnail) { |
+ thumbnail->CreateUIResource(); |
+ return thumbnail; |
+ } |
+ |
+ if (force_disk_read && |
+ 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(); |
+ } |
+ |
+ thumbnail = approximation_cache_.Get(tab_id); |
+ if (thumbnail) { |
+ thumbnail->CreateUIResource(); |
+ return thumbnail; |
+ } |
+ |
+ return NULL; |
+} |
+ |
+void ThumbnailStore::RemoveFromDiskAtAndAboveId(TabId min_id) { |
+ base::Closure remove_task = |
+ base::Bind(&ThumbnailStore::RemoveFromDiskAtAndAboveIdTask, |
+ disk_cache_path_, |
+ min_id); |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::FILE, FROM_HERE, remove_task); |
+} |
+ |
+void ThumbnailStore::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 ThumbnailStore::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 ThumbnailStore::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_.Get(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 ThumbnailStore::RemoveFromDisk(TabId tab_id) { |
+ base::FilePath file_path = GetFilePath(tab_id); |
+ base::Closure task = |
+ base::Bind(&ThumbnailStore::RemoveFromDiskTask, file_path); |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::FILE, FROM_HERE, task); |
+} |
+ |
+void ThumbnailStore::RemoveFromDiskTask(const base::FilePath& file_path) { |
+ if (base::PathExists(file_path)) |
+ base::DeleteFile(file_path, false); |
+} |
+ |
+void ThumbnailStore::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 ThumbnailStore::WriteThumbnailIfNecessary( |
+ TabId tab_id, |
+ skia::RefPtr<SkPixelRef> compressed_data, |
+ float scale, |
+ const gfx::Size& content_size) { |
+ if (write_tasks_count_ >= write_queue_max_size_) |
+ return; |
+ |
+ write_tasks_count_++; |
+ |
+ base::Callback<void()> post_write_task = |
+ base::Bind(&ThumbnailStore::PostWriteTask, weak_factory_.GetWeakPtr()); |
+ content::BrowserThread::PostTask(content::BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind(&ThumbnailStore::WriteTask, |
+ GetFilePath(tab_id), |
+ compressed_data, |
+ scale, |
+ content_size, |
+ post_write_task)); |
+} |
+ |
+void ThumbnailStore::CompressThumbnailIfNecessary( |
+ TabId tab_id, |
+ const base::Time& time_stamp, |
+ const SkBitmap& bitmap, |
+ float scale) { |
+ if (compression_tasks_count_ >= compression_queue_max_size_) { |
+ RemoveOnMatchedTimeStamp(tab_id, time_stamp); |
+ return; |
+ } |
+ |
+ compression_tasks_count_++; |
+ DCHECK(compression_thread_.message_loop_proxy()); |
+ |
+ base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)> |
+ post_compression_task = base::Bind(&ThumbnailStore::PostCompressionTask, |
+ weak_factory_.GetWeakPtr(), |
+ tab_id, |
+ time_stamp, |
+ scale); |
+ |
+ compression_thread_.message_loop_proxy()->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &ThumbnailStore::CompressionTask, bitmap, post_compression_task)); |
+} |
+ |
+void ThumbnailStore::ReadNextThumbnail() { |
+ if (read_queue_.empty() || read_in_progress_) |
+ return; |
+ |
+ TabId tab_id = read_queue_.front(); |
+ read_in_progress_ = true; |
+ |
+ base::FilePath file_path = GetFilePath(tab_id); |
+ |
+ base::Callback<void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)> |
+ post_read_task = base::Bind( |
+ &ThumbnailStore::PostReadTask, weak_factory_.GetWeakPtr(), tab_id); |
+ |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::FILE, |
+ FROM_HERE, |
+ base::Bind(&ThumbnailStore::ReadTask, file_path, post_read_task)); |
+} |
+ |
+void ThumbnailStore::MakeSpaceForNewItemIfNecessary(TabId tab_id) { |
+ if (cache_.Get(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_.Get(*riter)) { |
+ key_to_remove = *riter; |
+ break; |
+ found_key_to_remove = true; |
+ } |
+ } |
+ } |
+ |
+ if (found_key_to_remove) |
+ cache_.Remove(key_to_remove); |
+} |
+ |
+void ThumbnailStore::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 ThumbnailStore::InvalidateCachedThumbnail(Thumbnail* thumbnail) { |
+ DCHECK(thumbnail); |
+ TabId tab_id = thumbnail->tab_id(); |
+ cc::UIResourceId uid = thumbnail->ui_resource_id(); |
+ |
+ Thumbnail* cached_thumbnail = cache_.Get(tab_id); |
+ if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid) |
+ cache_.Remove(tab_id); |
+ |
+ cached_thumbnail = approximation_cache_.Get(tab_id); |
+ if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid) |
+ approximation_cache_.Remove(tab_id); |
+} |
+ |
+base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const { |
+ return disk_cache_path_.Append(base::IntToString(tab_id)); |
+} |
+ |
+void ThumbnailStore::WriteTask(const base::FilePath& file_path, |
+ skia::RefPtr<SkPixelRef> compressed_data, |
+ float scale, |
+ const gfx::Size& content_size, |
+ const base::Callback<void()>& post_write_task) { |
+ DCHECK(compressed_data); |
+ |
+ 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(); |
+ |
+ file.Close(); |
+ |
+ if (!success) |
+ base::DeleteFile(file_path, false); |
+ |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, FROM_HERE, post_write_task); |
+} |
+ |
+void ThumbnailStore::PostWriteTask() { |
+ write_tasks_count_--; |
+} |
+ |
+void ThumbnailStore::CompressionTask( |
+ SkBitmap raw_data, |
+ const base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)>& |
+ post_compression_task) { |
+ skia::RefPtr<SkPixelRef> compressed_data; |
+ gfx::Size content_size; |
+ |
+ if (!raw_data.empty()) { |
+ SkAutoLockPixels raw_data_lock(raw_data); |
+ gfx::Size raw_data_size(raw_data.width(), raw_data.height()); |
+ 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) { |
+ compressed_data = etc1_pixel_ref; |
+ content_size = raw_data_size; |
+ } |
+ } |
+ |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(post_compression_task, compressed_data, content_size)); |
+} |
+ |
+void ThumbnailStore::PostCompressionTask( |
+ TabId tab_id, |
+ const base::Time& time_stamp, |
+ float scale, |
+ skia::RefPtr<SkPixelRef> compressed_data, |
+ const gfx::Size& content_size) { |
+ compression_tasks_count_--; |
+ if (!compressed_data) { |
+ RemoveOnMatchedTimeStamp(tab_id, time_stamp); |
+ return; |
+ } |
+ |
+ Thumbnail* thumbnail = cache_.Get(tab_id); |
+ if (thumbnail) { |
+ if (thumbnail->time_stamp() != time_stamp) |
+ return; |
+ thumbnail->SetCompressedBitmap(compressed_data, content_size); |
+ thumbnail->CreateUIResource(); |
+ } |
+ WriteThumbnailIfNecessary(tab_id, compressed_data, scale, content_size); |
+} |
+ |
+void ThumbnailStore::ReadTask( |
+ const base::FilePath& file_path, |
+ const base::Callback< |
+ void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>& |
+ post_read_task) { |
+ skia::RefPtr<SkPixelRef> compressed_data; |
+ float scale = 0.f; |
+ gfx::Size content_size; |
+ |
+ if (base::PathExists(file_path)) { |
+ 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 = 0; |
+ int height = 0; |
+ if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < |
+ 0 || |
+ file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < |
+ 0) |
+ success = false; |
+ |
+ content_size = gfx::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); |
+ 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; |
+ |
+ file.Close(); |
+ |
+ skia::RefPtr<SkData> etc1_pixel_data = |
+ skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); |
+ compressed_data = skia::AdoptRef( |
+ SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
+ |
+ if (!success) { |
+ compressed_data.clear(); |
+ content_size = gfx::Size(); |
+ scale = 0.f; |
+ base::DeleteFile(file_path, false); |
+ } |
+ } |
+ |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(post_read_task, compressed_data, scale, content_size)); |
+} |
+ |
+void ThumbnailStore::PostReadTask(TabId tab_id, |
+ skia::RefPtr<SkPixelRef> compressed_data, |
+ float scale, |
+ const gfx::Size& content_size) { |
+ read_in_progress_ = false; |
+ |
+ TabIdList::iterator iter = |
+ std::find(read_queue_.begin(), read_queue_.end(), tab_id); |
+ if (iter == read_queue_.end()) { |
+ ReadNextThumbnail(); |
+ return; |
+ } |
+ |
+ read_queue_.erase(iter); |
+ |
+ if (!cache_.Get(tab_id) && compressed_data) { |
+ ThumbnailMetaDataMap::iterator meta_iter = |
+ thumbnail_meta_data_.find(tab_id); |
+ base::Time time_stamp = base::Time::Now(); |
+ if (meta_iter != thumbnail_meta_data_.end()) |
+ time_stamp = meta_iter->second.capture_time(); |
+ |
+ MakeSpaceForNewItemIfNecessary(tab_id); |
+ scoped_ptr<Thumbnail> thumbnail = Thumbnail::Create( |
+ tab_id, time_stamp, scale, ui_resource_provider_, this); |
+ thumbnail->SetCompressedBitmap(compressed_data, |
+ content_size); |
+ if (kPreferCPUMemory) |
+ thumbnail->CreateUIResource(); |
+ |
+ cache_.Put(tab_id, thumbnail.Pass()); |
+ NotifyObserversOfThumbnailRead(tab_id); |
+ } |
+ |
+ ReadNextThumbnail(); |
+} |
+ |
+void ThumbnailStore::NotifyObserversOfThumbnailRead(TabId tab_id) { |
+ FOR_EACH_OBSERVER( |
+ ThumbnailStoreObserver, observers_, OnFinishedThumbnailRead(tab_id)); |
+} |
+ |
+void ThumbnailStore::RemoveOnMatchedTimeStamp(TabId tab_id, |
+ const base::Time& time_stamp) { |
+ // We remove the cached version if it matches the tab_id and the time_stamp. |
+ Thumbnail* thumbnail = cache_.Get(tab_id); |
+ Thumbnail* approx_thumbnail = approximation_cache_.Get(tab_id); |
+ if ((thumbnail && thumbnail->time_stamp() == time_stamp) || |
+ (approx_thumbnail && approx_thumbnail->time_stamp() == time_stamp)) { |
+ Remove(tab_id); |
+ } |
+ return; |
+} |
+ |
+ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData() { |
+} |
+ |
+ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData( |
+ const base::Time& current_time, |
+ const GURL& url) |
+ : capture_time_(current_time), url_(url) { |
+} |
+ |
+std::pair<SkBitmap, float> ThumbnailStore::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.allocPixels(SkImageInfo::Make(dst_size.width(), |
+ dst_size.height(), |
+ bitmap.info().fColorType, |
+ bitmap.info().fAlphaType)); |
+ 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); |
+} |