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 "base/logging.h"
      8 #include "base/threading/simple_thread.h"
      9 #include "testing/gtest/include/gtest/gtest.h"
     10 #include "third_party/skia/include/core/SkBitmap.h"
     11 #include "ui/gfx/image/image_skia_rep.h"
     12 #include "ui/gfx/image/image_skia_source.h"
     13 #include "ui/gfx/size.h"
     14 
     15 // Duplicated from base/threading/non_thread_safe.h so that we can be
     16 // good citizens there and undef the macro.
     17 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
     18 #define ENABLE_NON_THREAD_SAFE 1
     19 #else
     20 #define ENABLE_NON_THREAD_SAFE 0
     21 #endif
     22 
     23 namespace gfx {
     24 
     25 namespace {
     26 
     27 class FixedSource : public ImageSkiaSource {
     28  public:
     29   FixedSource(const ImageSkiaRep& image) : image_(image) {}
     30 
     31   virtual ~FixedSource() {
     32   }
     33 
     34   virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
     35     return image_;
     36   }
     37 
     38  private:
     39   ImageSkiaRep image_;
     40 
     41   DISALLOW_COPY_AND_ASSIGN(FixedSource);
     42 };
     43 
     44 class DynamicSource : public ImageSkiaSource {
     45  public:
     46   DynamicSource(const gfx::Size& size) : size_(size) {}
     47 
     48   virtual ~DynamicSource() {
     49   }
     50 
     51   virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
     52     return gfx::ImageSkiaRep(size_, scale);
     53   }
     54 
     55  private:
     56   gfx::Size size_;
     57 
     58   DISALLOW_COPY_AND_ASSIGN(DynamicSource);
     59 };
     60 
     61 class NullSource: public ImageSkiaSource {
     62  public:
     63   NullSource() {
     64   }
     65 
     66   virtual ~NullSource() {
     67   }
     68 
     69   virtual ImageSkiaRep GetImageForScale(float scale) OVERRIDE {
     70     return gfx::ImageSkiaRep();
     71   }
     72 
     73  private:
     74   DISALLOW_COPY_AND_ASSIGN(NullSource);
     75 };
     76 
     77 }  // namespace
     78 
     79 namespace test {
     80 class TestOnThread : public base::SimpleThread {
     81  public:
     82   explicit TestOnThread(ImageSkia* image_skia)
     83       : SimpleThread("image_skia_on_thread"),
     84         image_skia_(image_skia),
     85         can_read_(false),
     86         can_modify_(false) {
     87   }
     88 
     89   virtual void Run() OVERRIDE {
     90     can_read_ = image_skia_->CanRead();
     91     can_modify_ = image_skia_->CanModify();
     92     if (can_read_)
     93       image_skia_->image_reps();
     94   }
     95 
     96   void StartAndJoin() {
     97     Start();
     98     Join();
     99   }
    100 
    101   bool can_read() const { return can_read_; }
    102 
    103   bool can_modify() const { return can_modify_; }
    104 
    105  private:
    106   ImageSkia* image_skia_;
    107 
    108   bool can_read_;
    109   bool can_modify_;
    110 
    111   DISALLOW_COPY_AND_ASSIGN(TestOnThread);
    112 };
    113 
    114 }  // namespace test
    115 
    116 TEST(ImageSkiaTest, FixedSource) {
    117   ImageSkiaRep image(Size(100, 200), 1.0f);
    118   ImageSkia image_skia(new FixedSource(image), Size(100, 200));
    119   EXPECT_EQ(0U, image_skia.image_reps().size());
    120 
    121   const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f);
    122   EXPECT_EQ(100, result_100p.GetWidth());
    123   EXPECT_EQ(200, result_100p.GetHeight());
    124   EXPECT_EQ(1.0f, result_100p.scale());
    125   EXPECT_EQ(1U, image_skia.image_reps().size());
    126 
    127   const ImageSkiaRep& result_200p = image_skia.GetRepresentation(2.0f);
    128 
    129   EXPECT_EQ(100, result_200p.GetWidth());
    130   EXPECT_EQ(200, result_200p.GetHeight());
    131   EXPECT_EQ(100, result_200p.pixel_width());
    132   EXPECT_EQ(200, result_200p.pixel_height());
    133   EXPECT_EQ(1.0f, result_200p.scale());
    134   EXPECT_EQ(1U, image_skia.image_reps().size());
    135 
    136   // Get the representation again and make sure it doesn't
    137   // generate new image skia rep.
    138   image_skia.GetRepresentation(1.0f);
    139   image_skia.GetRepresentation(2.0f);
    140   EXPECT_EQ(1U, image_skia.image_reps().size());
    141 }
    142 
    143 TEST(ImageSkiaTest, DynamicSource) {
    144   ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200));
    145   EXPECT_EQ(0U, image_skia.image_reps().size());
    146   const ImageSkiaRep& result_100p = image_skia.GetRepresentation(1.0f);
    147   EXPECT_EQ(100, result_100p.GetWidth());
    148   EXPECT_EQ(200, result_100p.GetHeight());
    149   EXPECT_EQ(1.0f, result_100p.scale());
    150   EXPECT_EQ(1U, image_skia.image_reps().size());
    151 
    152   const ImageSkiaRep& result_200p =
    153       image_skia.GetRepresentation(2.0f);
    154   EXPECT_EQ(100, result_200p.GetWidth());
    155   EXPECT_EQ(200, result_200p.GetHeight());
    156   EXPECT_EQ(200, result_200p.pixel_width());
    157   EXPECT_EQ(400, result_200p.pixel_height());
    158   EXPECT_EQ(2.0f, result_200p.scale());
    159   EXPECT_EQ(2U, image_skia.image_reps().size());
    160 
    161   // Get the representation again and make sure it doesn't
    162   // generate new image skia rep.
    163   image_skia.GetRepresentation(1.0f);
    164   EXPECT_EQ(2U, image_skia.image_reps().size());
    165   image_skia.GetRepresentation(2.0f);
    166   EXPECT_EQ(2U, image_skia.image_reps().size());
    167 }
    168 
    169 // Tests that image_reps returns all of the representations in the
    170 // image when there are multiple representations for a scale factor.
    171 // This currently is the case with ImageLoader::LoadImages.
    172 TEST(ImageSkiaTest, ManyRepsPerScaleFactor) {
    173   const int kSmallIcon1x = 16;
    174   const int kSmallIcon2x = 32;
    175   const int kLargeIcon1x = 32;
    176 
    177   ImageSkia image(new NullSource(), gfx::Size(kSmallIcon1x, kSmallIcon1x));
    178   // Simulate a source which loads images on a delay. Upon
    179   // GetImageForScaleFactor, it immediately returns null and starts loading
    180   // image reps slowly.
    181   image.GetRepresentation(1.0f);
    182   image.GetRepresentation(2.0f);
    183 
    184   // After a lengthy amount of simulated time, finally loaded image reps.
    185   image.AddRepresentation(ImageSkiaRep(
    186       gfx::Size(kSmallIcon1x, kSmallIcon1x), 1.0f));
    187   image.AddRepresentation(ImageSkiaRep(
    188       gfx::Size(kSmallIcon2x, kSmallIcon2x), 2.0f));
    189   image.AddRepresentation(ImageSkiaRep(
    190       gfx::Size(kLargeIcon1x, kLargeIcon1x), 1.0f));
    191 
    192   std::vector<ImageSkiaRep> image_reps = image.image_reps();
    193   EXPECT_EQ(3u, image_reps.size());
    194 
    195   int num_1x = 0;
    196   int num_2x = 0;
    197   for (size_t i = 0; i < image_reps.size(); ++i) {
    198     if (image_reps[i].scale() == 1.0f)
    199       num_1x++;
    200     else if (image_reps[i].scale() == 2.0f)
    201       num_2x++;
    202   }
    203   EXPECT_EQ(2, num_1x);
    204   EXPECT_EQ(1, num_2x);
    205 }
    206 
    207 TEST(ImageSkiaTest, GetBitmap) {
    208   ImageSkia image_skia(new DynamicSource(Size(100, 200)), Size(100, 200));
    209   const SkBitmap* bitmap = image_skia.bitmap();
    210   EXPECT_NE(static_cast<SkBitmap*>(NULL), bitmap);
    211   EXPECT_FALSE(bitmap->isNull());
    212 }
    213 
    214 TEST(ImageSkiaTest, GetBitmapFromEmpty) {
    215   // Create an image with 1 representation and remove it so the ImageSkiaStorage
    216   // is left with no representations.
    217   ImageSkia empty_image(ImageSkiaRep(Size(100, 200), 1.0f));
    218   ImageSkia empty_image_copy(empty_image);
    219   empty_image.RemoveRepresentation(1.0f);
    220 
    221   // Check that ImageSkia::bitmap() still returns a valid SkBitmap pointer for
    222   // the image and all its copies.
    223   const SkBitmap* bitmap = empty_image_copy.bitmap();
    224   ASSERT_NE(static_cast<SkBitmap*>(NULL), bitmap);
    225   EXPECT_TRUE(bitmap->isNull());
    226   EXPECT_TRUE(bitmap->empty());
    227 }
    228 
    229 TEST(ImageSkiaTest, BackedBySameObjectAs) {
    230   // Null images should all be backed by the same object (NULL).
    231   ImageSkia image;
    232   ImageSkia unrelated;
    233   EXPECT_TRUE(image.BackedBySameObjectAs(unrelated));
    234 
    235   image.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
    236                                             1.0f));
    237   ImageSkia copy = image;
    238   copy.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
    239                                            2.0f));
    240   unrelated.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
    241                                                 1.0f));
    242   EXPECT_TRUE(image.BackedBySameObjectAs(copy));
    243   EXPECT_FALSE(image.BackedBySameObjectAs(unrelated));
    244   EXPECT_FALSE(copy.BackedBySameObjectAs(unrelated));
    245 }
    246 
    247 #if ENABLE_NON_THREAD_SAFE
    248 TEST(ImageSkiaTest, EmptyOnThreadTest) {
    249   ImageSkia empty;
    250   test::TestOnThread empty_on_thread(&empty);
    251   empty_on_thread.Start();
    252   empty_on_thread.Join();
    253   EXPECT_TRUE(empty_on_thread.can_read());
    254   EXPECT_TRUE(empty_on_thread.can_modify());
    255 }
    256 
    257 TEST(ImageSkiaTest, StaticOnThreadTest) {
    258   ImageSkia image(ImageSkiaRep(Size(100, 200), 1.0f));
    259   EXPECT_FALSE(image.IsThreadSafe());
    260 
    261   test::TestOnThread image_on_thread(&image);
    262   // an image that was never accessed on this thread can be
    263   // read by other thread.
    264   image_on_thread.StartAndJoin();
    265   EXPECT_TRUE(image_on_thread.can_read());
    266   EXPECT_TRUE(image_on_thread.can_modify());
    267   EXPECT_FALSE(image.CanRead());
    268   EXPECT_FALSE(image.CanModify());
    269 
    270   image.DetachStorageFromThread();
    271   // An image is accessed by this thread,
    272   // so other thread cannot read/modify it.
    273   image.image_reps();
    274   test::TestOnThread image_on_thread2(&image);
    275   image_on_thread2.StartAndJoin();
    276   EXPECT_FALSE(image_on_thread2.can_read());
    277   EXPECT_FALSE(image_on_thread2.can_modify());
    278   EXPECT_TRUE(image.CanRead());
    279   EXPECT_TRUE(image.CanModify());
    280 
    281   image.DetachStorageFromThread();
    282   scoped_ptr<ImageSkia> deep_copy(image.DeepCopy());
    283   EXPECT_FALSE(deep_copy->IsThreadSafe());
    284   test::TestOnThread deepcopy_on_thread(deep_copy.get());
    285   deepcopy_on_thread.StartAndJoin();
    286   EXPECT_TRUE(deepcopy_on_thread.can_read());
    287   EXPECT_TRUE(deepcopy_on_thread.can_modify());
    288   EXPECT_FALSE(deep_copy->CanRead());
    289   EXPECT_FALSE(deep_copy->CanModify());
    290 
    291   scoped_ptr<ImageSkia> deep_copy2(image.DeepCopy());
    292   EXPECT_EQ(1U, deep_copy2->image_reps().size());
    293   // Access it from current thread so that it can't be
    294   // accessed from another thread.
    295   deep_copy2->image_reps();
    296   EXPECT_FALSE(deep_copy2->IsThreadSafe());
    297   test::TestOnThread deepcopy2_on_thread(deep_copy2.get());
    298   deepcopy2_on_thread.StartAndJoin();
    299   EXPECT_FALSE(deepcopy2_on_thread.can_read());
    300   EXPECT_FALSE(deepcopy2_on_thread.can_modify());
    301   EXPECT_TRUE(deep_copy2->CanRead());
    302   EXPECT_TRUE(deep_copy2->CanModify());
    303 
    304   image.DetachStorageFromThread();
    305   image.SetReadOnly();
    306   // A read-only ImageSkia with no source is thread safe.
    307   EXPECT_TRUE(image.IsThreadSafe());
    308   test::TestOnThread readonly_on_thread(&image);
    309   readonly_on_thread.StartAndJoin();
    310   EXPECT_TRUE(readonly_on_thread.can_read());
    311   EXPECT_FALSE(readonly_on_thread.can_modify());
    312   EXPECT_TRUE(image.CanRead());
    313   EXPECT_FALSE(image.CanModify());
    314 
    315   image.DetachStorageFromThread();
    316   image.MakeThreadSafe();
    317   EXPECT_TRUE(image.IsThreadSafe());
    318   test::TestOnThread threadsafe_on_thread(&image);
    319   threadsafe_on_thread.StartAndJoin();
    320   EXPECT_TRUE(threadsafe_on_thread.can_read());
    321   EXPECT_FALSE(threadsafe_on_thread.can_modify());
    322   EXPECT_TRUE(image.CanRead());
    323   EXPECT_FALSE(image.CanModify());
    324 }
    325 
    326 TEST(ImageSkiaTest, SourceOnThreadTest) {
    327   ImageSkia image(new DynamicSource(Size(100, 200)), Size(100, 200));
    328   EXPECT_FALSE(image.IsThreadSafe());
    329 
    330   test::TestOnThread image_on_thread(&image);
    331   image_on_thread.StartAndJoin();
    332   // an image that was never accessed on this thread can be
    333   // read by other thread.
    334   EXPECT_TRUE(image_on_thread.can_read());
    335   EXPECT_TRUE(image_on_thread.can_modify());
    336   EXPECT_FALSE(image.CanRead());
    337   EXPECT_FALSE(image.CanModify());
    338 
    339   image.DetachStorageFromThread();
    340   // An image is accessed by this thread,
    341   // so other thread cannot read/modify it.
    342   image.image_reps();
    343   test::TestOnThread image_on_thread2(&image);
    344   image_on_thread2.StartAndJoin();
    345   EXPECT_FALSE(image_on_thread2.can_read());
    346   EXPECT_FALSE(image_on_thread2.can_modify());
    347   EXPECT_TRUE(image.CanRead());
    348   EXPECT_TRUE(image.CanModify());
    349 
    350   image.DetachStorageFromThread();
    351   image.SetReadOnly();
    352   EXPECT_FALSE(image.IsThreadSafe());
    353   test::TestOnThread readonly_on_thread(&image);
    354   readonly_on_thread.StartAndJoin();
    355   EXPECT_TRUE(readonly_on_thread.can_read());
    356   EXPECT_FALSE(readonly_on_thread.can_modify());
    357   EXPECT_FALSE(image.CanRead());
    358   EXPECT_FALSE(image.CanModify());
    359 
    360   image.DetachStorageFromThread();
    361   image.MakeThreadSafe();
    362   EXPECT_TRUE(image.IsThreadSafe());
    363   // Check if image reps are generated for supported scale factors.
    364   EXPECT_EQ(ImageSkia::GetSupportedScales().size(),
    365            image.image_reps().size());
    366   test::TestOnThread threadsafe_on_thread(&image);
    367   threadsafe_on_thread.StartAndJoin();
    368   EXPECT_TRUE(threadsafe_on_thread.can_read());
    369   EXPECT_FALSE(threadsafe_on_thread.can_modify());
    370   EXPECT_TRUE(image.CanRead());
    371   EXPECT_FALSE(image.CanModify());
    372 }
    373 #endif  // ENABLE_NON_THREAD_SAFE
    374 
    375 // Just in case we ever get lumped together with other compilation units.
    376 #undef ENABLE_NON_THREAD_SAFE
    377 
    378 }  // namespace gfx
    379