Bug 1246851 (Part 4) - Add a test suite for SurfacePipes and SurfaceFilters. r=njn

This commit is contained in:
Seth Fowler 2016-02-25 16:21:29 -08:00
parent d58e9fda35
commit 618f960cdd
12 changed files with 3067 additions and 24 deletions

View File

@ -32,9 +32,15 @@ UNIFIED_SOURCES += [
'nsPNGDecoder.cpp',
]
# Decoders need RasterImage.h
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
# Access to Skia headers for Downscaler.
'/gfx/2d',
# Decoders need ImageLib headers.
'/image',
]
LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
FINAL_LIBRARY = 'xul'

View File

@ -6,7 +6,6 @@
#include "Common.h"
#include <cstdlib>
#include "gtest/gtest.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIDirectoryService.h"
@ -19,13 +18,14 @@
#include "nsString.h"
namespace mozilla {
namespace image {
using namespace gfx;
using std::abs;
///////////////////////////////////////////////////////////////////////////////
// Helpers
// General Helpers
///////////////////////////////////////////////////////////////////////////////
// These macros work like gtest's ASSERT_* macros, except that they can be used
@ -85,13 +85,41 @@ LoadFile(const char* aRelativePath)
}
bool
IsSolidColor(SourceSurface* aSurface, BGRAColor aColor, bool aFuzzy)
IsSolidColor(SourceSurface* aSurface,
BGRAColor aColor,
uint8_t aFuzz /* = 0 */)
{
IntSize size = aSurface->GetSize();
return RectIsSolidColor(aSurface, IntRect(0, 0, size.width, size.height),
aColor, aFuzz);
}
bool
RowsAreSolidColor(SourceSurface* aSurface,
int32_t aStartRow,
int32_t aRowCount,
BGRAColor aColor,
uint8_t aFuzz /* = 0 */)
{
IntSize size = aSurface->GetSize();
return RectIsSolidColor(aSurface, IntRect(0, aStartRow, size.width, aRowCount),
aColor, aFuzz);
}
bool
RectIsSolidColor(SourceSurface* aSurface,
const IntRect& aRect,
BGRAColor aColor,
uint8_t aFuzz /* = 0 */)
{
IntSize surfaceSize = aSurface->GetSize();
IntRect rect =
aRect.Intersect(IntRect(0, 0, surfaceSize.width, surfaceSize.height));
RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface();
ASSERT_TRUE_OR_RETURN(dataSurface != nullptr, false);
ASSERT_EQ_OR_RETURN(dataSurface->Stride(), aSurface->GetSize().width * 4,
false);
ASSERT_EQ_OR_RETURN(dataSurface->Stride(), surfaceSize.width * 4, false);
DataSourceSurface::ScopedMap mapping(dataSurface,
DataSourceSurface::MapType::READ);
@ -100,18 +128,21 @@ IsSolidColor(SourceSurface* aSurface, BGRAColor aColor, bool aFuzzy)
uint8_t* data = dataSurface->GetData();
ASSERT_TRUE_OR_RETURN(data != nullptr, false);
int32_t length = dataSurface->Stride() * aSurface->GetSize().height;
for (int32_t i = 0 ; i < length ; i += 4) {
if (aFuzzy) {
ASSERT_LE_OR_RETURN(abs(aColor.mBlue - data[i + 0]), 1, false);
ASSERT_LE_OR_RETURN(abs(aColor.mGreen - data[i + 1]), 1, false);
ASSERT_LE_OR_RETURN(abs(aColor.mRed - data[i + 2]), 1, false);
ASSERT_LE_OR_RETURN(abs(aColor.mAlpha - data[i + 3]), 1, false);
} else {
ASSERT_EQ_OR_RETURN(aColor.mBlue, data[i + 0], false);
ASSERT_EQ_OR_RETURN(aColor.mGreen, data[i + 1], false);
ASSERT_EQ_OR_RETURN(aColor.mRed, data[i + 2], false);
ASSERT_EQ_OR_RETURN(aColor.mAlpha, data[i + 3], false);
int32_t rowLength = dataSurface->Stride();
for (int32_t row = rect.y; row < rect.YMost(); ++row) {
for (int32_t col = rect.x; col < rect.XMost(); ++col) {
int32_t i = row * rowLength + col * 4;
if (aFuzz != 0) {
ASSERT_LE_OR_RETURN(abs(aColor.mBlue - data[i + 0]), aFuzz, false);
ASSERT_LE_OR_RETURN(abs(aColor.mGreen - data[i + 1]), aFuzz, false);
ASSERT_LE_OR_RETURN(abs(aColor.mRed - data[i + 2]), aFuzz, false);
ASSERT_LE_OR_RETURN(abs(aColor.mAlpha - data[i + 3]), aFuzz, false);
} else {
ASSERT_EQ_OR_RETURN(aColor.mBlue, data[i + 0], false);
ASSERT_EQ_OR_RETURN(aColor.mGreen, data[i + 1], false);
ASSERT_EQ_OR_RETURN(aColor.mRed, data[i + 2], false);
ASSERT_EQ_OR_RETURN(aColor.mAlpha, data[i + 3], false);
}
}
}
@ -119,6 +150,261 @@ IsSolidColor(SourceSurface* aSurface, BGRAColor aColor, bool aFuzzy)
}
///////////////////////////////////////////////////////////////////////////////
// SurfacePipe Helpers
///////////////////////////////////////////////////////////////////////////////
already_AddRefed<Decoder>
CreateTrivialDecoder()
{
gfxPrefs::GetSingleton();
DecoderType decoderType = DecoderFactory::GetDecoderType("image/gif");
RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer();
RefPtr<Decoder> decoder =
DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer,
DefaultSurfaceFlags());
return decoder.forget();
}
void AssertCorrectPipelineFinalState(SurfaceFilter* aFilter,
const gfx::IntRect& aInputSpaceRect,
const gfx::IntRect& aOutputSpaceRect)
{
EXPECT_TRUE(aFilter->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isSome());
EXPECT_EQ(aInputSpaceRect, invalidRect->mInputSpaceRect);
EXPECT_EQ(aOutputSpaceRect, invalidRect->mOutputSpaceRect);
}
void
CheckGeneratedImage(Decoder* aDecoder,
const IntRect& aRect,
uint8_t aFuzz /* = 0 */)
{
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
const IntSize surfaceSize = surface->GetSize();
// This diagram shows how the surface is divided into regions that the code
// below tests for the correct content. The output rect is the bounds of the
// region labeled 'C'.
//
// +---------------------------+
// | A |
// +---------+--------+--------+
// | B | C | D |
// +---------+--------+--------+
// | E |
// +---------------------------+
// Check that the output rect itself is green. (Region 'C'.)
EXPECT_TRUE(RectIsSolidColor(surface, aRect, BGRAColor::Green(), aFuzz));
// Check that the area above the output rect is transparent. (Region 'A'.)
EXPECT_TRUE(RectIsSolidColor(surface,
IntRect(0, 0, surfaceSize.width, aRect.y),
BGRAColor::Transparent(), aFuzz));
// Check that the area to the left of the output rect is transparent. (Region 'B'.)
EXPECT_TRUE(RectIsSolidColor(surface,
IntRect(0, aRect.y, aRect.x, aRect.YMost()),
BGRAColor::Transparent(), aFuzz));
// Check that the area to the right of the output rect is transparent. (Region 'D'.)
const int32_t widthOnRight = surfaceSize.width - aRect.XMost();
EXPECT_TRUE(RectIsSolidColor(surface,
IntRect(aRect.XMost(), aRect.y, widthOnRight, aRect.YMost()),
BGRAColor::Transparent(), aFuzz));
// Check that the area below the output rect is transparent. (Region 'E'.)
const int32_t heightBelow = surfaceSize.height - aRect.YMost();
EXPECT_TRUE(RectIsSolidColor(surface,
IntRect(0, aRect.YMost(), surfaceSize.width, heightBelow),
BGRAColor::Transparent(), aFuzz));
}
template <typename Func> void
CheckSurfacePipeWrite(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<IntRect> aOutputRect,
Maybe<IntRect> aInputRect,
Maybe<IntRect> aInputWriteRect,
Maybe<IntRect> aOutputWriteRect,
uint8_t aFuzz,
Func aFunc)
{
IntRect outputRect = aOutputRect.valueOr(IntRect(0, 0, 100, 100));
IntRect inputRect = aInputRect.valueOr(IntRect(0, 0, 100, 100));
IntRect inputWriteRect = aInputWriteRect.valueOr(inputRect);
IntRect outputWriteRect = aOutputWriteRect.valueOr(outputRect);
// Fill the image.
int32_t count = 0;
auto result = aFunc(count);
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(inputWriteRect.width * inputWriteRect.height, count);
AssertCorrectPipelineFinalState(aFilter, inputRect, outputRect);
// Attempt to write more data and make sure nothing changes.
const int32_t oldCount = count;
result = aFunc(count);
EXPECT_EQ(oldCount, count);
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_TRUE(aFilter->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
// Attempt to advance to the next row and make sure nothing changes.
aFilter->AdvanceRow();
EXPECT_TRUE(aFilter->IsSurfaceFinished());
invalidRect = aFilter->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
// Check that the generated image is correct.
CheckGeneratedImage(aDecoder, outputWriteRect, aFuzz);
}
void
CheckWritePixels(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<IntRect> aOutputRect /* = Nothing() */,
Maybe<IntRect> aInputRect /* = Nothing() */,
Maybe<IntRect> aInputWriteRect /* = Nothing() */,
Maybe<IntRect> aOutputWriteRect /* = Nothing() */,
uint8_t aFuzz /* = 0 */)
{
CheckSurfacePipeWrite(aDecoder, aFilter,
aOutputRect, aInputRect,
aInputWriteRect, aOutputWriteRect,
aFuzz,
[&](int32_t& aCount) {
return aFilter->WritePixels<uint32_t>([&] {
++aCount;
return AsVariant(BGRAColor::Green().AsPixel());
});
});
}
void
CheckWriteRows(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<IntRect> aOutputRect /* = Nothing() */,
Maybe<IntRect> aInputRect /* = Nothing() */,
Maybe<IntRect> aInputWriteRect /* = Nothing() */,
Maybe<IntRect> aOutputWriteRect /* = Nothing() */,
uint8_t aFuzz /* = 0 */)
{
CheckSurfacePipeWrite(aDecoder, aFilter,
aOutputRect, aInputRect,
aInputWriteRect, aOutputWriteRect,
aFuzz,
[&](int32_t& aCount) {
return aFilter->WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
for (; aLength > 0; --aLength, ++aRow, ++aCount) {
*aRow = BGRAColor::Green().AsPixel();
}
return Nothing();
});
});
}
template <typename Func> void
CheckPalettedSurfacePipeWrite(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<IntRect> aOutputRect,
Maybe<IntRect> aInputRect,
Maybe<IntRect> aInputWriteRect,
Maybe<IntRect> aOutputWriteRect,
uint8_t aFuzz,
Func aFunc)
{
IntRect outputRect = aOutputRect.valueOr(IntRect(0, 0, 100, 100));
IntRect inputRect = aInputRect.valueOr(IntRect(0, 0, 100, 100));
IntRect inputWriteRect = aInputWriteRect.valueOr(inputRect);
IntRect outputWriteRect = aOutputWriteRect.valueOr(outputRect);
// Fill the image.
int32_t count = 0;
auto result = aFunc(count);
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(inputWriteRect.width * inputWriteRect.height, count);
AssertCorrectPipelineFinalState(aFilter, inputRect, outputRect);
// Attempt to write more data and make sure nothing changes.
const int32_t oldCount = count;
result = aFunc(count);
EXPECT_EQ(oldCount, count);
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_TRUE(aFilter->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
// Attempt to advance to the next row and make sure nothing changes.
aFilter->AdvanceRow();
EXPECT_TRUE(aFilter->IsSurfaceFinished());
invalidRect = aFilter->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
// Check that the generated image is correct.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
uint8_t* imageData;
uint32_t imageLength;
currentFrame->GetImageData(&imageData, &imageLength);
ASSERT_TRUE(imageData != nullptr);
ASSERT_EQ(outputWriteRect.width * outputWriteRect.height, int32_t(imageLength));
for (uint32_t i = 0; i < imageLength; ++i) {
ASSERT_EQ(uint8_t(255), imageData[i]);
}
}
void
CheckPalettedWritePixels(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<IntRect> aOutputRect /* = Nothing() */,
Maybe<IntRect> aInputRect /* = Nothing() */,
Maybe<IntRect> aInputWriteRect /* = Nothing() */,
Maybe<IntRect> aOutputWriteRect /* = Nothing() */,
uint8_t aFuzz /* = 0 */)
{
CheckPalettedSurfacePipeWrite(aDecoder, aFilter,
aOutputRect, aInputRect,
aInputWriteRect, aOutputWriteRect,
aFuzz,
[&](int32_t& aCount) {
return aFilter->WritePixels<uint8_t>([&] {
++aCount;
return AsVariant(uint8_t(255));
});
});
}
void
CheckPalettedWriteRows(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<IntRect> aOutputRect /* = Nothing() */,
Maybe<IntRect> aInputRect /* = Nothing() */,
Maybe<IntRect> aInputWriteRect /* = Nothing() */,
Maybe<IntRect> aOutputWriteRect /* = Nothing() */,
uint8_t aFuzz /* = 0*/)
{
CheckPalettedSurfacePipeWrite(aDecoder, aFilter,
aOutputRect, aInputRect,
aInputWriteRect, aOutputWriteRect,
aFuzz,
[&](int32_t& aCount) {
return aFilter->WriteRows<uint8_t>([&](uint8_t* aRow, uint32_t aLength) {
for (; aLength > 0; --aLength, ++aRow, ++aCount) {
*aRow = uint8_t(255);
}
return Nothing();
});
});
}
///////////////////////////////////////////////////////////////////////////////
// Test Data
///////////////////////////////////////////////////////////////////////////////
@ -224,4 +510,5 @@ ImageTestCase NoFrameDelayGIFTestCase()
return ImageTestCase("no-frame-delay.gif", "image/gif", IntSize(100, 100));
}
} // namespace image
} // namespace mozilla

