Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  TestAnimationFrameBuffer.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include <utility>

#include "AnimationFrameBuffer.h"
#include "Common.h"
#include "gtest/gtest.h"

using namespace mozilla;
using namespace mozilla::image;

static already_AddRefed<imgFrame> CreateEmptyFrame(
    const gfx::IntSize& aSize = gfx::IntSize(1, 1),
    const gfx::IntRect& aFrameRect = gfx::IntRect(0, 0, 1, 1),
    bool aCanRecycle = true) {
  RefPtr<imgFrame> frame = new imgFrame();
  AnimationParams animParams{aFrameRect, FrameTimeout::Forever(),
                             /* aFrameNum */ 1, BlendMethod::OVER,
                             DisposalMethod::NOT_SPECIFIED};
  nsresult rv =
      frame->InitForDecoder(aSize, mozilla::gfx::SurfaceFormat::OS_RGBA, false,
                            Some(animParams), aCanRecycle);
  EXPECT_NS_SUCCEEDED(rv);
  RawAccessFrameRef frameRef = frame->RawAccessRef();
  // Normally the blend animation filter would set the dirty rect, but since
  // we aren't producing an actual animation here, we need to fake it.
  frame->SetDirtyRect(aFrameRect);
  frame->Finish();
  return frame.forget();
}

static bool ReinitForRecycle(RawAccessFrameRef& aFrame) {
  if (!aFrame) {
    return false;
  }

  AnimationParams animParams{aFrame->GetRect(), FrameTimeout::Forever(),
                             /* aFrameNum */ 1, BlendMethod::OVER,
                             DisposalMethod::NOT_SPECIFIED};
  return NS_SUCCEEDED(aFrame->InitForDecoderRecycle(animParams));
}

static void PrepareForDiscardingQueue(AnimationFrameRetainedBuffer& aQueue) {
  ASSERT_EQ(size_t(0), aQueue.Size());
  ASSERT_LT(size_t(1), aQueue.Batch());

  AnimationFrameBuffer::InsertStatus status = aQueue.Insert(CreateEmptyFrame());
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);

  while (true) {
    status = aQueue.Insert(CreateEmptyFrame());
    bool restartDecoder = aQueue.AdvanceTo(aQueue.Size() - 1);
    EXPECT_FALSE(restartDecoder);

    if (status == AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE) {
      break;
    }
    EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);
  }

  EXPECT_EQ(aQueue.Threshold(), aQueue.Size());
}

static void VerifyDiscardingQueueContents(
    AnimationFrameDiscardingQueue& aQueue) {
  auto frames = aQueue.Display();
  for (auto i : frames) {
    EXPECT_TRUE(i != nullptr);
  }
}

static void VerifyInsertInternal(AnimationFrameBuffer& aQueue,
                                 imgFrame* aFrame) {
  // Determine the frame index where we just inserted the frame.
  size_t frameIndex;
  if (aQueue.MayDiscard()) {
    const AnimationFrameDiscardingQueue& queue =
        *static_cast<AnimationFrameDiscardingQueue*>(&aQueue);
    frameIndex = queue.PendingInsert() == 0 ? queue.Size() - 1
                                            : queue.PendingInsert() - 1;
  } else {
    ASSERT_FALSE(aQueue.SizeKnown());
    frameIndex = aQueue.Size() - 1;
  }

  // Make sure we can get the frame from that index.
  RefPtr<imgFrame> frame = aQueue.Get(frameIndex, false);
  EXPECT_EQ(aFrame, frame.get());
}

static void VerifyAdvance(AnimationFrameBuffer& aQueue, size_t aExpectedFrame,
                          bool aExpectedRestartDecoder) {
  RefPtr<imgFrame> oldFrame;
  size_t totalRecycled;
  if (aQueue.IsRecycling()) {
    AnimationFrameRecyclingQueue& queue =
        *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
    oldFrame = queue.Get(queue.Displayed(), false);
    totalRecycled = queue.Recycle().size();
  }

  bool restartDecoder = aQueue.AdvanceTo(aExpectedFrame);
  EXPECT_EQ(aExpectedRestartDecoder, restartDecoder);

  if (aQueue.IsRecycling()) {
    const AnimationFrameRecyclingQueue& queue =
        *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
    EXPECT_FALSE(queue.Recycle().back().mDirtyRect.IsEmpty());
    EXPECT_TRUE(
        queue.Recycle().back().mDirtyRect.Contains(oldFrame->GetDirtyRect()));
    EXPECT_EQ(totalRecycled + 1, queue.Recycle().size());
    EXPECT_EQ(oldFrame.get(), queue.Recycle().back().mFrame.get());
  }
}

