Bug 1276061 (Part 4) - Add GTests for WriteBuffer() and WriteEmptyRow(). r=njn

--HG--
extra : rebase_source : 32628df4518af1fad89c69fa7aa5a87e3c325dc4
This commit is contained in:
Seth Fowler 2016-06-01 23:01:50 -07:00
Родитель dfd4e97c3d
Коммит 2e1d5487fb
2 изменённых файлов: 716 добавлений и 31 удалений

Просмотреть файл

@ -42,6 +42,93 @@ private:
} // namespace image
} // namespace mozilla
void
CheckSurfacePipeMethodResults(SurfacePipe* aPipe,
Decoder* aDecoder,
const IntRect& aRect = IntRect(0, 0, 100, 100))
{
// Check that the pipeline ended up in the state we expect. Note that we're
// explicitly testing the SurfacePipe versions of these methods, so we don't
// want to use AssertCorrectPipelineFinalState() here.
EXPECT_TRUE(aPipe->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aPipe->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isSome());
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
// Check the generated image.
CheckGeneratedImage(aDecoder, aRect);
// Reset and clear the image before the next test.
aPipe->ResetToFirstRow();
EXPECT_FALSE(aPipe->IsSurfaceFinished());
invalidRect = aPipe->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
uint32_t count = 0;
auto result = aPipe->WritePixels<uint32_t>([&]() {
++count;
return AsVariant(BGRAColor::Transparent().AsPixel());
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u * 100u, count);
EXPECT_TRUE(aPipe->IsSurfaceFinished());
invalidRect = aPipe->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isSome());
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
aPipe->ResetToFirstRow();
EXPECT_FALSE(aPipe->IsSurfaceFinished());
invalidRect = aPipe->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
}
void
CheckPalettedSurfacePipeMethodResults(SurfacePipe* aPipe,
Decoder* aDecoder,
const IntRect& aRect
= IntRect(0, 0, 100, 100))
{
// Check that the pipeline ended up in the state we expect. Note that we're
// explicitly testing the SurfacePipe versions of these methods, so we don't
// want to use AssertCorrectPipelineFinalState() here.
EXPECT_TRUE(aPipe->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aPipe->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isSome());
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
// Check the generated image.
CheckGeneratedPalettedImage(aDecoder, aRect);
// Reset and clear the image before the next test.
aPipe->ResetToFirstRow();
EXPECT_FALSE(aPipe->IsSurfaceFinished());
invalidRect = aPipe->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
uint32_t count = 0;
auto result = aPipe->WritePixels<uint8_t>([&]() {
++count;
return AsVariant(uint8_t(0));
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u * 100u, count);
EXPECT_TRUE(aPipe->IsSurfaceFinished());
invalidRect = aPipe->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isSome());
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
aPipe->ResetToFirstRow();
EXPECT_FALSE(aPipe->IsSurfaceFinished());
invalidRect = aPipe->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
}
TEST(ImageSurfacePipeIntegration, SurfacePipe)
{
// Test that SurfacePipe objects can be initialized and move constructed.
@ -56,34 +143,152 @@ TEST(ImageSurfacePipeIntegration, SurfacePipe)
auto sink = MakeUnique<SurfaceSink>();
nsresult rv =
sink->Configure(SurfaceConfig { decoder.get(), 0, IntSize(100, 100),
sink->Configure(SurfaceConfig { decoder, 0, IntSize(100, 100),
SurfaceFormat::B8G8R8A8, false });
ASSERT_TRUE(NS_SUCCEEDED(rv));
pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink);
// Test that SurfacePipe passes through method calls to the underlying pipeline.
int32_t count = 0;
auto result = pipe.WritePixels<uint32_t>([&]() {
++count;
return AsVariant(BGRAColor::Green().AsPixel());
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100 * 100, count);
// Test that WritePixels() gets passed through to the underlying pipeline.
{
uint32_t count = 0;
auto result = pipe.WritePixels<uint32_t>([&]() {
++count;
return AsVariant(BGRAColor::Green().AsPixel());
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u * 100u, count);
CheckSurfacePipeMethodResults(&pipe, decoder);
}
// Note that we're explicitly testing the SurfacePipe versions of these
// methods, so we don't want to use AssertCorrectPipelineFinalState() here.
EXPECT_TRUE(pipe.IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = pipe.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isSome());
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect);
// Create a buffer the same size as one row of the surface, containing all
// green pixels. We'll use this for the WriteBuffer() tests.
uint32_t buffer[100];
for (int i = 0; i < 100; ++i) {
buffer[i] = BGRAColor::Green().AsPixel();
}
CheckGeneratedImage(decoder, IntRect(0, 0, 100, 100));
// Test that WriteBuffer() gets passed through to the underlying pipeline.
{
uint32_t count = 0;
WriteState result = WriteState::NEED_MORE_DATA;
while (result == WriteState::NEED_MORE_DATA) {
result = pipe.WriteBuffer(buffer);
++count;
}
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u, count);
CheckSurfacePipeMethodResults(&pipe, decoder);
}
pipe.ResetToFirstRow();
EXPECT_FALSE(pipe.IsSurfaceFinished());
// Test that the 3 argument version of WriteBuffer() gets passed through to
// the underlying pipeline.
{
uint32_t count = 0;
WriteState result = WriteState::NEED_MORE_DATA;
while (result == WriteState::NEED_MORE_DATA) {
result = pipe.WriteBuffer(buffer, 0, 100);
++count;
}
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u, count);
CheckSurfacePipeMethodResults(&pipe, decoder);
}
// Test that WriteEmptyRow() gets passed through to the underlying pipeline.
{
uint32_t count = 0;
WriteState result = WriteState::NEED_MORE_DATA;
while (result == WriteState::NEED_MORE_DATA) {
result = pipe.WriteEmptyRow();
++count;
}
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u, count);
CheckSurfacePipeMethodResults(&pipe, decoder, IntRect(0, 0, 0, 0));
}
// Mark the frame as finished so we don't get an assertion.
RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef();
currentFrame->Finish();
}
TEST(ImageSurfacePipeIntegration, PalettedSurfacePipe)
{
// Create a SurfacePipe containing a PalettedSurfaceSink.
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
auto sink = MakeUnique<PalettedSurfaceSink>();
nsresult rv =
sink->Configure(PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
IntRect(0, 0, 100, 100),
SurfaceFormat::B8G8R8A8,
8, false });
ASSERT_TRUE(NS_SUCCEEDED(rv));
SurfacePipe pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink);
// Test that WritePixels() gets passed through to the underlying pipeline.
{
uint32_t count = 0;
auto result = pipe.WritePixels<uint8_t>([&]() {
++count;
return AsVariant(uint8_t(255));
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u * 100u, count);
CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
}
// Create a buffer the same size as one row of the surface, containing all
// 255 pixels. We'll use this for the WriteBuffer() tests.
uint8_t buffer[100];
for (int i = 0; i < 100; ++i) {
buffer[i] = 255;
}
// Test that WriteBuffer() gets passed through to the underlying pipeline.
{
uint32_t count = 0;
WriteState result = WriteState::NEED_MORE_DATA;
while (result == WriteState::NEED_MORE_DATA) {
result = pipe.WriteBuffer(buffer);
++count;
}
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u, count);
CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
}
// Test that the 3 argument version of WriteBuffer() gets passed through to
// the underlying pipeline.
{
uint32_t count = 0;
WriteState result = WriteState::NEED_MORE_DATA;
while (result == WriteState::NEED_MORE_DATA) {
result = pipe.WriteBuffer(buffer, 0, 100);
++count;
}
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u, count);
CheckPalettedSurfacePipeMethodResults(&pipe, decoder);
}
// Test that WriteEmptyRow() gets passed through to the underlying pipeline.
{
uint32_t count = 0;
WriteState result = WriteState::NEED_MORE_DATA;
while (result == WriteState::NEED_MORE_DATA) {
result = pipe.WriteEmptyRow();
++count;
}
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u, count);
CheckPalettedSurfacePipeMethodResults(&pipe, decoder, IntRect(0, 0, 0, 0));
}
// Mark the frame as finished so we don't get an assertion.
RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef();
currentFrame->Finish();
}

