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