static void VerifyInsertAndAdvance(
    AnimationFrameBuffer& aQueue, size_t aExpectedFrame,
    AnimationFrameBuffer::InsertStatus aExpectedStatus) {
  // Insert the decoded frame.
  RefPtr<imgFrame> frame = CreateEmptyFrame();
  AnimationFrameBuffer::InsertStatus status =
      aQueue.Insert(RefPtr<imgFrame>(frame));
  EXPECT_EQ(aExpectedStatus, status);
  EXPECT_TRUE(aQueue.IsLastInsertedFrame(frame));
  VerifyInsertInternal(aQueue, frame);

  // Advance the display frame.
  bool expectedRestartDecoder =
      aExpectedStatus == AnimationFrameBuffer::InsertStatus::YIELD;
  VerifyAdvance(aQueue, aExpectedFrame, expectedRestartDecoder);
}

static void VerifyMarkComplete(
    AnimationFrameBuffer& aQueue, bool aExpectedContinue,
    const gfx::IntRect& aRefreshArea = gfx::IntRect(0, 0, 1, 1)) {
  if (aQueue.IsRecycling() && !aQueue.SizeKnown()) {
    const AnimationFrameRecyclingQueue& queue =
        *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
    EXPECT_EQ(queue.FirstFrame()->GetRect(), queue.FirstFrameRefreshArea());
  }

  bool keepDecoding = aQueue.MarkComplete(aRefreshArea);
  EXPECT_EQ(aExpectedContinue, keepDecoding);

  if (aQueue.IsRecycling()) {
    const AnimationFrameRecyclingQueue& queue =
        *static_cast<AnimationFrameRecyclingQueue*>(&aQueue);
    EXPECT_EQ(aRefreshArea, queue.FirstFrameRefreshArea());
  }
}

static void VerifyInsert(AnimationFrameBuffer& aQueue,
                         AnimationFrameBuffer::InsertStatus aExpectedStatus) {
  RefPtr<imgFrame> frame = CreateEmptyFrame();
  AnimationFrameBuffer::InsertStatus status =
      aQueue.Insert(RefPtr<imgFrame>(frame));
  EXPECT_EQ(aExpectedStatus, status);
  EXPECT_TRUE(aQueue.IsLastInsertedFrame(frame));
  VerifyInsertInternal(aQueue, frame);
}

static void VerifyReset(AnimationFrameBuffer& aQueue, bool aExpectedContinue,
                        const imgFrame* aFirstFrame) {
  bool keepDecoding = aQueue.Reset();
  EXPECT_EQ(aExpectedContinue, keepDecoding);
  EXPECT_EQ(aQueue.Batch() * 2, aQueue.PendingDecode());
  EXPECT_EQ(aFirstFrame, aQueue.Get(0, true));

  if (!aQueue.MayDiscard()) {
    const AnimationFrameRetainedBuffer& queue =
        *static_cast<AnimationFrameRetainedBuffer*>(&aQueue);
    EXPECT_EQ(aFirstFrame, queue.Frames()[0].get());
    EXPECT_EQ(aFirstFrame, aQueue.Get(0, false));
  } else {
    const AnimationFrameDiscardingQueue& queue =
        *static_cast<AnimationFrameDiscardingQueue*>(&aQueue);
    EXPECT_EQ(size_t(0), queue.PendingInsert());
    EXPECT_EQ(size_t(0), queue.Display().size());
    EXPECT_EQ(aFirstFrame, queue.FirstFrame());
    EXPECT_EQ(nullptr, aQueue.Get(0, false));
  }
}

class ImageAnimationFrameBuffer : public ::testing::Test {
 public:
  ImageAnimationFrameBuffer() {}

 private:
  AutoInitializeImageLib mInit;
};

TEST_F(ImageAnimationFrameBuffer, RetainedInitialState) {
  const size_t kThreshold = 800;
  const size_t kBatch = 100;
  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);

  EXPECT_EQ(kThreshold, buffer.Threshold());
  EXPECT_EQ(kBatch, buffer.Batch());
  EXPECT_EQ(size_t(0), buffer.Displayed());
  EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
  EXPECT_FALSE(buffer.MayDiscard());
  EXPECT_FALSE(buffer.SizeKnown());
  EXPECT_EQ(size_t(0), buffer.Size());
}

TEST_F(ImageAnimationFrameBuffer, ThresholdTooSmall) {
  const size_t kThreshold = 0;
  const size_t kBatch = 10;
  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);

  EXPECT_EQ(kBatch * 2 + 1, buffer.Threshold());
  EXPECT_EQ(kBatch, buffer.Batch());
  EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
}

