• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
NextPowerOfTwo(size_t x)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 
RoundUpMod4(size_t x)51 size_t RoundUpMod4(size_t x) {
52   return (x + 3) & ~3;
53 }
54 
GetEncodedSize(const gfx::Size & bitmap_size,bool supports_npot)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>
ReadBigEndianFromFile(base::File & file,T * out)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>
WriteBigEndianToFile(base::File & file,T val)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 
ReadBigEndianFloatFromFile(base::File & file,float * out)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 
WriteBigEndianFloatToFile(base::File & file,float val)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 
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)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 
~ThumbnailStore()134 ThumbnailStore::~ThumbnailStore() {
135   SetUIResourceProvider(NULL);
136 }
137 
SetUIResourceProvider(content::UIResourceProvider * ui_resource_provider)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 
AddThumbnailStoreObserver(ThumbnailStoreObserver * observer)149 void ThumbnailStore::AddThumbnailStoreObserver(
150     ThumbnailStoreObserver* observer) {
151   if (!observers_.HasObserver(observer))
152     observers_.AddObserver(observer);
153 }
154 
RemoveThumbnailStoreObserver(ThumbnailStoreObserver * observer)155 void ThumbnailStore::RemoveThumbnailStoreObserver(
156     ThumbnailStoreObserver* observer) {
157   if (observers_.HasObserver(observer))
158     observers_.RemoveObserver(observer);
159 }
160 
Put(TabId tab_id,const SkBitmap & bitmap,float thumbnail_scale)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 
Remove(TabId tab_id)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 
Get(TabId tab_id,bool force_disk_read,bool allow_approximation)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 
RemoveFromDiskAtAndAboveId(TabId min_id)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 
InvalidateThumbnailIfChanged(TabId tab_id,const GURL & url)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 
CheckAndUpdateThumbnailMetaData(TabId tab_id,const GURL & url)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 
UpdateVisibleIds(const TabIdList & priority)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 
DecompressThumbnailFromFile(TabId tab_id,const base::Callback<void (bool,SkBitmap)> & post_decompress_callback)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 
RemoveFromDisk(TabId tab_id)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 
RemoveFromDiskTask(const base::FilePath & file_path)332 void ThumbnailStore::RemoveFromDiskTask(const base::FilePath& file_path) {
333   if (base::PathExists(file_path))
334     base::DeleteFile(file_path, false);
335 }
336 
RemoveFromDiskAtAndAboveIdTask(const base::FilePath & dir_path,TabId min_id)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 
WriteThumbnailIfNecessary(TabId tab_id,skia::RefPtr<SkPixelRef> compressed_data,float scale,const gfx::Size & content_size)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 
CompressThumbnailIfNecessary(TabId tab_id,const base::Time & time_stamp,const SkBitmap & bitmap,float scale)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 
ReadNextThumbnail()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 
MakeSpaceForNewItemIfNecessary(TabId tab_id)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 
RemoveFromReadQueue(TabId tab_id)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 
InvalidateCachedThumbnail(Thumbnail * thumbnail)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 
GetFilePath(TabId tab_id) const486 base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const {
487   return disk_cache_path_.Append(base::IntToString(tab_id));
488 }
489 
490 namespace {
491 
WriteToFile(base::File & file,const gfx::Size & content_size,const float scale,skia::RefPtr<SkPixelRef> compressed_data)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 
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)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 
PostWriteTask()567 void ThumbnailStore::PostWriteTask() {
568   write_tasks_count_--;
569 }
570 
CompressionTask(SkBitmap raw_data,gfx::Size encoded_size,const base::Callback<void (skia::RefPtr<SkPixelRef>,const gfx::Size &)> & post_compression_task)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 
PostCompressionTask(TabId tab_id,const base::Time & time_stamp,float scale,skia::RefPtr<SkPixelRef> compressed_data,const gfx::Size & content_size)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 
ReadFromFile(base::File & file,gfx::Size * out_content_size,float * out_scale,skia::RefPtr<SkPixelRef> * out_pixels)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 
ReadTask(bool decompress,const base::FilePath & file_path,const base::Callback<void (skia::RefPtr<SkPixelRef>,float,const gfx::Size &)> & post_read_task)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 
PostReadTask(TabId tab_id,skia::RefPtr<SkPixelRef> compressed_data,float scale,const gfx::Size & content_size)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 
NotifyObserversOfThumbnailRead(TabId tab_id)825 void ThumbnailStore::NotifyObserversOfThumbnailRead(TabId tab_id) {
826   FOR_EACH_OBSERVER(
827       ThumbnailStoreObserver, observers_, OnFinishedThumbnailRead(tab_id));
828 }
829 
RemoveOnMatchedTimeStamp(TabId tab_id,const base::Time & time_stamp)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 
DecompressionTask(const base::Callback<void (bool,SkBitmap)> & post_decompression_callback,skia::RefPtr<SkPixelRef> compressed_data,float scale,const gfx::Size & content_size)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 
ThumbnailMetaData()897 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData() {
898 }
899 
ThumbnailMetaData(const base::Time & current_time,const GURL & url)900 ThumbnailStore::ThumbnailMetaData::ThumbnailMetaData(
901     const base::Time& current_time,
902     const GURL& url)
903     : capture_time_(current_time), url_(url) {
904 }
905 
CreateApproximation(const SkBitmap & bitmap,float scale)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