diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /image/test/gtest/TestSurfacePipeIntegration.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'image/test/gtest/TestSurfacePipeIntegration.cpp')
-rw-r--r-- | image/test/gtest/TestSurfacePipeIntegration.cpp | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/image/test/gtest/TestSurfacePipeIntegration.cpp b/image/test/gtest/TestSurfacePipeIntegration.cpp new file mode 100644 index 000000000..5e8c19fc2 --- /dev/null +++ b/image/test/gtest/TestSurfacePipeIntegration.cpp @@ -0,0 +1,508 @@ +/* -*- 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; + +namespace mozilla { +namespace image { + +class TestSurfacePipeFactory +{ +public: + static SurfacePipe SimpleSurfacePipe() + { + SurfacePipe pipe; + return Move(pipe); + } + + template <typename T> + static SurfacePipe SurfacePipeFromPipeline(T&& aPipeline) + { + return SurfacePipe { Move(aPipeline) }; + } + +private: + TestSurfacePipeFactory() { } +}; + +} // 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()); +} + +class ImageSurfacePipeIntegration : public ::testing::Test +{ +protected: + AutoInitializeImageLib mInit; +}; + +TEST_F(ImageSurfacePipeIntegration, SurfacePipe) +{ + // Test that SurfacePipe objects can be initialized and move constructed. + SurfacePipe pipe = TestSurfacePipeFactory::SimpleSurfacePipe(); + + // Test that SurfacePipe objects can be move assigned. + pipe = TestSurfacePipeFactory::SimpleSurfacePipe(); + + // Test that SurfacePipe objects can be initialized with a pipeline. + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + auto sink = MakeUnique<SurfaceSink>(); + nsresult rv = + sink->Configure(SurfaceConfig { decoder, 0, IntSize(100, 100), + SurfaceFormat::B8G8R8A8, false }); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + + pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink); + + // 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); + } + + // 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(); + } + + // 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); + } + + // 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_F(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(); +} + +TEST_F(ImageSurfacePipeIntegration, DeinterlaceDownscaleWritePixels) +{ + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { + CheckWritePixels(aDecoder, aFilter, + /* aOutputRect = */ Some(IntRect(0, 0, 25, 25))); + }; + + WithFilterPipeline(decoder, test, + DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true }, + DownscalingConfig { IntSize(100, 100), + SurfaceFormat::B8G8R8A8 }, + SurfaceConfig { decoder, 0, IntSize(25, 25), + SurfaceFormat::B8G8R8A8, false }); +} + +TEST_F(ImageSurfacePipeIntegration, RemoveFrameRectBottomRightDownscaleWritePixels) +{ + // This test case uses a frame rect that extends beyond the borders of the + // image to the bottom and to the right. It looks roughly like this (with the + // box made of '#'s representing the frame rect): + // + // +------------+ + // + + + // + +------------+ + // + +############+ + // +------+############+ + // +############+ + // +------------+ + + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + // Note that aInputWriteRect is 100x50 because RemoveFrameRectFilter ignores + // trailing rows that don't show up in the output. (Leading rows unfortunately + // can't be ignored.) So the action of the pipeline is as follows: + // + // (1) RemoveFrameRectFilter reads a 100x50 region of the input. + // (aInputWriteRect captures this fact.) The remaining 50 rows are ignored + // because they extend off the bottom of the image due to the frame rect's + // (50, 50) offset. The 50 columns on the right also don't end up in the + // output, so ultimately only a 50x50 region in the output contains data + // from the input. The filter's output is not 50x50, though, but 100x100, + // because what RemoveFrameRectFilter does is introduce blank rows or + // columns as necessary to transform an image that needs a frame rect into + // an image that doesn't. + // + // (2) DownscalingFilter reads the output of RemoveFrameRectFilter (100x100) + // and downscales it to 20x20. + // + // (3) The surface owned by SurfaceSink logically has only a 10x10 region + // region in it that's non-blank; this is the downscaled version of the + // 50x50 region discussed in (1). (aOutputWriteRect captures this fact.) + // Some fuzz, as usual, is necessary when dealing with Lanczos downscaling. + + auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { + CheckWritePixels(aDecoder, aFilter, + /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)), + /* aInputRect = */ Some(IntRect(0, 0, 100, 100)), + /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 50)), + /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)), + /* aFuzz = */ 0x33); + }; + + WithFilterPipeline(decoder, test, + RemoveFrameRectConfig { IntRect(50, 50, 100, 100) }, + DownscalingConfig { IntSize(100, 100), + SurfaceFormat::B8G8R8A8 }, + SurfaceConfig { decoder, 0, IntSize(20, 20), + SurfaceFormat::B8G8R8A8, false }); +} + +TEST_F(ImageSurfacePipeIntegration, RemoveFrameRectTopLeftDownscaleWritePixels) +{ + // This test case uses a frame rect that extends beyond the borders of the + // image to the top and to the left. It looks roughly like this (with the + // box made of '#'s representing the frame rect): + // + // +------------+ + // +############+ + // +############+------+ + // +############+ + + // +------------+ + + // + + + // +------------+ + + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { + CheckWritePixels(aDecoder, aFilter, + /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)), + /* aInputRect = */ Some(IntRect(0, 0, 100, 100)), + /* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)), + /* aOutputWriteRect = */ Some(IntRect(0, 0, 10, 10)), + /* aFuzz = */ 0x21); + }; + + WithFilterPipeline(decoder, test, + RemoveFrameRectConfig { IntRect(-50, -50, 100, 100) }, + DownscalingConfig { IntSize(100, 100), + SurfaceFormat::B8G8R8A8 }, + SurfaceConfig { decoder, 0, IntSize(20, 20), + SurfaceFormat::B8G8R8A8, false }); +} + +TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectWritePixels) +{ + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + // Note that aInputRect is the full 100x100 size even though + // RemoveFrameRectFilter is part of this pipeline, because deinterlacing + // requires reading every row. + + auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { + CheckWritePixels(aDecoder, aFilter, + /* aOutputRect = */ Some(IntRect(0, 0, 100, 100)), + /* aInputRect = */ Some(IntRect(0, 0, 100, 100)), + /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)), + /* aOutputWriteRect = */ Some(IntRect(50, 50, 50, 50))); + }; + + WithFilterPipeline(decoder, test, + DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true }, + RemoveFrameRectConfig { IntRect(50, 50, 100, 100) }, + SurfaceConfig { decoder, 0, IntSize(100, 100), + SurfaceFormat::B8G8R8A8, false }); +} + +TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectDownscaleWritePixels) +{ + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { + CheckWritePixels(aDecoder, aFilter, + /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)), + /* aInputRect = */ Some(IntRect(0, 0, 100, 100)), + /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)), + /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)), + /* aFuzz = */ 33); + }; + + WithFilterPipeline(decoder, test, + DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true }, + RemoveFrameRectConfig { IntRect(50, 50, 100, 100) }, + DownscalingConfig { IntSize(100, 100), + SurfaceFormat::B8G8R8A8 }, + SurfaceConfig { decoder, 0, IntSize(20, 20), + SurfaceFormat::B8G8R8A8, false }); +} + +TEST_F(ImageSurfacePipeIntegration, ConfiguringPalettedRemoveFrameRectDownscaleFails) +{ + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + // This is an invalid pipeline for paletted images, so configuration should + // fail. + AssertConfiguringPipelineFails(decoder, + RemoveFrameRectConfig { IntRect(0, 0, 50, 50) }, + DownscalingConfig { IntSize(100, 100), + SurfaceFormat::B8G8R8A8 }, + PalettedSurfaceConfig { decoder, 0, IntSize(100, 100), + IntRect(0, 0, 50, 50), + SurfaceFormat::B8G8R8A8, 8, + false }); +} + +TEST_F(ImageSurfacePipeIntegration, ConfiguringPalettedDeinterlaceDownscaleFails) +{ + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + // This is an invalid pipeline for paletted images, so configuration should + // fail. + AssertConfiguringPipelineFails(decoder, + DeinterlacingConfig<uint8_t> { /* mProgressiveDisplay = */ true}, + DownscalingConfig { IntSize(100, 100), + SurfaceFormat::B8G8R8A8 }, + PalettedSurfaceConfig { decoder, 0, IntSize(100, 100), + IntRect(0, 0, 20, 20), + SurfaceFormat::B8G8R8A8, 8, + false }); +} + +TEST_F(ImageSurfacePipeIntegration, ConfiguringHugeDeinterlacingBufferFails) +{ + RefPtr<Decoder> decoder = CreateTrivialDecoder(); + ASSERT_TRUE(decoder != nullptr); + + // When DownscalingFilter is used, we may succeed in allocating an output + // surface for huge images, because we only need to store the scaled-down + // version of the image. However, regardless of downscaling, + // DeinterlacingFilter needs to allocate a buffer as large as the size of the + // input. This can cause OOMs on operating systems that allow overcommit. This + // test makes sure that we reject such allocations. + AssertConfiguringPipelineFails(decoder, + DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true}, + DownscalingConfig { IntSize(60000, 60000), + SurfaceFormat::B8G8R8A8 }, + SurfaceConfig { decoder, 0, IntSize(600, 600), + SurfaceFormat::B8G8R8A8, false }); +} |