TEST_F(ImageAnimationFrameBuffer, BatchTooSmall) {
  const size_t kThreshold = 10;
  const size_t kBatch = 0;
  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);

  EXPECT_EQ(kThreshold, buffer.Threshold());
  EXPECT_EQ(size_t(1), buffer.Batch());
  EXPECT_EQ(size_t(2), buffer.PendingDecode());
  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
}

TEST_F(ImageAnimationFrameBuffer, BatchTooBig) {
  const size_t kThreshold = 50;
  const size_t kBatch = SIZE_MAX;
  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);

  // The rounding is important here (e.g. SIZE_MAX/4 * 2 != SIZE_MAX/2).
  EXPECT_EQ(SIZE_MAX / 4, buffer.Batch());
  EXPECT_EQ(buffer.Batch() * 2 + 1, buffer.Threshold());
  EXPECT_EQ(buffer.Batch() * 2, buffer.PendingDecode());
  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
}

TEST_F(ImageAnimationFrameBuffer, FinishUnderBatchAndThreshold) {
  const size_t kThreshold = 30;
  const size_t kBatch = 10;
  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
  const auto& frames = buffer.Frames();

  EXPECT_EQ(kBatch * 2, buffer.PendingDecode());

  RefPtr<imgFrame> firstFrame;
  for (size_t i = 0; i < 5; ++i) {
    RefPtr<imgFrame> frame = CreateEmptyFrame();
    auto status = buffer.Insert(RefPtr<imgFrame>(frame));
    EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE);
    EXPECT_FALSE(buffer.SizeKnown());
    EXPECT_EQ(buffer.Size(), i + 1);

    if (i == 4) {
      EXPECT_EQ(size_t(15), buffer.PendingDecode());
      bool keepDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
      EXPECT_FALSE(keepDecoding);
      EXPECT_TRUE(buffer.SizeKnown());
      EXPECT_EQ(size_t(0), buffer.PendingDecode());
      EXPECT_FALSE(buffer.HasRedecodeError());
    }

    EXPECT_FALSE(buffer.MayDiscard());

    imgFrame* gotFrame = buffer.Get(i, false);
    EXPECT_EQ(frame.get(), gotFrame);
    ASSERT_EQ(i + 1, frames.Length());
    EXPECT_EQ(frame.get(), frames[i].get());

    if (i == 0) {
      firstFrame = std::move(frame);
      EXPECT_EQ(size_t(0), buffer.Displayed());
    } else {
      EXPECT_EQ(i - 1, buffer.Displayed());
      bool restartDecoder = buffer.AdvanceTo(i);
      EXPECT_FALSE(restartDecoder);
      EXPECT_EQ(i, buffer.Displayed());
    }

    gotFrame = buffer.Get(0, false);
    EXPECT_EQ(firstFrame.get(), gotFrame);
  }

  // Loop again over the animation and make sure it is still all there.
  for (size_t i = 0; i < frames.Length(); ++i) {
    EXPECT_TRUE(buffer.Get(i, false) != nullptr);

    bool restartDecoder = buffer.AdvanceTo(i);
    EXPECT_FALSE(restartDecoder);
  }
}

TEST_F(ImageAnimationFrameBuffer, FinishMultipleBatchesUnderThreshold) {
  const size_t kThreshold = 30;
  const size_t kBatch = 2;
  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, 0);
  const auto& frames = buffer.Frames();

  EXPECT_EQ(kBatch * 2, buffer.PendingDecode());

  // Add frames until it tells us to stop.
  AnimationFrameBuffer::InsertStatus status;
  do {
    status = buffer.Insert(CreateEmptyFrame());
    EXPECT_FALSE(buffer.SizeKnown());
    EXPECT_FALSE(buffer.MayDiscard());
  } while (status == AnimationFrameBuffer::InsertStatus::CONTINUE);

  EXPECT_EQ(size_t(0), buffer.PendingDecode());
  EXPECT_EQ(size_t(4), frames.Length());
  EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::YIELD);

  // Progress through the animation until it lets us decode again.
  bool restartDecoder = false;
  size_t i = 0;
  do {
    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    if (i > 0) {
      restartDecoder = buffer.AdvanceTo(i);
    }
    ++i;
  } while (!restartDecoder);

  EXPECT_EQ(size_t(2), buffer.PendingDecode());
  EXPECT_EQ(size_t(2), buffer.Displayed());

  // Add the last frame.
  status = buffer.Insert(CreateEmptyFrame());
  EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE);
  bool keepDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
  EXPECT_FALSE(keepDecoding);
  EXPECT_TRUE(buffer.SizeKnown());
  EXPECT_EQ(size_t(0), buffer.PendingDecode());
  EXPECT_EQ(size_t(5), frames.Length());
  EXPECT_FALSE(buffer.HasRedecodeError());

  // Finish progressing through the animation.
  for (; i < frames.Length(); ++i) {
    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    restartDecoder = buffer.AdvanceTo(i);
    EXPECT_FALSE(restartDecoder);
  }

  // Loop again over the animation and make sure it is still all there.
  for (i = 0; i < frames.Length(); ++i) {
    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    restartDecoder = buffer.AdvanceTo(i);
    EXPECT_FALSE(restartDecoder);
  }

  // Loop to the third frame and then reset the animation.
  for (i = 0; i < 3; ++i) {
    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
    restartDecoder = buffer.AdvanceTo(i);
    EXPECT_FALSE(restartDecoder);
  }

  // Since we are below the threshold, we can reset the get index only.
  // Nothing else should have changed.
  restartDecoder = buffer.Reset();
  EXPECT_FALSE(restartDecoder);
  for (i = 0; i < 5; ++i) {
    EXPECT_TRUE(buffer.Get(i, false) != nullptr);
  }
  EXPECT_EQ(size_t(0), buffer.PendingDecode());
  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
  EXPECT_EQ(size_t(0), buffer.Displayed());
}

