• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
FixedSource(const ImageSkiaRep & image)29   FixedSource(const ImageSkiaRep& image) : image_(image) {}
30 
~FixedSource()31   virtual ~FixedSource() {
32   }
33 
GetImageForScale(float scale)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:
DynamicSource(const gfx::Size & size)46   DynamicSource(const gfx::Size& size) : size_(size) {}
47 
~DynamicSource()48   virtual ~DynamicSource() {
49   }
50 
GetImageForScale(float scale)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:
NullSource()63   NullSource() {
64   }
65 
~NullSource()66   virtual ~NullSource() {
67   }
68 
GetImageForScale(float scale)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:
TestOnThread(ImageSkia * image_skia)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 
Run()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 
StartAndJoin()96   void StartAndJoin() {
97     Start();
98     Join();
99   }
100 
can_read() const101   bool can_read() const { return can_read_; }
102 
can_modify() const103   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 
TEST(ImageSkiaTest,FixedSource)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 
TEST(ImageSkiaTest,DynamicSource)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.
TEST(ImageSkiaTest,ManyRepsPerScaleFactor)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 
TEST(ImageSkiaTest,GetBitmap)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 
TEST(ImageSkiaTest,GetBitmapFromEmpty)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 
TEST(ImageSkiaTest,BackedBySameObjectAs)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
TEST(ImageSkiaTest,EmptyOnThreadTest)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 
TEST(ImageSkiaTest,StaticOnThreadTest)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 
TEST(ImageSkiaTest,SourceOnThreadTest)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