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