TEST_F(ImageAnimationFrameBuffer, StartAfterBeginning) {
  const size_t kThreshold = 30;
  const size_t kBatch = 2;
  const size_t kStartFrame = 7;
  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, kStartFrame);

  EXPECT_EQ(kStartFrame, buffer.PendingAdvance());

  // Add frames until it tells us to stop. It should be later than before,
  // because it auto-advances until its displayed frame is kStartFrame.
  AnimationFrameBuffer::InsertStatus status;
  size_t i = 0;
  do {
    status = buffer.Insert(CreateEmptyFrame());
    EXPECT_FALSE(buffer.SizeKnown());
    EXPECT_FALSE(buffer.MayDiscard());

    if (i <= kStartFrame) {
      EXPECT_EQ(i, buffer.Displayed());
      EXPECT_EQ(kStartFrame - i, buffer.PendingAdvance());
    } else {
      EXPECT_EQ(kStartFrame, buffer.Displayed());
      EXPECT_EQ(size_t(0), buffer.PendingAdvance());
    }

    i++;
  } while (status == AnimationFrameBuffer::InsertStatus::CONTINUE);

  EXPECT_EQ(size_t(0), buffer.PendingDecode());
  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
  EXPECT_EQ(size_t(10), buffer.Size());
}

TEST_F(ImageAnimationFrameBuffer, StartAfterBeginningAndReset) {
  const size_t kThreshold = 30;
  const size_t kBatch = 2;
  const size_t kStartFrame = 7;
  AnimationFrameRetainedBuffer buffer(kThreshold, kBatch, kStartFrame);

  EXPECT_EQ(kStartFrame, buffer.PendingAdvance());

  // Add frames until it tells us to stop. It should be later than before,
  // because it auto-advances until its displayed frame is kStartFrame.
  for (size_t i = 0; i < 5; ++i) {
    AnimationFrameBuffer::InsertStatus status =
        buffer.Insert(CreateEmptyFrame());
    EXPECT_EQ(status, AnimationFrameBuffer::InsertStatus::CONTINUE);
    EXPECT_FALSE(buffer.SizeKnown());
    EXPECT_FALSE(buffer.MayDiscard());
    EXPECT_EQ(i, buffer.Displayed());
    EXPECT_EQ(kStartFrame - i, buffer.PendingAdvance());
  }

  // When we reset the animation, it goes back to the beginning. That means
  // we can forget about what we were told to advance to at the start. While
  // we have plenty of frames in our buffer, we still need one more because
  // in the real scenario, the decoder thread is still running and it is easier
  // to let it insert its last frame than to coordinate quitting earlier.
  buffer.Reset();
  EXPECT_EQ(size_t(0), buffer.Displayed());
  EXPECT_EQ(size_t(1), buffer.PendingDecode());
  EXPECT_EQ(size_t(0), buffer.PendingAdvance());
  EXPECT_EQ(size_t(5), buffer.Size());
}

