summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordtrainor@chromium.org <dtrainor@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-20 20:03:30 +0000
committerdtrainor@chromium.org <dtrainor@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-20 20:03:30 +0000
commit2a55c34837603280ef26e88c6e1d324f410c75cd (patch)
tree9a1d9ae744c2d15f7a393a947d04145548f4a303
parent453ae2bb8f647d3d6b4a55c9ef0fb416b9e62c9b (diff)
downloadchromium_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.cc306
-rw-r--r--third_party/android_opengl/etc1/etc1.cpp60
-rw-r--r--third_party/android_opengl/etc1/etc1.h20
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_