/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "gtest/gtest.h"

#include "mozilla/gfx/2D.h"
#include "Common.h"
#include "Decoder.h"
#include "DecoderFactory.h"
#include "SourceBuffer.h"
#include "SurfacePipe.h"

using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::image;

enum class Orient { NORMAL, FLIP_VERTICALLY };

static void InitializeRowBuffer(uint32_t* aBuffer, size_t aSize,
                                size_t aStartPixel, size_t aEndPixel,
                                uint32_t aSetPixel) {
  uint32_t transparentPixel = BGRAColor::Transparent().AsPixel();
  for (size_t i = 0; i < aStartPixel && i < aSize; ++i) {
    aBuffer[i] = transparentPixel;
  }
  for (size_t i = aStartPixel; i < aEndPixel && i < aSize; ++i) {
    aBuffer[i] = aSetPixel;
  }
  for (size_t i = aEndPixel; i < aSize; ++i) {
    aBuffer[i] = transparentPixel;
  }
}

template <Orient Orientation, typename Func>
void WithSurfaceSink(Func aFunc) {
  RefPtr<image::Decoder> decoder = CreateTrivialDecoder();
  ASSERT_TRUE(decoder != nullptr);

  const bool flipVertically = Orientation == Orient::FLIP_VERTICALLY;

  WithFilterPipeline(decoder, std::forward<Func>(aFunc),
                     SurfaceConfig{decoder, IntSize(100, 100),
                                   SurfaceFormat::OS_RGBA, flipVertically});
}

void ResetForNextPass(SurfaceFilter* aSink) {
  aSink->ResetToFirstRow();
  EXPECT_FALSE(aSink->IsSurfaceFinished());
  Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
  EXPECT_TRUE(invalidRect.isNothing());
}

template <typename WriteFunc, typename CheckFunc>
void DoCheckIterativeWrite(SurfaceFilter* aSink, WriteFunc aWriteFunc,
                           CheckFunc aCheckFunc) {
  // Write the buffer to successive rows until every row of the surface
  // has been written.
  uint32_t row = 0;
  WriteState result = WriteState::NEED_MORE_DATA;
  while (result == WriteState::NEED_MORE_DATA) {
    result = aWriteFunc(row);
    ++row;
  }
  EXPECT_EQ(WriteState::FINISHED, result);
  EXPECT_EQ(100u, row);

  AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 100),
                                  IntRect(0, 0, 100, 100));

  // Check that the generated image is correct.
  aCheckFunc();
}

template <typename WriteFunc>
void CheckIterativeWrite(image::Decoder* aDecoder, SurfaceSink* aSink,
                         const IntRect& aOutputRect, WriteFunc aWriteFunc) {
  // Ignore the row passed to WriteFunc, since no callers use it.
  auto writeFunc = [&](uint32_t) { return aWriteFunc(); };

  DoCheckIterativeWrite(aSink, writeFunc,
                        [&] { CheckGeneratedImage(aDecoder, aOutputRect); });
}

