Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/android/thumbnail/thumbnail_cache.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <cmath> | |
| 9 | |
| 10 #include "base/file_util.h" | |
| 11 #include "base/files/file.h" | |
| 12 #include "base/files/file_enumerator.h" | |
| 13 #include "base/files/file_path.h" | |
| 14 #include "base/strings/string_number_conversions.h" | |
| 15 #include "base/time/time.h" | |
| 16 #include "content/public/browser/android/ui_resource_provider.h" | |
| 17 #include "content/public/browser/browser_thread.h" | |
| 18 #include "third_party/skia/include/core/SkBitmap.h" | |
| 19 #include "third_party/skia/include/core/SkCanvas.h" | |
| 20 #include "ui/gfx/geometry/size_conversions.h" | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 const bool kDropCachedNTPOnLowMemory = true; | |
| 25 const float kApproximationScaleFactor = 4.f; | |
| 26 const base::TimeDelta kCaptureMinRequestTimeMs( | |
| 27 base::TimeDelta::FromMilliseconds(1000)); | |
| 28 | |
| 29 } // anonymous namespace | |
| 30 | |
| 31 ThumbnailCache::ThumbnailCache(const std::string& disk_cache_path_str, | |
| 32 size_t default_cache_size, | |
| 33 size_t approximation_cache_size, | |
| 34 size_t compression_queue_max_size, | |
| 35 size_t write_queue_max_size, | |
| 36 bool use_approximation_thumbnail) | |
| 37 : disk_cache_path_(disk_cache_path_str), | |
| 38 compression_queue_max_size_(compression_queue_max_size), | |
| 39 write_queue_max_size_(write_queue_max_size), | |
| 40 use_approximation_thumbnail_(use_approximation_thumbnail), | |
| 41 cache_(default_cache_size), | |
| 42 approximation_cache_(approximation_cache_size), | |
| 43 ui_resource_provider_(NULL), | |
| 44 compression_thread_("thumbnail_compression"), | |
| 45 weak_factory_(this) { | |
| 46 compression_thread_.Start(); | |
| 47 } | |
| 48 | |
| 49 ThumbnailCache::~ThumbnailCache() { | |
| 50 compression_thread_.Stop(); | |
| 51 SetUIResourceProvider(NULL); | |
| 52 } | |
| 53 | |
| 54 void ThumbnailCache::SetUIResourceProvider( | |
| 55 content::UIResourceProvider* ui_resource_provider) { | |
| 56 if (ui_resource_provider_ == ui_resource_provider) | |
| 57 return; | |
| 58 | |
| 59 approximation_cache_.Clear(); | |
| 60 cache_.Clear(); | |
| 61 | |
| 62 ui_resource_provider_ = ui_resource_provider; | |
| 63 } | |
| 64 | |
| 65 void ThumbnailCache::AddThumbnailCacheObserver( | |
| 66 ThumbnailCacheObserver* observer) { | |
| 67 if (!observers_.HasObserver(observer)) | |
| 68 observers_.AddObserver(observer); | |
| 69 } | |
| 70 | |
| 71 void ThumbnailCache::RemoveThumbnailCacheObserver( | |
| 72 ThumbnailCacheObserver* observer) { | |
| 73 if (observers_.HasObserver(observer)) | |
| 74 observers_.RemoveObserver(observer); | |
| 75 } | |
| 76 | |
| 77 void ThumbnailCache::Put(TabId tab_id, | |
| 78 const SkBitmap& bitmap, | |
| 79 float thumbnail_scale) { | |
| 80 if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0) | |
| 81 return; | |
| 82 | |
| 83 scoped_refptr<ThumbnailImpl> thumbnail = make_scoped_refptr(new ThumbnailImpl( | |
| 84 tab_id, bitmap, thumbnail_scale, this, ui_resource_provider_)); | |
| 85 | |
| 86 RemoveFromQueues(tab_id); | |
| 87 MakeSpaceForNewItemIfNecessary(tab_id); | |
| 88 cache_.Put(tab_id, thumbnail); | |
| 89 | |
| 90 if (use_approximation_thumbnail_) { | |
| 91 std::pair<SkBitmap, float> approximation = | |
| 92 CreateApproximation(bitmap, thumbnail_scale); | |
| 93 scoped_refptr<ThumbnailImpl> approx_thumbnail = | |
| 94 make_scoped_refptr(new ThumbnailImpl(tab_id, | |
| 95 approximation.first, | |
| 96 approximation.second, | |
| 97 this, | |
| 98 ui_resource_provider_)); | |
| 99 approximation_cache_.Put(tab_id, approx_thumbnail); | |
| 100 } | |
| 101 | |
| 102 CompressThumbnailIfNecessary(thumbnail); | |
| 103 } | |
| 104 | |
| 105 void ThumbnailCache::Remove(TabId tab_id) { | |
| 106 cache_.Remove(tab_id); | |
| 107 approximation_cache_.Remove(tab_id); | |
| 108 thumbnail_meta_data_.erase(tab_id); | |
| 109 RemoveFileFromDisk(GetFilePath(tab_id)); | |
| 110 RemoveFromQueues(tab_id); | |
| 111 } | |
| 112 | |
| 113 scoped_refptr<Thumbnail> ThumbnailCache::Get(TabId tab_id) { | |
| 114 scoped_refptr<ThumbnailImpl> thumbnail = cache_.Get(tab_id); | |
| 115 if (!thumbnail) | |
| 116 thumbnail = approximation_cache_.Get(tab_id); | |
| 117 | |
| 118 // If we are trying to find a thumbnail that is not in memory cache and yet is | |
| 119 // visible, then try paging it in from disk. | |
| 120 if (!thumbnail && | |
| 121 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) != | |
| 122 visible_ids_.end() && | |
| 123 std::find(read_queue_.begin(), read_queue_.end(), tab_id) == | |
| 124 read_queue_.end()) { | |
| 125 read_queue_.push_back(tab_id); | |
| 126 ReadNextThumbnail(); | |
| 127 } | |
| 128 return thumbnail; | |
| 129 } | |
| 130 | |
| 131 void ThumbnailCache::RemoveFromDiskAtAndAboveId(TabId min_id) { | |
| 132 base::Closure remove_task = | |
| 133 base::Bind(&ThumbnailCache::RemoveFromDiskAtAndAboveIdTask, | |
| 134 disk_cache_path_, | |
| 135 min_id); | |
| 136 content::BrowserThread::PostTask( | |
| 137 content::BrowserThread::FILE, FROM_HERE, remove_task); | |
| 138 } | |
| 139 | |
| 140 void ThumbnailCache::InvalidateThumbnailIfChanged(TabId tab_id, | |
| 141 const GURL& url) { | |
| 142 ThumbnailMetaDataMap::iterator meta_data_iter = | |
| 143 thumbnail_meta_data_.find(tab_id); | |
| 144 if (meta_data_iter == thumbnail_meta_data_.end()) { | |
| 145 thumbnail_meta_data_[tab_id] = ThumbnailMetaData(base::Time(), url); | |
| 146 } else if (meta_data_iter->second.url() != url) { | |
| 147 Remove(tab_id); | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(TabId tab_id, | |
| 152 const GURL& url) { | |
| 153 ThumbnailMetaDataMap::iterator meta_data_iter = | |
| 154 thumbnail_meta_data_.find(tab_id); | |
| 155 if (meta_data_iter != thumbnail_meta_data_.end() && | |
| 156 meta_data_iter->second.url() == url && | |
| 157 (CurrentTime() - meta_data_iter->second.capture_time()) < | |
| 158 kCaptureMinRequestTimeMs) { | |
| 159 return false; | |
| 160 } | |
| 161 | |
| 162 thumbnail_meta_data_[tab_id] = ThumbnailMetaData(CurrentTime(), url); | |
| 163 return true; | |
| 164 } | |
| 165 | |
| 166 void ThumbnailCache::UpdateVisibleIds(const TabIdList& priority) { | |
| 167 if (priority.empty()) { | |
| 168 visible_ids_.clear(); | |
| 169 return; | |
| 170 } | |
| 171 | |
| 172 size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize()); | |
| 173 if (visible_ids_.size() == ids_size) { | |
| 174 // Early out if called with the same input as last time (We only care | |
| 175 // about the first mCache.MaximumCacheSize() entries). | |
| 176 bool lists_differ = false; | |
| 177 for (TabIdList::const_iterator visible_iter = visible_ids_.begin(), | |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
A while loop might look a bit cleaner.
powei
2014/06/19 23:05:59
Done.
| |
| 178 priority_iter = priority.begin(); | |
| 179 visible_iter != visible_ids_.end() && priority_iter != priority.end(); | |
| 180 visible_iter++, priority_iter++) { | |
| 181 if (*priority_iter != *visible_iter) { | |
| 182 lists_differ = true; | |
| 183 break; | |
| 184 } | |
| 185 } | |
| 186 if (!lists_differ) | |
| 187 return; | |
| 188 } | |
| 189 | |
| 190 read_queue_.clear(); | |
| 191 visible_ids_.clear(); | |
| 192 size_t count = 0; | |
| 193 for (TabIdList::const_iterator iter = priority.begin(); | |
| 194 iter != priority.end() && count < ids_size; | |
| 195 iter++, count++) { | |
| 196 TabId tab_id = *iter; | |
| 197 visible_ids_.push_back(tab_id); | |
| 198 if (!cache_.Get(tab_id) && | |
| 199 std::find(read_queue_.begin(), read_queue_.end(), tab_id) == | |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
Is this to check if the same id is in the list twi
powei
2014/06/19 23:05:59
Yes it is. We probably don't need this if we're s
| |
| 200 read_queue_.end()) { | |
| 201 read_queue_.push_back(tab_id); | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 ReadNextThumbnail(); | |
| 206 } | |
| 207 | |
| 208 void ThumbnailCache::RemoveFileFromDisk(const base::FilePath& file_path) { | |
| 209 base::Closure task = | |
| 210 base::Bind(&ThumbnailCache::RemoveFileFromDiskTask, file_path); | |
| 211 content::BrowserThread::PostTask( | |
| 212 content::BrowserThread::FILE, FROM_HERE, task); | |
| 213 } | |
| 214 | |
| 215 void ThumbnailCache::RemoveFileFromDiskTask(const base::FilePath& file_path) { | |
| 216 base::DeleteFile(file_path, false); | |
| 217 } | |
| 218 | |
| 219 void ThumbnailCache::RemoveFromDiskAtAndAboveIdTask( | |
| 220 const base::FilePath& dir_path, | |
| 221 TabId min_id) { | |
| 222 base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES); | |
| 223 while (true) { | |
| 224 base::FilePath path = enumerator.Next(); | |
| 225 if (path.empty()) | |
| 226 break; | |
| 227 base::FileEnumerator::FileInfo info = enumerator.GetInfo(); | |
| 228 TabId tab_id; | |
| 229 bool success = base::StringToInt(info.GetName().value(), &tab_id); | |
| 230 if (success && tab_id >= min_id) | |
| 231 base::DeleteFile(path, false); | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 void ThumbnailCache::WriteThumbnailIfNecessary( | |
| 236 scoped_refptr<ThumbnailImpl> thumbnail) { | |
| 237 RemoveDuplicateIdsFromQueueHelper(write_queue_, thumbnail); | |
| 238 if (write_queue_.size() > write_queue_max_size_) | |
| 239 write_queue_.pop_front(); | |
| 240 | |
| 241 write_queue_.push_back(thumbnail); | |
| 242 WriteNextThumbnail(); | |
| 243 } | |
| 244 | |
| 245 void ThumbnailCache::CompressThumbnailIfNecessary( | |
| 246 scoped_refptr<ThumbnailImpl> thumbnail) { | |
| 247 RemoveDuplicateIdsFromQueueHelper(compression_queue_, thumbnail); | |
| 248 if (compression_queue_.size() > compression_queue_max_size_) | |
| 249 compression_queue_.pop_front(); | |
| 250 | |
| 251 compression_queue_.push_back(thumbnail); | |
| 252 CompressNextThumbnail(); | |
| 253 } | |
| 254 | |
| 255 void ThumbnailCache::MakeSpaceForNewItemIfNecessary(TabId tab_id) { | |
| 256 if (cache_.Get(tab_id) || | |
| 257 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) == | |
| 258 visible_ids_.end() || | |
| 259 cache_.size() < cache_.MaximumCacheSize()) { | |
| 260 return; | |
| 261 } | |
| 262 | |
| 263 TabId key_to_remove; | |
| 264 bool found_key_to_remove = false; | |
| 265 | |
| 266 // 1. Find a cached item not in this list | |
| 267 for (ExpiringThumbnailCache::iterator iter = cache_.begin(); | |
| 268 iter != cache_.end(); | |
| 269 iter++) { | |
| 270 if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) == | |
| 271 visible_ids_.end()) { | |
| 272 key_to_remove = iter->first; | |
| 273 found_key_to_remove = true; | |
| 274 break; | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 if (!found_key_to_remove) { | |
| 279 // 2. Find the least important id we can remove. | |
| 280 for (TabIdList::reverse_iterator riter = visible_ids_.rbegin(); | |
| 281 riter != visible_ids_.rend(); | |
| 282 riter++) { | |
| 283 if (cache_.Get(*riter)) { | |
| 284 key_to_remove = *riter; | |
| 285 break; | |
| 286 found_key_to_remove = true; | |
| 287 } | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 if (found_key_to_remove) | |
| 292 cache_.Remove(key_to_remove); | |
| 293 } | |
| 294 | |
| 295 void ThumbnailCache::RemoveFromQueues(TabId tab_id) { | |
| 296 TabIdList::iterator read_iter = | |
| 297 std::find(read_queue_.begin(), read_queue_.end(), tab_id); | |
| 298 if (read_iter != read_queue_.end()) | |
| 299 read_queue_.erase(read_iter); | |
| 300 | |
| 301 ThumbnailQueue new_write_queue; | |
| 302 for (ThumbnailQueue::iterator iter = write_queue_.begin(); | |
| 303 iter != write_queue_.end(); | |
| 304 iter++) { | |
| 305 scoped_refptr<ThumbnailImpl> write_thumbnail = *iter; | |
| 306 if (write_thumbnail->tab_id() != tab_id) | |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
Why not if (==) iter.remove()? Same below and for
powei
2014/06/19 23:05:59
Done. changed to "iter = write_queue_.erase(iter);
| |
| 307 new_write_queue.push_back(write_thumbnail); | |
| 308 } | |
| 309 write_queue_ = new_write_queue; | |
| 310 | |
| 311 ThumbnailQueue new_compression_queue; | |
| 312 for (ThumbnailQueue::iterator iter = compression_queue_.begin(); | |
| 313 iter != compression_queue_.end(); | |
| 314 iter++) { | |
| 315 scoped_refptr<ThumbnailImpl> compress_thumbnail = *iter; | |
| 316 if (compress_thumbnail->tab_id() != tab_id) | |
| 317 new_compression_queue.push_back(compress_thumbnail); | |
| 318 } | |
| 319 compression_queue_ = new_compression_queue; | |
| 320 } | |
| 321 | |
| 322 bool ThumbnailCache::RemoveDuplicateIdsFromQueueHelper( | |
| 323 ThumbnailQueue& queue, | |
| 324 scoped_refptr<ThumbnailImpl> thumbnail) { | |
| 325 bool found = false; | |
| 326 ThumbnailQueue::iterator iter = queue.begin(); | |
| 327 ThumbnailQueue good_queue; | |
| 328 for (ThumbnailQueue::iterator iter = queue.begin(); iter != queue.end(); | |
| 329 iter++) { | |
| 330 if ((*iter)->tab_id() != thumbnail->tab_id()) { | |
| 331 good_queue.push_back(thumbnail); | |
| 332 } else { | |
| 333 found = true; | |
| 334 } | |
| 335 } | |
| 336 queue = good_queue; | |
| 337 return found; | |
| 338 } | |
| 339 | |
| 340 void ThumbnailCache::InvalidateCachedThumbnail( | |
| 341 scoped_refptr<ThumbnailImpl> thumbnail) { | |
| 342 TabId tab_id = thumbnail->tab_id(); | |
| 343 if (cache_.Get(tab_id) == thumbnail) | |
| 344 cache_.Remove(tab_id); | |
| 345 | |
| 346 if (approximation_cache_.Get(tab_id) == thumbnail) | |
| 347 approximation_cache_.Remove(tab_id); | |
| 348 } | |
| 349 | |
| 350 base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) const { | |
| 351 return disk_cache_path_.Append(base::IntToString(tab_id)); | |
| 352 } | |
| 353 | |
| 354 base::Time ThumbnailCache::CurrentTime() { | |
| 355 return base::Time::Now(); | |
| 356 } | |
| 357 | |
| 358 void ThumbnailCache::WriteNextThumbnail() { | |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
What if we're already waiting for a write to finis
powei
2014/06/19 23:05:59
Are you thinking about how the size of the write_q
David Trainor- moved to gerrit
2014/06/25 07:40:15
I'm more worried that we have a write queue that d
powei
2014/07/07 17:24:51
Agreed offline that we'd keep a count instead of a
| |
| 359 if (write_queue_.empty()) | |
| 360 return; | |
| 361 | |
| 362 scoped_refptr<ThumbnailImpl> thumbnail = write_queue_.front(); | |
| 363 write_queue_.pop_front(); | |
| 364 | |
| 365 if (!thumbnail->IsValid()) | |
| 366 return; | |
| 367 | |
| 368 scoped_refptr<ThumbnailImpl> in_thumbnail = | |
| 369 thumbnail->PassDataToNewThumbnail(); | |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
Won't this steal the reference from the object in
powei
2014/06/19 23:05:58
The point is to steal the reference. This way we'
David Trainor- moved to gerrit
2014/06/25 07:40:16
But since we're building the ui resource id on dem
powei
2014/07/07 17:24:51
Done. Moved to more explicit passing of ref-counte
| |
| 370 | |
| 371 base::Closure write_task = base::Bind(&ThumbnailCache::WriteTask, | |
| 372 in_thumbnail, | |
| 373 GetFilePath(thumbnail->tab_id())); | |
| 374 base::Closure post_write_task = base::Bind( | |
| 375 &ThumbnailCache::WriteNextThumbnail, weak_factory_.GetWeakPtr()); | |
| 376 | |
| 377 content::BrowserThread::PostTaskAndReply( | |
| 378 content::BrowserThread::FILE, FROM_HERE, write_task, post_write_task); | |
| 379 } | |
| 380 | |
| 381 void ThumbnailCache::WriteTask(scoped_refptr<ThumbnailImpl> thumbnail, | |
| 382 const base::FilePath& file_path) { | |
| 383 DCHECK(thumbnail); | |
| 384 DCHECK(thumbnail->IsValid()); | |
| 385 | |
| 386 base::File file(file_path, | |
| 387 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); | |
| 388 bool success = ThumbnailImpl::WriteToFile(thumbnail, file); | |
| 389 file.Close(); | |
| 390 | |
| 391 if (!success) | |
| 392 RemoveFileFromDisk(file_path); | |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
Is posting this safe? What if the app dies before
powei
2014/06/19 23:05:59
Done. Good catch. called DeleteFile directly ins
| |
| 393 } | |
| 394 | |
| 395 void ThumbnailCache::ReadNextThumbnail() { | |
| 396 if (read_queue_.empty()) | |
| 397 return; | |
| 398 | |
| 399 TabId tab_id = read_queue_.front(); | |
| 400 | |
| 401 // Create an empty thumbnail to hold the content of the read. | |
| 402 scoped_refptr<ThumbnailImpl> output_thumbnail = make_scoped_refptr( | |
| 403 new ThumbnailImpl(tab_id, SkBitmap(), 0.f, this, ui_resource_provider_)); | |
| 404 | |
| 405 base::FilePath file_path = GetFilePath(tab_id); | |
| 406 base::Closure read_task = | |
| 407 base::Bind(&ThumbnailCache::ReadTask, file_path, output_thumbnail); | |
| 408 base::Closure post_read_task = base::Bind(&ThumbnailCache::PostReadTask, | |
| 409 weak_factory_.GetWeakPtr(), | |
| 410 output_thumbnail); | |
| 411 content::BrowserThread::PostTaskAndReply( | |
| 412 content::BrowserThread::FILE, FROM_HERE, read_task, post_read_task); | |
| 413 } | |
| 414 | |
| 415 void ThumbnailCache::ReadTask(const base::FilePath& file_path, | |
| 416 scoped_refptr<ThumbnailImpl> thumbnail) { | |
| 417 DCHECK(thumbnail); | |
| 418 if (!base::PathExists(file_path)) | |
| 419 return; | |
| 420 | |
| 421 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); | |
| 422 DCHECK(file.IsValid()); | |
| 423 bool success = ThumbnailImpl::ReadFromFile(file, thumbnail); | |
| 424 file.Close(); | |
| 425 | |
| 426 if (!success) | |
| 427 RemoveFileFromDisk(file_path); | |
| 428 } | |
| 429 | |
| 430 void ThumbnailCache::PostReadTask(scoped_refptr<ThumbnailImpl> thumbnail) { | |
| 431 DCHECK(thumbnail); | |
| 432 TabIdList::iterator iter = | |
| 433 std::find(read_queue_.begin(), read_queue_.end(), thumbnail->tab_id()); | |
| 434 if (iter == read_queue_.end()) | |
| 435 return; | |
| 436 | |
| 437 read_queue_.erase(iter); | |
| 438 TabId tab_id = thumbnail->tab_id(); | |
| 439 MakeSpaceForNewItemIfNecessary(tab_id); | |
| 440 cache_.Put(tab_id, thumbnail); | |
| 441 NotifyObserversOfThumbnailRead(tab_id); | |
| 442 ReadNextThumbnail(); | |
| 443 } | |
| 444 | |
| 445 void ThumbnailCache::CompressNextThumbnail() { | |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
Shouldn't this queue up the tasks and only run one
powei
2014/06/19 23:05:59
see comment for write task.
| |
| 446 if (!compression_queue_.empty()) { | |
| 447 scoped_refptr<ThumbnailImpl> thumbnail = compression_queue_.front(); | |
| 448 compression_queue_.pop_front(); | |
| 449 | |
| 450 scoped_refptr<ThumbnailImpl> in_thumbnail = | |
| 451 thumbnail->PassDataToNewThumbnail(); | |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
Doesn't this also steal the bitmap/data from the t
powei
2014/06/19 23:05:59
This does steal the reference, and once compressio
David Trainor- moved to gerrit
2014/06/25 07:40:15
Still not sure how this works if we compress befor
| |
| 452 | |
| 453 scoped_refptr<ThumbnailImpl> out_thumbnail = | |
| 454 scoped_refptr<ThumbnailImpl>(new ThumbnailImpl(in_thumbnail->tab_id(), | |
| 455 SkBitmap(), | |
| 456 0.f, | |
| 457 this, | |
| 458 ui_resource_provider_)); | |
| 459 base::Closure compression_task = | |
| 460 base::Bind(&ThumbnailImpl::Compress, in_thumbnail, out_thumbnail); | |
| 461 base::Closure post_compression_task = | |
| 462 base::Bind(&ThumbnailCache::PostCompressionTask, | |
| 463 weak_factory_.GetWeakPtr(), | |
| 464 out_thumbnail); | |
| 465 | |
| 466 DCHECK(compression_thread_.message_loop_proxy()); | |
| 467 compression_thread_.message_loop_proxy()->PostTaskAndReply( | |
| 468 FROM_HERE, compression_task, post_compression_task); | |
| 469 } | |
| 470 } | |
| 471 | |
| 472 void ThumbnailCache::PostCompressionTask( | |
| 473 scoped_refptr<ThumbnailImpl> compressed_thumbnail) { | |
| 474 DCHECK(compressed_thumbnail); | |
| 475 TabId tab_id = compressed_thumbnail->tab_id(); | |
| 476 cache_.Remove(tab_id); | |
| 477 if (compressed_thumbnail->IsValid()) { | |
| 478 cache_.Put(tab_id, compressed_thumbnail); | |
| 479 WriteThumbnailIfNecessary(compressed_thumbnail); | |
| 480 } | |
| 481 CompressNextThumbnail(); | |
| 482 } | |
| 483 | |
| 484 void ThumbnailCache::NotifyObserversOfThumbnailRead(TabId tab_id) { | |
| 485 FOR_EACH_OBSERVER( | |
| 486 ThumbnailCacheObserver, observers_, OnFinishedThumbnailRead(tab_id)); | |
| 487 } | |
| 488 | |
| 489 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData() { | |
| 490 } | |
| 491 | |
| 492 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData( | |
| 493 const base::Time& current_time, | |
| 494 const GURL& url) | |
| 495 : capture_time_(current_time), url_(url) { | |
| 496 } | |
| 497 | |
| 498 std::pair<SkBitmap, float> ThumbnailCache::CreateApproximation( | |
| 499 const SkBitmap& bitmap, | |
| 500 float scale) { | |
| 501 DCHECK(!bitmap.empty()); | |
| 502 DCHECK_GT(scale, 0); | |
| 503 SkAutoLockPixels bitmap_lock(bitmap); | |
| 504 float new_scale = 1.f / kApproximationScaleFactor; | |
| 505 | |
| 506 gfx::Size dst_size = gfx::ToFlooredSize( | |
| 507 gfx::ScaleSize(gfx::Size(bitmap.width(), bitmap.height()), new_scale)); | |
| 508 SkBitmap dst_bitmap; | |
| 509 dst_bitmap.setConfig(bitmap.getConfig(), dst_size.width(), dst_size.height()); | |
| 510 dst_bitmap.allocPixels(); | |
| 511 dst_bitmap.eraseColor(0); | |
| 512 SkAutoLockPixels dst_bitmap_lock(dst_bitmap); | |
| 513 | |
| 514 SkCanvas canvas(dst_bitmap); | |
| 515 canvas.scale(new_scale, new_scale); | |
| 516 canvas.drawBitmap(bitmap, 0, 0, NULL); | |
| 517 dst_bitmap.setImmutable(); | |
| 518 | |
| 519 return std::make_pair(dst_bitmap, new_scale * scale); | |
| 520 } | |
| OLD | NEW |