/* -*- 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 "SurfaceFilters.h" #include "SurfacePipe.h" using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::image; template void WithDeinterlacingFilter(const IntSize& aSize, bool aProgressiveDisplay, Func aFunc) { RefPtr decoder = CreateTrivialDecoder(); ASSERT_TRUE(bool(decoder)); WithFilterPipeline( decoder, std::forward(aFunc), DeinterlacingConfig{aProgressiveDisplay}, SurfaceConfig{decoder, aSize, SurfaceFormat::OS_RGBA, false}); } void AssertConfiguringDeinterlacingFilterFails(const IntSize& aSize) { RefPtr decoder = CreateTrivialDecoder(); ASSERT_TRUE(decoder != nullptr); AssertConfiguringPipelineFails( decoder, DeinterlacingConfig{/* mProgressiveDisplay = */ true}, SurfaceConfig{decoder, aSize, SurfaceFormat::OS_RGBA, false}); } class ImageDeinterlacingFilter : public ::testing::Test { protected: AutoInitializeImageLib mInit; }; TEST_F(ImageDeinterlacingFilter, WritePixels100_100) { WithDeinterlacingFilter( IntSize(100, 100), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { CheckWritePixels(aDecoder, aFilter, /* aOutputRect = */ Some(IntRect(0, 0, 100, 100)), /* aInputRect = */ Some(IntRect(0, 0, 100, 100))); }); } TEST_F(ImageDeinterlacingFilter, WritePixels99_99) { WithDeinterlacingFilter(IntSize(99, 99), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { CheckWritePixels( aDecoder, aFilter, /* aOutputRect = */ Some(IntRect(0, 0, 99, 99)), /* aInputRect = */ Some(IntRect(0, 0, 99, 99))); }); } TEST_F(ImageDeinterlacingFilter, WritePixels8_8) { WithDeinterlacingFilter(IntSize(8, 8), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { CheckWritePixels( aDecoder, aFilter, /* aOutputRect = */ Some(IntRect(0, 0, 8, 8)), /* aInputRect = */ Some(IntRect(0, 0, 8, 8))); }); } TEST_F(ImageDeinterlacingFilter, WritePixels7_7) { WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { CheckWritePixels( aDecoder, aFilter, /* aOutputRect = */ Some(IntRect(0, 0, 7, 7)), /* aInputRect = */ Some(IntRect(0, 0, 7, 7))); }); } TEST_F(ImageDeinterlacingFilter, WritePixels3_3) { WithDeinterlacingFilter(IntSize(3, 3), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { CheckWritePixels( aDecoder, aFilter, /* aOutputRect = */ Some(IntRect(0, 0, 3, 3)), /* aInputRect = */ Some(IntRect(0, 0, 3, 3))); }); } TEST_F(ImageDeinterlacingFilter, WritePixels1_1) { WithDeinterlacingFilter(IntSize(1, 1), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { CheckWritePixels( aDecoder, aFilter, /* aOutputRect = */ Some(IntRect(0, 0, 1, 1)), /* aInputRect = */ Some(IntRect(0, 0, 1, 1))); }); } TEST_F(ImageDeinterlacingFilter, WritePixelsNonProgressiveOutput51_52) { WithDeinterlacingFilter( IntSize(51, 52), /* aProgressiveDisplay = */ false, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { // Fill the image. The output should be green for even rows and red for // odd rows but we need to write the rows in the order that the // deinterlacer expects them. uint32_t count = 0; auto result = aFilter->WritePixels([&]() { uint32_t row = count / 51; // Integer division. ++count; // Note that we use a switch statement here, even though it's quite // verbose, because it's useful to have the mappings between input and // output rows available when debugging these tests. switch (row) { // First pass. Output rows are positioned at 8n + 0. case 0: // Output row 0. case 1: // Output row 8. case 2: // Output row 16. case 3: // Output row 24. case 4: // Output row 32. case 5: // Output row 40. case 6: // Output row 48. return AsVariant(BGRAColor::Green().AsPixel()); // Second pass. Rows are positioned at 8n + 4. case 7: // Output row 4. case 8: // Output row 12. case 9: // Output row 20. case 10: // Output row 28. case 11: // Output row 36. case 12: // Output row 44. return AsVariant(BGRAColor::Green().AsPixel()); // Third pass. Rows are positioned at 4n + 2. case 13: // Output row 2. case 14: // Output row 6. case 15: // Output row 10. case 16: // Output row 14. case 17: // Output row 18. case 18: // Output row 22. case 19: // Output row 26. case 20: // Output row 30. case 21: // Output row 34. case 22: // Output row 38. case 23: // Output row 42. case 24: // Output row 46. case 25: // Output row 50. return AsVariant(BGRAColor::Green().AsPixel()); // Fourth pass. Rows are positioned at 2n + 1. case 26: // Output row 1. case 27: // Output row 3. case 28: // Output row 5. case 29: // Output row 7. case 30: // Output row 9. case 31: // Output row 11. case 32: // Output row 13. case 33: // Output row 15. case 34: // Output row 17. case 35: // Output row 19. case 36: // Output row 21. case 37: // Output row 23. case 38: // Output row 25. case 39: // Output row 27. case 40: // Output row 29. case 41: // Output row 31. case 42: // Output row 33. case 43: // Output row 35. case 44: // Output row 37. case 45: // Output row 39. case 46: // Output row 41. case 47: // Output row 43. case 48: // Output row 45. case 49: // Output row 47. case 50: // Output row 49. case 51: // Output row 51. return AsVariant(BGRAColor::Red().AsPixel()); default: MOZ_ASSERT_UNREACHABLE("Unexpected row"); return AsVariant(BGRAColor::Transparent().AsPixel()); } }); EXPECT_EQ(WriteState::FINISHED, result); EXPECT_EQ(51u * 52u, count); AssertCorrectPipelineFinalState(aFilter, IntRect(0, 0, 51, 52), IntRect(0, 0, 51, 52)); // Check that the generated image is correct. As mentioned above, we // expect even rows to be green and odd rows to be red. RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr surface = currentFrame->GetSourceSurface(); for (uint32_t row = 0; row < 52; ++row) { EXPECT_TRUE(RowsAreSolidColor( surface, row, 1, row % 2 == 0 ? BGRAColor::Green() : BGRAColor::Red())); } }); } TEST_F(ImageDeinterlacingFilter, WritePixelsOutput20_20) { WithDeinterlacingFilter( IntSize(20, 20), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { // Fill the image. The output should be green for even rows and red for // odd rows but we need to write the rows in the order that the // deinterlacer expects them. uint32_t count = 0; auto result = aFilter->WritePixels([&]() { uint32_t row = count / 20; // Integer division. ++count; // Note that we use a switch statement here, even though it's quite // verbose, because it's useful to have the mappings between input and // output rows available when debugging these tests. switch (row) { // First pass. Output rows are positioned at 8n + 0. case 0: // Output row 0. case 1: // Output row 8. case 2: // Output row 16. return AsVariant(BGRAColor::Green().AsPixel()); // Second pass. Rows are positioned at 8n + 4. case 3: // Output row 4. case 4: // Output row 12. return AsVariant(BGRAColor::Green().AsPixel()); // Third pass. Rows are positioned at 4n + 2. case 5: // Output row 2. case 6: // Output row 6. case 7: // Output row 10. case 8: // Output row 14. case 9: // Output row 18. return AsVariant(BGRAColor::Green().AsPixel()); // Fourth pass. Rows are positioned at 2n + 1. case 10: // Output row 1. case 11: // Output row 3. case 12: // Output row 5. case 13: // Output row 7. case 14: // Output row 9. case 15: // Output row 11. case 16: // Output row 13. case 17: // Output row 15. case 18: // Output row 17. case 19: // Output row 19. return AsVariant(BGRAColor::Red().AsPixel()); default: MOZ_ASSERT_UNREACHABLE("Unexpected row"); return AsVariant(BGRAColor::Transparent().AsPixel()); } }); EXPECT_EQ(WriteState::FINISHED, result); EXPECT_EQ(20u * 20u, count); AssertCorrectPipelineFinalState(aFilter, IntRect(0, 0, 20, 20), IntRect(0, 0, 20, 20)); // Check that the generated image is correct. As mentioned above, we // expect even rows to be green and odd rows to be red. RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr surface = currentFrame->GetSourceSurface(); for (uint32_t row = 0; row < 20; ++row) { EXPECT_TRUE(RowsAreSolidColor( surface, row, 1, row % 2 == 0 ? BGRAColor::Green() : BGRAColor::Red())); } }); } TEST_F(ImageDeinterlacingFilter, WritePixelsOutput7_7) { WithDeinterlacingFilter( IntSize(7, 7), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { // Fill the image. The output should be a repeating pattern of two green // rows followed by two red rows but we need to write the rows in the // order that the deinterlacer expects them. uint32_t count = 0; auto result = aFilter->WritePixels([&]() { uint32_t row = count / 7; // Integer division. ++count; switch (row) { // First pass. Output rows are positioned at 8n + 0. case 0: // Output row 0. return AsVariant(BGRAColor::Green().AsPixel()); // Second pass. Rows are positioned at 8n + 4. case 1: // Output row 4. return AsVariant(BGRAColor::Green().AsPixel()); // Third pass. Rows are positioned at 4n + 2. case 2: // Output row 2. case 3: // Output row 6. return AsVariant(BGRAColor::Red().AsPixel()); // Fourth pass. Rows are positioned at 2n + 1. case 4: // Output row 1. return AsVariant(BGRAColor::Green().AsPixel()); case 5: // Output row 3. return AsVariant(BGRAColor::Red().AsPixel()); case 6: // Output row 5. return AsVariant(BGRAColor::Green().AsPixel()); default: MOZ_ASSERT_UNREACHABLE("Unexpected row"); return AsVariant(BGRAColor::Transparent().AsPixel()); } }); EXPECT_EQ(WriteState::FINISHED, result); EXPECT_EQ(7u * 7u, count); AssertCorrectPipelineFinalState(aFilter, IntRect(0, 0, 7, 7), IntRect(0, 0, 7, 7)); // Check that the generated image is correct. As mentioned above, we // expect two green rows, followed by two red rows, then two green rows, // etc. RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr surface = currentFrame->GetSourceSurface(); for (uint32_t row = 0; row < 7; ++row) { BGRAColor color = row == 0 || row == 1 || row == 4 || row == 5 ? BGRAColor::Green() : BGRAColor::Red(); EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, color)); } }); } TEST_F(ImageDeinterlacingFilter, WritePixelsOutput3_3) { WithDeinterlacingFilter( IntSize(3, 3), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { // Fill the image. The output should be green, red, green in that order, // but we need to write the rows in the order that the deinterlacer // expects them. uint32_t count = 0; auto result = aFilter->WritePixels([&]() { uint32_t row = count / 3; // Integer division. ++count; switch (row) { // First pass. Output rows are positioned at 8n + 0. case 0: // Output row 0. return AsVariant(BGRAColor::Green().AsPixel()); // Second pass. Rows are positioned at 8n + 4. // No rows for this pass. // Third pass. Rows are positioned at 4n + 2. case 1: // Output row 2. return AsVariant(BGRAColor::Green().AsPixel()); // Fourth pass. Rows are positioned at 2n + 1. case 2: // Output row 1. return AsVariant(BGRAColor::Red().AsPixel()); default: MOZ_ASSERT_UNREACHABLE("Unexpected row"); return AsVariant(BGRAColor::Transparent().AsPixel()); } }); EXPECT_EQ(WriteState::FINISHED, result); EXPECT_EQ(3u * 3u, count); AssertCorrectPipelineFinalState(aFilter, IntRect(0, 0, 3, 3), IntRect(0, 0, 3, 3)); // Check that the generated image is correct. As mentioned above, we // expect green, red, green in that order. RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr surface = currentFrame->GetSourceSurface(); for (uint32_t row = 0; row < 3; ++row) { EXPECT_TRUE(RowsAreSolidColor( surface, row, 1, row == 0 || row == 2 ? BGRAColor::Green() : BGRAColor::Red())); } }); } TEST_F(ImageDeinterlacingFilter, WritePixelsOutput1_1) { WithDeinterlacingFilter( IntSize(1, 1), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { // Fill the image. The output should be a single red row. uint32_t count = 0; auto result = aFilter->WritePixels([&]() { ++count; return AsVariant(BGRAColor::Red().AsPixel()); }); EXPECT_EQ(WriteState::FINISHED, result); EXPECT_EQ(1u, count); AssertCorrectPipelineFinalState(aFilter, IntRect(0, 0, 1, 1), IntRect(0, 0, 1, 1)); // Check that the generated image is correct. As mentioned above, we // expect a single red row. RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr surface = currentFrame->GetSourceSurface(); EXPECT_TRUE(RowsAreSolidColor(surface, 0, 1, BGRAColor::Red())); }); } void WriteRowAndCheckInterlacerOutput(image::Decoder* aDecoder, SurfaceFilter* aFilter, BGRAColor aColor, WriteState aNextState, OrientedIntRect aInvalidRect, uint32_t aFirstHaeberliRow, uint32_t aLastHaeberliRow) { uint32_t count = 0; auto result = aFilter->WritePixels([&]() -> NextPixel { if (count < 7) { ++count; return AsVariant(aColor.AsPixel()); } return AsVariant(WriteState::NEED_MORE_DATA); }); EXPECT_EQ(aNextState, result); EXPECT_EQ(7u, count); // Assert that we got the expected invalidation region. Maybe invalidRect = aFilter->TakeInvalidRect(); EXPECT_TRUE(invalidRect.isSome()); EXPECT_EQ(aInvalidRect, invalidRect->mInputSpaceRect); EXPECT_EQ(aInvalidRect, invalidRect->mOutputSpaceRect); // Check that the portion of the image generated so far is correct. The rows // from aFirstHaeberliRow to aLastHaeberliRow should be filled with aColor. // Note that this is not the same as the set of rows in aInvalidRect, because // after writing a row the deinterlacer seeks to the next row to write, which // may involve copying previously-written rows in the buffer to the output // even though they don't change in this pass. RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr surface = currentFrame->GetSourceSurface(); for (uint32_t row = aFirstHaeberliRow; row <= aLastHaeberliRow; ++row) { EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, aColor)); } } TEST_F(ImageDeinterlacingFilter, WritePixelsIntermediateOutput7_7) { WithDeinterlacingFilter( IntSize(7, 7), /* aProgressiveDisplay = */ true, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { // Fill the image. The output should be a repeating pattern of two green // rows followed by two red rows but we need to write the rows in the // order that the deinterlacer expects them. // First pass. Output rows are positioned at 8n + 0. // Output row 0. The invalid rect is the entire image because this is // the end of the first pass. WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Green(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 0, 7, 7), 0, 4); // Second pass. Rows are positioned at 8n + 4. // Output row 4. The invalid rect is the entire image because this is // the end of the second pass. WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Green(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 0, 7, 7), 1, 4); // Third pass. Rows are positioned at 4n + 2. // Output row 2. The invalid rect contains the Haeberli rows for this // output row (rows 2 and 3) as well as the rows that we copy from // previous passes when seeking to the next output row (rows 4 and 5). WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Red(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 2, 7, 4), 2, 3); // Output row 6. The invalid rect is the entire image because this is // the end of the third pass. WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Red(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 0, 7, 7), 6, 6); // Fourth pass. Rows are positioned at 2n + 1. // Output row 1. The invalid rect contains the Haeberli rows for this // output row (just row 1) as well as the rows that we copy from // previous passes when seeking to the next output row (row 2). WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Green(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 1, 7, 2), 1, 1); // Output row 3. The invalid rect contains the Haeberli rows for this // output row (just row 3) as well as the rows that we copy from // previous passes when seeking to the next output row (row 4). WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Red(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 3, 7, 2), 3, 3); // Output row 5. The invalid rect contains the Haeberli rows for this // output row (just row 5) as well as the rows that we copy from // previous passes when seeking to the next output row (row 6). WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Green(), WriteState::FINISHED, OrientedIntRect(0, 5, 7, 2), 5, 5); // Assert that we're in the expected final state. EXPECT_TRUE(aFilter->IsSurfaceFinished()); Maybe invalidRect = aFilter->TakeInvalidRect(); EXPECT_TRUE(invalidRect.isNothing()); // Check that the generated image is correct. As mentioned above, we // expect two green rows, followed by two red rows, then two green rows, // etc. RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr surface = currentFrame->GetSourceSurface(); for (uint32_t row = 0; row < 7; ++row) { BGRAColor color = row == 0 || row == 1 || row == 4 || row == 5 ? BGRAColor::Green() : BGRAColor::Red(); EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, color)); } }); } TEST_F(ImageDeinterlacingFilter, WritePixelsNonProgressiveIntermediateOutput7_7) { WithDeinterlacingFilter( IntSize(7, 7), /* aProgressiveDisplay = */ false, [](image::Decoder* aDecoder, SurfaceFilter* aFilter) { // Fill the image. The output should be a repeating pattern of two green // rows followed by two red rows but we need to write the rows in the // order that the deinterlacer expects them. // First pass. Output rows are positioned at 8n + 0. // Output row 0. The invalid rect is the entire image because this is // the end of the first pass. WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Green(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 0, 7, 7), 0, 0); // Second pass. Rows are positioned at 8n + 4. // Output row 4. The invalid rect is the entire image because this is // the end of the second pass. WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Green(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 0, 7, 7), 4, 4); // Third pass. Rows are positioned at 4n + 2. // Output row 2. The invalid rect contains the Haeberli rows for this // output row (rows 2 and 3) as well as the rows that we copy from // previous passes when seeking to the next output row (rows 4 and 5). WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Red(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 2, 7, 4), 2, 2); // Output row 6. The invalid rect is the entire image because this is // the end of the third pass. WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Red(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 0, 7, 7), 6, 6); // Fourth pass. Rows are positioned at 2n + 1. // Output row 1. The invalid rect contains the Haeberli rows for this // output row (just row 1) as well as the rows that we copy from // previous passes when seeking to the next output row (row 2). WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Green(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 1, 7, 2), 1, 1); // Output row 3. The invalid rect contains the Haeberli rows for this // output row (just row 3) as well as the rows that we copy from // previous passes when seeking to the next output row (row 4). WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Red(), WriteState::NEED_MORE_DATA, OrientedIntRect(0, 3, 7, 2), 3, 3); // Output row 5. The invalid rect contains the Haeberli rows for this // output row (just row 5) as well as the rows that we copy from // previous passes when seeking to the next output row (row 6). WriteRowAndCheckInterlacerOutput(aDecoder, aFilter, BGRAColor::Green(), WriteState::FINISHED, OrientedIntRect(0, 5, 7, 2), 5, 5); // Assert that we're in the expected final state. EXPECT_TRUE(aFilter->IsSurfaceFinished()); Maybe invalidRect = aFilter->TakeInvalidRect(); EXPECT_TRUE(invalidRect.isNothing()); // Check that the generated image is correct. As mentioned above, we // expect two green rows, followed by two red rows, then two green rows, // etc. RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); RefPtr surface = currentFrame->GetSourceSurface(); for (uint32_t row = 0; row < 7; ++row) { BGRAColor color = row == 0 || row == 1 || row == 4 || row == 5 ? BGRAColor::Green() : BGRAColor::Red(); EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, color)); } }); } TEST_F(ImageDeinterlacingFilter, DeinterlacingFailsFor0_0) { // A 0x0 input size is invalid, so configuration should fail. AssertConfiguringDeinterlacingFilterFails(IntSize(0, 0)); } TEST_F(ImageDeinterlacingFilter, DeinterlacingFailsForMinus1_Minus1) { // A negative input size is invalid, so configuration should fail. AssertConfiguringDeinterlacingFilterFails(IntSize(-1, -1)); }