TEST(ImageSurfaceSink, SurfaceSinkInitialization)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        // Check initial state.
        EXPECT_FALSE(aSink->IsSurfaceFinished());
        Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
        EXPECT_TRUE(invalidRect.isNothing());

        // Check that the surface is zero-initialized. We verify this by calling
        // CheckGeneratedImage() and telling it that we didn't write to the
        // surface anyway (i.e., we wrote to the empty rect); it will then
        // expect the entire surface to be transparent, which is what it should
        // be if it was zero-initialied.
        CheckGeneratedImage(aDecoder, IntRect(0, 0, 0, 0));
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWritePixels)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        CheckWritePixels(aDecoder, aSink);
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWritePixelsFinish)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        // Write nothing into the surface; just finish immediately.
        uint32_t count = 0;
        auto result = aSink->WritePixels<uint32_t>([&]() {
          count++;
          return AsVariant(WriteState::FINISHED);
        });
        EXPECT_EQ(WriteState::FINISHED, result);
        EXPECT_EQ(1u, count);

        AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 100),
                                        IntRect(0, 0, 100, 100));

        // Attempt to write more and make sure that nothing gets written.
        count = 0;
        result = aSink->WritePixels<uint32_t>([&]() {
          count++;
          return AsVariant(BGRAColor::Red().AsPixel());
        });
        EXPECT_EQ(WriteState::FINISHED, result);
        EXPECT_EQ(0u, count);
        EXPECT_TRUE(aSink->IsSurfaceFinished());

        // Check that the generated image is correct.
        RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
        RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
        EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Transparent()));
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWritePixelsEarlyExit)
{
  auto checkEarlyExit = [](image::Decoder* aDecoder, SurfaceSink* aSink,
                           WriteState aState) {
    // Write half a row of green pixels and then exit early with |aState|. If
    // the lambda keeps getting called, we'll write red pixels, which will cause
    // the test to fail.
    uint32_t count = 0;
    auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
      if (count == 50) {
        return AsVariant(aState);
      }
      return count++ < 50 ? AsVariant(BGRAColor::Green().AsPixel())
                          : AsVariant(BGRAColor::Red().AsPixel());
    });

    EXPECT_EQ(aState, result);
    EXPECT_EQ(50u, count);
    CheckGeneratedImage(aDecoder, IntRect(0, 0, 50, 1));

    if (aState != WriteState::FINISHED) {
      // We should still be able to write more at this point.
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      // Verify that we can resume writing. We'll finish up the same row.
      count = 0;
      result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
        if (count == 50) {
          return AsVariant(WriteState::NEED_MORE_DATA);
        }
        ++count;
        return AsVariant(BGRAColor::Green().AsPixel());
      });

      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(50u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());
      CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, 1));

      return;
    }

    // We should've finished the surface at this point.
    AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 100),
                                    IntRect(0, 0, 100, 100));

    // Attempt to write more and make sure that nothing gets written.
    count = 0;
    result = aSink->WritePixels<uint32_t>([&] {
      count++;
      return AsVariant(BGRAColor::Red().AsPixel());
    });

    EXPECT_EQ(WriteState::FINISHED, result);
    EXPECT_EQ(0u, count);
    EXPECT_TRUE(aSink->IsSurfaceFinished());

    // Check that the generated image is still correct.
    CheckGeneratedImage(aDecoder, IntRect(0, 0, 50, 1));
  };

  WithSurfaceSink<Orient::NORMAL>(
      [&](image::Decoder* aDecoder, SurfaceSink* aSink) {
        checkEarlyExit(aDecoder, aSink, WriteState::NEED_MORE_DATA);
      });

  WithSurfaceSink<Orient::NORMAL>(
      [&](image::Decoder* aDecoder, SurfaceSink* aSink) {
        checkEarlyExit(aDecoder, aSink, WriteState::FAILURE);
      });

  WithSurfaceSink<Orient::NORMAL>(
      [&](image::Decoder* aDecoder, SurfaceSink* aSink) {
        checkEarlyExit(aDecoder, aSink, WriteState::FINISHED);
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWritePixelsToRow)
{
  WithSurfaceSink<Orient::NORMAL>([](image::Decoder* aDecoder,
                                     SurfaceSink* aSink) {
    // Write the first 99 rows of our 100x100 surface and verify that even
    // though our lambda will yield pixels forever, only one row is written
    // per call to WritePixelsToRow().
    for (int row = 0; row < 99; ++row) {
      uint32_t count = 0;
      WriteState result = aSink->WritePixelsToRow<uint32_t>([&] {
        ++count;
        return AsVariant(BGRAColor::Green().AsPixel());
      });

      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(100u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isSome());
      EXPECT_EQ(OrientedIntRect(0, row, 100, 1), invalidRect->mInputSpaceRect);
      EXPECT_EQ(OrientedIntRect(0, row, 100, 1), invalidRect->mOutputSpaceRect);

      CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, row + 1));
    }

    // Write the final line, which should finish the surface.
    uint32_t count = 0;
    WriteState result = aSink->WritePixelsToRow<uint32_t>([&] {
      ++count;
      return AsVariant(BGRAColor::Green().AsPixel());
    });

    EXPECT_EQ(WriteState::FINISHED, result);
    EXPECT_EQ(100u, count);

    // Note that the final invalid rect we expect here is only the last row;
    // that's because we called TakeInvalidRect() repeatedly in the loop
    // above.
    AssertCorrectPipelineFinalState(aSink, IntRect(0, 99, 100, 1),
                                    IntRect(0, 99, 100, 1));

    // Check that the generated image is correct.
    CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, 100));

    // Attempt to write more and make sure that nothing gets written.
    count = 0;
    result = aSink->WritePixelsToRow<uint32_t>([&] {
      count++;
      return AsVariant(BGRAColor::Red().AsPixel());
    });

    EXPECT_EQ(WriteState::FINISHED, result);
    EXPECT_EQ(0u, count);
    EXPECT_TRUE(aSink->IsSurfaceFinished());

    // Check that the generated image is still correct.
    CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, 100));
  });
}

