FrameAnimator.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. * This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "FrameAnimator.h"
  6. #include "mozilla/MemoryReporting.h"
  7. #include "mozilla/Move.h"
  8. #include "imgIContainer.h"
  9. #include "LookupResult.h"
  10. #include "MainThreadUtils.h"
  11. #include "RasterImage.h"
  12. #include "pixman.h"
  13. namespace mozilla {
  14. using namespace gfx;
  15. namespace image {
  16. ///////////////////////////////////////////////////////////////////////////////
  17. // AnimationState implementation.
  18. ///////////////////////////////////////////////////////////////////////////////
  19. void
  20. AnimationState::SetDoneDecoding(bool aDone)
  21. {
  22. mDoneDecoding = aDone;
  23. }
  24. void
  25. AnimationState::ResetAnimation()
  26. {
  27. mCurrentAnimationFrameIndex = 0;
  28. }
  29. void
  30. AnimationState::SetAnimationMode(uint16_t aAnimationMode)
  31. {
  32. mAnimationMode = aAnimationMode;
  33. }
  34. void
  35. AnimationState::UpdateKnownFrameCount(uint32_t aFrameCount)
  36. {
  37. if (aFrameCount <= mFrameCount) {
  38. // Nothing to do. Since we can redecode animated images, we may see the same
  39. // sequence of updates replayed again, so seeing a smaller frame count than
  40. // what we already know about doesn't indicate an error.
  41. return;
  42. }
  43. MOZ_ASSERT(!mDoneDecoding, "Adding new frames after decoding is finished?");
  44. MOZ_ASSERT(aFrameCount <= mFrameCount + 1, "Skipped a frame?");
  45. mFrameCount = aFrameCount;
  46. }
  47. Maybe<uint32_t>
  48. AnimationState::FrameCount() const
  49. {
  50. return mDoneDecoding ? Some(mFrameCount) : Nothing();
  51. }
  52. void
  53. AnimationState::SetFirstFrameRefreshArea(const IntRect& aRefreshArea)
  54. {
  55. mFirstFrameRefreshArea = aRefreshArea;
  56. }
  57. void
  58. AnimationState::InitAnimationFrameTimeIfNecessary()
  59. {
  60. if (mCurrentAnimationFrameTime.IsNull()) {
  61. mCurrentAnimationFrameTime = TimeStamp::Now();
  62. }
  63. }
  64. void
  65. AnimationState::SetAnimationFrameTime(const TimeStamp& aTime)
  66. {
  67. mCurrentAnimationFrameTime = aTime;
  68. }
  69. uint32_t
  70. AnimationState::GetCurrentAnimationFrameIndex() const
  71. {
  72. return mCurrentAnimationFrameIndex;
  73. }
  74. FrameTimeout
  75. AnimationState::LoopLength() const
  76. {
  77. // If we don't know the loop length yet, we have to treat it as infinite.
  78. if (!mLoopLength) {
  79. return FrameTimeout::Forever();
  80. }
  81. MOZ_ASSERT(mDoneDecoding, "We know the loop length but decoding isn't done?");
  82. // If we're not looping, a single loop time has no meaning.
  83. if (mAnimationMode != imgIContainer::kNormalAnimMode) {
  84. return FrameTimeout::Forever();
  85. }
  86. return *mLoopLength;
  87. }
  88. ///////////////////////////////////////////////////////////////////////////////
  89. // FrameAnimator implementation.
  90. ///////////////////////////////////////////////////////////////////////////////
  91. TimeStamp
  92. FrameAnimator::GetCurrentImgFrameEndTime(AnimationState& aState) const
  93. {
  94. TimeStamp currentFrameTime = aState.mCurrentAnimationFrameTime;
  95. FrameTimeout timeout = GetTimeoutForFrame(aState.mCurrentAnimationFrameIndex);
  96. if (timeout == FrameTimeout::Forever()) {
  97. // We need to return a sentinel value in this case, because our logic
  98. // doesn't work correctly if we have an infinitely long timeout. We use one
  99. // year in the future as the sentinel because it works with the loop in
  100. // RequestRefresh() below.
  101. // XXX(seth): It'd be preferable to make our logic work correctly with
  102. // infinitely long timeouts.
  103. return TimeStamp::NowLoRes() +
  104. TimeDuration::FromMilliseconds(31536000.0);
  105. }
  106. TimeDuration durationOfTimeout =
  107. TimeDuration::FromMilliseconds(double(timeout.AsMilliseconds()));
  108. TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
  109. return currentFrameEndTime;
  110. }
  111. RefreshResult
  112. FrameAnimator::AdvanceFrame(AnimationState& aState, TimeStamp aTime)
  113. {
  114. NS_ASSERTION(aTime <= TimeStamp::Now(),
  115. "Given time appears to be in the future");
  116. PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
  117. RefreshResult ret;
  118. // Determine what the next frame is, taking into account looping.
  119. uint32_t currentFrameIndex = aState.mCurrentAnimationFrameIndex;
  120. uint32_t nextFrameIndex = currentFrameIndex + 1;
  121. // Check if we're at the end of the loop. (FrameCount() returns Nothing() if
  122. // we don't know the total count yet.)
  123. if (aState.FrameCount() == Some(nextFrameIndex)) {
  124. // If we are not looping forever, initialize the loop counter
  125. if (aState.mLoopRemainingCount < 0 && aState.LoopCount() >= 0) {
  126. aState.mLoopRemainingCount = aState.LoopCount();
  127. }
  128. // If animation mode is "loop once", or we're at end of loop counter,
  129. // it's time to stop animating.
  130. if (aState.mAnimationMode == imgIContainer::kLoopOnceAnimMode ||
  131. aState.mLoopRemainingCount == 0) {
  132. ret.mAnimationFinished = true;
  133. }
  134. nextFrameIndex = 0;
  135. if (aState.mLoopRemainingCount > 0) {
  136. aState.mLoopRemainingCount--;
  137. }
  138. // If we're done, exit early.
  139. if (ret.mAnimationFinished) {
  140. return ret;
  141. }
  142. }
  143. if (nextFrameIndex >= aState.KnownFrameCount()) {
  144. // We've already advanced to the last decoded frame, nothing more we can do.
  145. // We're blocked by network/decoding from displaying the animation at the
  146. // rate specified, so that means the frame we are displaying (the latest
  147. // available) is the frame we want to be displaying at this time. So we
  148. // update the current animation time. If we didn't update the current
  149. // animation time then it could lag behind, which would indicate that we are
  150. // behind in the animation and should try to catch up. When we are done
  151. // decoding (and thus can loop around back to the start of the animation) we
  152. // would then jump to a random point in the animation to try to catch up.
  153. // But we were never behind in the animation.
  154. aState.mCurrentAnimationFrameTime = aTime;
  155. return ret;
  156. }
  157. // There can be frames in the surface cache with index >= KnownFrameCount()
  158. // which GetRawFrame() can access because an async decoder has decoded them,
  159. // but which AnimationState doesn't know about yet because we haven't received
  160. // the appropriate notification on the main thread. Make sure we stay in sync
  161. // with AnimationState.
  162. MOZ_ASSERT(nextFrameIndex < aState.KnownFrameCount());
  163. RawAccessFrameRef nextFrame = GetRawFrame(nextFrameIndex);
  164. // We should always check to see if we have the next frame even if we have
  165. // previously finished decoding. If we needed to redecode (e.g. due to a draw
  166. // failure) we would have discarded all the old frames and may not yet have
  167. // the new ones.
  168. if (!nextFrame || !nextFrame->IsFinished()) {
  169. // Uh oh, the frame we want to show is currently being decoded (partial)
  170. // Wait until the next refresh driver tick and try again
  171. return ret;
  172. }
  173. if (GetTimeoutForFrame(nextFrameIndex) == FrameTimeout::Forever()) {
  174. ret.mAnimationFinished = true;
  175. }
  176. if (nextFrameIndex == 0) {
  177. ret.mDirtyRect = aState.FirstFrameRefreshArea();
  178. } else {
  179. MOZ_ASSERT(nextFrameIndex == currentFrameIndex + 1);
  180. // Change frame
  181. if (!DoBlend(&ret.mDirtyRect, currentFrameIndex, nextFrameIndex)) {
  182. // something went wrong, move on to next
  183. NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
  184. nextFrame->SetCompositingFailed(true);
  185. aState.mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime(aState);
  186. aState.mCurrentAnimationFrameIndex = nextFrameIndex;
  187. return ret;
  188. }
  189. nextFrame->SetCompositingFailed(false);
  190. }
  191. aState.mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime(aState);
  192. // If we can get closer to the current time by a multiple of the image's loop
  193. // time, we should. We can only do this if we're done decoding; otherwise, we
  194. // don't know the full loop length, and LoopLength() will have to return
  195. // FrameTimeout::Forever().
  196. FrameTimeout loopTime = aState.LoopLength();
  197. if (loopTime != FrameTimeout::Forever()) {
  198. TimeDuration delay = aTime - aState.mCurrentAnimationFrameTime;
  199. if (delay.ToMilliseconds() > loopTime.AsMilliseconds()) {
  200. // Explicitly use integer division to get the floor of the number of
  201. // loops.
  202. uint64_t loops = static_cast<uint64_t>(delay.ToMilliseconds())
  203. / loopTime.AsMilliseconds();
  204. aState.mCurrentAnimationFrameTime +=
  205. TimeDuration::FromMilliseconds(loops * loopTime.AsMilliseconds());
  206. }
  207. }
  208. // Set currentAnimationFrameIndex at the last possible moment
  209. aState.mCurrentAnimationFrameIndex = nextFrameIndex;
  210. // If we're here, we successfully advanced the frame.
  211. ret.mFrameAdvanced = true;
  212. return ret;
  213. }
  214. RefreshResult
  215. FrameAnimator::RequestRefresh(AnimationState& aState, const TimeStamp& aTime)
  216. {
  217. // only advance the frame if the current time is greater than or
  218. // equal to the current frame's end time.
  219. TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
  220. // By default, an empty RefreshResult.
  221. RefreshResult ret;
  222. while (currentFrameEndTime <= aTime) {
  223. TimeStamp oldFrameEndTime = currentFrameEndTime;
  224. RefreshResult frameRes = AdvanceFrame(aState, aTime);
  225. // Accumulate our result for returning to callers.
  226. ret.Accumulate(frameRes);
  227. currentFrameEndTime = GetCurrentImgFrameEndTime(aState);
  228. // If we didn't advance a frame, and our frame end time didn't change,
  229. // then we need to break out of this loop & wait for the frame(s)
  230. // to finish downloading.
  231. if (!frameRes.mFrameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
  232. break;
  233. }
  234. }
  235. return ret;
  236. }
  237. LookupResult
  238. FrameAnimator::GetCompositedFrame(uint32_t aFrameNum)
  239. {
  240. // If we have a composited version of this frame, return that.
  241. if (mLastCompositedFrameIndex == int32_t(aFrameNum)) {
  242. return LookupResult(DrawableSurface(mCompositingFrame->DrawableRef()),
  243. MatchType::EXACT);
  244. }
  245. // Otherwise return the raw frame. DoBlend is required to ensure that we only
  246. // hit this case if the frame is not paletted and doesn't require compositing.
  247. LookupResult result =
  248. SurfaceCache::Lookup(ImageKey(mImage),
  249. RasterSurfaceKey(mSize,
  250. DefaultSurfaceFlags(),
  251. PlaybackType::eAnimated));
  252. if (!result) {
  253. return result;
  254. }
  255. // Seek to the appropriate frame. If seeking fails, it means that we couldn't
  256. // get the frame we're looking for; treat this as if the lookup failed.
  257. if (NS_FAILED(result.Surface().Seek(aFrameNum))) {
  258. return LookupResult(MatchType::NOT_FOUND);
  259. }
  260. MOZ_ASSERT(!result.Surface()->GetIsPaletted(),
  261. "About to return a paletted frame");
  262. return result;
  263. }
  264. FrameTimeout
  265. FrameAnimator::GetTimeoutForFrame(uint32_t aFrameNum) const
  266. {
  267. RawAccessFrameRef frame = GetRawFrame(aFrameNum);
  268. if (frame) {
  269. AnimationData data = frame->GetAnimationData();
  270. return data.mTimeout;
  271. }
  272. NS_WARNING("No frame; called GetTimeoutForFrame too early?");
  273. return FrameTimeout::FromRawMilliseconds(100);
  274. }
  275. static void
  276. DoCollectSizeOfCompositingSurfaces(const RawAccessFrameRef& aSurface,
  277. SurfaceMemoryCounterType aType,
  278. nsTArray<SurfaceMemoryCounter>& aCounters,
  279. MallocSizeOf aMallocSizeOf)
  280. {
  281. // Concoct a SurfaceKey for this surface.
  282. SurfaceKey key = RasterSurfaceKey(aSurface->GetImageSize(),
  283. DefaultSurfaceFlags(),
  284. PlaybackType::eStatic);
  285. // Create a counter for this surface.
  286. SurfaceMemoryCounter counter(key, /* aIsLocked = */ true, aType);
  287. // Extract the surface's memory usage information.
  288. size_t heap = 0, nonHeap = 0;
  289. aSurface->AddSizeOfExcludingThis(aMallocSizeOf, heap, nonHeap);
  290. counter.Values().SetDecodedHeap(heap);
  291. counter.Values().SetDecodedNonHeap(nonHeap);
  292. // Record it.
  293. aCounters.AppendElement(counter);
  294. }
  295. void
  296. FrameAnimator::CollectSizeOfCompositingSurfaces(
  297. nsTArray<SurfaceMemoryCounter>& aCounters,
  298. MallocSizeOf aMallocSizeOf) const
  299. {
  300. if (mCompositingFrame) {
  301. DoCollectSizeOfCompositingSurfaces(mCompositingFrame,
  302. SurfaceMemoryCounterType::COMPOSITING,
  303. aCounters,
  304. aMallocSizeOf);
  305. }
  306. if (mCompositingPrevFrame) {
  307. DoCollectSizeOfCompositingSurfaces(mCompositingPrevFrame,
  308. SurfaceMemoryCounterType::COMPOSITING_PREV,
  309. aCounters,
  310. aMallocSizeOf);
  311. }
  312. }
  313. RawAccessFrameRef
  314. FrameAnimator::GetRawFrame(uint32_t aFrameNum) const
  315. {
  316. LookupResult result =
  317. SurfaceCache::Lookup(ImageKey(mImage),
  318. RasterSurfaceKey(mSize,
  319. DefaultSurfaceFlags(),
  320. PlaybackType::eAnimated));
  321. if (!result) {
  322. return RawAccessFrameRef();
  323. }
  324. // Seek to the frame we want. If seeking fails, it means we couldn't get the
  325. // frame we're looking for, so we bail here to avoid returning the wrong frame
  326. // to the caller.
  327. if (NS_FAILED(result.Surface().Seek(aFrameNum))) {
  328. return RawAccessFrameRef(); // Not available yet.
  329. }
  330. return result.Surface()->RawAccessRef();
  331. }
  332. //******************************************************************************
  333. // DoBlend gets called when the timer for animation get fired and we have to
  334. // update the composited frame of the animation.
  335. bool
  336. FrameAnimator::DoBlend(IntRect* aDirtyRect,
  337. uint32_t aPrevFrameIndex,
  338. uint32_t aNextFrameIndex)
  339. {
  340. RawAccessFrameRef prevFrame = GetRawFrame(aPrevFrameIndex);
  341. RawAccessFrameRef nextFrame = GetRawFrame(aNextFrameIndex);
  342. MOZ_ASSERT(prevFrame && nextFrame, "Should have frames here");
  343. AnimationData prevFrameData = prevFrame->GetAnimationData();
  344. if (prevFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS &&
  345. !mCompositingPrevFrame) {
  346. prevFrameData.mDisposalMethod = DisposalMethod::CLEAR;
  347. }
  348. IntRect prevRect = prevFrameData.mBlendRect
  349. ? prevFrameData.mRect.Intersect(*prevFrameData.mBlendRect)
  350. : prevFrameData.mRect;
  351. bool isFullPrevFrame = prevRect.x == 0 && prevRect.y == 0 &&
  352. prevRect.width == mSize.width &&
  353. prevRect.height == mSize.height;
  354. // Optimization: DisposeClearAll if the previous frame is the same size as
  355. // container and it's clearing itself
  356. if (isFullPrevFrame &&
  357. (prevFrameData.mDisposalMethod == DisposalMethod::CLEAR)) {
  358. prevFrameData.mDisposalMethod = DisposalMethod::CLEAR_ALL;
  359. }
  360. AnimationData nextFrameData = nextFrame->GetAnimationData();
  361. IntRect nextRect = nextFrameData.mBlendRect
  362. ? nextFrameData.mRect.Intersect(*nextFrameData.mBlendRect)
  363. : nextFrameData.mRect;
  364. bool isFullNextFrame = nextRect.x == 0 && nextRect.y == 0 &&
  365. nextRect.width == mSize.width &&
  366. nextRect.height == mSize.height;
  367. if (!nextFrame->GetIsPaletted()) {
  368. // Optimization: Skip compositing if the previous frame wants to clear the
  369. // whole image
  370. if (prevFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL) {
  371. aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
  372. return true;
  373. }
  374. // Optimization: Skip compositing if this frame is the same size as the
  375. // container and it's fully drawing over prev frame (no alpha)
  376. if (isFullNextFrame &&
  377. (nextFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) &&
  378. !nextFrameData.mHasAlpha) {
  379. aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
  380. return true;
  381. }
  382. }
  383. // Calculate area that needs updating
  384. switch (prevFrameData.mDisposalMethod) {
  385. default:
  386. MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
  387. case DisposalMethod::NOT_SPECIFIED:
  388. case DisposalMethod::KEEP:
  389. *aDirtyRect = nextRect;
  390. break;
  391. case DisposalMethod::CLEAR_ALL:
  392. // Whole image container is cleared
  393. aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
  394. break;
  395. case DisposalMethod::CLEAR:
  396. // Calc area that needs to be redrawn (the combination of previous and
  397. // this frame)
  398. // XXX - This could be done with multiple framechanged calls
  399. // Having prevFrame way at the top of the image, and nextFrame
  400. // way at the bottom, and both frames being small, we'd be
  401. // telling framechanged to refresh the whole image when only two
  402. // small areas are needed.
  403. aDirtyRect->UnionRect(nextRect, prevRect);
  404. break;
  405. case DisposalMethod::RESTORE_PREVIOUS:
  406. aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
  407. break;
  408. }
  409. // Optimization:
  410. // Skip compositing if the last composited frame is this frame
  411. // (Only one composited frame was made for this animation. Example:
  412. // Only Frame 3 of a 10 frame image required us to build a composite frame
  413. // On the second loop, we do not need to rebuild the frame
  414. // since it's still sitting in compositingFrame)
  415. if (mLastCompositedFrameIndex == int32_t(aNextFrameIndex)) {
  416. return true;
  417. }
  418. bool needToBlankComposite = false;
  419. // Create the Compositing Frame
  420. if (!mCompositingFrame) {
  421. RefPtr<imgFrame> newFrame = new imgFrame;
  422. nsresult rv = newFrame->InitForDecoder(mSize,
  423. SurfaceFormat::B8G8R8A8);
  424. if (NS_FAILED(rv)) {
  425. mCompositingFrame.reset();
  426. return false;
  427. }
  428. mCompositingFrame = newFrame->RawAccessRef();
  429. needToBlankComposite = true;
  430. } else if (int32_t(aNextFrameIndex) != mLastCompositedFrameIndex+1) {
  431. // If we are not drawing on top of last composited frame,
  432. // then we are building a new composite frame, so let's clear it first.
  433. needToBlankComposite = true;
  434. }
  435. AnimationData compositingFrameData = mCompositingFrame->GetAnimationData();
  436. // More optimizations possible when next frame is not transparent
  437. // But if the next frame has DisposalMethod::RESTORE_PREVIOUS,
  438. // this "no disposal" optimization is not possible,
  439. // because the frame in "after disposal operation" state
  440. // needs to be stored in compositingFrame, so it can be
  441. // copied into compositingPrevFrame later.
  442. bool doDisposal = true;
  443. if (!nextFrameData.mHasAlpha &&
  444. nextFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) {
  445. if (isFullNextFrame) {
  446. // Optimization: No need to dispose prev.frame when
  447. // next frame is full frame and not transparent.
  448. doDisposal = false;
  449. // No need to blank the composite frame
  450. needToBlankComposite = false;
  451. } else {
  452. if ((prevRect.x >= nextRect.x) && (prevRect.y >= nextRect.y) &&
  453. (prevRect.x + prevRect.width <= nextRect.x + nextRect.width) &&
  454. (prevRect.y + prevRect.height <= nextRect.y + nextRect.height)) {
  455. // Optimization: No need to dispose prev.frame when
  456. // next frame fully overlaps previous frame.
  457. doDisposal = false;
  458. }
  459. }
  460. }
  461. if (doDisposal) {
  462. // Dispose of previous: clear, restore, or keep (copy)
  463. switch (prevFrameData.mDisposalMethod) {
  464. case DisposalMethod::CLEAR:
  465. if (needToBlankComposite) {
  466. // If we just created the composite, it could have anything in its
  467. // buffer. Clear whole frame
  468. ClearFrame(compositingFrameData.mRawData,
  469. compositingFrameData.mRect);
  470. } else {
  471. // Only blank out previous frame area (both color & Mask/Alpha)
  472. ClearFrame(compositingFrameData.mRawData,
  473. compositingFrameData.mRect,
  474. prevRect);
  475. }
  476. break;
  477. case DisposalMethod::CLEAR_ALL:
  478. ClearFrame(compositingFrameData.mRawData,
  479. compositingFrameData.mRect);
  480. break;
  481. case DisposalMethod::RESTORE_PREVIOUS:
  482. // It would be better to copy only the area changed back to
  483. // compositingFrame.
  484. if (mCompositingPrevFrame) {
  485. AnimationData compositingPrevFrameData =
  486. mCompositingPrevFrame->GetAnimationData();
  487. CopyFrameImage(compositingPrevFrameData.mRawData,
  488. compositingPrevFrameData.mRect,
  489. compositingFrameData.mRawData,
  490. compositingFrameData.mRect);
  491. // destroy only if we don't need it for this frame's disposal
  492. if (nextFrameData.mDisposalMethod !=
  493. DisposalMethod::RESTORE_PREVIOUS) {
  494. mCompositingPrevFrame.reset();
  495. }
  496. } else {
  497. ClearFrame(compositingFrameData.mRawData,
  498. compositingFrameData.mRect);
  499. }
  500. break;
  501. default:
  502. MOZ_FALLTHROUGH_ASSERT("Unexpected DisposalMethod");
  503. case DisposalMethod::NOT_SPECIFIED:
  504. case DisposalMethod::KEEP:
  505. // Copy previous frame into compositingFrame before we put the new
  506. // frame on top
  507. // Assumes that the previous frame represents a full frame (it could be
  508. // smaller in size than the container, as long as the frame before it
  509. // erased itself)
  510. // Note: Frame 1 never gets into DoBlend(), so (aNextFrameIndex - 1)
  511. // will always be a valid frame number.
  512. if (mLastCompositedFrameIndex != int32_t(aNextFrameIndex - 1)) {
  513. if (isFullPrevFrame && !prevFrame->GetIsPaletted()) {
  514. // Just copy the bits
  515. CopyFrameImage(prevFrameData.mRawData,
  516. prevRect,
  517. compositingFrameData.mRawData,
  518. compositingFrameData.mRect);
  519. } else {
  520. if (needToBlankComposite) {
  521. // Only blank composite when prev is transparent or not full.
  522. if (prevFrameData.mHasAlpha || !isFullPrevFrame) {
  523. ClearFrame(compositingFrameData.mRawData,
  524. compositingFrameData.mRect);
  525. }
  526. }
  527. DrawFrameTo(prevFrameData.mRawData, prevFrameData.mRect,
  528. prevFrameData.mPaletteDataLength,
  529. prevFrameData.mHasAlpha,
  530. compositingFrameData.mRawData,
  531. compositingFrameData.mRect,
  532. prevFrameData.mBlendMethod,
  533. prevFrameData.mBlendRect);
  534. }
  535. }
  536. }
  537. } else if (needToBlankComposite) {
  538. // If we just created the composite, it could have anything in its
  539. // buffers. Clear them
  540. ClearFrame(compositingFrameData.mRawData,
  541. compositingFrameData.mRect);
  542. }
  543. // Check if the frame we are composing wants the previous image restored after
  544. // it is done. Don't store it (again) if last frame wanted its image restored
  545. // too
  546. if ((nextFrameData.mDisposalMethod == DisposalMethod::RESTORE_PREVIOUS) &&
  547. (prevFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS)) {
  548. // We are storing the whole image.
  549. // It would be better if we just stored the area that nextFrame is going to
  550. // overwrite.
  551. if (!mCompositingPrevFrame) {
  552. RefPtr<imgFrame> newFrame = new imgFrame;
  553. nsresult rv = newFrame->InitForDecoder(mSize,
  554. SurfaceFormat::B8G8R8A8);
  555. if (NS_FAILED(rv)) {
  556. mCompositingPrevFrame.reset();
  557. return false;
  558. }
  559. mCompositingPrevFrame = newFrame->RawAccessRef();
  560. }
  561. AnimationData compositingPrevFrameData =
  562. mCompositingPrevFrame->GetAnimationData();
  563. CopyFrameImage(compositingFrameData.mRawData,
  564. compositingFrameData.mRect,
  565. compositingPrevFrameData.mRawData,
  566. compositingPrevFrameData.mRect);
  567. mCompositingPrevFrame->Finish();
  568. }
  569. // blit next frame into it's correct spot
  570. DrawFrameTo(nextFrameData.mRawData, nextFrameData.mRect,
  571. nextFrameData.mPaletteDataLength,
  572. nextFrameData.mHasAlpha,
  573. compositingFrameData.mRawData,
  574. compositingFrameData.mRect,
  575. nextFrameData.mBlendMethod,
  576. nextFrameData.mBlendRect);
  577. // Tell the image that it is fully 'downloaded'.
  578. mCompositingFrame->Finish();
  579. mLastCompositedFrameIndex = int32_t(aNextFrameIndex);
  580. return true;
  581. }
  582. //******************************************************************************
  583. // Fill aFrame with black. Does also clears the mask.
  584. void
  585. FrameAnimator::ClearFrame(uint8_t* aFrameData, const IntRect& aFrameRect)
  586. {
  587. if (!aFrameData) {
  588. return;
  589. }
  590. memset(aFrameData, 0, aFrameRect.width * aFrameRect.height * 4);
  591. }
  592. //******************************************************************************
  593. void
  594. FrameAnimator::ClearFrame(uint8_t* aFrameData, const IntRect& aFrameRect,
  595. const IntRect& aRectToClear)
  596. {
  597. if (!aFrameData || aFrameRect.width <= 0 || aFrameRect.height <= 0 ||
  598. aRectToClear.width <= 0 || aRectToClear.height <= 0) {
  599. return;
  600. }
  601. IntRect toClear = aFrameRect.Intersect(aRectToClear);
  602. if (toClear.IsEmpty()) {
  603. return;
  604. }
  605. uint32_t bytesPerRow = aFrameRect.width * 4;
  606. for (int row = toClear.y; row < toClear.y + toClear.height; ++row) {
  607. memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0,
  608. toClear.width * 4);
  609. }
  610. }
  611. //******************************************************************************
  612. // Whether we succeed or fail will not cause a crash, and there's not much
  613. // we can do about a failure, so there we don't return a nsresult
  614. bool
  615. FrameAnimator::CopyFrameImage(const uint8_t* aDataSrc,
  616. const IntRect& aRectSrc,
  617. uint8_t* aDataDest,
  618. const IntRect& aRectDest)
  619. {
  620. uint32_t dataLengthSrc = aRectSrc.width * aRectSrc.height * 4;
  621. uint32_t dataLengthDest = aRectDest.width * aRectDest.height * 4;
  622. if (!aDataDest || !aDataSrc || dataLengthSrc != dataLengthDest) {
  623. return false;
  624. }
  625. memcpy(aDataDest, aDataSrc, dataLengthDest);
  626. return true;
  627. }
  628. nsresult
  629. FrameAnimator::DrawFrameTo(const uint8_t* aSrcData, const IntRect& aSrcRect,
  630. uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
  631. uint8_t* aDstPixels, const IntRect& aDstRect,
  632. BlendMethod aBlendMethod, const Maybe<IntRect>& aBlendRect)
  633. {
  634. NS_ENSURE_ARG_POINTER(aSrcData);
  635. NS_ENSURE_ARG_POINTER(aDstPixels);
  636. // According to both AGIF and APNG specs, offsets are unsigned
  637. if (aSrcRect.x < 0 || aSrcRect.y < 0) {
  638. NS_WARNING("FrameAnimator::DrawFrameTo: negative offsets not allowed");
  639. return NS_ERROR_FAILURE;
  640. }
  641. // Outside the destination frame, skip it
  642. if ((aSrcRect.x > aDstRect.width) || (aSrcRect.y > aDstRect.height)) {
  643. return NS_OK;
  644. }
  645. if (aSrcPaletteLength) {
  646. // Larger than the destination frame, clip it
  647. int32_t width = std::min(aSrcRect.width, aDstRect.width - aSrcRect.x);
  648. int32_t height = std::min(aSrcRect.height, aDstRect.height - aSrcRect.y);
  649. // The clipped image must now fully fit within destination image frame
  650. NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
  651. (aSrcRect.x + width <= aDstRect.width) &&
  652. (aSrcRect.y + height <= aDstRect.height),
  653. "FrameAnimator::DrawFrameTo: Invalid aSrcRect");
  654. // clipped image size may be smaller than source, but not larger
  655. NS_ASSERTION((width <= aSrcRect.width) && (height <= aSrcRect.height),
  656. "FrameAnimator::DrawFrameTo: source must be smaller than dest");
  657. // Get pointers to image data
  658. const uint8_t* srcPixels = aSrcData + aSrcPaletteLength;
  659. uint32_t* dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
  660. const uint32_t* colormap = reinterpret_cast<const uint32_t*>(aSrcData);
  661. // Skip to the right offset
  662. dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.width);
  663. if (!aSrcHasAlpha) {
  664. for (int32_t r = height; r > 0; --r) {
  665. for (int32_t c = 0; c < width; c++) {
  666. dstPixels[c] = colormap[srcPixels[c]];
  667. }
  668. // Go to the next row in the source resp. destination image
  669. srcPixels += aSrcRect.width;
  670. dstPixels += aDstRect.width;
  671. }
  672. } else {
  673. for (int32_t r = height; r > 0; --r) {
  674. for (int32_t c = 0; c < width; c++) {
  675. const uint32_t color = colormap[srcPixels[c]];
  676. if (color) {
  677. dstPixels[c] = color;
  678. }
  679. }
  680. // Go to the next row in the source resp. destination image
  681. srcPixels += aSrcRect.width;
  682. dstPixels += aDstRect.width;
  683. }
  684. }
  685. } else {
  686. pixman_image_t* src =
  687. pixman_image_create_bits(
  688. aSrcHasAlpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
  689. aSrcRect.width, aSrcRect.height,
  690. reinterpret_cast<uint32_t*>(const_cast<uint8_t*>(aSrcData)),
  691. aSrcRect.width * 4);
  692. if (!src) {
  693. return NS_ERROR_OUT_OF_MEMORY;
  694. }
  695. pixman_image_t* dst =
  696. pixman_image_create_bits(PIXMAN_a8r8g8b8,
  697. aDstRect.width,
  698. aDstRect.height,
  699. reinterpret_cast<uint32_t*>(aDstPixels),
  700. aDstRect.width * 4);
  701. if (!dst) {
  702. pixman_image_unref(src);
  703. return NS_ERROR_OUT_OF_MEMORY;
  704. }
  705. // XXX(seth): This is inefficient but we'll remove it quite soon when we
  706. // move frame compositing into SurfacePipe. For now we need this because
  707. // RemoveFrameRectFilter has transformed PNG frames with frame rects into
  708. // imgFrame's with no frame rects, but with a region of 0 alpha where the
  709. // frame rect should be. This works really nicely if we're using
  710. // BlendMethod::OVER, but BlendMethod::SOURCE will result in that frame rect
  711. // area overwriting the previous frame, which makes the animation look
  712. // wrong. This quick hack fixes that by first compositing the whle new frame
  713. // with BlendMethod::OVER, and then recopying the area that uses
  714. // BlendMethod::SOURCE if needed. To make this work, the decoder has to
  715. // provide a "blend rect" that tells us where to do this. This is just the
  716. // frame rect, but hidden in a way that makes it invisible to most of the
  717. // system, so we can keep eliminating dependencies on it.
  718. auto op = aBlendMethod == BlendMethod::SOURCE ? PIXMAN_OP_SRC
  719. : PIXMAN_OP_OVER;
  720. if (aBlendMethod == BlendMethod::OVER || !aBlendRect ||
  721. (aBlendMethod == BlendMethod::SOURCE && aSrcRect.IsEqualEdges(*aBlendRect))) {
  722. // We don't need to do anything clever. (Or, in the case where no blend
  723. // rect was specified, we can't.)
  724. pixman_image_composite32(op,
  725. src,
  726. nullptr,
  727. dst,
  728. 0, 0,
  729. 0, 0,
  730. aSrcRect.x, aSrcRect.y,
  731. aSrcRect.width, aSrcRect.height);
  732. } else {
  733. // We need to do the OVER followed by SOURCE trick above.
  734. pixman_image_composite32(PIXMAN_OP_OVER,
  735. src,
  736. nullptr,
  737. dst,
  738. 0, 0,
  739. 0, 0,
  740. aSrcRect.x, aSrcRect.y,
  741. aSrcRect.width, aSrcRect.height);
  742. pixman_image_composite32(PIXMAN_OP_SRC,
  743. src,
  744. nullptr,
  745. dst,
  746. aBlendRect->x, aBlendRect->y,
  747. 0, 0,
  748. aBlendRect->x, aBlendRect->y,
  749. aBlendRect->width, aBlendRect->height);
  750. }
  751. pixman_image_unref(src);
  752. pixman_image_unref(dst);
  753. }
  754. return NS_OK;
  755. }
  756. } // namespace image
  757. } // namespace mozilla