Chromium Code Reviews| Index: chrome/browser/android/thumbnail/thumbnail_impl.cc |
| diff --git a/chrome/browser/android/thumbnail/thumbnail_impl.cc b/chrome/browser/android/thumbnail/thumbnail_impl.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e1cdb377964411e5b174c4d3d9a0151369786f12 |
| --- /dev/null |
| +++ b/chrome/browser/android/thumbnail/thumbnail_impl.cc |
| @@ -0,0 +1,280 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/android/thumbnail/thumbnail_impl.h" |
| + |
| +#include "base/files/file.h" |
| +#include "base/files/file_path.h" |
| +#include "cc/resources/ui_resource_bitmap.h" |
| +#include "chrome/browser/android/thumbnail/thumbnail_cache.h" |
| +#include "third_party/android_opengl/etc1/etc1.h" |
| +#include "third_party/skia/include/core/SkBitmap.h" |
| +#include "third_party/skia/include/core/SkCanvas.h" |
| +#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/geometry/size_conversions.h" |
| + |
| +namespace { |
| + |
| +const int kCompressedKey = 0xABABABAB; |
| +const int kDecompressedKey = 0xCDCDCDCD; |
| + |
| +// ETC1 texture sizes are multiples of four. |
| +size_t NextETC1Size(size_t s) { |
| + return (s / 4 + (s % 4 ? 1 : 0)) * 4; |
| +} |
| + |
| +gfx::Size GetEncodedSize(gfx::Size bitmap_size) { |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
const gfx::Size&
powei
2014/06/19 23:05:59
Done.
|
| + return gfx::Size(NextETC1Size(bitmap_size.width()), |
| + NextETC1Size(bitmap_size.height())); |
| +} |
| + |
| +} // namespace |
| + |
| +ThumbnailImpl::ThumbnailImpl(TabId tab_id, |
| + const SkBitmap& bitmap, |
| + float scale, |
| + ThumbnailImplManager* thumbnail_impl_manager, |
| + content::UIResourceProvider* ui_resource_provider) |
| + : tab_id_(tab_id), |
| + ui_resource_id_(0), |
| + thumbnail_impl_manager_(thumbnail_impl_manager), |
| + ui_resource_provider_(ui_resource_provider) { |
| + DCHECK(ui_resource_provider); |
| + SetRawData(bitmap, scale); |
| +} |
| + |
| +ThumbnailImpl::~ThumbnailImpl() { |
| + CleanupUIResource(); |
| +} |
| + |
| +void ThumbnailImpl::CleanupUIResource() { |
| + DCHECK(ui_resource_provider_); |
| + if (ui_resource_id_) |
| + ui_resource_provider_->DeleteUIResource(ui_resource_id_); |
| + ui_resource_id_ = 0; |
| +} |
| + |
| +void ThumbnailImpl::BuildUIResource() { |
| + CleanupUIResource(); |
| + if (raw_data_.empty() && !compressed_data_) |
| + return; |
| + ui_resource_id_ = ui_resource_provider_->CreateUIResource(this); |
| +} |
| + |
| +bool ThumbnailImpl::IsValid() const { |
| + return ui_resource_provider_ && scale_ > 0 && !data_size_.IsEmpty() && |
| + !content_size_.IsEmpty(); |
| +} |
| + |
| +gfx::Size ThumbnailImpl::GetScaledContentSize() const { |
| + return gfx::ToRoundedSize(gfx::ScaleSize(content_size_, 1.f / scale_)); |
| +} |
| + |
| +gfx::SizeF ThumbnailImpl::GetScaledDataSize() const { |
| + return gfx::ScaleSize(data_size_, 1.f / scale_); |
| +} |
| + |
| +void ThumbnailImpl::Compress(scoped_refptr<ThumbnailImpl> in_thumbnail, |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
I reviewed this before the actual cache, but why h
powei
2014/06/19 23:05:59
This is just to make this method be more functiona
David Trainor- moved to gerrit
2014/06/25 07:40:16
Could it still act on the same class that's passed
|
| + scoped_refptr<ThumbnailImpl> out_thumbnail) { |
| + SkBitmap raw_data = in_thumbnail->raw_data_; |
| + if (raw_data.empty()) |
| + return; |
| + |
| + SkAutoLockPixels raw_data_lock(raw_data); |
| + gfx::Size raw_data_size(raw_data.width(), raw_data.height()); |
| + DCHECK_EQ(raw_data.config(), SkBitmap::kARGB_8888_Config); |
| + size_t pixel_size = 4; // Pixel size is 4 bytes for kARGB_8888_Config. |
| + size_t stride = pixel_size * raw_data_size.width(); |
| + |
| + gfx::Size encoded_size = GetEncodedSize(raw_data_size); |
| + size_t encoded_bytes = |
| + etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height()); |
| + SkImageInfo info = {encoded_size.width(), |
| + encoded_size.height(), |
| + kUnknown_SkColorType, |
| + kUnpremul_SkAlphaType}; |
| + skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef( |
| + SkData::NewFromMalloc(new uint8_t[encoded_bytes], encoded_bytes)); |
| + skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef( |
| + SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
| + |
| + etc1_pixel_ref->lockPixels(); |
| + bool success = etc1_encode_image( |
| + reinterpret_cast<unsigned char*>(raw_data.getPixels()), |
| + raw_data_size.width(), |
| + raw_data_size.height(), |
| + pixel_size, |
| + stride, |
| + reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()), |
| + encoded_size.width(), |
| + encoded_size.height()); |
| + etc1_pixel_ref->setImmutable(); |
| + etc1_pixel_ref->unlockPixels(); |
| + if (success) { |
| + out_thumbnail->SetCompressedData( |
| + etc1_pixel_ref, in_thumbnail->scale_, raw_data_size); |
| + } |
| +} |
| + |
| +bool ThumbnailImpl::WriteToFile(scoped_refptr<ThumbnailImpl> thumbnail, |
| + base::File& file) { |
| + // We assume caller has already initialized the file and will also close it. |
| + DCHECK(file.IsValid()); |
| + DCHECK(thumbnail); |
| + DCHECK(thumbnail->compressed_data_); |
| + |
| + thumbnail->compressed_data_->lockPixels(); |
| + bool success = true; |
| + int content_width = thumbnail->content_size_.width(); |
| + int content_height = thumbnail->content_size_.height(); |
| + int data_width = thumbnail->data_size_.width(); |
| + int data_height = thumbnail->data_size_.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*>(&thumbnail->scale_), |
| + sizeof(float)) < 0) { |
| + success = false; |
| + } |
| + |
| + size_t compressed_bytes = etc1_get_encoded_data_size( |
| + thumbnail->data_size_.width(), thumbnail->data_size_.height()); |
| + |
| + if (file.WriteAtCurrentPos( |
| + reinterpret_cast<char*>(thumbnail->compressed_data_->pixels()), |
| + compressed_bytes) < 0) |
| + success = false; |
| + |
| + thumbnail->compressed_data_->unlockPixels(); |
| + return success; |
| +} |
| + |
| +bool ThumbnailImpl::ReadFromFile(base::File& file, |
| + scoped_refptr<ThumbnailImpl> thumbnail) { |
| + // We assume caller has already initialized the file and will also close it. |
| + DCHECK(file.IsValid()); |
| + |
| + int key; |
| + bool success = true; |
| + if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 || |
| + key != kCompressedKey) |
| + success = false; |
| + |
| + int 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 content_size = gfx::Size(width, height); |
|
David Trainor- moved to gerrit
2014/06/17 08:12:11
gfx::Size content_size(width, height);
Same for b
powei
2014/06/19 23:05:59
Done.
|
| + |
| + 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 = gfx::Size(width, height); |
| + |
| + float scale = 0.f; |
| + 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; |
| + |
| + skia::RefPtr<SkData> etc1_pixel_data = |
| + skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); |
| + skia::RefPtr<SkPixelRef> compressed_data = skia::AdoptRef( |
| + SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); |
| + |
| + if (success) |
| + thumbnail->SetCompressedData(compressed_data, scale, content_size); |
| + return success; |
| +} |
| + |
| +scoped_refptr<ThumbnailImpl> ThumbnailImpl::PassDataToNewThumbnail() { |
| + scoped_refptr<ThumbnailImpl> new_thumbnail = |
| + make_scoped_refptr(new ThumbnailImpl(tab_id_, |
| + SkBitmap(), |
| + 0.f, |
| + thumbnail_impl_manager_, |
| + ui_resource_provider_)); |
| + new_thumbnail->raw_data_ = raw_data_; |
| + new_thumbnail->compressed_data_ = compressed_data_; |
| + new_thumbnail->scale_ = scale_; |
| + new_thumbnail->data_size_ = data_size_; |
| + new_thumbnail->content_size_ = content_size_; |
| + |
| + // Release this Thumbnail's reference to the data. |
| + raw_data_ = SkBitmap(); |
| + compressed_data_.clear(); |
| + return new_thumbnail; |
| +} |
| + |
| +cc::UIResourceBitmap ThumbnailImpl::GetBitmap(cc::UIResourceId uid, |
| + bool resource_lost) { |
| + if (!raw_data_.empty()) { |
| + DCHECK(!compressed_data_); |
| + return cc::UIResourceBitmap(raw_data_); |
| + } else if (compressed_data_) { |
| + DCHECK(raw_data_.empty()); |
| + return cc::UIResourceBitmap(compressed_data_, data_size_); |
| + } |
| + |
| + // Return a place holder for all other calls to GetBitmap. |
| + SkBitmap tiny_bitmap; |
| + SkCanvas canvas(tiny_bitmap); |
| + tiny_bitmap.setConfig( |
| + SkBitmap::kARGB_8888_Config, 1, 1, 0, kOpaque_SkAlphaType); |
| + tiny_bitmap.allocPixels(); |
| + canvas.drawColor(SK_ColorWHITE); |
| + tiny_bitmap.setImmutable(); |
| + return cc::UIResourceBitmap(tiny_bitmap); |
| +} |
| + |
| +void ThumbnailImpl::UIResourceIsInvalid() { |
| + ui_resource_id_ = 0; |
| + if (thumbnail_impl_manager_) { |
| + scoped_refptr<ThumbnailImpl> this_thumbnail(this); |
| + thumbnail_impl_manager_->InvalidateCachedThumbnail(this_thumbnail); |
| + } |
| +} |
| + |
| +void ThumbnailImpl::SetCompressedData(skia::RefPtr<SkPixelRef> compressed_data, |
| + float scale, |
| + const gfx::Size& content_size) { |
| + DCHECK(compressed_data); |
| + compressed_data_ = compressed_data; |
| + raw_data_ = SkBitmap(); |
| + scale_ = scale; |
| + content_size_ = content_size; |
| + data_size_ = gfx::Size(compressed_data_->info().width(), |
| + compressed_data_->info().height()); |
| + BuildUIResource(); |
|
powei
2014/06/19 23:05:59
Just realized this can be called on the wrong thre
David Trainor- moved to gerrit
2014/06/25 07:40:16
Won't this defeat the purpose of using the UI reso
|
| +} |
| + |
| +void ThumbnailImpl::SetRawData(SkBitmap bitmap, float scale) { |
| + raw_data_ = bitmap; |
| + compressed_data_.clear(); |
| + data_size_ = gfx::Size(bitmap.width(), bitmap.height()); |
| + content_size_ = data_size_; |
| + scale_ = scale; |
| + BuildUIResource(); |
| +} |