Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Side by Side Diff: third_party/WebKit/Source/platform/graphics/BitmapImageTest.cpp

Issue 1925533003: High CPU and increased memory usage fix for high-res (GIF, WEBP...) animations. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2013, Google Inc. All rights reserved. 2 * Copyright (c) 2013, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 20 matching lines...) Expand all
31 #include "platform/graphics/BitmapImage.h" 31 #include "platform/graphics/BitmapImage.h"
32 32
33 #include "platform/SharedBuffer.h" 33 #include "platform/SharedBuffer.h"
34 #include "platform/graphics/BitmapImageMetrics.h" 34 #include "platform/graphics/BitmapImageMetrics.h"
35 #include "platform/graphics/DeferredImageDecoder.h" 35 #include "platform/graphics/DeferredImageDecoder.h"
36 #include "platform/graphics/ImageObserver.h" 36 #include "platform/graphics/ImageObserver.h"
37 #include "platform/testing/HistogramTester.h" 37 #include "platform/testing/HistogramTester.h"
38 #include "platform/testing/UnitTestHelpers.h" 38 #include "platform/testing/UnitTestHelpers.h"
39 #include "testing/gtest/include/gtest/gtest.h" 39 #include "testing/gtest/include/gtest/gtest.h"
40 40
41 #define BITMAP_IMAGE_TEST 1
Peter Kasting 2016/04/28 23:07:54 Don't add this.
aleksandar.stojiljkovic 2016/04/29 17:17:43 Done.
42
41 namespace blink { 43 namespace blink {
42 44
43 class BitmapImageTest : public ::testing::Test { 45 class BitmapImageTest : public ::testing::Test {
44 public: 46 public:
45 class FakeImageObserver : public GarbageCollectedFinalized<FakeImageObserver >, public ImageObserver { 47 class FakeImageObserver : public GarbageCollectedFinalized<FakeImageObserver >, public ImageObserver {
46 USING_GARBAGE_COLLECTED_MIXIN(FakeImageObserver); 48 USING_GARBAGE_COLLECTED_MIXIN(FakeImageObserver);
47 public: 49 public:
48 FakeImageObserver() : m_lastDecodedSizeChangedDelta(0) { } 50 FakeImageObserver() : m_lastDecodedSizeChangedDelta(0) { }
49 51
50 virtual void decodedSizeChanged(const Image*, int delta) 52 virtual void decodedSizeChanged(const Image*, int delta)
(...skipping 13 matching lines...) Expand all
64 BitmapImageTest(bool enableDeferredDecoding) : m_enableDeferredDecoding(enab leDeferredDecoding) { } 66 BitmapImageTest(bool enableDeferredDecoding) : m_enableDeferredDecoding(enab leDeferredDecoding) { }
65 67
66 static PassRefPtr<SharedBuffer> readFile(const char* fileName) 68 static PassRefPtr<SharedBuffer> readFile(const char* fileName)
67 { 69 {
68 String filePath = testing::blinkRootDir(); 70 String filePath = testing::blinkRootDir();
69 filePath.append(fileName); 71 filePath.append(fileName);
70 return testing::readFromFile(filePath); 72 return testing::readFromFile(filePath);
71 } 73 }
72 74
73 // Accessors to BitmapImage's protected methods. 75 // Accessors to BitmapImage's protected methods.
74 void destroyDecodedData(bool destroyAll) { m_image->destroyDecodedData(destr oyAll); } 76 void destroyDecodedData() { m_image->destroyDecodedData(); }
75 size_t frameCount() { return m_image->frameCount(); } 77 size_t frameCount() { return m_image->frameCount(); }
76 void frameAtIndex(size_t index) 78 PassRefPtr<SkImage> frameAtIndex(size_t index)
77 { 79 {
78 m_image->frameAtIndex(index); 80 return m_image->frameAtIndex(index);
79 } 81 }
80 void setCurrentFrame(size_t frame) { m_image->m_currentFrame = frame; } 82 void setCurrentFrame(size_t frame) { m_image->m_currentFrame = frame; }
81 size_t frameDecodedSize(size_t frame) { return m_image->m_frames[frame].m_fr ameBytes; } 83 size_t frameDecodedSize(size_t frame) { return m_image->m_frames[frame].m_fr ameBytes; }
82 size_t decodedFramesCount() const { return m_image->m_frames.size(); } 84 size_t decodedFramesCount() const { return m_image->m_frames.size(); }
83 85
84 void setFirstFrameNotComplete() { m_image->m_frames[0].m_isComplete = false; } 86 void setFirstFrameNotComplete() { m_image->m_frames[0].m_isComplete = false; }
85 87
86 void loadImage(const char* fileName, bool loadAllFrames = true) 88 void loadImage(const char* fileName, bool loadAllFrames = true)
87 { 89 {
88 RefPtr<SharedBuffer> imageData = readFile(fileName); 90 RefPtr<SharedBuffer> imageData = readFile(fileName);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 int animationFinished() 127 int animationFinished()
126 { 128 {
127 return m_image->m_animationFinished; 129 return m_image->m_animationFinished;
128 } 130 }
129 131
130 PassRefPtr<Image> imageForDefaultFrame() 132 PassRefPtr<Image> imageForDefaultFrame()
131 { 133 {
132 return m_image->imageForDefaultFrame(); 134 return m_image->imageForDefaultFrame();
133 } 135 }
134 136
137 void setAnimationCacheSizeForTesting(size_t maxCacheSize, size_t maxAnimatio nSizeInCache)
138 {
139 BitmapImage::setAnimationCacheSizeForTesting(maxCacheSize, maxAnimationS izeInCache);
140 }
141
142 int lastDecodedSizeChange()
143 {
144 return m_imageObserver->m_lastDecodedSizeChangedDelta;
145 }
146
135 protected: 147 protected:
136 void SetUp() override 148 void SetUp() override
137 { 149 {
138 DeferredImageDecoder::setEnabled(m_enableDeferredDecoding); 150 DeferredImageDecoder::setEnabled(m_enableDeferredDecoding);
139 m_imageObserver = new FakeImageObserver; 151 m_imageObserver = new FakeImageObserver;
140 m_image = BitmapImage::create(m_imageObserver.get()); 152 m_image = BitmapImage::create(m_imageObserver.get());
141 } 153 }
142 154
143 Persistent<FakeImageObserver> m_imageObserver; 155 Persistent<FakeImageObserver> m_imageObserver;
144 RefPtr<BitmapImage> m_image; 156 RefPtr<BitmapImage> m_image;
145 157
146 private: 158 private:
147 bool m_enableDeferredDecoding; 159 bool m_enableDeferredDecoding;
148 }; 160 };
149 161
150 TEST_F(BitmapImageTest, destroyDecodedDataExceptCurrentFrame)
151 {
152 loadImage("/LayoutTests/fast/images/resources/animated-10color.gif");
153 size_t totalSize = decodedSize();
154 size_t frame = frameCount() / 2;
155 setCurrentFrame(frame);
156 size_t size = frameDecodedSize(frame);
157 destroyDecodedData(false);
158 EXPECT_LT(m_imageObserver->m_lastDecodedSizeChangedDelta, 0);
159 EXPECT_GE(m_imageObserver->m_lastDecodedSizeChangedDelta, -static_cast<int>( totalSize - size));
160 }
161
162 TEST_F(BitmapImageTest, destroyAllDecodedData) 162 TEST_F(BitmapImageTest, destroyAllDecodedData)
Peter Kasting 2016/04/28 23:07:54 Nit: Remove "All"?
aleksandar.stojiljkovic 2016/04/29 17:17:43 Done.
163 { 163 {
164 loadImage("/LayoutTests/fast/images/resources/animated-10color.gif"); 164 loadImage("/LayoutTests/fast/images/resources/animated-10color.gif");
165 size_t totalSize = decodedSize(); 165 size_t totalSize = decodedSize();
166 EXPECT_GT(totalSize, 0u); 166 EXPECT_GT(totalSize, 0u);
167 destroyDecodedData(true); 167 destroyDecodedData();
168 EXPECT_EQ(-static_cast<int>(totalSize), m_imageObserver->m_lastDecodedSizeCh angedDelta); 168 EXPECT_EQ(-static_cast<int>(totalSize), lastDecodedSizeChange());
169 EXPECT_EQ(0u, decodedSize()); 169 EXPECT_EQ(0u, decodedSize());
170 } 170 }
171 171
172 TEST_F(BitmapImageTest, maybeAnimated) 172 TEST_F(BitmapImageTest, maybeAnimated)
173 { 173 {
174 loadImage("/LayoutTests/fast/images/resources/gif-loop-count.gif"); 174 loadImage("/LayoutTests/fast/images/resources/gif-loop-count.gif");
175 for (size_t i = 0; i < frameCount(); ++i) { 175 for (size_t i = 0; i < frameCount(); ++i) {
176 EXPECT_TRUE(m_image->maybeAnimated()); 176 EXPECT_TRUE(m_image->maybeAnimated());
177 advanceAnimation(); 177 advanceAnimation();
178 } 178 }
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
260 } 260 }
261 261
262 TEST_F(BitmapImageTest, correctDecodedDataSize) 262 TEST_F(BitmapImageTest, correctDecodedDataSize)
263 { 263 {
264 // When requesting a frame of a multi-frame GIF causes another frame to be 264 // When requesting a frame of a multi-frame GIF causes another frame to be
265 // decoded as well, both frames' sizes should be reported by the source and 265 // decoded as well, both frames' sizes should be reported by the source and
266 // thus included in the decoded size changed notification. 266 // thus included in the decoded size changed notification.
267 loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false); 267 loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false);
268 frameAtIndex(1); 268 frameAtIndex(1);
269 int frameSize = static_cast<int>(m_image->size().area() * sizeof(ImageFrame: :PixelData)); 269 int frameSize = static_cast<int>(m_image->size().area() * sizeof(ImageFrame: :PixelData));
270 EXPECT_EQ(frameSize * 2, m_imageObserver->m_lastDecodedSizeChangedDelta); 270 EXPECT_EQ(frameSize * 2, lastDecodedSizeChange());
271
272 // Trying to destroy all data except an undecoded frame should cause the
273 // decoder to seek backwards and preserve the most recent previous frame
274 // necessary to decode that undecoded frame, and destroy all other frames.
275 setCurrentFrame(2);
276 destroyDecodedData(false);
277 EXPECT_EQ(-frameSize, m_imageObserver->m_lastDecodedSizeChangedDelta);
278 } 271 }
279 272
280 TEST_F(BitmapImageTest, recachingFrameAfterDataChanged) 273 TEST_F(BitmapImageTest, recachingFrameAfterDataChanged)
281 { 274 {
282 loadImage("/LayoutTests/fast/images/resources/green.jpg"); 275 loadImage("/LayoutTests/fast/images/resources/green.jpg");
283 setFirstFrameNotComplete(); 276 setFirstFrameNotComplete();
284 EXPECT_GT(m_imageObserver->m_lastDecodedSizeChangedDelta, 0); 277 EXPECT_GT(lastDecodedSizeChange(), 0);
285 m_imageObserver->m_lastDecodedSizeChangedDelta = 0; 278 m_imageObserver->m_lastDecodedSizeChangedDelta = 0;
286 279
287 // Calling dataChanged causes the cache to flush, but doesn't affect the 280 // Calling dataChanged causes the cache to flush, but doesn't affect the
288 // source's decoded frames. It shouldn't affect decoded size. 281 // source's decoded frames. It shouldn't affect decoded size.
289 m_image->dataChanged(true); 282 m_image->dataChanged(true);
290 EXPECT_EQ(0, m_imageObserver->m_lastDecodedSizeChangedDelta); 283 EXPECT_EQ(0, lastDecodedSizeChange());
291 // Recaching the first frame also shouldn't affect decoded size. 284 // Recaching the first frame also shouldn't affect decoded size.
292 m_image->imageForCurrentFrame(); 285 m_image->imageForCurrentFrame();
293 EXPECT_EQ(0, m_imageObserver->m_lastDecodedSizeChangedDelta); 286 EXPECT_EQ(0, lastDecodedSizeChange());
294 } 287 }
295 288
296 class BitmapImageDeferredDecodingTest : public BitmapImageTest { 289 class BitmapImageDeferredDecodingTest : public BitmapImageTest {
297 public: 290 public:
298 BitmapImageDeferredDecodingTest() : BitmapImageTest(true) { } 291 BitmapImageDeferredDecodingTest() : BitmapImageTest(true) { }
299 }; 292 };
300 293
294 class BitmapImageTestEmptyImpl : public BitmapImageTest {
295 public:
296 BitmapImageTestEmptyImpl() : BitmapImageTest(true)
297 {
298 SetUp();
299 }
300
301 void TestBody() override
302 {
303 }
304 };
305
306 TEST_F(BitmapImageDeferredDecodingTest, animationBitmapImageCacheMaxPerAnimation )
307 {
308 loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false);
309 uint32_t image0 = frameAtIndex(0)->uniqueID();
310 int frameSize = static_cast<int>(m_image->size().area() * sizeof(ImageFrame: :PixelData));
Peter Kasting 2016/04/28 23:07:54 Nit: Should be size_t with no cast since that's wh
aleksandar.stojiljkovic 2016/04/29 17:17:43 This method is removed in patch#2 so Done applies
311 setAnimationCacheSizeForTesting(frameSize * 4, frameSize * 2);
312
313 uint32_t image1 = frameAtIndex(1)->uniqueID();
314 uint32_t image0v1 = frameAtIndex(0)->uniqueID();
Peter Kasting 2016/04/28 23:07:55 Nit: Inline into next statement
315 EXPECT_EQ(image0, image0v1);
316 uint32_t image2 = frameAtIndex(2)->uniqueID();
317 uint32_t image1v1 = frameAtIndex(1)->uniqueID();
Peter Kasting 2016/04/28 23:07:54 Nit: Inline into next statement
318 // It is expected that 0 and 1 is kept in the cache, and 2 is not cached
319 // as only first two frames are cached.
320 EXPECT_EQ(image1, image1v1);
321 uint32_t image2v1 = frameAtIndex(2)->uniqueID();
Peter Kasting 2016/04/28 23:07:54 Nit: Inline into next statement
322 EXPECT_NE(image2, image2v1);
323 }
324
325 TEST_F(BitmapImageDeferredDecodingTest, animationBitmapImageCacheEvict)
326 {
327 loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false);
328 uint32_t image0 = frameAtIndex(0)->uniqueID();
329 int frameSize = static_cast<int>(m_image->size().area() * sizeof(ImageFrame: :PixelData));
330 setAnimationCacheSizeForTesting(frameSize * 2, frameSize * 2);
331
332 // Load another animation that would evict first animation frames
Peter Kasting 2016/04/28 23:07:54 Nit: Trailing period
333 BitmapImageTestEmptyImpl second;
334 second.loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false);
335 uint32_t second0 = second.frameAtIndex(0)->uniqueID();
336
337 uint32_t image0v1 = frameAtIndex(0)->uniqueID();
338 EXPECT_EQ(image0, image0v1);
339
340 uint32_t second0v1 = second.frameAtIndex(0)->uniqueID();
341 EXPECT_EQ(second0, second0v1);
342 second.frameAtIndex(1);
343
344 // Second 0 and second 1 are now in the cache.
345 uint32_t image0v2 = frameAtIndex(0)->uniqueID();
346 EXPECT_NE(image0v1, image0v2);
Peter Kasting 2016/04/28 23:07:54 Nit: Compare to |image0| so |image0v1| above can b
347
348 // Second was evicted as original frame 0 + second 2 frames are above cache
349 // size even second itself is under per animation limit size.
Peter Kasting 2016/04/28 23:07:55 This sentence has grammar issues severe enough tha
350 uint32_t second0v2 = second.frameAtIndex(0)->uniqueID();
351 EXPECT_NE(second0v1, second0v2);
Peter Kasting 2016/04/28 23:07:54 Nit: Compare to |second0| so |second0v1| above can
352
353 uint32_t image0v3 = frameAtIndex(0)->uniqueID();
354 EXPECT_EQ(image0v2, image0v3);
355 uint32_t second0v3 = second.frameAtIndex(0)->uniqueID();
356 EXPECT_EQ(second0v2, second0v3);
357
358 // By setAnimationCacheSizeForTesting it is defined to keep in cache first
359 // two frames per animation. Other frames, like frame 2 is cached only
360 // temporarily, until access to some other frame.
361 // When making space for frame 2 it in cache, the very same ImageBitmap that
362 // contains this frame will first get cleared and removed from cache (as it
363 // is first for removal in MRU cache), frame index 2 added to it and cached.
Peter Kasting 2016/04/28 23:07:54 Similarly this whole comment has significant gramm
364 frameAtIndex(2);
365 uint32_t image0v4 = frameAtIndex(0)->uniqueID();
366 EXPECT_NE(image0v3, image0v4);
Peter Kasting 2016/04/28 23:07:55 Nit: Compare to |image0v2| so |image0v3| above can
367 uint32_t second0v4 = second.frameAtIndex(0)->uniqueID();
368 EXPECT_EQ(second0v3, second0v4);
Peter Kasting 2016/04/28 23:07:54 Nit: Compare to |second0v2| so |second0v3| above c
369
370 BitmapImageTestEmptyImpl third;
371 third.loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false);
372 third.frameAtIndex(0); // It should evict image0v4.
373 uint32_t image0v5 = frameAtIndex(0)->uniqueID();
Peter Kasting 2016/04/28 23:07:54 Nit: Inline into next statement
374 EXPECT_NE(image0v4, image0v5);
375 }
376
301 TEST_F(BitmapImageDeferredDecodingTest, correctDecodedDataSize) 377 TEST_F(BitmapImageDeferredDecodingTest, correctDecodedDataSize)
302 { 378 {
303 // When deferred decoding is enabled, requesting any one frame shouldn't 379 // When deferred decoding is enabled, requesting any one frame shouldn't
304 // result in decoding any other frames. 380 // result in decoding any other frames.
305 loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false); 381 loadImage("/LayoutTests/fast/images/resources/anim_none.gif", false);
306 frameAtIndex(1); 382 frameAtIndex(1);
307 int frameSize = static_cast<int>(m_image->size().area() * sizeof(ImageFrame: :PixelData)); 383 int frameSize = static_cast<int>(m_image->size().area() * sizeof(ImageFrame: :PixelData));
308 EXPECT_EQ(frameSize, m_imageObserver->m_lastDecodedSizeChangedDelta); 384 EXPECT_EQ(frameSize, lastDecodedSizeChange());
309 frameAtIndex(0);
310
311 // Trying to destroy all data except an undecoded frame should go ahead and
312 // destroy all other frames.
313 setCurrentFrame(2);
314 destroyDecodedData(false);
315 EXPECT_EQ(-frameSize * 2, m_imageObserver->m_lastDecodedSizeChangedDelta);
316 } 385 }
317 386
318 template <typename HistogramEnumType> 387 template <typename HistogramEnumType>
319 struct HistogramTestParams { 388 struct HistogramTestParams {
320 const char* filename; 389 const char* filename;
321 HistogramEnumType type; 390 HistogramEnumType type;
322 }; 391 };
323 392
324 template <typename HistogramEnumType> 393 template <typename HistogramEnumType>
325 class BitmapHistogramTest 394 class BitmapHistogramTest
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 {"/LayoutTests/fast/images/resources/exif-orientation-5-lu.jpg", OriginLeftT op}, 437 {"/LayoutTests/fast/images/resources/exif-orientation-5-lu.jpg", OriginLeftT op},
369 {"/LayoutTests/fast/images/resources/exif-orientation-6-ru.jpg", OriginRight Top}, 438 {"/LayoutTests/fast/images/resources/exif-orientation-6-ru.jpg", OriginRight Top},
370 {"/LayoutTests/fast/images/resources/exif-orientation-7-rl.jpg", OriginRight Bottom}, 439 {"/LayoutTests/fast/images/resources/exif-orientation-7-rl.jpg", OriginRight Bottom},
371 {"/LayoutTests/fast/images/resources/exif-orientation-8-llo.jpg", OriginLeft Bottom} 440 {"/LayoutTests/fast/images/resources/exif-orientation-8-llo.jpg", OriginLeft Bottom}
372 }; 441 };
373 442
374 INSTANTIATE_TEST_CASE_P(DecodedImageOrientationHistogramTest, DecodedImageOrient ationHistogramTest, 443 INSTANTIATE_TEST_CASE_P(DecodedImageOrientationHistogramTest, DecodedImageOrient ationHistogramTest,
375 ::testing::ValuesIn(kDecodedImageOrientationHistogramTestParams)); 444 ::testing::ValuesIn(kDecodedImageOrientationHistogramTestParams));
376 445
377 } // namespace blink 446 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698