Home | History | Annotate | Download | only in thumbnail
      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/big_endian.h"
     11 #include "base/files/file.h"
     12 #include "base/files/file_enumerator.h"
     13 #include "base/files/file_path.h"
     14 #include "base/files/file_util.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/threading/worker_pool.h"
     17 #include "base/time/time.h"
     18 #include "content/public/browser/android/ui_resource_provider.h"
     19 #include "content/public/browser/browser_thread.h"
     20 #include "third_party/android_opengl/etc1/etc1.h"
     21 #include "third_party/skia/include/core/SkBitmap.h"
     22 #include "third_party/skia/include/core/SkCanvas.h"
     23 #include "third_party/skia/include/core/SkData.h"
     24 #include "third_party/skia/include/core/SkMallocPixelRef.h"
     25 #include "third_party/skia/include/core/SkPixelRef.h"
     26 #include "ui/gfx/android/device_display_info.h"
     27 #include "ui/gfx/geometry/size_conversions.h"
     28 
     29 namespace {
     30 
     31 const float kApproximationScaleFactor = 4.f;
     32 const base::TimeDelta kCaptureMinRequestTimeMs(
     33     base::TimeDelta::FromMilliseconds(1000));
     34 
     35 const int kCompressedKey = 0xABABABAB;
     36 const int kCurrentExtraVersion = 1;
     37 
     38 // Indicates whether we prefer to have more free CPU memory over GPU memory.
     39 const bool kPreferCPUMemory = true;
     40 
     41 size_t NextPowerOfTwo(size_t x) {
     42   --x;
     43   x |= x >> 1;
     44   x |= x >> 2;
     45   x |= x >> 4;
     46   x |= x >> 8;
     47   x |= x >> 16;
     48   return x + 1;
     49 }
     50 
     51 size_t RoundUpMod4(size_t x) {
     52   return (x + 3) & ~3;
     53 }
     54 
     55 gfx::Size GetEncodedSize(const gfx::Size& bitmap_size, bool supports_npot) {
     56   DCHECK(!bitmap_size.IsEmpty());
     57   if (!supports_npot)
     58     return gfx::Size(NextPowerOfTwo(bitmap_size.width()),
     59                      NextPowerOfTwo(bitmap_size.height()));
     60   else
     61     return gfx::Size(RoundUpMod4(bitmap_size.width()),
     62                      RoundUpMod4(bitmap_size.height()));
     63 }
     64 
     65 template<typename T>
     66 bool ReadBigEndianFromFile(base::File& file, T* out) {
     67   char buffer[sizeof(T)];
     68   if (file.ReadAtCurrentPos(buffer, sizeof(T)) != sizeof(T))
     69     return false;
     70   base::ReadBigEndian(buffer, out);
     71   return true;
     72 }
     73 
     74 template<typename T>
     75 bool WriteBigEndianToFile(base::File& file, T val) {
     76   char buffer[sizeof(T)];
     77   base::WriteBigEndian(buffer, val);
     78   return file.WriteAtCurrentPos(buffer, sizeof(T)) == sizeof(T);
     79 }
     80 
     81 bool ReadBigEndianFloatFromFile(base::File& file, float* out) {
     82   char buffer[sizeof(float)];
     83   if (file.ReadAtCurrentPos(buffer, sizeof(buffer)) != sizeof(buffer))
     84     return false;
     85 
     86 #if defined(ARCH_CPU_LITTLE_ENDIAN)
     87   for (size_t i = 0; i < sizeof(float) / 2; i++) {
     88     char tmp = buffer[i];
     89     buffer[i] = buffer[sizeof(float) - 1 - i];
     90     buffer[sizeof(float) - 1 - i] = tmp;
     91   }
     92 #endif
     93   memcpy(out, buffer, sizeof(buffer));
     94 
     95   return true;
     96 }
     97 
     98 bool WriteBigEndianFloatToFile(base::File& file, float val) {
     99   char buffer[sizeof(float)];
    100   memcpy(buffer, &val, sizeof(buffer));
    101 
    102 #if defined(ARCH_CPU_LITTLE_ENDIAN)
    103   for (size_t i = 0; i < sizeof(float) / 2; i++) {
    104     char tmp = buffer[i];
    105     buffer[i] = buffer[sizeof(float) - 1 - i];
    106     buffer[sizeof(float) - 1 - i] = tmp;
    107   }
    108 #endif
    109   return file.WriteAtCurrentPos(buffer, sizeof(buffer)) == sizeof(buffer);
    110 }
    111 
    112 }  // anonymous namespace
    113 
    114 ThumbnailStore::ThumbnailStore(const std::string& disk_cache_path_str,
    115                                size_t default_cache_size,
    116                                size_t approximation_cache_size,
    117                                size_t compression_queue_max_size,
    118                                size_t write_queue_max_size,
    119                                bool use_approximation_thumbnail)
    120     : disk_cache_path_(disk_cache_path_str),
    121       compression_queue_max_size_(compression_queue_max_size),
    122       write_queue_max_size_(write_queue_max_size),
    123       use_approximation_thumbnail_(use_approximation_thumbnail),
    124       compression_tasks_count_(0),
    125       write_tasks_count_(0),
    126       read_in_progress_(false),
    127       cache_(default_cache_size),
    128       approximation_cache_(approximation_cache_size),
    129       ui_resource_provider_(NULL),
    130       weak_factory_(this) {
    131   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    132 }
    133 
    134 ThumbnailStore::~ThumbnailStore() {
    135   SetUIResourceProvider(NULL);
    136 }
    137 
    138 void ThumbnailStore::SetUIResourceProvider(
    139     content::UIResourceProvider* ui_resource_provider) {
    140   if (ui_resource_provider_ == ui_resource_provider)
    141     return;
    142 
    143   approximation_cache_.Clear();
    144   cache_.Clear();
    145 
    146   ui_resource_provider_ = ui_resource_provider;
    147 }
    148 
    149 void ThumbnailStore::AddThumbnailStoreObserver(
    150     ThumbnailStoreObserver* observer) {
    151   if (!observers_.HasObserver(observer))
    152     observers_.AddObserver(observer);
    153 }
    154 
    155 void ThumbnailStore::RemoveThumbnailStoreObserver(
    156     ThumbnailStoreObserver* observer) {
    157   if (observers_.HasObserver(observer))
    158     observers_.RemoveObserver(observer);
    159 }
    160 
    161 void ThumbnailStore::Put(TabId tab_id,
    162                          const SkBitmap& bitmap,
    163                          float thumbnail_scale) {
    164   if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0)
    165     return;
    166 
    167   DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end());
    168 
    169   base::Time time_stamp = thumbnail_meta_data_[tab_id].capture_time();
    170   scoped_ptr<Thumbnail> thumbnail = Thumbnail::Create(
    171       tab_id, time_stamp, thumbnail_scale, ui_resource_provider_, this);
    172   thumbnail->SetBitmap(bitmap);
    173 
    174   RemoveFromReadQueue(tab_id);
    175   MakeSpaceForNewItemIfNecessary(tab_id);
    176   cache_.Put(tab_id, thumbnail.Pass());
    177 
    178   if (use_approximation_thumbnail_) {
    179     std::pair<SkBitmap, float> approximation =
    180         CreateApproximation(bitmap, thumbnail_scale);
    181     scoped_ptr<Thumbnail> approx_thumbnail = Thumbnail::Create(
    182         tab_id, time_stamp, approximation.second, ui_resource_provider_, this);
    183     approx_thumbnail->SetBitmap(approximation.first);
    184     approximation_cache_.Put(tab_id, approx_thumbnail.Pass());
    185   }
    186   CompressThumbnailIfNecessary(tab_id, time_stamp, bitmap, thumbnail_scale);
    187 }
    188 
    189 void ThumbnailStore::Remove(TabId tab_id) {
    190   cache_.Remove(tab_id);
    191   approximation_cache_.Remove(tab_id);
    192   thumbnail_meta_data_.erase(tab_id);
    193   RemoveFromDisk(tab_id);
    194   RemoveFromReadQueue(tab_id);
    195 }
    196 
    197 Thumbnail* ThumbnailStore::Get(TabId tab_id,
    198                                bool force_disk_read,
    199                                bool allow_approximation) {
    200   Thumbnail* thumbnail = cache_.Get(tab_id);
    201   if (thumbnail) {
    202     thumbnail->CreateUIResource();
    203     return thumbnail;
    204   }
    205 
    206   if (force_disk_read &&
    207       std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) !=
    208           visible_ids_.end() &&
    209       std::find(read_queue_.begin(), read_queue_.end(), tab_id) ==
    210           read_queue_.end()) {
    211     read_queue_.push_back(tab_id);
    212     ReadNextThumbnail();
    213   }
    214 
    215   if (allow_approximation) {
    216     thumbnail = approximation_cache_.Get(tab_id);
    217     if (thumbnail) {
    218       thumbnail->CreateUIResource();
    219       return thumbnail;
    220     }
    221   }
    222 
    223   return NULL;
    224 }
    225 
    226 void ThumbnailStore::RemoveFromDiskAtAndAboveId(TabId min_id) {
    227   base::Closure remove_task =
    228       base::Bind(&ThumbnailStore::RemoveFromDiskAtAndAboveIdTask,
    229                  disk_cache_path_,
    230                  min_id);
    231   content::BrowserThread::PostTask(
    232       content::BrowserThread::FILE, FROM_HERE, remove_task);
    233 }
    234 
    235 void ThumbnailStore::InvalidateThumbnailIfChanged(TabId tab_id,
    236                                                   const GURL& url) {
    237   ThumbnailMetaDataMap::iterator meta_data_iter =
    238       thumbnail_meta_data_.find(tab_id);
    239   if (meta_data_iter == thumbnail_meta_data_.end()) {
    240     thumbnail_meta_data_[tab_id] = ThumbnailMetaData(base::Time(), url);
    241   } else if (meta_data_iter->second.url() != url) {
    242     Remove(tab_id);
    243   }
    244 }
    245 
    246 bool ThumbnailStore::CheckAndUpdateThumbnailMetaData(TabId tab_id,
    247                                                      const GURL& url) {
    248   base::Time current_time = base::Time::Now();
    249   ThumbnailMetaDataMap::iterator meta_data_iter =
    250       thumbnail_meta_data_.find(tab_id);
    251   if (meta_data_iter != thumbnail_meta_data_.end() &&
    252       meta_data_iter->second.url() == url &&
    253       (current_time - meta_data_iter->second.capture_time()) <
    254           kCaptureMinRequestTimeMs) {
    255     return false;
    256   }
    257 
    258   thumbnail_meta_data_[tab_id] = ThumbnailMetaData(current_time, url);
    259   return true;
    260 }
    261 
    262 void ThumbnailStore::UpdateVisibleIds(const TabIdList& priority) {
    263   if (priority.empty()) {
    264     visible_ids_.clear();
    265     return;
    266   }
    267 
    268   size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize());
    269   if (visible_ids_.size() == ids_size) {
    270     // Early out if called with the same input as last time (We only care
    271     // about the first mCache.MaximumCacheSize() entries).
    272     bool lists_differ = false;
    273     TabIdList::const_iterator visible_iter = visible_ids_.begin();
    274     TabIdList::const_iterator priority_iter = priority.begin();
    275     while (visible_iter != visible_ids_.end() &&
    276            priority_iter != priority.end()) {
    277       if (*priority_iter != *visible_iter) {
    278         lists_differ = true;
    279         break;
    280       }
    281       visible_iter++;
    282       priority_iter++;
    283     }
    284 
    285     if (!lists_differ)
    286       return;
    287   }
    288 
    289   read_queue_.clear();
    290   visible_ids_.clear();
    291   size_t count = 0;
    292   TabIdList::const_iterator iter = priority.begin();
    293   while (iter != priority.end() && count < ids_size) {
    294     TabId tab_id = *iter;
    295     visible_ids_.push_back(tab_id);
    296     if (!cache_.Get(tab_id) &&
    297         std::find(read_queue_.begin(), read_queue_.end(), tab_id) ==
    298             read_queue_.end()) {
    299       read_queue_.push_back(tab_id);
    300     }
    301     iter++;
    302     count++;
    303   }
    304 
    305   ReadNextThumbnail();
    306 }
    307 
    308 void ThumbnailStore::DecompressThumbnailFromFile(
    309     TabId tab_id,
    310     const base::Callback<void(bool, SkBitmap)>&
    311         post_decompress_callback) {
    312   base::FilePath file_path = GetFilePath(tab_id);
    313 
    314   base::Callback<void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>
    315       decompress_task = base::Bind(
    316           &ThumbnailStore::DecompressionTask, post_decompress_callback);
    317 
    318   content::BrowserThread::PostTask(
    319       content::BrowserThread::FILE,
    320       FROM_HERE,
    321       base::Bind(&ThumbnailStore::ReadTask, true, file_path, decompress_task));
    322 }
    323 
    324 void ThumbnailStore::RemoveFromDisk(TabId tab_id) {
    325   base::FilePath file_path = GetFilePath(tab_id);
    326   base::Closure task =
    327       base::Bind(&ThumbnailStore::RemoveFromDiskTask, file_path);
    328   content::BrowserThread::PostTask(
    329       content::BrowserThread::FILE, FROM_HERE, task);
    330 }
    331 
    332 void ThumbnailStore::RemoveFromDiskTask(const base::FilePath& file_path) {
    333   if (base::PathExists(file_path))
    334     base::DeleteFile(file_path, false);
    335 }
    336 
    337 void ThumbnailStore::RemoveFromDiskAtAndAboveIdTask(
    338     const base::FilePath& dir_path,
    339     TabId min_id) {
    340   base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES);
    341   while (true) {
    342     base::FilePath path = enumerator.Next();
    343     if (path.empty())
    344       break;
    345     base::FileEnumerator::FileInfo info = enumerator.GetInfo();
    346     TabId tab_id;
    347     bool success = base::StringToInt(info.GetName().value(), &tab_id);
    348     if (success && tab_id >= min_id)
    349       base::DeleteFile(path, false);
    350   }
    351 }
    352 
    353 void ThumbnailStore::WriteThumbnailIfNecessary(
    354     TabId tab_id,
    355     skia::RefPtr<SkPixelRef> compressed_data,
    356     float scale,
    357     const gfx::Size& content_size) {
    358   if (write_tasks_count_ >= write_queue_max_size_)
    359     return;
    360 
    361   write_tasks_count_++;
    362 
    363   base::Callback<void()> post_write_task =
    364       base::Bind(&ThumbnailStore::PostWriteTask, weak_factory_.GetWeakPtr());
    365   content::BrowserThread::PostTask(content::BrowserThread::FILE,
    366                                    FROM_HERE,
    367                                    base::Bind(&ThumbnailStore::WriteTask,
    368                                               GetFilePath(tab_id),
    369                                               compressed_data,
    370                                               scale,
    371                                               content_size,
    372                                               post_write_task));
    373 }
    374 
    375 void ThumbnailStore::CompressThumbnailIfNecessary(
    376     TabId tab_id,
    377     const base::Time& time_stamp,
    378     const SkBitmap& bitmap,
    379     float scale) {
    380   if (compression_tasks_count_ >= compression_queue_max_size_) {
    381     RemoveOnMatchedTimeStamp(tab_id, time_stamp);
    382     return;
    383   }
    384 
    385   compression_tasks_count_++;
    386 
    387   base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)>
    388       post_compression_task = base::Bind(&ThumbnailStore::PostCompressionTask,
    389                                          weak_factory_.GetWeakPtr(),
    390                                          tab_id,
    391                                          time_stamp,
    392                                          scale);
    393 
    394   gfx::Size raw_data_size(bitmap.width(), bitmap.height());
    395   gfx::Size encoded_size = GetEncodedSize(
    396       raw_data_size, ui_resource_provider_->SupportsETC1NonPowerOfTwo());
    397 
    398   base::WorkerPool::PostTask(FROM_HERE,
    399                              base::Bind(&ThumbnailStore::CompressionTask,
    400                                         bitmap,
    401                                         encoded_size,
    402                                         post_compression_task),
    403                              true);
    404 }
    405 
    406 void ThumbnailStore::ReadNextThumbnail() {
    407   if (read_queue_.empty() || read_in_progress_)
    408     return;
    409 
    410   TabId tab_id = read_queue_.front();
    411   read_in_progress_ = true;
    412 
    413   base::FilePath file_path = GetFilePath(tab_id);
    414 
    415   base::Callback<void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>
    416       post_read_task = base::Bind(
    417           &ThumbnailStore::PostReadTask, weak_factory_.GetWeakPtr(), tab_id);
    418 
    419   content::BrowserThread::PostTask(
    420       content::BrowserThread::FILE,
    421       FROM_HERE,
    422       base::Bind(&ThumbnailStore::ReadTask, false, file_path, post_read_task));
    423 }
    424 
    425 void ThumbnailStore::MakeSpaceForNewItemIfNecessary(TabId tab_id) {
    426   if (cache_.Get(tab_id) ||
    427       std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) ==
    428           visible_ids_.end() ||
    429       cache_.size() < cache_.MaximumCacheSize()) {
    430     return;
    431   }
    432 
    433   TabId key_to_remove;
    434   bool found_key_to_remove = false;
    435 
    436   // 1. Find a cached item not in this list
    437   for (ExpiringThumbnailCache::iterator iter = cache_.begin();
    438        iter != cache_.end();
    439        iter++) {
    440     if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) ==
    441         visible_ids_.end()) {
    442       key_to_remove = iter->first;
    443       found_key_to_remove = true;
    444       break;
    445     }
    446   }
    447 
    448   if (!found_key_to_remove) {
    449     // 2. Find the least important id we can remove.
    450     for (TabIdList::reverse_iterator riter = visible_ids_.rbegin();
    451          riter != visible_ids_.rend();
    452          riter++) {
    453       if (cache_.Get(*riter)) {
    454         key_to_remove = *riter;
    455         break;
    456         found_key_to_remove = true;
    457       }
    458     }
    459   }
    460 
    461   if (found_key_to_remove)
    462     cache_.Remove(key_to_remove);
    463 }
    464 
    465 void ThumbnailStore::RemoveFromReadQueue(TabId tab_id) {
    466   TabIdList::iterator read_iter =
    467       std::find(read_queue_.begin(), read_queue_.end(), tab_id);
    468   if (read_iter != read_queue_.end())
    469     read_queue_.erase(read_iter);
    470 }
    471 
    472 void ThumbnailStore::InvalidateCachedThumbnail(Thumbnail* thumbnail) {
    473   DCHECK(thumbnail);
    474   TabId tab_id = thumbnail->tab_id();
    475   cc::UIResourceId uid = thumbnail->ui_resource_id();
    476 
    477   Thumbnail* cached_thumbnail = cache_.Get(tab_id);
    478   if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid)
    479     cache_.Remove(tab_id);
    480 
    481   cached_thumbnail = approximation_cache_.Get(tab_id);
    482   if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid)
    483     approximation_cache_.Remove(tab_id);
    484 }
    485 
    486 base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const {
    487   return disk_cache_path_.Append(base::IntToString(tab_id));
    488 }
    489 
    490 namespace {
    491 
    492 bool WriteToFile(base::File& file,
    493                  const gfx::Size& content_size,
    494                  const float scale,
    495                  skia::RefPtr<SkPixelRef> compressed_data) {
    496   if (!file.IsValid())
    497     return false;
    498 
    499   if (!WriteBigEndianToFile(file, kCompressedKey))
    500     return false;
    501 
    502   if (!WriteBigEndianToFile(file, content_size.width()))
    503     return false;
    504 
    505   if (!WriteBigEndianToFile(file, content_size.height()))
    506     return false;
    507 
    508   // Write ETC1 header.
    509   compressed_data->lockPixels();
    510 
    511   unsigned char etc1_buffer[ETC_PKM_HEADER_SIZE];
    512   etc1_pkm_format_header(etc1_buffer,
    513                          compressed_data->info().width(),
    514                          compressed_data->info().height());
    515 
    516   int header_bytes_written = file.WriteAtCurrentPos(
    517       reinterpret_cast<char*>(etc1_buffer), ETC_PKM_HEADER_SIZE);
    518   if (header_bytes_written != ETC_PKM_HEADER_SIZE)
    519     return false;
    520 
    521   int data_size = etc1_get_encoded_data_size(
    522       compressed_data->info().width(),
    523       compressed_data->info().height());
    524   int pixel_bytes_written = file.WriteAtCurrentPos(
    525       reinterpret_cast<char*>(compressed_data->pixels()),
    526       data_size);
    527   if (pixel_bytes_written != data_size)
    528     return false;
    529 
    530   compressed_data->unlockPixels();
    531 
    532   if (!WriteBigEndianToFile(file, kCurrentExtraVersion))
    533     return false;
    534 
    535   if (!WriteBigEndianFloatToFile(file, 1.f / scale))
    536     return false;
    537 
    538   return true;
    539 }
    540 
    541 }  // anonymous namespace
    542 
    543 void ThumbnailStore::WriteTask(const base::FilePath& file_path,
    544                                skia::RefPtr<SkPixelRef> compressed_data,
    545                                float scale,
    546                                const gfx::Size& content_size,
    547                                const base::Callback<void()>& post_write_task) {
    548   DCHECK(compressed_data);
    549 
    550   base::File file(file_path,
    551                   base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
    552 
    553   bool success = WriteToFile(file,
    554                              content_size,
    555                              scale,
    556                              compressed_data);
    557 
    558   file.Close();
    559 
    560   if (!success)
    561     base::DeleteFile(file_path, false);
    562 
    563   content::BrowserThread::PostTask(
    564       content::BrowserThread::UI, FROM_HERE, post_write_task);
    565 }
    566 
    567 void ThumbnailStore::PostWriteTask() {
    568   write_tasks_count_--;
    569 }
    570 
    571 void ThumbnailStore::CompressionTask(
    572     SkBitmap raw_data,
    573     gfx::Size encoded_size,
    574     const base::Callback<void(skia::RefPtr<SkPixelRef>, const gfx::Size&)>&
    575         post_compression_task) {
    576   skia::RefPtr<SkPixelRef> compressed_data;
    577   gfx::Size content_size;
    578 
    579   if (!raw_data.empty()) {
    580     SkAutoLockPixels raw_data_lock(raw_data);
    581     gfx::Size raw_data_size(raw_data.width(), raw_data.height());
    582     size_t pixel_size = 4;  // Pixel size is 4 bytes for kARGB_8888_Config.
    583     size_t stride = pixel_size * raw_data_size.width();
    584 
    585     size_t encoded_bytes =
    586         etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height());
    587     SkImageInfo info = SkImageInfo::Make(encoded_size.width(),
    588                                          encoded_size.height(),
    589                                          kUnknown_SkColorType,
    590                                          kUnpremul_SkAlphaType);
    591     skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef(
    592         SkData::NewUninitialized(encoded_bytes));
    593     skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef(
    594         SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get()));
    595 
    596     etc1_pixel_ref->lockPixels();
    597     bool success = etc1_encode_image(
    598         reinterpret_cast<unsigned char*>(raw_data.getPixels()),
    599         raw_data_size.width(),
    600         raw_data_size.height(),
    601         pixel_size,
    602         stride,
    603         reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()),
    604         encoded_size.width(),
    605         encoded_size.height());
    606     etc1_pixel_ref->setImmutable();
    607     etc1_pixel_ref->unlockPixels();
    608 
    609     if (success) {
    610       compressed_data = etc1_pixel_ref;
    611       content_size = raw_data_size;
    612     }
    613   }
    614 
    615   content::BrowserThread::PostTask(
    616       content::BrowserThread::UI,
    617       FROM_HERE,
    618       base::Bind(post_compression_task, compressed_data, content_size));
    619 }
    620 
    621 void ThumbnailStore::PostCompressionTask(
    622     TabId tab_id,
    623     const base::Time& time_stamp,
    624     float scale,
    625     skia::RefPtr<SkPixelRef> compressed_data,
    626     const gfx::Size& content_size) {
    627   compression_tasks_count_--;
    628   if (!compressed_data) {
    629     RemoveOnMatchedTimeStamp(tab_id, time_stamp);
    630     return;
    631   }
    632 
    633   Thumbnail* thumbnail = cache_.Get(tab_id);
    634   if (thumbnail) {
    635     if (thumbnail->time_stamp() != time_stamp)
    636       return;
    637     thumbnail->SetCompressedBitmap(compressed_data, content_size);
    638     thumbnail->CreateUIResource();
    639   }
    640   WriteThumbnailIfNecessary(tab_id, compressed_data, scale, content_size);
    641 }
    642 
    643 namespace {
    644 
    645 bool ReadFromFile(base::File& file,
    646                   gfx::Size* out_content_size,
    647                   float* out_scale,
    648                   skia::RefPtr<SkPixelRef>* out_pixels) {
    649   if (!file.IsValid())
    650     return false;
    651 
    652   int key = 0;
    653   if (!ReadBigEndianFromFile(file, &key))
    654     return false;
    655 
    656   if (key != kCompressedKey)
    657     return false;
    658 
    659   int content_width = 0;
    660   if (!ReadBigEndianFromFile(file, &content_width) || content_width <= 0)
    661     return false;
    662 
    663   int content_height = 0;
    664   if (!ReadBigEndianFromFile(file, &content_height) || content_height <= 0)
    665     return false;
    666 
    667   out_content_size->SetSize(content_width, content_height);
    668 
    669   // Read ETC1 header.
    670   int header_bytes_read = 0;
    671   unsigned char etc1_buffer[ETC_PKM_HEADER_SIZE];
    672   header_bytes_read = file.ReadAtCurrentPos(
    673       reinterpret_cast<char*>(etc1_buffer),
    674       ETC_PKM_HEADER_SIZE);
    675   if (header_bytes_read != ETC_PKM_HEADER_SIZE)
    676     return false;
    677 
    678   if (!etc1_pkm_is_valid(etc1_buffer))
    679     return false;
    680 
    681   int raw_width = 0;
    682   raw_width = etc1_pkm_get_width(etc1_buffer);
    683   if (raw_width <= 0)
    684     return false;
    685 
    686   int raw_height = 0;
    687   raw_height = etc1_pkm_get_height(etc1_buffer);
    688   if (raw_height <= 0)
    689     return false;
    690 
    691   // Do some simple sanity check validation.  We can't have thumbnails larger
    692   // than the max display size of the screen.  We also can't have etc1 texture
    693   // data larger than the next power of 2 up from that.
    694   gfx::DeviceDisplayInfo display_info;
    695   int max_dimension = std::max(display_info.GetDisplayWidth(),
    696                                display_info.GetDisplayHeight());
    697 
    698   if (content_width > max_dimension
    699       || content_height > max_dimension
    700       || static_cast<size_t>(raw_width) > NextPowerOfTwo(max_dimension)
    701       || static_cast<size_t>(raw_height) > NextPowerOfTwo(max_dimension)) {
    702     return false;
    703   }
    704 
    705   int data_size = etc1_get_encoded_data_size(raw_width, raw_height);
    706   skia::RefPtr<SkData> etc1_pixel_data =
    707       skia::AdoptRef(SkData::NewUninitialized(data_size));
    708 
    709   int pixel_bytes_read = file.ReadAtCurrentPos(
    710       reinterpret_cast<char*>(etc1_pixel_data->writable_data()),
    711       data_size);
    712 
    713   if (pixel_bytes_read != data_size)
    714     return false;
    715 
    716   SkImageInfo info = SkImageInfo::Make(raw_width,
    717                                        raw_height,
    718                                        kUnknown_SkColorType,
    719                                        kUnpremul_SkAlphaType);
    720 
    721   *out_pixels = skia::AdoptRef(
    722       SkMallocPixelRef::NewWithData(info,
    723                                     0,
    724                                     NULL,
    725                                     etc1_pixel_data.get()));
    726 
    727   int extra_data_version = 0;
    728   if (!ReadBigEndianFromFile(file, &extra_data_version))
    729     return false;
    730 
    731   *out_scale = 1.f;
    732   if (extra_data_version == 1) {
    733     if (!ReadBigEndianFloatFromFile(file, out_scale))
    734       return false;
    735 
    736     if (*out_scale == 0.f)
    737       return false;
    738 
    739     *out_scale = 1.f / *out_scale;
    740   }
    741 
    742   return true;
    743 }
    744 
    745 }// anonymous namespace
    746 
    747 void ThumbnailStore::ReadTask(
    748     bool decompress,
    749     const base::FilePath& file_path,
    750     const base::Callback<
    751         void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>&
    752         post_read_task) {
    753   gfx::Size content_size;
    754   float scale = 0.f;
    755   skia::RefPtr<SkPixelRef> compressed_data;
    756 
    757   if (base::PathExists(file_path)) {
    758     base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
    759 
    760 
    761     bool valid_contents = ReadFromFile(file,
    762                                        &content_size,
    763                                        &scale,
    764                                        &compressed_data);
    765     file.Close();
    766 
    767     if (!valid_contents) {
    768       content_size.SetSize(0, 0);
    769             scale = 0.f;
    770             compressed_data.clear();
    771             base::DeleteFile(file_path, false);
    772     }
    773   }
    774 
    775   if (decompress) {
    776     base::WorkerPool::PostTask(
    777         FROM_HERE,
    778         base::Bind(post_read_task, compressed_data, scale, content_size),
    779         true);
    780   } else {
    781     content::BrowserThread::PostTask(
    782         content::BrowserThread::UI,
    783         FROM_HERE,
    784         base::Bind(post_read_task, compressed_data, scale, content_size));
    785   }
    786 }
    787 
    788 void ThumbnailStore::PostReadTask(TabId tab_id,
    789                                   skia::RefPtr<SkPixelRef> compressed_data,
    790                                   float scale,
    791                                   const gfx::Size& content_size) {
    792   read_in_progress_ = false;
    793 
    794   TabIdList::iterator iter =
    795       std::find(read_queue_.begin(), read_queue_.end(), tab_id);
    796   if (iter == read_queue_.end()) {
    797     ReadNextThumbnail();
    798     return;
    799   }
    800 
    801   read_queue_.erase(iter);
    802 
    803   if (!cache_.Get(tab_id) && compressed_data) {
    804     ThumbnailMetaDataMap::iterator meta_iter =
    805         thumbnail_meta_data_.find(tab_id);
    806     base::Time time_stamp = base::Time::Now();
    807     if (meta_iter != thumbnail_meta_data_.end())
    808       time_stamp = meta_iter->second.capture_time();
    809 
    810     MakeSpaceForNewItemIfNecessary(tab_id);
    811     scoped_ptr<Thumbnail> thumbnail = Thumbnail::Create(
    812         tab_id, time_stamp, scale, ui_resource_provider_, this);
    813     thumbnail->SetCompressedBitmap(compressed_data,
    814                                    content_size);
    815     if (kPreferCPUMemory)
    816       thumbnail->CreateUIResource();
    817 
    818     cache_.Put(tab_id, thumbnail.Pass());
    819     NotifyObserversOfThumbnailRead(tab_id);
    820   }
    821 
    822   ReadNextThumbnail();
    823 }
    824 
    825 void ThumbnailStore::NotifyObserversOfThumbnailRead(TabId tab_id) {
    826   FOR_EACH_OBSERVER(
    827       ThumbnailStoreObserver, observers_, OnFinishedThumbnailRead(tab_id));
    828 }
    829 
    830 void ThumbnailStore::RemoveOnMatchedTimeStamp(TabId tab_id,
    831                                               const base::Time& time_stamp) {
    832   // We remove the cached version if it matches the tab_id and the time_stamp.
    833   Thumbnail* thumbnail = cache_.Get(tab_id);
    834   Thumbnail* approx_thumbnail = approximation_cache_.Get(tab_id);
    835   if ((thumbnail && thumbnail->time_stamp() == time_stamp) ||
    836       (approx_thumbnail && approx_thumbnail->time_stamp() == time_stamp)) {
    837     Remove(tab_id);
    838   }
    839   return;
    840 }
    841 
    842 void ThumbnailStore::DecompressionTask(
    843     const base::Callback<void(bool, SkBitmap)>&
    844         post_decompression_callback,
    845     skia::RefPtr<SkPixelRef> compressed_data,
    846     float scale,
    847     const gfx::Size& content_size) {
    848   SkBitmap raw_data_small;
    849   bool success = false;
    850 
    851   if (compressed_data.get()) {
    852     gfx::Size buffer_size = gfx::Size(compressed_data->info().width(),
    853                                       compressed_data->info().height());
    854 
    855     SkBitmap raw_data;
    856     raw_data.allocPixels(SkImageInfo::Make(buffer_size.width(),
    857                                            buffer_size.height(),
    858                                            kRGBA_8888_SkColorType,
    859                                            kOpaque_SkAlphaType));
    860     SkAutoLockPixels raw_data_lock(raw_data);
    861     compressed_data->lockPixels();
    862     success = etc1_decode_image(
    863         reinterpret_cast<unsigned char*>(compressed_data->pixels()),
    864         reinterpret_cast<unsigned char*>(raw_data.getPixels()),
    865         buffer_size.width(),
    866         buffer_size.height(),
    867         raw_data.bytesPerPixel(),
    868         raw_data.rowBytes());
    869     compressed_data->unlockPixels();
    870     raw_data.setImmutable();
    871 
    872     if (!success) {
    873       // Leave raw_data_small empty for consistency with other failure modes.
    874     } else if (content_size == buffer_size) {
    875       // Shallow copy the pixel reference.
    876       raw_data_small = raw_data;
    877     } else {
    878       // The content size is smaller than the buffer size (likely because of
    879       // a power-of-two rounding), so deep copy the bitmap.
    880       raw_data_small.allocPixels(SkImageInfo::Make(content_size.width(),
    881                                                    content_size.height(),
    882                                                    kRGBA_8888_SkColorType,
    883                                                    kOpaque_SkAlphaType));
    884       SkAutoLockPixels raw_data_small_lock(raw_data_small);
    885       SkCanvas small_canvas(raw_data_small);
    886       small_canvas.drawBitmap(raw_data, 0, 0);
    887       raw_data_small.setImmutable();
    888     }
    889   }
    890 
    891   content::BrowserThread::PostTask(
    892       content::BrowserThread::UI,
    893       FROM_HERE,
    894       base::Bind(post_decompression_callback, success, raw_data_small));
    895 }
    896 
    897 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData() {
    898 }
    899 
    900 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData(
    901     const base::Time& current_time,
    902     const GURL& url)
    903     : capture_time_(current_time), url_(url) {
    904 }
    905 
    906 std::pair<SkBitmap, float> ThumbnailStore::CreateApproximation(
    907     const SkBitmap& bitmap,
    908     float scale) {
    909   DCHECK(!bitmap.empty());
    910   DCHECK_GT(scale, 0);
    911   SkAutoLockPixels bitmap_lock(bitmap);
    912   float new_scale = 1.f / kApproximationScaleFactor;
    913 
    914   gfx::Size dst_size = gfx::ToFlooredSize(
    915       gfx::ScaleSize(gfx::Size(bitmap.width(), bitmap.height()), new_scale));
    916   SkBitmap dst_bitmap;
    917   dst_bitmap.allocPixels(SkImageInfo::Make(dst_size.width(),
    918                                            dst_size.height(),
    919                                            bitmap.info().fColorType,
    920                                            bitmap.info().fAlphaType));
    921   dst_bitmap.eraseColor(0);
    922   SkAutoLockPixels dst_bitmap_lock(dst_bitmap);
    923 
    924   SkCanvas canvas(dst_bitmap);
    925   canvas.scale(new_scale, new_scale);
    926   canvas.drawBitmap(bitmap, 0, 0, NULL);
    927   dst_bitmap.setImmutable();
    928 
    929   return std::make_pair(dst_bitmap, new_scale * scale);
    930 }
    931