TEST(ImageSurfaceSink, SurfaceSinkWritePixelsToRowEarlyExit)
{
  auto checkEarlyExit = [](image::Decoder* aDecoder, SurfaceSink* aSink,
                           WriteState aState) {
    // Write half a row of green pixels and then exit early with |aState|. If
    // the lambda keeps getting called, we'll write red pixels, which will cause
    // the test to fail.
    uint32_t count = 0;
    auto result =
        aSink->WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> {
          if (count == 50) {
            return AsVariant(aState);
          }
          return count++ < 50 ? AsVariant(BGRAColor::Green().AsPixel())
                              : AsVariant(BGRAColor::Red().AsPixel());
        });

    EXPECT_EQ(aState, result);
    EXPECT_EQ(50u, count);
    CheckGeneratedImage(aDecoder, IntRect(0, 0, 50, 1));

    if (aState != WriteState::FINISHED) {
      // We should still be able to write more at this point.
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      // Verify that we can resume the same row and still stop at the end.
      count = 0;
      WriteState result = aSink->WritePixelsToRow<uint32_t>([&] {
        ++count;
        return AsVariant(BGRAColor::Green().AsPixel());
      });

      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(50u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());
      CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, 1));

      return;
    }

    // We should've finished the surface at this point.
    AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 100),
                                    IntRect(0, 0, 100, 100));

    // Attempt to write more and make sure that nothing gets written.
    count = 0;
    result = aSink->WritePixelsToRow<uint32_t>([&] {
      count++;
      return AsVariant(BGRAColor::Red().AsPixel());
    });

    EXPECT_EQ(WriteState::FINISHED, result);
    EXPECT_EQ(0u, count);
    EXPECT_TRUE(aSink->IsSurfaceFinished());

    // Check that the generated image is still correct.
    CheckGeneratedImage(aDecoder, IntRect(0, 0, 50, 1));
  };

  WithSurfaceSink<Orient::NORMAL>(
      [&](image::Decoder* aDecoder, SurfaceSink* aSink) {
        checkEarlyExit(aDecoder, aSink, WriteState::NEED_MORE_DATA);
      });

  WithSurfaceSink<Orient::NORMAL>(
      [&](image::Decoder* aDecoder, SurfaceSink* aSink) {
        checkEarlyExit(aDecoder, aSink, WriteState::FAILURE);
      });

  WithSurfaceSink<Orient::NORMAL>(
      [&](image::Decoder* aDecoder, SurfaceSink* aSink) {
        checkEarlyExit(aDecoder, aSink, WriteState::FINISHED);
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWriteBuffer)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        // Create a green buffer the same size as one row of the surface (which
        // is 100x100), containing 60 pixels of green in the middle and 20
        // transparent pixels on either side.
        uint32_t buffer[100];
        InitializeRowBuffer(buffer, 100, 20, 80, BGRAColor::Green().AsPixel());

        // Write the buffer to every row of the surface and check that the
        // generated image is correct.
        CheckIterativeWrite(aDecoder, aSink, IntRect(20, 0, 60, 100),
                            [&] { return aSink->WriteBuffer(buffer); });
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWriteBufferPartialRow)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        // Create a buffer the same size as one row of the surface, containing
        // all green pixels.
        uint32_t buffer[100];
        for (int i = 0; i < 100; ++i) {
          buffer[i] = BGRAColor::Green().AsPixel();
        }

        // Write the buffer to the middle 60 pixels of every row of the surface
        // and check that the generated image is correct.
        CheckIterativeWrite(aDecoder, aSink, IntRect(20, 0, 60, 100),
                            [&] { return aSink->WriteBuffer(buffer, 20, 60); });
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWriteBufferPartialRowStartColOverflow)
{
  WithSurfaceSink<Orient::NORMAL>([](image::Decoder* aDecoder,
                                     SurfaceSink* aSink) {
    // Create a buffer the same size as one row of the surface, containing all
    // green pixels.
    uint32_t buffer[100];
    for (int i = 0; i < 100; ++i) {
      buffer[i] = BGRAColor::Green().AsPixel();
    }

    {
      // Write the buffer to successive rows until every row of the surface
      // has been written. We place the start column beyond the end of the row,
      // which will prevent us from writing anything, so we check that the
      // generated image is entirely transparent.
      CheckIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0),
                          [&] { return aSink->WriteBuffer(buffer, 100, 100); });
    }

    ResetForNextPass(aSink);

    {
      // Write the buffer to successive rows until every row of the surface
      // has been written. We use column 50 as the start column, but we still
      // write the buffer, which means we overflow the right edge of the surface
      // by 50 pixels. We check that the left half of the generated image is
      // transparent and the right half is green.
      CheckIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100),
                          [&] { return aSink->WriteBuffer(buffer, 50, 100); });
    }
  });
}