Просмотреть файл

@ -48,6 +48,71 @@ WithPalettedSurfaceSink(const IntRect& aFrameRect, Func aFunc)
8, false });
}
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(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);
});
}
template <typename WriteFunc> void
CheckPalettedIterativeWrite(Decoder* aDecoder,
PalettedSurfaceSink* 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, [&]{
CheckGeneratedPalettedImage(aDecoder, aOutputRect);
});
}
TEST(ImageSurfaceSink, NullSurfaceSink)
{
// Create the NullSurfaceSink.
@ -68,9 +133,31 @@ TEST(ImageSurfaceSink, NullSurfaceSink)
Maybe<SurfaceInvalidRect> invalidRect = sink.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
result = sink.WritePixels<uint32_t>([&]() {
uint32_t source = BGRAColor::Red().AsPixel();
result = sink.WriteBuffer(&source);
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_TRUE(sink.IsSurfaceFinished());
invalidRect = sink.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
result = sink.WriteBuffer(&source, 0, 1);
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_TRUE(sink.IsSurfaceFinished());
invalidRect = sink.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
result = sink.WriteEmptyRow();
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_TRUE(sink.IsSurfaceFinished());
invalidRect = sink.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
result = sink.WriteUnsafeComputedRow<uint32_t>([&](uint32_t* aRow,
uint32_t aLength) {
gotCalled = true;
return AsVariant(BGRAColor::Red().AsPixel());
for (uint32_t col = 0; col < aLength; ++col, ++aRow) {
*aRow = BGRAColor::Red().AsPixel();
}
});
EXPECT_FALSE(gotCalled);
EXPECT_EQ(WriteState::FINISHED, result);
@ -145,6 +232,206 @@ TEST(ImageSurfaceSink, SurfaceSinkWritePixelsFinish)
});
}
TEST(ImageSurfaceSink, SurfaceSinkWriteBuffer)
{
WithSurfaceSink<Orient::NORMAL>([](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];
for (int i = 0; i < 100; ++i) {
buffer[i] = 20 <= i && i < 80 ? BGRAColor::Green().AsPixel()
: BGRAColor::Transparent().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>([](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>([](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>([](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>([](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>([](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);
{
// 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->GetSurface();
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>([](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, SurfaceSinkProgressivePasses)
{
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
@ -169,11 +456,7 @@ TEST(ImageSurfaceSink, SurfaceSinkProgressivePasses)
}
{
// Reset for the second pass.
aSink->ResetToFirstRow();
EXPECT_FALSE(aSink->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
ResetForNextPass(aSink);
// Check that the generated image is still the first pass image.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
@ -353,11 +636,7 @@ TEST(ImageSurfaceSink, SurfaceSinkFlipVertically)
}
{
// Reset for the second pass.
aSink->ResetToFirstRow();
EXPECT_FALSE(aSink->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
ResetForNextPass(aSink);
// Check that the generated image is still the first pass image.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
@ -504,3 +783,204 @@ TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsFor75_75_50_50)
/* aOutputWriteRect = */ Some(IntRect(75, 75, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBuffer)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
// Create a buffer the same size as one row of the surface (which is 100x100),
// containing 60 pixels of 255 in the middle and 20 transparent pixels of 0 on
// either side.
uint8_t buffer[100];
for (int i = 0; i < 100; ++i) {
buffer[i] = 20 <= i && i < 80 ? 255 : 0;
}
// Write the buffer to every row of the surface and check that the generated
// image is correct.
CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(20, 0, 60, 100), [&]{
return aSink->WriteBuffer(buffer);
});
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBufferPartialRow)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
// Create a buffer the same size as one row of the surface, containing all
// 255 pixels.
uint8_t buffer[100];
for (int i = 0; i < 100; ++i) {
buffer[i] = 255;
}
// Write the buffer to the middle 60 pixels of every row of the surface and
// check that the generated image is correct.
CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(20, 0, 60, 100), [&]{
return aSink->WriteBuffer(buffer, 20, 60);
});
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBufferPartialRowStartColOverflow)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
// Create a buffer the same size as one row of the surface, containing all
// 255 pixels.
uint8_t buffer[100];
for (int i = 0; i < 100; ++i) {
buffer[i] = 255;
}
{
// 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 0.
CheckPalettedIterativeWrite(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
// 0 and the right half is 255.
CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{
return aSink->WriteBuffer(buffer, 50, 100);
});
}
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBufferPartialRowBufferOverflow)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* 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 255 pixels,
// while the second half will contain 128 pixels.
uint8_t buffer[200];
for (int i = 0; i < 200; ++i) {
buffer[i] = i < 100 ? 255 : 128;
}
{
// 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 255 since the
// pixels on the right side of the buffer shouldn't have been written to
// the surface.
CheckPalettedIterativeWrite(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 0, 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 0 on the left side and 255 on the right.
CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{
return aSink->WriteBuffer(buffer, 50, 200);
});
}
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBufferFromNullSource)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
// Calling WriteBuffer() with a null pointer should fail without making any
// changes to the surface.
uint8_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.
CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 0, 0));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteEmptyRow)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
{
// Write an empty row to each row of the surface. We check that the
// generated image is entirely 0.
CheckPalettedIterativeWrite(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
// 255 pixels.
uint8_t buffer[100];
for (int i = 0; i < 100; ++i) {
buffer[i] = 255;
}
// Write an empty row to the middle 60 rows of the surface. The first 20
// and last 20 rows will be 255. (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 = [&]{
EXPECT_TRUE(PalettedRowsAreSolidColor(aDecoder, 0, 20, 255));
EXPECT_TRUE(PalettedRowsAreSolidColor(aDecoder, 20, 60, 0));
EXPECT_TRUE(PalettedRowsAreSolidColor(aDecoder, 80, 20, 255));
};
DoCheckIterativeWrite(aSink, writeFunc, checkFunc);
}
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteUnsafeComputedRow)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
// Create an all-255 buffer the same size as one row of the surface.
uint8_t buffer[100];
for (int i = 0; i < 100; ++i) {
buffer[i] = 255;
}
// 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 0 and the right side is 255.
CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{
return aSink->WriteUnsafeComputedRow<uint8_t>([&](uint8_t* aRow,
uint32_t aLength) {
EXPECT_EQ(100u, aLength );
memcpy(aRow + 50, buffer, 50 * sizeof(uint8_t));
});
});
});
}