Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/android/thumbnail/thumbnail_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 } | |
| OLD | NEW |