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

Side by Side Diff: third_party/WebKit/Source/platform/graphics/BitmapImage.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: review #23 fixes 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) 2006 Samuel Weinig (sam.weinig@gmail.com) 2 * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
3 * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
7 * are met: 7 * are met:
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. 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 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 RefPtr<BitmapImage> result = adoptRef(new BitmapImage(bitmap)); 50 RefPtr<BitmapImage> result = adoptRef(new BitmapImage(bitmap));
51 result->m_frames[0].m_orientation = orientation; 51 result->m_frames[0].m_orientation = orientation;
52 if (orientation.usesWidthAsHeight()) 52 if (orientation.usesWidthAsHeight())
53 result->m_sizeRespectingOrientation = result->m_size.transposedSize(); 53 result->m_sizeRespectingOrientation = result->m_size.transposedSize();
54 return result.release(); 54 return result.release();
55 } 55 }
56 56
57 BitmapImage::BitmapImage(ImageObserver* observer) 57 BitmapImage::BitmapImage(ImageObserver* observer)
58 : Image(observer) 58 : Image(observer)
59 , m_currentFrame(0) 59 , m_currentFrame(0)
60 , m_cachedFrameIndex(0)
60 , m_repetitionCount(cAnimationNone) 61 , m_repetitionCount(cAnimationNone)
61 , m_repetitionCountStatus(Unknown) 62 , m_repetitionCountStatus(Unknown)
62 , m_repetitionsComplete(0) 63 , m_repetitionsComplete(0)
63 , m_desiredFrameStartTime(0) 64 , m_desiredFrameStartTime(0)
64 , m_frameCount(0) 65 , m_frameCount(0)
65 , m_animationPolicy(ImageAnimationPolicyAllowed) 66 , m_animationPolicy(ImageAnimationPolicyAllowed)
66 , m_animationFinished(false) 67 , m_animationFinished(false)
67 , m_allDataReceived(false) 68 , m_allDataReceived(false)
68 , m_haveSize(false) 69 , m_haveSize(false)
69 , m_sizeAvailable(false) 70 , m_sizeAvailable(false)
70 , m_hasUniformFrameSize(true) 71 , m_hasUniformFrameSize(true)
71 , m_haveFrameCount(false) 72 , m_haveFrameCount(false)
72 { 73 {
73 } 74 }
74 75
75 BitmapImage::BitmapImage(const SkBitmap& bitmap, ImageObserver* observer) 76 BitmapImage::BitmapImage(const SkBitmap& bitmap, ImageObserver* observer)
76 : Image(observer) 77 : Image(observer)
77 , m_size(bitmap.width(), bitmap.height()) 78 , m_size(bitmap.width(), bitmap.height())
78 , m_currentFrame(0) 79 , m_currentFrame(0)
80 , m_cachedFrame(adoptRef(SkImage::NewFromBitmap(bitmap)))
81 , m_cachedFrameIndex(0)
79 , m_repetitionCount(cAnimationNone) 82 , m_repetitionCount(cAnimationNone)
80 , m_repetitionCountStatus(Unknown) 83 , m_repetitionCountStatus(Unknown)
81 , m_repetitionsComplete(0) 84 , m_repetitionsComplete(0)
82 , m_frameCount(1) 85 , m_frameCount(1)
83 , m_animationPolicy(ImageAnimationPolicyAllowed) 86 , m_animationPolicy(ImageAnimationPolicyAllowed)
84 , m_animationFinished(true) 87 , m_animationFinished(true)
85 , m_allDataReceived(true) 88 , m_allDataReceived(true)
86 , m_haveSize(true) 89 , m_haveSize(true)
87 , m_sizeAvailable(true) 90 , m_sizeAvailable(true)
88 , m_haveFrameCount(true) 91 , m_haveFrameCount(true)
89 { 92 {
90 // Since we don't have a decoder, we can't figure out the image orientation. 93 // Since we don't have a decoder, we can't figure out the image orientation.
91 // Set m_sizeRespectingOrientation to be the same as m_size so it's not 0x0. 94 // Set m_sizeRespectingOrientation to be the same as m_size so it's not 0x0.
92 m_sizeRespectingOrientation = m_size; 95 m_sizeRespectingOrientation = m_size;
93 96
94 m_frames.grow(1); 97 m_frames.grow(1);
95 m_frames[0].m_hasAlpha = !bitmap.isOpaque(); 98 m_frames[0].m_hasAlpha = !bitmap.isOpaque();
96 m_frames[0].m_frame = adoptRef(SkImage::NewFromBitmap(bitmap));
97 m_frames[0].m_haveMetadata = true; 99 m_frames[0].m_haveMetadata = true;
98 } 100 }
99 101
100 BitmapImage::~BitmapImage() 102 BitmapImage::~BitmapImage()
101 { 103 {
102 stopAnimation(); 104 stopAnimation();
103 } 105 }
104 106
105 bool BitmapImage::currentFrameHasSingleSecurityOrigin() const 107 bool BitmapImage::currentFrameHasSingleSecurityOrigin() const
106 { 108 {
107 return true; 109 return true;
108 } 110 }
109 111
110 void BitmapImage::destroyDecodedData(bool destroyAll) 112 void BitmapImage::destroyDecodedData()
111 { 113 {
112 for (size_t i = 0; i < m_frames.size(); ++i) { 114 m_cachedFrame.clear();
113 // The underlying frame isn't actually changing (we're just trying to 115 for (size_t i = 0; i < m_frames.size(); ++i)
114 // save the memory for the framebuffer data), so we don't need to clear 116 m_frames[i].clear(true);
115 // the metadata. 117 m_source.clearCacheExceptFrame(kNotFound);
116 m_frames[i].clear(false);
117 }
118
119 m_source.clearCacheExceptFrame(destroyAll ? kNotFound : m_currentFrame);
120 notifyMemoryChanged(); 118 notifyMemoryChanged();
121 } 119 }
122 120
123 void BitmapImage::destroyDecodedDataIfNecessary()
124 {
125 // Animated images >5MB are considered large enough that we'll only hang on
126 // to one frame at a time.
127 static const size_t cLargeAnimationCutoff = 5242880;
128 size_t allFrameBytes = 0;
129 for (size_t i = 0; i < m_frames.size(); ++i)
130 allFrameBytes += m_frames[i].m_frameBytes;
131
132 if (allFrameBytes > cLargeAnimationCutoff) {
133 destroyDecodedData(false);
134 }
135 }
136
137 void BitmapImage::notifyMemoryChanged() 121 void BitmapImage::notifyMemoryChanged()
138 { 122 {
139 if (getImageObserver()) 123 if (getImageObserver())
140 getImageObserver()->decodedSizeChangedTo(this, totalFrameBytes()); 124 getImageObserver()->decodedSizeChangedTo(this, totalFrameBytes());
141 } 125 }
142 126
143 size_t BitmapImage::totalFrameBytes() 127 size_t BitmapImage::totalFrameBytes()
144 { 128 {
145 const size_t numFrames = frameCount(); 129 const size_t numFrames = frameCount();
146 size_t totalBytes = 0; 130 size_t totalBytes = 0;
147 for (size_t i = 0; i < numFrames; ++i) 131 for (size_t i = 0; i < numFrames; ++i)
148 totalBytes += m_source.frameBytesAtIndex(i); 132 totalBytes += m_source.frameBytesAtIndex(i);
149 return totalBytes; 133 return totalBytes;
150 } 134 }
151 135
152 void BitmapImage::cacheFrame(size_t index) 136 PassRefPtr<SkImage> BitmapImage::decodeAndCacheFrame(size_t index)
153 { 137 {
154 size_t numFrames = frameCount(); 138 size_t numFrames = frameCount();
155 if (m_frames.size() < numFrames) 139 if (m_frames.size() < numFrames)
156 m_frames.grow(numFrames); 140 m_frames.grow(numFrames);
157 141
158 // We are caching frame snapshots. This is OK even for partially decoded fr ames, 142 // We are caching frame snapshots. This is OK even for partially decoded fr ames,
159 // as they are cleared by dataChanged() when new data arrives. 143 // as they are cleared by dataChanged() when new data arrives.
160 m_frames[index].m_frame = m_source.createFrameAtIndex(index); 144 RefPtr<SkImage> image = m_source.createFrameAtIndex(index);
145 m_cachedFrame = image;
146 m_cachedFrameIndex = index;
161 147
162 m_frames[index].m_orientation = m_source.orientationAtIndex(index); 148 m_frames[index].m_orientation = m_source.orientationAtIndex(index);
163 m_frames[index].m_haveMetadata = true; 149 m_frames[index].m_haveMetadata = true;
164 m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index); 150 m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index);
165 if (repetitionCount(false) != cAnimationNone) 151 if (repetitionCount(false) != cAnimationNone)
166 m_frames[index].m_duration = m_source.frameDurationAtIndex(index); 152 m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
167 m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index); 153 m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
168 m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index); 154 m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index);
169 155
170 const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size); 156 const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size);
171 if (frameSize != m_size) 157 if (frameSize != m_size)
172 m_hasUniformFrameSize = false; 158 m_hasUniformFrameSize = false;
173 159
174 notifyMemoryChanged(); 160 notifyMemoryChanged();
161 return image.release();
175 } 162 }
176 163
177 void BitmapImage::updateSize() const 164 void BitmapImage::updateSize() const
178 { 165 {
179 if (!m_sizeAvailable || m_haveSize) 166 if (!m_sizeAvailable || m_haveSize)
180 return; 167 return;
181 168
182 m_size = m_source.size(); 169 m_size = m_source.size();
183 m_sizeRespectingOrientation = m_source.size(RespectImageOrientation); 170 m_sizeRespectingOrientation = m_source.size(RespectImageOrientation);
184 m_haveSize = true; 171 m_haveSize = true;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 // and the frames aren't even guaranteed to appear in the file in the same 206 // and the frames aren't even guaranteed to appear in the file in the same
220 // order as in the directory, so an arbitrary number of the frames might be 207 // order as in the directory, so an arbitrary number of the frames might be
221 // incomplete (if we ask for frames for which we've not yet reached the 208 // incomplete (if we ask for frames for which we've not yet reached the
222 // start of the frame data), and any or none of them might be the particular 209 // start of the frame data), and any or none of them might be the particular
223 // frame affected by appending new data here. Thus we have to clear all the 210 // frame affected by appending new data here. Thus we have to clear all the
224 // incomplete frames to be safe. 211 // incomplete frames to be safe.
225 for (size_t i = 0; i < m_frames.size(); ++i) { 212 for (size_t i = 0; i < m_frames.size(); ++i) {
226 // NOTE: Don't call frameIsCompleteAtIndex() here, that will try to 213 // NOTE: Don't call frameIsCompleteAtIndex() here, that will try to
227 // decode any uncached (i.e. never-decoded or 214 // decode any uncached (i.e. never-decoded or
228 // cleared-on-a-previous-pass) frames! 215 // cleared-on-a-previous-pass) frames!
229 if (m_frames[i].m_haveMetadata && !m_frames[i].m_isComplete) 216 if (m_frames[i].m_haveMetadata && !m_frames[i].m_isComplete) {
230 m_frames[i].clear(true); 217 m_frames[i].clear(true);
218 if (i == m_cachedFrameIndex)
219 m_cachedFrame.clear();
scroggo_chromium 2016/05/04 21:15:54 Should we set m_cachedFrameIndex to an invalid val
aleksandar.stojiljkovic 2016/05/04 21:28:07 No need, once a non null value is assigned to it a
220 }
231 } 221 }
232 222
233 // Feed all the data we've seen so far to the image decoder. 223 // Feed all the data we've seen so far to the image decoder.
234 m_allDataReceived = allDataReceived; 224 m_allDataReceived = allDataReceived;
235 ASSERT(data()); 225 ASSERT(data());
236 m_source.setData(*data(), allDataReceived); 226 m_source.setData(*data(), allDataReceived);
237 227
238 m_haveFrameCount = false; 228 m_haveFrameCount = false;
239 m_hasUniformFrameSize = true; 229 m_hasUniformFrameSize = true;
240 return isSizeAvailable(); 230 return isSizeAvailable();
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
284 // the image, as it was needed for page layout, so we need to revers e it back here. 274 // the image, as it was needed for page layout, so we need to revers e it back here.
285 adjustedDstRect = FloatRect(adjustedDstRect.x(), adjustedDstRect.y() , adjustedDstRect.height(), adjustedDstRect.width()); 275 adjustedDstRect = FloatRect(adjustedDstRect.x(), adjustedDstRect.y() , adjustedDstRect.height(), adjustedDstRect.width());
286 } 276 }
287 } 277 }
288 278
289 SkRect skSrcRect = adjustedSrcRect; 279 SkRect skSrcRect = adjustedSrcRect;
290 canvas->drawImageRect(image.get(), skSrcRect, adjustedDstRect, &paint, 280 canvas->drawImageRect(image.get(), skSrcRect, adjustedDstRect, &paint,
291 WebCoreClampingModeToSkiaRectConstraint(clampMode)); 281 WebCoreClampingModeToSkiaRectConstraint(clampMode));
292 canvas->restoreToCount(initialSaveCount); 282 canvas->restoreToCount(initialSaveCount);
293 283
294 if (currentFrameIsLazyDecoded()) 284 if (image->isLazyGenerated())
295 PlatformInstrumentation::didDrawLazyPixelRef(image->uniqueID()); 285 PlatformInstrumentation::didDrawLazyPixelRef(image->uniqueID());
296 286
297 if (ImageObserver* observer = getImageObserver()) 287 if (ImageObserver* observer = getImageObserver())
298 observer->didDraw(this); 288 observer->didDraw(this);
299 289
300 startAnimation(); 290 startAnimation();
301 } 291 }
302 292
303 size_t BitmapImage::frameCount() 293 size_t BitmapImage::frameCount()
304 { 294 {
(...skipping 21 matching lines...) Expand all
326 316
327 if (m_sizeAvailable && hasVisibleImageSize(size())) { 317 if (m_sizeAvailable && hasVisibleImageSize(size())) {
328 BitmapImageMetrics::countDecodedImageType(m_source.filenameExtension()); 318 BitmapImageMetrics::countDecodedImageType(m_source.filenameExtension());
329 if (m_source.filenameExtension() == "jpg") 319 if (m_source.filenameExtension() == "jpg")
330 BitmapImageMetrics::countImageOrientation(m_source.orientationAtInde x(0).orientation()); 320 BitmapImageMetrics::countImageOrientation(m_source.orientationAtInde x(0).orientation());
331 } 321 }
332 322
333 return m_sizeAvailable; 323 return m_sizeAvailable;
334 } 324 }
335 325
336 bool BitmapImage::ensureFrameIsCached(size_t index)
337 {
338 if (index >= frameCount())
339 return false;
340
341 if (index >= m_frames.size() || !m_frames[index].m_frame)
342 cacheFrame(index);
343
344 return true;
345 }
346
347 PassRefPtr<SkImage> BitmapImage::frameAtIndex(size_t index) 326 PassRefPtr<SkImage> BitmapImage::frameAtIndex(size_t index)
348 { 327 {
349 if (!ensureFrameIsCached(index)) 328 if (index >= frameCount())
350 return nullptr; 329 return nullptr;
351 330
352 return m_frames[index].m_frame; 331 if (index == m_cachedFrameIndex && m_cachedFrame)
332 return m_cachedFrame;
Peter Kasting 2016/05/07 01:58:29 Does this implicitly ref m_cachedFrame and then pa
aleksandar.stojiljkovic 2016/05/07 19:50:53 Yes, see PasRefPtr.h. template <typename U> PassR
333
334 return decodeAndCacheFrame(index);
353 } 335 }
354 336
355 bool BitmapImage::frameIsCompleteAtIndex(size_t index) 337 bool BitmapImage::frameIsCompleteAtIndex(size_t index)
356 { 338 {
357 if (index < m_frames.size() && m_frames[index].m_haveMetadata && m_frames[in dex].m_isComplete) 339 if (index < m_frames.size() && m_frames[index].m_haveMetadata && m_frames[in dex].m_isComplete)
358 return true; 340 return true;
359 341
360 return m_source.frameIsCompleteAtIndex(index); 342 return m_source.frameIsCompleteAtIndex(index);
361 } 343 }
362 344
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
558 m_frameTimer.clear(); 540 m_frameTimer.clear();
559 } 541 }
560 542
561 void BitmapImage::resetAnimation() 543 void BitmapImage::resetAnimation()
562 { 544 {
563 stopAnimation(); 545 stopAnimation();
564 m_currentFrame = 0; 546 m_currentFrame = 0;
565 m_repetitionsComplete = 0; 547 m_repetitionsComplete = 0;
566 m_desiredFrameStartTime = 0; 548 m_desiredFrameStartTime = 0;
567 m_animationFinished = false; 549 m_animationFinished = false;
568 550 m_cachedFrame.clear();
569 // For extremely large animations, when the animation is reset, we just thro w everything away.
570 destroyDecodedDataIfNecessary();
571 } 551 }
572 552
573 bool BitmapImage::maybeAnimated() 553 bool BitmapImage::maybeAnimated()
574 { 554 {
575 if (m_animationFinished) 555 if (m_animationFinished)
576 return false; 556 return false;
577 if (frameCount() > 1) 557 if (frameCount() > 1)
578 return true; 558 return true;
579 559
580 return m_source.repetitionCount() != cAnimationNone; 560 return m_source.repetitionCount() != cAnimationNone;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
618 // because it is 0 (see comments on its declaration in ImageAnimation.h) . 598 // because it is 0 (see comments on its declaration in ImageAnimation.h) .
619 if ((repetitionCount(true) != cAnimationLoopInfinite && m_repetitionsCom plete > m_repetitionCount) 599 if ((repetitionCount(true) != cAnimationLoopInfinite && m_repetitionsCom plete > m_repetitionCount)
620 || (m_animationPolicy == ImageAnimationPolicyAnimateOnce && m_repeti tionsComplete > 0)) { 600 || (m_animationPolicy == ImageAnimationPolicyAnimateOnce && m_repeti tionsComplete > 0)) {
621 m_animationFinished = true; 601 m_animationFinished = true;
622 m_desiredFrameStartTime = 0; 602 m_desiredFrameStartTime = 0;
623 --m_currentFrame; 603 --m_currentFrame;
624 advancedAnimation = false; 604 advancedAnimation = false;
625 } else 605 } else
626 m_currentFrame = 0; 606 m_currentFrame = 0;
627 } 607 }
628 destroyDecodedDataIfNecessary();
629 608
630 // We need to draw this frame if we advanced to it while not skipping, or if 609 // We need to draw this frame if we advanced to it while not skipping, or if
631 // while trying to skip frames we hit the last frame and thus had to stop. 610 // while trying to skip frames we hit the last frame and thus had to stop.
632 if (skippingFrames != advancedAnimation) 611 if (skippingFrames != advancedAnimation)
633 getImageObserver()->animationAdvanced(this); 612 getImageObserver()->animationAdvanced(this);
613
634 return advancedAnimation; 614 return advancedAnimation;
635 } 615 }
636 616
637 } // namespace blink 617 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698