View File

@ -6,12 +6,21 @@
#ifndef mozilla_image_test_gtest_Common_h
#define mozilla_image_test_gtest_Common_h
#include "gtest/gtest.h"
#include "mozilla/Maybe.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/2D.h"
#include "Decoder.h"
#include "gfxColor.h"
#include "nsCOMPtr.h"
#include "SurfacePipe.h"
#include "SurfacePipeFactory.h"
class nsIInputStream;
namespace mozilla {
namespace image {
///////////////////////////////////////////////////////////////////////////////
// Types
@ -54,6 +63,10 @@ struct BGRAColor
{ }
static BGRAColor Green() { return BGRAColor(0x00, 0xFF, 0x00, 0xFF); }
static BGRAColor Red() { return BGRAColor(0x00, 0x00, 0xFF, 0xFF); }
static BGRAColor Transparent() { return BGRAColor(0x00, 0x00, 0x00, 0x00); }
uint32_t AsPixel() const { return gfxPackedPixel(mAlpha, mRed, mGreen, mBlue); }
uint8_t mBlue;
uint8_t mGreen;
@ -63,7 +76,7 @@ struct BGRAColor
///////////////////////////////////////////////////////////////////////////////
// Helpers
// General Helpers
///////////////////////////////////////////////////////////////////////////////
/// Loads a file from the current directory. @return an nsIInputStream for it.
@ -72,12 +85,206 @@ already_AddRefed<nsIInputStream> LoadFile(const char* aRelativePath);
/**
* @returns true if every pixel of @aSurface is @aColor.
*
* If @aFuzzy is true, a tolerance of 1 is allowed in each color component. This
* may be necessary for tests that involve JPEG images.
* If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
* component. This may be necessary for tests that involve JPEG images or
* downscaling.
*/
bool IsSolidColor(gfx::SourceSurface* aSurface,
BGRAColor aColor,
bool aFuzzy = false);
uint8_t aFuzz = 0);
/**
* @returns true if every pixel in the range of rows specified by @aStartRow and
* @aRowCount of @aSurface is @aColor.
*
* If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
* component. This may be necessary for tests that involve JPEG images or
* downscaling.
*/
bool RowsAreSolidColor(gfx::SourceSurface* aSurface,
int32_t aStartRow,
int32_t aRowCount,
BGRAColor aColor,
uint8_t aFuzz = 0);
/**
* @returns true if every pixel in the rect specified by @aRect is @aColor.
*
* If @aFuzz is nonzero, a tolerance of @aFuzz is allowed in each color
* component. This may be necessary for tests that involve JPEG images or
* downscaling.
*/
bool RectIsSolidColor(gfx::SourceSurface* aSurface,
const gfx::IntRect& aRect,
BGRAColor aColor,
uint8_t aFuzz = 0);
///////////////////////////////////////////////////////////////////////////////
// SurfacePipe Helpers
///////////////////////////////////////////////////////////////////////////////
/**
* Creates a decoder with no data associated with, suitable for testing code
* that requires a decoder to initialize or to allocate surfaces but doesn't
* actually need the decoder to do any decoding.
*
* XXX(seth): We only need this because SurfaceSink and PalettedSurfaceSink
* defer to the decoder for surface allocation. Once all decoders use
* SurfacePipe we won't need to do that anymore and we can remove this function.
*/
already_AddRefed<Decoder> CreateTrivialDecoder();
/**
* Creates a pipeline of SurfaceFilters from a list of Config structs and passes
* it to the provided lambda @aFunc. Assertions that the pipeline is constructly
* correctly and cleanup of any allocated surfaces is handled automatically.
*
* @param aDecoder The decoder to use for allocating surfaces.
* @param aFunc The lambda function to pass the filter pipeline to.
* @param aConfigs The configuration for the pipeline.
*/
template <typename Func, typename... Configs>
void WithFilterPipeline(Decoder* aDecoder, Func aFunc, Configs... aConfigs)
{
auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
nsresult rv = pipe->Configure(aConfigs...);
ASSERT_TRUE(NS_SUCCEEDED(rv));
aFunc(aDecoder, pipe.get());
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
if (currentFrame) {
currentFrame->Finish();
}
}
/**
* Creates a pipeline of SurfaceFilters from a list of Config structs and
* asserts that configuring it fails. Cleanup of any allocated surfaces is
* handled automatically.
*
* @param aDecoder The decoder to use for allocating surfaces.
* @param aConfigs The configuration for the pipeline.
*/
template <typename... Configs>
void AssertConfiguringPipelineFails(Decoder* aDecoder, Configs... aConfigs)
{
auto pipe = MakeUnique<typename detail::FilterPipeline<Configs...>::Type>();
nsresult rv = pipe->Configure(aConfigs...);
// Callers expect configuring the pipeline to fail.
ASSERT_TRUE(NS_FAILED(rv));
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
if (currentFrame) {
currentFrame->Finish();
}
}
/**
* Asserts that the provided filter pipeline is in the correct final state,
* which is to say, the entire surface has been written to (IsSurfaceFinished()
* returns true) and the invalid rects are as expected.
*
* @param aFilter The filter pipeline to check.
* @param aInputSpaceRect The expect invalid rect, in input space.
* @param aoutputSpaceRect The expect invalid rect, in output space.
*/
void AssertCorrectPipelineFinalState(SurfaceFilter* aFilter,
const gfx::IntRect& aInputSpaceRect,
const gfx::IntRect& aOutputSpaceRect);
/**
* Checks a generated image for correctness. Reports any unexpected deviation
* from the expected image as GTest failures.
*
* @param aDecoder The decoder which contains the image. The decoder's current
* frame will be checked.
* @param aRect The region in the space of the output surface that the filter
* pipeline will actually write to. It's expected that pixels in
* this region are green, while pixels outside this region are
* transparent. Defaults to the entire output rect.
* @param aFuzz The amount of fuzz to use in pixel comparisons.
*/
void CheckGeneratedImage(Decoder* aDecoder,
const gfx::IntRect& aRect,
uint8_t aFuzz = 0);
/**
* Tests the result of calling WritePixels() using the provided SurfaceFilter
* pipeline. The pipeline must be a normal (i.e., non-paletted) pipeline.
*
* The arguments are specified in the an order intended to minimize the number
* of arguments that most test cases need to pass.
*
* @param aDecoder The decoder whose current frame will be written to.
* @param aFilter The SurfaceFilter pipeline to use.
* @param aOutputRect The region in the space of the output surface that will be
* invalidated by the filter pipeline. Defaults to
* (0, 0, 100, 100).
* @param aInputRect The region in the space of the input image that will be
* invalidated by the filter pipeline. Defaults to
* (0, 0, 100, 100).
* @param aInputWriteRect The region in the space of the input image that the
* filter pipeline will allow writes to. Note the
* difference from @aInputRect: @aInputRect is the actual
* region invalidated, while @aInputWriteRect is the
* region that is written to. These can differ in cases
* where the input is not clipped to the size of the image.
* Defaults to the entire input rect.
* @param aOutputWriteRect The region in the space of the output surface that
* the filter pipeline will actually write to. It's
* expected that pixels in this region are green, while
* pixels outside this region are transparent. Defaults
* to the entire output rect.
*/
void CheckWritePixels(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<gfx::IntRect> aOutputRect = Nothing(),
Maybe<gfx::IntRect> aInputRect = Nothing(),
Maybe<gfx::IntRect> aInputWriteRect = Nothing(),
Maybe<gfx::IntRect> aOutputWriteRect = Nothing(),
uint8_t aFuzz = 0);
/**
* Tests the result of calling WriteRows() using the provided SurfaceFilter
* pipeline. The pipeline must be a normal (i.e., non-paletted) pipeline.
* @see CheckWritePixels() for documentation of the arguments.
*/
void CheckWriteRows(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<gfx::IntRect> aOutputRect = Nothing(),
Maybe<gfx::IntRect> aInputRect = Nothing(),
Maybe<gfx::IntRect> aInputWriteRect = Nothing(),
Maybe<gfx::IntRect> aOutputWriteRect = Nothing(),
uint8_t aFuzz = 0);
/**
* Tests the result of calling WritePixels() using the provided SurfaceFilter
* pipeline. The pipeline must be a paletted pipeline.
* @see CheckWritePixels() for documentation of the arguments.
*/
void CheckPalettedWritePixels(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<gfx::IntRect> aOutputRect = Nothing(),
Maybe<gfx::IntRect> aInputRect = Nothing(),
Maybe<gfx::IntRect> aInputWriteRect = Nothing(),
Maybe<gfx::IntRect> aOutputWriteRect = Nothing(),
uint8_t aFuzz = 0);
/**
* Tests the result of calling WriteRows() using the provided SurfaceFilter
* pipeline. The pipeline must be a paletted pipeline.
* @see CheckWritePixels() for documentation of the arguments.
*/
void CheckPalettedWriteRows(Decoder* aDecoder,
SurfaceFilter* aFilter,
Maybe<gfx::IntRect> aOutputRect = Nothing(),
Maybe<gfx::IntRect> aInputRect = Nothing(),
Maybe<gfx::IntRect> aInputWriteRect = Nothing(),
Maybe<gfx::IntRect> aOutputWriteRect = Nothing(),
uint8_t aFuzz = 0);
///////////////////////////////////////////////////////////////////////////////
@ -105,6 +312,7 @@ ImageTestCase TransparentBMPWhenBMPAlphaEnabledTestCase();
ImageTestCase RLE4BMPTestCase();
ImageTestCase RLE8BMPTestCase();
} // namespace image
} // namespace mozilla
#endif // mozilla_image_test_gtest_Common_h