static void TestDiscardingQueueLoop(AnimationFrameDiscardingQueue& aQueue,
                                    const imgFrame* aFirstFrame,
                                    size_t aThreshold, size_t aBatch,
                                    size_t aStartFrame) {
  // We should be advanced right up to the last decoded frame.
  EXPECT_TRUE(aQueue.MayDiscard());
  EXPECT_FALSE(aQueue.SizeKnown());
  EXPECT_EQ(aBatch, aQueue.Batch());
  EXPECT_EQ(aThreshold, aQueue.PendingInsert());
  EXPECT_EQ(aThreshold, aQueue.Size());
  EXPECT_EQ(aFirstFrame, aQueue.FirstFrame());
  EXPECT_EQ(size_t(1), aQueue.Display().size());
  EXPECT_EQ(size_t(3), aQueue.PendingDecode());
  VerifyDiscardingQueueContents(aQueue);

  // Make sure frames get removed as we advance.
  VerifyInsertAndAdvance(aQueue, 5,
                         AnimationFrameBuffer::InsertStatus::CONTINUE);
  EXPECT_EQ(size_t(1), aQueue.Display().size());
  VerifyInsertAndAdvance(aQueue, 6,
                         AnimationFrameBuffer::InsertStatus::CONTINUE);
  EXPECT_EQ(size_t(1), aQueue.Display().size());

  // We actually will yield if we are recycling instead of continuing because
  // the pending calculation is slightly different. We will actually request one
  // less frame than we have to recycle.
  if (aQueue.IsRecycling()) {
    VerifyInsertAndAdvance(aQueue, 7,
                           AnimationFrameBuffer::InsertStatus::YIELD);
  } else {
    VerifyInsertAndAdvance(aQueue, 7,
                           AnimationFrameBuffer::InsertStatus::CONTINUE);
  }
  EXPECT_EQ(size_t(1), aQueue.Display().size());

  // We should get throttled if we insert too much.
  VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::CONTINUE);
  EXPECT_EQ(size_t(2), aQueue.Display().size());
  EXPECT_EQ(size_t(1), aQueue.PendingDecode());
  VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::YIELD);
  EXPECT_EQ(size_t(3), aQueue.Display().size());
  EXPECT_EQ(size_t(0), aQueue.PendingDecode());

  // We should get restarted if we advance.
  VerifyAdvance(aQueue, 8, true);
  EXPECT_EQ(size_t(2), aQueue.PendingDecode());
  VerifyAdvance(aQueue, 9, false);
  EXPECT_EQ(size_t(2), aQueue.PendingDecode());

  // We should continue decoding if we completed, since we are discarding.
  VerifyMarkComplete(aQueue, true);
  EXPECT_EQ(size_t(2), aQueue.PendingDecode());
  EXPECT_EQ(size_t(10), aQueue.Size());
  EXPECT_TRUE(aQueue.SizeKnown());
  EXPECT_FALSE(aQueue.HasRedecodeError());

  // Insert the first frames of the animation.
  VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsert(aQueue, AnimationFrameBuffer::InsertStatus::YIELD);
  EXPECT_EQ(size_t(0), aQueue.PendingDecode());
  EXPECT_EQ(size_t(10), aQueue.Size());

  // Advance back at the beginning. The first frame should only match for
  // display purposes.
  VerifyAdvance(aQueue, 0, true);
  EXPECT_EQ(size_t(2), aQueue.PendingDecode());
  EXPECT_TRUE(aQueue.FirstFrame() != nullptr);
  EXPECT_TRUE(aQueue.Get(0, false) != nullptr);
  EXPECT_NE(aQueue.FirstFrame(), aQueue.Get(0, false));
  EXPECT_EQ(aQueue.FirstFrame(), aQueue.Get(0, true));

  // Reiterate one more time and make it loops back.
  VerifyInsertAndAdvance(aQueue, 1,
                         AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsertAndAdvance(aQueue, 2, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(aQueue, 3,
                         AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsertAndAdvance(aQueue, 4, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(aQueue, 5,
                         AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsertAndAdvance(aQueue, 6, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(aQueue, 7,
                         AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsertAndAdvance(aQueue, 8, AnimationFrameBuffer::InsertStatus::YIELD);

  EXPECT_EQ(size_t(10), aQueue.PendingInsert());
  VerifyMarkComplete(aQueue, true);
  EXPECT_EQ(size_t(0), aQueue.PendingInsert());

  VerifyInsertAndAdvance(aQueue, 9,
                         AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsertAndAdvance(aQueue, 0, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(aQueue, 1,
                         AnimationFrameBuffer::InsertStatus::CONTINUE);
}

TEST_F(ImageAnimationFrameBuffer, DiscardingLoop) {
  const size_t kThreshold = 5;
  const size_t kBatch = 2;
  const size_t kStartFrame = 0;
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
  PrepareForDiscardingQueue(retained);
  const imgFrame* firstFrame = retained.Frames()[0].get();
  AnimationFrameDiscardingQueue buffer(std::move(retained));
  TestDiscardingQueueLoop(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
}

TEST_F(ImageAnimationFrameBuffer, RecyclingLoop) {
  const size_t kThreshold = 5;
  const size_t kBatch = 2;
  const size_t kStartFrame = 0;
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
  PrepareForDiscardingQueue(retained);
  const imgFrame* firstFrame = retained.Frames()[0].get();
  AnimationFrameRecyclingQueue buffer(std::move(retained));

  // We should not start with any recycled frames.
  ASSERT_TRUE(buffer.Recycle().empty());

  TestDiscardingQueueLoop(buffer, firstFrame, kThreshold, kBatch, kStartFrame);

  // All the frames we inserted should have been recycleable.
  ASSERT_FALSE(buffer.Recycle().empty());
  while (!buffer.Recycle().empty()) {
    gfx::IntRect expectedRect(0, 0, 1, 1);
    RefPtr<imgFrame> expectedFrame = buffer.Recycle().front().mFrame;
    EXPECT_FALSE(expectedRect.IsEmpty());
    EXPECT_TRUE(expectedFrame.get() != nullptr);

    gfx::IntRect gotRect;
    RawAccessFrameRef gotFrame = buffer.RecycleFrame(gotRect);
    EXPECT_EQ(expectedFrame.get(), gotFrame.get());
    EXPECT_EQ(expectedRect, gotRect);
    EXPECT_TRUE(ReinitForRecycle(gotFrame));
  }

  // Trying to pull a recycled frame when we have nothing should be safe too.
  gfx::IntRect gotRect;
  RawAccessFrameRef gotFrame = buffer.RecycleFrame(gotRect);
  EXPECT_TRUE(gotFrame.get() == nullptr);
  EXPECT_FALSE(ReinitForRecycle(gotFrame));
}

static void TestDiscardingQueueReset(AnimationFrameDiscardingQueue& aQueue,
                                     const imgFrame* aFirstFrame,
                                     size_t aThreshold, size_t aBatch,
                                     size_t aStartFrame) {
  // We should be advanced right up to the last decoded frame.
  EXPECT_TRUE(aQueue.MayDiscard());
  EXPECT_FALSE(aQueue.SizeKnown());
  EXPECT_EQ(aBatch, aQueue.Batch());
  EXPECT_EQ(aThreshold, aQueue.PendingInsert());
  EXPECT_EQ(aThreshold, aQueue.Size());
  EXPECT_EQ(aFirstFrame, aQueue.FirstFrame());
  EXPECT_EQ(size_t(1), aQueue.Display().size());
  EXPECT_EQ(size_t(4), aQueue.PendingDecode());
  VerifyDiscardingQueueContents(aQueue);

  // Reset should clear everything except the first frame.
  VerifyReset(aQueue, false, aFirstFrame);
}

TEST_F(ImageAnimationFrameBuffer, DiscardingReset) {
  const size_t kThreshold = 8;
  const size_t kBatch = 3;
  const size_t kStartFrame = 0;
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
  PrepareForDiscardingQueue(retained);
  const imgFrame* firstFrame = retained.Frames()[0].get();
  AnimationFrameDiscardingQueue buffer(std::move(retained));
  TestDiscardingQueueReset(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
}

TEST_F(ImageAnimationFrameBuffer, ResetBeforeDiscardingThreshold) {
  const size_t kThreshold = 3;
  const size_t kBatch = 1;
  const size_t kStartFrame = 0;

  // Get the starting buffer to just before the point where we need to switch
  // to a discarding buffer, reset the animation so advancing points at the
  // first frame, and insert the last frame to cross the threshold.
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsertAndAdvance(retained, 1,
                         AnimationFrameBuffer::InsertStatus::YIELD);
  bool restartDecoder = retained.Reset();
  EXPECT_FALSE(restartDecoder);
  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);

  const imgFrame* firstFrame = retained.Frames()[0].get();
  EXPECT_TRUE(firstFrame != nullptr);
  AnimationFrameDiscardingQueue buffer(std::move(retained));
  const imgFrame* displayFirstFrame = buffer.Get(0, true);
  const imgFrame* advanceFirstFrame = buffer.Get(0, false);
  EXPECT_EQ(firstFrame, displayFirstFrame);
  EXPECT_EQ(firstFrame, advanceFirstFrame);
}

TEST_F(ImageAnimationFrameBuffer, DiscardingTooFewFrames) {
  const size_t kThreshold = 3;
  const size_t kBatch = 1;
  const size_t kStartFrame = 0;

  // First get us to a discarding buffer state.
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsertAndAdvance(retained, 1,
                         AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);

  // Insert one more frame.
  AnimationFrameDiscardingQueue buffer(std::move(retained));
  VerifyAdvance(buffer, 2, true);
  VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD);

  // Mark it as complete.
  bool restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
  EXPECT_FALSE(restartDecoder);
  EXPECT_FALSE(buffer.HasRedecodeError());

  // Insert one fewer frame than before.
  VerifyAdvance(buffer, 3, true);
  VerifyInsertAndAdvance(buffer, 0, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(buffer, 1, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(buffer, 2, AnimationFrameBuffer::InsertStatus::YIELD);

  // When we mark it as complete, it should fail due to too few frames.
  restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
  EXPECT_TRUE(buffer.HasRedecodeError());
  EXPECT_EQ(size_t(0), buffer.PendingDecode());
  EXPECT_EQ(size_t(4), buffer.Size());
}

TEST_F(ImageAnimationFrameBuffer, DiscardingTooManyFrames) {
  const size_t kThreshold = 3;
  const size_t kBatch = 1;
  const size_t kStartFrame = 0;

  // First get us to a discarding buffer state.
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::CONTINUE);
  VerifyInsertAndAdvance(retained, 1,
                         AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsert(retained, AnimationFrameBuffer::InsertStatus::DISCARD_YIELD);

  // Insert one more frame.
  AnimationFrameDiscardingQueue buffer(std::move(retained));
  VerifyAdvance(buffer, 2, true);
  VerifyInsert(buffer, AnimationFrameBuffer::InsertStatus::YIELD);

  // Mark it as complete.
  bool restartDecoder = buffer.MarkComplete(gfx::IntRect(0, 0, 1, 1));
  EXPECT_FALSE(restartDecoder);
  EXPECT_FALSE(buffer.HasRedecodeError());

  // Advance and insert to get us back to the end on the redecode.
  VerifyAdvance(buffer, 3, true);
  VerifyInsertAndAdvance(buffer, 0, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(buffer, 1, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(buffer, 2, AnimationFrameBuffer::InsertStatus::YIELD);
  VerifyInsertAndAdvance(buffer, 3, AnimationFrameBuffer::InsertStatus::YIELD);

  // Attempt to insert a 5th frame, it should fail.
  RefPtr<imgFrame> frame = CreateEmptyFrame();
  AnimationFrameBuffer::InsertStatus status = buffer.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);
  EXPECT_TRUE(buffer.HasRedecodeError());
  EXPECT_EQ(size_t(0), buffer.PendingDecode());
  EXPECT_EQ(size_t(4), buffer.Size());
}

TEST_F(ImageAnimationFrameBuffer, RecyclingReset) {
  const size_t kThreshold = 8;
  const size_t kBatch = 3;
  const size_t kStartFrame = 0;
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);
  PrepareForDiscardingQueue(retained);
  const imgFrame* firstFrame = retained.Frames()[0].get();
  AnimationFrameRecyclingQueue buffer(std::move(retained));
  TestDiscardingQueueReset(buffer, firstFrame, kThreshold, kBatch, kStartFrame);
}

TEST_F(ImageAnimationFrameBuffer, RecyclingResetBeforeComplete) {
  const size_t kThreshold = 3;
  const size_t kBatch = 1;
  const size_t kStartFrame = 0;
  const gfx::IntSize kImageSize(100, 100);
  const gfx::IntRect kImageRect(gfx::IntPoint(0, 0), kImageSize);
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);

  // Get the starting buffer to just before the point where we need to switch
  // to a discarding buffer, reset the animation so advancing points at the
  // first frame, and insert the last frame to cross the threshold.
  RefPtr<imgFrame> frame;
  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
  AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);

  frame = CreateEmptyFrame(
      kImageSize, gfx::IntRect(gfx::IntPoint(10, 10), gfx::IntSize(1, 1)),
      false);
  status = retained.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);

  VerifyAdvance(retained, 1, true);

  frame = CreateEmptyFrame(
      kImageSize, gfx::IntRect(gfx::IntPoint(20, 10), gfx::IntSize(1, 1)),
      false);
  status = retained.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_YIELD, status);

  AnimationFrameRecyclingQueue buffer(std::move(retained));
  bool restartDecoding = buffer.Reset();
  EXPECT_TRUE(restartDecoding);

  // None of the buffers were recyclable.
  EXPECT_FALSE(buffer.Recycle().empty());
  while (!buffer.Recycle().empty()) {
    gfx::IntRect recycleRect;
    RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
    EXPECT_FALSE(frameRef);
  }

  // Reinsert the first two frames as recyclable and reset again.
  frame = CreateEmptyFrame(kImageSize, kImageRect, true);
  status = buffer.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);

  frame = CreateEmptyFrame(
      kImageSize, gfx::IntRect(gfx::IntPoint(10, 10), gfx::IntSize(1, 1)),
      true);
  status = buffer.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);

  restartDecoding = buffer.Reset();
  EXPECT_TRUE(restartDecoding);

  // Now both buffers should have been saved and the dirty rect replaced with
  // the full image rect since we don't know the first frame refresh area yet.
  EXPECT_EQ(size_t(2), buffer.Recycle().size());
  for (const auto& entry : buffer.Recycle()) {
    EXPECT_EQ(kImageRect, entry.mDirtyRect);
  }
}

TEST_F(ImageAnimationFrameBuffer, RecyclingRect) {
  const size_t kThreshold = 5;
  const size_t kBatch = 2;
  const size_t kStartFrame = 0;
  const gfx::IntSize kImageSize(100, 100);
  const gfx::IntRect kImageRect(gfx::IntPoint(0, 0), kImageSize);
  AnimationFrameRetainedBuffer retained(kThreshold, kBatch, kStartFrame);

  // Let's get to the recycling state while marking all of the frames as not
  // recyclable, just like AnimationFrameBuffer / the decoders would do.
  RefPtr<imgFrame> frame;
  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
  AnimationFrameBuffer::InsertStatus status = retained.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);

  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
  status = retained.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);

  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
  status = retained.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::CONTINUE, status);

  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
  status = retained.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);

  VerifyAdvance(retained, 1, false);
  VerifyAdvance(retained, 2, true);
  VerifyAdvance(retained, 3, false);

  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
  status = retained.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::DISCARD_CONTINUE, status);

  AnimationFrameRecyclingQueue buffer(std::move(retained));

  // The first frame is now the candidate for recycling. Since it was marked as
  // not recyclable, we should get nothing.
  VerifyAdvance(buffer, 4, false);

  gfx::IntRect recycleRect;
  EXPECT_FALSE(buffer.Recycle().empty());
  RawAccessFrameRef frameRef = buffer.RecycleFrame(recycleRect);
  EXPECT_FALSE(frameRef);
  EXPECT_TRUE(buffer.Recycle().empty());

  // Insert a recyclable partial frame. Its dirty rect shouldn't matter since
  // the previous frame was not recyclable.
  frame = CreateEmptyFrame(kImageSize, gfx::IntRect(0, 0, 25, 25));
  status = buffer.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);

  VerifyAdvance(buffer, 5, true);
  EXPECT_FALSE(buffer.Recycle().empty());
  frameRef = buffer.RecycleFrame(recycleRect);
  EXPECT_FALSE(frameRef);
  EXPECT_TRUE(buffer.Recycle().empty());

  // Insert a recyclable partial frame. Its dirty rect should match the recycle
  // rect since it is the only frame in the buffer.
  frame = CreateEmptyFrame(kImageSize, gfx::IntRect(25, 0, 50, 50));
  status = buffer.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);

  VerifyAdvance(buffer, 6, true);
  EXPECT_FALSE(buffer.Recycle().empty());
  frameRef = buffer.RecycleFrame(recycleRect);
  EXPECT_TRUE(frameRef);
  EXPECT_TRUE(ReinitForRecycle(frameRef));
  EXPECT_EQ(gfx::IntRect(25, 0, 50, 50), recycleRect);
  EXPECT_TRUE(buffer.Recycle().empty());

  // Insert the last frame and mark us as complete. The next recycled frame is
  // producing the first frame again, so we should use the first frame refresh
  // area instead of its dirty rect.
  frame = CreateEmptyFrame(kImageSize, gfx::IntRect(10, 10, 60, 10));
  status = buffer.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);

  bool continueDecoding = buffer.MarkComplete(gfx::IntRect(0, 0, 75, 50));
  EXPECT_FALSE(continueDecoding);

  VerifyAdvance(buffer, 7, true);
  EXPECT_FALSE(buffer.Recycle().empty());
  frameRef = buffer.RecycleFrame(recycleRect);
  EXPECT_TRUE(frameRef);
  EXPECT_TRUE(ReinitForRecycle(frameRef));
  EXPECT_EQ(gfx::IntRect(0, 0, 75, 50), recycleRect);
  EXPECT_TRUE(buffer.Recycle().empty());

  // Now let's reinsert the first frame. The recycle rect should still be the
  // first frame refresh area instead of the dirty rect of the first frame (e.g.
  // the full frame).
  frame = CreateEmptyFrame(kImageSize, kImageRect, false);
  status = buffer.Insert(std::move(frame));
  EXPECT_EQ(AnimationFrameBuffer::InsertStatus::YIELD, status);

  VerifyAdvance(buffer, 0, true);
  EXPECT_FALSE(buffer.Recycle().empty());
  frameRef = buffer.RecycleFrame(recycleRect);
  EXPECT_TRUE(frameRef);
  EXPECT_TRUE(ReinitForRecycle(frameRef));
  EXPECT_EQ(gfx::IntRect(0, 0, 75, 50), recycleRect);
  EXPECT_TRUE(buffer.Recycle().empty());
}

Messung V0.5
C=87 H=96 G=91

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge