Home | History | Annotate | Download | only in image
      1 // Copyright (c) 2012 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 "ui/gfx/image/image_skia.h"
      6 
      7 #include <algorithm>
      8 #include <cmath>
      9 #include <limits>
     10 
     11 #include "base/logging.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/threading/non_thread_safe.h"
     14 #include "ui/gfx/image/image_skia_operations.h"
     15 #include "ui/gfx/image/image_skia_source.h"
     16 #include "ui/gfx/rect.h"
     17 #include "ui/gfx/size.h"
     18 #include "ui/gfx/skia_util.h"
     19 
     20 namespace gfx {
     21 namespace {
     22 
     23 // static
     24 gfx::ImageSkiaRep& NullImageRep() {
     25   CR_DEFINE_STATIC_LOCAL(ImageSkiaRep, null_image_rep, ());
     26   return null_image_rep;
     27 }
     28 
     29 std::vector<float>* g_supported_scales = NULL;
     30 }  // namespace
     31 
     32 namespace internal {
     33 namespace {
     34 
     35 class Matcher {
     36  public:
     37   explicit Matcher(float scale) : scale_(scale) {
     38   }
     39 
     40   bool operator()(const ImageSkiaRep& rep) const {
     41     return rep.scale() == scale_;
     42   }
     43 
     44  private:
     45   float scale_;
     46 };
     47 
     48 }  // namespace
     49 
     50 // A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a
     51 // refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's
     52 // information. Having both |base::RefCountedThreadSafe| and
     53 // |base::NonThreadSafe| may sounds strange but necessary to turn
     54 // the 'thread-non-safe modifiable ImageSkiaStorage' into
     55 // the 'thread-safe read-only ImageSkiaStorage'.
     56 class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>,
     57                          public base::NonThreadSafe {
     58  public:
     59   ImageSkiaStorage(ImageSkiaSource* source, const gfx::Size& size)
     60       : source_(source),
     61         size_(size),
     62         read_only_(false) {
     63   }
     64 
     65   ImageSkiaStorage(ImageSkiaSource* source, float scale)
     66       : source_(source),
     67         read_only_(false) {
     68     ImageSkia::ImageSkiaReps::iterator it = FindRepresentation(scale, true);
     69     if (it == image_reps_.end() || it->is_null())
     70       source_.reset();
     71     else
     72       size_.SetSize(it->GetWidth(), it->GetHeight());
     73   }
     74 
     75   bool has_source() const { return source_.get() != NULL; }
     76 
     77   std::vector<gfx::ImageSkiaRep>& image_reps() { return image_reps_; }
     78 
     79   const gfx::Size& size() const { return size_; }
     80 
     81   bool read_only() const { return read_only_; }
     82 
     83   void DeleteSource() {
     84     source_.reset();
     85   }
     86 
     87   void SetReadOnly() {
     88     read_only_ = true;
     89   }
     90 
     91   void DetachFromThread() {
     92     base::NonThreadSafe::DetachFromThread();
     93   }
     94 
     95   // Checks if the current thread can safely modify the storage.
     96   bool CanModify() const {
     97     return !read_only_ && CalledOnValidThread();
     98   }
     99 
    100   // Checks if the current thread can safely read the storage.
    101   bool CanRead() const {
    102     return (read_only_ && !source_.get()) || CalledOnValidThread();
    103   }
    104 
    105   // Returns the iterator of the image rep whose density best matches
    106   // |scale|. If the image for the |scale| doesn't exist in the storage and
    107   // |storage| is set, it fetches new image by calling
    108   // |ImageSkiaSource::GetImageForScale|. If the source returns the image with
    109   // different scale (if the image doesn't exist in resource, for example), it
    110   // will fallback to closest image rep.
    111   std::vector<ImageSkiaRep>::iterator FindRepresentation(
    112       float scale, bool fetch_new_image) const {
    113     ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this);
    114 
    115     ImageSkia::ImageSkiaReps::iterator closest_iter =
    116         non_const->image_reps().end();
    117     ImageSkia::ImageSkiaReps::iterator exact_iter =
    118         non_const->image_reps().end();
    119     float smallest_diff = std::numeric_limits<float>::max();
    120     for (ImageSkia::ImageSkiaReps::iterator it =
    121              non_const->image_reps().begin();
    122          it < image_reps_.end(); ++it) {
    123       if (it->scale() == scale) {
    124         // found exact match
    125         fetch_new_image = false;
    126         if (it->is_null())
    127           continue;
    128         exact_iter = it;
    129         break;
    130       }
    131       float diff = std::abs(it->scale() - scale);
    132       if (diff < smallest_diff && !it->is_null()) {
    133         closest_iter = it;
    134         smallest_diff = diff;
    135       }
    136     }
    137 
    138     if (fetch_new_image && source_.get()) {
    139       DCHECK(CalledOnValidThread()) <<
    140           "An ImageSkia with the source must be accessed by the same thread.";
    141 
    142       ImageSkiaRep image = source_->GetImageForScale(scale);
    143 
    144       // If the source returned the new image, store it.
    145       if (!image.is_null() &&
    146           std::find_if(image_reps_.begin(), image_reps_.end(),
    147                        Matcher(image.scale())) == image_reps_.end()) {
    148         non_const->image_reps().push_back(image);
    149       }
    150 
    151       // If the result image's scale isn't same as the expected scale, create
    152       // null ImageSkiaRep with the |scale| so that the next lookup will
    153       // fallback to the closest scale.
    154       if (image.is_null() || image.scale() != scale) {
    155         non_const->image_reps().push_back(ImageSkiaRep(SkBitmap(), scale));
    156       }
    157 
    158       // image_reps_ must have the exact much now, so find again.
    159       return FindRepresentation(scale, false);
    160     }
    161     return exact_iter != image_reps_.end() ? exact_iter : closest_iter;
    162   }
    163 
    164  private:
    165   virtual ~ImageSkiaStorage() {
    166     // We only care if the storage is modified by the same thread.
    167     // Don't blow up even if someone else deleted the ImageSkia.
    168     DetachFromThread();
    169   }
    170 
    171   // Vector of bitmaps and their associated scale.
    172   std::vector<gfx::ImageSkiaRep> image_reps_;
    173 
    174   scoped_ptr<ImageSkiaSource> source_;
    175 
    176   // Size of the image in DIP.
    177   gfx::Size size_;
    178 
    179   bool read_only_;
    180 
    181   friend class base::RefCountedThreadSafe<ImageSkiaStorage>;
    182 };
    183 
    184 }  // internal
    185 
    186 ImageSkia::ImageSkia() : storage_(NULL) {
    187 }
    188 
    189 ImageSkia::ImageSkia(ImageSkiaSource* source, const gfx::Size& size)
    190     : storage_(new internal::ImageSkiaStorage(source, size)) {
    191   DCHECK(source);
    192   // No other thread has reference to this, so it's safe to detach the thread.
    193   DetachStorageFromThread();
    194 }
    195 
    196 ImageSkia::ImageSkia(ImageSkiaSource* source, float scale)
    197     : storage_(new internal::ImageSkiaStorage(source, scale)) {
    198   DCHECK(source);
    199   if (!storage_->has_source())
    200     storage_ = NULL;
    201   // No other thread has reference to this, so it's safe to detach the thread.
    202   DetachStorageFromThread();
    203 }
    204 
    205 ImageSkia::ImageSkia(const ImageSkiaRep& image_rep) {
    206   Init(image_rep);
    207   // No other thread has reference to this, so it's safe to detach the thread.
    208   DetachStorageFromThread();
    209 }
    210 
    211 ImageSkia::ImageSkia(const ImageSkia& other) : storage_(other.storage_) {
    212 }
    213 
    214 ImageSkia& ImageSkia::operator=(const ImageSkia& other) {
    215   storage_ = other.storage_;
    216   return *this;
    217 }
    218 
    219 ImageSkia::~ImageSkia() {
    220 }
    221 
    222 // static
    223 void ImageSkia::SetSupportedScales(const std::vector<float>& supported_scales) {
    224   if (g_supported_scales != NULL)
    225     delete g_supported_scales;
    226   g_supported_scales = new std::vector<float>(supported_scales);
    227   std::sort(g_supported_scales->begin(), g_supported_scales->end());
    228 }
    229 
    230 // static
    231 const std::vector<float>& ImageSkia::GetSupportedScales() {
    232   DCHECK(g_supported_scales != NULL);
    233   return *g_supported_scales;
    234 }
    235 
    236 // static
    237 float ImageSkia::GetMaxSupportedScale() {
    238   return g_supported_scales->back();
    239 }
    240 
    241 // static
    242 ImageSkia ImageSkia::CreateFrom1xBitmap(const SkBitmap& bitmap) {
    243   return ImageSkia(ImageSkiaRep(bitmap, 1.0f));
    244 }
    245 
    246 scoped_ptr<ImageSkia> ImageSkia::DeepCopy() const {
    247   ImageSkia* copy = new ImageSkia;
    248   if (isNull())
    249     return scoped_ptr<ImageSkia>(copy);
    250 
    251   CHECK(CanRead());
    252 
    253   std::vector<gfx::ImageSkiaRep>& reps = storage_->image_reps();
    254   for (std::vector<gfx::ImageSkiaRep>::iterator iter = reps.begin();
    255        iter != reps.end(); ++iter) {
    256     copy->AddRepresentation(*iter);
    257   }
    258   // The copy has its own storage. Detach the copy from the current
    259   // thread so that other thread can use this.
    260   if (!copy->isNull())
    261     copy->storage_->DetachFromThread();
    262   return scoped_ptr<ImageSkia>(copy);
    263 }
    264 
    265 bool ImageSkia::BackedBySameObjectAs(const gfx::ImageSkia& other) const {
    266   return storage_.get() == other.storage_.get();
    267 }
    268 
    269 void ImageSkia::AddRepresentation(const ImageSkiaRep& image_rep) {
    270   DCHECK(!image_rep.is_null());
    271 
    272   // TODO(oshima): This method should be called |SetRepresentation|
    273   // and replace the existing rep if there is already one with the
    274   // same scale so that we can guarantee that a ImageSkia instance contains only
    275   // one image rep per scale. This is not possible now as ImageLoader currently
    276   // stores need this feature, but this needs to be fixed.
    277   if (isNull()) {
    278     Init(image_rep);
    279   } else {
    280     CHECK(CanModify());
    281     storage_->image_reps().push_back(image_rep);
    282   }
    283 }
    284 
    285 void ImageSkia::RemoveRepresentation(float scale) {
    286   if (isNull())
    287     return;
    288   CHECK(CanModify());
    289 
    290   ImageSkiaReps& image_reps = storage_->image_reps();
    291   ImageSkiaReps::iterator it =
    292       storage_->FindRepresentation(scale, false);
    293   if (it != image_reps.end() && it->scale() == scale)
    294     image_reps.erase(it);
    295 }
    296 
    297 bool ImageSkia::HasRepresentation(float scale) const {
    298   if (isNull())
    299     return false;
    300   CHECK(CanRead());
    301 
    302   ImageSkiaReps::iterator it = storage_->FindRepresentation(scale, false);
    303   return (it != storage_->image_reps().end() && it->scale() == scale);
    304 }
    305 
    306 const ImageSkiaRep& ImageSkia::GetRepresentation(float scale) const {
    307   if (isNull())
    308     return NullImageRep();
    309 
    310   CHECK(CanRead());
    311 
    312   ImageSkiaReps::iterator it = storage_->FindRepresentation(scale, true);
    313   if (it == storage_->image_reps().end())
    314     return NullImageRep();
    315 
    316   return *it;
    317 }
    318 
    319 void ImageSkia::SetReadOnly() {
    320   CHECK(storage_.get());
    321   storage_->SetReadOnly();
    322   DetachStorageFromThread();
    323 }
    324 
    325 void ImageSkia::MakeThreadSafe() {
    326   CHECK(storage_.get());
    327   EnsureRepsForSupportedScales();
    328   // Delete source as we no longer needs it.
    329   if (storage_.get())
    330     storage_->DeleteSource();
    331   storage_->SetReadOnly();
    332   CHECK(IsThreadSafe());
    333 }
    334 
    335 bool ImageSkia::IsThreadSafe() const {
    336   return !storage_.get() || (storage_->read_only() && !storage_->has_source());
    337 }
    338 
    339 int ImageSkia::width() const {
    340   return isNull() ? 0 : storage_->size().width();
    341 }
    342 
    343 gfx::Size ImageSkia::size() const {
    344   return gfx::Size(width(), height());
    345 }
    346 
    347 int ImageSkia::height() const {
    348   return isNull() ? 0 : storage_->size().height();
    349 }
    350 
    351 std::vector<ImageSkiaRep> ImageSkia::image_reps() const {
    352   if (isNull())
    353     return std::vector<ImageSkiaRep>();
    354 
    355   CHECK(CanRead());
    356 
    357   ImageSkiaReps internal_image_reps = storage_->image_reps();
    358   // Create list of image reps to return, skipping null image reps which were
    359   // added for caching purposes only.
    360   ImageSkiaReps image_reps;
    361   for (ImageSkiaReps::iterator it = internal_image_reps.begin();
    362        it != internal_image_reps.end(); ++it) {
    363     if (!it->is_null())
    364       image_reps.push_back(*it);
    365   }
    366 
    367   return image_reps;
    368 }
    369 
    370 void ImageSkia::EnsureRepsForSupportedScales() const {
    371   DCHECK(g_supported_scales != NULL);
    372   // Don't check ReadOnly because the source may generate images
    373   // even for read only ImageSkia. Concurrent access will be protected
    374   // by |DCHECK(CalledOnValidThread())| in FindRepresentation.
    375   if (storage_.get() && storage_->has_source()) {
    376     for (std::vector<float>::const_iterator it = g_supported_scales->begin();
    377          it != g_supported_scales->end(); ++it)
    378       storage_->FindRepresentation(*it, true);
    379   }
    380 }
    381 
    382 void ImageSkia::Init(const ImageSkiaRep& image_rep) {
    383   // TODO(pkotwicz): The image should be null whenever image rep is null.
    384   if (image_rep.sk_bitmap().empty()) {
    385     storage_ = NULL;
    386     return;
    387   }
    388   storage_ = new internal::ImageSkiaStorage(
    389       NULL, gfx::Size(image_rep.GetWidth(), image_rep.GetHeight()));
    390   storage_->image_reps().push_back(image_rep);
    391 }
    392 
    393 SkBitmap& ImageSkia::GetBitmap() const {
    394   if (isNull()) {
    395     // Callers expect a ImageSkiaRep even if it is |isNull()|.
    396     // TODO(pkotwicz): Fix this.
    397     return NullImageRep().mutable_sk_bitmap();
    398   }
    399 
    400   // TODO(oshima): This made a few tests flaky on Windows.
    401   // Fix the root cause and re-enable this. crbug.com/145623.
    402 #if !defined(OS_WIN)
    403   CHECK(CanRead());
    404 #endif
    405 
    406   ImageSkiaReps::iterator it = storage_->FindRepresentation(1.0f, true);
    407   if (it != storage_->image_reps().end())
    408     return it->mutable_sk_bitmap();
    409   return NullImageRep().mutable_sk_bitmap();
    410 }
    411 
    412 bool ImageSkia::CanRead() const {
    413   return !storage_.get() || storage_->CanRead();
    414 }
    415 
    416 bool ImageSkia::CanModify() const {
    417   return !storage_.get() || storage_->CanModify();
    418 }
    419 
    420 void ImageSkia::DetachStorageFromThread() {
    421   if (storage_.get())
    422     storage_->DetachFromThread();
    423 }
    424 
    425 }  // namespace gfx
    426