diff options
author | dtrainor@chromium.org <dtrainor@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-20 20:03:30 +0000 |
---|---|---|
committer | dtrainor@chromium.org <dtrainor@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-20 20:03:30 +0000 |
commit | 2a55c34837603280ef26e88c6e1d324f410c75cd (patch) | |
tree | 9a1d9ae744c2d15f7a393a947d04145548f4a303 | |
parent | 453ae2bb8f647d3d6b4a55c9ef0fb416b9e62c9b (diff) | |
download | chromium_src-2a55c34837603280ef26e88c6e1d324f410c75cd.zip chromium_src-2a55c34837603280ef26e88c6e1d324f410c75cd.tar.gz chromium_src-2a55c34837603280ef26e88c6e1d324f410c75cd.tar.bz2 |
Merge 290708 "Fix thumbnail store crashing on loading old files"
> Fix thumbnail store crashing on loading old files
>
> - Was loading thumbnails in little endian, but since the files were from java's
> output stream they were in big endian. This was causing all sorts of issues
> with old thumbnails.
> - Added back ETC1 header and fixed the file format to match the old version.
>
> BUG=398319
>
> Review URL: https://codereview.chromium.org/486093002
TBR=dtrainor@chromium.org
Review URL: https://codereview.chromium.org/483753005
git-svn-id: svn://svn.chromium.org/chrome/branches/2125/src@290902 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/android/thumbnail/thumbnail_store.cc | 306 | ||||
-rw-r--r-- | third_party/android_opengl/etc1/etc1.cpp | 60 | ||||
-rw-r--r-- | third_party/android_opengl/etc1/etc1.h | 20 |
3 files changed, 306 insertions, 80 deletions
diff --git a/chrome/browser/android/thumbnail/thumbnail_store.cc b/chrome/browser/android/thumbnail/thumbnail_store.cc index 1df7faf..52d9205 100644 --- a/chrome/browser/android/thumbnail/thumbnail_store.cc +++ b/chrome/browser/android/thumbnail/thumbnail_store.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <cmath> +#include "base/big_endian.h" #include "base/file_util.h" #include "base/files/file.h" #include "base/files/file_enumerator.h" @@ -22,6 +23,7 @@ #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkMallocPixelRef.h" #include "third_party/skia/include/core/SkPixelRef.h" +#include "ui/gfx/android/device_display_info.h" #include "ui/gfx/geometry/size_conversions.h" namespace { @@ -31,6 +33,7 @@ const base::TimeDelta kCaptureMinRequestTimeMs( base::TimeDelta::FromMilliseconds(1000)); const int kCompressedKey = 0xABABABAB; +const int kCurrentExtraVersion = 1; // Indicates whether we prefer to have more free CPU memory over GPU memory. const bool kPreferCPUMemory = true; @@ -53,6 +56,53 @@ gfx::Size GetEncodedSize(const gfx::Size& bitmap_size) { NextPowerOfTwo(bitmap_size.height())); } +template<typename T> +bool ReadBigEndianFromFile(base::File& file, T* out) { + char buffer[sizeof(T)]; + if (file.ReadAtCurrentPos(buffer, sizeof(T)) != sizeof(T)) + return false; + base::ReadBigEndian(buffer, out); + return true; +} + +template<typename T> +bool WriteBigEndianToFile(base::File& file, T val) { + char buffer[sizeof(T)]; + base::WriteBigEndian(buffer, val); + return file.WriteAtCurrentPos(buffer, sizeof(T)) == sizeof(T); +} + +bool ReadBigEndianFloatFromFile(base::File& file, float* out) { + char buffer[sizeof(float)]; + if (file.ReadAtCurrentPos(buffer, sizeof(buffer)) != sizeof(buffer)) + return false; + +#if defined(ARCH_CPU_LITTLE_ENDIAN) + for (size_t i = 0; i < sizeof(float) / 2; i++) { + char tmp = buffer[i]; + buffer[i] = buffer[sizeof(float) - 1 - i]; + buffer[sizeof(float) - 1 - i] = tmp; + } +#endif + memcpy(out, buffer, sizeof(buffer)); + + return true; +} + +bool WriteBigEndianFloatToFile(base::File& file, float val) { + char buffer[sizeof(float)]; + memcpy(buffer, &val, sizeof(buffer)); + +#if defined(ARCH_CPU_LITTLE_ENDIAN) + for (size_t i = 0; i < sizeof(float) / 2; i++) { + char tmp = buffer[i]; + buffer[i] = buffer[sizeof(float) - 1 - i]; + buffer[sizeof(float) - 1 - i] = tmp; + } +#endif + return file.WriteAtCurrentPos(buffer, sizeof(buffer)) == sizeof(buffer); +} + } // anonymous namespace ThumbnailStore::ThumbnailStore(const std::string& disk_cache_path_str, @@ -406,6 +456,59 @@ base::FilePath ThumbnailStore::GetFilePath(TabId tab_id) const { return disk_cache_path_.Append(base::IntToString(tab_id)); } +namespace { + +bool WriteToFile(base::File& file, + const gfx::Size& content_size, + const float scale, + skia::RefPtr<SkPixelRef> compressed_data) { + if (!file.IsValid()) + return false; + + if (!WriteBigEndianToFile(file, kCompressedKey)) + return false; + + if (!WriteBigEndianToFile(file, content_size.width())) + return false; + + if (!WriteBigEndianToFile(file, content_size.height())) + return false; + + // Write ETC1 header. + compressed_data->lockPixels(); + + unsigned char etc1_buffer[ETC_PKM_HEADER_SIZE]; + etc1_pkm_format_header(etc1_buffer, + compressed_data->info().width(), + compressed_data->info().height()); + + int header_bytes_written = file.WriteAtCurrentPos( + reinterpret_cast<char*>(etc1_buffer), ETC_PKM_HEADER_SIZE); + if (header_bytes_written != ETC_PKM_HEADER_SIZE) + return false; + + int data_size = etc1_get_encoded_data_size( + compressed_data->info().width(), + compressed_data->info().height()); + int pixel_bytes_written = file.WriteAtCurrentPos( + reinterpret_cast<char*>(compressed_data->pixels()), + data_size); + if (pixel_bytes_written != data_size) + return false; + + compressed_data->unlockPixels(); + + if (!WriteBigEndianToFile(file, kCurrentExtraVersion)) + return false; + + if (!WriteBigEndianFloatToFile(file, 1.f / scale)) + return false; + + return true; +} + +} // anonymous namespace + void ThumbnailStore::WriteTask(const base::FilePath& file_path, skia::RefPtr<SkPixelRef> compressed_data, float scale, @@ -415,36 +518,11 @@ void ThumbnailStore::WriteTask(const base::FilePath& file_path, base::File file(file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); - DCHECK(file.IsValid()); - - compressed_data->lockPixels(); - bool success = true; - int content_width = content_size.width(); - int content_height = content_size.height(); - int data_width = compressed_data->info().width(); - int data_height = compressed_data->info().height(); - - if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey), - sizeof(int)) < 0 || - file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_width), - sizeof(int)) < 0 || - file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_height), - sizeof(int)) < 0 || - file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width), - sizeof(int)) < 0 || - file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height), - sizeof(int)) < 0 || - file.WriteAtCurrentPos(reinterpret_cast<const char*>(&scale), - sizeof(float)) < 0) { - success = false; - } - - size_t compressed_bytes = etc1_get_encoded_data_size(data_width, data_height); - if (file.WriteAtCurrentPos(reinterpret_cast<char*>(compressed_data->pixels()), - compressed_bytes) < 0) - success = false; - compressed_data->unlockPixels(); + bool success = WriteToFile(file, + content_size, + scale, + compressed_data); file.Close(); @@ -531,69 +609,137 @@ void ThumbnailStore::PostCompressionTask( WriteThumbnailIfNecessary(tab_id, compressed_data, scale, content_size); } +namespace { + +bool ReadFromFile(base::File& file, + gfx::Size* out_content_size, + float* out_scale, + skia::RefPtr<SkPixelRef>* out_pixels) { + if (!file.IsValid()) + return false; + + int key = 0; + if (!ReadBigEndianFromFile(file, &key)) + return false; + + if (key != kCompressedKey) + return false; + + int content_width = 0; + if (!ReadBigEndianFromFile(file, &content_width) || content_width <= 0) + return false; + + int content_height = 0; + if (!ReadBigEndianFromFile(file, &content_height) || content_height <= 0) + return false; + + out_content_size->SetSize(content_width, content_height); + + // Read ETC1 header. + int header_bytes_read = 0; + unsigned char etc1_buffer[ETC_PKM_HEADER_SIZE]; + header_bytes_read = file.ReadAtCurrentPos( + reinterpret_cast<char*>(etc1_buffer), + ETC_PKM_HEADER_SIZE); + if (header_bytes_read != ETC_PKM_HEADER_SIZE) + return false; + + if (!etc1_pkm_is_valid(etc1_buffer)) + return false; + + int raw_width = 0; + raw_width = etc1_pkm_get_width(etc1_buffer); + if (raw_width <= 0) + return false; + + int raw_height = 0; + raw_height = etc1_pkm_get_height(etc1_buffer); + if (raw_height <= 0) + return false; + + // Do some simple sanity check validation. We can't have thumbnails larger + // than the max display size of the screen. We also can't have etc1 texture + // data larger than the next power of 2 up from that. + gfx::DeviceDisplayInfo display_info; + int max_dimension = std::max(display_info.GetDisplayWidth(), + display_info.GetDisplayHeight()); + + if (content_width > max_dimension + || content_height > max_dimension + || static_cast<size_t>(raw_width) > NextPowerOfTwo(max_dimension) + || static_cast<size_t>(raw_height) > NextPowerOfTwo(max_dimension)) { + return false; + } + + skia::RefPtr<SkData> etc1_pixel_data; + int data_size = etc1_get_encoded_data_size(raw_width, raw_height); + scoped_ptr<uint8_t[]> raw_data = + scoped_ptr<uint8_t[]>(new uint8_t[data_size]); + + int pixel_bytes_read = file.ReadAtCurrentPos( + reinterpret_cast<char*>(raw_data.get()), + data_size); + + if (pixel_bytes_read != data_size) + return false; + + SkImageInfo info = {raw_width, + raw_height, + kUnknown_SkColorType, + kUnpremul_SkAlphaType}; + + etc1_pixel_data = skia::AdoptRef( + SkData::NewFromMalloc(raw_data.release(), data_size)); + + *out_pixels = skia::AdoptRef( + SkMallocPixelRef::NewWithData(info, + 0, + NULL, + etc1_pixel_data.get())); + + int extra_data_version = 0; + if (!ReadBigEndianFromFile(file, &extra_data_version)) + return false; + + *out_scale = 1.f; + if (extra_data_version == 1) { + if (!ReadBigEndianFloatFromFile(file, out_scale)) + return false; + + if (*out_scale == 0.f) + return false; + + *out_scale = 1.f / *out_scale; + } + + return true; +} + +}// anonymous namespace + void ThumbnailStore::ReadTask( const base::FilePath& file_path, const base::Callback< void(skia::RefPtr<SkPixelRef>, float, const gfx::Size&)>& post_read_task) { - skia::RefPtr<SkPixelRef> compressed_data; - float scale = 0.f; gfx::Size content_size; + float scale = 0.f; + skia::RefPtr<SkPixelRef> compressed_data; if (base::PathExists(file_path)) { base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); - DCHECK(file.IsValid()); - - int key; - bool success = true; - if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 || - key != kCompressedKey) - success = false; - - int width = 0; - int height = 0; - if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < - 0 || - file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < - 0) - success = false; - - content_size = gfx::Size(width, height); - if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < - 0 || - file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < - 0) - success = false; - - gfx::Size data_size(width, height); - if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale), sizeof(float)) < - 0) - success = false; - - size_t compressed_bytes = - etc1_get_encoded_data_size(data_size.width(), data_size.height()); - SkImageInfo info = {data_size.width(), - data_size.height(), - kUnknown_SkColorType, - kUnpremul_SkAlphaType}; - - scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]); - if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()), - compressed_bytes) < 0) - success = false; + bool valid_contents = ReadFromFile(file, + &content_size, + &scale, + &compressed_data); file.Close(); - skia::RefPtr<SkData> etc1_pixel_data = - skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); - compressed_data = skia::AdoptRef( - SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); - - if (!success) { - compressed_data.clear(); - content_size = gfx::Size(); - scale = 0.f; - base::DeleteFile(file_path, false); + if (!valid_contents) { + content_size.SetSize(0, 0); + scale = 0.f; + compressed_data.clear(); + base::DeleteFile(file_path, false); } } diff --git a/third_party/android_opengl/etc1/etc1.cpp b/third_party/android_opengl/etc1/etc1.cpp index 7dd08d9..99b5f15 100644 --- a/third_party/android_opengl/etc1/etc1.cpp +++ b/third_party/android_opengl/etc1/etc1.cpp @@ -587,3 +587,63 @@ bool etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 heig } return true; } + +static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' }; + +static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6; +static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8; +static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10; +static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12; +static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14; + +static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0; + +static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) { + pOut[0] = (etc1_byte) (data >> 8); + pOut[1] = (etc1_byte) data; +} + +static etc1_uint32 readBEUint16(const etc1_byte* pIn) { + return (pIn[0] << 8) | pIn[1]; +} + +// Format a PKM header + +void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) { + memcpy(pHeader, kMagic, sizeof(kMagic)); + etc1_uint32 encodedWidth = (width + 3) & ~3; + etc1_uint32 encodedHeight = (height + 3) & ~3; + writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS); + writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth); + writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight); + writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width); + writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height); +} + +// Check if a PKM header is correctly formatted. + +bool etc1_pkm_is_valid(const etc1_byte* pHeader) { + if (memcmp(pHeader, kMagic, sizeof(kMagic))) { + return false; + } + etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET); + etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET); + etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET); + etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET); + etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET); + return format == ETC1_RGB_NO_MIPMAPS && + encodedWidth >= width && encodedWidth - width < 4 && + encodedHeight >= height && encodedHeight - height < 4; +} + +// Read the image width from a PKM header + +etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) { + return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET); +} + +// Read the image height from a PKM header + +etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){ + return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET); +} diff --git a/third_party/android_opengl/etc1/etc1.h b/third_party/android_opengl/etc1/etc1.h index 0106967..1c4d193 100644 --- a/third_party/android_opengl/etc1/etc1.h +++ b/third_party/android_opengl/etc1/etc1.h @@ -35,4 +35,24 @@ bool etc1_encode_image(const unsigned char* pIn, unsigned int width, unsigned in unsigned int pixelSize, unsigned int stride, unsigned char* pOut, unsigned int outWidth, unsigned int outHeight); +// Size of a PKM header, in bytes. + +#define ETC_PKM_HEADER_SIZE 16 + +// Format a PKM header + +void etc1_pkm_format_header(unsigned char* pHeader, unsigned int width, unsigned int height); + +// Check if a PKM header is correctly formatted. + +bool etc1_pkm_is_valid(const unsigned char* pHeader); + +// Read the image width from a PKM header + +unsigned int etc1_pkm_get_width(const unsigned char* pHeader); + +// Read the image height from a PKM header + +unsigned int etc1_pkm_get_height(const unsigned char* pHeader); + #endif // THIRD_PARTY_ANDROID_OPENGL_ETC1_ETC1_H_ |