TEST(ImageSurfaceSink, SurfaceSinkWriteBufferPartialRowBufferOverflow)
{
  WithSurfaceSink<Orient::NORMAL>([](image::Decoder* aDecoder,
                                     SurfaceSink* aSink) {
    // Create a buffer twice as large as a row of the surface. The first half
    // (which is as large as a row of the image) will contain green pixels,
    // while the second half will contain red pixels.
    uint32_t buffer[200];
    for (int i = 0; i < 200; ++i) {
      buffer[i] =
          i < 100 ? BGRAColor::Green().AsPixel() : BGRAColor::Red().AsPixel();
    }

    {
      // Write the buffer to successive rows until every row of the surface has
      // been written. The buffer extends 100 pixels to the right of a row of
      // the surface, but bounds checking will prevent us from overflowing the
      // buffer. We check that the generated image is entirely green since the
      // pixels on the right side of the buffer shouldn't have been written to
      // the surface.
      CheckIterativeWrite(aDecoder, aSink, IntRect(0, 0, 100, 100),
                          [&] { return aSink->WriteBuffer(buffer, 0, 200); });
    }

    ResetForNextPass(aSink);

    {
      // Write from the buffer to the middle of each row of the surface. That
      // means that the left side of each row should be transparent, since we
      // didn't write anything there. A buffer overflow would cause us to write
      // buffer contents into the left side of each row. We check that the
      // generated image is transparent on the left side and green on the right.
      CheckIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100),
                          [&] { return aSink->WriteBuffer(buffer, 50, 200); });
    }
  });
}

