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

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

Issue 486093002: Fix thumbnail store crashing on loading old files (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Handle endianness of float reads Created 6 years, 4 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 | Annotate | Revision Log
« no previous file with comments | « no previous file | third_party/android_opengl/etc1/etc1.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/android/thumbnail/thumbnail_store.h" 5 #include "chrome/browser/android/thumbnail/thumbnail_store.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 9
10 #include "base/big_endian.h"
10 #include "base/file_util.h" 11 #include "base/file_util.h"
11 #include "base/files/file.h" 12 #include "base/files/file.h"
12 #include "base/files/file_enumerator.h" 13 #include "base/files/file_enumerator.h"
13 #include "base/files/file_path.h" 14 #include "base/files/file_path.h"
14 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
15 #include "base/threading/worker_pool.h" 16 #include "base/threading/worker_pool.h"
16 #include "base/time/time.h" 17 #include "base/time/time.h"
17 #include "content/public/browser/android/ui_resource_provider.h" 18 #include "content/public/browser/android/ui_resource_provider.h"
18 #include "content/public/browser/browser_thread.h" 19 #include "content/public/browser/browser_thread.h"
19 #include "third_party/android_opengl/etc1/etc1.h" 20 #include "third_party/android_opengl/etc1/etc1.h"
20 #include "third_party/skia/include/core/SkBitmap.h" 21 #include "third_party/skia/include/core/SkBitmap.h"
21 #include "third_party/skia/include/core/SkCanvas.h" 22 #include "third_party/skia/include/core/SkCanvas.h"
22 #include "third_party/skia/include/core/SkData.h" 23 #include "third_party/skia/include/core/SkData.h"
23 #include "third_party/skia/include/core/SkMallocPixelRef.h" 24 #include "third_party/skia/include/core/SkMallocPixelRef.h"
24 #include "third_party/skia/include/core/SkPixelRef.h" 25 #include "third_party/skia/include/core/SkPixelRef.h"
26 #include "ui/gfx/android/device_display_info.h"
25 #include "ui/gfx/geometry/size_conversions.h" 27 #include "ui/gfx/geometry/size_conversions.h"
26 28
27 namespace { 29 namespace {
28 30
29 const float kApproximationScaleFactor = 4.f; 31 const float kApproximationScaleFactor = 4.f;
30 const base::TimeDelta kCaptureMinRequestTimeMs( 32 const base::TimeDelta kCaptureMinRequestTimeMs(
31 base::TimeDelta::FromMilliseconds(1000)); 33 base::TimeDelta::FromMilliseconds(1000));
32 34
33 const int kCompressedKey = 0xABABABAB; 35 const int kCompressedKey = 0xABABABAB;
36 const int kCurrentExtraVersion = 1;
34 37
35 // Indicates whether we prefer to have more free CPU memory over GPU memory. 38 // Indicates whether we prefer to have more free CPU memory over GPU memory.
36 const bool kPreferCPUMemory = true; 39 const bool kPreferCPUMemory = true;
37 40
38 // TODO(): ETC1 texture sizes should be multiples of four, but some drivers only 41 // TODO(): ETC1 texture sizes should be multiples of four, but some drivers only
39 // allow power-of-two ETC1 textures. Find better work around. 42 // allow power-of-two ETC1 textures. Find better work around.
40 size_t NextPowerOfTwo(size_t x) { 43 size_t NextPowerOfTwo(size_t x) {
41 --x; 44 --x;
42 x |= x >> 1; 45 x |= x >> 1;
43 x |= x >> 2; 46 x |= x >> 2;
44 x |= x >> 4; 47 x |= x >> 4;
45 x |= x >> 8; 48 x |= x >> 8;
46 x |= x >> 16; 49 x |= x >> 16;
47 return x + 1; 50 return x + 1;
48 } 51 }
49 52
50 gfx::Size GetEncodedSize(const gfx::Size& bitmap_size) { 53 gfx::Size GetEncodedSize(const gfx::Size& bitmap_size) {
51 DCHECK(!bitmap_size.IsEmpty()); 54 DCHECK(!bitmap_size.IsEmpty());
52 return gfx::Size(NextPowerOfTwo(bitmap_size.width()), 55 return gfx::Size(NextPowerOfTwo(bitmap_size.width()),
53 NextPowerOfTwo(bitmap_size.height())); 56 NextPowerOfTwo(bitmap_size.height()));
54 } 57 }
55 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
56 } // anonymous namespace 106 } // anonymous namespace
57 107
58 ThumbnailStore::ThumbnailStore(const std::string& disk_cache_path_str, 108 ThumbnailStore::ThumbnailStore(const std::string& disk_cache_path_str,
59 size_t default_cache_size, 109 size_t default_cache_size,
60 size_t approximation_cache_size, 110 size_t approximation_cache_size,
61 size_t compression_queue_max_size, 111 size_t compression_queue_max_size,
62 size_t write_queue_max_size, 112 size_t write_queue_max_size,
63 bool use_approximation_thumbnail) 113 bool use_approximation_thumbnail)
64 : disk_cache_path_(disk_cache_path_str), 114 : disk_cache_path_(disk_cache_path_str),
65 compression_queue_max_size_(compression_queue_max_size), 115 compression_queue_max_size_(compression_queue_max_size),
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 449
400 cached_thumbnail = approximation_cache_.Get(tab_id); 450 cached_thumbnail = approximation_cache_.Get(tab_id);
401 if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid) 451 if (cached_thumbnail && cached_thumbnail->ui_resource_id() == uid)
402 approximation_cache_.Remove(tab_id); 452 approximation_cache_.Remove(tab_id);
403 } 453 }
404 454
405 base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const { 455 base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const {
406 return disk_cache_path_.Append(base::IntToString(tab_id)); 456 return disk_cache_path_.Append(base::IntToString(tab_id));
407 } 457 }
408 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
Ted C 2014/08/19 21:02:03 Normally, people leave out "anonymous"
511
409 void ThumbnailStore::WriteTask(const base::FilePath& file_path, 512 void ThumbnailStore::WriteTask(const base::FilePath& file_path,
410 skia::RefPtr<SkPixelRef> compressed_data, 513 skia::RefPtr<SkPixelRef> compressed_data,
411 float scale, 514 float scale,
412 const gfx::Size& content_size, 515 const gfx::Size& content_size,
413 const base::Callback<void()>& post_write_task) { 516 const base::Callback<void()>& post_write_task) {
414 DCHECK(compressed_data); 517 DCHECK(compressed_data);
415 518
416 base::File file(file_path, 519 base::File file(file_path,
417 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); 520 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
418 DCHECK(file.IsValid());
419 521
420 compressed_data->lockPixels(); 522 bool success = WriteToFile(file,
421 bool success = true; 523 content_size,
422 int content_width = content_size.width(); 524 scale,
423 int content_height = content_size.height(); 525 compressed_data);
424 int data_width = compressed_data->info().width();
425 int data_height = compressed_data->info().height();
426
427 if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey),
428 sizeof(int)) < 0 ||
429 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_width),
430 sizeof(int)) < 0 ||
431 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_height),
432 sizeof(int)) < 0 ||
433 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width),
434 sizeof(int)) < 0 ||
435 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height),
436 sizeof(int)) < 0 ||
437 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&scale),
438 sizeof(float)) < 0) {
439 success = false;
440 }
441
442 size_t compressed_bytes = etc1_get_encoded_data_size(data_width, data_height);
443 if (file.WriteAtCurrentPos(reinterpret_cast<char*>(compressed_data->pixels()),
444 compressed_bytes) < 0)
445 success = false;
446
447 compressed_data->unlockPixels();
448 526
449 file.Close(); 527 file.Close();
450 528
451 if (!success) 529 if (!success)
452 base::DeleteFile(file_path, false); 530 base::DeleteFile(file_path, false);
453 531
454 content::BrowserThread::PostTask( 532 content::BrowserThread::PostTask(
455 content::BrowserThread::UI, FROM_HERE, post_write_task); 533 content::BrowserThread::UI, FROM_HERE, post_write_task);
456 } 534 }
457 535
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
524 Thumbnail* thumbnail = cache_.Get(tab_id); 602 Thumbnail* thumbnail = cache_.Get(tab_id);
525 if (thumbnail) { 603 if (thumbnail) {
526 if (thumbnail->time_stamp() != time_stamp) 604 if (thumbnail->time_stamp() != time_stamp)
527 return; 605 return;
528 thumbnail->SetCompressedBitmap(compressed_data, content_size); 606 thumbnail->SetCompressedBitmap(compressed_data, content_size);
529 thumbnail->CreateUIResource(); 607 thumbnail->CreateUIResource();
530 } 608 }
531 WriteThumbnailIfNecessary(tab_id, compressed_data, scale, content_size); 609 WriteThumbnailIfNecessary(tab_id, compressed_data, scale, content_size);
532 } 610 }
533 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
534 void ThumbnailStore::ReadTask( 720 void ThumbnailStore::ReadTask(
535 const base::FilePath& file_path, 721 const base::FilePath& file_path,
536 const base::Callback< 722 const base::Callback<
537 void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>& 723 void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>&
538 post_read_task) { 724 post_read_task) {
725 gfx::Size content_size;
726 float scale = 0.f;
539 skia::RefPtr<SkPixelRef> compressed_data; 727 skia::RefPtr<SkPixelRef> compressed_data;
540 float scale = 0.f;
541 gfx::Size content_size;
542 728
543 if (base::PathExists(file_path)) { 729 if (base::PathExists(file_path)) {
544 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); 730 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
545 DCHECK(file.IsValid());
546 731
547 int key; 732 bool valid_contents = ReadFromFile(file,
548 bool success = true; 733 &content_size,
549 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 || 734 &scale,
550 key != kCompressedKey) 735 &compressed_data);
551 success = false;
552
553 int width = 0;
554 int height = 0;
555 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) <
556 0 ||
557 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) <
558 0)
559 success = false;
560
561 content_size = gfx::Size(width, height);
562 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) <
563 0 ||
564 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) <
565 0)
566 success = false;
567
568 gfx::Size data_size(width, height);
569 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale), sizeof(float)) <
570 0)
571 success = false;
572
573 size_t compressed_bytes =
574 etc1_get_encoded_data_size(data_size.width(), data_size.height());
575 SkImageInfo info = {data_size.width(),
576 data_size.height(),
577 kUnknown_SkColorType,
578 kUnpremul_SkAlphaType};
579
580 scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]);
581 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()),
582 compressed_bytes) < 0)
583 success = false;
584
585 file.Close(); 736 file.Close();
586 737
587 skia::RefPtr<SkData> etc1_pixel_data = 738 if (!valid_contents) {
588 skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); 739 content_size.SetSize(0, 0);
589 compressed_data = skia::AdoptRef( 740 scale = 0.f;
590 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); 741 compressed_data.clear();
591 742 base::DeleteFile(file_path, false);
592 if (!success) {
593 compressed_data.clear();
594 content_size = gfx::Size();
595 scale = 0.f;
596 base::DeleteFile(file_path, false);
597 } 743 }
598 } 744 }
599 745
600 content::BrowserThread::PostTask( 746 content::BrowserThread::PostTask(
601 content::BrowserThread::UI, 747 content::BrowserThread::UI,
602 FROM_HERE, 748 FROM_HERE,
603 base::Bind(post_read_task, compressed_data, scale, content_size)); 749 base::Bind(post_read_task, compressed_data, scale, content_size));
604 } 750 }
605 751
606 void ThumbnailStore::PostReadTask(TabId tab_id, 752 void ThumbnailStore::PostReadTask(TabId tab_id,
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
684 dst_bitmap.eraseColor(0); 830 dst_bitmap.eraseColor(0);
685 SkAutoLockPixels dst_bitmap_lock(dst_bitmap); 831 SkAutoLockPixels dst_bitmap_lock(dst_bitmap);
686 832
687 SkCanvas canvas(dst_bitmap); 833 SkCanvas canvas(dst_bitmap);
688 canvas.scale(new_scale, new_scale); 834 canvas.scale(new_scale, new_scale);
689 canvas.drawBitmap(bitmap, 0, 0, NULL); 835 canvas.drawBitmap(bitmap, 0, 0, NULL);
690 dst_bitmap.setImmutable(); 836 dst_bitmap.setImmutable();
691 837
692 return std::make_pair(dst_bitmap, new_scale * scale); 838 return std::make_pair(dst_bitmap, new_scale * scale);
693 } 839 }
OLDNEW
« no previous file with comments | « no previous file | third_party/android_opengl/etc1/etc1.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698