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_store.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/android_opengl/etc1/etc1.h" |
| 19 #include "third_party/skia/include/core/SkBitmap.h" |
| 20 #include "third_party/skia/include/core/SkCanvas.h" |
| 21 #include "third_party/skia/include/core/SkData.h" |
| 22 #include "third_party/skia/include/core/SkMallocPixelRef.h" |
| 23 #include "third_party/skia/include/core/SkPixelRef.h" |
| 24 #include "ui/gfx/geometry/size_conversions.h" |
| 25 |
| 26 namespace { |
| 27 |
| 28 const float kApproximationScaleFactor = 4.f; |
| 29 const base::TimeDelta kCaptureMinRequestTimeMs( |
| 30 base::TimeDelta::FromMilliseconds(1000)); |
| 31 |
| 32 const int kCompressedKey = 0xABABABAB; |
| 33 |
| 34 // Indicates whether we prefer to have more free CPU memory over GPU memory. |
| 35 const bool kPreferCPUMemory = true; |
| 36 |
| 37 // ETC1 texture sizes are multiples of four. |
| 38 size_t NextETC1Size(size_t s) { |
| 39 return (s / 4 + (s % 4 ? 1 : 0)) * 4; |
| 40 } |
| 41 |
| 42 gfx::Size GetEncodedSize(const gfx::Size& bitmap_size) { |
| 43 return gfx::Size(NextETC1Size(bitmap_size.width()), |
| 44 NextETC1Size(bitmap_size.height())); |
| 45 } |
| 46 |
| 47 } // anonymous namespace |
| 48 |
| 49 ThumbnailStore::ThumbnailStore(const std::string& disk_cache_path_str, |
| 50 size_t default_cache_size, |
| 51 size_t approximation_cache_size, |
| 52 size_t compression_queue_max_size, |
| 53 size_t write_queue_max_size, |
| 54 bool use_approximation_thumbnail) |
| 55 : disk_cache_path_(disk_cache_path_str), |
| 56 compression_queue_max_size_(compression_queue_max_size), |
| 57 write_queue_max_size_(write_queue_max_size), |
| 58 use_approximation_thumbnail_(use_approximation_thumbnail), |
| 59 compression_tasks_count_(0), |
| 60 write_tasks_count_(0), |
| 61 read_in_progress_(false), |
| 62 cache_(default_cache_size), |
| 63 approximation_cache_(approximation_cache_size), |
| 64 ui_resource_provider_(NULL), |
| 65 compression_thread_("thumbnail_compression"), |
| 66 weak_factory_(this) { |
| 67 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 68 compression_thread_.Start(); |
| 69 } |
| 70 |
| 71 ThumbnailStore::~ThumbnailStore() { |
| 72 compression_thread_.Stop(); |
| 73 SetUIResourceProvider(NULL); |
| 74 } |
| 75 |
| 76 void ThumbnailStore::SetUIResourceProvider( |
| 77 content::UIResourceProvider* ui_resource_provider) { |
| 78 if (ui_resource_provider_ == ui_resource_provider) |
| 79 return; |
| 80 |
| 81 approximation_cache_.Clear(); |
| 82 cache_.Clear(); |
| 83 |
| 84 ui_resource_provider_ = ui_resource_provider; |
| 85 } |
| 86 |
| 87 void ThumbnailStore::AddThumbnailStoreObserver( |
| 88 ThumbnailStoreObserver* observer) { |
| 89 if (!observers_.HasObserver(observer)) |
| 90 observers_.AddObserver(observer); |
| 91 } |
| 92 |
| 93 void ThumbnailStore::RemoveThumbnailStoreObserver( |
| 94 ThumbnailStoreObserver* observer) { |
| 95 if (observers_.HasObserver(observer)) |
| 96 observers_.RemoveObserver(observer); |
| 97 } |
| 98 |
| 99 void ThumbnailStore::Put(TabId tab_id, |
| 100 const SkBitmap& bitmap, |
| 101 float thumbnail_scale) { |
| 102 if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0) |
| 103 return; |
| 104 |
| 105 DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end()); |
| 106 |
| 107 base::Time time_stamp = thumbnail_meta_data_[tab_id].capture_time(); |
| 108 scoped_ptr<Thumbnail> thumbnail = Thumbnail::Create( |
| 109 tab_id, time_stamp, thumbnail_scale, ui_resource_provider_, this); |
| 110 thumbnail->SetBitmap(bitmap); |
| 111 |
| 112 RemoveFromReadQueue(tab_id); |
| 113 MakeSpaceForNewItemIfNecessary(tab_id); |
| 114 cache_.Put(tab_id, thumbnail.Pass()); |
| 115 |
| 116 if (use_approximation_thumbnail_) { |
| 117 std::pair<SkBitmap, float> approximation = |
| 118 CreateApproximation(bitmap, thumbnail_scale); |
| 119 scoped_ptr<Thumbnail> approx_thumbnail = Thumbnail::Create( |
| 120 tab_id, time_stamp, approximation.second, ui_resource_provider_, this); |
| 121 approx_thumbnail->SetBitmap(approximation.first); |
| 122 approximation_cache_.Put(tab_id, approx_thumbnail.Pass()); |
| 123 } |
| 124 CompressThumbnailIfNecessary(tab_id, time_stamp, bitmap, thumbnail_scale); |
| 125 } |
| 126 |
| 127 void ThumbnailStore::Remove(TabId tab_id) { |
| 128 cache_.Remove(tab_id); |
| 129 approximation_cache_.Remove(tab_id); |
| 130 thumbnail_meta_data_.erase(tab_id); |
| 131 RemoveFromDisk(tab_id); |
| 132 RemoveFromReadQueue(tab_id); |
| 133 } |
| 134 |
| 135 Thumbnail* ThumbnailStore::Get(TabId tab_id, bool force_disk_read) { |
| 136 Thumbnail* thumbnail = cache_.Get(tab_id); |
| 137 if (thumbnail) { |
| 138 thumbnail->CreateUIResource(); |
| 139 return thumbnail; |
| 140 } |
| 141 |
| 142 if (force_disk_read && |
| 143 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) != |
| 144 visible_ids_.end() && |
| 145 std::find(read_queue_.begin(), read_queue_.end(), tab_id) == |
| 146 read_queue_.end()) { |
| 147 read_queue_.push_back(tab_id); |
| 148 ReadNextThumbnail(); |
| 149 } |
| 150 |
| 151 thumbnail = approximation_cache_.Get(tab_id); |
| 152 if (thumbnail) { |
| 153 thumbnail->CreateUIResource(); |
| 154 return thumbnail; |
| 155 } |
| 156 |
| 157 return NULL; |
| 158 } |
| 159 |
| 160 void ThumbnailStore::RemoveFromDiskAtAndAboveId(TabId min_id) { |
| 161 base::Closure remove_task = |
| 162 base::Bind(&ThumbnailStore::RemoveFromDiskAtAndAboveIdTask, |
| 163 disk_cache_path_, |
| 164 min_id); |
| 165 content::BrowserThread::PostTask( |
| 166 content::BrowserThread::FILE, FROM_HERE, remove_task); |
| 167 } |
| 168 |
| 169 void ThumbnailStore::InvalidateThumbnailIfChanged(TabId tab_id, |
| 170 const GURL& url) { |
| 171 ThumbnailMetaDataMap::iterator meta_data_iter = |
| 172 thumbnail_meta_data_.find(tab_id); |
| 173 if (meta_data_iter == thumbnail_meta_data_.end()) { |
| 174 thumbnail_meta_data_[tab_id] = ThumbnailMetaData(base::Time(), url); |
| 175 } else if (meta_data_iter->second.url() != url) { |
| 176 Remove(tab_id); |
| 177 } |
| 178 } |
| 179 |
| 180 bool ThumbnailStore::CheckAndUpdateThumbnailMetaData(TabId tab_id, |
| 181 const GURL& url) { |
| 182 base::Time current_time = base::Time::Now(); |
| 183 ThumbnailMetaDataMap::iterator meta_data_iter = |
| 184 thumbnail_meta_data_.find(tab_id); |
| 185 if (meta_data_iter != thumbnail_meta_data_.end() && |
| 186 meta_data_iter->second.url() == url && |
| 187 (current_time - meta_data_iter->second.capture_time()) < |
| 188 kCaptureMinRequestTimeMs) { |
| 189 return false; |
| 190 } |
| 191 |
| 192 thumbnail_meta_data_[tab_id] = ThumbnailMetaData(current_time, url); |
| 193 return true; |
| 194 } |
| 195 |
| 196 void ThumbnailStore::UpdateVisibleIds(const TabIdList& priority) { |
| 197 if (priority.empty()) { |
| 198 visible_ids_.clear(); |
| 199 return; |
| 200 } |
| 201 |
| 202 size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize()); |
| 203 if (visible_ids_.size() == ids_size) { |
| 204 // Early out if called with the same input as last time (We only care |
| 205 // about the first mCache.MaximumCacheSize() entries). |
| 206 bool lists_differ = false; |
| 207 TabIdList::const_iterator visible_iter = visible_ids_.begin(); |
| 208 TabIdList::const_iterator priority_iter = priority.begin(); |
| 209 while (visible_iter != visible_ids_.end() && |
| 210 priority_iter != priority.end()) { |
| 211 if (*priority_iter != *visible_iter) { |
| 212 lists_differ = true; |
| 213 break; |
| 214 } |
| 215 visible_iter++; |
| 216 priority_iter++; |
| 217 } |
| 218 |
| 219 if (!lists_differ) |
| 220 return; |
| 221 } |
| 222 |
| 223 read_queue_.clear(); |
| 224 visible_ids_.clear(); |
| 225 size_t count = 0; |
| 226 TabIdList::const_iterator iter = priority.begin(); |
| 227 while (iter != priority.end() && count < ids_size) { |
| 228 TabId tab_id = *iter; |
| 229 visible_ids_.push_back(tab_id); |
| 230 if (!cache_.Get(tab_id) && |
| 231 std::find(read_queue_.begin(), read_queue_.end(), tab_id) == |
| 232 read_queue_.end()) { |
| 233 read_queue_.push_back(tab_id); |
| 234 } |
| 235 iter++; |
| 236 count++; |
| 237 } |
| 238 |
| 239 ReadNextThumbnail(); |
| 240 } |
| 241 |
| 242 void ThumbnailStore::RemoveFromDisk(TabId tab_id) { |
| 243 base::FilePath file_path = GetFilePath(tab_id); |
| 244 base::Closure task = |
| 245 base::Bind(&ThumbnailStore::RemoveFromDiskTask, file_path); |
| 246 content::BrowserThread::PostTask( |
| 247 content::BrowserThread::FILE, FROM_HERE, task); |
| 248 } |
| 249 |
| 250 void ThumbnailStore::RemoveFromDiskTask(const base::FilePath& file_path) { |
| 251 if (base::PathExists(file_path)) |
| 252 base::DeleteFile(file_path, false); |
| 253 } |
| 254 |
| 255 void ThumbnailStore::RemoveFromDiskAtAndAboveIdTask( |
| 256 const base::FilePath& dir_path, |
| 257 TabId min_id) { |
| 258 base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES); |
| 259 while (true) { |
| 260 base::FilePath path = enumerator.Next(); |
| 261 if (path.empty()) |
| 262 break; |
| 263 base::FileEnumerator::FileInfo info = enumerator.GetInfo(); |
| 264 TabId tab_id; |
| 265 bool success = base::StringToInt(info.GetName().value(), &tab_id); |
| 266 if (success && tab_id >= min_id) |
| 267 base::DeleteFile(path, false); |
| 268 } |
| 269 } |
| 270 |
| 271 void ThumbnailStore::WriteThumbnailIfNecessary( |
| 272 TabId tab_id, |
| 273 skia::RefPtr<SkPixelRef> compressed_data, |
| 274 float scale, |
| 275 const gfx::Size& content_size) { |
| 276 if (write_tasks_count_ >= write_queue_max_size_) |
| 277 return; |
| 278 |
| 279 write_tasks_count_++; |
| 280 |
| 281 base::Callback<void()> post_write_task = |
| 282 base::Bind(&ThumbnailStore::PostWriteTask, weak_factory_.GetWeakPtr()); |
| 283 content::BrowserThread::PostTask(content::BrowserThread::FILE, |
| 284 FROM_HERE, |
| 285 base::Bind(&ThumbnailStore::WriteTask, |
| 286 GetFilePath(tab_id), |
| 287 compressed_data, |
| 288 scale, |
| 289 content_size, |
| 290 post_write_task)); |
| 291 } |
| 292 |
| 293 void ThumbnailStore::CompressThumbnailIfNecessary( |
| 294 TabId tab_id, |
| 295 const base::Time& time_stamp, |
| 296 const SkBitmap& bitmap, |
| 297 float scale) { |
| 298 if (compression_tasks_count_ >= compression_queue_max_size_) { |
| 299 RemoveOnMatchedTimeStamp(tab_id, time_stamp); |
| 300 return; |
| 301 } |
| 302 |
| 303 compression_tasks_count_++; |
| 304 DCHECK(compression_thread_.message_loop_proxy()); |
| 305 |
| 306 base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)> |
| 307 post_compression_task = base::Bind(&ThumbnailStore::PostCompressionTask, |
| 308 weak_factory_.GetWeakPtr(), |
| 309 tab_id, |
| 310 time_stamp, |
| 311 scale); |
| 312 |
| 313 compression_thread_.message_loop_proxy()->PostTask( |
| 314 FROM_HERE, |
| 315 base::Bind( |
| 316 &ThumbnailStore::CompressionTask, bitmap, post_compression_task)); |
| 317 } |
| 318 |
| 319 void ThumbnailStore::ReadNextThumbnail() { |
| 320 if (read_queue_.empty() || read_in_progress_) |
| 321 return; |
| 322 |
| 323 TabId tab_id = read_queue_.front(); |
| 324 read_in_progress_ = true; |
| 325 |
| 326 base::FilePath file_path = GetFilePath(tab_id); |
| 327 |
| 328 base::Callback<void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)> |
| 329 post_read_task = base::Bind( |
| 330 &ThumbnailStore::PostReadTask, weak_factory_.GetWeakPtr(), tab_id); |
| 331 |
| 332 content::BrowserThread::PostTask( |
| 333 content::BrowserThread::FILE, |
| 334 FROM_HERE, |
| 335 base::Bind(&ThumbnailStore::ReadTask, file_path, post_read_task)); |
| 336 } |
| 337 |
| 338 void ThumbnailStore::MakeSpaceForNewItemIfNecessary(TabId tab_id) { |
| 339 if (cache_.Get(tab_id) || |
| 340 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) == |
| 341 visible_ids_.end() || |
| 342 cache_.size() < cache_.MaximumCacheSize()) { |
| 343 return; |
| 344 } |
| 345 |
| 346 TabId key_to_remove; |
| 347 bool found_key_to_remove = false; |
| 348 |
| 349 // 1. Find a cached item not in this list |
| 350 for (ExpiringThumbnailCache::iterator iter = cache_.begin(); |
| 351 iter != cache_.end(); |
| 352 iter++) { |
| 353 if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) == |
| 354 visible_ids_.end()) { |
| 355 key_to_remove = iter->first; |
| 356 found_key_to_remove = true; |
| 357 break; |
| 358 } |
| 359 } |
| 360 |
| 361 if (!found_key_to_remove) { |
| 362 // 2. Find the least important id we can remove. |
| 363 for (TabIdList::reverse_iterator riter = visible_ids_.rbegin(); |
| 364 riter != visible_ids_.rend(); |
| 365 riter++) { |
| 366 if (cache_.Get(*riter)) { |
| 367 key_to_remove = *riter; |
| 368 break; |
| 369 found_key_to_remove = true; |
| 370 } |
| 371 } |
| 372 } |
| 373 |
| 374 if (found_key_to_remove) |
| 375 cache_.Remove(key_to_remove); |
| 376 } |
| 377 |
| 378 void ThumbnailStore::RemoveFromReadQueue(TabId tab_id) { |
| 379 TabIdList::iterator read_iter = |
| 380 std::find(read_queue_.begin(), read_queue_.end(), tab_id); |
| 381 if (read_iter != read_queue_.end()) |
| 382 read_queue_.erase(read_iter); |
| 383 } |
| 384 |
| 385 void ThumbnailStore::InvalidateCachedThumbnail(Thumbnail* thumbnail) { |
| 386 DCHECK(thumbnail); |
| 387 TabId tab_id = thumbnail->tab_id(); |
| 388 cc::UIResourceId uid = thumbnail->ui_resource_id(); |
| 389 |
| 390 Thumbnail* cached_thumbnail = cache_.Get(tab_id); |
| 391 if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid) |
| 392 cache_.Remove(tab_id); |
| 393 |
| 394 cached_thumbnail = approximation_cache_.Get(tab_id); |
| 395 if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid) |
| 396 approximation_cache_.Remove(tab_id); |
| 397 } |
| 398 |
| 399 base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const { |
| 400 return disk_cache_path_.Append(base::IntToString(tab_id)); |
| 401 } |
| 402 |
| 403 void ThumbnailStore::WriteTask(const base::FilePath& file_path, |
| 404 skia::RefPtr<SkPixelRef> compressed_data, |
| 405 float scale, |
| 406 const gfx::Size& content_size, |
| 407 const base::Callback<void()>& post_write_task) { |
| 408 DCHECK(compressed_data); |
| 409 |
| 410 base::File file(file_path, |
| 411 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); |
| 412 DCHECK(file.IsValid()); |
| 413 |
| 414 compressed_data->lockPixels(); |
| 415 bool success = true; |
| 416 int content_width = content_size.width(); |
| 417 int content_height = content_size.height(); |
| 418 int data_width = compressed_data->info().width(); |
| 419 int data_height = compressed_data->info().height(); |
| 420 |
| 421 if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey), |
| 422 sizeof(int)) < 0 || |
| 423 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_width), |
| 424 sizeof(int)) < 0 || |
| 425 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_height), |
| 426 sizeof(int)) < 0 || |
| 427 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width), |
| 428 sizeof(int)) < 0 || |
| 429 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height), |
| 430 sizeof(int)) < 0 || |
| 431 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&scale), |
| 432 sizeof(float)) < 0) { |
| 433 success = false; |
| 434 } |
| 435 |
| 436 size_t compressed_bytes = etc1_get_encoded_data_size(data_width, data_height); |
| 437 if (file.WriteAtCurrentPos(reinterpret_cast<char*>(compressed_data->pixels()), |
| 438 compressed_bytes) < 0) |
| 439 success = false; |
| 440 |
| 441 compressed_data->unlockPixels(); |
| 442 |
| 443 file.Close(); |
| 444 |
| 445 if (!success) |
| 446 base::DeleteFile(file_path, false); |
| 447 |
| 448 content::BrowserThread::PostTask( |
| 449 content::BrowserThread::UI, FROM_HERE, post_write_task); |
| 450 } |
| 451 |
| 452 void ThumbnailStore::PostWriteTask() { |
| 453 write_tasks_count_--; |
| 454 } |
| 455 |
| 456 void ThumbnailStore::CompressionTask( |
| 457 SkBitmap raw_data, |
| 458 const base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)>& |
| 459 post_compression_task) { |
| 460 skia::RefPtr<SkPixelRef> compressed_data; |
| 461 gfx::Size content_size; |
| 462 |
| 463 if (!raw_data.empty()) { |
| 464 SkAutoLockPixels raw_data_lock(raw_data); |
| 465 gfx::Size raw_data_size(raw_data.width(), raw_data.height()); |
| 466 size_t pixel_size = 4; // Pixel size is 4 bytes for kARGB_8888_Config. |
| 467 size_t stride = pixel_size * raw_data_size.width(); |
| 468 |
| 469 gfx::Size encoded_size = GetEncodedSize(raw_data_size); |
| 470 size_t encoded_bytes = |
| 471 etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height()); |
| 472 SkImageInfo info = {encoded_size.width(), |
| 473 encoded_size.height(), |
| 474 kUnknown_SkColorType, |
| 475 kUnpremul_SkAlphaType}; |
| 476 skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef( |
| 477 SkData::NewFromMalloc(new uint8_t[encoded_bytes], encoded_bytes)); |
| 478 skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef( |
| 479 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
| 480 |
| 481 etc1_pixel_ref->lockPixels(); |
| 482 bool success = etc1_encode_image( |
| 483 reinterpret_cast<unsigned char*>(raw_data.getPixels()), |
| 484 raw_data_size.width(), |
| 485 raw_data_size.height(), |
| 486 pixel_size, |
| 487 stride, |
| 488 reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()), |
| 489 encoded_size.width(), |
| 490 encoded_size.height()); |
| 491 etc1_pixel_ref->setImmutable(); |
| 492 etc1_pixel_ref->unlockPixels(); |
| 493 |
| 494 if (success) { |
| 495 compressed_data = etc1_pixel_ref; |
| 496 content_size = raw_data_size; |
| 497 } |
| 498 } |
| 499 |
| 500 content::BrowserThread::PostTask( |
| 501 content::BrowserThread::UI, |
| 502 FROM_HERE, |
| 503 base::Bind(post_compression_task, compressed_data, content_size)); |
| 504 } |
| 505 |
| 506 void ThumbnailStore::PostCompressionTask( |
| 507 TabId tab_id, |
| 508 const base::Time& time_stamp, |
| 509 float scale, |
| 510 skia::RefPtr<SkPixelRef> compressed_data, |
| 511 const gfx::Size& content_size) { |
| 512 compression_tasks_count_--; |
| 513 if (!compressed_data) { |
| 514 RemoveOnMatchedTimeStamp(tab_id, time_stamp); |
| 515 return; |
| 516 } |
| 517 |
| 518 Thumbnail* thumbnail = cache_.Get(tab_id); |
| 519 if (thumbnail) { |
| 520 if (thumbnail->time_stamp() != time_stamp) |
| 521 return; |
| 522 thumbnail->SetCompressedBitmap(compressed_data, content_size); |
| 523 thumbnail->CreateUIResource(); |
| 524 } |
| 525 WriteThumbnailIfNecessary(tab_id, compressed_data, scale, content_size); |
| 526 } |
| 527 |
| 528 void ThumbnailStore::ReadTask( |
| 529 const base::FilePath& file_path, |
| 530 const base::Callback< |
| 531 void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>& |
| 532 post_read_task) { |
| 533 skia::RefPtr<SkPixelRef> compressed_data; |
| 534 float scale = 0.f; |
| 535 gfx::Size content_size; |
| 536 |
| 537 if (base::PathExists(file_path)) { |
| 538 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 539 DCHECK(file.IsValid()); |
| 540 |
| 541 int key; |
| 542 bool success = true; |
| 543 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 || |
| 544 key != kCompressedKey) |
| 545 success = false; |
| 546 |
| 547 int width = 0; |
| 548 int height = 0; |
| 549 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < |
| 550 0 || |
| 551 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < |
| 552 0) |
| 553 success = false; |
| 554 |
| 555 content_size = gfx::Size(width, height); |
| 556 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < |
| 557 0 || |
| 558 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < |
| 559 0) |
| 560 success = false; |
| 561 |
| 562 gfx::Size data_size(width, height); |
| 563 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale), sizeof(float)) < |
| 564 0) |
| 565 success = false; |
| 566 |
| 567 size_t compressed_bytes = |
| 568 etc1_get_encoded_data_size(data_size.width(), data_size.height()); |
| 569 SkImageInfo info = {data_size.width(), |
| 570 data_size.height(), |
| 571 kUnknown_SkColorType, |
| 572 kUnpremul_SkAlphaType}; |
| 573 |
| 574 scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]); |
| 575 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()), |
| 576 compressed_bytes) < 0) |
| 577 success = false; |
| 578 |
| 579 file.Close(); |
| 580 |
| 581 skia::RefPtr<SkData> etc1_pixel_data = |
| 582 skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); |
| 583 compressed_data = skia::AdoptRef( |
| 584 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
| 585 |
| 586 if (!success) { |
| 587 compressed_data.clear(); |
| 588 content_size = gfx::Size(); |
| 589 scale = 0.f; |
| 590 base::DeleteFile(file_path, false); |
| 591 } |
| 592 } |
| 593 |
| 594 content::BrowserThread::PostTask( |
| 595 content::BrowserThread::UI, |
| 596 FROM_HERE, |
| 597 base::Bind(post_read_task, compressed_data, scale, content_size)); |
| 598 } |
| 599 |
| 600 void ThumbnailStore::PostReadTask(TabId tab_id, |
| 601 skia::RefPtr<SkPixelRef> compressed_data, |
| 602 float scale, |
| 603 const gfx::Size& content_size) { |
| 604 read_in_progress_ = false; |
| 605 |
| 606 TabIdList::iterator iter = |
| 607 std::find(read_queue_.begin(), read_queue_.end(), tab_id); |
| 608 if (iter == read_queue_.end()) { |
| 609 ReadNextThumbnail(); |
| 610 return; |
| 611 } |
| 612 |
| 613 read_queue_.erase(iter); |
| 614 |
| 615 if (!cache_.Get(tab_id) && compressed_data) { |
| 616 ThumbnailMetaDataMap::iterator meta_iter = |
| 617 thumbnail_meta_data_.find(tab_id); |
| 618 base::Time time_stamp = base::Time::Now(); |
| 619 if (meta_iter != thumbnail_meta_data_.end()) |
| 620 time_stamp = meta_iter->second.capture_time(); |
| 621 |
| 622 MakeSpaceForNewItemIfNecessary(tab_id); |
| 623 scoped_ptr<Thumbnail> thumbnail = Thumbnail::Create( |
| 624 tab_id, time_stamp, scale, ui_resource_provider_, this); |
| 625 thumbnail->SetCompressedBitmap(compressed_data, |
| 626 content_size); |
| 627 if (kPreferCPUMemory) |
| 628 thumbnail->CreateUIResource(); |
| 629 |
| 630 cache_.Put(tab_id, thumbnail.Pass()); |
| 631 NotifyObserversOfThumbnailRead(tab_id); |
| 632 } |
| 633 |
| 634 ReadNextThumbnail(); |
| 635 } |
| 636 |
| 637 void ThumbnailStore::NotifyObserversOfThumbnailRead(TabId tab_id) { |
| 638 FOR_EACH_OBSERVER( |
| 639 ThumbnailStoreObserver, observers_, OnFinishedThumbnailRead(tab_id)); |
| 640 } |
| 641 |
| 642 void ThumbnailStore::RemoveOnMatchedTimeStamp(TabId tab_id, |
| 643 const base::Time& time_stamp) { |
| 644 // We remove the cached version if it matches the tab_id and the time_stamp. |
| 645 Thumbnail* thumbnail = cache_.Get(tab_id); |
| 646 Thumbnail* approx_thumbnail = approximation_cache_.Get(tab_id); |
| 647 if ((thumbnail && thumbnail->time_stamp() == time_stamp) || |
| 648 (approx_thumbnail && approx_thumbnail->time_stamp() == time_stamp)) { |
| 649 Remove(tab_id); |
| 650 } |
| 651 return; |
| 652 } |
| 653 |
| 654 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData() { |
| 655 } |
| 656 |
| 657 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData( |
| 658 const base::Time& current_time, |
| 659 const GURL& url) |
| 660 : capture_time_(current_time), url_(url) { |
| 661 } |
| 662 |
| 663 std::pair<SkBitmap, float> ThumbnailStore::CreateApproximation( |
| 664 const SkBitmap& bitmap, |
| 665 float scale) { |
| 666 DCHECK(!bitmap.empty()); |
| 667 DCHECK_GT(scale, 0); |
| 668 SkAutoLockPixels bitmap_lock(bitmap); |
| 669 float new_scale = 1.f / kApproximationScaleFactor; |
| 670 |
| 671 gfx::Size dst_size = gfx::ToFlooredSize( |
| 672 gfx::ScaleSize(gfx::Size(bitmap.width(), bitmap.height()), new_scale)); |
| 673 SkBitmap dst_bitmap; |
| 674 dst_bitmap.allocPixels(SkImageInfo::Make(dst_size.width(), |
| 675 dst_size.height(), |
| 676 bitmap.info().fColorType, |
| 677 bitmap.info().fAlphaType)); |
| 678 dst_bitmap.eraseColor(0); |
| 679 SkAutoLockPixels dst_bitmap_lock(dst_bitmap); |
| 680 |
| 681 SkCanvas canvas(dst_bitmap); |
| 682 canvas.scale(new_scale, new_scale); |
| 683 canvas.drawBitmap(bitmap, 0, 0, NULL); |
| 684 dst_bitmap.setImmutable(); |
| 685 |
| 686 return std::make_pair(dst_bitmap, new_scale * scale); |
| 687 } |
OLD | NEW |