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