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

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: Rebasing. DCHECK. Thanks fmalita@. 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_haveFrameCount(false) 71 , m_haveFrameCount(false)
71 { 72 {
72 } 73 }
73 74
74 BitmapImage::BitmapImage(const SkBitmap& bitmap, ImageObserver* observer) 75 BitmapImage::BitmapImage(const SkBitmap& bitmap, ImageObserver* observer)
75 : Image(observer) 76 : Image(observer)
76 , m_size(bitmap.width(), bitmap.height()) 77 , m_size(bitmap.width(), bitmap.height())
77 , m_currentFrame(0) 78 , m_currentFrame(0)
79 , m_cachedFrame(adoptRef(SkImage::NewFromBitmap(bitmap)))
80 , m_cachedFrameIndex(0)
78 , m_repetitionCount(cAnimationNone) 81 , m_repetitionCount(cAnimationNone)
79 , m_repetitionCountStatus(Unknown) 82 , m_repetitionCountStatus(Unknown)
80 , m_repetitionsComplete(0) 83 , m_repetitionsComplete(0)
81 , m_frameCount(1) 84 , m_frameCount(1)
82 , m_animationPolicy(ImageAnimationPolicyAllowed) 85 , m_animationPolicy(ImageAnimationPolicyAllowed)
83 , m_animationFinished(true) 86 , m_animationFinished(true)
84 , m_allDataReceived(true) 87 , m_allDataReceived(true)
85 , m_haveSize(true) 88 , m_haveSize(true)
86 , m_sizeAvailable(true) 89 , m_sizeAvailable(true)
87 , m_haveFrameCount(true) 90 , m_haveFrameCount(true)
88 { 91 {
89 // Since we don't have a decoder, we can't figure out the image orientation. 92 // Since we don't have a decoder, we can't figure out the image orientation.
90 // Set m_sizeRespectingOrientation to be the same as m_size so it's not 0x0. 93 // Set m_sizeRespectingOrientation to be the same as m_size so it's not 0x0.
91 m_sizeRespectingOrientation = m_size; 94 m_sizeRespectingOrientation = m_size;
92 95
93 m_frames.grow(1); 96 m_frames.grow(1);
94 m_frames[0].m_hasAlpha = !bitmap.isOpaque(); 97 m_frames[0].m_hasAlpha = !bitmap.isOpaque();
95 m_frames[0].m_frame = adoptRef(SkImage::NewFromBitmap(bitmap));
96 m_frames[0].m_haveMetadata = true; 98 m_frames[0].m_haveMetadata = true;
97 } 99 }
98 100
99 BitmapImage::~BitmapImage() 101 BitmapImage::~BitmapImage()
100 { 102 {
101 stopAnimation(); 103 stopAnimation();
102 } 104 }
103 105
104 bool BitmapImage::currentFrameHasSingleSecurityOrigin() const 106 bool BitmapImage::currentFrameHasSingleSecurityOrigin() const
105 { 107 {
106 return true; 108 return true;
107 } 109 }
108 110
109 void BitmapImage::destroyDecodedData(bool destroyAll) 111 void BitmapImage::destroyDecodedData()
110 { 112 {
111 for (size_t i = 0; i < m_frames.size(); ++i) { 113 m_cachedFrame.clear();
112 // The underlying frame isn't actually changing (we're just trying to 114 for (size_t i = 0; i < m_frames.size(); ++i)
113 // save the memory for the framebuffer data), so we don't need to clear 115 m_frames[i].clear(true);
114 // the metadata. 116 m_source.clearCacheExceptFrame(kNotFound);
115 m_frames[i].clear(false);
116 }
117
118 m_source.clearCacheExceptFrame(destroyAll ? kNotFound : m_currentFrame);
119 notifyMemoryChanged(); 117 notifyMemoryChanged();
120 } 118 }
121 119
122 void BitmapImage::destroyDecodedDataIfNecessary()
123 {
124 // Animated images >5MB are considered large enough that we'll only hang on
125 // to one frame at a time.
126 static const size_t cLargeAnimationCutoff = 5242880;
127 size_t allFrameBytes = 0;
128 for (size_t i = 0; i < m_frames.size(); ++i)
129 allFrameBytes += m_frames[i].m_frameBytes;
130
131 if (allFrameBytes > cLargeAnimationCutoff) {
132 destroyDecodedData(false);
133 }
134 }
135
136 void BitmapImage::notifyMemoryChanged() 120 void BitmapImage::notifyMemoryChanged()
137 { 121 {
138 if (getImageObserver()) 122 if (getImageObserver())
139 getImageObserver()->decodedSizeChangedTo(this, totalFrameBytes()); 123 getImageObserver()->decodedSizeChangedTo(this, totalFrameBytes());
140 } 124 }
141 125
142 size_t BitmapImage::totalFrameBytes() 126 size_t BitmapImage::totalFrameBytes()
143 { 127 {
144 const size_t numFrames = frameCount(); 128 const size_t numFrames = frameCount();
145 size_t totalBytes = 0; 129 size_t totalBytes = 0;
146 for (size_t i = 0; i < numFrames; ++i) 130 for (size_t i = 0; i < numFrames; ++i)
147 totalBytes += m_source.frameBytesAtIndex(i); 131 totalBytes += m_source.frameBytesAtIndex(i);
148 return totalBytes; 132 return totalBytes;
149 } 133 }
150 134
151 void BitmapImage::cacheFrame(size_t index) 135 PassRefPtr<SkImage> BitmapImage::decodeAndCacheFrame(size_t index)
152 { 136 {
153 size_t numFrames = frameCount(); 137 size_t numFrames = frameCount();
154 if (m_frames.size() < numFrames) 138 if (m_frames.size() < numFrames)
155 m_frames.grow(numFrames); 139 m_frames.grow(numFrames);
156 140
157 // We are caching frame snapshots. This is OK even for partially decoded fr ames, 141 // We are caching frame snapshots. This is OK even for partially decoded fr ames,
158 // as they are cleared by dataChanged() when new data arrives. 142 // as they are cleared by dataChanged() when new data arrives.
159 m_frames[index].m_frame = m_source.createFrameAtIndex(index); 143 RefPtr<SkImage> image = m_source.createFrameAtIndex(index);
144 m_cachedFrame = image;
145 m_cachedFrameIndex = index;
160 146
161 m_frames[index].m_orientation = m_source.orientationAtIndex(index); 147 m_frames[index].m_orientation = m_source.orientationAtIndex(index);
162 m_frames[index].m_haveMetadata = true; 148 m_frames[index].m_haveMetadata = true;
163 m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index); 149 m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index);
164 if (repetitionCount(false) != cAnimationNone) 150 if (repetitionCount(false) != cAnimationNone)
165 m_frames[index].m_duration = m_source.frameDurationAtIndex(index); 151 m_frames[index].m_duration = m_source.frameDurationAtIndex(index);
166 m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index); 152 m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index);
167 m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index); 153 m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index);
168 154
169 notifyMemoryChanged(); 155 notifyMemoryChanged();
156 return image.release();
170 } 157 }
171 158
172 void BitmapImage::updateSize() const 159 void BitmapImage::updateSize() const
173 { 160 {
174 if (!m_sizeAvailable || m_haveSize) 161 if (!m_sizeAvailable || m_haveSize)
175 return; 162 return;
176 163
177 m_size = m_source.size(); 164 m_size = m_source.size();
178 m_sizeRespectingOrientation = m_source.size(RespectImageOrientation); 165 m_sizeRespectingOrientation = m_source.size(RespectImageOrientation);
179 m_haveSize = true; 166 m_haveSize = true;
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 // and the frames aren't even guaranteed to appear in the file in the same 201 // and the frames aren't even guaranteed to appear in the file in the same
215 // order as in the directory, so an arbitrary number of the frames might be 202 // order as in the directory, so an arbitrary number of the frames might be
216 // incomplete (if we ask for frames for which we've not yet reached the 203 // incomplete (if we ask for frames for which we've not yet reached the
217 // start of the frame data), and any or none of them might be the particular 204 // start of the frame data), and any or none of them might be the particular
218 // frame affected by appending new data here. Thus we have to clear all the 205 // frame affected by appending new data here. Thus we have to clear all the
219 // incomplete frames to be safe. 206 // incomplete frames to be safe.
220 for (size_t i = 0; i < m_frames.size(); ++i) { 207 for (size_t i = 0; i < m_frames.size(); ++i) {
221 // NOTE: Don't call frameIsCompleteAtIndex() here, that will try to 208 // NOTE: Don't call frameIsCompleteAtIndex() here, that will try to
222 // decode any uncached (i.e. never-decoded or 209 // decode any uncached (i.e. never-decoded or
223 // cleared-on-a-previous-pass) frames! 210 // cleared-on-a-previous-pass) frames!
224 if (m_frames[i].m_haveMetadata && !m_frames[i].m_isComplete) 211 if (m_frames[i].m_haveMetadata && !m_frames[i].m_isComplete) {
225 m_frames[i].clear(true); 212 m_frames[i].clear(true);
213 if (i == m_cachedFrameIndex)
214 m_cachedFrame.clear();
215 }
226 } 216 }
227 217
228 // Feed all the data we've seen so far to the image decoder. 218 // Feed all the data we've seen so far to the image decoder.
229 m_allDataReceived = allDataReceived; 219 m_allDataReceived = allDataReceived;
230 ASSERT(data()); 220 ASSERT(data());
231 m_source.setData(*data(), allDataReceived); 221 m_source.setData(*data(), allDataReceived);
232 222
233 m_haveFrameCount = false; 223 m_haveFrameCount = false;
234 return isSizeAvailable(); 224 return isSizeAvailable();
235 } 225 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
276 if (orientation.usesWidthAsHeight()) { 266 if (orientation.usesWidthAsHeight()) {
277 // The destination rect will have it's width and height already reve rsed for the orientation of 267 // The destination rect will have it's width and height already reve rsed for the orientation of
278 // the image, as it was needed for page layout, so we need to revers e it back here. 268 // the image, as it was needed for page layout, so we need to revers e it back here.
279 adjustedDstRect = FloatRect(adjustedDstRect.x(), adjustedDstRect.y() , adjustedDstRect.height(), adjustedDstRect.width()); 269 adjustedDstRect = FloatRect(adjustedDstRect.x(), adjustedDstRect.y() , adjustedDstRect.height(), adjustedDstRect.width());
280 } 270 }
281 } 271 }
282 272
283 canvas->drawImageRect(image.get(), adjustedSrcRect, adjustedDstRect, &paint, 273 canvas->drawImageRect(image.get(), adjustedSrcRect, adjustedDstRect, &paint,
284 WebCoreClampingModeToSkiaRectConstraint(clampMode)); 274 WebCoreClampingModeToSkiaRectConstraint(clampMode));
285 275
286 if (currentFrameIsLazyDecoded()) 276 if (image->isLazyGenerated())
287 PlatformInstrumentation::didDrawLazyPixelRef(image->uniqueID()); 277 PlatformInstrumentation::didDrawLazyPixelRef(image->uniqueID());
288 278
289 if (ImageObserver* observer = getImageObserver()) 279 if (ImageObserver* observer = getImageObserver())
290 observer->didDraw(this); 280 observer->didDraw(this);
291 281
292 startAnimation(); 282 startAnimation();
293 } 283 }
294 284
295 size_t BitmapImage::frameCount() 285 size_t BitmapImage::frameCount()
296 { 286 {
(...skipping 21 matching lines...) Expand all
318 308
319 if (m_sizeAvailable && hasVisibleImageSize(size())) { 309 if (m_sizeAvailable && hasVisibleImageSize(size())) {
320 BitmapImageMetrics::countDecodedImageType(m_source.filenameExtension()); 310 BitmapImageMetrics::countDecodedImageType(m_source.filenameExtension());
321 if (m_source.filenameExtension() == "jpg") 311 if (m_source.filenameExtension() == "jpg")
322 BitmapImageMetrics::countImageOrientation(m_source.orientationAtInde x(0).orientation()); 312 BitmapImageMetrics::countImageOrientation(m_source.orientationAtInde x(0).orientation());
323 } 313 }
324 314
325 return m_sizeAvailable; 315 return m_sizeAvailable;
326 } 316 }
327 317
328 bool BitmapImage::ensureFrameIsCached(size_t index)
329 {
330 if (index >= frameCount())
331 return false;
332
333 if (index >= m_frames.size() || !m_frames[index].m_frame)
334 cacheFrame(index);
335
336 return true;
337 }
338
339 PassRefPtr<SkImage> BitmapImage::frameAtIndex(size_t index) 318 PassRefPtr<SkImage> BitmapImage::frameAtIndex(size_t index)
340 { 319 {
341 if (!ensureFrameIsCached(index)) 320 if (index >= frameCount())
342 return nullptr; 321 return nullptr;
343 322
344 return m_frames[index].m_frame; 323 if (index == m_cachedFrameIndex && m_cachedFrame)
324 return m_cachedFrame;
325
326 return decodeAndCacheFrame(index);
345 } 327 }
346 328
347 bool BitmapImage::frameIsCompleteAtIndex(size_t index) 329 bool BitmapImage::frameIsCompleteAtIndex(size_t index)
348 { 330 {
349 if (index < m_frames.size() && m_frames[index].m_haveMetadata && m_frames[in dex].m_isComplete) 331 if (index < m_frames.size() && m_frames[index].m_haveMetadata && m_frames[in dex].m_isComplete)
350 return true; 332 return true;
351 333
352 return m_source.frameIsCompleteAtIndex(index); 334 return m_source.frameIsCompleteAtIndex(index);
353 } 335 }
354 336
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 m_frameTimer.clear(); 532 m_frameTimer.clear();
551 } 533 }
552 534
553 void BitmapImage::resetAnimation() 535 void BitmapImage::resetAnimation()
554 { 536 {
555 stopAnimation(); 537 stopAnimation();
556 m_currentFrame = 0; 538 m_currentFrame = 0;
557 m_repetitionsComplete = 0; 539 m_repetitionsComplete = 0;
558 m_desiredFrameStartTime = 0; 540 m_desiredFrameStartTime = 0;
559 m_animationFinished = false; 541 m_animationFinished = false;
560 542 m_cachedFrame.clear();
561 // For extremely large animations, when the animation is reset, we just thro w everything away.
562 destroyDecodedDataIfNecessary();
563 } 543 }
564 544
565 bool BitmapImage::maybeAnimated() 545 bool BitmapImage::maybeAnimated()
566 { 546 {
567 if (m_animationFinished) 547 if (m_animationFinished)
568 return false; 548 return false;
569 if (frameCount() > 1) 549 if (frameCount() > 1)
570 return true; 550 return true;
571 551
572 return m_source.repetitionCount() != cAnimationNone; 552 return m_source.repetitionCount() != cAnimationNone;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
610 // because it is 0 (see comments on its declaration in ImageAnimation.h) . 590 // because it is 0 (see comments on its declaration in ImageAnimation.h) .
611 if ((repetitionCount(true) != cAnimationLoopInfinite && m_repetitionsCom plete > m_repetitionCount) 591 if ((repetitionCount(true) != cAnimationLoopInfinite && m_repetitionsCom plete > m_repetitionCount)
612 || (m_animationPolicy == ImageAnimationPolicyAnimateOnce && m_repeti tionsComplete > 0)) { 592 || (m_animationPolicy == ImageAnimationPolicyAnimateOnce && m_repeti tionsComplete > 0)) {
613 m_animationFinished = true; 593 m_animationFinished = true;
614 m_desiredFrameStartTime = 0; 594 m_desiredFrameStartTime = 0;
615 --m_currentFrame; 595 --m_currentFrame;
616 advancedAnimation = false; 596 advancedAnimation = false;
617 } else 597 } else
618 m_currentFrame = 0; 598 m_currentFrame = 0;
619 } 599 }
620 destroyDecodedDataIfNecessary();
621 600
622 // We need to draw this frame if we advanced to it while not skipping, or if 601 // We need to draw this frame if we advanced to it while not skipping, or if
623 // while trying to skip frames we hit the last frame and thus had to stop. 602 // while trying to skip frames we hit the last frame and thus had to stop.
624 if (skippingFrames != advancedAnimation) 603 if (skippingFrames != advancedAnimation)
625 getImageObserver()->animationAdvanced(this); 604 getImageObserver()->animationAdvanced(this);
605
626 return advancedAnimation; 606 return advancedAnimation;
627 } 607 }
628 608
629 } // namespace blink 609 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698