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_impl.h" | |
| 6 | |
| 7 #include "base/files/file.h" | |
| 8 #include "base/files/file_path.h" | |
| 9 #include "cc/resources/ui_resource_bitmap.h" | |
| 10 #include "chrome/browser/android/thumbnail/thumbnail_cache.h" | |
| 11 #include "third_party/android_opengl/etc1/etc1.h" | |
| 12 #include "third_party/skia/include/core/SkBitmap.h" | |
| 13 #include "third_party/skia/include/core/SkCanvas.h" | |
| 14 #include "third_party/skia/include/core/SkData.h" | |
| 15 #include "third_party/skia/include/core/SkMallocPixelRef.h" | |
| 16 #include "third_party/skia/include/core/SkPixelRef.h" | |
| 17 #include "ui/gfx/geometry/size_conversions.h" | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 const int kCompressedKey = 0xABABABAB; | |
| 22 const int kDecompressedKey = 0xCDCDCDCD; | |
| 23 | |
| 24 // ETC1 texture sizes are multiples of four. | |
| 25 size_t NextETC1Size(size_t s) { | |
| 26 return (s / 4 + (s % 4 ? 1 : 0)) * 4; | |
| 27 } | |
| 28 | |
| 29 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.
| |
| 30 return gfx::Size(NextETC1Size(bitmap_size.width()), | |
| 31 NextETC1Size(bitmap_size.height())); | |
| 32 } | |
| 33 | |
| 34 } // namespace | |
| 35 | |
| 36 ThumbnailImpl::ThumbnailImpl(TabId tab_id, | |
| 37 const SkBitmap& bitmap, | |
| 38 float scale, | |
| 39 ThumbnailImplManager* thumbnail_impl_manager, | |
| 40 content::UIResourceProvider* ui_resource_provider) | |
| 41 : tab_id_(tab_id), | |
| 42 ui_resource_id_(0), | |
| 43 thumbnail_impl_manager_(thumbnail_impl_manager), | |
| 44 ui_resource_provider_(ui_resource_provider) { | |
| 45 DCHECK(ui_resource_provider); | |
| 46 SetRawData(bitmap, scale); | |
| 47 } | |
| 48 | |
| 49 ThumbnailImpl::~ThumbnailImpl() { | |
| 50 CleanupUIResource(); | |
| 51 } | |
| 52 | |
| 53 void ThumbnailImpl::CleanupUIResource() { | |
| 54 DCHECK(ui_resource_provider_); | |
| 55 if (ui_resource_id_) | |
| 56 ui_resource_provider_->DeleteUIResource(ui_resource_id_); | |
| 57 ui_resource_id_ = 0; | |
| 58 } | |
| 59 | |
| 60 void ThumbnailImpl::BuildUIResource() { | |
| 61 CleanupUIResource(); | |
| 62 if (raw_data_.empty() && !compressed_data_) | |
| 63 return; | |
| 64 ui_resource_id_ = ui_resource_provider_->CreateUIResource(this); | |
| 65 } | |
| 66 | |
| 67 bool ThumbnailImpl::IsValid() const { | |
| 68 return ui_resource_provider_ && scale_ > 0 && !data_size_.IsEmpty() && | |
| 69 !content_size_.IsEmpty(); | |
| 70 } | |
| 71 | |
| 72 gfx::Size ThumbnailImpl::GetScaledContentSize() const { | |
| 73 return gfx::ToRoundedSize(gfx::ScaleSize(content_size_, 1.f / scale_)); | |
| 74 } | |
| 75 | |
| 76 gfx::SizeF ThumbnailImpl::GetScaledDataSize() const { | |
| 77 return gfx::ScaleSize(data_size_, 1.f / scale_); | |
| 78 } | |
| 79 | |
| 80 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
| |
| 81 scoped_refptr<ThumbnailImpl> out_thumbnail) { | |
| 82 SkBitmap raw_data = in_thumbnail->raw_data_; | |
| 83 if (raw_data.empty()) | |
| 84 return; | |
| 85 | |
| 86 SkAutoLockPixels raw_data_lock(raw_data); | |
| 87 gfx::Size raw_data_size(raw_data.width(), raw_data.height()); | |
| 88 DCHECK_EQ(raw_data.config(), SkBitmap::kARGB_8888_Config); | |
| 89 size_t pixel_size = 4; // Pixel size is 4 bytes for kARGB_8888_Config. | |
| 90 size_t stride = pixel_size * raw_data_size.width(); | |
| 91 | |
| 92 gfx::Size encoded_size = GetEncodedSize(raw_data_size); | |
| 93 size_t encoded_bytes = | |
| 94 etc1_get_encoded_data_size(encoded_size.width(), encoded_size.height()); | |
| 95 SkImageInfo info = {encoded_size.width(), | |
| 96 encoded_size.height(), | |
| 97 kUnknown_SkColorType, | |
| 98 kUnpremul_SkAlphaType}; | |
| 99 skia::RefPtr<SkData> etc1_pixel_data = skia::AdoptRef( | |
| 100 SkData::NewFromMalloc(new uint8_t[encoded_bytes], encoded_bytes)); | |
| 101 skia::RefPtr<SkMallocPixelRef> etc1_pixel_ref = skia::AdoptRef( | |
| 102 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); | |
| 103 | |
| 104 etc1_pixel_ref->lockPixels(); | |
| 105 bool success = etc1_encode_image( | |
| 106 reinterpret_cast<unsigned char*>(raw_data.getPixels()), | |
| 107 raw_data_size.width(), | |
| 108 raw_data_size.height(), | |
| 109 pixel_size, | |
| 110 stride, | |
| 111 reinterpret_cast<unsigned char*>(etc1_pixel_ref->pixels()), | |
| 112 encoded_size.width(), | |
| 113 encoded_size.height()); | |
| 114 etc1_pixel_ref->setImmutable(); | |
| 115 etc1_pixel_ref->unlockPixels(); | |
| 116 if (success) { | |
| 117 out_thumbnail->SetCompressedData( | |
| 118 etc1_pixel_ref, in_thumbnail->scale_, raw_data_size); | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 bool ThumbnailImpl::WriteToFile(scoped_refptr<ThumbnailImpl> thumbnail, | |
| 123 base::File& file) { | |
| 124 // We assume caller has already initialized the file and will also close it. | |
| 125 DCHECK(file.IsValid()); | |
| 126 DCHECK(thumbnail); | |
| 127 DCHECK(thumbnail->compressed_data_); | |
| 128 | |
| 129 thumbnail->compressed_data_->lockPixels(); | |
| 130 bool success = true; | |
| 131 int content_width = thumbnail->content_size_.width(); | |
| 132 int content_height = thumbnail->content_size_.height(); | |
| 133 int data_width = thumbnail->data_size_.width(); | |
| 134 int data_height = thumbnail->data_size_.height(); | |
| 135 | |
| 136 if (file.WriteAtCurrentPos(reinterpret_cast<const char*>(&kCompressedKey), | |
| 137 sizeof(int)) < 0 || | |
| 138 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_width), | |
| 139 sizeof(int)) < 0 || | |
| 140 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&content_height), | |
| 141 sizeof(int)) < 0 || | |
| 142 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_width), | |
| 143 sizeof(int)) < 0 || | |
| 144 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&data_height), | |
| 145 sizeof(int)) < 0 || | |
| 146 file.WriteAtCurrentPos(reinterpret_cast<const char*>(&thumbnail->scale_), | |
| 147 sizeof(float)) < 0) { | |
| 148 success = false; | |
| 149 } | |
| 150 | |
| 151 size_t compressed_bytes = etc1_get_encoded_data_size( | |
| 152 thumbnail->data_size_.width(), thumbnail->data_size_.height()); | |
| 153 | |
| 154 if (file.WriteAtCurrentPos( | |
| 155 reinterpret_cast<char*>(thumbnail->compressed_data_->pixels()), | |
| 156 compressed_bytes) < 0) | |
| 157 success = false; | |
| 158 | |
| 159 thumbnail->compressed_data_->unlockPixels(); | |
| 160 return success; | |
| 161 } | |
| 162 | |
| 163 bool ThumbnailImpl::ReadFromFile(base::File& file, | |
| 164 scoped_refptr<ThumbnailImpl> thumbnail) { | |
| 165 // We assume caller has already initialized the file and will also close it. | |
| 166 DCHECK(file.IsValid()); | |
| 167 | |
| 168 int key; | |
| 169 bool success = true; | |
| 170 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&key), sizeof(int)) < 0 || | |
| 171 key != kCompressedKey) | |
| 172 success = false; | |
| 173 | |
| 174 int width, height; | |
| 175 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < 0 || | |
| 176 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < 0) | |
| 177 success = false; | |
| 178 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.
| |
| 179 | |
| 180 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&width), sizeof(int)) < 0 || | |
| 181 file.ReadAtCurrentPos(reinterpret_cast<char*>(&height), sizeof(int)) < 0) | |
| 182 success = false; | |
| 183 gfx::Size data_size = gfx::Size(width, height); | |
| 184 | |
| 185 float scale = 0.f; | |
| 186 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(&scale), sizeof(float)) < 0) | |
| 187 success = false; | |
| 188 | |
| 189 size_t compressed_bytes = | |
| 190 etc1_get_encoded_data_size(data_size.width(), data_size.height()); | |
| 191 | |
| 192 SkImageInfo info = {data_size.width(), | |
| 193 data_size.height(), | |
| 194 kUnknown_SkColorType, | |
| 195 kUnpremul_SkAlphaType}; | |
| 196 | |
| 197 scoped_ptr<uint8_t[]> data(new uint8_t[compressed_bytes]); | |
| 198 if (file.ReadAtCurrentPos(reinterpret_cast<char*>(data.get()), | |
| 199 compressed_bytes) < 0) | |
| 200 success = false; | |
| 201 | |
| 202 skia::RefPtr<SkData> etc1_pixel_data = | |
| 203 skia::AdoptRef(SkData::NewFromMalloc(data.release(), compressed_bytes)); | |
| 204 skia::RefPtr<SkPixelRef> compressed_data = skia::AdoptRef( | |
| 205 SkMallocPixelRef::NewWithData(info, 0, NULL, etc1_pixel_data.get())); | |
| 206 | |
| 207 if (success) | |
| 208 thumbnail->SetCompressedData(compressed_data, scale, content_size); | |
| 209 return success; | |
| 210 } | |
| 211 | |
| 212 scoped_refptr<ThumbnailImpl> ThumbnailImpl::PassDataToNewThumbnail() { | |
| 213 scoped_refptr<ThumbnailImpl> new_thumbnail = | |
| 214 make_scoped_refptr(new ThumbnailImpl(tab_id_, | |
| 215 SkBitmap(), | |
| 216 0.f, | |
| 217 thumbnail_impl_manager_, | |
| 218 ui_resource_provider_)); | |
| 219 new_thumbnail->raw_data_ = raw_data_; | |
| 220 new_thumbnail->compressed_data_ = compressed_data_; | |
| 221 new_thumbnail->scale_ = scale_; | |
| 222 new_thumbnail->data_size_ = data_size_; | |
| 223 new_thumbnail->content_size_ = content_size_; | |
| 224 | |
| 225 // Release this Thumbnail's reference to the data. | |
| 226 raw_data_ = SkBitmap(); | |
| 227 compressed_data_.clear(); | |
| 228 return new_thumbnail; | |
| 229 } | |
| 230 | |
| 231 cc::UIResourceBitmap ThumbnailImpl::GetBitmap(cc::UIResourceId uid, | |
| 232 bool resource_lost) { | |
| 233 if (!raw_data_.empty()) { | |
| 234 DCHECK(!compressed_data_); | |
| 235 return cc::UIResourceBitmap(raw_data_); | |
| 236 } else if (compressed_data_) { | |
| 237 DCHECK(raw_data_.empty()); | |
| 238 return cc::UIResourceBitmap(compressed_data_, data_size_); | |
| 239 } | |
| 240 | |
| 241 // Return a place holder for all other calls to GetBitmap. | |
| 242 SkBitmap tiny_bitmap; | |
| 243 SkCanvas canvas(tiny_bitmap); | |
| 244 tiny_bitmap.setConfig( | |
| 245 SkBitmap::kARGB_8888_Config, 1, 1, 0, kOpaque_SkAlphaType); | |
| 246 tiny_bitmap.allocPixels(); | |
| 247 canvas.drawColor(SK_ColorWHITE); | |
| 248 tiny_bitmap.setImmutable(); | |
| 249 return cc::UIResourceBitmap(tiny_bitmap); | |
| 250 } | |
| 251 | |
| 252 void ThumbnailImpl::UIResourceIsInvalid() { | |
| 253 ui_resource_id_ = 0; | |
| 254 if (thumbnail_impl_manager_) { | |
| 255 scoped_refptr<ThumbnailImpl> this_thumbnail(this); | |
| 256 thumbnail_impl_manager_->InvalidateCachedThumbnail(this_thumbnail); | |
| 257 } | |
| 258 } | |
| 259 | |
| 260 void ThumbnailImpl::SetCompressedData(skia::RefPtr<SkPixelRef> compressed_data, | |
| 261 float scale, | |
| 262 const gfx::Size& content_size) { | |
| 263 DCHECK(compressed_data); | |
| 264 compressed_data_ = compressed_data; | |
| 265 raw_data_ = SkBitmap(); | |
| 266 scale_ = scale; | |
| 267 content_size_ = content_size; | |
| 268 data_size_ = gfx::Size(compressed_data_->info().width(), | |
| 269 compressed_data_->info().height()); | |
| 270 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
| |
| 271 } | |
| 272 | |
| 273 void ThumbnailImpl::SetRawData(SkBitmap bitmap, float scale) { | |
| 274 raw_data_ = bitmap; | |
| 275 compressed_data_.clear(); | |
| 276 data_size_ = gfx::Size(bitmap.width(), bitmap.height()); | |
| 277 content_size_ = data_size_; | |
| 278 scale_ = scale; | |
| 279 BuildUIResource(); | |
| 280 } | |
| OLD | NEW |