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