Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3924)

Unified Diff: cc/tiles/image_decode_controller.cc

Issue 1385733002: (not for review) nwayover (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « cc/tiles/image_decode_controller.h ('k') | cc/tiles/tile_manager.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: cc/tiles/image_decode_controller.cc
diff --git a/cc/tiles/image_decode_controller.cc b/cc/tiles/image_decode_controller.cc
index 19e16c47ec6c871a5ee4b95be3b415b320c8e1f4..ecbddea5ff95cea77167b451365ec7d7dbad1bea 100644
--- a/cc/tiles/image_decode_controller.cc
+++ b/cc/tiles/image_decode_controller.cc
@@ -5,6 +5,8 @@
#include "cc/tiles/image_decode_controller.h"
#include "cc/debug/devtools_instrumentation.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "base/memory/discardable_memory.h"
namespace cc {
namespace {
@@ -12,12 +14,13 @@ namespace {
class ImageDecodeTaskImpl : public ImageDecodeTask {
public:
ImageDecodeTaskImpl(ImageDecodeController* controller,
- const SkImage* image,
- int layer_id,
+ const ImageDecodeController::ImageKey& image_key,
+ const PositionImage& image,
uint64_t source_prepare_tiles_id)
: controller_(controller),
- image_(skia::SharePtr(image)),
- layer_id_(layer_id),
+ image_key_(image_key),
+ image_(image),
+ image_ref_(skia::SharePtr(image.image)),
source_prepare_tiles_id_(source_prepare_tiles_id) {}
// Overridden from Task:
@@ -25,19 +28,18 @@ class ImageDecodeTaskImpl : public ImageDecodeTask {
TRACE_EVENT1("cc", "ImageDecodeTaskImpl::RunOnWorkerThread",
"source_prepare_tiles_id", source_prepare_tiles_id_);
devtools_instrumentation::ScopedImageDecodeTask image_decode_task(
- image_.get());
- controller_->DecodeImage(image_.get());
+ image_ref_.get());
+ controller_->DecodeImage(image_key_, image_);
// Release the reference after decoding image to ensure that it is not kept
// alive unless needed.
- image_.clear();
+ image_ref_.clear();
}
// Overridden from TileTask:
void ScheduleOnOriginThread(TileTaskClient* client) override {}
void CompleteOnOriginThread(TileTaskClient* client) override {
- controller_->OnImageDecodeTaskCompleted(layer_id_, image_.get(),
- !HasFinishedRunning());
+ controller_->OnImageDecodeTaskCompleted(image_key_, !HasFinishedRunning());
}
protected:
@@ -45,13 +47,29 @@ class ImageDecodeTaskImpl : public ImageDecodeTask {
private:
ImageDecodeController* controller_;
- skia::RefPtr<const SkImage> image_;
- int layer_id_;
+ ImageDecodeController::ImageKey image_key_;
+ PositionImage image_;
+ skia::RefPtr<const SkImage> image_ref_;
uint64_t source_prepare_tiles_id_;
DISALLOW_COPY_AND_ASSIGN(ImageDecodeTaskImpl);
};
+// Return the scaled size of the image under the given transformation.
+gfx::Size ExtractSizeFromImage(const SkImage* image,
+ const SkMatrix& total_matrix) {
+ SkSize scale =
+ SkSize::Make(total_matrix.getScaleX(), total_matrix.getScaleY());
+ if (total_matrix.getType() & SkMatrix::kAffine_Mask) {
+ if (!total_matrix.decomposeScale(&scale))
+ scale.set(1, 1);
+ }
+
+ return gfx::Size(
+ std::abs(SkScalarRoundToInt(image->width() * scale.width())),
+ std::abs(SkScalarRoundToInt(image->height() * scale.height())));
+}
+
} // namespace
ImageDecodeController::ImageDecodeController() {}
@@ -60,61 +78,198 @@ ImageDecodeController::~ImageDecodeController() {}
scoped_refptr<ImageDecodeTask> ImageDecodeController::GetTaskForImage(
const PositionImage& image,
- int layer_id,
uint64_t prepare_tiles_id) {
- uint32_t generation_id = image.image->uniqueID();
- scoped_refptr<ImageDecodeTask>& decode_task =
- image_decode_tasks_[layer_id][generation_id];
- if (!decode_task)
- decode_task = CreateTaskForImage(image.image, layer_id, prepare_tiles_id);
- return decode_task;
+ ImageKey key =
+ ImageKey::FromImageData(image.image, image.matrix, image.filter_quality);
+ if (LockDecodedImageIfPossible(key))
+ return nullptr;
+
+ auto decode_task_iterator =
+ std::find_if(pending_image_tasks_.begin(), pending_image_tasks_.end(),
+ [key](const AnnotatedDecodeTask& annotated_task) {
+ return key == annotated_task.first;
+ });
+ // If the task doesn't exist, create it.
+ if (decode_task_iterator == pending_image_tasks_.end()) {
+ pending_image_tasks_.push_back(AnnotatedDecodeTask(
+ key, CreateTaskForImage(key, image, prepare_tiles_id)));
+ return pending_image_tasks_.back().second;
+ }
+ return decode_task_iterator->second;
+}
+
+bool ImageDecodeController::LockDecodedImageIfPossible(const ImageKey& key) {
+ base::AutoLock lock(lock_);
+ auto decoded_images_iterator =
+ std::find_if(decoded_images_.begin(), decoded_images_.end(),
+ [key](const AnnotatedDecodedImage& annotated_image) {
+ return key == annotated_image.first;
+ });
+ if (decoded_images_iterator == decoded_images_.end())
+ return false;
+
+ scoped_refptr<DecodedImage>& decoded_image = decoded_images_iterator->second;
+
+ // Figure out if the image is locked or try to lock it.
+ bool locked = decoded_image->is_locked();
+ if (!locked)
+ locked = decoded_image->Lock();
+
+ if (locked)
+ return true;
+
+ // If we couldn't lock the image, then we might as well clean up the entry in
+ // the cache, since for all intents and purposes it is a cache miss.
+ decoded_images_.erase(decoded_images_iterator);
+ return false;
}
scoped_refptr<ImageDecodeTask> ImageDecodeController::CreateTaskForImage(
- const SkImage* image,
- int layer_id,
+ const ImageKey& key,
+ const PositionImage& image,
uint64_t prepare_tiles_id) {
return make_scoped_refptr(
- new ImageDecodeTaskImpl(this, image, layer_id, prepare_tiles_id));
+ new ImageDecodeTaskImpl(this, key, image, prepare_tiles_id));
+}
+
+void ImageDecodeController::DecodeImage(const ImageKey& key,
+ const PositionImage& image) {
+ // For low quality, just decode the original image.
+ if (image.filter_quality == kLow_SkFilterQuality) {
+ image.image->preroll();
+ return;
+ }
+
+ // TODO(vmpstr): We need to start generating/caching mipmaps here (if needed)
+ // and interpolating the image between the nearest mipmaps.
+ if (image.filter_quality == kMedium_SkFilterQuality) {
+ image.image->preroll();
+ return;
+ }
+
+ // TODO(vmpstr, reed): Scale the image here without caching it in skia.
+ SkImageInfo info = SkImageInfo::MakeN32Premul(key.target_size().width(),
+ key.target_size().height());
+ scoped_ptr<base::DiscardableMemory> locked_memory =
+ base::DiscardableMemoryAllocator::GetInstance()
+ ->AllocateLockedDiscardableMemory(info.minRowBytes() * info.height());
+
+ skia::RefPtr<SkCanvas> canvas = skia::AdoptRef(SkCanvas::NewRasterDirect(
+ info, locked_memory->data(), info.minRowBytes()));
+ canvas->setMatrix(SkMatrix::MakeRectToRect(
+ SkRect::MakeWH(image.image->width(), image.image->height()),
+ SkRect::MakeWH(key.target_size().width(), key.target_size().height()),
+ SkMatrix::kFill_ScaleToFit));
+ canvas->clear(SK_ColorTRANSPARENT);
+ SkPaint paint;
+ paint.setFilterQuality(kHigh_SkFilterQuality);
+ canvas->drawImage(image.image, 0, 0, &paint);
+ canvas->flush();
+
+ // Add the image to the cache.
+ base::AutoLock lock(lock_);
+ decoded_images_.push_back(AnnotatedDecodedImage(
+ key, make_scoped_refptr(new DecodedImage(info, locked_memory.Pass()))));
}
-void ImageDecodeController::DecodeImage(const SkImage* image) {
- image->preroll();
+void ImageDecodeController::OnImageDecodeTaskCompleted(const ImageKey& key,
+ bool was_canceled) {
+ pending_image_tasks_.erase(
+ std::remove_if(
+ pending_image_tasks_.begin(), pending_image_tasks_.end(),
+ [key](const AnnotatedDecodeTask& task) { return key == task.first; }),
+ pending_image_tasks_.end());
}
-void ImageDecodeController::AddLayerUsedCount(int layer_id) {
- ++used_layer_counts_[layer_id];
+DecodedPositionImage ImageDecodeController::GetDecodedImage(
+ const PositionImage& image) {
+ // We don't cache low filter quality images.
+ if (image.filter_quality == kLow_SkFilterQuality) {
+ return DecodedPositionImage(image.image, SkSize::Make(1.f, 1.f),
+ image.filter_quality);
+ }
+
+ // TODO(vmpstr): We need to start caching mipmaps for medium quality and
+ // caching the interpolated values from those. For now, pass these through
+ // unchanged.
+ if (image.filter_quality == kMedium_SkFilterQuality) {
+ return DecodedPositionImage(image.image, SkSize::Make(1.f, 1.f),
+ image.filter_quality);
+ }
+
+ // XXX look up or decode. XXX XXX XXX XXX XXX XXX XXX XXX
+ // Also need to figure when to free the memory? When to unlock.
+ ImageKey key =
+ ImageKey::FromImageData(image.image, image.matrix, image.filter_quality);
+
+ base::AutoLock lock(lock_);
+ auto decoded_images_iterator =
+ std::find_if(decoded_images_.begin(), decoded_images_.end(),
+ [key](const AnnotatedDecodedImage& annotated_image) {
+ return key == annotated_image.first;
+ });
+ if (decoded_images_iterator != decoded_images_.end()) {
+ float x_scale =
+ key.target_size().width() / static_cast<float>(image.image->width());
+ float y_scale =
+ key.target_size().height() / static_cast<float>(image.image->height());
+
+ const scoped_refptr<DecodedImage>& decoded_image =
+ decoded_images_iterator->second;
+ return DecodedPositionImage(decoded_image->image(),
+ SkSize::Make(x_scale, y_scale),
+ kLow_SkFilterQuality);
+ }
+ CHECK(false);
+ return DecodedPositionImage(image.image, SkSize::Make(1.f, 1.f),
+ image.filter_quality);
}
-void ImageDecodeController::SubtractLayerUsedCount(int layer_id) {
- if (--used_layer_counts_[layer_id])
- return;
+ImageDecodeController::ImageKey ImageDecodeController::ImageKey::FromImageData(
+ const SkImage* image,
+ const SkMatrix& matrix,
+ SkFilterQuality filter_quality) {
+ return ImageKey(image->uniqueID(), ExtractSizeFromImage(image, matrix),
+ filter_quality);
+}
+
+ImageDecodeController::ImageKey::ImageKey(uint32_t image_id,
+ const gfx::Size& size,
+ SkFilterQuality filter_quality)
+ : image_id_(image_id), size_(size), filter_quality_(filter_quality) {}
- // Clean up decode tasks once a layer is no longer used.
- used_layer_counts_.erase(layer_id);
- image_decode_tasks_.erase(layer_id);
+// TODO(vmpstr): Not sure if this needs to do something. It could have some
+// extra state to DCHECK if we pass it DecodedImage object as the context.
+static void noop(const void* pixels, void* context) {}
+
+ImageDecodeController::DecodedImage::DecodedImage(
+ const SkImageInfo& info,
+ scoped_ptr<base::DiscardableMemory> memory)
+ : locked_(true), image_info_(info), memory_(memory.Pass()) {
+ CreateImageFromLockedMemory();
}
-void ImageDecodeController::OnImageDecodeTaskCompleted(int layer_id,
- const SkImage* image,
- bool was_canceled) {
- // If the task has successfully finished, then keep the task until the layer
- // is no longer in use. This ensures that we only decode a image once.
- // TODO(vmpstr): Remove this when decode lifetime is controlled by cc.
- if (!was_canceled)
- return;
+ImageDecodeController::DecodedImage::~DecodedImage() {}
- // Otherwise, we have to clean up the task so that a new one can be created if
- // we need to decode the image again.
- LayerImageTaskMap::iterator layer_it = image_decode_tasks_.find(layer_id);
- if (layer_it == image_decode_tasks_.end())
- return;
+bool ImageDecodeController::DecodedImage::Lock() {
+ DCHECK(!locked_);
+ bool success = memory_->Lock();
+ if (!success)
+ return false;
+ CreateImageFromLockedMemory();
+ return true;
+}
- ImageTaskMap& image_tasks = layer_it->second;
- ImageTaskMap::iterator task_it = image_tasks.find(image->uniqueID());
- if (task_it == image_tasks.end())
- return;
- image_tasks.erase(task_it);
+void ImageDecodeController::DecodedImage::Unlock() {
+ DCHECK(locked_);
+ image_.clear();
+ memory_->Unlock();
+ locked_ = false;
+}
+
+void ImageDecodeController::DecodedImage::CreateImageFromLockedMemory() {
+ image_ = skia::AdoptRef(SkImage::NewFromRaster(
+ image_info_, memory_->data(), image_info_.minRowBytes(), &noop, nullptr));
}
} // namespace cc
« no previous file with comments | « cc/tiles/image_decode_controller.h ('k') | cc/tiles/tile_manager.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698