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