TEST(ImageSurfaceSink, SurfaceSinkWriteBufferFromNullSource)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        // Calling WriteBuffer() with a null pointer should fail without making
        // any changes to the surface.
        uint32_t* nullBuffer = nullptr;
        WriteState result = aSink->WriteBuffer(nullBuffer);

        EXPECT_EQ(WriteState::FAILURE, result);
        EXPECT_FALSE(aSink->IsSurfaceFinished());
        Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
        EXPECT_TRUE(invalidRect.isNothing());

        // Check that nothing got written to the surface.
        CheckGeneratedImage(aDecoder, IntRect(0, 0, 0, 0));
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWriteEmptyRow)
{
  WithSurfaceSink<Orient::NORMAL>([](image::Decoder* aDecoder,
                                     SurfaceSink* aSink) {
    {
      // Write an empty row to each row of the surface. We check that the
      // generated image is entirely transparent.
      CheckIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0),
                          [&] { return aSink->WriteEmptyRow(); });
    }

    ResetForNextPass(aSink);

    {
      // Write a partial row before we begin calling WriteEmptyRow(). We check
      // that the generated image is entirely transparent, which is to be
      // expected since WriteEmptyRow() overwrites the current row even if some
      // data has already been written to it.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
        if (count == 50) {
          return AsVariant(WriteState::NEED_MORE_DATA);
        }
        ++count;
        return AsVariant(BGRAColor::Green().AsPixel());
      });

      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(50u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      CheckIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0),
                          [&] { return aSink->WriteEmptyRow(); });
    }

    ResetForNextPass(aSink);

    {
      // Create a buffer the same size as one row of the surface, containing all
      // green pixels.
      uint32_t buffer[100];
      for (int i = 0; i < 100; ++i) {
        buffer[i] = BGRAColor::Green().AsPixel();
      }

      // Write an empty row to the middle 60 rows of the surface. The first 20
      // and last 20 rows will be green. (We need to use DoCheckIterativeWrite()
      // here because we need a custom function to check the output, since it
      // can't be described by a simple rect.)
      auto writeFunc = [&](uint32_t aRow) {
        if (aRow < 20 || aRow >= 80) {
          return aSink->WriteBuffer(buffer);
        } else {
          return aSink->WriteEmptyRow();
        }
      };

      auto checkFunc = [&] {
        RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
        RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();

        EXPECT_TRUE(RowsAreSolidColor(surface, 0, 20, BGRAColor::Green()));
        EXPECT_TRUE(
            RowsAreSolidColor(surface, 20, 60, BGRAColor::Transparent()));
        EXPECT_TRUE(RowsAreSolidColor(surface, 80, 20, BGRAColor::Green()));
      };

      DoCheckIterativeWrite(aSink, writeFunc, checkFunc);
    }
  });
}

TEST(ImageSurfaceSink, SurfaceSinkWriteUnsafeComputedRow)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        // Create a green buffer the same size as one row of the surface.
        uint32_t buffer[100];
        for (int i = 0; i < 100; ++i) {
          buffer[i] = BGRAColor::Green().AsPixel();
        }

        // Write the buffer to successive rows until every row of the surface
        // has been written. We only write to the right half of each row, so we
        // check that the left side of the generated image is transparent and
        // the right side is green.
        CheckIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&] {
          return aSink->WriteUnsafeComputedRow<uint32_t>(
              [&](uint32_t* aRow, uint32_t aLength) {
                EXPECT_EQ(100u, aLength);
                memcpy(aRow + 50, buffer, 50 * sizeof(uint32_t));
              });
        });
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWritePixelBlocks)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        // Create a green buffer the same size as one row of the surface (which
        // is 100x100), containing 60 pixels of green in the middle and 20
        // transparent pixels on either side.
        uint32_t buffer[100];
        InitializeRowBuffer(buffer, 100, 20, 80, BGRAColor::Green().AsPixel());

        uint32_t count = 0;
        WriteState result = aSink->WritePixelBlocks<uint32_t>(
            [&](uint32_t* aBlockStart, int32_t aLength) {
              ++count;
              EXPECT_EQ(int32_t(100), aLength);
              memcpy(aBlockStart, buffer, 100 * sizeof(uint32_t));
              return std::make_tuple(int32_t(100), Maybe<WriteState>());
            });

        EXPECT_EQ(WriteState::FINISHED, result);
        EXPECT_EQ(100u, count);

        AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 100),
                                        IntRect(0, 0, 100, 100));

        // Check that the generated image is correct.
        CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, 100));

        // Attempt to write more and make sure that nothing gets written.
        count = 0;
        result = aSink->WritePixelBlocks<uint32_t>(
            [&](uint32_t* aBlockStart, int32_t aLength) {
              count++;
              for (int32_t i = 0; i < aLength; ++i) {
                aBlockStart[i] = BGRAColor::Red().AsPixel();
              }
              return std::make_tuple(aLength, Maybe<WriteState>());
            });

        EXPECT_EQ(WriteState::FINISHED, result);
        EXPECT_EQ(0u, count);
        EXPECT_TRUE(aSink->IsSurfaceFinished());

        // Check that the generated image is still correct.
        CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, 100));
      });
}

