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.
5 #include "ui/gfx/image/image_skia.h"
7 #include "base/command_line.h"
8 #include "base/logging.h"
9 #include "base/threading/simple_thread.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/skia/include/core/SkBitmap.h"
12 #include "ui/gfx/image/image_skia_rep.h"
13 #include "ui/gfx/image/image_skia_source.h"
14 #include "ui/gfx/size.h"
15 #include "ui/gfx/switches.h"
17 // Duplicated from base/threading/non_thread_safe.h so that we can be
18 // good citizens there and undef the macro.
19 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
20 #define ENABLE_NON_THREAD_SAFE 1
22 #define ENABLE_NON_THREAD_SAFE 0
29 class FixedSource
: public ImageSkiaSource
{
31 FixedSource(const ImageSkiaRep
& image
) : image_(image
) {}
33 virtual ~FixedSource() {
36 virtual ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
43 DISALLOW_COPY_AND_ASSIGN(FixedSource
);
46 class DynamicSource
: public ImageSkiaSource
{
48 DynamicSource(const gfx::Size
& size
)
50 last_requested_scale_(0.0f
) {}
52 virtual ~DynamicSource() {
55 virtual ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
56 last_requested_scale_
= scale
;
57 return gfx::ImageSkiaRep(size_
, scale
);
60 float GetLastRequestedScaleAndReset() {
61 float result
= last_requested_scale_
;
62 last_requested_scale_
= 0.0f
;
68 float last_requested_scale_
;
70 DISALLOW_COPY_AND_ASSIGN(DynamicSource
);
73 class NullSource
: public ImageSkiaSource
{
78 virtual ~NullSource() {
81 virtual ImageSkiaRep
GetImageForScale(float scale
) OVERRIDE
{
82 return gfx::ImageSkiaRep();
86 DISALLOW_COPY_AND_ASSIGN(NullSource
);
92 class TestOnThread
: public base::SimpleThread
{
94 explicit TestOnThread(ImageSkia
* image_skia
)
95 : SimpleThread("image_skia_on_thread"),
96 image_skia_(image_skia
),
101 virtual void Run() OVERRIDE
{
102 can_read_
= image_skia_
->CanRead();
103 can_modify_
= image_skia_
->CanModify();
105 image_skia_
->image_reps();
108 void StartAndJoin() {
113 bool can_read() const { return can_read_
; }
115 bool can_modify() const { return can_modify_
; }
118 ImageSkia
* image_skia_
;
123 DISALLOW_COPY_AND_ASSIGN(TestOnThread
);
128 class ImageSkiaTest
: public testing::Test
{
131 // In the test, we assume that we support 1.0f and 2.0f DSFs.
132 old_scales_
= ImageSkia::GetSupportedScales();
134 // Sets the list of scale factors supported by resource bundle.
135 std::vector
<float> supported_scales
;
136 supported_scales
.push_back(1.0f
);
137 supported_scales
.push_back(2.0f
);
138 ImageSkia::SetSupportedScales(supported_scales
);
140 virtual ~ImageSkiaTest() {
141 ImageSkia::SetSupportedScales(old_scales_
);
145 std::vector
<float> old_scales_
;
146 DISALLOW_COPY_AND_ASSIGN(ImageSkiaTest
);
149 TEST_F(ImageSkiaTest
, FixedSource
) {
150 ImageSkiaRep
image(Size(100, 200), 1.0f
);
151 ImageSkia
image_skia(new FixedSource(image
), Size(100, 200));
152 EXPECT_EQ(0U, image_skia
.image_reps().size());
154 const ImageSkiaRep
& result_100p
= image_skia
.GetRepresentation(1.0f
);
155 EXPECT_EQ(100, result_100p
.GetWidth());
156 EXPECT_EQ(200, result_100p
.GetHeight());
157 EXPECT_EQ(1.0f
, result_100p
.scale());
158 EXPECT_EQ(1U, image_skia
.image_reps().size());
160 const ImageSkiaRep
& result_200p
= image_skia
.GetRepresentation(2.0f
);
162 EXPECT_EQ(100, result_200p
.GetWidth());
163 EXPECT_EQ(200, result_200p
.GetHeight());
164 EXPECT_EQ(100, result_200p
.pixel_width());
165 EXPECT_EQ(200, result_200p
.pixel_height());
166 EXPECT_EQ(1.0f
, result_200p
.scale());
167 EXPECT_EQ(1U, image_skia
.image_reps().size());
169 // Get the representation again and make sure it doesn't
170 // generate new image skia rep.
171 image_skia
.GetRepresentation(1.0f
);
172 image_skia
.GetRepresentation(2.0f
);
173 EXPECT_EQ(1U, image_skia
.image_reps().size());
176 TEST_F(ImageSkiaTest
, DynamicSource
) {
177 ImageSkia
image_skia(new DynamicSource(Size(100, 200)), Size(100, 200));
178 EXPECT_EQ(0U, image_skia
.image_reps().size());
179 const ImageSkiaRep
& result_100p
= image_skia
.GetRepresentation(1.0f
);
180 EXPECT_EQ(100, result_100p
.GetWidth());
181 EXPECT_EQ(200, result_100p
.GetHeight());
182 EXPECT_EQ(1.0f
, result_100p
.scale());
183 EXPECT_EQ(1U, image_skia
.image_reps().size());
185 const ImageSkiaRep
& result_200p
=
186 image_skia
.GetRepresentation(2.0f
);
187 EXPECT_EQ(100, result_200p
.GetWidth());
188 EXPECT_EQ(200, result_200p
.GetHeight());
189 EXPECT_EQ(200, result_200p
.pixel_width());
190 EXPECT_EQ(400, result_200p
.pixel_height());
191 EXPECT_EQ(2.0f
, result_200p
.scale());
192 EXPECT_EQ(2U, image_skia
.image_reps().size());
194 // Get the representation again and make sure it doesn't
195 // generate new image skia rep.
196 image_skia
.GetRepresentation(1.0f
);
197 EXPECT_EQ(2U, image_skia
.image_reps().size());
198 image_skia
.GetRepresentation(2.0f
);
199 EXPECT_EQ(2U, image_skia
.image_reps().size());
202 // Tests that image_reps returns all of the representations in the
203 // image when there are multiple representations for a scale factor.
204 // This currently is the case with ImageLoader::LoadImages.
205 TEST_F(ImageSkiaTest
, ManyRepsPerScaleFactor
) {
206 const int kSmallIcon1x
= 16;
207 const int kSmallIcon2x
= 32;
208 const int kLargeIcon1x
= 32;
210 ImageSkia
image(new NullSource(), gfx::Size(kSmallIcon1x
, kSmallIcon1x
));
211 // Simulate a source which loads images on a delay. Upon
212 // GetImageForScaleFactor, it immediately returns null and starts loading
213 // image reps slowly.
214 image
.GetRepresentation(1.0f
);
215 image
.GetRepresentation(2.0f
);
217 // After a lengthy amount of simulated time, finally loaded image reps.
218 image
.AddRepresentation(ImageSkiaRep(
219 gfx::Size(kSmallIcon1x
, kSmallIcon1x
), 1.0f
));
220 image
.AddRepresentation(ImageSkiaRep(
221 gfx::Size(kSmallIcon2x
, kSmallIcon2x
), 2.0f
));
222 image
.AddRepresentation(ImageSkiaRep(
223 gfx::Size(kLargeIcon1x
, kLargeIcon1x
), 1.0f
));
225 std::vector
<ImageSkiaRep
> image_reps
= image
.image_reps();
226 EXPECT_EQ(3u, image_reps
.size());
230 for (size_t i
= 0; i
< image_reps
.size(); ++i
) {
231 if (image_reps
[i
].scale() == 1.0f
)
233 else if (image_reps
[i
].scale() == 2.0f
)
236 EXPECT_EQ(2, num_1x
);
237 EXPECT_EQ(1, num_2x
);
240 TEST_F(ImageSkiaTest
, GetBitmap
) {
241 ImageSkia
image_skia(new DynamicSource(Size(100, 200)), Size(100, 200));
242 const SkBitmap
* bitmap
= image_skia
.bitmap();
243 EXPECT_NE(static_cast<SkBitmap
*>(NULL
), bitmap
);
244 EXPECT_FALSE(bitmap
->isNull());
247 TEST_F(ImageSkiaTest
, GetBitmapFromEmpty
) {
248 // Create an image with 1 representation and remove it so the ImageSkiaStorage
249 // is left with no representations.
250 ImageSkia
empty_image(ImageSkiaRep(Size(100, 200), 1.0f
));
251 ImageSkia
empty_image_copy(empty_image
);
252 empty_image
.RemoveRepresentation(1.0f
);
254 // Check that ImageSkia::bitmap() still returns a valid SkBitmap pointer for
255 // the image and all its copies.
256 const SkBitmap
* bitmap
= empty_image_copy
.bitmap();
257 ASSERT_NE(static_cast<SkBitmap
*>(NULL
), bitmap
);
258 EXPECT_TRUE(bitmap
->isNull());
259 EXPECT_TRUE(bitmap
->empty());
262 TEST_F(ImageSkiaTest
, BackedBySameObjectAs
) {
263 // Null images should all be backed by the same object (NULL).
266 EXPECT_TRUE(image
.BackedBySameObjectAs(unrelated
));
268 image
.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
270 ImageSkia copy
= image
;
271 copy
.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
273 unrelated
.AddRepresentation(gfx::ImageSkiaRep(gfx::Size(10, 10),
275 EXPECT_TRUE(image
.BackedBySameObjectAs(copy
));
276 EXPECT_FALSE(image
.BackedBySameObjectAs(unrelated
));
277 EXPECT_FALSE(copy
.BackedBySameObjectAs(unrelated
));
280 #if ENABLE_NON_THREAD_SAFE
281 TEST_F(ImageSkiaTest
, EmptyOnThreadTest
) {
283 test::TestOnThread
empty_on_thread(&empty
);
284 empty_on_thread
.Start();
285 empty_on_thread
.Join();
286 EXPECT_TRUE(empty_on_thread
.can_read());
287 EXPECT_TRUE(empty_on_thread
.can_modify());
290 TEST_F(ImageSkiaTest
, StaticOnThreadTest
) {
291 ImageSkia
image(ImageSkiaRep(Size(100, 200), 1.0f
));
292 EXPECT_FALSE(image
.IsThreadSafe());
294 test::TestOnThread
image_on_thread(&image
);
295 // an image that was never accessed on this thread can be
296 // read by other thread.
297 image_on_thread
.StartAndJoin();
298 EXPECT_TRUE(image_on_thread
.can_read());
299 EXPECT_TRUE(image_on_thread
.can_modify());
300 EXPECT_FALSE(image
.CanRead());
301 EXPECT_FALSE(image
.CanModify());
303 image
.DetachStorageFromThread();
304 // An image is accessed by this thread,
305 // so other thread cannot read/modify it.
307 test::TestOnThread
image_on_thread2(&image
);
308 image_on_thread2
.StartAndJoin();
309 EXPECT_FALSE(image_on_thread2
.can_read());
310 EXPECT_FALSE(image_on_thread2
.can_modify());
311 EXPECT_TRUE(image
.CanRead());
312 EXPECT_TRUE(image
.CanModify());
314 image
.DetachStorageFromThread();
315 scoped_ptr
<ImageSkia
> deep_copy(image
.DeepCopy());
316 EXPECT_FALSE(deep_copy
->IsThreadSafe());
317 test::TestOnThread
deepcopy_on_thread(deep_copy
.get());
318 deepcopy_on_thread
.StartAndJoin();
319 EXPECT_TRUE(deepcopy_on_thread
.can_read());
320 EXPECT_TRUE(deepcopy_on_thread
.can_modify());
321 EXPECT_FALSE(deep_copy
->CanRead());
322 EXPECT_FALSE(deep_copy
->CanModify());
324 scoped_ptr
<ImageSkia
> deep_copy2(image
.DeepCopy());
325 EXPECT_EQ(1U, deep_copy2
->image_reps().size());
326 // Access it from current thread so that it can't be
327 // accessed from another thread.
328 deep_copy2
->image_reps();
329 EXPECT_FALSE(deep_copy2
->IsThreadSafe());
330 test::TestOnThread
deepcopy2_on_thread(deep_copy2
.get());
331 deepcopy2_on_thread
.StartAndJoin();
332 EXPECT_FALSE(deepcopy2_on_thread
.can_read());
333 EXPECT_FALSE(deepcopy2_on_thread
.can_modify());
334 EXPECT_TRUE(deep_copy2
->CanRead());
335 EXPECT_TRUE(deep_copy2
->CanModify());
337 image
.DetachStorageFromThread();
339 // A read-only ImageSkia with no source is thread safe.
340 EXPECT_TRUE(image
.IsThreadSafe());
341 test::TestOnThread
readonly_on_thread(&image
);
342 readonly_on_thread
.StartAndJoin();
343 EXPECT_TRUE(readonly_on_thread
.can_read());
344 EXPECT_FALSE(readonly_on_thread
.can_modify());
345 EXPECT_TRUE(image
.CanRead());
346 EXPECT_FALSE(image
.CanModify());
348 image
.DetachStorageFromThread();
349 image
.MakeThreadSafe();
350 EXPECT_TRUE(image
.IsThreadSafe());
351 test::TestOnThread
threadsafe_on_thread(&image
);
352 threadsafe_on_thread
.StartAndJoin();
353 EXPECT_TRUE(threadsafe_on_thread
.can_read());
354 EXPECT_FALSE(threadsafe_on_thread
.can_modify());
355 EXPECT_TRUE(image
.CanRead());
356 EXPECT_FALSE(image
.CanModify());
359 TEST_F(ImageSkiaTest
, SourceOnThreadTest
) {
360 ImageSkia
image(new DynamicSource(Size(100, 200)), Size(100, 200));
361 EXPECT_FALSE(image
.IsThreadSafe());
363 test::TestOnThread
image_on_thread(&image
);
364 image_on_thread
.StartAndJoin();
365 // an image that was never accessed on this thread can be
366 // read by other thread.
367 EXPECT_TRUE(image_on_thread
.can_read());
368 EXPECT_TRUE(image_on_thread
.can_modify());
369 EXPECT_FALSE(image
.CanRead());
370 EXPECT_FALSE(image
.CanModify());
372 image
.DetachStorageFromThread();
373 // An image is accessed by this thread,
374 // so other thread cannot read/modify it.
376 test::TestOnThread
image_on_thread2(&image
);
377 image_on_thread2
.StartAndJoin();
378 EXPECT_FALSE(image_on_thread2
.can_read());
379 EXPECT_FALSE(image_on_thread2
.can_modify());
380 EXPECT_TRUE(image
.CanRead());
381 EXPECT_TRUE(image
.CanModify());
383 image
.DetachStorageFromThread();
385 EXPECT_FALSE(image
.IsThreadSafe());
386 test::TestOnThread
readonly_on_thread(&image
);
387 readonly_on_thread
.StartAndJoin();
388 EXPECT_TRUE(readonly_on_thread
.can_read());
389 EXPECT_FALSE(readonly_on_thread
.can_modify());
390 EXPECT_FALSE(image
.CanRead());
391 EXPECT_FALSE(image
.CanModify());
393 image
.DetachStorageFromThread();
394 image
.MakeThreadSafe();
395 EXPECT_TRUE(image
.IsThreadSafe());
396 // Check if image reps are generated for supported scale factors.
397 EXPECT_EQ(ImageSkia::GetSupportedScales().size(),
398 image
.image_reps().size());
399 test::TestOnThread
threadsafe_on_thread(&image
);
400 threadsafe_on_thread
.StartAndJoin();
401 EXPECT_TRUE(threadsafe_on_thread
.can_read());
402 EXPECT_FALSE(threadsafe_on_thread
.can_modify());
403 EXPECT_TRUE(image
.CanRead());
404 EXPECT_FALSE(image
.CanModify());
406 #endif // ENABLE_NON_THREAD_SAFE
408 // Just in case we ever get lumped together with other compilation units.
409 #undef ENABLE_NON_THREAD_SAFE
411 TEST_F(ImageSkiaTest
, Unscaled
) {
414 // An ImageSkia created with 1x bitmap is unscaled.
415 ImageSkia image_skia
= ImageSkia::CreateFrom1xBitmap(bitmap
);
416 EXPECT_TRUE(image_skia
.GetRepresentation(1.0f
).unscaled());
417 ImageSkiaRep
rep_2x(Size(100, 100), 2.0f
);
419 // When reps for other scales are added, the unscaled image
421 image_skia
.AddRepresentation(rep_2x
);
422 EXPECT_FALSE(image_skia
.GetRepresentation(1.0f
).unscaled());
423 EXPECT_FALSE(image_skia
.GetRepresentation(2.0f
).unscaled());
428 std::vector
<float> GetSortedScaleFactors(const gfx::ImageSkia
& image
) {
429 const std::vector
<ImageSkiaRep
>& image_reps
= image
.image_reps();
430 std::vector
<float> scale_factors
;
431 for (size_t i
= 0; i
< image_reps
.size(); ++i
) {
432 scale_factors
.push_back(image_reps
[i
].scale());
434 std::sort(scale_factors
.begin(), scale_factors
.end());
435 return scale_factors
;
440 TEST_F(ImageSkiaTest
, ArbitraryScaleFactor
) {
441 // Do not test if the ImageSkia doesn't support arbitrary scale factors.
442 if (!ImageSkia::IsDSFScalingInImageSkiaEnabled())
445 // source is owned by |image|
446 DynamicSource
* source
= new DynamicSource(Size(100, 200));
447 ImageSkia
image(source
, gfx::Size(100, 200));
449 image
.GetRepresentation(1.5f
);
450 EXPECT_EQ(2.0f
, source
->GetLastRequestedScaleAndReset());
451 std::vector
<ImageSkiaRep
> image_reps
= image
.image_reps();
452 EXPECT_EQ(2u, image_reps
.size());
454 std::vector
<float> scale_factors
= GetSortedScaleFactors(image
);
455 EXPECT_EQ(1.5f
, scale_factors
[0]);
456 EXPECT_EQ(2.0f
, scale_factors
[1]);
458 // Requesting 1.75 scale factor also falls back to 2.0f and rescale.
459 // However, the image already has the 2.0f data, so it won't fetch again.
460 image
.GetRepresentation(1.75f
);
461 EXPECT_EQ(0.0f
, source
->GetLastRequestedScaleAndReset());
462 image_reps
= image
.image_reps();
463 EXPECT_EQ(3u, image_reps
.size());
465 scale_factors
= GetSortedScaleFactors(image
);
466 EXPECT_EQ(1.5f
, scale_factors
[0]);
467 EXPECT_EQ(1.75f
, scale_factors
[1]);
468 EXPECT_EQ(2.0f
, scale_factors
[2]);
470 // Requesting 1.25 scale factor also falls back to 2.0f and rescale.
471 // However, the image already has the 2.0f data, so it won't fetch again.
472 image
.GetRepresentation(1.25f
);
473 EXPECT_EQ(0.0f
, source
->GetLastRequestedScaleAndReset());
474 image_reps
= image
.image_reps();
475 EXPECT_EQ(4u, image_reps
.size());
476 scale_factors
= GetSortedScaleFactors(image
);
477 EXPECT_EQ(1.25f
, scale_factors
[0]);
478 EXPECT_EQ(1.5f
, scale_factors
[1]);
479 EXPECT_EQ(1.75f
, scale_factors
[2]);
480 EXPECT_EQ(2.0f
, scale_factors
[3]);
482 // 1.20 is falled back to 1.0.
483 image
.GetRepresentation(1.20f
);
484 EXPECT_EQ(1.0f
, source
->GetLastRequestedScaleAndReset());
485 image_reps
= image
.image_reps();
486 EXPECT_EQ(6u, image_reps
.size());
487 scale_factors
= GetSortedScaleFactors(image
);
488 EXPECT_EQ(1.0f
, scale_factors
[0]);
489 EXPECT_EQ(1.2f
, scale_factors
[1]);
490 EXPECT_EQ(1.25f
, scale_factors
[2]);
491 EXPECT_EQ(1.5f
, scale_factors
[3]);
492 EXPECT_EQ(1.75f
, scale_factors
[4]);
493 EXPECT_EQ(2.0f
, scale_factors
[5]);
495 // Scale factor less than 1.0f will be falled back to 1.0f
496 image
.GetRepresentation(0.75f
);
497 EXPECT_EQ(0.0f
, source
->GetLastRequestedScaleAndReset());
498 image_reps
= image
.image_reps();
499 EXPECT_EQ(7u, image_reps
.size());
501 scale_factors
= GetSortedScaleFactors(image
);
502 EXPECT_EQ(0.75f
, scale_factors
[0]);
503 EXPECT_EQ(1.0f
, scale_factors
[1]);
504 EXPECT_EQ(1.2f
, scale_factors
[2]);
505 EXPECT_EQ(1.25f
, scale_factors
[3]);
506 EXPECT_EQ(1.5f
, scale_factors
[4]);
507 EXPECT_EQ(1.75f
, scale_factors
[5]);
508 EXPECT_EQ(2.0f
, scale_factors
[6]);
510 // Scale factor greater than 2.0f is falled back to 2.0f because it's not
512 image
.GetRepresentation(3.0f
);
513 EXPECT_EQ(0.0f
, source
->GetLastRequestedScaleAndReset());
514 image_reps
= image
.image_reps();
515 EXPECT_EQ(8u, image_reps
.size());
518 TEST_F(ImageSkiaTest
, ArbitraryScaleFactorWithMissingResource
) {
519 // Do not test if the ImageSkia doesn't support arbitrary scale factors.
520 if (!ImageSkia::IsDSFScalingInImageSkiaEnabled())
523 ImageSkia
image(new FixedSource(
524 ImageSkiaRep(Size(100, 200), 1.0f
)), Size(100, 200));
526 // Requesting 1.5f -- falls back to 2.0f, but couldn't find. It should
527 // look up 1.0f and then rescale it.
528 const ImageSkiaRep
& rep
= image
.GetRepresentation(1.5f
);
529 EXPECT_EQ(1.5f
, rep
.scale());
530 EXPECT_EQ(2U, image
.image_reps().size());
531 EXPECT_EQ(1.0f
, image
.image_reps()[0].scale());
532 EXPECT_EQ(1.5f
, image
.image_reps()[1].scale());
535 TEST_F(ImageSkiaTest
, UnscaledImageForArbitraryScaleFactor
) {
536 // Do not test if the ImageSkia doesn't support arbitrary scale factors.
537 if (!ImageSkia::IsDSFScalingInImageSkiaEnabled())
540 // 0.0f means unscaled.
541 ImageSkia
image(new FixedSource(
542 ImageSkiaRep(Size(100, 200), 0.0f
)), Size(100, 200));
544 // Requesting 2.0f, which should return 1.0f unscaled image.
545 const ImageSkiaRep
& rep
= image
.GetRepresentation(2.0f
);
546 EXPECT_EQ(1.0f
, rep
.scale());
547 EXPECT_EQ("100x200", rep
.pixel_size().ToString());
548 EXPECT_TRUE(rep
.unscaled());
549 EXPECT_EQ(1U, image
.image_reps().size());
551 // Same for any other scale factors.
552 const ImageSkiaRep
& rep15
= image
.GetRepresentation(1.5f
);
553 EXPECT_EQ(1.0f
, rep15
.scale());
554 EXPECT_EQ("100x200", rep15
.pixel_size().ToString());
555 EXPECT_TRUE(rep15
.unscaled());
556 EXPECT_EQ(1U, image
.image_reps().size());
558 const ImageSkiaRep
& rep12
= image
.GetRepresentation(1.2f
);
559 EXPECT_EQ(1.0f
, rep12
.scale());
560 EXPECT_EQ("100x200", rep12
.pixel_size().ToString());
561 EXPECT_TRUE(rep12
.unscaled());
562 EXPECT_EQ(1U, image
.image_reps().size());