View File

@ -63,7 +63,7 @@ public:
EXPECT_EQ(mTestCase.mSize, mSurface->GetSize());
EXPECT_TRUE(IsSolidColor(mSurface, BGRAColor::Green(),
mTestCase.mFlags & TEST_CASE_IS_FUZZY));
mTestCase.mFlags & TEST_CASE_IS_FUZZY ? 1 : 0));
}
private:

View File

@ -79,7 +79,7 @@ CheckDecoderResults(const ImageTestCase& aTestCase, Decoder* aDecoder)
surface->GetFormat() == SurfaceFormat::B8G8R8A8);
EXPECT_EQ(aTestCase.mSize, surface->GetSize());
EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green(),
aTestCase.mFlags & TEST_CASE_IS_FUZZY));
aTestCase.mFlags & TEST_CASE_IS_FUZZY ? 1 : 0));
}
static void

View File

@ -0,0 +1,636 @@
/* -*- 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 <typename Func> void
WithDeinterlacingFilter(const IntSize& aSize,
bool aProgressiveDisplay,
Func aFunc)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(bool(decoder));
WithFilterPipeline(decoder, Forward<Func>(aFunc),
DeinterlacingConfig<uint32_t> { aProgressiveDisplay },
SurfaceConfig { decoder, 0, aSize,
SurfaceFormat::B8G8R8A8, false });
}
template <typename Func> void
WithPalettedDeinterlacingFilter(const IntSize& aSize,
Func aFunc)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
WithFilterPipeline(decoder, Forward<Func>(aFunc),
DeinterlacingConfig<uint8_t> { /* mProgressiveDisplay = */ true },
PalettedSurfaceConfig { decoder, 0, aSize,
IntRect(0, 0, 100, 100),
SurfaceFormat::B8G8R8A8, 8,
false });
}
void
AssertConfiguringDeinterlacingFilterFails(const IntSize& aSize)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
AssertConfiguringPipelineFails(decoder,
DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true},
SurfaceConfig { decoder, 0, aSize,
SurfaceFormat::B8G8R8A8, false });
}
TEST(ImageDeinterlacingFilter, WritePixels100_100)
{
WithDeinterlacingFilter(IntSize(100, 100), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)));
});
}
TEST(ImageDeinterlacingFilter, WriteRows100_100)
{
WithDeinterlacingFilter(IntSize(100, 100), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)));
});
}
TEST(ImageDeinterlacingFilter, WritePixels99_99)
{
WithDeinterlacingFilter(IntSize(99, 99), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 99, 99)),
/* aInputRect = */ Some(IntRect(0, 0, 99, 99)));
});
}
TEST(ImageDeinterlacingFilter, WriteRows99_99)
{
WithDeinterlacingFilter(IntSize(99, 99), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 99, 99)),
/* aInputRect = */ Some(IntRect(0, 0, 99, 99)));
});
}
TEST(ImageDeinterlacingFilter, WritePixels8_8)
{
WithDeinterlacingFilter(IntSize(8, 8), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 8, 8)),
/* aInputRect = */ Some(IntRect(0, 0, 8, 8)));
});
}
TEST(ImageDeinterlacingFilter, WriteRows8_8)
{
WithDeinterlacingFilter(IntSize(8, 8), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 8, 8)),
/* aInputRect = */ Some(IntRect(0, 0, 8, 8)));
});
}
TEST(ImageDeinterlacingFilter, WritePixels7_7)
{
WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 7, 7)),
/* aInputRect = */ Some(IntRect(0, 0, 7, 7)));
});
}
TEST(ImageDeinterlacingFilter, WriteRows7_7)
{
WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 7, 7)),
/* aInputRect = */ Some(IntRect(0, 0, 7, 7)));
});
}
TEST(ImageDeinterlacingFilter, WritePixels3_3)
{
WithDeinterlacingFilter(IntSize(3, 3), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 3, 3)),
/* aInputRect = */ Some(IntRect(0, 0, 3, 3)));
});
}
TEST(ImageDeinterlacingFilter, WriteRows3_3)
{
WithDeinterlacingFilter(IntSize(3, 3), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 3, 3)),
/* aInputRect = */ Some(IntRect(0, 0, 3, 3)));
});
}
TEST(ImageDeinterlacingFilter, WritePixels1_1)
{
WithDeinterlacingFilter(IntSize(1, 1), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 1, 1)),
/* aInputRect = */ Some(IntRect(0, 0, 1, 1)));
});
}
TEST(ImageDeinterlacingFilter, WriteRows1_1)
{
WithDeinterlacingFilter(IntSize(1, 1), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 1, 1)),
/* aInputRect = */ Some(IntRect(0, 0, 1, 1)));
});
}
TEST(ImageDeinterlacingFilter, PalettedWritePixels)
{
WithPalettedDeinterlacingFilter(IntSize(100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckPalettedWritePixels(aDecoder, aFilter);
});
}
TEST(ImageDeinterlacingFilter, PalettedWriteRows)
{
WithPalettedDeinterlacingFilter(IntSize(100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckPalettedWriteRows(aDecoder, aFilter);
});
}
TEST(ImageDeinterlacingFilter, WritePixelsOutput20_20)
{
WithDeinterlacingFilter(IntSize(20, 20), /* aProgressiveDisplay = */ true,
[](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>([&]() {
uint32_t row = count / 20; // Integer division.
++count;
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<SourceSurface> surface = currentFrame->GetSurface();
for (uint32_t row = 0; row < 20; ++row) {
EXPECT_TRUE(RowsAreSolidColor(surface, row, 1,
row % 2 == 0 ? BGRAColor::Green()
: BGRAColor::Red()));
}
});
}
TEST(ImageDeinterlacingFilter, WriteRowsOutput7_7)
{
WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ true,
[](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;
uint32_t row = 0;
auto result = aFilter->WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
uint32_t color = 0;
switch (row) {
// First pass. Output rows are positioned at 8n + 0.
case 0: // Output row 0.
color = BGRAColor::Green().AsPixel();
break;
// Second pass. Rows are positioned at 8n + 4.
case 1: // Output row 4.
color = BGRAColor::Green().AsPixel();
break;
// Third pass. Rows are positioned at 4n + 2.
case 2: // Output row 2.
case 3: // Output row 6.
color = BGRAColor::Red().AsPixel();
break;
// Fourth pass. Rows are positioned at 2n + 1.
case 4: // Output row 1.
color = BGRAColor::Green().AsPixel();
break;
case 5: // Output row 3.
color = BGRAColor::Red().AsPixel();
break;
case 6: // Output row 5.
color = BGRAColor::Green().AsPixel();
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected row");
}
++row;
for (; aLength > 0; --aLength, ++aRow, ++count) {
*aRow = color;
}
return Nothing();
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(7u * 7u, count);
EXPECT_EQ(7u, row);
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<SourceSurface> surface = currentFrame->GetSurface();
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(ImageDeinterlacingFilter, WritePixelsOutput3_3)
{
WithDeinterlacingFilter(IntSize(3, 3), /* aProgressiveDisplay = */ true,
[](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>([&]() {
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<SourceSurface> surface = currentFrame->GetSurface();
for (uint32_t row = 0; row < 3; ++row) {
EXPECT_TRUE(RowsAreSolidColor(surface, row, 1,
row == 0 || row == 2 ? BGRAColor::Green()
: BGRAColor::Red()));
}
});
}
TEST(ImageDeinterlacingFilter, WritePixelsOutput1_1)
{
WithDeinterlacingFilter(IntSize(1, 1), /* aProgressiveDisplay = */ true,
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Fill the image. The output should be a single red row.
uint32_t count = 0;
auto result = aFilter->WritePixels<uint32_t>([&]() {
++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<SourceSurface> surface = currentFrame->GetSurface();
EXPECT_TRUE(RowsAreSolidColor(surface, 0, 1, BGRAColor::Red()));
});
}
void
WriteRowAndCheckInterlacerOutput(Decoder* aDecoder,
SurfaceFilter* aFilter,
BGRAColor aColor,
WriteState aNextState,
IntRect aInvalidRect,
uint32_t aFirstHaeberliRow,
uint32_t aLastHaeberliRow)
{
uint32_t count = 0;
auto result = aFilter->WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
for (; aLength > 0; --aLength, ++aRow, ++count) {
*aRow = aColor.AsPixel();
}
return Some(WriteState::NEED_MORE_DATA);
});
EXPECT_EQ(aNextState, result);
EXPECT_EQ(7u, count);
// Assert that we got the expected invalidation region.
Maybe<SurfaceInvalidRect> 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<SourceSurface> surface = currentFrame->GetSurface();
for (uint32_t row = aFirstHaeberliRow; row <= aLastHaeberliRow; ++row) {
EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, aColor));
}
}
TEST(ImageDeinterlacingFilter, WriteRowsIntermediateOutput7_7)
{
WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ true,
[](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,
IntRect(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,
IntRect(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,
IntRect(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,
IntRect(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,
IntRect(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,
IntRect(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,
IntRect(0, 5, 7, 2), 5, 5);
// Assert that we're in the expected final state.
EXPECT_TRUE(aFilter->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> 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<SourceSurface> surface = currentFrame->GetSurface();
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(ImageDeinterlacingFilter, WriteRowsNonProgressiveIntermediateOutput7_7)
{
WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ false,
[](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,
IntRect(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,
IntRect(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,
IntRect(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,
IntRect(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,
IntRect(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,
IntRect(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,
IntRect(0, 5, 7, 2), 5, 5);
// Assert that we're in the expected final state.
EXPECT_TRUE(aFilter->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> 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<SourceSurface> surface = currentFrame->GetSurface();
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(ImageDeinterlacingFilter, DeinterlacingFailsFor0_0)
{
// A 0x0 input size is invalid, so configuration should fail.
AssertConfiguringDeinterlacingFilterFails(IntSize(0, 0));
}
TEST(ImageDeinterlacingFilter, DeinterlacingFailsForMinus1_Minus1)
{
// A negative input size is invalid, so configuration should fail.
AssertConfiguringDeinterlacingFilterFails(IntSize(-1, -1));
}

View File

@ -0,0 +1,364 @@
/* -*- 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 <typename Func> void
WithDownscalingFilter(const IntSize& aInputSize,
const IntSize& aOutputSize,
Func aFunc)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
WithFilterPipeline(decoder, Forward<Func>(aFunc),
DownscalingConfig { aInputSize,
SurfaceFormat::B8G8R8A8 },
SurfaceConfig { decoder, 0, aOutputSize,
SurfaceFormat::B8G8R8A8, false });
}
void
AssertConfiguringDownscalingFilterFails(const IntSize& aInputSize,
const IntSize& aOutputSize)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
AssertConfiguringPipelineFails(decoder,
DownscalingConfig { aInputSize,
SurfaceFormat::B8G8R8A8 },
SurfaceConfig { decoder, 0, aOutputSize,
SurfaceFormat::B8G8R8A8, false });
}
TEST(ImageDownscalingFilter, WritePixels100_100to99_99)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(99, 99),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 99, 99)));
});
}
TEST(ImageDownscalingFilter, WriteRows100_100to99_99)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(99, 99),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 99, 99)));
});
}
TEST(ImageDownscalingFilter, WritePixels100_100to33_33)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(33, 33),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 33, 33)));
});
}
TEST(ImageDownscalingFilter, WriteRows100_100to33_33)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(33, 33),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 33, 33)));
});
}
TEST(ImageDownscalingFilter, WritePixels100_100to1_1)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(1, 1),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 1, 1)));
});
}
TEST(ImageDownscalingFilter, WriteRows100_100to1_1)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(1, 1),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 1, 1)));
});
}
TEST(ImageDownscalingFilter, WritePixels100_100to33_99)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(33, 99),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 33, 99)));
});
}
TEST(ImageDownscalingFilter, WriteRows100_100to33_99)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(33, 99),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 33, 99)));
});
}
TEST(ImageDownscalingFilter, WritePixels100_100to99_33)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(99, 33),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 99, 33)));
});
}
TEST(ImageDownscalingFilter, WriteRows100_100to99_33)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(99, 33),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 99, 33)));
});
}
TEST(ImageDownscalingFilter, WritePixels100_100to99_1)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(99, 1),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 99, 1)));
});
}
TEST(ImageDownscalingFilter, WriteRows100_100to99_1)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(99, 1),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 99, 1)));
});
}
TEST(ImageDownscalingFilter, WritePixels100_100to1_99)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(1, 99),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 1, 99)));
});
}
TEST(ImageDownscalingFilter, WriteRows100_100to1_99)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(1, 99),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 1, 99)));
});
}
TEST(ImageDownscalingFilter, DownscalingFailsFor100_100to101_101)
{
// Upscaling is disallowed.
AssertConfiguringDownscalingFilterFails(IntSize(100, 100), IntSize(101, 101));
}
TEST(ImageDownscalingFilter, DownscalingFailsFor100_100to100_100)
{
// "Scaling" to the same size is disallowed.
AssertConfiguringDownscalingFilterFails(IntSize(100, 100), IntSize(100, 100));
}
TEST(ImageDownscalingFilter, DownscalingFailsFor0_0toMinus1_Minus1)
{
// A 0x0 input size is disallowed.
AssertConfiguringDownscalingFilterFails(IntSize(0, 0), IntSize(-1, -1));
}
TEST(ImageDownscalingFilter, DownscalingFailsForMinus1_Minus1toMinus2_Minus2)
{
// A negative input size is disallowed.
AssertConfiguringDownscalingFilterFails(IntSize(-1, -1), IntSize(-2, -2));
}
TEST(ImageDownscalingFilter, DownscalingFailsFor100_100to0_0)
{
// A 0x0 output size is disallowed.
AssertConfiguringDownscalingFilterFails(IntSize(100, 100), IntSize(0, 0));
}
TEST(ImageDownscalingFilter, DownscalingFailsFor100_100toMinus1_Minus1)
{
// A negative output size is disallowed.
AssertConfiguringDownscalingFilterFails(IntSize(100, 100), IntSize(-1, -1));
}
TEST(ImageDownscalingFilter, WritePixelsOutput100_100to20_20)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(20, 20),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Fill the image. It consists of 25 lines of green, followed by 25 lines of
// red, followed by 25 lines of green, followed by 25 more lines of red.
uint32_t count = 0;
auto result = aFilter->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
uint32_t color = (count <= 25 * 100) || (count > 50 * 100 && count <= 75 * 100)
? BGRAColor::Green().AsPixel()
: BGRAColor::Red().AsPixel();
++count;
return AsVariant(color);
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u * 100u, count);
AssertCorrectPipelineFinalState(aFilter,
IntRect(0, 0, 100, 100),
IntRect(0, 0, 20, 20));
// Check that the generated image is correct. Note that we skip rows near
// the transitions between colors, since the downscaler does not produce a
// sharp boundary at these points. Even some of the rows we test need a
// small amount of fuzz; this is just the nature of Lanczos downscaling.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4, BGRAColor::Green(), /* aFuzz = */ 2));
EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3, BGRAColor::Red(), /* aFuzz = */ 3));
EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(), /* aFuzz = */ 3));
EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4, BGRAColor::Red(), /* aFuzz = */ 3));
});
}
TEST(ImageDownscalingFilter, WriteRowsOutput100_100to20_20)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(20, 20),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Fill the image. It consists of 25 lines of green, followed by 25 lines of
// red, followed by 25 lines of green, followed by 25 more lines of red.
uint32_t count = 0;
auto result = aFilter->WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
uint32_t color = (count <= 25 * 100) || (count > 50 * 100 && count <= 75 * 100)
? BGRAColor::Green().AsPixel()
: BGRAColor::Red().AsPixel();
for (; aLength > 0; --aLength, ++aRow, ++count) {
*aRow = color;
}
return Nothing();
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u * 100u, count);
AssertCorrectPipelineFinalState(aFilter,
IntRect(0, 0, 100, 100),
IntRect(0, 0, 20, 20));
// Check that the generated image is correct. (Note that we skip rows near
// the transitions between colors, since the downscaler does not produce a
// sharp boundary at these points.)
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4, BGRAColor::Green(), /* aFuzz = */ 2));
EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3, BGRAColor::Red(), /* aFuzz = */ 3));
EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(), /* aFuzz = */ 3));
EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4, BGRAColor::Red(), /* aFuzz = */ 3));
});
}
TEST(ImageDownscalingFilter, WritePixelsOutput100_100to10_20)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(10, 20),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Fill the image. It consists of 25 lines of green, followed by 25 lines of
// red, followed by 25 lines of green, followed by 25 more lines of red.
uint32_t count = 0;
auto result = aFilter->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> {
uint32_t color = (count <= 25 * 100) || (count > 50 * 100 && count <= 75 * 100)
? BGRAColor::Green().AsPixel()
: BGRAColor::Red().AsPixel();
++count;
return AsVariant(color);
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u * 100u, count);
AssertCorrectPipelineFinalState(aFilter,
IntRect(0, 0, 100, 100),
IntRect(0, 0, 10, 20));
// Check that the generated image is correct. Note that we skip rows near
// the transitions between colors, since the downscaler does not produce a
// sharp boundary at these points. Even some of the rows we test need a
// small amount of fuzz; this is just the nature of Lanczos downscaling.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4, BGRAColor::Green(), /* aFuzz = */ 2));
EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3, BGRAColor::Red(), /* aFuzz = */ 3));
EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(), /* aFuzz = */ 3));
EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4, BGRAColor::Red(), /* aFuzz = */ 3));
});
}
TEST(ImageDownscalingFilter, WriteRowsOutput100_100to10_20)
{
WithDownscalingFilter(IntSize(100, 100), IntSize(10, 20),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Fill the image. It consists of 25 lines of green, followed by 25 lines of
// red, followed by 25 lines of green, followed by 25 more lines of red.
uint32_t count = 0;
auto result = aFilter->WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
uint32_t color = (count <= 25 * 100) || (count > 50 * 100 && count <= 75 * 100)
? BGRAColor::Green().AsPixel()
: BGRAColor::Red().AsPixel();
for (; aLength > 0; --aLength, ++aRow, ++count) {
*aRow = color;
}
return Nothing();
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(100u * 100u, count);
AssertCorrectPipelineFinalState(aFilter,
IntRect(0, 0, 100, 100),
IntRect(0, 0, 10, 20));
// Check that the generated image is correct. (Note that we skip rows near
// the transitions between colors, since the downscaler does not produce a
// sharp boundary at these points.)
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4, BGRAColor::Green(), /* aFuzz = */ 2));
EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3, BGRAColor::Red(), /* aFuzz = */ 3));
EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(), /* aFuzz = */ 3));
EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4, BGRAColor::Red(), /* aFuzz = */ 3));
});
}
TEST(ImageDownscalingFilter, ConfiguringPalettedDownscaleFails)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
// DownscalingFilter does not support paletted images, so configuration should
// fail.
AssertConfiguringPipelineFails(decoder,
DownscalingConfig { IntSize(100, 100),
SurfaceFormat::B8G8R8A8 },
PalettedSurfaceConfig { decoder, 0, IntSize(20, 20),
IntRect(0, 0, 20, 20),
SurfaceFormat::B8G8R8A8, 8,
false });
}