TEST(ImageSurfaceSink, SurfaceSinkWritePixelBlocksPartialRow)
{
  WithSurfaceSink<Orient::NORMAL>([](image::Decoder* aDecoder,
                                     SurfaceSink* aSink) {
    // Create a green buffer the same size as one row of the surface (which is
    // 100x100), containing 60 pixels of green in the middle and 20 transparent
    // pixels on either side.
    uint32_t buffer[100];
    InitializeRowBuffer(buffer, 100, 20, 80, BGRAColor::Green().AsPixel());

    // Write the first 99 rows of our 100x100 surface and verify that even
    // though our lambda will yield pixels forever, only one row is written per
    // call to WritePixelsToRow().
    for (int row = 0; row < 99; ++row) {
      for (int32_t written = 0; written < 100;) {
        WriteState result = aSink->WritePixelBlocks<uint32_t>(
            [&](uint32_t* aBlockStart, int32_t aLength) {
              // When we write the final block of pixels, it will request we
              // start another row. We should abort at that point.
              if (aLength == int32_t(100) && written == int32_t(100)) {
                return std::make_tuple(int32_t(0),
                                       Some(WriteState::NEED_MORE_DATA));
              }

              // It should always request enough data to fill the row. So it
              // should request 100, 75, 50, and finally 25 pixels.
              EXPECT_EQ(int32_t(100) - written, aLength);

              // Only write one quarter of the pixels for the row.
              memcpy(aBlockStart, &buffer[written], 25 * sizeof(uint32_t));
              written += 25;

              // We've written the last pixels remaining for the row.
              if (written == int32_t(100)) {
                return std::make_tuple(int32_t(25), Maybe<WriteState>());
              }

              // We've written another quarter of the row but not yet all of it.
              return std::make_tuple(int32_t(25),
                                     Some(WriteState::NEED_MORE_DATA));
            });

        EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      }

      EXPECT_FALSE(aSink->IsSurfaceFinished());

      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isSome());
      EXPECT_EQ(OrientedIntRect(0, row, 100, 1), invalidRect->mInputSpaceRect);
      EXPECT_EQ(OrientedIntRect(0, row, 100, 1), invalidRect->mOutputSpaceRect);

      CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, row + 1));
    }

    // Write the final line, which should finish the surface.
    uint32_t count = 0;
    WriteState result = aSink->WritePixelBlocks<uint32_t>(
        [&](uint32_t* aBlockStart, int32_t aLength) {
          ++count;
          EXPECT_EQ(int32_t(100), aLength);
          memcpy(aBlockStart, buffer, 100 * sizeof(uint32_t));
          return std::make_tuple(int32_t(100), Maybe<WriteState>());
        });

    EXPECT_EQ(WriteState::FINISHED, result);
    EXPECT_EQ(1u, count);

    // Note that the final invalid rect we expect here is only the last row;
    // that's because we called TakeInvalidRect() repeatedly in the loop above.
    AssertCorrectPipelineFinalState(aSink, IntRect(0, 99, 100, 1),
                                    IntRect(0, 99, 100, 1));

    // Check that the generated image is correct.
    CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, 100));

    // Attempt to write more and make sure that nothing gets written.
    count = 0;
    result = aSink->WritePixelBlocks<uint32_t>(
        [&](uint32_t* aBlockStart, int32_t aLength) {
          count++;
          for (int32_t i = 0; i < aLength; ++i) {
            aBlockStart[i] = BGRAColor::Red().AsPixel();
          }
          return std::make_tuple(aLength, Maybe<WriteState>());
        });

    EXPECT_EQ(WriteState::FINISHED, result);
    EXPECT_EQ(0u, count);
    EXPECT_TRUE(aSink->IsSurfaceFinished());

    // Check that the generated image is still correct.
    CheckGeneratedImage(aDecoder, IntRect(20, 0, 60, 100));
  });
}

