Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(345)

Side by Side Diff: chrome/browser/android/thumbnail/thumbnail_cache.cc

Issue 262543002: android: add ThumbnailCache (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@codec
Patch Set: addressed comments Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/android/thumbnail/thumbnail_cache.h"
6
7 #include <algorithm>
8 #include <cmath>
9
10 #include "base/file_util.h"
11 #include "base/files/file.h"
12 #include "base/files/file_enumerator.h"
13 #include "base/files/file_path.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "content/public/browser/android/ui_resource_provider.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "third_party/android_opengl/etc1/etc1.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #include "third_party/skia/include/core/SkCanvas.h"
21 #include "third_party/skia/include/core/SkData.h"
22 #include "third_party/skia/include/core/SkMallocPixelRef.h"
23 #include "third_party/skia/include/core/SkPixelRef.h"
24 #include "ui/gfx/geometry/size_conversions.h"
25
26 namespace {
27
28 const float kApproximationScaleFactor = 4.f;
29 const base::TimeDelta kCaptureMinRequestTimeMs(
30 base::TimeDelta::FromMilliseconds(1000));
31 const Thumbnail kInvalidThumbnail;
32
33 const int kCompressedKey = 0xABABABAB;
34 const int kDecompressedKey = 0xCDCDCDCD;
35
36 // ETC1 texture sizes are multiples of four.
37 size_t NextETC1Size(size_t s) {
38 return (s / 4 + (s % 4 ? 1 : 0)) * 4;
39 }
40
41 gfx::Size GetEncodedSize(const gfx::Size& bitmap_size) {
42 return gfx::Size(NextETC1Size(bitmap_size.width()),
43 NextETC1Size(bitmap_size.height()));
44 }
45
46 } // anonymous namespace
47
48 ThumbnailCache::ThumbnailCache(const std::string& disk_cache_path_str,
49 size_t default_cache_size,
50 size_t approximation_cache_size,
51 size_t compression_queue_max_size,
52 size_t write_queue_max_size,
53 bool use_approximation_thumbnail)
54 : disk_cache_path_(disk_cache_path_str),
55 compression_queue_max_size_(compression_queue_max_size),
56 write_queue_max_size_(write_queue_max_size),
57 use_approximation_thumbnail_(use_approximation_thumbnail),
58 compression_tasks_count_(0),
59 write_tasks_count_(0),
60 cache_(default_cache_size),
61 approximation_cache_(approximation_cache_size),
62 ui_resource_provider_(NULL),
63 compression_thread_("thumbnail_compression"),
64 weak_factory_(this) {
65 compression_thread_.Start();
66 }
67
68 ThumbnailCache::~ThumbnailCache() {
69 compression_thread_.Stop();
70 SetUIResourceProvider(NULL);
71 }
72
73 void ThumbnailCache::SetUIResourceProvider(
74 content::UIResourceProvider* ui_resource_provider) {
75 if (ui_resource_provider_ == ui_resource_provider)
76 return;
77
78 approximation_cache_.Clear();
79 cache_.Clear();
80
81 ui_resource_provider_ = ui_resource_provider;
82 }
83
84 void ThumbnailCache::AddThumbnailCacheObserver(
85 ThumbnailCacheObserver* observer) {
86 if (!observers_.HasObserver(observer))
87 observers_.AddObserver(observer);
88 }
89
90 void ThumbnailCache::RemoveThumbnailCacheObserver(
91 ThumbnailCacheObserver* observer) {
92 if (observers_.HasObserver(observer))
93 observers_.RemoveObserver(observer);
94 }
95
96 void ThumbnailCache::Put(TabId tab_id,
97 const SkBitmap& bitmap,
98 float thumbnail_scale) {
99 if (!ui_resource_provider_ || bitmap.empty() || thumbnail_scale <= 0)
100 return;
101
102 DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end());
103
104 base::Time time_stamp = thumbnail_meta_data_[tab_id].capture_time();
105
106 Thumbnail thumbnail(
107 tab_id, time_stamp, thumbnail_scale, ui_resource_provider_, this);
108 thumbnail.SetBitmap(bitmap);
109
110 RemoveFromReadQueue(tab_id);
111 MakeSpaceForNewItemIfNecessary(tab_id);
112 cache_.Put(tab_id, thumbnail);
David Trainor- moved to gerrit 2014/07/14 20:30:46 I'm not a huge fan of using references instead of
powei 2014/07/15 20:06:09 Done. Note that lru_expiring_cache is now scoped_
113
114 if (use_approximation_thumbnail_) {
115 std::pair<SkBitmap, float> approximation =
116 CreateApproximation(bitmap, thumbnail_scale);
117 Thumbnail approx_thumbnail(tab_id,
118 time_stamp,
119 approximation.second,
120 ui_resource_provider_,
121 this);
122 approx_thumbnail.SetBitmap(approximation.first);
123 approximation_cache_.Put(tab_id, approx_thumbnail);
124 }
125 CompressThumbnailIfNecessary(tab_id, bitmap, thumbnail_scale, time_stamp);
126 }
127
128 void ThumbnailCache::Remove(TabId tab_id) {
129 cache_.Remove(tab_id);
130 approximation_cache_.Remove(tab_id);
131 thumbnail_meta_data_.erase(tab_id);
132 RemoveFromDisk(tab_id);
133 RemoveFromReadQueue(tab_id);
134 }
135
136 const Thumbnail& ThumbnailCache::Get(TabId tab_id) {
David Trainor- moved to gerrit 2014/07/14 20:30:46 This feels a bit heavyweight, as in someone has to
powei 2014/07/15 20:06:09 Done.
137 if (cache_.Contains(tab_id)) {
138 Thumbnail& thumbnail = cache_.Get(tab_id);
139 thumbnail.CreateUIResource();
140 return thumbnail;
141 }
142
143 if (written_thumbnails_.find(tab_id) != written_thumbnails_.end() &&
David Trainor- moved to gerrit 2014/07/14 20:30:46 written_thumbnails_ won't be up to date if we rest
powei 2014/07/14 23:56:25 This use to fallback to just checking the disk, bu
powei 2014/07/15 20:06:09 |written_thumbnails_| is a misnomer. It's more li
David Trainor- moved to gerrit 2014/07/15 20:08:24 What kind of regression? Couldn't the read task j
powei 2014/07/18 00:51:49 Currently we ask for the *static page* (i.e. thumb
144 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) !=
145 visible_ids_.end() &&
146 std::find(read_queue_.begin(), read_queue_.end(), tab_id) ==
147 read_queue_.end()) {
148 read_queue_.push_back(tab_id);
149 ReadNextThumbnail();
150 }
151
152 if (approximation_cache_.Contains(tab_id)) {
153 Thumbnail& thumbnail = approximation_cache_.Get(tab_id);
154 thumbnail.CreateUIResource();
155 return thumbnail;
156 }
157
158 return kInvalidThumbnail;
159 }
160
161 void ThumbnailCache::RemoveFromDiskAtAndAboveId(TabId min_id) {
162 base::Closure remove_task =
163 base::Bind(&ThumbnailCache::RemoveFromDiskAtAndAboveIdTask,
164 disk_cache_path_,
165 min_id);
166 content::BrowserThread::PostTask(
167 content::BrowserThread::FILE, FROM_HERE, remove_task);
168 }
169
170 void ThumbnailCache::InvalidateThumbnailIfChanged(TabId tab_id,
171 const GURL& url) {
172 ThumbnailMetaDataMap::iterator meta_data_iter =
173 thumbnail_meta_data_.find(tab_id);
174 if (meta_data_iter == thumbnail_meta_data_.end()) {
175 thumbnail_meta_data_[tab_id] = ThumbnailMetaData(base::Time(), url);
176 } else if (meta_data_iter->second.url() != url) {
177 Remove(tab_id);
178 }
179 }
180
181 bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(TabId tab_id,
182 const GURL& url) {
183 base::Time current_time = base::Time::Now();
184 ThumbnailMetaDataMap::iterator meta_data_iter =
185 thumbnail_meta_data_.find(tab_id);
186 if (meta_data_iter != thumbnail_meta_data_.end() &&
187 meta_data_iter->second.url() == url &&
188 (current_time - meta_data_iter->second.capture_time()) <
189 kCaptureMinRequestTimeMs) {
190 return false;
191 }
192
193 thumbnail_meta_data_[tab_id] = ThumbnailMetaData(current_time, url);
194 return true;
195 }
196
197 void ThumbnailCache::UpdateVisibleIds(const TabIdList& priority) {
198 if (priority.empty()) {
199 visible_ids_.clear();
200 return;
201 }
202
203 size_t ids_size = std::min(priority.size(), cache_.MaximumCacheSize());
204 if (visible_ids_.size() == ids_size) {
205 // Early out if called with the same input as last time (We only care
206 // about the first mCache.MaximumCacheSize() entries).
207 bool lists_differ = false;
208 TabIdList::const_iterator visible_iter = visible_ids_.begin();
209 TabIdList::const_iterator priority_iter = priority.begin();
210 while (visible_iter != visible_ids_.end() &&
211 priority_iter != priority.end()) {
212 if (*priority_iter != *visible_iter) {
213 lists_differ = true;
214 break;
215 }
216 visible_iter++;
217 priority_iter++;
218 }
219
220 if (!lists_differ)
221 return;
222 }
223
224 read_queue_.clear();
225 visible_ids_.clear();
226 size_t count = 0;
227 TabIdList::const_iterator iter = priority.begin();
228 while (iter != priority.end() && count < ids_size) {
229 TabId tab_id = *iter;
230 visible_ids_.push_back(tab_id);
231 if (!cache_.Contains(tab_id) &&
232 std::find(read_queue_.begin(), read_queue_.end(), tab_id) ==
233 read_queue_.end()) {
234 read_queue_.push_back(tab_id);
235 }
236 iter++;
237 count++;
238 }
239
240 ReadNextThumbnail();
241 }
242
243 void ThumbnailCache::RemoveFromDisk(TabId tab_id) {
244 if (written_thumbnails_.find(tab_id) == written_thumbnails_.end())
245 return;
246
247 written_thumbnails_.erase(tab_id);
248
249 base::FilePath file_path = GetFilePath(tab_id);
250 base::Closure task =
251 base::Bind(&ThumbnailCache::RemoveFromDiskTask, file_path);
252 content::BrowserThread::PostTask(
253 content::BrowserThread::FILE, FROM_HERE, task);
254 }
255
256 void ThumbnailCache::RemoveFromDiskTask(const base::FilePath& file_path) {
257 DCHECK(base::PathExists(file_path));
258 base::DeleteFile(file_path, false);
259 }
260
261 void ThumbnailCache::RemoveFromDiskAtAndAboveIdTask(
262 const base::FilePath& dir_path,
263 TabId min_id) {
264 base::FileEnumerator enumerator(dir_path, false, base::FileEnumerator::FILES);
265 while (true) {
266 base::FilePath path = enumerator.Next();
267 if (path.empty())
268 break;
269 base::FileEnumerator::FileInfo info = enumerator.GetInfo();
270 TabId tab_id;
271 bool success = base::StringToInt(info.GetName().value(), &tab_id);
272 if (success && tab_id >= min_id)
273 base::DeleteFile(path, false);
274 }
275 }
276
277 void ThumbnailCache::WriteThumbnailIfNecessary(
278 TabId tab_id,
279 skia::RefPtr<SkPixelRef> compressed_data,
280 float scale,
281 const gfx::Size& content_size) {
282 if (write_tasks_count_ >= write_queue_max_size_)
David Trainor- moved to gerrit 2014/07/14 20:30:46 What do we do if we're over this limit? Do we jus
powei 2014/07/14 23:56:25 Yes, it is just never written. Maybe we'll need t
powei 2014/07/15 20:06:09 Actually, I think we can live with not writing it
David Trainor- moved to gerrit 2014/07/15 20:08:23 Do these max write queue/compress queue checks do
powei 2014/07/18 00:51:49 I did some manual testing. We do hit the compress
David Trainor- moved to gerrit 2014/07/18 17:03:59 Oh interesting... if we're peaked out in the compr
283 return;
284
285 write_tasks_count_++;
286
287 scoped_refptr<WriteTaskResult> result =
288 make_scoped_refptr(new WriteTaskResult());
289 base::Closure write_task = base::Bind(&ThumbnailCache::WriteTask,
290 GetFilePath(tab_id),
291 compressed_data,
David Trainor- moved to gerrit 2014/07/14 20:30:46 Huh is skia::RefPtr<> thread safe? I just realize
powei 2014/07/14 23:56:25 SkRefCnt does use atomic operations and barrier fo
David Trainor- moved to gerrit 2014/07/18 17:03:59 w00t
292 scale,
293 content_size,
294 result);
295 base::Closure post_write_task = base::Bind(&ThumbnailCache::PostWriteTask,
296 weak_factory_.GetWeakPtr(),
297 tab_id,
298 result);
299
300 content::BrowserThread::PostTaskAndReply(
301 content::BrowserThread::FILE, FROM_HERE, write_task, post_write_task);
302 }
303
304 void ThumbnailCache::CompressThumbnailIfNecessary(TabId tab_id,
305 const SkBitmap& bitmap,
306 float scale,
307 base::Time& time_stamp) {
308 if (compression_tasks_count_ >= compression_queue_max_size_)
David Trainor- moved to gerrit 2014/07/14 20:30:46 What do we do if we're over this limit? If we jus
powei 2014/07/14 23:56:25 Yeah, maybe we'll need to drop if the approximated
powei 2014/07/15 20:06:09 Done. Added line to remove the cached version.
David Trainor- moved to gerrit 2014/07/15 20:08:23 Yeah that makes sense :)
309 return;
310
311 compression_tasks_count_++;
312
313 scoped_refptr<CompressedDataTransport> transport =
314 make_scoped_refptr(new CompressedDataTransport());
315 base::Closure compression_task =
316 base::Bind(&ThumbnailCache::CompressionTask, bitmap, transport);
317 base::Closure post_compression_task =
318 base::Bind(&ThumbnailCache::PostCompressionTask,
319 weak_factory_.GetWeakPtr(),
320 tab_id,
321 scale,
322 time_stamp,
323 transport);
David Trainor- moved to gerrit 2014/07/14 20:30:46 Is this the only way to do this? Do you need to k
powei 2014/07/14 23:56:25 Closure should have no unbound parameters (https:/
David Trainor- moved to gerrit 2014/07/15 20:08:24 Do you have to use PostTaskAndReply? Would it be
powei 2014/07/18 00:51:49 Done. Pass the Post*Task() as callback params to
324
325 DCHECK(compression_thread_.message_loop_proxy());
326 compression_thread_.message_loop_proxy()->PostTaskAndReply(
327 FROM_HERE, compression_task, post_compression_task);
328 }
329
330 void ThumbnailCache::ReadNextThumbnail() {
331 if (read_queue_.empty())
David Trainor- moved to gerrit 2014/07/15 20:08:24 I'm still a bit confused by this queue :(. It mig
powei 2014/07/18 00:51:49 I added a boolean to indicate that a read is curre
332 return;
333
334 TabId tab_id = read_queue_.front();
335
336 scoped_refptr<CompressedDataTransport> transport =
337 make_scoped_refptr(new CompressedDataTransport());
338 base::FilePath file_path = GetFilePath(tab_id);
339 base::Closure read_task =
340 base::Bind(&ThumbnailCache::ReadTask, file_path, transport);
341 base::Closure post_read_task = base::Bind(&ThumbnailCache::PostReadTask,
342 weak_factory_.GetWeakPtr(),
343 tab_id,
344 transport);
345
346 content::BrowserThread::PostTaskAndReply(
347 content::BrowserThread::FILE, FROM_HERE, read_task, post_read_task);
348 }
349
350 void ThumbnailCache::MakeSpaceForNewItemIfNecessary(TabId tab_id) {
351 if (cache_.Contains(tab_id) ||
352 std::find(visible_ids_.begin(), visible_ids_.end(), tab_id) ==
353 visible_ids_.end() ||
354 cache_.size() < cache_.MaximumCacheSize()) {
355 return;
356 }
357
358 TabId key_to_remove;
359 bool found_key_to_remove = false;
360
361 // 1. Find a cached item not in this list
362 for (ExpiringThumbnailCache::iterator iter = cache_.begin();
363 iter != cache_.end();
364 iter++) {
365 if (std::find(visible_ids_.begin(), visible_ids_.end(), iter->first) ==
366 visible_ids_.end()) {
367 key_to_remove = iter->first;
368 found_key_to_remove = true;
369 break;
370 }
371 }
372
373 if (!found_key_to_remove) {
374 // 2. Find the least important id we can remove.
375 for (TabIdList::reverse_iterator riter = visible_ids_.rbegin();
376 riter != visible_ids_.rend();
377 riter++) {
378 if (cache_.Contains(*riter)) {
379 key_to_remove = *riter;
380 break;
381 found_key_to_remove = true;
382 }
383 }
384 }
385
386 if (found_key_to_remove)
387 cache_.Remove(key_to_remove);
388 }
389
390 void ThumbnailCache::RemoveFromReadQueue(TabId tab_id) {
391 TabIdList::iterator read_iter =
392 std::find(read_queue_.begin(), read_queue_.end(), tab_id);
393 if (read_iter != read_queue_.end())
394 read_queue_.erase(read_iter);
395 }
396
397 void ThumbnailCache::InvalidateCachedThumbnail(const Thumbnail& thumbnail) {
398 TabId tab_id = thumbnail.tab_id();
399 base::Time time_stamp = thumbnail.time_stamp();
400
401 if (cache_.Contains(tab_id) && cache_.Get(tab_id).time_stamp() == time_stamp)
402 cache_.Remove(tab_id);
403
404 if (approximation_cache_.Contains(tab_id) &&
405 approximation_cache_.Get(tab_id).time_stamp() == time_stamp)
406 approximation_cache_.Remove(tab_id);
407 }
408
409 base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) const {
410 return disk_cache_path_.Append(base::IntToString(tab_id));
411 }
412
413 void ThumbnailCache::WriteTask(
414 const base::FilePath& file_path,
415 skia::RefPtr<SkPixelRef> compressed_data,
416 float scale,
417 const gfx::Size& content_size,
418 scoped_refptr<ThumbnailCache::WriteTaskResult> result) {
419 DCHECK(compressed_data);
420 DCHECK(result);
421
422 base::File file(file_path,
423 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
424 DCHECK(file.IsValid());
425
426 compressed_data->lockPixels();
427 bool success = true;
428 int content_width = content_size.width();
429 int content_height = content_size.height();
430 int data_width = compressed_data->info().width();
431 int data_height = compressed_data->info().height();
432
433 if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey),
434 sizeof(int)) < 0 ||
435 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_width),
436 sizeof(int)) < 0 ||
437 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_height),
438 sizeof(int)) < 0 ||
439 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width),
440 sizeof(int)) < 0 ||
441 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height),
442 sizeof(int)) < 0 ||
443 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&scale),
444 sizeof(float)) < 0) {
445 success = false;
446 }
447
448 size_t compressed_bytes = etc1_get_encoded_data_size(data_width, data_height);
449 if (file.WriteAtCurrentPos(reinterpret_cast<char*>(compressed_data->pixels()),
450 compressed_bytes) < 0)
451 success = false;
452
453 compressed_data->unlockPixels();
454
455 if (!success)
456 base::DeleteFile(file_path, false);
457
458 file.Close();
David Trainor- moved to gerrit 2014/07/15 20:08:23 Do you have to close this before you delete the fi
powei 2014/07/18 00:51:49 Done. Moved close to before delete.
459
460 result->SetSuccess(success);
461 }
462
463 void ThumbnailCache::PostWriteTask(
464 TabId tab_id,
465 scoped_refptr<ThumbnailCache::WriteTaskResult> result) {
466 write_tasks_count_--;
467 if (result->success())
468 written_thumbnails_.insert(tab_id);
469 }
470
471 void ThumbnailCache::CompressionTask(
472 SkBitmap raw_data,
473 scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) {
474 if (raw_data.empty())
475 return;
476
477 SkAutoLockPixels raw_data_lock(raw_data);
478 gfx::Size raw_data_size(raw_data.width(), raw_data.height());
479 DCHECK_EQ(raw_data.config(), SkBitmap::kARGB_8888_Config);
480 size_t pixel_size = 4; // Pixel size is 4 bytes for kARGB_8888_Config.
481 size_t stride = pixel_size * raw_data_size.width();
482
483 gfx::Size encoded_size = GetEncodedSize(raw_data_size);
484 size_t encoded_bytes =
485 etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height());
486 SkImageInfo info = {encoded_size.width(),
487 encoded_size.height(),
488 kUnknown_SkColorType,
489 kUnpremul_SkAlphaType};
490 skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef(
491 SkData::NewFromMalloc(new uint8_t[encoded_bytes], encoded_bytes));
492 skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef(
493 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get()));
494
495 etc1_pixel_ref->lockPixels();
496 bool success = etc1_encode_image(
497 reinterpret_cast<unsigned char*>(raw_data.getPixels()),
498 raw_data_size.width(),
499 raw_data_size.height(),
500 pixel_size,
501 stride,
502 reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()),
503 encoded_size.width(),
504 encoded_size.height());
505 etc1_pixel_ref->setImmutable();
506 etc1_pixel_ref->unlockPixels();
507 if (success) {
508 transport->compressed_data = etc1_pixel_ref;
509 transport->content_size = raw_data_size;
510 }
511 }
512
513 void ThumbnailCache::PostCompressionTask(
514 TabId tab_id,
515 float scale,
516 const base::Time& time_stamp,
517 scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) {
518 DCHECK(transport);
519 compression_tasks_count_--;
520
521 if (!transport->compressed_data)
522 return;
David Trainor- moved to gerrit 2014/07/15 20:08:24 Do we need to clean anything up here?
powei 2014/07/18 00:51:49 Done.
523
524 if (cache_.Contains(tab_id)) {
525 Thumbnail& thumbnail = cache_.Get(tab_id);
526 if (thumbnail.time_stamp() != time_stamp)
527 return;
528 thumbnail.SetCompressedBitmap(transport->compressed_data,
529 transport->content_size);
530 thumbnail.CreateUIResource();
531 }
532 WriteThumbnailIfNecessary(
533 tab_id, transport->compressed_data, scale, transport->content_size);
534 }
535
536 void ThumbnailCache::ReadTask(
537 const base::FilePath& file_path,
538 scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) {
539 DCHECK(transport);
540 if (!base::PathExists(file_path))
541 return;
542
543 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
544 DCHECK(file.IsValid());
545
546 int key;
547 bool success = true;
548 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 ||
549 key != kCompressedKey)
550 success = false;
551
552 int width, height;
553 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < 0 ||
554 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < 0)
555 success = false;
556 gfx::Size content_size(width, height);
557
558 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < 0 ||
559 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < 0)
560 success = false;
561 gfx::Size data_size(width, height);
562
563 float scale = 0.f;
564 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale), sizeof(float)) < 0)
565 success = false;
566
567 size_t compressed_bytes =
568 etc1_get_encoded_data_size(data_size.width(), data_size.height());
569
570 SkImageInfo info = {data_size.width(),
571 data_size.height(),
572 kUnknown_SkColorType,
573 kUnpremul_SkAlphaType};
574
575 scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]);
576 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()),
577 compressed_bytes) < 0)
578 success = false;
579
580 skia::RefPtr<SkData> etc1_pixel_data =
581 skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes));
582 skia::RefPtr<SkPixelRef> compressed_data = skia::AdoptRef(
583 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get()));
584
585 if (success) {
586 transport->compressed_data = compressed_data;
587 transport->scale = scale;
588 transport->content_size = content_size;
589 } else {
590 base::DeleteFile(file_path, false);
591 }
592
593 file.Close();
594 }
595
596 void ThumbnailCache::PostReadTask(
597 TabId tab_id,
598 scoped_refptr<ThumbnailCache::CompressedDataTransport> transport) {
599 TabIdList::iterator iter =
600 std::find(read_queue_.begin(), read_queue_.end(), tab_id);
601 if (iter == read_queue_.end())
602 return;
603
604 read_queue_.erase(iter);
605
606 if (!cache_.Contains(tab_id) && transport->compressed_data) {
607 DCHECK(thumbnail_meta_data_.find(tab_id) != thumbnail_meta_data_.end());
608
609 MakeSpaceForNewItemIfNecessary(tab_id);
610 Thumbnail thumbnail(tab_id,
611 thumbnail_meta_data_[tab_id].capture_time(),
612 transport->scale,
613 ui_resource_provider_,
614 this);
615 thumbnail.SetCompressedBitmap(transport->compressed_data,
616 transport->content_size);
617 cache_.Put(tab_id, thumbnail);
David Trainor- moved to gerrit 2014/07/14 20:30:46 thumbnail.CreateUIResource?
powei 2014/07/14 23:56:25 Just to make sure. This means we prefer cpu mem o
powei 2014/07/15 20:06:09 Done.
David Trainor- moved to gerrit 2014/07/15 20:08:23 Yes. At least when it was in Dalvik that was the
powei 2014/07/22 18:16:19 Done. Forgot the flag in the last patch. Now add
618 NotifyObserversOfThumbnailRead(tab_id);
619 }
620
621 ReadNextThumbnail();
622 }
623
624 void ThumbnailCache::NotifyObserversOfThumbnailRead(TabId tab_id) {
625 FOR_EACH_OBSERVER(
626 ThumbnailCacheObserver, observers_, OnFinishedThumbnailRead(tab_id));
627 }
628
629 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData() {
630 }
631
632 ThumbnailCache::ThumbnailMetaData::ThumbnailMetaData(
633 const base::Time& current_time,
634 const GURL& url)
635 : capture_time_(current_time), url_(url) {
636 }
637
638 std::pair<SkBitmap, float> ThumbnailCache::CreateApproximation(
639 const SkBitmap& bitmap,
640 float scale) {
641 DCHECK(!bitmap.empty());
642 DCHECK_GT(scale, 0);
643 SkAutoLockPixels bitmap_lock(bitmap);
644 float new_scale = 1.f / kApproximationScaleFactor;
645
646 gfx::Size dst_size = gfx::ToFlooredSize(
647 gfx::ScaleSize(gfx::Size(bitmap.width(), bitmap.height()), new_scale));
648 SkBitmap dst_bitmap;
649 dst_bitmap.setConfig(bitmap.getConfig(), dst_size.width(), dst_size.height());
650 dst_bitmap.allocPixels();
651 dst_bitmap.eraseColor(0);
652 SkAutoLockPixels dst_bitmap_lock(dst_bitmap);
653
654 SkCanvas canvas(dst_bitmap);
655 canvas.scale(new_scale, new_scale);
656 canvas.drawBitmap(bitmap, 0, 0, NULL);
657 dst_bitmap.setImmutable();
658
659 return std::make_pair(dst_bitmap, new_scale * scale);
660 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698