View File

@ -0,0 +1,57 @@
/* -*- 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 "Decoder.h"
#include "DecoderFactory.h"
#include "SourceBuffer.h"
#include "SurfacePipe.h"
// We want to ensure that we're testing the non-Skia fallback version of
// DownscalingFilter, but there are two issues:
// (1) We don't know whether Skia is currently enabled.
// (2) If we force disable it, the disabled version will get linked into the
// binary and will cause the tests in TestDownscalingFilter to fail.
// To avoid these problems, we ensure that MOZ_ENABLE_SKIA is defined when
// including DownscalingFilter.h, and we use the preprocessor to redefine the
// DownscalingFilter class to DownscalingFilterNoSkia.
#define DownscalingFilter DownscalingFilterNoSkia
#ifdef MOZ_ENABLE_SKIA
#undef MOZ_ENABLE_SKIA
#include "Common.h"
#include "DownscalingFilter.h"
#define MOZ_ENABLE_SKIA
#else
#include "Common.h"
#include "DownscalingFilter.h"
#endif
#undef DownscalingFilter
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::image;
TEST(ImageDownscalingFilter, NoSkia)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(bool(decoder));
// Configuring a DownscalingFilter should fail without Skia.
AssertConfiguringPipelineFails(decoder,
DownscalingConfig { IntSize(100, 100),
SurfaceFormat::B8G8R8A8 },
SurfaceConfig { decoder, 0, IntSize(50, 50),
SurfaceFormat::B8G8R8A8, false });
}

View File

@ -0,0 +1,565 @@
/* -*- 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 <typename Func> void
WithRemoveFrameRectFilter(const IntSize& aSize,
const IntRect& aFrameRect,
Func aFunc)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
WithFilterPipeline(decoder, Forward<Func>(aFunc),
RemoveFrameRectConfig { aFrameRect },
SurfaceConfig { decoder, 0, aSize,
SurfaceFormat::B8G8R8A8, false });
}
void
AssertConfiguringRemoveFrameRectFilterFails(const IntSize& aSize,
const IntRect& aFrameRect)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
AssertConfiguringPipelineFails(decoder,
RemoveFrameRectConfig { aFrameRect },
SurfaceConfig { decoder, 0, aSize,
SurfaceFormat::B8G8R8A8, false });
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_0_0_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_0_0_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_0_0_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(0, 0, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_0_0_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(0, 0, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_Minus50_50_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-50, 50, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_Minus50_50_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-50, 50, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_50_Minus50_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(50, -50, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_50_Minus50_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(50, -50, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_150_50_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(150, 50, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_150_50_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(150, 50, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_50_150_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(50, 150, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_50_150_0_0)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(50, 150, 0, 0),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_200_200_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(200, 200, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_200_200_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(200, 200, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_Minus200_25_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-200, 25, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_Minus200_25_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-200, 25, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_25_Minus200_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(25, -200, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_25_Minus200_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(25, -200, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_200_25_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(200, 25, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_200_25_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(200, 25, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_25_200_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(25, 200, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_25_200_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(25, 200, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is zero-size because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_Minus200_Minus200_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-200, -200, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_Minus200_Minus200_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-200, -200, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 0, 0)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 0, 0)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_Minus50_Minus50_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-50, -50, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 50, 50)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_Minus50_Minus50_100_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-50, -50, 100, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)),
/* aOutputWriteRect = */ Some(IntRect(0, 0, 50, 50)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_Minus50_25_100_50)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-50, 25, 100, 50),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 100, 50)),
/* aOutputWriteRect = */ Some(IntRect(0, 25, 50, 50)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_Minus50_25_100_50)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(-50, 25, 100, 50),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 100, 50)),
/* aOutputWriteRect = */ Some(IntRect(0, 25, 50, 50)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_25_Minus50_50_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(25, -50, 50, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 50, 100)),
/* aOutputWriteRect = */ Some(IntRect(25, 0, 50, 50)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_25_Minus50_50_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(25, -50, 50, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 50, 100)),
/* aOutputWriteRect = */ Some(IntRect(25, 0, 50, 50)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_50_25_100_50)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(50, 25, 100, 50),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 100, 50)),
/* aOutputWriteRect = */ Some(IntRect(50, 25, 50, 50)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_50_25_100_50)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(50, 25, 100, 50),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 100, 50)),
/* aOutputWriteRect = */ Some(IntRect(50, 25, 50, 50)));
});
}
TEST(ImageRemoveFrameRectFilter, WritePixels100_100_to_25_50_50_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(25, 50, 50, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is 50x50 because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWritePixels(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(25, 50, 50, 100)));
});
}
TEST(ImageRemoveFrameRectFilter, WriteRows100_100_to_25_50_50_100)
{
WithRemoveFrameRectFilter(IntSize(100, 100),
IntRect(25, 50, 50, 100),
[](Decoder* aDecoder, SurfaceFilter* aFilter) {
// Note that aInputRect is 50x50 because RemoveFrameRectFilter ignores
// trailing rows that don't show up in the output. (Leading rows
// unfortunately can't be ignored.)
CheckWriteRows(aDecoder, aFilter,
/* aOutputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputRect = */ Some(IntRect(0, 0, 100, 100)),
/* aInputWriteRect = */ Some(IntRect(0, 0, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(25, 50, 50, 100)));
});
}
TEST(ImageRemoveFrameRectFilter, RemoveFrameRectFailsFor0_0_to_0_0_100_100)
{
// A zero-size image is disallowed.
AssertConfiguringRemoveFrameRectFilterFails(IntSize(0, 0),
IntRect(0, 0, 100, 100));
}
TEST(ImageRemoveFrameRectFilter, RemoveFrameRectFailsForMinus1_Minus1_to_0_0_100_100)
{
// A negative-size image is disallowed.
AssertConfiguringRemoveFrameRectFilterFails(IntSize(-1, -1),
IntRect(0, 0, 100, 100));
}
TEST(ImageRemoveFrameRectFilter, RemoveFrameRectFailsFor100_100_to_0_0_0_0)
{
// A zero size frame rect is disallowed.
AssertConfiguringRemoveFrameRectFilterFails(IntSize(100, 100),
IntRect(0, 0, -1, -1));
}
TEST(ImageRemoveFrameRectFilter, RemoveFrameRectFailsFor100_100_to_0_0_Minus1_Minus1)
{
// A negative size frame rect is disallowed.
AssertConfiguringRemoveFrameRectFilterFails(IntSize(100, 100),
IntRect(0, 0, -1, -1));
}
TEST(ImageRemoveFrameRectFilter, ConfiguringPalettedRemoveFrameRectFails)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
// RemoveFrameRectFilter does not support paletted images, so configuration
// should fail.
AssertConfiguringPipelineFails(decoder,
RemoveFrameRectConfig { IntRect(0, 0, 50, 50) },
PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
IntRect(0, 0, 50, 50),
SurfaceFormat::B8G8R8A8, 8,
false });
}