TEST(ImageSurfaceSink, SurfaceSinkProgressivePasses)
{
  WithSurfaceSink<Orient::NORMAL>(
      [](image::Decoder* aDecoder, SurfaceSink* aSink) {
        {
          // Fill the image with a first pass of red.
          uint32_t count = 0;
          auto result = aSink->WritePixels<uint32_t>([&]() {
            ++count;
            return AsVariant(BGRAColor::Red().AsPixel());
          });
          EXPECT_EQ(WriteState::FINISHED, result);
          EXPECT_EQ(100u * 100u, count);

          AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 100),
                                          IntRect(0, 0, 100, 100));

          // Check that the generated image is correct.
          RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
          RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
          EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red()));
        }

        {
          ResetForNextPass(aSink);

          // Check that the generated image is still the first pass image.
          RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
          RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
          EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red()));
        }

        {
          // Fill the image with a second pass of green.
          uint32_t count = 0;
          auto result = aSink->WritePixels<uint32_t>([&]() {
            ++count;
            return AsVariant(BGRAColor::Green().AsPixel());
          });
          EXPECT_EQ(WriteState::FINISHED, result);
          EXPECT_EQ(100u * 100u, count);

          AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 100),
                                          IntRect(0, 0, 100, 100));

          // Check that the generated image is correct.
          RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
          RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
          EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green()));
        }
      });
}

TEST(ImageSurfaceSink, SurfaceSinkInvalidRect)
{
  WithSurfaceSink<Orient::NORMAL>([](image::Decoder* aDecoder,
                                     SurfaceSink* aSink) {
    {
      // Write one row.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
        if (count == 100) {
          return AsVariant(WriteState::NEED_MORE_DATA);
        }
        count++;
        return AsVariant(BGRAColor::Green().AsPixel());
      });
      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(100u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      // Assert that we have the right invalid rect.
      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isSome());
      EXPECT_EQ(OrientedIntRect(0, 0, 100, 1), invalidRect->mInputSpaceRect);
      EXPECT_EQ(OrientedIntRect(0, 0, 100, 1), invalidRect->mOutputSpaceRect);
    }

    {
      // Write eight rows.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
        if (count == 100 * 8) {
          return AsVariant(WriteState::NEED_MORE_DATA);
        }
        count++;
        return AsVariant(BGRAColor::Green().AsPixel());
      });
      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(100u * 8u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      // Assert that we have the right invalid rect.
      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isSome());
      EXPECT_EQ(OrientedIntRect(0, 1, 100, 8), invalidRect->mInputSpaceRect);
      EXPECT_EQ(OrientedIntRect(0, 1, 100, 8), invalidRect->mOutputSpaceRect);
    }

    {
      // Write the left half of one row.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
        if (count == 50) {
          return AsVariant(WriteState::NEED_MORE_DATA);
        }
        count++;
        return AsVariant(BGRAColor::Green().AsPixel());
      });
      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(50u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      // Assert that we don't have an invalid rect, since the invalid rect only
      // gets updated when a row gets completed.
      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isNothing());
    }

    {
      // Write the right half of the same row.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
        if (count == 50) {
          return AsVariant(WriteState::NEED_MORE_DATA);
        }
        count++;
        return AsVariant(BGRAColor::Green().AsPixel());
      });
      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(50u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      // Assert that we have the right invalid rect, which will include both the
      // left and right halves of this row now that we've completed it.
      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isSome());
      EXPECT_EQ(OrientedIntRect(0, 9, 100, 1), invalidRect->mInputSpaceRect);
      EXPECT_EQ(OrientedIntRect(0, 9, 100, 1), invalidRect->mOutputSpaceRect);
    }

    {
      // Write no rows.
      auto result = aSink->WritePixels<uint32_t>(
          [&]() { return AsVariant(WriteState::NEED_MORE_DATA); });
      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      // Assert that we don't have an invalid rect.
      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isNothing());
    }

    {
      // Fill the rest of the image.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() {
        count++;
        return AsVariant(BGRAColor::Green().AsPixel());
      });
      EXPECT_EQ(WriteState::FINISHED, result);
      EXPECT_EQ(100u * 90u, count);
      EXPECT_TRUE(aSink->IsSurfaceFinished());

      // Assert that we have the right invalid rect.
      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isSome());
      EXPECT_EQ(OrientedIntRect(0, 10, 100, 90), invalidRect->mInputSpaceRect);
      EXPECT_EQ(OrientedIntRect(0, 10, 100, 90), invalidRect->mOutputSpaceRect);

      // Check that the generated image is correct.
      RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
      RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
      EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green()));
    }
  });
}

