Index: ui/gfx/image/image_skia.cc |
diff --git a/ui/gfx/image/image_skia.cc b/ui/gfx/image/image_skia.cc |
index f0b90a15ffa9601481de40cdf16f74076ce7610a..37fcfe93b45d81e25129d2339ee3a9d00566fb58 100644 |
--- a/ui/gfx/image/image_skia.cc |
+++ b/ui/gfx/image/image_skia.cc |
@@ -10,6 +10,7 @@ |
#include "base/logging.h" |
#include "base/memory/scoped_ptr.h" |
+#include "base/threading/non_thread_safe.h" |
#include "ui/gfx/image/image_skia_operations.h" |
#include "ui/gfx/image/image_skia_source.h" |
#include "ui/gfx/rect.h" |
@@ -48,11 +49,13 @@ class Matcher { |
// A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a |
// refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's |
// information. |
-class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage> { |
+class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage>, |
+ public base::NonThreadSafe { |
public: |
ImageSkiaStorage(ImageSkiaSource* source, const gfx::Size& size) |
: source_(source), |
- size_(size) { |
+ size_(size), |
+ read_only_(false) { |
} |
bool has_source() const { return source_.get() != NULL; } |
@@ -61,6 +64,30 @@ class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage> { |
const gfx::Size& size() const { return size_; } |
+ bool read_only() const { return read_only_; } |
+ |
+ void DeleteSource() { |
+ source_.reset(); |
+ } |
+ |
+ void SetReadOnly() { |
+ read_only_ = true; |
+ } |
+ |
+ void DetachFromThread() { |
+ base::NonThreadSafe::DetachFromThread(); |
+ } |
+ |
+ // Checks if the current thread can safely modify the storage. |
+ bool CanModify() const { |
+ return !read_only_ && CalledOnValidThread(); |
+ } |
+ |
+ // Checks if the current thread can safely read the storage. |
+ bool CanRead() const { |
+ return (read_only_ && !source_.get()) || CalledOnValidThread(); |
+ } |
+ |
// Returns the iterator of the image rep whose density best matches |
// |scale_factor|. If the image for the |scale_factor| doesn't exist |
// in the storage and |storage| is set, it fetches new image by calling |
@@ -96,6 +123,9 @@ class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage> { |
} |
if (fetch_new_image && source_.get()) { |
+ DCHECK(CalledOnValidThread()) << |
+ "An ImageSkia with the source must be accessed by the same thread."; |
+ |
ImageSkiaRep image = source_->GetImageForScale(scale_factor); |
// If the source returned the new image, store it. |
@@ -120,7 +150,10 @@ class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage> { |
} |
private: |
- ~ImageSkiaStorage() { |
+ virtual ~ImageSkiaStorage() { |
+ // We only care if the storage is modified by the same thread. |
+ // Don't blow up even if someone else deleted the ImageSkia. |
+ DetachFromThread(); |
} |
// Vector of bitmaps and their associated scale factor. |
@@ -131,6 +164,8 @@ class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage> { |
// Size of the image in DIP. |
const gfx::Size size_; |
+ bool read_only_; |
+ |
friend class base::RefCounted<ImageSkiaStorage>; |
}; |
@@ -142,14 +177,20 @@ ImageSkia::ImageSkia() : storage_(NULL) { |
ImageSkia::ImageSkia(ImageSkiaSource* source, const gfx::Size& size) |
: storage_(new internal::ImageSkiaStorage(source, size)) { |
DCHECK(source); |
+ // No other thread has reference to this, so it's safe to detach the thread. |
+ DetachStorageFromThread(); |
} |
ImageSkia::ImageSkia(const SkBitmap& bitmap) { |
Init(ImageSkiaRep(bitmap, ui::SCALE_FACTOR_100P)); |
+ // No other thread has reference to this, so it's safe to detach the thread. |
+ DetachStorageFromThread(); |
} |
ImageSkia::ImageSkia(const ImageSkiaRep& image_rep) { |
Init(image_rep); |
+ // No other thread has reference to this, so it's safe to detach the thread. |
+ DetachStorageFromThread(); |
} |
ImageSkia::ImageSkia(const ImageSkia& other) : storage_(other.storage_) { |
@@ -163,6 +204,25 @@ ImageSkia& ImageSkia::operator=(const ImageSkia& other) { |
ImageSkia::~ImageSkia() { |
} |
+ImageSkia ImageSkia::DeepCopy() const { |
+ ImageSkia copy; |
+ if (isNull()) |
+ return copy; |
+ |
+ CHECK(CanRead()); |
+ |
+ std::vector<gfx::ImageSkiaRep>& reps = storage_->image_reps(); |
+ for (std::vector<gfx::ImageSkiaRep>::iterator iter = reps.begin(); |
+ iter != reps.end(); ++iter) { |
+ copy.AddRepresentation(*iter); |
+ } |
+ // The copy has its own storage. Detach the copy from the current |
+ // thread so that other thread can use this. |
+ if (!copy.isNull()) |
+ copy.storage_->DetachFromThread(); |
+ return copy; |
+} |
+ |
bool ImageSkia::BackedBySameObjectAs(const gfx::ImageSkia& other) const { |
return storage_.get() == other.storage_.get(); |
} |
@@ -170,15 +230,24 @@ bool ImageSkia::BackedBySameObjectAs(const gfx::ImageSkia& other) const { |
void ImageSkia::AddRepresentation(const ImageSkiaRep& image_rep) { |
DCHECK(!image_rep.is_null()); |
- if (isNull()) |
+ // TODO(oshima): This method should be called |SetRepresentation| |
+ // and replace the existing rep if there is already one with the |
+ // same scale factor so that we can guarantee that a ImageSkia |
+ // instance contians only one image rep per scale factor. This is |
+ // not possible now as ImageLoadingTracker currently stores need |
+ // this feature, but this needs to be fixed. |
+ if (isNull()) { |
Init(image_rep); |
- else |
+ } else { |
+ CHECK(CanModify()); |
storage_->image_reps().push_back(image_rep); |
+ } |
} |
void ImageSkia::RemoveRepresentation(ui::ScaleFactor scale_factor) { |
if (isNull()) |
return; |
+ CHECK(CanModify()); |
ImageSkiaReps& image_reps = storage_->image_reps(); |
ImageSkiaReps::iterator it = |
@@ -190,6 +259,7 @@ void ImageSkia::RemoveRepresentation(ui::ScaleFactor scale_factor) { |
bool ImageSkia::HasRepresentation(ui::ScaleFactor scale_factor) const { |
if (isNull()) |
return false; |
+ CHECK(CanRead()); |
ImageSkiaReps::iterator it = |
storage_->FindRepresentation(scale_factor, false); |
@@ -202,6 +272,8 @@ const ImageSkiaRep& ImageSkia::GetRepresentation( |
if (isNull()) |
return NullImageRep(); |
+ CHECK(CanRead()); |
+ |
ImageSkiaReps::iterator it = storage_->FindRepresentation(scale_factor, true); |
if (it == storage_->image_reps().end()) |
return NullImageRep(); |
@@ -209,28 +281,25 @@ const ImageSkiaRep& ImageSkia::GetRepresentation( |
return *it; |
} |
-#if defined(OS_MACOSX) |
- |
-std::vector<ImageSkiaRep> ImageSkia::GetRepresentations() const { |
- if (isNull()) |
- return std::vector<ImageSkiaRep>(); |
- |
- if (!storage_->has_source()) |
- return image_reps(); |
- |
- // Attempt to generate image reps for as many scale factors supported by |
- // this platform as possible. |
- // Do not build return array here because the mapping from scale factor to |
- // image rep is one to many in some cases. |
- std::vector<ui::ScaleFactor> supported_scale_factors = |
- ui::GetSupportedScaleFactors(); |
- for (size_t i = 0; i < supported_scale_factors.size(); ++i) |
- storage_->FindRepresentation(supported_scale_factors[i], true); |
+void ImageSkia::SetReadOnly() { |
+ CHECK(storage_); |
+ storage_->SetReadOnly(); |
+ DetachStorageFromThread(); |
+} |
- return image_reps(); |
+void ImageSkia::MakeThreadSafe() { |
+ CHECK(storage_); |
+ EnsureRepsForSupportedScaleFactors(); |
+ // Delete source as we no longer needs it. |
+ if (storage_) |
+ storage_->DeleteSource(); |
+ storage_->SetReadOnly(); |
+ CHECK(IsThreadSafe()); |
} |
-#endif // OS_MACOSX |
+bool ImageSkia::IsThreadSafe() const { |
+ return !storage_ || (storage_->read_only() && !storage_->has_source()); |
+} |
int ImageSkia::width() const { |
return isNull() ? 0 : storage_->size().width(); |
@@ -248,6 +317,8 @@ std::vector<ImageSkiaRep> ImageSkia::image_reps() const { |
if (isNull()) |
return std::vector<ImageSkiaRep>(); |
+ CHECK(CanRead()); |
+ |
ImageSkiaReps internal_image_reps = storage_->image_reps(); |
// Create list of image reps to return, skipping null image reps which were |
// added for caching purposes only. |
@@ -261,6 +332,18 @@ std::vector<ImageSkiaRep> ImageSkia::image_reps() const { |
return image_reps; |
} |
+void ImageSkia::EnsureRepsForSupportedScaleFactors() const { |
+ // Don't check ReadOnly because the source may generate images |
+ // even for read only ImageSkia. Concurrent access will be protected |
+ // by |DCHECK(CalledOnValidThread())| in FindRepresentation. |
+ if (storage_ && storage_->has_source()) { |
+ std::vector<ui::ScaleFactor> supported_scale_factors = |
+ ui::GetSupportedScaleFactors(); |
+ for (size_t i = 0; i < supported_scale_factors.size(); ++i) |
+ storage_->FindRepresentation(supported_scale_factors[i], true); |
+ } |
+} |
+ |
void ImageSkia::Init(const ImageSkiaRep& image_rep) { |
// TODO(pkotwicz): The image should be null whenever image rep is null. |
if (image_rep.sk_bitmap().empty()) { |
@@ -279,6 +362,8 @@ SkBitmap& ImageSkia::GetBitmap() const { |
return NullImageRep().mutable_sk_bitmap(); |
} |
+ CHECK(CanRead()); |
+ |
ImageSkiaReps::iterator it = |
storage_->FindRepresentation(ui::SCALE_FACTOR_100P, true); |
if (it != storage_->image_reps().end()) |
@@ -286,4 +371,17 @@ SkBitmap& ImageSkia::GetBitmap() const { |
return NullImageRep().mutable_sk_bitmap(); |
} |
+bool ImageSkia::CanRead() const { |
+ return !storage_ || storage_->CanRead(); |
+} |
+ |
+bool ImageSkia::CanModify() const { |
+ return !storage_ || storage_->CanModify(); |
+} |
+ |
+void ImageSkia::DetachStorageFromThread() { |
+ if (storage_) |
+ storage_->DetachFromThread(); |
+} |
+ |
} // namespace gfx |