| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 "cc/tiles/image_decode_controller.h" | 5 #include "cc/tiles/image_decode_controller.h" |
| 6 | 6 |
| 7 #include "cc/debug/devtools_instrumentation.h" | 7 #include "cc/debug/devtools_instrumentation.h" |
| 8 #include "third_party/skia/include/core/SkCanvas.h" |
| 9 #include "base/memory/discardable_memory.h" |
| 8 | 10 |
| 9 namespace cc { | 11 namespace cc { |
| 10 namespace { | 12 namespace { |
| 11 | 13 |
| 12 class ImageDecodeTaskImpl : public ImageDecodeTask { | 14 class ImageDecodeTaskImpl : public ImageDecodeTask { |
| 13 public: | 15 public: |
| 14 ImageDecodeTaskImpl(ImageDecodeController* controller, | 16 ImageDecodeTaskImpl(ImageDecodeController* controller, |
| 15 const SkImage* image, | 17 const ImageDecodeController::ImageKey& image_key, |
| 16 int layer_id, | 18 const PositionImage& image, |
| 17 uint64_t source_prepare_tiles_id) | 19 uint64_t source_prepare_tiles_id) |
| 18 : controller_(controller), | 20 : controller_(controller), |
| 19 image_(skia::SharePtr(image)), | 21 image_key_(image_key), |
| 20 layer_id_(layer_id), | 22 image_(image), |
| 23 image_ref_(skia::SharePtr(image.image)), |
| 21 source_prepare_tiles_id_(source_prepare_tiles_id) {} | 24 source_prepare_tiles_id_(source_prepare_tiles_id) {} |
| 22 | 25 |
| 23 // Overridden from Task: | 26 // Overridden from Task: |
| 24 void RunOnWorkerThread() override { | 27 void RunOnWorkerThread() override { |
| 25 TRACE_EVENT1("cc", "ImageDecodeTaskImpl::RunOnWorkerThread", | 28 TRACE_EVENT1("cc", "ImageDecodeTaskImpl::RunOnWorkerThread", |
| 26 "source_prepare_tiles_id", source_prepare_tiles_id_); | 29 "source_prepare_tiles_id", source_prepare_tiles_id_); |
| 27 devtools_instrumentation::ScopedImageDecodeTask image_decode_task( | 30 devtools_instrumentation::ScopedImageDecodeTask image_decode_task( |
| 28 image_.get()); | 31 image_ref_.get()); |
| 29 controller_->DecodeImage(image_.get()); | 32 controller_->DecodeImage(image_key_, image_); |
| 30 | 33 |
| 31 // Release the reference after decoding image to ensure that it is not kept | 34 // Release the reference after decoding image to ensure that it is not kept |
| 32 // alive unless needed. | 35 // alive unless needed. |
| 33 image_.clear(); | 36 image_ref_.clear(); |
| 34 } | 37 } |
| 35 | 38 |
| 36 // Overridden from TileTask: | 39 // Overridden from TileTask: |
| 37 void ScheduleOnOriginThread(TileTaskClient* client) override {} | 40 void ScheduleOnOriginThread(TileTaskClient* client) override {} |
| 38 void CompleteOnOriginThread(TileTaskClient* client) override { | 41 void CompleteOnOriginThread(TileTaskClient* client) override { |
| 39 controller_->OnImageDecodeTaskCompleted(layer_id_, image_.get(), | 42 controller_->OnImageDecodeTaskCompleted(image_key_, !HasFinishedRunning()); |
| 40 !HasFinishedRunning()); | |
| 41 } | 43 } |
| 42 | 44 |
| 43 protected: | 45 protected: |
| 44 ~ImageDecodeTaskImpl() override {} | 46 ~ImageDecodeTaskImpl() override {} |
| 45 | 47 |
| 46 private: | 48 private: |
| 47 ImageDecodeController* controller_; | 49 ImageDecodeController* controller_; |
| 48 skia::RefPtr<const SkImage> image_; | 50 ImageDecodeController::ImageKey image_key_; |
| 49 int layer_id_; | 51 PositionImage image_; |
| 52 skia::RefPtr<const SkImage> image_ref_; |
| 50 uint64_t source_prepare_tiles_id_; | 53 uint64_t source_prepare_tiles_id_; |
| 51 | 54 |
| 52 DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl); | 55 DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl); |
| 53 }; | 56 }; |
| 54 | 57 |
| 58 // Return the scaled size of the image under the given transformation. |
| 59 gfx::Size ExtractSizeFromImage(const SkImage* image, |
| 60 const SkMatrix& total_matrix) { |
| 61 SkSize scale = |
| 62 SkSize::Make(total_matrix.getScaleX(), total_matrix.getScaleY()); |
| 63 if (total_matrix.getType() & SkMatrix::kAffine_Mask) { |
| 64 if (!total_matrix.decomposeScale(&scale)) |
| 65 scale.set(1, 1); |
| 66 } |
| 67 |
| 68 return gfx::Size( |
| 69 std::abs(SkScalarRoundToInt(image->width() * scale.width())), |
| 70 std::abs(SkScalarRoundToInt(image->height() * scale.height()))); |
| 71 } |
| 72 |
| 55 } // namespace | 73 } // namespace |
| 56 | 74 |
| 57 ImageDecodeController::ImageDecodeController() {} | 75 ImageDecodeController::ImageDecodeController() {} |
| 58 | 76 |
| 59 ImageDecodeController::~ImageDecodeController() {} | 77 ImageDecodeController::~ImageDecodeController() {} |
| 60 | 78 |
| 61 scoped_refptr<ImageDecodeTask> ImageDecodeController::GetTaskForImage( | 79 scoped_refptr<ImageDecodeTask> ImageDecodeController::GetTaskForImage( |
| 62 const PositionImage& image, | 80 const PositionImage& image, |
| 63 int layer_id, | |
| 64 uint64_t prepare_tiles_id) { | 81 uint64_t prepare_tiles_id) { |
| 65 uint32_t generation_id = image.image->uniqueID(); | 82 ImageKey key = |
| 66 scoped_refptr<ImageDecodeTask>& decode_task = | 83 ImageKey::FromImageData(image.image, image.matrix, image.filter_quality); |
| 67 image_decode_tasks_[layer_id][generation_id]; | 84 if (LockDecodedImageIfPossible(key)) |
| 68 if (!decode_task) | 85 return nullptr; |
| 69 decode_task = CreateTaskForImage(image.image, layer_id, prepare_tiles_id); | 86 |
| 70 return decode_task; | 87 auto decode_task_iterator = |
| 88 std::find_if(pending_image_tasks_.begin(), pending_image_tasks_.end(), |
| 89 [key](const AnnotatedDecodeTask& annotated_task) { |
| 90 return key == annotated_task.first; |
| 91 }); |
| 92 // If the task doesn't exist, create it. |
| 93 if (decode_task_iterator == pending_image_tasks_.end()) { |
| 94 pending_image_tasks_.push_back(AnnotatedDecodeTask( |
| 95 key, CreateTaskForImage(key, image, prepare_tiles_id))); |
| 96 return pending_image_tasks_.back().second; |
| 97 } |
| 98 return decode_task_iterator->second; |
| 99 } |
| 100 |
| 101 bool ImageDecodeController::LockDecodedImageIfPossible(const ImageKey& key) { |
| 102 base::AutoLock lock(lock_); |
| 103 auto decoded_images_iterator = |
| 104 std::find_if(decoded_images_.begin(), decoded_images_.end(), |
| 105 [key](const AnnotatedDecodedImage& annotated_image) { |
| 106 return key == annotated_image.first; |
| 107 }); |
| 108 if (decoded_images_iterator == decoded_images_.end()) |
| 109 return false; |
| 110 |
| 111 scoped_refptr<DecodedImage>& decoded_image = decoded_images_iterator->second; |
| 112 |
| 113 // Figure out if the image is locked or try to lock it. |
| 114 bool locked = decoded_image->is_locked(); |
| 115 if (!locked) |
| 116 locked = decoded_image->Lock(); |
| 117 |
| 118 if (locked) |
| 119 return true; |
| 120 |
| 121 // If we couldn't lock the image, then we might as well clean up the entry in |
| 122 // the cache, since for all intents and purposes it is a cache miss. |
| 123 decoded_images_.erase(decoded_images_iterator); |
| 124 return false; |
| 71 } | 125 } |
| 72 | 126 |
| 73 scoped_refptr<ImageDecodeTask> ImageDecodeController::CreateTaskForImage( | 127 scoped_refptr<ImageDecodeTask> ImageDecodeController::CreateTaskForImage( |
| 74 const SkImage* image, | 128 const ImageKey& key, |
| 75 int layer_id, | 129 const PositionImage& image, |
| 76 uint64_t prepare_tiles_id) { | 130 uint64_t prepare_tiles_id) { |
| 77 return make_scoped_refptr( | 131 return make_scoped_refptr( |
| 78 new ImageDecodeTaskImpl(this, image, layer_id, prepare_tiles_id)); | 132 new ImageDecodeTaskImpl(this, key, image, prepare_tiles_id)); |
| 79 } | 133 } |
| 80 | 134 |
| 81 void ImageDecodeController::DecodeImage(const SkImage* image) { | 135 void ImageDecodeController::DecodeImage(const ImageKey& key, |
| 82 image->preroll(); | 136 const PositionImage& image) { |
| 137 // For low quality, just decode the original image. |
| 138 if (image.filter_quality == kLow_SkFilterQuality) { |
| 139 image.image->preroll(); |
| 140 return; |
| 141 } |
| 142 |
| 143 // TODO(vmpstr): We need to start generating/caching mipmaps here (if needed) |
| 144 // and interpolating the image between the nearest mipmaps. |
| 145 if (image.filter_quality == kMedium_SkFilterQuality) { |
| 146 image.image->preroll(); |
| 147 return; |
| 148 } |
| 149 |
| 150 // TODO(vmpstr, reed): Scale the image here without caching it in skia. |
| 151 SkImageInfo info = SkImageInfo::MakeN32Premul(key.target_size().width(), |
| 152 key.target_size().height()); |
| 153 scoped_ptr<base::DiscardableMemory> locked_memory = |
| 154 base::DiscardableMemoryAllocator::GetInstance() |
| 155 ->AllocateLockedDiscardableMemory(info.minRowBytes() * info.height()); |
| 156 |
| 157 skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(SkCanvas::NewRasterDirect( |
| 158 info, locked_memory->data(), info.minRowBytes())); |
| 159 canvas->setMatrix(SkMatrix::MakeRectToRect( |
| 160 SkRect::MakeWH(image.image->width(), image.image->height()), |
| 161 SkRect::MakeWH(key.target_size().width(), key.target_size().height()), |
| 162 SkMatrix::kFill_ScaleToFit)); |
| 163 canvas->clear(SK_ColorTRANSPARENT); |
| 164 SkPaint paint; |
| 165 paint.setFilterQuality(kHigh_SkFilterQuality); |
| 166 canvas->drawImage(image.image, 0, 0, &paint); |
| 167 canvas->flush(); |
| 168 |
| 169 // Add the image to the cache. |
| 170 base::AutoLock lock(lock_); |
| 171 decoded_images_.push_back(AnnotatedDecodedImage( |
| 172 key, make_scoped_refptr(new DecodedImage(info, locked_memory.Pass())))); |
| 83 } | 173 } |
| 84 | 174 |
| 85 void ImageDecodeController::AddLayerUsedCount(int layer_id) { | 175 void ImageDecodeController::OnImageDecodeTaskCompleted(const ImageKey& key, |
| 86 ++used_layer_counts_[layer_id]; | 176 bool was_canceled) { |
| 177 pending_image_tasks_.erase( |
| 178 std::remove_if( |
| 179 pending_image_tasks_.begin(), pending_image_tasks_.end(), |
| 180 [key](const AnnotatedDecodeTask& task) { return key == task.first; }), |
| 181 pending_image_tasks_.end()); |
| 87 } | 182 } |
| 88 | 183 |
| 89 void ImageDecodeController::SubtractLayerUsedCount(int layer_id) { | 184 DecodedPositionImage ImageDecodeController::GetDecodedImage( |
| 90 if (--used_layer_counts_[layer_id]) | 185 const PositionImage& image) { |
| 91 return; | 186 // We don't cache low filter quality images. |
| 187 if (image.filter_quality == kLow_SkFilterQuality) { |
| 188 return DecodedPositionImage(image.image, SkSize::Make(1.f, 1.f), |
| 189 image.filter_quality); |
| 190 } |
| 92 | 191 |
| 93 // Clean up decode tasks once a layer is no longer used. | 192 // TODO(vmpstr): We need to start caching mipmaps for medium quality and |
| 94 used_layer_counts_.erase(layer_id); | 193 // caching the interpolated values from those. For now, pass these through |
| 95 image_decode_tasks_.erase(layer_id); | 194 // unchanged. |
| 195 if (image.filter_quality == kMedium_SkFilterQuality) { |
| 196 return DecodedPositionImage(image.image, SkSize::Make(1.f, 1.f), |
| 197 image.filter_quality); |
| 198 } |
| 199 |
| 200 // XXX look up or decode. XXX XXX XXX XXX XXX XXX XXX XXX |
| 201 // Also need to figure when to free the memory? When to unlock. |
| 202 ImageKey key = |
| 203 ImageKey::FromImageData(image.image, image.matrix, image.filter_quality); |
| 204 |
| 205 base::AutoLock lock(lock_); |
| 206 auto decoded_images_iterator = |
| 207 std::find_if(decoded_images_.begin(), decoded_images_.end(), |
| 208 [key](const AnnotatedDecodedImage& annotated_image) { |
| 209 return key == annotated_image.first; |
| 210 }); |
| 211 if (decoded_images_iterator != decoded_images_.end()) { |
| 212 float x_scale = |
| 213 key.target_size().width() / static_cast<float>(image.image->width()); |
| 214 float y_scale = |
| 215 key.target_size().height() / static_cast<float>(image.image->height()); |
| 216 |
| 217 const scoped_refptr<DecodedImage>& decoded_image = |
| 218 decoded_images_iterator->second; |
| 219 return DecodedPositionImage(decoded_image->image(), |
| 220 SkSize::Make(x_scale, y_scale), |
| 221 kLow_SkFilterQuality); |
| 222 } |
| 223 CHECK(false); |
| 224 return DecodedPositionImage(image.image, SkSize::Make(1.f, 1.f), |
| 225 image.filter_quality); |
| 96 } | 226 } |
| 97 | 227 |
| 98 void ImageDecodeController::OnImageDecodeTaskCompleted(int layer_id, | 228 ImageDecodeController::ImageKey ImageDecodeController::ImageKey::FromImageData( |
| 99 const SkImage* image, | 229 const SkImage* image, |
| 100 bool was_canceled) { | 230 const SkMatrix& matrix, |
| 101 // If the task has successfully finished, then keep the task until the layer | 231 SkFilterQuality filter_quality) { |
| 102 // is no longer in use. This ensures that we only decode a image once. | 232 return ImageKey(image->uniqueID(), ExtractSizeFromImage(image, matrix), |
| 103 // TODO(vmpstr): Remove this when decode lifetime is controlled by cc. | 233 filter_quality); |
| 104 if (!was_canceled) | 234 } |
| 105 return; | |
| 106 | 235 |
| 107 // Otherwise, we have to clean up the task so that a new one can be created if | 236 ImageDecodeController::ImageKey::ImageKey(uint32_t image_id, |
| 108 // we need to decode the image again. | 237 const gfx::Size& size, |
| 109 LayerImageTaskMap::iterator layer_it = image_decode_tasks_.find(layer_id); | 238 SkFilterQuality filter_quality) |
| 110 if (layer_it == image_decode_tasks_.end()) | 239 : image_id_(image_id), size_(size), filter_quality_(filter_quality) {} |
| 111 return; | |
| 112 | 240 |
| 113 ImageTaskMap& image_tasks = layer_it->second; | 241 // TODO(vmpstr): Not sure if this needs to do something. It could have some |
| 114 ImageTaskMap::iterator task_it = image_tasks.find(image->uniqueID()); | 242 // extra state to DCHECK if we pass it DecodedImage object as the context. |
| 115 if (task_it == image_tasks.end()) | 243 static void noop(const void* pixels, void* context) {} |
| 116 return; | 244 |
| 117 image_tasks.erase(task_it); | 245 ImageDecodeController::DecodedImage::DecodedImage( |
| 246 const SkImageInfo& info, |
| 247 scoped_ptr<base::DiscardableMemory> memory) |
| 248 : locked_(true), image_info_(info), memory_(memory.Pass()) { |
| 249 CreateImageFromLockedMemory(); |
| 250 } |
| 251 |
| 252 ImageDecodeController::DecodedImage::~DecodedImage() {} |
| 253 |
| 254 bool ImageDecodeController::DecodedImage::Lock() { |
| 255 DCHECK(!locked_); |
| 256 bool success = memory_->Lock(); |
| 257 if (!success) |
| 258 return false; |
| 259 CreateImageFromLockedMemory(); |
| 260 return true; |
| 261 } |
| 262 |
| 263 void ImageDecodeController::DecodedImage::Unlock() { |
| 264 DCHECK(locked_); |
| 265 image_.clear(); |
| 266 memory_->Unlock(); |
| 267 locked_ = false; |
| 268 } |
| 269 |
| 270 void ImageDecodeController::DecodedImage::CreateImageFromLockedMemory() { |
| 271 image_ = skia::AdoptRef(SkImage::NewFromRaster( |
| 272 image_info_, memory_->data(), image_info_.minRowBytes(), &noop, nullptr)); |
| 118 } | 273 } |
| 119 | 274 |
| 120 } // namespace cc | 275 } // namespace cc |
| OLD | NEW |