| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) | |
| 3 * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. | |
| 4 * | |
| 5 * Redistribution and use in source and binary forms, with or without | |
| 6 * modification, are permitted provided that the following conditions | |
| 7 * are met: | |
| 8 * 1. Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 * notice, this list of conditions and the following disclaimer in the | |
| 12 * documentation and/or other materials provided with the distribution. | |
| 13 * | |
| 14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY | |
| 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR | |
| 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
| 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
| 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 */ | |
| 26 | |
| 27 #include "config.h" | |
| 28 #include "core/platform/graphics/BitmapImage.h" | |
| 29 | |
| 30 #include "core/platform/graphics/GraphicsContextStateSaver.h" | |
| 31 #include "core/platform/graphics/skia/NativeImageSkia.h" | |
| 32 #include "core/platform/graphics/skia/SkiaUtils.h" | |
| 33 #include "platform/Timer.h" | |
| 34 #include "platform/geometry/FloatRect.h" | |
| 35 #include "platform/graphics/ImageObserver.h" | |
| 36 #include "wtf/CurrentTime.h" | |
| 37 #include "wtf/PassRefPtr.h" | |
| 38 #include "wtf/Vector.h" | |
| 39 #include "wtf/text/WTFString.h" | |
| 40 | |
| 41 namespace WebCore { | |
| 42 | |
| 43 BitmapImage::BitmapImage(ImageObserver* observer) | |
| 44 : Image(observer) | |
| 45 , m_currentFrame(0) | |
| 46 , m_frames(0) | |
| 47 , m_frameTimer(0) | |
| 48 , m_repetitionCount(cAnimationNone) | |
| 49 , m_repetitionCountStatus(Unknown) | |
| 50 , m_repetitionsComplete(0) | |
| 51 , m_desiredFrameStartTime(0) | |
| 52 , m_frameCount(0) | |
| 53 , m_isSolidColor(false) | |
| 54 , m_checkedForSolidColor(false) | |
| 55 , m_animationFinished(false) | |
| 56 , m_allDataReceived(false) | |
| 57 , m_haveSize(false) | |
| 58 , m_sizeAvailable(false) | |
| 59 , m_hasUniformFrameSize(true) | |
| 60 , m_haveFrameCount(false) | |
| 61 { | |
| 62 } | |
| 63 | |
| 64 BitmapImage::BitmapImage(PassRefPtr<NativeImageSkia> nativeImage, ImageObserver*
observer) | |
| 65 : Image(observer) | |
| 66 , m_size(nativeImage->bitmap().width(), nativeImage->bitmap().height()) | |
| 67 , m_currentFrame(0) | |
| 68 , m_frames(0) | |
| 69 , m_frameTimer(0) | |
| 70 , m_repetitionCount(cAnimationNone) | |
| 71 , m_repetitionCountStatus(Unknown) | |
| 72 , m_repetitionsComplete(0) | |
| 73 , m_frameCount(1) | |
| 74 , m_isSolidColor(false) | |
| 75 , m_checkedForSolidColor(false) | |
| 76 , m_animationFinished(true) | |
| 77 , m_allDataReceived(true) | |
| 78 , m_haveSize(true) | |
| 79 , m_sizeAvailable(true) | |
| 80 , m_haveFrameCount(true) | |
| 81 { | |
| 82 // Since we don't have a decoder, we can't figure out the image orientation. | |
| 83 // Set m_sizeRespectingOrientation to be the same as m_size so it's not 0x0. | |
| 84 m_sizeRespectingOrientation = m_size; | |
| 85 | |
| 86 m_frames.grow(1); | |
| 87 m_frames[0].m_hasAlpha = !nativeImage->bitmap().isOpaque(); | |
| 88 m_frames[0].m_frame = nativeImage; | |
| 89 m_frames[0].m_haveMetadata = true; | |
| 90 | |
| 91 checkForSolidColor(); | |
| 92 } | |
| 93 | |
| 94 BitmapImage::~BitmapImage() | |
| 95 { | |
| 96 stopAnimation(); | |
| 97 } | |
| 98 | |
| 99 bool BitmapImage::isBitmapImage() const | |
| 100 { | |
| 101 return true; | |
| 102 } | |
| 103 | |
| 104 void BitmapImage::destroyDecodedData(bool destroyAll) | |
| 105 { | |
| 106 for (size_t i = 0; i < m_frames.size(); ++i) { | |
| 107 // The underlying frame isn't actually changing (we're just trying to | |
| 108 // save the memory for the framebuffer data), so we don't need to clear | |
| 109 // the metadata. | |
| 110 m_frames[i].clear(false); | |
| 111 } | |
| 112 | |
| 113 destroyMetadataAndNotify(m_source.clearCacheExceptFrame(destroyAll ? kNotFou
nd : m_currentFrame)); | |
| 114 } | |
| 115 | |
| 116 void BitmapImage::destroyDecodedDataIfNecessary() | |
| 117 { | |
| 118 // Animated images >5MB are considered large enough that we'll only hang on | |
| 119 // to one frame at a time. | |
| 120 static const size_t cLargeAnimationCutoff = 5242880; | |
| 121 size_t allFrameBytes = 0; | |
| 122 for (size_t i = 0; i < m_frames.size(); ++i) | |
| 123 allFrameBytes += m_frames[i].m_frameBytes; | |
| 124 | |
| 125 if (allFrameBytes > cLargeAnimationCutoff) | |
| 126 destroyDecodedData(false); | |
| 127 } | |
| 128 | |
| 129 void BitmapImage::destroyMetadataAndNotify(size_t frameBytesCleared) | |
| 130 { | |
| 131 m_isSolidColor = false; | |
| 132 m_checkedForSolidColor = false; | |
| 133 | |
| 134 if (frameBytesCleared && imageObserver()) | |
| 135 imageObserver()->decodedSizeChanged(this, -safeCast<int>(frameBytesClear
ed)); | |
| 136 } | |
| 137 | |
| 138 void BitmapImage::cacheFrame(size_t index) | |
| 139 { | |
| 140 size_t numFrames = frameCount(); | |
| 141 if (m_frames.size() < numFrames) | |
| 142 m_frames.grow(numFrames); | |
| 143 | |
| 144 m_frames[index].m_frame = m_source.createFrameAtIndex(index); | |
| 145 if (numFrames == 1 && m_frames[index].m_frame) | |
| 146 checkForSolidColor(); | |
| 147 | |
| 148 m_frames[index].m_orientation = m_source.orientationAtIndex(index); | |
| 149 m_frames[index].m_haveMetadata = true; | |
| 150 m_frames[index].m_isComplete = m_source.frameIsCompleteAtIndex(index); | |
| 151 if (repetitionCount(false) != cAnimationNone) | |
| 152 m_frames[index].m_duration = m_source.frameDurationAtIndex(index); | |
| 153 m_frames[index].m_hasAlpha = m_source.frameHasAlphaAtIndex(index); | |
| 154 m_frames[index].m_frameBytes = m_source.frameBytesAtIndex(index); | |
| 155 | |
| 156 const IntSize frameSize(index ? m_source.frameSizeAtIndex(index) : m_size); | |
| 157 if (frameSize != m_size) | |
| 158 m_hasUniformFrameSize = false; | |
| 159 if (m_frames[index].m_frame) { | |
| 160 int deltaBytes = safeCast<int>(m_frames[index].m_frameBytes); | |
| 161 // The fully-decoded frame will subsume the partially decoded data used | |
| 162 // to determine image properties. | |
| 163 if (imageObserver()) | |
| 164 imageObserver()->decodedSizeChanged(this, deltaBytes); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 void BitmapImage::updateSize() const | |
| 169 { | |
| 170 if (!m_sizeAvailable || m_haveSize) | |
| 171 return; | |
| 172 | |
| 173 m_size = m_source.size(); | |
| 174 m_sizeRespectingOrientation = m_source.size(RespectImageOrientation); | |
| 175 m_haveSize = true; | |
| 176 } | |
| 177 | |
| 178 IntSize BitmapImage::size() const | |
| 179 { | |
| 180 updateSize(); | |
| 181 return m_size; | |
| 182 } | |
| 183 | |
| 184 IntSize BitmapImage::sizeRespectingOrientation() const | |
| 185 { | |
| 186 updateSize(); | |
| 187 return m_sizeRespectingOrientation; | |
| 188 } | |
| 189 | |
| 190 IntSize BitmapImage::currentFrameSize() const | |
| 191 { | |
| 192 if (!m_currentFrame || m_hasUniformFrameSize) | |
| 193 return size(); | |
| 194 IntSize frameSize = m_source.frameSizeAtIndex(m_currentFrame); | |
| 195 return frameSize; | |
| 196 } | |
| 197 | |
| 198 bool BitmapImage::getHotSpot(IntPoint& hotSpot) const | |
| 199 { | |
| 200 bool result = m_source.getHotSpot(hotSpot); | |
| 201 return result; | |
| 202 } | |
| 203 | |
| 204 bool BitmapImage::dataChanged(bool allDataReceived) | |
| 205 { | |
| 206 TRACE_EVENT0("webkit", "BitmapImage::dataChanged"); | |
| 207 | |
| 208 // Clear all partially-decoded frames. For most image formats, there is only | |
| 209 // one frame, but at least GIF and ICO can have more. With GIFs, the frames | |
| 210 // come in order and we ask to decode them in order, waiting to request a | |
| 211 // subsequent frame until the prior one is complete. Given that we clear | |
| 212 // incomplete frames here, this means there is at most one incomplete frame | |
| 213 // (even if we use destroyDecodedData() -- since it doesn't reset the | |
| 214 // metadata), and it is after all the complete frames. | |
| 215 // | |
| 216 // With ICOs, on the other hand, we may ask for arbitrary frames at | |
| 217 // different times (e.g. because we're displaying a higher-resolution image | |
| 218 // in the content area and using a lower-resolution one for the favicon), | |
| 219 // 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 | |
| 221 // 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 | |
| 223 // frame affected by appending new data here. Thus we have to clear all the | |
| 224 // incomplete frames to be safe. | |
| 225 unsigned frameBytesCleared = 0; | |
| 226 for (size_t i = 0; i < m_frames.size(); ++i) { | |
| 227 // NOTE: Don't call frameIsCompleteAtIndex() here, that will try to | |
| 228 // decode any uncached (i.e. never-decoded or | |
| 229 // cleared-on-a-previous-pass) frames! | |
| 230 unsigned frameBytes = m_frames[i].m_frameBytes; | |
| 231 if (m_frames[i].m_haveMetadata && !m_frames[i].m_isComplete) | |
| 232 frameBytesCleared += (m_frames[i].clear(true) ? frameBytes : 0); | |
| 233 } | |
| 234 destroyMetadataAndNotify(frameBytesCleared); | |
| 235 | |
| 236 // Feed all the data we've seen so far to the image decoder. | |
| 237 m_allDataReceived = allDataReceived; | |
| 238 m_source.setData(data(), allDataReceived); | |
| 239 | |
| 240 m_haveFrameCount = false; | |
| 241 m_hasUniformFrameSize = true; | |
| 242 return isSizeAvailable(); | |
| 243 } | |
| 244 | |
| 245 String BitmapImage::filenameExtension() const | |
| 246 { | |
| 247 return m_source.filenameExtension(); | |
| 248 } | |
| 249 | |
| 250 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const Fl
oatRect& srcRect, CompositeOperator compositeOp, blink::WebBlendMode blendMode) | |
| 251 { | |
| 252 draw(ctxt, dstRect, srcRect, compositeOp, blendMode, DoNotRespectImageOrient
ation); | |
| 253 } | |
| 254 | |
| 255 void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const Fl
oatRect& srcRect, CompositeOperator compositeOp, blink::WebBlendMode blendMode,
RespectImageOrientationEnum shouldRespectImageOrientation) | |
| 256 { | |
| 257 // Spin the animation to the correct frame before we try to draw it, so we | |
| 258 // don't draw an old frame and then immediately need to draw a newer one, | |
| 259 // causing flicker and wasting CPU. | |
| 260 startAnimation(); | |
| 261 | |
| 262 RefPtr<NativeImageSkia> bm = nativeImageForCurrentFrame(); | |
| 263 if (!bm) | |
| 264 return; // It's too early and we don't have an image yet. | |
| 265 | |
| 266 FloatRect normDstRect = adjustForNegativeSize(dstRect); | |
| 267 FloatRect normSrcRect = adjustForNegativeSize(srcRect); | |
| 268 normSrcRect.intersect(FloatRect(0, 0, bm->bitmap().width(), bm->bitmap().hei
ght())); | |
| 269 | |
| 270 if (normSrcRect.isEmpty() || normDstRect.isEmpty()) | |
| 271 return; // Nothing to draw. | |
| 272 | |
| 273 ImageOrientation orientation = DefaultImageOrientation; | |
| 274 if (shouldRespectImageOrientation == RespectImageOrientation) | |
| 275 orientation = frameOrientationAtIndex(m_currentFrame); | |
| 276 | |
| 277 GraphicsContextStateSaver saveContext(*ctxt, false); | |
| 278 if (orientation != DefaultImageOrientation) { | |
| 279 saveContext.save(); | |
| 280 | |
| 281 // ImageOrientation expects the origin to be at (0, 0) | |
| 282 ctxt->translate(normDstRect.x(), normDstRect.y()); | |
| 283 normDstRect.setLocation(FloatPoint()); | |
| 284 | |
| 285 ctxt->concatCTM(orientation.transformFromDefault(normDstRect.size())); | |
| 286 | |
| 287 if (orientation.usesWidthAsHeight()) { | |
| 288 // The destination rect will have it's width and height already reve
rsed for the orientation of | |
| 289 // the image, as it was needed for page layout, so we need to revers
e it back here. | |
| 290 normDstRect = FloatRect(normDstRect.x(), normDstRect.y(), normDstRec
t.height(), normDstRect.width()); | |
| 291 } | |
| 292 } | |
| 293 | |
| 294 bm->draw(ctxt, normSrcRect, normDstRect, WebCoreCompositeToSkiaComposite(com
positeOp, blendMode)); | |
| 295 | |
| 296 if (ImageObserver* observer = imageObserver()) | |
| 297 observer->didDraw(this); | |
| 298 } | |
| 299 | |
| 300 size_t BitmapImage::frameCount() | |
| 301 { | |
| 302 if (!m_haveFrameCount) { | |
| 303 m_frameCount = m_source.frameCount(); | |
| 304 // If decoder is not initialized yet, m_source.frameCount() returns 0. | |
| 305 if (m_frameCount) { | |
| 306 m_haveFrameCount = true; | |
| 307 } | |
| 308 } | |
| 309 return m_frameCount; | |
| 310 } | |
| 311 | |
| 312 bool BitmapImage::isSizeAvailable() | |
| 313 { | |
| 314 if (m_sizeAvailable) | |
| 315 return true; | |
| 316 | |
| 317 m_sizeAvailable = m_source.isSizeAvailable(); | |
| 318 | |
| 319 return m_sizeAvailable; | |
| 320 } | |
| 321 | |
| 322 bool BitmapImage::ensureFrameIsCached(size_t index) | |
| 323 { | |
| 324 if (index >= frameCount()) | |
| 325 return false; | |
| 326 | |
| 327 if (index >= m_frames.size() || !m_frames[index].m_frame) | |
| 328 cacheFrame(index); | |
| 329 return true; | |
| 330 } | |
| 331 | |
| 332 PassRefPtr<NativeImageSkia> BitmapImage::frameAtIndex(size_t index) | |
| 333 { | |
| 334 if (!ensureFrameIsCached(index)) | |
| 335 return 0; | |
| 336 return m_frames[index].m_frame; | |
| 337 } | |
| 338 | |
| 339 bool BitmapImage::frameIsCompleteAtIndex(size_t index) | |
| 340 { | |
| 341 if (index < m_frames.size() && m_frames[index].m_haveMetadata && m_frames[in
dex].m_isComplete) | |
| 342 return true; | |
| 343 return m_source.frameIsCompleteAtIndex(index); | |
| 344 } | |
| 345 | |
| 346 float BitmapImage::frameDurationAtIndex(size_t index) | |
| 347 { | |
| 348 if (index < m_frames.size() && m_frames[index].m_haveMetadata) | |
| 349 return m_frames[index].m_duration; | |
| 350 return m_source.frameDurationAtIndex(index); | |
| 351 } | |
| 352 | |
| 353 PassRefPtr<NativeImageSkia> BitmapImage::nativeImageForCurrentFrame() | |
| 354 { | |
| 355 return frameAtIndex(currentFrame()); | |
| 356 } | |
| 357 | |
| 358 bool BitmapImage::frameHasAlphaAtIndex(size_t index) | |
| 359 { | |
| 360 if (m_frames.size() <= index) | |
| 361 return true; | |
| 362 | |
| 363 if (m_frames[index].m_haveMetadata) | |
| 364 return m_frames[index].m_hasAlpha; | |
| 365 | |
| 366 return m_source.frameHasAlphaAtIndex(index); | |
| 367 } | |
| 368 | |
| 369 bool BitmapImage::currentFrameKnownToBeOpaque() | |
| 370 { | |
| 371 return !frameHasAlphaAtIndex(currentFrame()); | |
| 372 } | |
| 373 | |
| 374 ImageOrientation BitmapImage::currentFrameOrientation() | |
| 375 { | |
| 376 return frameOrientationAtIndex(currentFrame()); | |
| 377 } | |
| 378 | |
| 379 ImageOrientation BitmapImage::frameOrientationAtIndex(size_t index) | |
| 380 { | |
| 381 if (m_frames.size() <= index) | |
| 382 return DefaultImageOrientation; | |
| 383 | |
| 384 if (m_frames[index].m_haveMetadata) | |
| 385 return m_frames[index].m_orientation; | |
| 386 | |
| 387 return m_source.orientationAtIndex(index); | |
| 388 } | |
| 389 | |
| 390 #if !ASSERT_DISABLED | |
| 391 bool BitmapImage::notSolidColor() | |
| 392 { | |
| 393 return size().width() != 1 || size().height() != 1 || frameCount() > 1; | |
| 394 } | |
| 395 #endif | |
| 396 | |
| 397 | |
| 398 | |
| 399 int BitmapImage::repetitionCount(bool imageKnownToBeComplete) | |
| 400 { | |
| 401 if ((m_repetitionCountStatus == Unknown) || ((m_repetitionCountStatus == Unc
ertain) && imageKnownToBeComplete)) { | |
| 402 // Snag the repetition count. If |imageKnownToBeComplete| is false, the | |
| 403 // repetition count may not be accurate yet for GIFs; in this case the | |
| 404 // decoder will default to cAnimationLoopOnce, and we'll try and read | |
| 405 // the count again once the whole image is decoded. | |
| 406 m_repetitionCount = m_source.repetitionCount(); | |
| 407 m_repetitionCountStatus = (imageKnownToBeComplete || m_repetitionCount =
= cAnimationNone) ? Certain : Uncertain; | |
| 408 } | |
| 409 return m_repetitionCount; | |
| 410 } | |
| 411 | |
| 412 bool BitmapImage::shouldAnimate() | |
| 413 { | |
| 414 return (repetitionCount(false) != cAnimationNone && !m_animationFinished &&
imageObserver()); | |
| 415 } | |
| 416 | |
| 417 void BitmapImage::startAnimation(bool catchUpIfNecessary) | |
| 418 { | |
| 419 if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) | |
| 420 return; | |
| 421 | |
| 422 // If we aren't already animating, set now as the animation start time. | |
| 423 const double time = monotonicallyIncreasingTime(); | |
| 424 if (!m_desiredFrameStartTime) | |
| 425 m_desiredFrameStartTime = time; | |
| 426 | |
| 427 // Don't advance the animation to an incomplete frame. | |
| 428 size_t nextFrame = (m_currentFrame + 1) % frameCount(); | |
| 429 if (!m_allDataReceived && !frameIsCompleteAtIndex(nextFrame)) | |
| 430 return; | |
| 431 | |
| 432 // Don't advance past the last frame if we haven't decoded the whole image | |
| 433 // yet and our repetition count is potentially unset. The repetition count | |
| 434 // in a GIF can potentially come after all the rest of the image data, so | |
| 435 // wait on it. | |
| 436 if (!m_allDataReceived && repetitionCount(false) == cAnimationLoopOnce && m_
currentFrame >= (frameCount() - 1)) | |
| 437 return; | |
| 438 | |
| 439 // Determine time for next frame to start. By ignoring paint and timer lag | |
| 440 // in this calculation, we make the animation appear to run at its desired | |
| 441 // rate regardless of how fast it's being repainted. | |
| 442 const double currentDuration = frameDurationAtIndex(m_currentFrame); | |
| 443 m_desiredFrameStartTime += currentDuration; | |
| 444 | |
| 445 // When an animated image is more than five minutes out of date, the | |
| 446 // user probably doesn't care about resyncing and we could burn a lot of | |
| 447 // time looping through frames below. Just reset the timings. | |
| 448 const double cAnimationResyncCutoff = 5 * 60; | |
| 449 if ((time - m_desiredFrameStartTime) > cAnimationResyncCutoff) | |
| 450 m_desiredFrameStartTime = time + currentDuration; | |
| 451 | |
| 452 // The image may load more slowly than it's supposed to animate, so that by | |
| 453 // the time we reach the end of the first repetition, we're well behind. | |
| 454 // Clamp the desired frame start time in this case, so that we don't skip | |
| 455 // frames (or whole iterations) trying to "catch up". This is a tradeoff: | |
| 456 // It guarantees users see the whole animation the second time through and | |
| 457 // don't miss any repetitions, and is closer to what other browsers do; on | |
| 458 // the other hand, it makes animations "less accurate" for pages that try to | |
| 459 // sync an image and some other resource (e.g. audio), especially if users | |
| 460 // switch tabs (and thus stop drawing the animation, which will pause it) | |
| 461 // during that initial loop, then switch back later. | |
| 462 if (nextFrame == 0 && m_repetitionsComplete == 0 && m_desiredFrameStartTime
< time) | |
| 463 m_desiredFrameStartTime = time; | |
| 464 | |
| 465 if (!catchUpIfNecessary || time < m_desiredFrameStartTime) { | |
| 466 // Haven't yet reached time for next frame to start; delay until then. | |
| 467 m_frameTimer = new Timer<BitmapImage>(this, &BitmapImage::advanceAnimati
on); | |
| 468 m_frameTimer->startOneShot(std::max(m_desiredFrameStartTime - time, 0.))
; | |
| 469 } else { | |
| 470 // We've already reached or passed the time for the next frame to start. | |
| 471 // See if we've also passed the time for frames after that to start, in | |
| 472 // case we need to skip some frames entirely. Remember not to advance | |
| 473 // to an incomplete frame. | |
| 474 for (size_t frameAfterNext = (nextFrame + 1) % frameCount(); frameIsComp
leteAtIndex(frameAfterNext); frameAfterNext = (nextFrame + 1) % frameCount()) { | |
| 475 // Should we skip the next frame? | |
| 476 double frameAfterNextStartTime = m_desiredFrameStartTime + frameDura
tionAtIndex(nextFrame); | |
| 477 if (time < frameAfterNextStartTime) | |
| 478 break; | |
| 479 | |
| 480 // Yes; skip over it without notifying our observers. | |
| 481 if (!internalAdvanceAnimation(true)) | |
| 482 return; | |
| 483 m_desiredFrameStartTime = frameAfterNextStartTime; | |
| 484 nextFrame = frameAfterNext; | |
| 485 } | |
| 486 | |
| 487 // Draw the next frame immediately. Note that m_desiredFrameStartTime | |
| 488 // may be in the past, meaning the next time through this function we'll | |
| 489 // kick off the next advancement sooner than this frame's duration would | |
| 490 // suggest. | |
| 491 if (internalAdvanceAnimation(false)) { | |
| 492 // The image region has been marked dirty, but once we return to our | |
| 493 // caller, draw() will clear it, and nothing will cause the | |
| 494 // animation to advance again. We need to start the timer for the | |
| 495 // next frame running, or the animation can hang. (Compare this | |
| 496 // with when advanceAnimation() is called, and the region is dirtied | |
| 497 // while draw() is not in the callstack, meaning draw() gets called | |
| 498 // to update the region and thus startAnimation() is reached again.) | |
| 499 // NOTE: For large images with slow or heavily-loaded systems, | |
| 500 // throwing away data as we go (see destroyDecodedData()) means we | |
| 501 // can spend so much time re-decoding data above that by the time we | |
| 502 // reach here we're behind again. If we let startAnimation() run | |
| 503 // the catch-up code again, we can get long delays without painting | |
| 504 // as we race the timer, or even infinite recursion. In this | |
| 505 // situation the best we can do is to simply change frames as fast | |
| 506 // as possible, so force startAnimation() to set a zero-delay timer | |
| 507 // and bail out if we're not caught up. | |
| 508 startAnimation(false); | |
| 509 } | |
| 510 } | |
| 511 } | |
| 512 | |
| 513 void BitmapImage::stopAnimation() | |
| 514 { | |
| 515 // This timer is used to animate all occurrences of this image. Don't inval
idate | |
| 516 // the timer unless all renderers have stopped drawing. | |
| 517 delete m_frameTimer; | |
| 518 m_frameTimer = 0; | |
| 519 } | |
| 520 | |
| 521 void BitmapImage::resetAnimation() | |
| 522 { | |
| 523 stopAnimation(); | |
| 524 m_currentFrame = 0; | |
| 525 m_repetitionsComplete = 0; | |
| 526 m_desiredFrameStartTime = 0; | |
| 527 m_animationFinished = false; | |
| 528 | |
| 529 // For extremely large animations, when the animation is reset, we just thro
w everything away. | |
| 530 destroyDecodedDataIfNecessary(); | |
| 531 } | |
| 532 | |
| 533 void BitmapImage::advanceAnimation(Timer<BitmapImage>*) | |
| 534 { | |
| 535 internalAdvanceAnimation(false); | |
| 536 // At this point the image region has been marked dirty, and if it's | |
| 537 // onscreen, we'll soon make a call to draw(), which will call | |
| 538 // startAnimation() again to keep the animation moving. | |
| 539 } | |
| 540 | |
| 541 bool BitmapImage::internalAdvanceAnimation(bool skippingFrames) | |
| 542 { | |
| 543 // Stop the animation. | |
| 544 stopAnimation(); | |
| 545 | |
| 546 // See if anyone is still paying attention to this animation. If not, we do
n't | |
| 547 // advance and will remain suspended at the current frame until the animatio
n is resumed. | |
| 548 if (!skippingFrames && imageObserver()->shouldPauseAnimation(this)) | |
| 549 return false; | |
| 550 | |
| 551 ++m_currentFrame; | |
| 552 bool advancedAnimation = true; | |
| 553 if (m_currentFrame >= frameCount()) { | |
| 554 ++m_repetitionsComplete; | |
| 555 | |
| 556 // Get the repetition count again. If we weren't able to get a | |
| 557 // repetition count before, we should have decoded the whole image by | |
| 558 // now, so it should now be available. | |
| 559 // Note that we don't need to special-case cAnimationLoopOnce here | |
| 560 // because it is 0 (see comments on its declaration in ImageSource.h). | |
| 561 if (repetitionCount(true) != cAnimationLoopInfinite && m_repetitionsComp
lete > m_repetitionCount) { | |
| 562 m_animationFinished = true; | |
| 563 m_desiredFrameStartTime = 0; | |
| 564 --m_currentFrame; | |
| 565 advancedAnimation = false; | |
| 566 } else | |
| 567 m_currentFrame = 0; | |
| 568 } | |
| 569 destroyDecodedDataIfNecessary(); | |
| 570 | |
| 571 // We need to draw this frame if we advanced to it while not skipping, or if | |
| 572 // while trying to skip frames we hit the last frame and thus had to stop. | |
| 573 if (skippingFrames != advancedAnimation) | |
| 574 imageObserver()->animationAdvanced(this); | |
| 575 return advancedAnimation; | |
| 576 } | |
| 577 | |
| 578 void BitmapImage::checkForSolidColor() | |
| 579 { | |
| 580 m_isSolidColor = false; | |
| 581 m_checkedForSolidColor = true; | |
| 582 | |
| 583 if (frameCount() > 1) | |
| 584 return; | |
| 585 | |
| 586 RefPtr<NativeImageSkia> frame = frameAtIndex(0); | |
| 587 | |
| 588 if (frame && size().width() == 1 && size().height() == 1) { | |
| 589 SkAutoLockPixels lock(frame->bitmap()); | |
| 590 if (!frame->bitmap().getPixels()) | |
| 591 return; | |
| 592 | |
| 593 m_isSolidColor = true; | |
| 594 m_solidColor = Color(frame->bitmap().getColor(0, 0)); | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 bool BitmapImage::mayFillWithSolidColor() | |
| 599 { | |
| 600 if (!m_checkedForSolidColor && frameCount() > 0) { | |
| 601 checkForSolidColor(); | |
| 602 ASSERT(m_checkedForSolidColor); | |
| 603 } | |
| 604 return m_isSolidColor && !m_currentFrame; | |
| 605 } | |
| 606 | |
| 607 Color BitmapImage::solidColor() const | |
| 608 { | |
| 609 return m_solidColor; | |
| 610 } | |
| 611 | |
| 612 } | |
| OLD | NEW |