View File

@ -0,0 +1,322 @@
/* -*- 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
TEST(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.get(), 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);
// 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);
CheckGeneratedImage(decoder, IntRect(0, 0, 100, 100));
pipe.ResetToFirstRow();
EXPECT_FALSE(pipe.IsSurfaceFinished());
RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef();
currentFrame->Finish();
}
TEST(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(ImageSurfacePipeIntegration, DeinterlaceDownscaleWriteRows)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(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(ImageSurfacePipeIntegration, RemoveFrameRectDownscaleWritePixels)
{
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(ImageSurfacePipeIntegration, RemoveFrameRectDownscaleWriteRows)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
// See the WritePixels version of this test for a discussion of where the
// numbers below come from.
auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(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(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(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectWriteRows)
{
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) {
CheckWriteRows(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(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(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectDownscaleWriteRows)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) {
CheckWriteRows(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(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(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 });
}

View File

@ -0,0 +1,578 @@
/* -*- 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
};
template <Orient Orientation, typename Func> void
WithSurfaceSink(Func aFunc)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
const bool flipVertically = Orientation == Orient::FLIP_VERTICALLY;
WithFilterPipeline(decoder, Forward<Func>(aFunc),
SurfaceConfig { decoder, 0, IntSize(100, 100),
SurfaceFormat::B8G8R8A8, flipVertically });
}
template <typename Func> void
WithPalettedSurfaceSink(const IntRect& aFrameRect, Func aFunc)
{
RefPtr<Decoder> decoder = CreateTrivialDecoder();
ASSERT_TRUE(decoder != nullptr);
WithFilterPipeline(decoder, Forward<Func>(aFunc),
PalettedSurfaceConfig { decoder, 0, IntSize(100, 100),
aFrameRect, SurfaceFormat::B8G8R8A8,
8, false });
}
TEST(ImageSurfaceSink, NullSurfaceSink)
{
// Create the NullSurfaceSink.
NullSurfaceSink sink;
nsresult rv = sink.Configure(NullSurfaceConfig { });
ASSERT_TRUE(NS_SUCCEEDED(rv));
EXPECT_TRUE(!sink.IsValidPalettedPipe());
// Ensure that we can't write anything.
bool gotCalled = false;
auto result = sink.WritePixels<uint32_t>([&]() {
gotCalled = true;
return AsVariant(BGRAColor::Green().AsPixel());
});
EXPECT_FALSE(gotCalled);
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_TRUE(sink.IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = sink.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
result = sink.WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
gotCalled = true;
for (; aLength > 0; --aLength, ++aRow) {
*aRow = BGRAColor::Green().AsPixel();
}
return Nothing();
});
EXPECT_FALSE(gotCalled);
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_TRUE(sink.IsSurfaceFinished());
invalidRect = sink.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
// Attempt to advance to the next row and make sure nothing changes.
sink.AdvanceRow();
EXPECT_TRUE(sink.IsSurfaceFinished());
invalidRect = sink.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
// Attempt to advance to the next pass and make sure nothing changes.
sink.ResetToFirstRow();
EXPECT_TRUE(sink.IsSurfaceFinished());
invalidRect = sink.TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
}
TEST(ImageSurfaceSink, SurfaceSinkWritePixels)
{
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
CheckWritePixels(aDecoder, aSink);
});
}
TEST(ImageSurfaceSink, SurfaceSinkWriteRows)
{
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
CheckWriteRows(aDecoder, aSink);
});
}
TEST(ImageSurfaceSink, SurfaceSinkWritePixelsFinish)
{
WithSurfaceSink<Orient::NORMAL>([](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);
EXPECT_TRUE(aSink->IsSurfaceFinished());
// 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->GetSurface();
EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Transparent()));
});
}
TEST(ImageSurfaceSink, SurfaceSinkWriteRowsFinish)
{
WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) {
// Write nothing into the surface; just finish immediately.
uint32_t count = 0;
auto result = aSink->WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
count++;
return Some(WriteState::FINISHED);
});
EXPECT_EQ(WriteState::FINISHED, result);
EXPECT_EQ(1u, count);
EXPECT_TRUE(aSink->IsSurfaceFinished());
// Attempt to write more and make sure that nothing gets written.
count = 0;
result = aSink->WriteRows<uint32_t>([&](uint32_t* aRow, uint32_t aLength) {
count++;
for (; aLength > 0; --aLength, ++aRow) {
*aRow = BGRAColor::Green().AsPixel();
}
return Nothing();
});
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->GetSurface();
EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Transparent()));
});
}
TEST(ImageSurfaceSink, SurfaceSinkProgressivePasses)
{
WithSurfaceSink<Orient::NORMAL>([](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->GetSurface();
EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red()));
}
{
// Reset for the second pass.
aSink->ResetToFirstRow();
EXPECT_FALSE(aSink->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
// Check that the generated image is still the first pass image.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
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->GetSurface();
EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green()));
}
});
}
TEST(ImageSurfaceSink, SurfaceSinkInvalidRect)
{
WithSurfaceSink<Orient::NORMAL>([](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(IntRect(0, 0, 100, 1), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(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(IntRect(0, 1, 100, 8), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(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(IntRect(0, 9, 100, 1), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(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(IntRect(0, 10, 100, 90), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(0, 10, 100, 90), invalidRect->mOutputSpaceRect);
// Check that the generated image is correct.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green()));
}
});
}
TEST(ImageSurfaceSink, SurfaceSinkFlipVertically)
{
WithSurfaceSink<Orient::FLIP_VERTICALLY>([](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->GetSurface();
EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red()));
}
{
// Reset for the second pass.
aSink->ResetToFirstRow();
EXPECT_FALSE(aSink->IsSurfaceFinished());
Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect();
EXPECT_TRUE(invalidRect.isNothing());
// Check that the generated image is still the first pass image.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
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(IntRect(0, 75, 100, 25), invalidRect->mInputSpaceRect);
EXPECT_EQ(IntRect(0, 75, 100, 25), invalidRect->mOutputSpaceRect);
// Check that the generated image is correct.
RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
RefPtr<SourceSurface> surface = currentFrame->GetSurface();
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->GetSurface();
EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green()));
}
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsFor0_0_100_100)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWritePixels(aDecoder, aSink);
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteRowsFor0_0_100_100)
{
WithPalettedSurfaceSink(IntRect(0, 0, 100, 100),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWriteRows(aDecoder, aSink);
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsFor25_25_50_50)
{
WithPalettedSurfaceSink(IntRect(25, 25, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWritePixels(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(25, 25, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(25, 25, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteRowsFor25_25_50_50)
{
WithPalettedSurfaceSink(IntRect(25, 25, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWriteRows(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(25, 25, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(25, 25, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsForMinus25_Minus25_50_50)
{
WithPalettedSurfaceSink(IntRect(-25, -25, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWritePixels(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(-25, -25, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(-25, -25, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteRowsForMinus25_Minus25_50_50)
{
WithPalettedSurfaceSink(IntRect(-25, -25, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWriteRows(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(-25, -25, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(-25, -25, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsFor75_Minus25_50_50)
{
WithPalettedSurfaceSink(IntRect(75, -25, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWritePixels(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(75, -25, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(75, -25, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteRowsFor75_Minus25_50_50)
{
WithPalettedSurfaceSink(IntRect(75, -25, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWriteRows(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(75, -25, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(75, -25, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsForMinus25_75_50_50)
{
WithPalettedSurfaceSink(IntRect(-25, 75, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWritePixels(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(-25, 75, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(-25, 75, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteRowsForMinus25_75_50_50)
{
WithPalettedSurfaceSink(IntRect(-25, 75, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWriteRows(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(-25, 75, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(-25, 75, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsFor75_75_50_50)
{
WithPalettedSurfaceSink(IntRect(75, 75, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWritePixels(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(75, 75, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(75, 75, 50, 50)));
});
}
TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteRowsFor75_75_50_50)
{
WithPalettedSurfaceSink(IntRect(75, 75, 50, 50),
[](Decoder* aDecoder, PalettedSurfaceSink* aSink) {
CheckPalettedWriteRows(aDecoder, aSink,
/* aOutputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputRect = */ Some(IntRect(0, 0, 50, 50)),
/* aInputWriteRect = */ Some(IntRect(75, 75, 50, 50)),
/* aOutputWriteRect = */ Some(IntRect(75, 75, 50, 50)));
});
}

View File

@ -11,8 +11,22 @@ UNIFIED_SOURCES = [
'TestCopyOnWrite.cpp',
'TestDecoders.cpp',
'TestDecodeToSurface.cpp',
'TestDeinterlacingFilter.cpp',
'TestMetadata.cpp',
'TestRemoveFrameRectFilter.cpp',
'TestStreamingLexer.cpp',
'TestSurfaceSink.cpp',
]
if CONFIG['MOZ_ENABLE_SKIA']:
UNIFIED_SOURCES += [
'TestDownscalingFilter.cpp',
'TestSurfacePipeIntegration.cpp',
]
SOURCES += [
# Can't be unified because it manipulates the preprocessor environment.
'TestDownscalingFilterNoSkia.cpp',
]
TEST_HARNESS_FILES.gtest += [
@ -34,8 +48,14 @@ TEST_HARNESS_FILES.gtest += [
'transparent.png',
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'/dom/base',
'/gfx/2d',
'/image',
]
LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES']
FINAL_LIBRARY = 'xul-gtest'