TEST(ImageSurfaceSink, SurfaceSinkFlipVertically)
{
  WithSurfaceSink<Orient::FLIP_VERTICALLY>([](image::Decoder* aDecoder,
                                              SurfaceSink* aSink) {
    {
      // Fill the image with a first pass of red.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() {
        ++count;
        return AsVariant(BGRAColor::Red().AsPixel());
      });
      EXPECT_EQ(WriteState::FINISHED, result);
      EXPECT_EQ(100u * 100u, count);

      AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 100),
                                      IntRect(0, 0, 100, 100));

      // Check that the generated image is correct.
      RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
      RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
      EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red()));
    }

    {
      ResetForNextPass(aSink);

      // Check that the generated image is still the first pass image.
      RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
      RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
      EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red()));
    }

    {
      // Fill 25 rows of the image with green and make sure everything is OK.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
        if (count == 25 * 100) {
          return AsVariant(WriteState::NEED_MORE_DATA);
        }
        count++;
        return AsVariant(BGRAColor::Green().AsPixel());
      });
      EXPECT_EQ(WriteState::NEED_MORE_DATA, result);
      EXPECT_EQ(25u * 100u, count);
      EXPECT_FALSE(aSink->IsSurfaceFinished());

      // Assert that we have the right invalid rect, which should include the
      // *bottom* (since we're flipping vertically) 25 rows of the image.
      Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
      EXPECT_TRUE(invalidRect.isSome());
      EXPECT_EQ(OrientedIntRect(0, 75, 100, 25), invalidRect->mInputSpaceRect);
      EXPECT_EQ(OrientedIntRect(0, 75, 100, 25), invalidRect->mOutputSpaceRect);

      // Check that the generated image is correct.
      RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
      RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
      EXPECT_TRUE(RowsAreSolidColor(surface, 0, 75, BGRAColor::Red()));
      EXPECT_TRUE(RowsAreSolidColor(surface, 75, 25, BGRAColor::Green()));
    }

    {
      // Fill the rest of the image with a second pass of green.
      uint32_t count = 0;
      auto result = aSink->WritePixels<uint32_t>([&]() {
        ++count;
        return AsVariant(BGRAColor::Green().AsPixel());
      });
      EXPECT_EQ(WriteState::FINISHED, result);
      EXPECT_EQ(75u * 100u, count);

      AssertCorrectPipelineFinalState(aSink, IntRect(0, 0, 100, 75),
                                      IntRect(0, 0, 100, 75));

      // Check that the generated image is correct.
      RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
      RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
      EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green()));
    }
  });
}