diff options
Diffstat (limited to 'image')
46 files changed, 0 insertions, 8279 deletions
diff --git a/image/moz.build b/image/moz.build index 7e7e0fe700..9eed46d31e 100644 --- a/image/moz.build +++ b/image/moz.build @@ -5,8 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. DIRS += ['build', 'decoders', 'encoders'] -if CONFIG['ENABLE_TESTS']: - DIRS += ['test/gtest'] with Files('**'): BUG_COMPONENT = ('Core', 'ImageLib') diff --git a/image/test/gtest/Common.cpp b/image/test/gtest/Common.cpp deleted file mode 100644 index 5a24bbb145..0000000000 --- a/image/test/gtest/Common.cpp +++ /dev/null @@ -1,673 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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 "Common.h" - -#include <cstdlib> - -#include "nsDirectoryServiceDefs.h" -#include "nsIDirectoryService.h" -#include "nsIFile.h" -#include "nsIInputStream.h" -#include "nsIProperties.h" -#include "nsNetUtil.h" -#include "mozilla/RefPtr.h" -#include "nsStreamUtils.h" -#include "nsString.h" - -namespace mozilla { -namespace image { - -using namespace gfx; - -using std::abs; -using std::vector; - -/////////////////////////////////////////////////////////////////////////////// -// General Helpers -/////////////////////////////////////////////////////////////////////////////// - -// These macros work like gtest's ASSERT_* macros, except that they can be used -// in functions that return values. -#define ASSERT_TRUE_OR_RETURN(e, rv) \ - EXPECT_TRUE(e); \ - if (!(e)) { \ - return rv; \ - } - -#define ASSERT_EQ_OR_RETURN(a, b, rv) \ - EXPECT_EQ(a, b); \ - if ((a) != (b)) { \ - return rv; \ - } - -#define ASSERT_GE_OR_RETURN(a, b, rv) \ - EXPECT_GE(a, b); \ - if (!((a) >= (b))) { \ - return rv; \ - } - -#define ASSERT_LE_OR_RETURN(a, b, rv) \ - EXPECT_LE(a, b); \ - if (!((a) <= (b))) { \ - return rv; \ - } - -#define ASSERT_LT_OR_RETURN(a, b, rv) \ - EXPECT_LT(a, b); \ - if (!((a) < (b))) { \ - return rv; \ - } - -already_AddRefed<nsIInputStream> -LoadFile(const char* aRelativePath) -{ - nsresult rv; - - nsCOMPtr<nsIProperties> dirService = - do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); - ASSERT_TRUE_OR_RETURN(dirService != nullptr, nullptr); - - // Retrieve the current working directory. - nsCOMPtr<nsIFile> file; - rv = dirService->Get(NS_OS_CURRENT_WORKING_DIR, - NS_GET_IID(nsIFile), getter_AddRefs(file)); - ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr); - - // Construct the final path by appending the working path to the current - // working directory. - file->AppendNative(nsDependentCString(aRelativePath)); - - // Construct an input stream for the requested file. - nsCOMPtr<nsIInputStream> inputStream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), file); - ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr); - - // Ensure the resulting input stream is buffered. - if (!NS_InputStreamIsBuffered(inputStream)) { - nsCOMPtr<nsIInputStream> bufStream; - rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), - inputStream, 1024); - ASSERT_TRUE_OR_RETURN(NS_SUCCEEDED(rv), nullptr); - inputStream = bufStream; - } - - return inputStream.forget(); -} - -bool -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 -IsSolidPalettedColor(Decoder* aDecoder, uint8_t aColor) -{ - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - return PalettedRectIsSolidColor(aDecoder, currentFrame->GetRect(), aColor); -} - -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 -PalettedRowsAreSolidColor(Decoder* aDecoder, - int32_t aStartRow, - int32_t aRowCount, - uint8_t aColor) -{ - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - IntRect frameRect = currentFrame->GetRect(); - IntRect solidColorRect(frameRect.x, aStartRow, frameRect.width, aRowCount); - return PalettedRectIsSolidColor(aDecoder, solidColorRect, aColor); -} - -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(), surfaceSize.width * 4, false); - - DataSourceSurface::ScopedMap mapping(dataSurface, - DataSourceSurface::MapType::READ); - ASSERT_TRUE_OR_RETURN(mapping.IsMapped(), false); - - uint8_t* data = dataSurface->GetData(); - ASSERT_TRUE_OR_RETURN(data != nullptr, 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); - } - } - } - - return true; -} - -bool -PalettedRectIsSolidColor(Decoder* aDecoder, const IntRect& aRect, uint8_t aColor) -{ - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - uint8_t* imageData; - uint32_t imageLength; - currentFrame->GetImageData(&imageData, &imageLength); - ASSERT_TRUE_OR_RETURN(imageData, false); - - // Clamp to the frame rect. If any pixels outside the frame rect are included, - // we immediately fail, because such pixels don't have any "color" in the - // sense this function measures - they're transparent, and that doesn't - // necessarily correspond to any color palette index at all. - IntRect frameRect = currentFrame->GetRect(); - ASSERT_EQ_OR_RETURN(imageLength, uint32_t(frameRect.Area()), false); - IntRect rect = aRect.Intersect(frameRect); - ASSERT_EQ_OR_RETURN(rect.Area(), aRect.Area(), false); - - // Translate |rect| by |frameRect.TopLeft()| to reflect the fact that the - // frame rect's offset doesn't actually mean anything in terms of the - // in-memory representation of the surface. The image data starts at the upper - // left corner of the frame rect, in other words. - rect -= frameRect.TopLeft(); - - // Walk through the image data and make sure that the entire rect has the - // palette index |aColor|. - int32_t rowLength = frameRect.width; - 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; - ASSERT_EQ_OR_RETURN(aColor, imageData[i], false); - } - } - - return true; -} - -bool -RowHasPixels(SourceSurface* aSurface, - int32_t aRow, - const vector<BGRAColor>& aPixels) -{ - ASSERT_GE_OR_RETURN(aRow, 0, false); - - IntSize surfaceSize = aSurface->GetSize(); - ASSERT_EQ_OR_RETURN(aPixels.size(), size_t(surfaceSize.width), false); - ASSERT_LT_OR_RETURN(aRow, surfaceSize.height, false); - - RefPtr<DataSourceSurface> dataSurface = aSurface->GetDataSurface(); - ASSERT_TRUE_OR_RETURN(dataSurface, false); - - ASSERT_EQ_OR_RETURN(dataSurface->Stride(), surfaceSize.width * 4, false); - - DataSourceSurface::ScopedMap mapping(dataSurface, - DataSourceSurface::MapType::READ); - ASSERT_TRUE_OR_RETURN(mapping.IsMapped(), false); - - uint8_t* data = dataSurface->GetData(); - ASSERT_TRUE_OR_RETURN(data != nullptr, false); - - int32_t rowLength = dataSurface->Stride(); - for (int32_t col = 0; col < surfaceSize.width; ++col) { - int32_t i = aRow * rowLength + col * 4; - ASSERT_EQ_OR_RETURN(aPixels[col].mBlue, data[i + 0], false); - ASSERT_EQ_OR_RETURN(aPixels[col].mGreen, data[i + 1], false); - ASSERT_EQ_OR_RETURN(aPixels[col].mRed, data[i + 2], false); - ASSERT_EQ_OR_RETURN(aPixels[col].mAlpha, data[i + 3], false); - } - - return true; -} - - -/////////////////////////////////////////////////////////////////////////////// -// SurfacePipe Helpers -/////////////////////////////////////////////////////////////////////////////// - -already_AddRefed<Decoder> -CreateTrivialDecoder() -{ - gfxPrefs::GetSingleton(); - DecoderType decoderType = DecoderFactory::GetDecoderType("image/gif"); - NotNull<RefPtr<SourceBuffer>> sourceBuffer = WrapNotNull(new SourceBuffer()); - RefPtr<Decoder> decoder = - DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, Nothing(), - 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->GetSourceSurface(); - 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)); -} - -void -CheckGeneratedPalettedImage(Decoder* aDecoder, const IntRect& aRect) -{ - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - IntSize imageSize = currentFrame->GetImageSize(); - - // 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 all 255's. (Region 'C'.) - EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, aRect, 255)); - - // Check that the area above the output rect is all 0's. (Region 'A'.) - EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, - IntRect(0, 0, imageSize.width, aRect.y), - 0)); - - // Check that the area to the left of the output rect is all 0's. (Region 'B'.) - EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, - IntRect(0, aRect.y, aRect.x, aRect.YMost()), - 0)); - - // Check that the area to the right of the output rect is all 0's. (Region 'D'.) - const int32_t widthOnRight = imageSize.width - aRect.XMost(); - EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, - IntRect(aRect.XMost(), aRect.y, widthOnRight, aRect.YMost()), - 0)); - - // Check that the area below the output rect is transparent. (Region 'E'.) - const int32_t heightBelow = imageSize.height - aRect.YMost(); - EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, - IntRect(0, aRect.YMost(), imageSize.width, heightBelow), - 0)); -} - -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 */) -{ - 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 = aFilter->WritePixels<uint32_t>([&] { - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - 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 = aFilter->WritePixels<uint32_t>([&] { - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - 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 -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 */) -{ - 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 = aFilter->WritePixels<uint8_t>([&] { - ++count; - return AsVariant(uint8_t(255)); - }); - 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 = aFilter->WritePixels<uint8_t>([&] { - ++count; - return AsVariant(uint8_t(255)); - }); - 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]); - } -} - - -/////////////////////////////////////////////////////////////////////////////// -// Test Data -/////////////////////////////////////////////////////////////////////////////// - -ImageTestCase GreenPNGTestCase() -{ - return ImageTestCase("green.png", "image/png", IntSize(100, 100)); -} - -ImageTestCase GreenGIFTestCase() -{ - return ImageTestCase("green.gif", "image/gif", IntSize(100, 100)); -} - -ImageTestCase GreenJPGTestCase() -{ - return ImageTestCase("green.jpg", "image/jpeg", IntSize(100, 100), - TEST_CASE_IS_FUZZY); -} - -ImageTestCase GreenBMPTestCase() -{ - return ImageTestCase("green.bmp", "image/bmp", IntSize(100, 100)); -} - -ImageTestCase GreenICOTestCase() -{ - // This ICO contains a 32-bit BMP, and we use a BMP's alpha data by default - // when the BMP is embedded in an ICO, so it's transparent. - return ImageTestCase("green.ico", "image/x-icon", IntSize(100, 100), - TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase GreenIconTestCase() -{ - return ImageTestCase("green.icon", "image/icon", IntSize(100, 100), - TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase GreenFirstFrameAnimatedGIFTestCase() -{ - return ImageTestCase("first-frame-green.gif", "image/gif", IntSize(100, 100), - TEST_CASE_IS_ANIMATED); -} - -ImageTestCase GreenFirstFrameAnimatedPNGTestCase() -{ - return ImageTestCase("first-frame-green.png", "image/png", IntSize(100, 100), - TEST_CASE_IS_TRANSPARENT | TEST_CASE_IS_ANIMATED); -} - -ImageTestCase CorruptTestCase() -{ - return ImageTestCase("corrupt.jpg", "image/jpeg", IntSize(100, 100), - TEST_CASE_HAS_ERROR); -} - -ImageTestCase CorruptBMPWithTruncatedHeader() -{ - // This BMP has a header which is truncated right between the BIH and the - // bitfields, which is a particularly error-prone place w.r.t. the BMP decoder - // state machine. - return ImageTestCase("invalid-truncated-metadata.bmp", "image/bmp", - IntSize(100, 100), TEST_CASE_HAS_ERROR); -} - -ImageTestCase CorruptICOWithBadBMPWidthTestCase() -{ - // This ICO contains a BMP icon which has a width that doesn't match the size - // listed in the corresponding ICO directory entry. - return ImageTestCase("corrupt-with-bad-bmp-width.ico", "image/x-icon", - IntSize(100, 100), TEST_CASE_HAS_ERROR); -} - -ImageTestCase CorruptICOWithBadBMPHeightTestCase() -{ - // This ICO contains a BMP icon which has a height that doesn't match the size - // listed in the corresponding ICO directory entry. - return ImageTestCase("corrupt-with-bad-bmp-height.ico", "image/x-icon", - IntSize(100, 100), TEST_CASE_HAS_ERROR); -} - -ImageTestCase TransparentPNGTestCase() -{ - return ImageTestCase("transparent.png", "image/png", IntSize(32, 32), - TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase TransparentGIFTestCase() -{ - return ImageTestCase("transparent.gif", "image/gif", IntSize(16, 16), - TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase FirstFramePaddingGIFTestCase() -{ - return ImageTestCase("transparent.gif", "image/gif", IntSize(16, 16), - TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase TransparentIfWithinICOBMPTestCase(TestCaseFlags aFlags) -{ - // This is a BMP that is only transparent when decoded as if it is within an - // ICO file. (Note: aFlags needs to be set to TEST_CASE_DEFAULT_FLAGS or - // TEST_CASE_IS_TRANSPARENT accordingly.) - return ImageTestCase("transparent-if-within-ico.bmp", "image/bmp", - IntSize(32, 32), aFlags); -} - -ImageTestCase RLE4BMPTestCase() -{ - return ImageTestCase("rle4.bmp", "image/bmp", IntSize(320, 240), - TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase RLE8BMPTestCase() -{ - return ImageTestCase("rle8.bmp", "image/bmp", IntSize(32, 32), - TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase NoFrameDelayGIFTestCase() -{ - // This is an invalid (or at least, questionably valid) GIF that's animated - // even though it specifies a frame delay of zero. It's animated, but it's not - // marked TEST_CASE_IS_ANIMATED because the metadata decoder can't detect that - // it's animated. - return ImageTestCase("no-frame-delay.gif", "image/gif", IntSize(100, 100)); -} - -ImageTestCase ExtraImageSubBlocksAnimatedGIFTestCase() -{ - // This is a corrupt GIF that has extra image sub blocks between the first and - // second frame. - return ImageTestCase("animated-with-extra-image-sub-blocks.gif", "image/gif", - IntSize(100, 100)); -} - -ImageTestCase DownscaledPNGTestCase() -{ - // This testcase (and all the other "downscaled") testcases) 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. It's intended that tests downscale it - // from 100x100 to 20x20, so we specify a 20x20 output size. - return ImageTestCase("downscaled.png", "image/png", IntSize(100, 100), - IntSize(20, 20)); -} - -ImageTestCase DownscaledGIFTestCase() -{ - return ImageTestCase("downscaled.gif", "image/gif", IntSize(100, 100), - IntSize(20, 20)); -} - -ImageTestCase DownscaledJPGTestCase() -{ - return ImageTestCase("downscaled.jpg", "image/jpeg", IntSize(100, 100), - IntSize(20, 20)); -} - -ImageTestCase DownscaledBMPTestCase() -{ - return ImageTestCase("downscaled.bmp", "image/bmp", IntSize(100, 100), - IntSize(20, 20)); -} - -ImageTestCase DownscaledICOTestCase() -{ - return ImageTestCase("downscaled.ico", "image/x-icon", IntSize(100, 100), - IntSize(20, 20), TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase DownscaledIconTestCase() -{ - return ImageTestCase("downscaled.icon", "image/icon", IntSize(100, 100), - IntSize(20, 20), TEST_CASE_IS_TRANSPARENT); -} - -ImageTestCase DownscaledTransparentICOWithANDMaskTestCase() -{ - // This test case is an ICO with AND mask transparency. We want to ensure that - // we can downscale it without crashing or triggering ASAN failures, but its - // content isn't simple to verify, so for now we don't check the output. - return ImageTestCase("transparent-ico-with-and-mask.ico", "image/x-icon", - IntSize(32, 32), IntSize(20, 20), - TEST_CASE_IS_TRANSPARENT | TEST_CASE_IGNORE_OUTPUT); -} - -ImageTestCase TruncatedSmallGIFTestCase() -{ - return ImageTestCase("green-1x1-truncated.gif", "image/gif", IntSize(1, 1)); -} - -} // namespace image -} // namespace mozilla diff --git a/image/test/gtest/Common.h b/image/test/gtest/Common.h deleted file mode 100644 index 0c288cddcb..0000000000 --- a/image/test/gtest/Common.h +++ /dev/null @@ -1,419 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -#ifndef mozilla_image_test_gtest_Common_h -#define mozilla_image_test_gtest_Common_h - -#include <vector> - -#include "gtest/gtest.h" - -#include "mozilla/Maybe.h" -#include "mozilla/UniquePtr.h" -#include "mozilla/gfx/2D.h" -#include "Decoder.h" -#include "gfxColor.h" -#include "imgITools.h" -#include "nsCOMPtr.h" -#include "SurfacePipe.h" -#include "SurfacePipeFactory.h" - -class nsIInputStream; - -namespace mozilla { -namespace image { - -/////////////////////////////////////////////////////////////////////////////// -// Types -/////////////////////////////////////////////////////////////////////////////// - -enum TestCaseFlags -{ - TEST_CASE_DEFAULT_FLAGS = 0, - TEST_CASE_IS_FUZZY = 1 << 0, - TEST_CASE_HAS_ERROR = 1 << 1, - TEST_CASE_IS_TRANSPARENT = 1 << 2, - TEST_CASE_IS_ANIMATED = 1 << 3, - TEST_CASE_IGNORE_OUTPUT = 1 << 4, -}; - -struct ImageTestCase -{ - ImageTestCase(const char* aPath, - const char* aMimeType, - gfx::IntSize aSize, - uint32_t aFlags = TEST_CASE_DEFAULT_FLAGS) - : mPath(aPath) - , mMimeType(aMimeType) - , mSize(aSize) - , mOutputSize(aSize) - , mFlags(aFlags) - { } - - ImageTestCase(const char* aPath, - const char* aMimeType, - gfx::IntSize aSize, - gfx::IntSize aOutputSize, - uint32_t aFlags = TEST_CASE_DEFAULT_FLAGS) - : mPath(aPath) - , mMimeType(aMimeType) - , mSize(aSize) - , mOutputSize(aOutputSize) - , mFlags(aFlags) - { } - - const char* mPath; - const char* mMimeType; - gfx::IntSize mSize; - gfx::IntSize mOutputSize; - uint32_t mFlags; -}; - -struct BGRAColor -{ - BGRAColor() : BGRAColor(0, 0, 0, 0) { } - - BGRAColor(uint8_t aBlue, uint8_t aGreen, uint8_t aRed, uint8_t aAlpha) - : mBlue(aBlue) - , mGreen(aGreen) - , mRed(aRed) - , mAlpha(aAlpha) - { } - - static BGRAColor Green() { return BGRAColor(0x00, 0xFF, 0x00, 0xFF); } - static BGRAColor Red() { return BGRAColor(0x00, 0x00, 0xFF, 0xFF); } - static BGRAColor Blue() { return BGRAColor(0xFF, 0x00, 0x00, 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; - uint8_t mRed; - uint8_t mAlpha; -}; - - -/////////////////////////////////////////////////////////////////////////////// -// General Helpers -/////////////////////////////////////////////////////////////////////////////// - -/** - * A RAII class that ensure that ImageLib services are available. Any tests that - * require ImageLib to be initialized (for example, any test that uses the - * SurfaceCache; see image::EnsureModuleInitialized() for the full list) can - * use this class to ensure that ImageLib services are available. Failure to do - * so can result in strange, non-deterministic failures. - */ -struct AutoInitializeImageLib -{ - AutoInitializeImageLib() - { - // Ensure that ImageLib services are initialized. - nsCOMPtr<imgITools> imgTools = do_CreateInstance("@mozilla.org/image/tools;1"); - EXPECT_TRUE(imgTools != nullptr); - } -}; - -/// Loads a file from the current directory. @return an nsIInputStream for it. -already_AddRefed<nsIInputStream> LoadFile(const char* aRelativePath); - -/** - * @returns true if every pixel 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 IsSolidColor(gfx::SourceSurface* aSurface, - BGRAColor aColor, - uint8_t aFuzz = 0); - -/** - * @returns true if every pixel of @aDecoder's surface has the palette index - * specified by @aColor. - */ -bool IsSolidPalettedColor(Decoder* aDecoder, uint8_t aColor); - -/** - * @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 range of rows specified by @aStartRow and - * @aRowCount of @aDecoder's surface has the palette index specified by @aColor. - */ -bool PalettedRowsAreSolidColor(Decoder* aDecoder, - int32_t aStartRow, - int32_t aRowCount, - uint8_t aColor); - -/** - * @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); - -/** - * @returns true if every pixel in the rect specified by @aRect has the palette - * index specified by @aColor. - */ -bool PalettedRectIsSolidColor(Decoder* aDecoder, - const gfx::IntRect& aRect, - uint8_t aColor); - -/** - * @returns true if the pixels in @aRow of @aSurface match the pixels given in - * @aPixels. - */ -bool RowHasPixels(gfx::SourceSurface* aSurface, - int32_t aRow, - const std::vector<BGRAColor>& aPixels); - -// ExpectNoResume is an IResumable implementation for use by tests that expect -// Resume() to never get called. -class ExpectNoResume final : public IResumable -{ -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExpectNoResume, override) - - void Resume() override { FAIL() << "Resume() should not get called"; } - -private: - ~ExpectNoResume() override { } -}; - -// CountResumes is an IResumable implementation for use by tests that expect -// Resume() to get called a certain number of times. -class CountResumes : public IResumable -{ -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CountResumes, override) - - CountResumes() : mCount(0) { } - - void Resume() override { mCount++; } - uint32_t Count() const { return mCount; } - -private: - ~CountResumes() override { } - - uint32_t mCount; -}; - - -/////////////////////////////////////////////////////////////////////////////// -// 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, const 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, const 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. - * @param aFuzz The amount of fuzz to use in pixel comparisons. - */ -void CheckGeneratedImage(Decoder* aDecoder, - const gfx::IntRect& aRect, - uint8_t aFuzz = 0); - -/** - * Checks a generated paletted 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 have a palette index of 255, while pixels outside - * this region have a palette index of 0. - */ -void CheckGeneratedPalettedImage(Decoder* aDecoder, const gfx::IntRect& aRect); - -/** - * 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 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); - - -/////////////////////////////////////////////////////////////////////////////// -// Test Data -/////////////////////////////////////////////////////////////////////////////// - -ImageTestCase GreenPNGTestCase(); -ImageTestCase GreenGIFTestCase(); -ImageTestCase GreenJPGTestCase(); -ImageTestCase GreenBMPTestCase(); -ImageTestCase GreenICOTestCase(); -ImageTestCase GreenIconTestCase(); - -ImageTestCase GreenFirstFrameAnimatedGIFTestCase(); -ImageTestCase GreenFirstFrameAnimatedPNGTestCase(); - -ImageTestCase CorruptTestCase(); -ImageTestCase CorruptBMPWithTruncatedHeader(); -ImageTestCase CorruptICOWithBadBMPWidthTestCase(); -ImageTestCase CorruptICOWithBadBMPHeightTestCase(); - -ImageTestCase TransparentPNGTestCase(); -ImageTestCase TransparentGIFTestCase(); -ImageTestCase FirstFramePaddingGIFTestCase(); -ImageTestCase NoFrameDelayGIFTestCase(); -ImageTestCase ExtraImageSubBlocksAnimatedGIFTestCase(); - -ImageTestCase TransparentBMPWhenBMPAlphaEnabledTestCase(); -ImageTestCase RLE4BMPTestCase(); -ImageTestCase RLE8BMPTestCase(); - -ImageTestCase DownscaledPNGTestCase(); -ImageTestCase DownscaledGIFTestCase(); -ImageTestCase DownscaledJPGTestCase(); -ImageTestCase DownscaledBMPTestCase(); -ImageTestCase DownscaledICOTestCase(); -ImageTestCase DownscaledIconTestCase(); -ImageTestCase DownscaledTransparentICOWithANDMaskTestCase(); - -ImageTestCase TruncatedSmallGIFTestCase(); - -} // namespace image -} // namespace mozilla - -#endif // mozilla_image_test_gtest_Common_h diff --git a/image/test/gtest/TestADAM7InterpolatingFilter.cpp b/image/test/gtest/TestADAM7InterpolatingFilter.cpp deleted file mode 100644 index d11224251e..0000000000 --- a/image/test/gtest/TestADAM7InterpolatingFilter.cpp +++ /dev/null @@ -1,671 +0,0 @@ -/* -*- 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 <algorithm> -#include <vector> - -#include "gtest/gtest.h" - -#include "mozilla/gfx/2D.h" -#include "mozilla/Maybe.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; - -using std::generate; -using std::vector; - -template <typename Func> void -WithADAM7InterpolatingFilter(const IntSize& aSize, Func aFunc) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(bool(decoder)); - - WithFilterPipeline(decoder, Forward<Func>(aFunc), - ADAM7InterpolatingConfig { }, - SurfaceConfig { decoder, aSize, - SurfaceFormat::B8G8R8A8, false }); -} - -void -AssertConfiguringADAM7InterpolatingFilterFails(const IntSize& aSize) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(bool(decoder)); - - AssertConfiguringPipelineFails(decoder, - ADAM7InterpolatingConfig { }, - SurfaceConfig { decoder, aSize, - SurfaceFormat::B8G8R8A8, false }); -} - -uint8_t -InterpolateByte(uint8_t aByteA, uint8_t aByteB, float aWeight) -{ - return uint8_t(aByteA * aWeight + aByteB * (1.0f - aWeight)); -} - -BGRAColor -InterpolateColors(BGRAColor aColor1, BGRAColor aColor2, float aWeight) -{ - return BGRAColor(InterpolateByte(aColor1.mBlue, aColor2.mBlue, aWeight), - InterpolateByte(aColor1.mGreen, aColor2.mGreen, aWeight), - InterpolateByte(aColor1.mRed, aColor2.mRed, aWeight), - InterpolateByte(aColor1.mAlpha, aColor2.mAlpha, aWeight)); -} - -enum class ShouldInterpolate -{ - eYes, - eNo -}; - -BGRAColor -HorizontallyInterpolatedPixel(uint32_t aCol, - uint32_t aWidth, - const vector<float>& aWeights, - ShouldInterpolate aShouldInterpolate, - const vector<BGRAColor>& aColors) -{ - // We cycle through the vector of weights forever. - float weight = aWeights[aCol % aWeights.size()]; - - // Find the columns of the two final pixels for this set of weights. - uint32_t finalPixel1 = aCol - aCol % aWeights.size(); - uint32_t finalPixel2 = finalPixel1 + aWeights.size(); - - // If |finalPixel2| is past the end of the row, that means that there is no - // final pixel after the pixel at |finalPixel1|. In that case, we just want to - // duplicate |finalPixel1|'s color until the end of the row. We can do that by - // setting |finalPixel2| equal to |finalPixel1| so that the interpolation has - // no effect. - if (finalPixel2 >= aWidth) { - finalPixel2 = finalPixel1; - } - - // We cycle through the vector of colors forever (subject to the above - // constraint about the end of the row). - BGRAColor color1 = aColors[finalPixel1 % aColors.size()]; - BGRAColor color2 = aColors[finalPixel2 % aColors.size()]; - - // If we're not interpolating, we treat all pixels which aren't final as - // transparent. Since the number of weights we have is equal to the stride - // between final pixels, we can check if |aCol| is a final pixel by checking - // whether |aCol| is a multiple of |aWeights.size()|. - if (aShouldInterpolate == ShouldInterpolate::eNo) { - return aCol % aWeights.size() == 0 ? color1 - : BGRAColor::Transparent(); - } - - // Interpolate. - return InterpolateColors(color1, color2, weight); -} - -vector<float>& -InterpolationWeights(int32_t aStride) -{ - // Precalculated interpolation weights. These are used to interpolate - // between final pixels or between important rows. Although no interpolation - // is actually applied to the previous final pixel or important row value, - // the arrays still start with 1.0f, which is always skipped, primarily - // because otherwise |stride1Weights| would have zero elements. - static vector<float> stride8Weights = - { 1.0f, 7 / 8.0f, 6 / 8.0f, 5 / 8.0f, 4 / 8.0f, 3 / 8.0f, 2 / 8.0f, 1 / 8.0f }; - static vector<float> stride4Weights = { 1.0f, 3 / 4.0f, 2 / 4.0f, 1 / 4.0f }; - static vector<float> stride2Weights = { 1.0f, 1 / 2.0f }; - static vector<float> stride1Weights = { 1.0f }; - - switch (aStride) { - case 8: return stride8Weights; - case 4: return stride4Weights; - case 2: return stride2Weights; - case 1: return stride1Weights; - default: - MOZ_CRASH(); - } -} - -int32_t -ImportantRowStride(uint8_t aPass) -{ - // The stride between important rows for each pass, with a dummy value for - // the nonexistent pass 0 and for pass 8, since the tests run an extra pass to - // make sure nothing breaks. - static int32_t strides[] = { 1, 8, 8, 4, 4, 2, 2, 1, 1 }; - - return strides[aPass]; -} - -size_t -FinalPixelStride(uint8_t aPass) -{ - // The stride between the final pixels in important rows for each pass, with - // a dummy value for the nonexistent pass 0 and for pass 8, since the tests - // run an extra pass to make sure nothing breaks. - static size_t strides[] = { 1, 8, 4, 4, 2, 2, 1, 1, 1 }; - - return strides[aPass]; -} - -bool -IsImportantRow(int32_t aRow, uint8_t aPass) -{ - return aRow % ImportantRowStride(aPass) == 0; -} - -/** - * ADAM7 breaks up the image into 8x8 blocks. On each of the 7 passes, a new - * set of pixels in each block receives their final values, according to the - * following pattern: - * - * 1 6 4 6 2 6 4 6 - * 7 7 7 7 7 7 7 7 - * 5 6 5 6 5 6 5 6 - * 7 7 7 7 7 7 7 7 - * 3 6 4 6 3 6 4 6 - * 7 7 7 7 7 7 7 7 - * 5 6 5 6 5 6 5 6 - * 7 7 7 7 7 7 7 7 - * - * This function produces a row of pixels @aWidth wide, suitable for testing - * horizontal interpolation on pass @aPass. The pattern of pixels used is - * determined by @aPass and @aRow, which determine which pixels are final - * according to the table above, and @aColors, from which the pixel values - * are selected. - * - * There are two different behaviors: if |eNo| is passed for - * @aShouldInterpolate, non-final pixels are treated as transparent. If |eNo| - * is passed, non-final pixels get interpolated in from the surrounding final - * pixels. The intention is that |eNo| is passed to generate input which will - * be run through ADAM7InterpolatingFilter, and |eYes| is passed to generate - * reference data to check that the filter is performing horizontal - * interpolation correctly. - * - * This function does not perform vertical interpolation. Rows which aren't on - * the current pass are filled with transparent pixels. - * - * @return a vector<BGRAColor> representing a row of pixels. - */ -vector<BGRAColor> -ADAM7HorizontallyInterpolatedRow(uint8_t aPass, - uint32_t aRow, - uint32_t aWidth, - ShouldInterpolate aShouldInterpolate, - const vector<BGRAColor>& aColors) -{ - EXPECT_GT(aPass, 0); - EXPECT_LE(aPass, 8); - EXPECT_GT(aColors.size(), 0u); - - vector<BGRAColor> result(aWidth); - - if (IsImportantRow(aRow, aPass)) { - vector<float>& weights = InterpolationWeights(FinalPixelStride(aPass)); - - // Compute the horizontally interpolated row. - uint32_t col = 0; - generate(result.begin(), result.end(), [&]{ - return HorizontallyInterpolatedPixel(col++, aWidth, weights, - aShouldInterpolate, aColors); - }); - } else { - // This is an unimportant row; just make the entire thing transparent. - generate(result.begin(), result.end(), []{ - return BGRAColor::Transparent(); - }); - } - - EXPECT_EQ(result.size(), size_t(aWidth)); - - return result; -} - -WriteState -WriteUninterpolatedPixels(SurfaceFilter* aFilter, - const IntSize& aSize, - uint8_t aPass, - const vector<BGRAColor>& aColors) -{ - WriteState result = WriteState::NEED_MORE_DATA; - - for (int32_t row = 0; row < aSize.height; ++row) { - // Compute uninterpolated pixels for this row. - vector<BGRAColor> pixels = - Move(ADAM7HorizontallyInterpolatedRow(aPass, row, aSize.width, - ShouldInterpolate::eNo, aColors)); - - // Write them to the surface. - auto pixelIterator = pixels.cbegin(); - result = aFilter->WritePixelsToRow<uint32_t>([&]{ - return AsVariant((*pixelIterator++).AsPixel()); - }); - - if (result != WriteState::NEED_MORE_DATA) { - break; - } - } - - return result; -} - -bool -CheckHorizontallyInterpolatedImage(Decoder* aDecoder, - const IntSize& aSize, - uint8_t aPass, - const vector<BGRAColor>& aColors) -{ - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - - for (int32_t row = 0; row < aSize.height; ++row) { - if (!IsImportantRow(row, aPass)) { - continue; // Don't check rows which aren't important on this pass. - } - - // Compute the expected pixels, *with* interpolation to match what the - // filter should have done. - vector<BGRAColor> expectedPixels = - Move(ADAM7HorizontallyInterpolatedRow(aPass, row, aSize.width, - ShouldInterpolate::eYes, aColors)); - - if (!RowHasPixels(surface, row, expectedPixels)) { - return false; - } - } - - return true; -} - -void -CheckHorizontalInterpolation(const IntSize& aSize, - const vector<BGRAColor>& aColors) -{ - const IntRect surfaceRect(IntPoint(0, 0), aSize); - - WithADAM7InterpolatingFilter(aSize, - [&](Decoder* aDecoder, SurfaceFilter* aFilter) { - // We check horizontal interpolation behavior for each pass individually. In - // addition to the normal 7 passes that ADAM7 includes, we also check an - // eighth pass to verify that nothing breaks if extra data is written. - for (uint8_t pass = 1; pass <= 8; ++pass) { - // Write our color pattern to the surface. We don't perform any - // interpolation when writing to the filter so that we can check that the - // filter itself *does*. - WriteState result = - WriteUninterpolatedPixels(aFilter, aSize, pass, aColors); - - EXPECT_EQ(WriteState::FINISHED, result); - AssertCorrectPipelineFinalState(aFilter, surfaceRect, surfaceRect); - - // Check that the generated image matches the expected pattern, with - // interpolation applied. - EXPECT_TRUE(CheckHorizontallyInterpolatedImage(aDecoder, aSize, - pass, aColors)); - - // Prepare for the next pass. - aFilter->ResetToFirstRow(); - } - }); -} - -BGRAColor -ADAM7RowColor(int32_t aRow, - uint8_t aPass, - const vector<BGRAColor>& aColors) -{ - EXPECT_LT(0, aPass); - EXPECT_GE(8, aPass); - EXPECT_LT(0u, aColors.size()); - - // If this is an important row, select the color from the provided vector of - // colors, which we cycle through infinitely. If not, just fill the row with - // transparent pixels. - return IsImportantRow(aRow, aPass) ? aColors[aRow % aColors.size()] - : BGRAColor::Transparent(); -} - -WriteState -WriteRowColorPixels(SurfaceFilter* aFilter, - const IntSize& aSize, - uint8_t aPass, - const vector<BGRAColor>& aColors) -{ - WriteState result = WriteState::NEED_MORE_DATA; - - for (int32_t row = 0; row < aSize.height; ++row) { - const uint32_t color = ADAM7RowColor(row, aPass, aColors).AsPixel(); - - // Fill the surface with |color| pixels. - result = aFilter->WritePixelsToRow<uint32_t>([&]{ return AsVariant(color); }); - - if (result != WriteState::NEED_MORE_DATA) { - break; - } - } - - return result; -} - -bool -CheckVerticallyInterpolatedImage(Decoder* aDecoder, - const IntSize& aSize, - uint8_t aPass, - const vector<BGRAColor>& aColors) -{ - vector<float>& weights = InterpolationWeights(ImportantRowStride(aPass)); - - for (int32_t row = 0; row < aSize.height; ++row) { - // Vertically interpolation takes place between two important rows. The - // separation between the important rows is determined by the stride of this - // pass. When there is no "next" important row because we'd run off the - // bottom of the image, we use the same row for both. This matches - // ADAM7InterpolatingFilter's behavior of duplicating the last important row - // since there isn't another important row to vertically interpolate it - // with. - const int32_t stride = ImportantRowStride(aPass); - const int32_t prevImportantRow = row - row % stride; - const int32_t maybeNextImportantRow = prevImportantRow + stride; - const int32_t nextImportantRow = maybeNextImportantRow < aSize.height - ? maybeNextImportantRow - : prevImportantRow; - - // Retrieve the colors for the important rows we're going to interpolate. - const BGRAColor prevImportantRowColor = - ADAM7RowColor(prevImportantRow, aPass, aColors); - const BGRAColor nextImportantRowColor = - ADAM7RowColor(nextImportantRow, aPass, aColors); - - // The weight we'll use for interpolation is also determined by the stride. - // A row halfway between two important rows should have pixels that have a - // 50% contribution from each of the important rows, for example. - const float weight = weights[row % stride]; - const BGRAColor interpolatedColor = - InterpolateColors(prevImportantRowColor, nextImportantRowColor, weight); - - // Generate a row of expected pixels. Every pixel in the row is always the - // same color since we're only testing vertical interpolation between - // solid-colored rows. - vector<BGRAColor> expectedPixels(aSize.width); - generate(expectedPixels.begin(), expectedPixels.end(), [&]{ - return interpolatedColor; - }); - - // Check that the pixels match. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - if (!RowHasPixels(surface, row, expectedPixels)) { - return false; - } - } - - return true; -} - -void -CheckVerticalInterpolation(const IntSize& aSize, - const vector<BGRAColor>& aColors) -{ - const IntRect surfaceRect(IntPoint(0, 0), aSize); - - WithADAM7InterpolatingFilter(aSize, - [&](Decoder* aDecoder, SurfaceFilter* aFilter) { - for (uint8_t pass = 1; pass <= 8; ++pass) { - // Write a pattern of rows to the surface. Important rows will receive a - // color selected from |aColors|; unimportant rows will be transparent. - WriteState result = WriteRowColorPixels(aFilter, aSize, pass, aColors); - - EXPECT_EQ(WriteState::FINISHED, result); - AssertCorrectPipelineFinalState(aFilter, surfaceRect, surfaceRect); - - // Check that the generated image matches the expected pattern, with - // interpolation applied. - EXPECT_TRUE(CheckVerticallyInterpolatedImage(aDecoder, aSize, - pass, aColors)); - - // Prepare for the next pass. - aFilter->ResetToFirstRow(); - } - }); -} - -void -CheckInterpolation(const IntSize& aSize, const vector<BGRAColor>& aColors) -{ - CheckHorizontalInterpolation(aSize, aColors); - CheckVerticalInterpolation(aSize, aColors); -} - -void -CheckADAM7InterpolatingWritePixels(const IntSize& aSize) -{ - // This test writes 8 passes of green pixels (the seven ADAM7 passes, plus one - // extra to make sure nothing goes wrong if we write too much input) and verifies - // that the output is a solid green surface each time. Because all the pixels - // are the same color, interpolation doesn't matter; we test the correctness - // of the interpolation algorithm itself separately. - WithADAM7InterpolatingFilter(aSize, - [&](Decoder* aDecoder, SurfaceFilter* aFilter) { - IntRect rect(IntPoint(0, 0), aSize); - - for (int32_t pass = 1; pass <= 8; ++pass) { - // We only actually write up to the last important row for each pass, - // because that row unambiguously determines the remaining rows. - const int32_t lastRow = aSize.height - 1; - const int32_t lastImportantRow = - lastRow - (lastRow % ImportantRowStride(pass)); - const IntRect inputWriteRect(0, 0, aSize.width, lastImportantRow + 1); - - CheckWritePixels(aDecoder, aFilter, - /* aOutputRect = */ Some(rect), - /* aInputRect = */ Some(rect), - /* aInputWriteRect = */ Some(inputWriteRect)); - - aFilter->ResetToFirstRow(); - EXPECT_FALSE(aFilter->IsSurfaceFinished()); - Maybe<SurfaceInvalidRect> invalidRect = aFilter->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - } - }); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels100_100) -{ - CheckADAM7InterpolatingWritePixels(IntSize(100, 100)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels99_99) -{ - CheckADAM7InterpolatingWritePixels(IntSize(99, 99)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels66_33) -{ - CheckADAM7InterpolatingWritePixels(IntSize(66, 33)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels33_66) -{ - CheckADAM7InterpolatingWritePixels(IntSize(33, 66)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels15_15) -{ - CheckADAM7InterpolatingWritePixels(IntSize(15, 15)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels9_9) -{ - CheckADAM7InterpolatingWritePixels(IntSize(9, 9)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels8_8) -{ - CheckADAM7InterpolatingWritePixels(IntSize(8, 8)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels7_7) -{ - CheckADAM7InterpolatingWritePixels(IntSize(7, 7)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels3_3) -{ - CheckADAM7InterpolatingWritePixels(IntSize(3, 3)); -} - -TEST(ImageADAM7InterpolatingFilter, WritePixels1_1) -{ - CheckADAM7InterpolatingWritePixels(IntSize(1, 1)); -} - -TEST(ImageADAM7InterpolatingFilter, TrivialInterpolation48_48) -{ - CheckInterpolation(IntSize(48, 48), { BGRAColor::Green() }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput33_17) -{ - // We check interpolation using irregular patterns to make sure that the - // interpolation will look different for different passes. - CheckInterpolation(IntSize(33, 17), { - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue(), BGRAColor::Blue(), - BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue(), - BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput32_16) -{ - CheckInterpolation(IntSize(32, 16), { - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue(), BGRAColor::Blue(), - BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue(), - BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput31_15) -{ - CheckInterpolation(IntSize(31, 15), { - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue(), BGRAColor::Blue(), - BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue(), - BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput17_33) -{ - CheckInterpolation(IntSize(17, 33), { - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), - BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput16_32) -{ - CheckInterpolation(IntSize(16, 32), { - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), - BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput15_31) -{ - CheckInterpolation(IntSize(15, 31), { - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), - BGRAColor::Red(), BGRAColor::Green(), BGRAColor::Blue(), BGRAColor::Red(), - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput9_9) -{ - CheckInterpolation(IntSize(9, 9), { - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput8_8) -{ - CheckInterpolation(IntSize(8, 8), { - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput7_7) -{ - CheckInterpolation(IntSize(7, 7), { - BGRAColor::Blue(), BGRAColor::Blue(), BGRAColor::Red(), BGRAColor::Green(), - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Red(), BGRAColor::Blue() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput3_3) -{ - CheckInterpolation(IntSize(3, 3), { - BGRAColor::Green(), BGRAColor::Red(), BGRAColor::Blue(), BGRAColor::Red() - }); -} - -TEST(ImageADAM7InterpolatingFilter, InterpolationOutput1_1) -{ - CheckInterpolation(IntSize(1, 1), { BGRAColor::Blue() }); -} - -TEST(ImageADAM7InterpolatingFilter, ADAM7InterpolationFailsFor0_0) -{ - // A 0x0 input size is invalid, so configuration should fail. - AssertConfiguringADAM7InterpolatingFilterFails(IntSize(0, 0)); -} - -TEST(ImageADAM7InterpolatingFilter, ADAM7InterpolationFailsForMinus1_Minus1) -{ - // A negative input size is invalid, so configuration should fail. - AssertConfiguringADAM7InterpolatingFilterFails(IntSize(-1, -1)); -} - -TEST(ImageADAM7InterpolatingFilter, ConfiguringPalettedADAM7InterpolatingFilterFails) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - // ADAM7InterpolatingFilter does not support paletted images, so configuration - // should fail. - AssertConfiguringPipelineFails(decoder, - ADAM7InterpolatingConfig { }, - PalettedSurfaceConfig { decoder, IntSize(100, 100), - IntRect(0, 0, 50, 50), - SurfaceFormat::B8G8R8A8, 8, - false }); -} diff --git a/image/test/gtest/TestCopyOnWrite.cpp b/image/test/gtest/TestCopyOnWrite.cpp deleted file mode 100644 index 0d420b6722..0000000000 --- a/image/test/gtest/TestCopyOnWrite.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* 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 "CopyOnWrite.h" - -using namespace mozilla; -using namespace mozilla::image; - -struct ValueStats -{ - int32_t mCopies = 0; - int32_t mFrees = 0; - int32_t mCalls = 0; - int32_t mConstCalls = 0; - int32_t mSerial = 0; -}; - -struct Value -{ - NS_INLINE_DECL_REFCOUNTING(Value) - - explicit Value(ValueStats& aStats) - : mStats(aStats) - , mSerial(mStats.mSerial++) - { } - - Value(const Value& aOther) - : mStats(aOther.mStats) - , mSerial(mStats.mSerial++) - { - mStats.mCopies++; - } - - void Go() { mStats.mCalls++; } - void Go() const { mStats.mConstCalls++; } - - int32_t Serial() const { return mSerial; } - -protected: - ~Value() { mStats.mFrees++; } - -private: - ValueStats& mStats; - int32_t mSerial; -}; - -TEST(ImageCopyOnWrite, Read) -{ - ValueStats stats; - - { - CopyOnWrite<Value> cow(new Value(stats)); - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_TRUE(cow.CanRead()); - - cow.Read([&](const Value* aValue) { - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(0, aValue->Serial()); - EXPECT_TRUE(cow.CanRead()); - EXPECT_TRUE(cow.CanWrite()); - - aValue->Go(); - - EXPECT_EQ(0, stats.mCalls); - EXPECT_EQ(1, stats.mConstCalls); - }); - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(0, stats.mCalls); - EXPECT_EQ(1, stats.mConstCalls); - } - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(1, stats.mFrees); -} - -TEST(ImageCopyOnWrite, RecursiveRead) -{ - ValueStats stats; - - { - CopyOnWrite<Value> cow(new Value(stats)); - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_TRUE(cow.CanRead()); - - cow.Read([&](const Value* aValue) { - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(0, aValue->Serial()); - EXPECT_TRUE(cow.CanRead()); - EXPECT_TRUE(cow.CanWrite()); - - // Make sure that Read() inside a Read() succeeds. - cow.Read([&](const Value* aValue) { - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(0, aValue->Serial()); - EXPECT_TRUE(cow.CanRead()); - EXPECT_TRUE(cow.CanWrite()); - - aValue->Go(); - - EXPECT_EQ(0, stats.mCalls); - EXPECT_EQ(1, stats.mConstCalls); - }, []() { - // This gets called if we can't read. We shouldn't get here. - EXPECT_TRUE(false); - }); - }); - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(0, stats.mCalls); - EXPECT_EQ(1, stats.mConstCalls); - } - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(1, stats.mFrees); -} - -TEST(ImageCopyOnWrite, Write) -{ - ValueStats stats; - - { - CopyOnWrite<Value> cow(new Value(stats)); - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_TRUE(cow.CanRead()); - EXPECT_TRUE(cow.CanWrite()); - - cow.Write([&](Value* aValue) { - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(0, aValue->Serial()); - EXPECT_TRUE(!cow.CanRead()); - EXPECT_TRUE(!cow.CanWrite()); - - aValue->Go(); - - EXPECT_EQ(1, stats.mCalls); - EXPECT_EQ(0, stats.mConstCalls); - }); - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(1, stats.mCalls); - EXPECT_EQ(0, stats.mConstCalls); - } - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(1, stats.mFrees); -} - -TEST(ImageCopyOnWrite, WriteRecursive) -{ - ValueStats stats; - - { - CopyOnWrite<Value> cow(new Value(stats)); - - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_TRUE(cow.CanRead()); - EXPECT_TRUE(cow.CanWrite()); - - cow.Read([&](const Value* aValue) { - EXPECT_EQ(0, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(0, aValue->Serial()); - EXPECT_TRUE(cow.CanRead()); - EXPECT_TRUE(cow.CanWrite()); - - // Make sure Write() inside a Read() succeeds. - cow.Write([&](Value* aValue) { - EXPECT_EQ(1, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(1, aValue->Serial()); - EXPECT_TRUE(!cow.CanRead()); - EXPECT_TRUE(!cow.CanWrite()); - - aValue->Go(); - - EXPECT_EQ(1, stats.mCalls); - EXPECT_EQ(0, stats.mConstCalls); - - // Make sure Read() inside a Write() fails. - cow.Read([](const Value* aValue) { - // This gets called if we can read. We shouldn't get here. - EXPECT_TRUE(false); - }, []() { - // This gets called if we can't read. We *should* get here. - EXPECT_TRUE(true); - }); - - // Make sure Write() inside a Write() fails. - cow.Write([](Value* aValue) { - // This gets called if we can write. We shouldn't get here. - EXPECT_TRUE(false); - }, []() { - // This gets called if we can't write. We *should* get here. - EXPECT_TRUE(true); - }); - }, []() { - // This gets called if we can't write. We shouldn't get here. - EXPECT_TRUE(false); - }); - - aValue->Go(); - - EXPECT_EQ(1, stats.mCopies); - EXPECT_EQ(0, stats.mFrees); - EXPECT_EQ(1, stats.mCalls); - EXPECT_EQ(1, stats.mConstCalls); - }); - - EXPECT_EQ(1, stats.mCopies); - EXPECT_EQ(1, stats.mFrees); - EXPECT_EQ(1, stats.mCalls); - EXPECT_EQ(1, stats.mConstCalls); - } - - EXPECT_EQ(1, stats.mCopies); - EXPECT_EQ(2, stats.mFrees); -} diff --git a/image/test/gtest/TestDecodeToSurface.cpp b/image/test/gtest/TestDecodeToSurface.cpp deleted file mode 100644 index bd52e75901..0000000000 --- a/image/test/gtest/TestDecodeToSurface.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* 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 "Common.h" -#include "imgIContainer.h" -#include "imgITools.h" -#include "ImageOps.h" -#include "mozilla/gfx/2D.h" -#include "nsComponentManagerUtils.h" -#include "nsCOMPtr.h" -#include "nsIInputStream.h" -#include "nsIRunnable.h" -#include "nsIThread.h" -#include "mozilla/RefPtr.h" -#include "nsString.h" -#include "nsThreadUtils.h" - -using namespace mozilla; -using namespace mozilla::gfx; -using namespace mozilla::image; - -class DecodeToSurfaceRunnable : public Runnable -{ -public: - DecodeToSurfaceRunnable(RefPtr<SourceSurface>& aSurface, - nsIInputStream* aInputStream, - const ImageTestCase& aTestCase) - : mSurface(aSurface) - , mInputStream(aInputStream) - , mTestCase(aTestCase) - { } - - NS_IMETHOD Run() override - { - Go(); - return NS_OK; - } - - void Go() - { - mSurface = - ImageOps::DecodeToSurface(mInputStream, - nsDependentCString(mTestCase.mMimeType), - imgIContainer::DECODE_FLAGS_DEFAULT); - ASSERT_TRUE(mSurface != nullptr); - - EXPECT_EQ(SurfaceType::DATA, mSurface->GetType()); - EXPECT_TRUE(mSurface->GetFormat() == SurfaceFormat::B8G8R8X8 || - mSurface->GetFormat() == SurfaceFormat::B8G8R8A8); - EXPECT_EQ(mTestCase.mSize, mSurface->GetSize()); - - EXPECT_TRUE(IsSolidColor(mSurface, BGRAColor::Green(), - mTestCase.mFlags & TEST_CASE_IS_FUZZY ? 1 : 0)); - } - -private: - RefPtr<SourceSurface>& mSurface; - nsCOMPtr<nsIInputStream> mInputStream; - ImageTestCase mTestCase; -}; - -static void -RunDecodeToSurface(const ImageTestCase& aTestCase) -{ - nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath); - ASSERT_TRUE(inputStream != nullptr); - - nsCOMPtr<nsIThread> thread; - nsresult rv = NS_NewThread(getter_AddRefs(thread), nullptr); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // We run the DecodeToSurface tests off-main-thread to ensure that - // DecodeToSurface doesn't require any main-thread-only code. - RefPtr<SourceSurface> surface; - nsCOMPtr<nsIRunnable> runnable = - new DecodeToSurfaceRunnable(surface, inputStream, aTestCase); - thread->Dispatch(runnable, nsIThread::DISPATCH_SYNC); - - thread->Shutdown(); - - // Explicitly release the SourceSurface on the main thread. - surface = nullptr; -} - -class ImageDecodeToSurface : public ::testing::Test -{ -protected: - AutoInitializeImageLib mInit; -}; - -TEST_F(ImageDecodeToSurface, PNG) { RunDecodeToSurface(GreenPNGTestCase()); } -TEST_F(ImageDecodeToSurface, GIF) { RunDecodeToSurface(GreenGIFTestCase()); } -TEST_F(ImageDecodeToSurface, JPG) { RunDecodeToSurface(GreenJPGTestCase()); } -TEST_F(ImageDecodeToSurface, BMP) { RunDecodeToSurface(GreenBMPTestCase()); } -TEST_F(ImageDecodeToSurface, ICO) { RunDecodeToSurface(GreenICOTestCase()); } -TEST_F(ImageDecodeToSurface, Icon) { RunDecodeToSurface(GreenIconTestCase()); } - -TEST_F(ImageDecodeToSurface, AnimatedGIF) -{ - RunDecodeToSurface(GreenFirstFrameAnimatedGIFTestCase()); -} - -TEST_F(ImageDecodeToSurface, AnimatedPNG) -{ - RunDecodeToSurface(GreenFirstFrameAnimatedPNGTestCase()); -} - -TEST_F(ImageDecodeToSurface, Corrupt) -{ - ImageTestCase testCase = CorruptTestCase(); - - nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath); - ASSERT_TRUE(inputStream != nullptr); - - RefPtr<SourceSurface> surface = - ImageOps::DecodeToSurface(inputStream, - nsDependentCString(testCase.mMimeType), - imgIContainer::DECODE_FLAGS_DEFAULT); - EXPECT_TRUE(surface == nullptr); -} diff --git a/image/test/gtest/TestDecoders.cpp b/image/test/gtest/TestDecoders.cpp deleted file mode 100644 index 58caa77a2e..0000000000 --- a/image/test/gtest/TestDecoders.cpp +++ /dev/null @@ -1,669 +0,0 @@ -/* 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 "Common.h" -#include "Decoder.h" -#include "DecoderFactory.h" -#include "decoders/nsBMPDecoder.h" -#include "IDecodingTask.h" -#include "imgIContainer.h" -#include "imgITools.h" -#include "ImageFactory.h" -#include "mozilla/gfx/2D.h" -#include "nsComponentManagerUtils.h" -#include "nsCOMPtr.h" -#include "nsIInputStream.h" -#include "nsIRunnable.h" -#include "nsIThread.h" -#include "mozilla/RefPtr.h" -#include "nsStreamUtils.h" -#include "nsString.h" -#include "nsThreadUtils.h" -#include "ProgressTracker.h" -#include "SourceBuffer.h" - -using namespace mozilla; -using namespace mozilla::gfx; -using namespace mozilla::image; - -static already_AddRefed<SourceSurface> -CheckDecoderState(const ImageTestCase& aTestCase, Decoder* aDecoder) -{ - EXPECT_TRUE(aDecoder->GetDecodeDone()); - EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR), - aDecoder->HasError()); - - // Verify that the decoder made the expected progress. - Progress progress = aDecoder->TakeProgress(); - EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_HAS_ERROR), - bool(progress & FLAG_HAS_ERROR)); - - if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) { - return nullptr; // That's all we can check for bad images. - } - - EXPECT_TRUE(bool(progress & FLAG_SIZE_AVAILABLE)); - EXPECT_TRUE(bool(progress & FLAG_DECODE_COMPLETE)); - EXPECT_TRUE(bool(progress & FLAG_FRAME_COMPLETE)); - EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT), - bool(progress & FLAG_HAS_TRANSPARENCY)); - EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED), - bool(progress & FLAG_IS_ANIMATED)); - - // The decoder should get the correct size. - IntSize size = aDecoder->Size(); - EXPECT_EQ(aTestCase.mSize.width, size.width); - EXPECT_EQ(aTestCase.mSize.height, size.height); - - // Get the current frame, which is always the first frame of the image - // because CreateAnonymousDecoder() forces a first-frame-only decode. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - - // Verify that the resulting surfaces matches our expectations. - EXPECT_EQ(SurfaceType::DATA, surface->GetType()); - EXPECT_TRUE(surface->GetFormat() == SurfaceFormat::B8G8R8X8 || - surface->GetFormat() == SurfaceFormat::B8G8R8A8); - EXPECT_EQ(aTestCase.mOutputSize, surface->GetSize()); - - return surface.forget(); -} - -static void -CheckDecoderResults(const ImageTestCase& aTestCase, Decoder* aDecoder) -{ - RefPtr<SourceSurface> surface = CheckDecoderState(aTestCase, aDecoder); - if (!surface) { - return; - } - - if (aTestCase.mFlags & TEST_CASE_IGNORE_OUTPUT) { - return; - } - - // Check the output. - EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green(), - aTestCase.mFlags & TEST_CASE_IS_FUZZY ? 1 : 0)); -} - -template <typename Func> -void WithSingleChunkDecode(const ImageTestCase& aTestCase, - const Maybe<IntSize>& aOutputSize, - Func aResultChecker) -{ - nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath); - ASSERT_TRUE(inputStream != nullptr); - - // Figure out how much data we have. - uint64_t length; - nsresult rv = inputStream->Available(&length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Write the data into a SourceBuffer. - NotNull<RefPtr<SourceBuffer>> sourceBuffer = WrapNotNull(new SourceBuffer()); - sourceBuffer->ExpectLength(length); - rv = sourceBuffer->AppendFromInputStream(inputStream, length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - sourceBuffer->Complete(NS_OK); - - // Create a decoder. - DecoderType decoderType = - DecoderFactory::GetDecoderType(aTestCase.mMimeType); - RefPtr<Decoder> decoder = - DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, aOutputSize, - DefaultSurfaceFlags()); - ASSERT_TRUE(decoder != nullptr); - RefPtr<IDecodingTask> task = new AnonymousDecodingTask(WrapNotNull(decoder)); - - // Run the full decoder synchronously. - task->Run(); - - // Call the lambda to verify the expected results. - aResultChecker(decoder); -} - -static void -CheckDecoderSingleChunk(const ImageTestCase& aTestCase) -{ - WithSingleChunkDecode(aTestCase, Nothing(), [&](Decoder* aDecoder) { - CheckDecoderResults(aTestCase, aDecoder); - }); -} - -static void -CheckDecoderMultiChunk(const ImageTestCase& aTestCase) -{ - nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath); - ASSERT_TRUE(inputStream != nullptr); - - // Figure out how much data we have. - uint64_t length; - nsresult rv = inputStream->Available(&length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Create a SourceBuffer and a decoder. - NotNull<RefPtr<SourceBuffer>> sourceBuffer = WrapNotNull(new SourceBuffer()); - sourceBuffer->ExpectLength(length); - DecoderType decoderType = - DecoderFactory::GetDecoderType(aTestCase.mMimeType); - RefPtr<Decoder> decoder = - DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, Nothing(), - DefaultSurfaceFlags()); - ASSERT_TRUE(decoder != nullptr); - RefPtr<IDecodingTask> task = new AnonymousDecodingTask(WrapNotNull(decoder)); - - for (uint64_t read = 0; read < length ; ++read) { - uint64_t available = 0; - rv = inputStream->Available(&available); - ASSERT_TRUE(available > 0); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - rv = sourceBuffer->AppendFromInputStream(inputStream, 1); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - task->Run(); - } - - sourceBuffer->Complete(NS_OK); - task->Run(); - - CheckDecoderResults(aTestCase, decoder); -} - -static void -CheckDownscaleDuringDecode(const ImageTestCase& aTestCase) -{ - // This function expects that |aTestCase| 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. We'll downscale it from 100x100 to 20x20. - IntSize outputSize(20, 20); - - WithSingleChunkDecode(aTestCase, Some(outputSize), [&](Decoder* aDecoder) { - RefPtr<SourceSurface> surface = CheckDecoderState(aTestCase, aDecoder); - - // There are no downscale-during-decode tests that have TEST_CASE_HAS_ERROR - // set, so we expect to always get a surface here. - EXPECT_TRUE(surface != nullptr); - - if (aTestCase.mFlags & TEST_CASE_IGNORE_OUTPUT) { - return; - } - - // Check that the downscaled 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. - EXPECT_TRUE(RowsAreSolidColor(surface, 0, 4, BGRAColor::Green(), /* aFuzz = */ 47)); - EXPECT_TRUE(RowsAreSolidColor(surface, 6, 3, BGRAColor::Red(), /* aFuzz = */ 27)); - EXPECT_TRUE(RowsAreSolidColor(surface, 11, 3, BGRAColor::Green(), /* aFuzz = */ 47)); - EXPECT_TRUE(RowsAreSolidColor(surface, 16, 4, BGRAColor::Red(), /* aFuzz = */ 27)); - }); -} - -class ImageDecoders : public ::testing::Test -{ -protected: - AutoInitializeImageLib mInit; -}; - -TEST_F(ImageDecoders, PNGSingleChunk) -{ - CheckDecoderSingleChunk(GreenPNGTestCase()); -} - -TEST_F(ImageDecoders, PNGMultiChunk) -{ - CheckDecoderMultiChunk(GreenPNGTestCase()); -} - -TEST_F(ImageDecoders, PNGDownscaleDuringDecode) -{ - CheckDownscaleDuringDecode(DownscaledPNGTestCase()); -} - -TEST_F(ImageDecoders, GIFSingleChunk) -{ - CheckDecoderSingleChunk(GreenGIFTestCase()); -} - -TEST_F(ImageDecoders, GIFMultiChunk) -{ - CheckDecoderMultiChunk(GreenGIFTestCase()); -} - -TEST_F(ImageDecoders, GIFDownscaleDuringDecode) -{ - CheckDownscaleDuringDecode(DownscaledGIFTestCase()); -} - -TEST_F(ImageDecoders, JPGSingleChunk) -{ - CheckDecoderSingleChunk(GreenJPGTestCase()); -} - -TEST_F(ImageDecoders, JPGMultiChunk) -{ - CheckDecoderMultiChunk(GreenJPGTestCase()); -} - -TEST_F(ImageDecoders, JPGDownscaleDuringDecode) -{ - CheckDownscaleDuringDecode(DownscaledJPGTestCase()); -} - -TEST_F(ImageDecoders, BMPSingleChunk) -{ - CheckDecoderSingleChunk(GreenBMPTestCase()); -} - -TEST_F(ImageDecoders, BMPMultiChunk) -{ - CheckDecoderMultiChunk(GreenBMPTestCase()); -} - -TEST_F(ImageDecoders, BMPDownscaleDuringDecode) -{ - CheckDownscaleDuringDecode(DownscaledBMPTestCase()); -} - -TEST_F(ImageDecoders, ICOSingleChunk) -{ - CheckDecoderSingleChunk(GreenICOTestCase()); -} - -TEST_F(ImageDecoders, ICOMultiChunk) -{ - CheckDecoderMultiChunk(GreenICOTestCase()); -} - -TEST_F(ImageDecoders, ICODownscaleDuringDecode) -{ - CheckDownscaleDuringDecode(DownscaledICOTestCase()); -} - -TEST_F(ImageDecoders, ICOWithANDMaskDownscaleDuringDecode) -{ - CheckDownscaleDuringDecode(DownscaledTransparentICOWithANDMaskTestCase()); -} - -TEST_F(ImageDecoders, IconSingleChunk) -{ - CheckDecoderSingleChunk(GreenIconTestCase()); -} - -TEST_F(ImageDecoders, IconMultiChunk) -{ - CheckDecoderMultiChunk(GreenIconTestCase()); -} - -TEST_F(ImageDecoders, IconDownscaleDuringDecode) -{ - CheckDownscaleDuringDecode(DownscaledIconTestCase()); -} - -TEST_F(ImageDecoders, AnimatedGIFSingleChunk) -{ - CheckDecoderSingleChunk(GreenFirstFrameAnimatedGIFTestCase()); -} - -TEST_F(ImageDecoders, AnimatedGIFMultiChunk) -{ - CheckDecoderMultiChunk(GreenFirstFrameAnimatedGIFTestCase()); -} - -TEST_F(ImageDecoders, AnimatedPNGSingleChunk) -{ - CheckDecoderSingleChunk(GreenFirstFrameAnimatedPNGTestCase()); -} - -TEST_F(ImageDecoders, AnimatedPNGMultiChunk) -{ - CheckDecoderMultiChunk(GreenFirstFrameAnimatedPNGTestCase()); -} - -TEST_F(ImageDecoders, CorruptSingleChunk) -{ - CheckDecoderSingleChunk(CorruptTestCase()); -} - -TEST_F(ImageDecoders, CorruptMultiChunk) -{ - CheckDecoderMultiChunk(CorruptTestCase()); -} - -TEST_F(ImageDecoders, CorruptBMPWithTruncatedHeaderSingleChunk) -{ - CheckDecoderSingleChunk(CorruptBMPWithTruncatedHeader()); -} - -TEST_F(ImageDecoders, CorruptBMPWithTruncatedHeaderMultiChunk) -{ - CheckDecoderMultiChunk(CorruptBMPWithTruncatedHeader()); -} - -TEST_F(ImageDecoders, CorruptICOWithBadBMPWidthSingleChunk) -{ - CheckDecoderSingleChunk(CorruptICOWithBadBMPWidthTestCase()); -} - -TEST_F(ImageDecoders, CorruptICOWithBadBMPWidthMultiChunk) -{ - CheckDecoderMultiChunk(CorruptICOWithBadBMPWidthTestCase()); -} - -TEST_F(ImageDecoders, CorruptICOWithBadBMPHeightSingleChunk) -{ - CheckDecoderSingleChunk(CorruptICOWithBadBMPHeightTestCase()); -} - -TEST_F(ImageDecoders, CorruptICOWithBadBMPHeightMultiChunk) -{ - CheckDecoderMultiChunk(CorruptICOWithBadBMPHeightTestCase()); -} - -TEST_F(ImageDecoders, AnimatedGIFWithFRAME_FIRST) -{ - ImageTestCase testCase = GreenFirstFrameAnimatedGIFTestCase(); - - // Verify that we can decode this test case and retrieve the first frame using - // imgIContainer::FRAME_FIRST. This ensures that we correctly trigger a - // single-frame decode rather than an animated decode when - // imgIContainer::FRAME_FIRST is requested. - - // Create an image. - RefPtr<Image> image = - ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType)); - ASSERT_TRUE(!image->HasError()); - - nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath); - ASSERT_TRUE(inputStream); - - // Figure out how much data we have. - uint64_t length; - nsresult rv = inputStream->Available(&length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Write the data into the image. - rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0, - static_cast<uint32_t>(length)); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Let the image know we've sent all the data. - rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); - tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); - - // Lock the image so its surfaces don't disappear during the test. - image->LockImage(); - - // Use GetFrame() to force a sync decode of the image, specifying FRAME_FIRST - // to ensure that we don't get an animated decode. - RefPtr<SourceSurface> surface = - image->GetFrame(imgIContainer::FRAME_FIRST, - imgIContainer::FLAG_SYNC_DECODE); - - // Ensure that the image's metadata meets our expectations. - IntSize imageSize(0, 0); - rv = image->GetWidth(&imageSize.width); - EXPECT_TRUE(NS_SUCCEEDED(rv)); - rv = image->GetHeight(&imageSize.height); - EXPECT_TRUE(NS_SUCCEEDED(rv)); - - EXPECT_EQ(testCase.mSize.width, imageSize.width); - EXPECT_EQ(testCase.mSize.height, imageSize.height); - - Progress imageProgress = tracker->GetProgress(); - - EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false); - EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true); - - // Ensure that we decoded the static version of the image. - { - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eStatic)); - ASSERT_EQ(MatchType::EXACT, result.Type()); - EXPECT_TRUE(bool(result.Surface())); - } - - // Ensure that we didn't decode the animated version of the image. - { - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eAnimated)); - ASSERT_EQ(MatchType::NOT_FOUND, result.Type()); - } - - // Use GetFrame() to force a sync decode of the image, this time specifying - // FRAME_CURRENT to ensure that we get an animated decode. - RefPtr<SourceSurface> animatedSurface = - image->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE); - - // Ensure that we decoded both frames of the animated version of the image. - { - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eAnimated)); - ASSERT_EQ(MatchType::EXACT, result.Type()); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0))); - EXPECT_TRUE(bool(result.Surface())); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(1))); - EXPECT_TRUE(bool(result.Surface())); - } - - // Ensure that the static version is still around. - { - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eStatic)); - ASSERT_EQ(MatchType::EXACT, result.Type()); - EXPECT_TRUE(bool(result.Surface())); - } -} - -TEST_F(ImageDecoders, AnimatedGIFWithFRAME_CURRENT) -{ - ImageTestCase testCase = GreenFirstFrameAnimatedGIFTestCase(); - - // Verify that we can decode this test case and retrieve the entire sequence - // of frames using imgIContainer::FRAME_CURRENT. This ensures that we - // correctly trigger an animated decode rather than a single-frame decode when - // imgIContainer::FRAME_CURRENT is requested. - - // Create an image. - RefPtr<Image> image = - ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType)); - ASSERT_TRUE(!image->HasError()); - - nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath); - ASSERT_TRUE(inputStream); - - // Figure out how much data we have. - uint64_t length; - nsresult rv = inputStream->Available(&length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Write the data into the image. - rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0, - static_cast<uint32_t>(length)); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Let the image know we've sent all the data. - rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); - tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); - - // Lock the image so its surfaces don't disappear during the test. - image->LockImage(); - - // Use GetFrame() to force a sync decode of the image, specifying - // FRAME_CURRENT to ensure we get an animated decode. - RefPtr<SourceSurface> surface = - image->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE); - - // Ensure that the image's metadata meets our expectations. - IntSize imageSize(0, 0); - rv = image->GetWidth(&imageSize.width); - EXPECT_TRUE(NS_SUCCEEDED(rv)); - rv = image->GetHeight(&imageSize.height); - EXPECT_TRUE(NS_SUCCEEDED(rv)); - - EXPECT_EQ(testCase.mSize.width, imageSize.width); - EXPECT_EQ(testCase.mSize.height, imageSize.height); - - Progress imageProgress = tracker->GetProgress(); - - EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false); - EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true); - - // Ensure that we decoded both frames of the animated version of the image. - { - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eAnimated)); - ASSERT_EQ(MatchType::EXACT, result.Type()); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0))); - EXPECT_TRUE(bool(result.Surface())); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(1))); - EXPECT_TRUE(bool(result.Surface())); - } - - // Ensure that we didn't decode the static version of the image. - { - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eStatic)); - ASSERT_EQ(MatchType::NOT_FOUND, result.Type()); - } - - // Use GetFrame() to force a sync decode of the image, this time specifying - // FRAME_FIRST to ensure that we get a single-frame decode. - RefPtr<SourceSurface> animatedSurface = - image->GetFrame(imgIContainer::FRAME_FIRST, - imgIContainer::FLAG_SYNC_DECODE); - - // Ensure that we decoded the static version of the image. - { - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eStatic)); - ASSERT_EQ(MatchType::EXACT, result.Type()); - EXPECT_TRUE(bool(result.Surface())); - } - - // Ensure that both frames of the animated version are still around. - { - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eAnimated)); - ASSERT_EQ(MatchType::EXACT, result.Type()); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0))); - EXPECT_TRUE(bool(result.Surface())); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(1))); - EXPECT_TRUE(bool(result.Surface())); - } -} - -TEST_F(ImageDecoders, AnimatedGIFWithExtraImageSubBlocks) -{ - ImageTestCase testCase = ExtraImageSubBlocksAnimatedGIFTestCase(); - - // Verify that we can decode this test case and get two frames, even though - // there are extra image sub blocks between the first and second frame. The - // extra data shouldn't confuse the decoder or cause the decode to fail. - - // Create an image. - RefPtr<Image> image = - ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType)); - ASSERT_TRUE(!image->HasError()); - - nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath); - ASSERT_TRUE(inputStream); - - // Figure out how much data we have. - uint64_t length; - nsresult rv = inputStream->Available(&length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Write the data into the image. - rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0, - static_cast<uint32_t>(length)); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Let the image know we've sent all the data. - rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); - tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); - - // Use GetFrame() to force a sync decode of the image. - RefPtr<SourceSurface> surface = - image->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE); - - // Ensure that the image's metadata meets our expectations. - IntSize imageSize(0, 0); - rv = image->GetWidth(&imageSize.width); - EXPECT_TRUE(NS_SUCCEEDED(rv)); - rv = image->GetHeight(&imageSize.height); - EXPECT_TRUE(NS_SUCCEEDED(rv)); - - EXPECT_EQ(testCase.mSize.width, imageSize.width); - EXPECT_EQ(testCase.mSize.height, imageSize.height); - - Progress imageProgress = tracker->GetProgress(); - - EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false); - EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true); - - // Ensure that we decoded both frames of the image. - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eAnimated)); - ASSERT_EQ(MatchType::EXACT, result.Type()); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0))); - EXPECT_TRUE(bool(result.Surface())); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(1))); - EXPECT_TRUE(bool(result.Surface())); -} - -TEST_F(ImageDecoders, TruncatedSmallGIFSingleChunk) -{ - CheckDecoderSingleChunk(TruncatedSmallGIFTestCase()); -} diff --git a/image/test/gtest/TestDeinterlacingFilter.cpp b/image/test/gtest/TestDeinterlacingFilter.cpp deleted file mode 100644 index 82637bbf71..0000000000 --- a/image/test/gtest/TestDeinterlacingFilter.cpp +++ /dev/null @@ -1,672 +0,0 @@ -/* -*- 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, 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, 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, aSize, - SurfaceFormat::B8G8R8A8, false }); -} - -class ImageDeinterlacingFilter : public ::testing::Test -{ -protected: - AutoInitializeImageLib mInit; -}; - -TEST_F(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_F(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_F(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_F(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_F(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_F(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_F(ImageDeinterlacingFilter, PalettedWritePixels) -{ - WithPalettedDeinterlacingFilter(IntSize(100, 100), - [](Decoder* aDecoder, SurfaceFilter* aFilter) { - CheckPalettedWritePixels(aDecoder, aFilter); - }); -} - -TEST_F(ImageDeinterlacingFilter, WritePixelsNonProgressiveOutput51_52) -{ - WithDeinterlacingFilter(IntSize(51, 52), /* aProgressiveDisplay = */ false, - [](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 / 51; // Integer division. - ++count; - - // Note that we use a switch statement here, even though it's quite - // verbose, because it's useful to have the mappings between input and - // output rows available when debugging these tests. - - switch (row) { - // First pass. Output rows are positioned at 8n + 0. - case 0: // Output row 0. - case 1: // Output row 8. - case 2: // Output row 16. - case 3: // Output row 24. - case 4: // Output row 32. - case 5: // Output row 40. - case 6: // Output row 48. - return AsVariant(BGRAColor::Green().AsPixel()); - - // Second pass. Rows are positioned at 8n + 4. - case 7: // Output row 4. - case 8: // Output row 12. - case 9: // Output row 20. - case 10: // Output row 28. - case 11: // Output row 36. - case 12: // Output row 44. - return AsVariant(BGRAColor::Green().AsPixel()); - - // Third pass. Rows are positioned at 4n + 2. - case 13: // Output row 2. - case 14: // Output row 6. - case 15: // Output row 10. - case 16: // Output row 14. - case 17: // Output row 18. - case 18: // Output row 22. - case 19: // Output row 26. - case 20: // Output row 30. - case 21: // Output row 34. - case 22: // Output row 38. - case 23: // Output row 42. - case 24: // Output row 46. - case 25: // Output row 50. - return AsVariant(BGRAColor::Green().AsPixel()); - - // Fourth pass. Rows are positioned at 2n + 1. - case 26: // Output row 1. - case 27: // Output row 3. - case 28: // Output row 5. - case 29: // Output row 7. - case 30: // Output row 9. - case 31: // Output row 11. - case 32: // Output row 13. - case 33: // Output row 15. - case 34: // Output row 17. - case 35: // Output row 19. - case 36: // Output row 21. - case 37: // Output row 23. - case 38: // Output row 25. - case 39: // Output row 27. - case 40: // Output row 29. - case 41: // Output row 31. - case 42: // Output row 33. - case 43: // Output row 35. - case 44: // Output row 37. - case 45: // Output row 39. - case 46: // Output row 41. - case 47: // Output row 43. - case 48: // Output row 45. - case 49: // Output row 47. - case 50: // Output row 49. - case 51: // Output row 51. - return AsVariant(BGRAColor::Red().AsPixel()); - - default: - MOZ_ASSERT_UNREACHABLE("Unexpected row"); - return AsVariant(BGRAColor::Transparent().AsPixel()); - } - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(51u * 52u, count); - - AssertCorrectPipelineFinalState(aFilter, - IntRect(0, 0, 51, 52), - IntRect(0, 0, 51, 52)); - - // Check that the generated image is correct. As mentioned above, we expect - // even rows to be green and odd rows to be red. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - - for (uint32_t row = 0; row < 52; ++row) { - EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, - row % 2 == 0 ? BGRAColor::Green() - : BGRAColor::Red())); - } - }); -} - -TEST_F(ImageDeinterlacingFilter, WritePixelsOutput20_20) -{ - WithDeinterlacingFilter(IntSize(20, 20), /* aProgressiveDisplay = */ true, - [](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; - - // Note that we use a switch statement here, even though it's quite - // verbose, because it's useful to have the mappings between input and - // output rows available when debugging these tests. - - switch (row) { - // First pass. Output rows are positioned at 8n + 0. - case 0: // Output row 0. - case 1: // Output row 8. - case 2: // Output row 16. - return AsVariant(BGRAColor::Green().AsPixel()); - - // Second pass. Rows are positioned at 8n + 4. - case 3: // Output row 4. - case 4: // Output row 12. - return AsVariant(BGRAColor::Green().AsPixel()); - - // Third pass. Rows are positioned at 4n + 2. - case 5: // Output row 2. - case 6: // Output row 6. - case 7: // Output row 10. - case 8: // Output row 14. - case 9: // Output row 18. - return AsVariant(BGRAColor::Green().AsPixel()); - - // Fourth pass. Rows are positioned at 2n + 1. - case 10: // Output row 1. - case 11: // Output row 3. - case 12: // Output row 5. - case 13: // Output row 7. - case 14: // Output row 9. - case 15: // Output row 11. - case 16: // Output row 13. - case 17: // Output row 15. - case 18: // Output row 17. - case 19: // Output row 19. - return AsVariant(BGRAColor::Red().AsPixel()); - - default: - MOZ_ASSERT_UNREACHABLE("Unexpected row"); - return AsVariant(BGRAColor::Transparent().AsPixel()); - } - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(20u * 20u, count); - - AssertCorrectPipelineFinalState(aFilter, - IntRect(0, 0, 20, 20), - IntRect(0, 0, 20, 20)); - - // Check that the generated image is correct. As mentioned above, we expect - // even rows to be green and odd rows to be red. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - - for (uint32_t row = 0; row < 20; ++row) { - EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, - row % 2 == 0 ? BGRAColor::Green() - : BGRAColor::Red())); - } - }); -} - -TEST_F(ImageDeinterlacingFilter, WritePixelsOutput7_7) -{ - WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ true, - [](Decoder* aDecoder, SurfaceFilter* aFilter) { - // Fill the image. The output should be a repeating pattern of two green - // rows followed by two red rows but we need to write the rows in the order - // that the deinterlacer expects them. - uint32_t count = 0; - auto result = aFilter->WritePixels<uint32_t>([&]() { - uint32_t row = count / 7; // Integer division. - ++count; - - switch (row) { - // First pass. Output rows are positioned at 8n + 0. - case 0: // Output row 0. - return AsVariant(BGRAColor::Green().AsPixel()); - - // Second pass. Rows are positioned at 8n + 4. - case 1: // Output row 4. - return AsVariant(BGRAColor::Green().AsPixel()); - - // Third pass. Rows are positioned at 4n + 2. - case 2: // Output row 2. - case 3: // Output row 6. - return AsVariant(BGRAColor::Red().AsPixel()); - - // Fourth pass. Rows are positioned at 2n + 1. - case 4: // Output row 1. - return AsVariant(BGRAColor::Green().AsPixel()); - - case 5: // Output row 3. - return AsVariant(BGRAColor::Red().AsPixel()); - - case 6: // Output row 5. - return AsVariant(BGRAColor::Green().AsPixel()); - - default: - MOZ_ASSERT_UNREACHABLE("Unexpected row"); - return AsVariant(BGRAColor::Transparent().AsPixel()); - } - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(7u * 7u, count); - - AssertCorrectPipelineFinalState(aFilter, - IntRect(0, 0, 7, 7), - IntRect(0, 0, 7, 7)); - - // Check that the generated image is correct. As mentioned above, we expect - // two green rows, followed by two red rows, then two green rows, etc. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - - for (uint32_t row = 0; row < 7; ++row) { - BGRAColor color = row == 0 || row == 1 || row == 4 || row == 5 - ? BGRAColor::Green() - : BGRAColor::Red(); - EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, color)); - } - }); -} - -TEST_F(ImageDeinterlacingFilter, WritePixelsOutput3_3) -{ - WithDeinterlacingFilter(IntSize(3, 3), /* aProgressiveDisplay = */ true, - [](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->GetSourceSurface(); - - for (uint32_t row = 0; row < 3; ++row) { - EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, - row == 0 || row == 2 ? BGRAColor::Green() - : BGRAColor::Red())); - } - }); -} - -TEST_F(ImageDeinterlacingFilter, WritePixelsOutput1_1) -{ - WithDeinterlacingFilter(IntSize(1, 1), /* aProgressiveDisplay = */ true, - [](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->GetSourceSurface(); - - 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->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> { - if (count < 7) { - ++count; - return AsVariant(aColor.AsPixel()); - } - return AsVariant(WriteState::NEED_MORE_DATA); - }); - - EXPECT_EQ(aNextState, result); - EXPECT_EQ(7u, count); - - // Assert that we got the expected invalidation region. - Maybe<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->GetSourceSurface(); - - for (uint32_t row = aFirstHaeberliRow; row <= aLastHaeberliRow; ++row) { - EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, aColor)); - } -} - -TEST_F(ImageDeinterlacingFilter, WritePixelsIntermediateOutput7_7) -{ - WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ true, - [](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->GetSourceSurface(); - - for (uint32_t row = 0; row < 7; ++row) { - BGRAColor color = row == 0 || row == 1 || row == 4 || row == 5 - ? BGRAColor::Green() - : BGRAColor::Red(); - EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, color)); - } - }); -} - -TEST_F(ImageDeinterlacingFilter, WritePixelsNonProgressiveIntermediateOutput7_7) -{ - WithDeinterlacingFilter(IntSize(7, 7), /* aProgressiveDisplay = */ false, - [](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->GetSourceSurface(); - - for (uint32_t row = 0; row < 7; ++row) { - BGRAColor color = row == 0 || row == 1 || row == 4 || row == 5 - ? BGRAColor::Green() - : BGRAColor::Red(); - EXPECT_TRUE(RowsAreSolidColor(surface, row, 1, color)); - } - }); -} - - -TEST_F(ImageDeinterlacingFilter, DeinterlacingFailsFor0_0) -{ - // A 0x0 input size is invalid, so configuration should fail. - AssertConfiguringDeinterlacingFilterFails(IntSize(0, 0)); -} - -TEST_F(ImageDeinterlacingFilter, DeinterlacingFailsForMinus1_Minus1) -{ - // A negative input size is invalid, so configuration should fail. - AssertConfiguringDeinterlacingFilterFails(IntSize(-1, -1)); -} diff --git a/image/test/gtest/TestDownscalingFilter.cpp b/image/test/gtest/TestDownscalingFilter.cpp deleted file mode 100644 index d7aa0ead2a..0000000000 --- a/image/test/gtest/TestDownscalingFilter.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/* -*- 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, 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, 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, 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, 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, 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, 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, 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, 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, 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->GetSourceSurface(); - 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->GetSourceSurface(); - 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, IntSize(20, 20), - IntRect(0, 0, 20, 20), - SurfaceFormat::B8G8R8A8, 8, - false }); -} diff --git a/image/test/gtest/TestDownscalingFilterNoSkia.cpp b/image/test/gtest/TestDownscalingFilterNoSkia.cpp deleted file mode 100644 index 80928a8809..0000000000 --- a/image/test/gtest/TestDownscalingFilterNoSkia.cpp +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- 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, IntSize(50, 50), - SurfaceFormat::B8G8R8A8, false }); -} diff --git a/image/test/gtest/TestLoader.cpp b/image/test/gtest/TestLoader.cpp deleted file mode 100644 index 5551f3f053..0000000000 --- a/image/test/gtest/TestLoader.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- 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 "Common.h" -#include "imgLoader.h" -#include "nsMimeTypes.h" -#include "nsString.h" - -using namespace mozilla; -using namespace mozilla::image; - -static void -CheckMimeType(const char* aContents, size_t aLength, const char* aExpected) -{ - nsAutoCString detected; - nsresult rv = imgLoader::GetMimeTypeFromContent(aContents, aLength, detected); - if (aExpected) { - ASSERT_TRUE(NS_SUCCEEDED(rv)); - EXPECT_TRUE(detected.EqualsASCII(aExpected)); - } else { - ASSERT_TRUE(NS_FAILED(rv)); - EXPECT_TRUE(detected.IsEmpty()); - } -} - -class ImageLoader : public ::testing::Test -{ -protected: - AutoInitializeImageLib mInit; -}; - -TEST_F(ImageLoader, DetectGIF) -{ - const char buffer[] = "GIF87a"; - CheckMimeType(buffer, sizeof(buffer), IMAGE_GIF); -} - -TEST_F(ImageLoader, DetectPNG) -{ - const char buffer[] = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; - CheckMimeType(buffer, sizeof(buffer), IMAGE_PNG); -} - -TEST_F(ImageLoader, DetectJPEG) -{ - const char buffer[] = "\xFF\xD8\xFF"; - CheckMimeType(buffer, sizeof(buffer), IMAGE_JPEG); -} - -TEST_F(ImageLoader, DetectART) -{ - const char buffer[] = "\x4A\x47\xFF\xFF\x00"; - CheckMimeType(buffer, sizeof(buffer), IMAGE_ART); -} - -TEST_F(ImageLoader, DetectBMP) -{ - const char buffer[] = "BM"; - CheckMimeType(buffer, sizeof(buffer), IMAGE_BMP); -} - -TEST_F(ImageLoader, DetectICO) -{ - const char buffer[] = "\x00\x00\x01\x00"; - CheckMimeType(buffer, sizeof(buffer), IMAGE_ICO); -} - -TEST_F(ImageLoader, DetectWebP) -{ - const char buffer[] = "RIFF\xFF\xFF\xFF\xFFWEBPVP8L"; - CheckMimeType(buffer, sizeof(buffer), IMAGE_WEBP); -} - -TEST_F(ImageLoader, DetectNone) -{ - const char buffer[] = "abcdefghijklmnop"; - CheckMimeType(buffer, sizeof(buffer), nullptr); -} - diff --git a/image/test/gtest/TestMetadata.cpp b/image/test/gtest/TestMetadata.cpp deleted file mode 100644 index 9f3a648988..0000000000 --- a/image/test/gtest/TestMetadata.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* 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 "Common.h" -#include "Decoder.h" -#include "DecoderFactory.h" -#include "decoders/nsBMPDecoder.h" -#include "IDecodingTask.h" -#include "imgIContainer.h" -#include "imgITools.h" -#include "ImageFactory.h" -#include "mozilla/gfx/2D.h" -#include "nsComponentManagerUtils.h" -#include "nsCOMPtr.h" -#include "nsIInputStream.h" -#include "nsIRunnable.h" -#include "nsIThread.h" -#include "mozilla/RefPtr.h" -#include "nsStreamUtils.h" -#include "nsString.h" -#include "nsThreadUtils.h" -#include "ProgressTracker.h" -#include "SourceBuffer.h" - -using namespace mozilla; -using namespace mozilla::gfx; -using namespace mozilla::image; - -enum class BMPWithinICO -{ - NO, - YES -}; - -static void -CheckMetadata(const ImageTestCase& aTestCase, - BMPWithinICO aBMPWithinICO = BMPWithinICO::NO) -{ - nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath); - ASSERT_TRUE(inputStream != nullptr); - - // Figure out how much data we have. - uint64_t length; - nsresult rv = inputStream->Available(&length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Write the data into a SourceBuffer. - NotNull<RefPtr<SourceBuffer>> sourceBuffer = WrapNotNull(new SourceBuffer()); - sourceBuffer->ExpectLength(length); - rv = sourceBuffer->AppendFromInputStream(inputStream, length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - sourceBuffer->Complete(NS_OK); - - // Create a metadata decoder. - DecoderType decoderType = - DecoderFactory::GetDecoderType(aTestCase.mMimeType); - RefPtr<Decoder> decoder = - DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, sourceBuffer); - ASSERT_TRUE(decoder != nullptr); - RefPtr<IDecodingTask> task = new AnonymousDecodingTask(WrapNotNull(decoder)); - - if (aBMPWithinICO == BMPWithinICO::YES) { - static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO(); - } - - // Run the metadata decoder synchronously. - task->Run(); - - // Ensure that the metadata decoder didn't make progress it shouldn't have - // (which would indicate that it decoded past the header of the image). - Progress metadataProgress = decoder->TakeProgress(); - EXPECT_TRUE(0 == (metadataProgress & ~(FLAG_SIZE_AVAILABLE | - FLAG_HAS_TRANSPARENCY | - FLAG_IS_ANIMATED))); - - // If the test case is corrupt, assert what we can and return early. - if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) { - EXPECT_TRUE(decoder->GetDecodeDone()); - EXPECT_TRUE(decoder->HasError()); - return; - } - - EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError()); - - // Check that we got the expected metadata. - EXPECT_TRUE(metadataProgress & FLAG_SIZE_AVAILABLE); - - IntSize metadataSize = decoder->Size(); - EXPECT_EQ(aTestCase.mSize.width, metadataSize.width); - EXPECT_EQ(aTestCase.mSize.height, metadataSize.height); - - bool expectTransparency = aBMPWithinICO == BMPWithinICO::YES - ? true - : bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT); - EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY)); - - EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED), - bool(metadataProgress & FLAG_IS_ANIMATED)); - - // Create a full decoder, so we can compare the result. - decoder = - DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer, Nothing(), - DefaultSurfaceFlags()); - ASSERT_TRUE(decoder != nullptr); - task = new AnonymousDecodingTask(WrapNotNull(decoder)); - - if (aBMPWithinICO == BMPWithinICO::YES) { - static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO(); - } - - // Run the full decoder synchronously. - task->Run(); - - EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError()); - Progress fullProgress = decoder->TakeProgress(); - - // If the metadata decoder set a progress bit, the full decoder should also - // have set the same bit. - EXPECT_EQ(fullProgress, metadataProgress | fullProgress); - - // The full decoder and the metadata decoder should agree on the image's size. - IntSize fullSize = decoder->Size(); - EXPECT_EQ(metadataSize.width, fullSize.width); - EXPECT_EQ(metadataSize.height, fullSize.height); - - // We should not discover transparency during the full decode that we didn't - // discover during the metadata decode, unless the image is animated. - EXPECT_TRUE(!(fullProgress & FLAG_HAS_TRANSPARENCY) || - (metadataProgress & FLAG_HAS_TRANSPARENCY) || - (fullProgress & FLAG_IS_ANIMATED)); -} - -class ImageDecoderMetadata : public ::testing::Test -{ -protected: - AutoInitializeImageLib mInit; -}; - -TEST_F(ImageDecoderMetadata, PNG) { CheckMetadata(GreenPNGTestCase()); } -TEST_F(ImageDecoderMetadata, TransparentPNG) { CheckMetadata(TransparentPNGTestCase()); } -TEST_F(ImageDecoderMetadata, GIF) { CheckMetadata(GreenGIFTestCase()); } -TEST_F(ImageDecoderMetadata, TransparentGIF) { CheckMetadata(TransparentGIFTestCase()); } -TEST_F(ImageDecoderMetadata, JPG) { CheckMetadata(GreenJPGTestCase()); } -TEST_F(ImageDecoderMetadata, BMP) { CheckMetadata(GreenBMPTestCase()); } -TEST_F(ImageDecoderMetadata, ICO) { CheckMetadata(GreenICOTestCase()); } -TEST_F(ImageDecoderMetadata, Icon) { CheckMetadata(GreenIconTestCase()); } - -TEST_F(ImageDecoderMetadata, AnimatedGIF) -{ - CheckMetadata(GreenFirstFrameAnimatedGIFTestCase()); -} - -TEST_F(ImageDecoderMetadata, AnimatedPNG) -{ - CheckMetadata(GreenFirstFrameAnimatedPNGTestCase()); -} - -TEST_F(ImageDecoderMetadata, FirstFramePaddingGIF) -{ - CheckMetadata(FirstFramePaddingGIFTestCase()); -} - -TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPNotWithinICO) -{ - CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_DEFAULT_FLAGS), - BMPWithinICO::NO); -} - -TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPWithinICO) -{ - CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_IS_TRANSPARENT), - BMPWithinICO::YES); -} - -TEST_F(ImageDecoderMetadata, RLE4BMP) { CheckMetadata(RLE4BMPTestCase()); } -TEST_F(ImageDecoderMetadata, RLE8BMP) { CheckMetadata(RLE8BMPTestCase()); } - -TEST_F(ImageDecoderMetadata, Corrupt) { CheckMetadata(CorruptTestCase()); } - -TEST_F(ImageDecoderMetadata, NoFrameDelayGIF) -{ - CheckMetadata(NoFrameDelayGIFTestCase()); -} - -TEST_F(ImageDecoderMetadata, NoFrameDelayGIFFullDecode) -{ - ImageTestCase testCase = NoFrameDelayGIFTestCase(); - - // The previous test (NoFrameDelayGIF) verifies that we *don't* detect that - // this test case is animated, because it has a zero frame delay for the first - // frame. This test verifies that when we do a full decode, we detect the - // animation at that point and successfully decode all the frames. - - // Create an image. - RefPtr<Image> image = - ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType)); - ASSERT_TRUE(!image->HasError()); - - nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath); - ASSERT_TRUE(inputStream != nullptr); - - // Figure out how much data we have. - uint64_t length; - nsresult rv = inputStream->Available(&length); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Write the data into the image. - rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0, - static_cast<uint32_t>(length)); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - // Let the image know we've sent all the data. - rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - RefPtr<ProgressTracker> tracker = image->GetProgressTracker(); - tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE); - - // Use GetFrame() to force a sync decode of the image. - RefPtr<SourceSurface> surface = - image->GetFrame(imgIContainer::FRAME_CURRENT, - imgIContainer::FLAG_SYNC_DECODE); - - // Ensure that the image's metadata meets our expectations. - IntSize imageSize(0, 0); - rv = image->GetWidth(&imageSize.width); - EXPECT_TRUE(NS_SUCCEEDED(rv)); - rv = image->GetHeight(&imageSize.height); - EXPECT_TRUE(NS_SUCCEEDED(rv)); - - EXPECT_EQ(testCase.mSize.width, imageSize.width); - EXPECT_EQ(testCase.mSize.height, imageSize.height); - - Progress imageProgress = tracker->GetProgress(); - - EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false); - EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true); - - // Ensure that we decoded both frames of the image. - LookupResult result = - SurfaceCache::Lookup(ImageKey(image.get()), - RasterSurfaceKey(imageSize, - DefaultSurfaceFlags(), - PlaybackType::eAnimated)); - ASSERT_EQ(MatchType::EXACT, result.Type()); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0))); - EXPECT_TRUE(bool(result.Surface())); - - EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(1))); - EXPECT_TRUE(bool(result.Surface())); -} diff --git a/image/test/gtest/TestRemoveFrameRectFilter.cpp b/image/test/gtest/TestRemoveFrameRectFilter.cpp deleted file mode 100644 index ad1f944fc4..0000000000 --- a/image/test/gtest/TestRemoveFrameRectFilter.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* -*- 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, IntSize(100, 100), - IntRect(0, 0, 50, 50), - SurfaceFormat::B8G8R8A8, 8, - false }); -} diff --git a/image/test/gtest/TestSourceBuffer.cpp b/image/test/gtest/TestSourceBuffer.cpp deleted file mode 100644 index 05a88093f5..0000000000 --- a/image/test/gtest/TestSourceBuffer.cpp +++ /dev/null @@ -1,810 +0,0 @@ -/* 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 <algorithm> -#include <cstdint> - -#include "mozilla/Move.h" -#include "SourceBuffer.h" -#include "SurfaceCache.h" - -using namespace mozilla; -using namespace mozilla::image; - -using std::min; - -void -ExpectChunkAndByteCount(const SourceBufferIterator& aIterator, - uint32_t aChunks, - size_t aBytes) -{ - EXPECT_EQ(aChunks, aIterator.ChunkCount()); - EXPECT_EQ(aBytes, aIterator.ByteCount()); -} - -void -ExpectRemainingBytes(const SourceBufferIterator& aIterator, size_t aBytes) -{ - EXPECT_TRUE(aIterator.RemainingBytesIsNoMoreThan(aBytes)); - EXPECT_TRUE(aIterator.RemainingBytesIsNoMoreThan(aBytes + 1)); - - if (aBytes > 0) { - EXPECT_FALSE(aIterator.RemainingBytesIsNoMoreThan(0)); - EXPECT_FALSE(aIterator.RemainingBytesIsNoMoreThan(aBytes - 1)); - } -} - -char -GenerateByte(size_t aIndex) -{ - uint8_t byte = aIndex % 256; - return *reinterpret_cast<char*>(&byte); -} - -void -GenerateData(char* aOutput, size_t aOffset, size_t aLength) -{ - for (size_t i = 0; i < aLength; ++i) { - aOutput[i] = GenerateByte(aOffset + i); - } -} - -void -GenerateData(char* aOutput, size_t aLength) -{ - GenerateData(aOutput, 0, aLength); -} - -void -CheckData(const char* aData, size_t aOffset, size_t aLength) -{ - for (size_t i = 0; i < aLength; ++i) { - ASSERT_EQ(GenerateByte(aOffset + i), aData[i]); - } -} - -enum class AdvanceMode -{ - eAdvanceAsMuchAsPossible, - eAdvanceByLengthExactly -}; - -class ImageSourceBuffer : public ::testing::Test -{ -public: - ImageSourceBuffer() - : mSourceBuffer(new SourceBuffer) - , mExpectNoResume(new ExpectNoResume) - , mCountResumes(new CountResumes) - { - GenerateData(mData, sizeof(mData)); - EXPECT_FALSE(mSourceBuffer->IsComplete()); - } - -protected: - void CheckedAppendToBuffer(const char* aData, size_t aLength) - { - EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->Append(aData, aLength))); - } - - void CheckedAppendToBufferLastByteForLength(size_t aLength) - { - const char lastByte = GenerateByte(aLength); - CheckedAppendToBuffer(&lastByte, 1); - } - - void CheckedAppendToBufferInChunks(size_t aChunkLength, size_t aTotalLength) - { - char* data = new char[aChunkLength]; - - size_t bytesWritten = 0; - while (bytesWritten < aTotalLength) { - GenerateData(data, bytesWritten, aChunkLength); - size_t toWrite = min(aChunkLength, aTotalLength - bytesWritten); - CheckedAppendToBuffer(data, toWrite); - bytesWritten += toWrite; - } - - delete[] data; - } - - void CheckedCompleteBuffer(nsresult aCompletionStatus = NS_OK) - { - mSourceBuffer->Complete(aCompletionStatus); - EXPECT_TRUE(mSourceBuffer->IsComplete()); - } - - void CheckedCompleteBuffer(SourceBufferIterator& aIterator, - size_t aLength, - nsresult aCompletionStatus = NS_OK) - { - CheckedCompleteBuffer(aCompletionStatus); - ExpectRemainingBytes(aIterator, aLength); - } - - void CheckedAdvanceIteratorStateOnly(SourceBufferIterator& aIterator, - size_t aLength, - uint32_t aChunks, - size_t aTotalLength, - AdvanceMode aAdvanceMode - = AdvanceMode::eAdvanceAsMuchAsPossible) - { - const size_t advanceBy = aAdvanceMode == AdvanceMode::eAdvanceAsMuchAsPossible - ? SIZE_MAX - : aLength; - - auto state = aIterator.AdvanceOrScheduleResume(advanceBy, mExpectNoResume); - ASSERT_EQ(SourceBufferIterator::READY, state); - EXPECT_TRUE(aIterator.Data()); - EXPECT_EQ(aLength, aIterator.Length()); - - ExpectChunkAndByteCount(aIterator, aChunks, aTotalLength); - } - - void CheckedAdvanceIteratorStateOnly(SourceBufferIterator& aIterator, - size_t aLength) - { - CheckedAdvanceIteratorStateOnly(aIterator, aLength, 1, aLength); - } - - void CheckedAdvanceIterator(SourceBufferIterator& aIterator, - size_t aLength, - uint32_t aChunks, - size_t aTotalLength, - AdvanceMode aAdvanceMode - = AdvanceMode::eAdvanceAsMuchAsPossible) - { - // Check that the iterator is in the expected state. - CheckedAdvanceIteratorStateOnly(aIterator, aLength, aChunks, - aTotalLength, aAdvanceMode); - - // Check that we read the expected data. To do this, we need to compute our - // offset in the SourceBuffer, but fortunately that's pretty easy: it's the - // total number of bytes the iterator has advanced through, minus the length - // of the current chunk. - const size_t offset = aIterator.ByteCount() - aIterator.Length(); - CheckData(aIterator.Data(), offset, aIterator.Length()); - } - - void CheckedAdvanceIterator(SourceBufferIterator& aIterator, size_t aLength) - { - CheckedAdvanceIterator(aIterator, aLength, 1, aLength); - } - - void CheckIteratorMustWait(SourceBufferIterator& aIterator, - IResumable* aOnResume) - { - auto state = aIterator.AdvanceOrScheduleResume(1, aOnResume); - EXPECT_EQ(SourceBufferIterator::WAITING, state); - } - - void CheckIteratorIsComplete(SourceBufferIterator& aIterator, - uint32_t aChunks, - size_t aTotalLength, - nsresult aCompletionStatus = NS_OK) - { - ASSERT_TRUE(mSourceBuffer->IsComplete()); - auto state = aIterator.AdvanceOrScheduleResume(1, mExpectNoResume); - ASSERT_EQ(SourceBufferIterator::COMPLETE, state); - EXPECT_EQ(aCompletionStatus, aIterator.CompletionStatus()); - ExpectRemainingBytes(aIterator, 0); - ExpectChunkAndByteCount(aIterator, aChunks, aTotalLength); - } - - void CheckIteratorIsComplete(SourceBufferIterator& aIterator, - size_t aTotalLength) - { - CheckIteratorIsComplete(aIterator, 1, aTotalLength); - } - - AutoInitializeImageLib mInit; - char mData[9]; - RefPtr<SourceBuffer> mSourceBuffer; - RefPtr<ExpectNoResume> mExpectNoResume; - RefPtr<CountResumes> mCountResumes; -}; - -TEST_F(ImageSourceBuffer, InitialState) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // RemainingBytesIsNoMoreThan() should always return false in the initial - // state, since we can't know the answer until Complete() has been called. - EXPECT_FALSE(iterator.RemainingBytesIsNoMoreThan(0)); - EXPECT_FALSE(iterator.RemainingBytesIsNoMoreThan(SIZE_MAX)); - - // We haven't advanced our iterator at all, so its counters should be zero. - ExpectChunkAndByteCount(iterator, 0, 0); - - // Attempt to advance; we should fail, and end up in the WAITING state. We - // expect no resumes because we don't actually append anything to the - // SourceBuffer in this test. - CheckIteratorMustWait(iterator, mExpectNoResume); -} - -TEST_F(ImageSourceBuffer, ZeroLengthBufferAlwaysFails) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Complete the buffer without writing to it, providing a successful - // completion status. - CheckedCompleteBuffer(iterator, 0); - - // Completing a buffer without writing to it results in an automatic failure; - // make sure that the actual completion status we get from the iterator - // reflects this. - CheckIteratorIsComplete(iterator, 0, 0, NS_ERROR_FAILURE); -} - -TEST_F(ImageSourceBuffer, CompleteSuccess) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write a single byte to the buffer and complete the buffer. (We have to - // write at least one byte because completing a zero length buffer always - // fails; see the ZeroLengthBufferAlwaysFails test.) - CheckedAppendToBuffer(mData, 1); - CheckedCompleteBuffer(iterator, 1); - - // We should be able to advance once (to read the single byte) and then should - // reach the COMPLETE state with a successful status. - CheckedAdvanceIterator(iterator, 1); - CheckIteratorIsComplete(iterator, 1); -} - -TEST_F(ImageSourceBuffer, CompleteFailure) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write a single byte to the buffer and complete the buffer. (We have to - // write at least one byte because completing a zero length buffer always - // fails; see the ZeroLengthBufferAlwaysFails test.) - CheckedAppendToBuffer(mData, 1); - CheckedCompleteBuffer(iterator, 1, NS_ERROR_FAILURE); - - // Advance the iterator. Because a failing status is propagated to the - // iterator as soon as it advances, we won't be able to read the single byte - // that we wrote above; we go directly into the COMPLETE state. - CheckIteratorIsComplete(iterator, 0, 0, NS_ERROR_FAILURE); -} - -TEST_F(ImageSourceBuffer, Append) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write test data to the buffer. - EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->ExpectLength(sizeof(mData)))); - CheckedAppendToBuffer(mData, sizeof(mData)); - CheckedCompleteBuffer(iterator, sizeof(mData)); - - // Verify that we can read it back via the iterator, and that the final state - // is what we expect. - CheckedAdvanceIterator(iterator, sizeof(mData)); - CheckIteratorIsComplete(iterator, sizeof(mData)); -} - -TEST_F(ImageSourceBuffer, HugeAppendFails) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // We should fail to append anything bigger than what the SurfaceCache can - // hold, so use the SurfaceCache's maximum capacity to calculate what a - // "massive amount of data" (see below) consists of on this platform. - ASSERT_LT(SurfaceCache::MaximumCapacity(), SIZE_MAX); - const size_t hugeSize = SurfaceCache::MaximumCapacity() + 1; - - // Attempt to write a massive amount of data and verify that it fails. (We'd - // get a buffer overrun during the test if it succeeds, but if it succeeds - // that's the least of our problems.) - EXPECT_TRUE(NS_FAILED(mSourceBuffer->Append(mData, hugeSize))); - EXPECT_TRUE(mSourceBuffer->IsComplete()); - CheckIteratorIsComplete(iterator, 0, 0, NS_ERROR_OUT_OF_MEMORY); -} - -TEST_F(ImageSourceBuffer, AppendFromInputStream) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Construct an input stream with some arbitrary data. (We use test data from - // one of the decoder tests.) - nsCOMPtr<nsIInputStream> inputStream = LoadFile(GreenPNGTestCase().mPath); - ASSERT_TRUE(inputStream != nullptr); - - // Figure out how much data we have. - uint64_t length; - ASSERT_TRUE(NS_SUCCEEDED(inputStream->Available(&length))); - - // Write test data to the buffer. - EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->AppendFromInputStream(inputStream, - length))); - CheckedCompleteBuffer(iterator, length); - - // Verify that the iterator sees the appropriate amount of data. - CheckedAdvanceIteratorStateOnly(iterator, length); - CheckIteratorIsComplete(iterator, length); -} - -TEST_F(ImageSourceBuffer, AppendAfterComplete) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write test data to the buffer. - EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->ExpectLength(sizeof(mData)))); - CheckedAppendToBuffer(mData, sizeof(mData)); - CheckedCompleteBuffer(iterator, sizeof(mData)); - - // Verify that we can read it back via the iterator, and that the final state - // is what we expect. - CheckedAdvanceIterator(iterator, sizeof(mData)); - CheckIteratorIsComplete(iterator, sizeof(mData)); - - // Write more data to the completed buffer. - EXPECT_TRUE(NS_FAILED(mSourceBuffer->Append(mData, sizeof(mData)))); - - // Try to read with a new iterator and verify that the new data got ignored. - SourceBufferIterator iterator2 = mSourceBuffer->Iterator(); - CheckedAdvanceIterator(iterator2, sizeof(mData)); - CheckIteratorIsComplete(iterator2, sizeof(mData)); -} - -TEST_F(ImageSourceBuffer, MinChunkCapacity) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write test data to the buffer using many small appends. Since - // ExpectLength() isn't being called, we should be able to write up to - // SourceBuffer::MIN_CHUNK_CAPACITY bytes without a second chunk being - // allocated. - CheckedAppendToBufferInChunks(10, SourceBuffer::MIN_CHUNK_CAPACITY); - - // Verify that the iterator sees the appropriate amount of data. - CheckedAdvanceIterator(iterator, SourceBuffer::MIN_CHUNK_CAPACITY); - - // Write one more byte; we expect to see that it triggers an allocation. - CheckedAppendToBufferLastByteForLength(SourceBuffer::MIN_CHUNK_CAPACITY); - CheckedCompleteBuffer(iterator, 1); - - // Verify that the iterator sees the new byte and a new chunk has been - // allocated. - CheckedAdvanceIterator(iterator, 1, 2, SourceBuffer::MIN_CHUNK_CAPACITY + 1); - CheckIteratorIsComplete(iterator, 2, SourceBuffer::MIN_CHUNK_CAPACITY + 1); -} - -TEST_F(ImageSourceBuffer, ExpectLengthDoesNotShrinkBelowMinCapacity) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the buffer, - // but call ExpectLength() first to make SourceBuffer expect only a single - // byte. We expect this to still result in only one chunk, because - // regardless of ExpectLength() we won't allocate a chunk smaller than - // MIN_CHUNK_CAPACITY bytes. - EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->ExpectLength(1))); - CheckedAppendToBufferInChunks(10, SourceBuffer::MIN_CHUNK_CAPACITY); - CheckedCompleteBuffer(iterator, SourceBuffer::MIN_CHUNK_CAPACITY); - - // Verify that the iterator sees a single chunk. - CheckedAdvanceIterator(iterator, SourceBuffer::MIN_CHUNK_CAPACITY); - CheckIteratorIsComplete(iterator, 1, SourceBuffer::MIN_CHUNK_CAPACITY); -} - -TEST_F(ImageSourceBuffer, ExpectLengthGrowsAboveMinCapacity) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write two times SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the - // buffer, calling ExpectLength() with the correct length first. We expect - // this to result in only one chunk, because ExpectLength() allows us to - // allocate a larger first chunk than MIN_CHUNK_CAPACITY bytes. - const size_t length = 2 * SourceBuffer::MIN_CHUNK_CAPACITY; - EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->ExpectLength(length))); - CheckedAppendToBufferInChunks(10, length); - - // Verify that the iterator sees a single chunk. - CheckedAdvanceIterator(iterator, length); - - // Write one more byte; we expect to see that it triggers an allocation. - CheckedAppendToBufferLastByteForLength(length); - CheckedCompleteBuffer(iterator, 1); - - // Verify that the iterator sees the new byte and a new chunk has been - // allocated. - CheckedAdvanceIterator(iterator, 1, 2, length + 1); - CheckIteratorIsComplete(iterator, 2, length + 1); -} - -TEST_F(ImageSourceBuffer, HugeExpectLengthFails) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // ExpectLength() should fail if the length is bigger than what the - // SurfaceCache can hold, so use the SurfaceCache's maximum capacity to - // calculate what a "massive amount of data" (see below) consists of on this - // platform. - ASSERT_LT(SurfaceCache::MaximumCapacity(), SIZE_MAX); - const size_t hugeSize = SurfaceCache::MaximumCapacity() + 1; - - // Attempt to write a massive amount of data and verify that it fails. (We'd - // get a buffer overrun during the test if it succeeds, but if it succeeds - // that's the least of our problems.) - EXPECT_TRUE(NS_FAILED(mSourceBuffer->ExpectLength(hugeSize))); - EXPECT_TRUE(mSourceBuffer->IsComplete()); - CheckIteratorIsComplete(iterator, 0, 0, NS_ERROR_OUT_OF_MEMORY); -} - -TEST_F(ImageSourceBuffer, LargeAppendsAllocateOnlyOneChunk) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write two times SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the - // buffer in a single Append() call. We expect this to result in only one - // chunk even though ExpectLength() wasn't called, because we should always - // allocate a new chunk large enough to store the data we have at hand. - constexpr size_t length = 2 * SourceBuffer::MIN_CHUNK_CAPACITY; - char data[length]; - GenerateData(data, sizeof(data)); - CheckedAppendToBuffer(data, length); - - // Verify that the iterator sees a single chunk. - CheckedAdvanceIterator(iterator, length); - - // Write one more byte; we expect to see that it triggers an allocation. - CheckedAppendToBufferLastByteForLength(length); - CheckedCompleteBuffer(iterator, 1); - - // Verify that the iterator sees the new byte and a new chunk has been - // allocated. - CheckedAdvanceIterator(iterator, 1, 2, length + 1); - CheckIteratorIsComplete(iterator, 2, length + 1); -} - -TEST_F(ImageSourceBuffer, LargeAppendsAllocateAtMostOneChunk) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Allocate some data we'll use below. - constexpr size_t firstWriteLength = SourceBuffer::MIN_CHUNK_CAPACITY / 2; - constexpr size_t secondWriteLength = 3 * SourceBuffer::MIN_CHUNK_CAPACITY; - constexpr size_t totalLength = firstWriteLength + secondWriteLength; - char data[totalLength]; - GenerateData(data, sizeof(data)); - - // Write half of SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the - // buffer in a single Append() call. This should fill half of the first chunk. - CheckedAppendToBuffer(data, firstWriteLength); - - // Write three times SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the - // buffer in a single Append() call. We expect this to result in the first of - // the first chunk being filled and a new chunk being allocated for the - // remainder. - CheckedAppendToBuffer(data + firstWriteLength, secondWriteLength); - - // Verify that the iterator sees a MIN_CHUNK_CAPACITY-length chunk. - CheckedAdvanceIterator(iterator, SourceBuffer::MIN_CHUNK_CAPACITY); - - // Verify that the iterator sees a second chunk of the length we expect. - const size_t expectedSecondChunkLength = - totalLength - SourceBuffer::MIN_CHUNK_CAPACITY; - CheckedAdvanceIterator(iterator, expectedSecondChunkLength, 2, totalLength); - - // Write one more byte; we expect to see that it triggers an allocation. - CheckedAppendToBufferLastByteForLength(totalLength); - CheckedCompleteBuffer(iterator, 1); - - // Verify that the iterator sees the new byte and a new chunk has been - // allocated. - CheckedAdvanceIterator(iterator, 1, 3, totalLength + 1); - CheckIteratorIsComplete(iterator, 3, totalLength + 1); -} - -TEST_F(ImageSourceBuffer, CompactionHappensWhenBufferIsComplete) -{ - constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY; - constexpr size_t totalLength = 2 * chunkLength; - - // Write enough data to create two chunks. - CheckedAppendToBufferInChunks(chunkLength, totalLength); - - { - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Verify that the iterator sees two chunks. - CheckedAdvanceIterator(iterator, chunkLength); - CheckedAdvanceIterator(iterator, chunkLength, 2, totalLength); - } - - // Complete the buffer, which should trigger compaction implicitly. - CheckedCompleteBuffer(); - - { - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Verify that compaction happened and there's now only one chunk. - CheckedAdvanceIterator(iterator, totalLength); - CheckIteratorIsComplete(iterator, 1, totalLength); - } -} - -TEST_F(ImageSourceBuffer, CompactionIsDelayedWhileIteratorsExist) -{ - constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY; - constexpr size_t totalLength = 2 * chunkLength; - - { - SourceBufferIterator outerIterator = mSourceBuffer->Iterator(); - - { - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Write enough data to create two chunks. - CheckedAppendToBufferInChunks(chunkLength, totalLength); - CheckedCompleteBuffer(iterator, totalLength); - - // Verify that the iterator sees two chunks. Since there are live - // iterators, compaction shouldn't have happened when we completed the - // buffer. - CheckedAdvanceIterator(iterator, chunkLength); - CheckedAdvanceIterator(iterator, chunkLength, 2, totalLength); - CheckIteratorIsComplete(iterator, 2, totalLength); - } - - // Now |iterator| has been destroyed, but |outerIterator| still exists, so - // we expect no compaction to have occurred at this point. - CheckedAdvanceIterator(outerIterator, chunkLength); - CheckedAdvanceIterator(outerIterator, chunkLength, 2, totalLength); - CheckIteratorIsComplete(outerIterator, 2, totalLength); - } - - // Now all iterators have been destroyed. Since the buffer was already - // complete, we expect compaction to happen implicitly here. - - { - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Verify that compaction happened and there's now only one chunk. - CheckedAdvanceIterator(iterator, totalLength); - CheckIteratorIsComplete(iterator, 1, totalLength); - } -} - -TEST_F(ImageSourceBuffer, SourceBufferIteratorsCanBeMoved) -{ - constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY; - constexpr size_t totalLength = 2 * chunkLength; - - // Write enough data to create two chunks. We create an iterator here to make - // sure that compaction doesn't happen during the test. - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - CheckedAppendToBufferInChunks(chunkLength, totalLength); - CheckedCompleteBuffer(iterator, totalLength); - - auto GetIterator = [&]{ - SourceBufferIterator lambdaIterator = mSourceBuffer->Iterator(); - CheckedAdvanceIterator(lambdaIterator, chunkLength); - return lambdaIterator; - }; - - // Move-construct |movedIterator| from the iterator returned from - // GetIterator() and check that its state is as we expect. - SourceBufferIterator movedIterator = Move(GetIterator()); - EXPECT_TRUE(movedIterator.Data()); - EXPECT_EQ(chunkLength, movedIterator.Length()); - ExpectChunkAndByteCount(movedIterator, 1, chunkLength); - - // Make sure that we can advance the iterator. - CheckedAdvanceIterator(movedIterator, chunkLength, 2, totalLength); - - // Make sure that the iterator handles completion properly. - CheckIteratorIsComplete(movedIterator, 2, totalLength); - - // Move-assign |movedIterator| from the iterator returned from - // GetIterator() and check that its state is as we expect. - movedIterator = Move(GetIterator()); - EXPECT_TRUE(movedIterator.Data()); - EXPECT_EQ(chunkLength, movedIterator.Length()); - ExpectChunkAndByteCount(movedIterator, 1, chunkLength); - - // Make sure that we can advance the iterator. - CheckedAdvanceIterator(movedIterator, chunkLength, 2, totalLength); - - // Make sure that the iterator handles completion properly. - CheckIteratorIsComplete(movedIterator, 2, totalLength); -} - -TEST_F(ImageSourceBuffer, SubchunkAdvance) -{ - constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY; - constexpr size_t totalLength = 2 * chunkLength; - - // Write enough data to create two chunks. We create our iterator here to make - // sure that compaction doesn't happen during the test. - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - CheckedAppendToBufferInChunks(chunkLength, totalLength); - CheckedCompleteBuffer(iterator, totalLength); - - // Advance through the first chunk. The chunk count should not increase. - // We check that by always passing 1 for the |aChunks| parameter of - // CheckedAdvanceIteratorStateOnly(). We have to call CheckData() manually - // because the offset calculation in CheckedAdvanceIterator() assumes that - // we're advancing a chunk at a time. - size_t offset = 0; - while (offset < chunkLength) { - CheckedAdvanceIteratorStateOnly(iterator, 1, 1, chunkLength, - AdvanceMode::eAdvanceByLengthExactly); - CheckData(iterator.Data(), offset++, iterator.Length()); - } - - // Read the first byte of the second chunk. This is the point at which we - // can't advance within the same chunk, so the chunk count should increase. We - // check that by passing 2 for the |aChunks| parameter of - // CheckedAdvanceIteratorStateOnly(). - CheckedAdvanceIteratorStateOnly(iterator, 1, 2, totalLength, - AdvanceMode::eAdvanceByLengthExactly); - CheckData(iterator.Data(), offset++, iterator.Length()); - - // Read the rest of the second chunk. The chunk count should not increase. - while (offset < totalLength) { - CheckedAdvanceIteratorStateOnly(iterator, 1, 2, totalLength, - AdvanceMode::eAdvanceByLengthExactly); - CheckData(iterator.Data(), offset++, iterator.Length()); - } - - // Make sure we reached the end. - CheckIteratorIsComplete(iterator, 2, totalLength); -} - -TEST_F(ImageSourceBuffer, SubchunkZeroByteAdvance) -{ - constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY; - constexpr size_t totalLength = 2 * chunkLength; - - // Write enough data to create two chunks. We create our iterator here to make - // sure that compaction doesn't happen during the test. - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - CheckedAppendToBufferInChunks(chunkLength, totalLength); - CheckedCompleteBuffer(iterator, totalLength); - - // Make an initial zero-length advance. Although a zero-length advance - // normally won't cause us to read a chunk from the SourceBuffer, we'll do so - // if the iterator is in the initial state to keep the invariant that - // SourceBufferIterator in the READY state always returns a non-null pointer - // from Data(). - CheckedAdvanceIteratorStateOnly(iterator, 0, 1, chunkLength, - AdvanceMode::eAdvanceByLengthExactly); - - // Advance through the first chunk. As in the |SubchunkAdvance| test, the - // chunk count should not increase. We do a zero-length advance after each - // normal advance to ensure that zero-length advances do not change the - // iterator's position or cause a new chunk to be read. - size_t offset = 0; - while (offset < chunkLength) { - CheckedAdvanceIteratorStateOnly(iterator, 1, 1, chunkLength, - AdvanceMode::eAdvanceByLengthExactly); - CheckData(iterator.Data(), offset++, iterator.Length()); - CheckedAdvanceIteratorStateOnly(iterator, 0, 1, chunkLength, - AdvanceMode::eAdvanceByLengthExactly); - } - - // Read the first byte of the second chunk. This is the point at which we - // can't advance within the same chunk, so the chunk count should increase. As - // before, we do a zero-length advance afterward. - CheckedAdvanceIteratorStateOnly(iterator, 1, 2, totalLength, - AdvanceMode::eAdvanceByLengthExactly); - CheckData(iterator.Data(), offset++, iterator.Length()); - CheckedAdvanceIteratorStateOnly(iterator, 0, 2, totalLength, - AdvanceMode::eAdvanceByLengthExactly); - - // Read the rest of the second chunk. The chunk count should not increase. As - // before, we do a zero-length advance after each normal advance. - while (offset < totalLength) { - CheckedAdvanceIteratorStateOnly(iterator, 1, 2, totalLength, - AdvanceMode::eAdvanceByLengthExactly); - CheckData(iterator.Data(), offset++, iterator.Length()); - CheckedAdvanceIteratorStateOnly(iterator, 0, 2, totalLength, - AdvanceMode::eAdvanceByLengthExactly); - } - - // Make sure we reached the end. - CheckIteratorIsComplete(iterator, 2, totalLength); -} - -TEST_F(ImageSourceBuffer, SubchunkZeroByteAdvanceWithNoData) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Check that advancing by zero bytes still makes us enter the WAITING state. - // This is because if we entered the READY state before reading any data at - // all, we'd break the invariant that SourceBufferIterator::Data() always - // returns a non-null pointer in the READY state. - auto state = iterator.AdvanceOrScheduleResume(0, mCountResumes); - EXPECT_EQ(SourceBufferIterator::WAITING, state); - - // Call Complete(). This should trigger a resume. - CheckedCompleteBuffer(); - EXPECT_EQ(1u, mCountResumes->Count()); -} - -TEST_F(ImageSourceBuffer, NullIResumable) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Check that we can't advance. - CheckIteratorMustWait(iterator, nullptr); - - // Append to the buffer, which would cause a resume if we had passed a - // non-null IResumable. - CheckedAppendToBuffer(mData, sizeof(mData)); - CheckedCompleteBuffer(iterator, sizeof(mData)); -} - -TEST_F(ImageSourceBuffer, AppendTriggersResume) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Check that we can't advance. - CheckIteratorMustWait(iterator, mCountResumes); - - // Call Append(). This should trigger a resume. - mSourceBuffer->Append(mData, sizeof(mData)); - EXPECT_EQ(1u, mCountResumes->Count()); -} - -TEST_F(ImageSourceBuffer, OnlyOneResumeTriggeredPerAppend) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Check that we can't advance. - CheckIteratorMustWait(iterator, mCountResumes); - - // Allocate some data we'll use below. - constexpr size_t firstWriteLength = SourceBuffer::MIN_CHUNK_CAPACITY / 2; - constexpr size_t secondWriteLength = 3 * SourceBuffer::MIN_CHUNK_CAPACITY; - constexpr size_t totalLength = firstWriteLength + secondWriteLength; - char data[totalLength]; - GenerateData(data, sizeof(data)); - - // Write half of SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the - // buffer in a single Append() call. This should fill half of the first chunk. - // This should trigger a resume. - CheckedAppendToBuffer(data, firstWriteLength); - EXPECT_EQ(1u, mCountResumes->Count()); - - // Advance past the new data and wait again. - CheckedAdvanceIterator(iterator, firstWriteLength); - CheckIteratorMustWait(iterator, mCountResumes); - - // Write three times SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the - // buffer in a single Append() call. We expect this to result in the first of - // the first chunk being filled and a new chunk being allocated for the - // remainder. Even though two chunks are getting written to here, only *one* - // resume should get triggered, for a total of two in this test. - CheckedAppendToBuffer(data + firstWriteLength, secondWriteLength); - EXPECT_EQ(2u, mCountResumes->Count()); -} - -TEST_F(ImageSourceBuffer, CompleteTriggersResume) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Check that we can't advance. - CheckIteratorMustWait(iterator, mCountResumes); - - // Call Complete(). This should trigger a resume. - CheckedCompleteBuffer(); - EXPECT_EQ(1u, mCountResumes->Count()); -} - -TEST_F(ImageSourceBuffer, ExpectLengthDoesNotTriggerResume) -{ - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - - // Check that we can't advance. - CheckIteratorMustWait(iterator, mExpectNoResume); - - // Call ExpectLength(). If this triggers a resume, |mExpectNoResume| will - // ensure that the test fails. - mSourceBuffer->ExpectLength(1000); -} diff --git a/image/test/gtest/TestStreamingLexer.cpp b/image/test/gtest/TestStreamingLexer.cpp deleted file mode 100644 index 590b10e818..0000000000 --- a/image/test/gtest/TestStreamingLexer.cpp +++ /dev/null @@ -1,973 +0,0 @@ -/* 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/Vector.h" -#include "StreamingLexer.h" - -using namespace mozilla; -using namespace mozilla::image; - -enum class TestState -{ - ONE, - TWO, - THREE, - UNBUFFERED, - TRUNCATED_SUCCESS, - TRUNCATED_FAILURE -}; - -void -CheckLexedData(const char* aData, - size_t aLength, - size_t aOffset, - size_t aExpectedLength) -{ - EXPECT_TRUE(aLength == aExpectedLength); - - for (size_t i = 0; i < aLength; ++i) { - EXPECT_EQ(aData[i], char(aOffset + i + 1)); - } -} - -LexerTransition<TestState> -DoLex(TestState aState, const char* aData, size_t aLength) -{ - switch (aState) { - case TestState::ONE: - CheckLexedData(aData, aLength, 0, 3); - return Transition::To(TestState::TWO, 3); - case TestState::TWO: - CheckLexedData(aData, aLength, 3, 3); - return Transition::To(TestState::THREE, 3); - case TestState::THREE: - CheckLexedData(aData, aLength, 6, 3); - return Transition::TerminateSuccess(); - case TestState::TRUNCATED_SUCCESS: - return Transition::TerminateSuccess(); - case TestState::TRUNCATED_FAILURE: - return Transition::TerminateFailure(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithUnbuffered(TestState aState, const char* aData, size_t aLength, - Vector<char>& aUnbufferedVector) -{ - switch (aState) { - case TestState::ONE: - CheckLexedData(aData, aLength, 0, 3); - return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3); - case TestState::TWO: - CheckLexedData(aUnbufferedVector.begin(), aUnbufferedVector.length(), 3, 3); - return Transition::To(TestState::THREE, 3); - case TestState::THREE: - CheckLexedData(aData, aLength, 6, 3); - return Transition::TerminateSuccess(); - case TestState::UNBUFFERED: - EXPECT_TRUE(aLength <= 3); - EXPECT_TRUE(aUnbufferedVector.append(aData, aLength)); - return Transition::ContinueUnbuffered(TestState::UNBUFFERED); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithUnbufferedTerminate(TestState aState, const char* aData, size_t aLength) -{ - switch (aState) { - case TestState::ONE: - CheckLexedData(aData, aLength, 0, 3); - return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3); - case TestState::UNBUFFERED: - return Transition::TerminateSuccess(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithYield(TestState aState, const char* aData, size_t aLength) -{ - switch (aState) { - case TestState::ONE: - CheckLexedData(aData, aLength, 0, 3); - return Transition::ToAfterYield(TestState::TWO); - case TestState::TWO: - CheckLexedData(aData, aLength, 0, 3); - return Transition::To(TestState::THREE, 6); - case TestState::THREE: - CheckLexedData(aData, aLength, 3, 6); - return Transition::TerminateSuccess(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithTerminateAfterYield(TestState aState, const char* aData, size_t aLength) -{ - switch (aState) { - case TestState::ONE: - CheckLexedData(aData, aLength, 0, 3); - return Transition::ToAfterYield(TestState::TWO); - case TestState::TWO: - return Transition::TerminateSuccess(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithZeroLengthStates(TestState aState, const char* aData, size_t aLength) -{ - switch (aState) { - case TestState::ONE: - EXPECT_TRUE(aLength == 0); - return Transition::To(TestState::TWO, 0); - case TestState::TWO: - EXPECT_TRUE(aLength == 0); - return Transition::To(TestState::THREE, 9); - case TestState::THREE: - CheckLexedData(aData, aLength, 0, 9); - return Transition::TerminateSuccess(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithZeroLengthStatesAtEnd(TestState aState, const char* aData, size_t aLength) -{ - switch (aState) { - case TestState::ONE: - CheckLexedData(aData, aLength, 0, 9); - return Transition::To(TestState::TWO, 0); - case TestState::TWO: - EXPECT_TRUE(aLength == 0); - return Transition::To(TestState::THREE, 0); - case TestState::THREE: - EXPECT_TRUE(aLength == 0); - return Transition::TerminateSuccess(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithZeroLengthYield(TestState aState, const char* aData, size_t aLength) -{ - switch (aState) { - case TestState::ONE: - EXPECT_EQ(0u, aLength); - return Transition::ToAfterYield(TestState::TWO); - case TestState::TWO: - EXPECT_EQ(0u, aLength); - return Transition::To(TestState::THREE, 9); - case TestState::THREE: - CheckLexedData(aData, aLength, 0, 9); - return Transition::TerminateSuccess(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithZeroLengthStatesUnbuffered(TestState aState, - const char* aData, - size_t aLength) -{ - switch (aState) { - case TestState::ONE: - EXPECT_TRUE(aLength == 0); - return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 0); - case TestState::TWO: - EXPECT_TRUE(aLength == 0); - return Transition::To(TestState::THREE, 9); - case TestState::THREE: - CheckLexedData(aData, aLength, 0, 9); - return Transition::TerminateSuccess(); - case TestState::UNBUFFERED: - ADD_FAILURE() << "Should not enter zero-length unbuffered state"; - return Transition::TerminateFailure(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -LexerTransition<TestState> -DoLexWithZeroLengthStatesAfterUnbuffered(TestState aState, - const char* aData, - size_t aLength) -{ - switch (aState) { - case TestState::ONE: - EXPECT_TRUE(aLength == 0); - return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 9); - case TestState::TWO: - EXPECT_TRUE(aLength == 0); - return Transition::To(TestState::THREE, 0); - case TestState::THREE: - EXPECT_TRUE(aLength == 0); - return Transition::TerminateSuccess(); - case TestState::UNBUFFERED: - CheckLexedData(aData, aLength, 0, 9); - return Transition::ContinueUnbuffered(TestState::UNBUFFERED); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } -} - -class ImageStreamingLexer : public ::testing::Test -{ -public: - // Note that mLexer is configured to enter TerminalState::FAILURE immediately - // if the input data is truncated. We don't expect that to happen in most - // tests, so we want to detect that issue. If a test needs a different - // behavior, we create a special StreamingLexer just for that test. - ImageStreamingLexer() - : mLexer(Transition::To(TestState::ONE, 3), Transition::TerminateFailure()) - , mSourceBuffer(new SourceBuffer) - , mIterator(mSourceBuffer->Iterator()) - , mExpectNoResume(new ExpectNoResume) - , mCountResumes(new CountResumes) - { } - -protected: - void CheckTruncatedState(StreamingLexer<TestState>& aLexer, - TerminalState aExpectedTerminalState, - nsresult aCompletionStatus = NS_OK) - { - for (unsigned i = 0; i < 9; ++i) { - if (i < 2) { - mSourceBuffer->Append(mData + i, 1); - } else if (i == 2) { - mSourceBuffer->Complete(aCompletionStatus); - } - - LexerResult result = aLexer.Lex(mIterator, mCountResumes, DoLex); - - if (i >= 2) { - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(aExpectedTerminalState, result.as<TerminalState>()); - } else { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - EXPECT_EQ(2u, mCountResumes->Count()); - } - - AutoInitializeImageLib mInit; - const char mData[9] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - StreamingLexer<TestState> mLexer; - RefPtr<SourceBuffer> mSourceBuffer; - SourceBufferIterator mIterator; - RefPtr<ExpectNoResume> mExpectNoResume; - RefPtr<CountResumes> mCountResumes; -}; - -TEST_F(ImageStreamingLexer, ZeroLengthData) -{ - // Test a zero-length input. - mSourceBuffer->Complete(NS_OK); - - LexerResult result = mLexer.Lex(mIterator, mExpectNoResume, DoLex); - - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, ZeroLengthDataUnbuffered) -{ - // Test a zero-length input. - mSourceBuffer->Complete(NS_OK); - - // Create a special StreamingLexer for this test because we want the first - // state to be unbuffered. - StreamingLexer<TestState> lexer(Transition::ToUnbuffered(TestState::ONE, - TestState::UNBUFFERED, - sizeof(mData)), - Transition::TerminateFailure()); - - LexerResult result = lexer.Lex(mIterator, mExpectNoResume, DoLex); - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, StartWithTerminal) -{ - // Create a special StreamingLexer for this test because we want the first - // state to be a terminal state. This doesn't really make sense, but we should - // handle it. - StreamingLexer<TestState> lexer(Transition::TerminateSuccess(), - Transition::TerminateFailure()); - LexerResult result = lexer.Lex(mIterator, mExpectNoResume, DoLex); - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, SingleChunk) -{ - // Test delivering all the data at once. - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - LexerResult result = mLexer.Lex(mIterator, mExpectNoResume, DoLex); - - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, SingleChunkWithUnbuffered) -{ - Vector<char> unbufferedVector; - - // Test delivering all the data at once. - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - LexerResult result = - mLexer.Lex(mIterator, mExpectNoResume, - [&](TestState aState, const char* aData, size_t aLength) { - return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector); - }); - - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, SingleChunkWithYield) -{ - // Test delivering all the data at once. - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - LexerResult result = mLexer.Lex(mIterator, mExpectNoResume, DoLexWithYield); - ASSERT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - - result = mLexer.Lex(mIterator, mExpectNoResume, DoLexWithYield); - ASSERT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, ChunkPerState) -{ - // Test delivering in perfectly-sized chunks, one per state. - for (unsigned i = 0; i < 3; ++i) { - mSourceBuffer->Append(mData + 3 * i, 3); - LexerResult result = mLexer.Lex(mIterator, mCountResumes, DoLex); - - if (i == 2) { - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - } else { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - EXPECT_EQ(2u, mCountResumes->Count()); - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, ChunkPerStateWithUnbuffered) -{ - Vector<char> unbufferedVector; - - // Test delivering in perfectly-sized chunks, one per state. - for (unsigned i = 0; i < 3; ++i) { - mSourceBuffer->Append(mData + 3 * i, 3); - LexerResult result = - mLexer.Lex(mIterator, mCountResumes, - [&](TestState aState, const char* aData, size_t aLength) { - return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector); - }); - - if (i == 2) { - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - } else { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - EXPECT_EQ(2u, mCountResumes->Count()); - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, ChunkPerStateWithYield) -{ - // Test delivering in perfectly-sized chunks, one per state. - mSourceBuffer->Append(mData, 3); - LexerResult result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield); - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - - result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield); - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - - mSourceBuffer->Append(mData + 3, 6); - result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield); - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - - EXPECT_EQ(1u, mCountResumes->Count()); - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, ChunkPerStateWithUnbufferedYield) -{ - size_t unbufferedCallCount = 0; - Vector<char> unbufferedVector; - auto lexerFunc = [&](TestState aState, const char* aData, size_t aLength) - -> LexerTransition<TestState> { - switch (aState) { - case TestState::ONE: - CheckLexedData(aData, aLength, 0, 3); - return Transition::ToUnbuffered(TestState::TWO, TestState::UNBUFFERED, 3); - case TestState::TWO: - CheckLexedData(unbufferedVector.begin(), unbufferedVector.length(), 3, 3); - return Transition::To(TestState::THREE, 3); - case TestState::THREE: - CheckLexedData(aData, aLength, 6, 3); - return Transition::TerminateSuccess(); - case TestState::UNBUFFERED: - switch (unbufferedCallCount) { - case 0: - CheckLexedData(aData, aLength, 3, 3); - EXPECT_TRUE(unbufferedVector.append(aData, 2)); - unbufferedCallCount++; - - // Continue after yield, telling StreamingLexer we consumed 2 bytes. - return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 2); - - case 1: - CheckLexedData(aData, aLength, 5, 1); - EXPECT_TRUE(unbufferedVector.append(aData, 1)); - unbufferedCallCount++; - - // Continue after yield, telling StreamingLexer we consumed 1 byte. - // We should end up in the TWO state. - return Transition::ContinueUnbuffered(TestState::UNBUFFERED); - } - ADD_FAILURE() << "Too many invocations of TestState::UNBUFFERED"; - return Transition::TerminateFailure(); - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } - }; - - // Test delivering in perfectly-sized chunks, one per state. - for (unsigned i = 0; i < 3; ++i) { - mSourceBuffer->Append(mData + 3 * i, 3); - LexerResult result = mLexer.Lex(mIterator, mCountResumes, lexerFunc); - - switch (i) { - case 0: - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - EXPECT_EQ(0u, unbufferedCallCount); - break; - - case 1: - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - EXPECT_EQ(1u, unbufferedCallCount); - - result = mLexer.Lex(mIterator, mCountResumes, lexerFunc); - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - EXPECT_EQ(2u, unbufferedCallCount); - break; - - case 2: - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - break; - } - } - - EXPECT_EQ(2u, mCountResumes->Count()); - mSourceBuffer->Complete(NS_OK); - - LexerResult result = mLexer.Lex(mIterator, mCountResumes, lexerFunc); - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, OneByteChunks) -{ - // Test delivering in one byte chunks. - for (unsigned i = 0; i < 9; ++i) { - mSourceBuffer->Append(mData + i, 1); - LexerResult result = mLexer.Lex(mIterator, mCountResumes, DoLex); - - if (i == 8) { - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - } else { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - EXPECT_EQ(8u, mCountResumes->Count()); - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, OneByteChunksWithUnbuffered) -{ - Vector<char> unbufferedVector; - - // Test delivering in one byte chunks. - for (unsigned i = 0; i < 9; ++i) { - mSourceBuffer->Append(mData + i, 1); - LexerResult result = - mLexer.Lex(mIterator, mCountResumes, - [&](TestState aState, const char* aData, size_t aLength) { - return DoLexWithUnbuffered(aState, aData, aLength, unbufferedVector); - }); - - if (i == 8) { - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - } else { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - EXPECT_EQ(8u, mCountResumes->Count()); - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, OneByteChunksWithYield) -{ - // Test delivering in one byte chunks. - for (unsigned i = 0; i < 9; ++i) { - mSourceBuffer->Append(mData + i, 1); - LexerResult result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield); - - switch (i) { - case 2: - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - - result = mLexer.Lex(mIterator, mCountResumes, DoLexWithYield); - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - break; - - case 8: - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - break; - - default: - EXPECT_TRUE(i < 9); - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - EXPECT_EQ(8u, mCountResumes->Count()); - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, ZeroLengthState) -{ - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - // Create a special StreamingLexer for this test because we want the first - // state to be zero length. - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 0), - Transition::TerminateFailure()); - - LexerResult result = - lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthStates); - - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, ZeroLengthStatesAtEnd) -{ - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - // Create a special StreamingLexer for this test because we want the first - // state to consume the full input. - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 9), - Transition::TerminateFailure()); - - LexerResult result = - lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthStatesAtEnd); - - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, ZeroLengthStateWithYield) -{ - // Create a special StreamingLexer for this test because we want the first - // state to be zero length. - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 0), - Transition::TerminateFailure()); - - mSourceBuffer->Append(mData, 3); - LexerResult result = - lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthYield); - ASSERT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - - result = lexer.Lex(mIterator, mCountResumes, DoLexWithZeroLengthYield); - ASSERT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - - mSourceBuffer->Append(mData + 3, sizeof(mData) - 3); - mSourceBuffer->Complete(NS_OK); - result = lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthYield); - ASSERT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - EXPECT_EQ(1u, mCountResumes->Count()); -} - -TEST_F(ImageStreamingLexer, ZeroLengthStateWithUnbuffered) -{ - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - // Create a special StreamingLexer for this test because we want the first - // state to be both zero length and unbuffered. - StreamingLexer<TestState> lexer(Transition::ToUnbuffered(TestState::ONE, - TestState::UNBUFFERED, - 0), - Transition::TerminateFailure()); - - LexerResult result = - lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthStatesUnbuffered); - - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, ZeroLengthStateAfterUnbuffered) -{ - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - // Create a special StreamingLexer for this test because we want the first - // state to be zero length. - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 0), - Transition::TerminateFailure()); - - LexerResult result = - lexer.Lex(mIterator, mExpectNoResume, DoLexWithZeroLengthStatesAfterUnbuffered); - - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, ZeroLengthStateWithUnbufferedYield) -{ - size_t unbufferedCallCount = 0; - auto lexerFunc = [&](TestState aState, const char* aData, size_t aLength) - -> LexerTransition<TestState> { - switch (aState) { - case TestState::ONE: - EXPECT_EQ(0u, aLength); - return Transition::TerminateSuccess(); - - case TestState::UNBUFFERED: - switch (unbufferedCallCount) { - case 0: - CheckLexedData(aData, aLength, 0, 3); - unbufferedCallCount++; - - // Continue after yield, telling StreamingLexer we consumed 0 bytes. - return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 0); - - case 1: - CheckLexedData(aData, aLength, 0, 3); - unbufferedCallCount++; - - // Continue after yield, telling StreamingLexer we consumed 2 bytes. - return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 2); - - case 2: - EXPECT_EQ(1u, aLength); - CheckLexedData(aData, aLength, 2, 1); - unbufferedCallCount++; - - // Continue after yield, telling StreamingLexer we consumed 1 bytes. - return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 1); - - case 3: - CheckLexedData(aData, aLength, 3, 6); - unbufferedCallCount++; - - // Continue after yield, telling StreamingLexer we consumed 6 bytes. - // We should transition to TestState::ONE when we return from the - // yield. - return Transition::ContinueUnbufferedAfterYield(TestState::UNBUFFERED, 6); - } - - ADD_FAILURE() << "Too many invocations of TestState::UNBUFFERED"; - return Transition::TerminateFailure(); - - default: - MOZ_CRASH("Unexpected or unhandled TestState"); - } - }; - - // Create a special StreamingLexer for this test because we want the first - // state to be unbuffered. - StreamingLexer<TestState> lexer(Transition::ToUnbuffered(TestState::ONE, - TestState::UNBUFFERED, - sizeof(mData)), - Transition::TerminateFailure()); - - mSourceBuffer->Append(mData, 3); - LexerResult result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc); - ASSERT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - EXPECT_EQ(1u, unbufferedCallCount); - - result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc); - ASSERT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - EXPECT_EQ(2u, unbufferedCallCount); - - result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc); - ASSERT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - EXPECT_EQ(3u, unbufferedCallCount); - - result = lexer.Lex(mIterator, mCountResumes, lexerFunc); - ASSERT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - EXPECT_EQ(3u, unbufferedCallCount); - - mSourceBuffer->Append(mData + 3, 6); - mSourceBuffer->Complete(NS_OK); - EXPECT_EQ(1u, mCountResumes->Count()); - result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc); - ASSERT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - EXPECT_EQ(4u, unbufferedCallCount); - - result = lexer.Lex(mIterator, mExpectNoResume, lexerFunc); - ASSERT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, TerminateSuccess) -{ - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - // Test that Terminate is "sticky". - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - LexerResult result = - mLexer.Lex(iterator, mExpectNoResume, - [&](TestState aState, const char* aData, size_t aLength) { - EXPECT_TRUE(aState == TestState::ONE); - return Transition::TerminateSuccess(); - }); - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - - SourceBufferIterator iterator2 = mSourceBuffer->Iterator(); - result = - mLexer.Lex(iterator2, mExpectNoResume, - [&](TestState aState, const char* aData, size_t aLength) { - EXPECT_TRUE(false); // Shouldn't get here. - return Transition::TerminateFailure(); - }); - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, TerminateFailure) -{ - mSourceBuffer->Append(mData, sizeof(mData)); - mSourceBuffer->Complete(NS_OK); - - // Test that Terminate is "sticky". - SourceBufferIterator iterator = mSourceBuffer->Iterator(); - LexerResult result = - mLexer.Lex(iterator, mExpectNoResume, - [&](TestState aState, const char* aData, size_t aLength) { - EXPECT_TRUE(aState == TestState::ONE); - return Transition::TerminateFailure(); - }); - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>()); - - SourceBufferIterator iterator2 = mSourceBuffer->Iterator(); - result = - mLexer.Lex(iterator2, mExpectNoResume, - [&](TestState aState, const char* aData, size_t aLength) { - EXPECT_TRUE(false); // Shouldn't get here. - return Transition::TerminateFailure(); - }); - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, TerminateUnbuffered) -{ - // Test that Terminate works during an unbuffered read. - for (unsigned i = 0; i < 9; ++i) { - mSourceBuffer->Append(mData + i, 1); - LexerResult result = - mLexer.Lex(mIterator, mCountResumes, DoLexWithUnbufferedTerminate); - - if (i > 2) { - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - } else { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - // We expect 3 resumes because TestState::ONE consumes 3 bytes and then - // transitions to TestState::UNBUFFERED, which calls TerminateSuccess() as - // soon as it receives a single byte. That's four bytes total, which are - // delivered one at a time, requiring 3 resumes. - EXPECT_EQ(3u, mCountResumes->Count()); - - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, TerminateAfterYield) -{ - // Test that Terminate works after yielding. - for (unsigned i = 0; i < 9; ++i) { - mSourceBuffer->Append(mData + i, 1); - LexerResult result = - mLexer.Lex(mIterator, mCountResumes, DoLexWithTerminateAfterYield); - - if (i > 2) { - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - } else if (i == 2) { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::OUTPUT_AVAILABLE, result.as<Yield>()); - } else { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - // We expect 2 resumes because TestState::ONE consumes 3 bytes and then - // yields. When the lexer resumes at TestState::TWO, which receives the same 3 - // bytes, TerminateSuccess() gets called immediately. That's three bytes - // total, which are delivered one at a time, requiring 2 resumes. - EXPECT_EQ(2u, mCountResumes->Count()); - - mSourceBuffer->Complete(NS_OK); -} - -TEST_F(ImageStreamingLexer, SourceBufferImmediateComplete) -{ - // Test calling SourceBuffer::Complete() without appending any data. This - // causes the SourceBuffer to automatically have a failing completion status, - // no matter what you pass, so we expect TerminalState::FAILURE below. - mSourceBuffer->Complete(NS_OK); - - LexerResult result = mLexer.Lex(mIterator, mExpectNoResume, DoLex); - - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::FAILURE, result.as<TerminalState>()); -} - -TEST_F(ImageStreamingLexer, SourceBufferTruncatedTerminalStateSuccess) -{ - // Test that using a terminal state (in this case TerminalState::SUCCESS) as a - // truncated state works. - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3), - Transition::TerminateSuccess()); - - CheckTruncatedState(lexer, TerminalState::SUCCESS); -} - -TEST_F(ImageStreamingLexer, SourceBufferTruncatedTerminalStateFailure) -{ - // Test that using a terminal state (in this case TerminalState::FAILURE) as a - // truncated state works. - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3), - Transition::TerminateFailure()); - - CheckTruncatedState(lexer, TerminalState::FAILURE); -} - -TEST_F(ImageStreamingLexer, SourceBufferTruncatedStateReturningSuccess) -{ - // Test that a truncated state that returns TerminalState::SUCCESS works. When - // |lexer| discovers that the data is truncated, it invokes the - // TRUNCATED_SUCCESS state, which returns TerminalState::SUCCESS. - // CheckTruncatedState() verifies that this happens. - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3), - Transition::To(TestState::TRUNCATED_SUCCESS, 0)); - - CheckTruncatedState(lexer, TerminalState::SUCCESS); -} - -TEST_F(ImageStreamingLexer, SourceBufferTruncatedStateReturningFailure) -{ - // Test that a truncated state that returns TerminalState::FAILURE works. When - // |lexer| discovers that the data is truncated, it invokes the - // TRUNCATED_FAILURE state, which returns TerminalState::FAILURE. - // CheckTruncatedState() verifies that this happens. - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3), - Transition::To(TestState::TRUNCATED_FAILURE, 0)); - - CheckTruncatedState(lexer, TerminalState::FAILURE); -} - -TEST_F(ImageStreamingLexer, SourceBufferTruncatedFailingCompleteStatus) -{ - // Test that calling SourceBuffer::Complete() with a failing status results in - // an immediate TerminalState::FAILURE result. (Note that |lexer|'s truncated - // state is TerminalState::SUCCESS, so if we ignore the failing status, the - // test will fail.) - StreamingLexer<TestState> lexer(Transition::To(TestState::ONE, 3), - Transition::TerminateSuccess()); - - CheckTruncatedState(lexer, TerminalState::FAILURE, NS_ERROR_FAILURE); -} - -TEST_F(ImageStreamingLexer, NoSourceBufferResumable) -{ - // Test delivering in one byte chunks with no IResumable. - for (unsigned i = 0; i < 9; ++i) { - mSourceBuffer->Append(mData + i, 1); - LexerResult result = mLexer.Lex(mIterator, nullptr, DoLex); - - if (i == 8) { - EXPECT_TRUE(result.is<TerminalState>()); - EXPECT_EQ(TerminalState::SUCCESS, result.as<TerminalState>()); - } else { - EXPECT_TRUE(result.is<Yield>()); - EXPECT_EQ(Yield::NEED_MORE_DATA, result.as<Yield>()); - } - } - - mSourceBuffer->Complete(NS_OK); -} diff --git a/image/test/gtest/TestSurfacePipeIntegration.cpp b/image/test/gtest/TestSurfacePipeIntegration.cpp deleted file mode 100644 index 27138a3ee6..0000000000 --- a/image/test/gtest/TestSurfacePipeIntegration.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "gtest/gtest.h" - -#include "mozilla/gfx/2D.h" -#include "Common.h" -#include "Decoder.h" -#include "DecoderFactory.h" -#include "SourceBuffer.h" -#include "SurfacePipe.h" - -using namespace mozilla; -using namespace mozilla::gfx; -using namespace mozilla::image; - -namespace mozilla { -namespace image { - -class TestSurfacePipeFactory -{ -public: - static SurfacePipe SimpleSurfacePipe() - { - SurfacePipe pipe; - return Move(pipe); - } - - template <typename T> - static SurfacePipe SurfacePipeFromPipeline(T&& aPipeline) - { - return SurfacePipe { Move(aPipeline) }; - } - -private: - TestSurfacePipeFactory() { } -}; - -} // namespace image -} // namespace mozilla - -void -CheckSurfacePipeMethodResults(SurfacePipe* aPipe, - Decoder* aDecoder, - const IntRect& aRect = IntRect(0, 0, 100, 100)) -{ - // Check that the pipeline ended up in the state we expect. Note that we're - // explicitly testing the SurfacePipe versions of these methods, so we don't - // want to use AssertCorrectPipelineFinalState() here. - EXPECT_TRUE(aPipe->IsSurfaceFinished()); - Maybe<SurfaceInvalidRect> invalidRect = aPipe->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isSome()); - EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect); - EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect); - - // Check the generated image. - CheckGeneratedImage(aDecoder, aRect); - - // Reset and clear the image before the next test. - aPipe->ResetToFirstRow(); - EXPECT_FALSE(aPipe->IsSurfaceFinished()); - invalidRect = aPipe->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - uint32_t count = 0; - auto result = aPipe->WritePixels<uint32_t>([&]() { - ++count; - return AsVariant(BGRAColor::Transparent().AsPixel()); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u * 100u, count); - - EXPECT_TRUE(aPipe->IsSurfaceFinished()); - invalidRect = aPipe->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isSome()); - EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect); - EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect); - - aPipe->ResetToFirstRow(); - EXPECT_FALSE(aPipe->IsSurfaceFinished()); - invalidRect = aPipe->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); -} - -void -CheckPalettedSurfacePipeMethodResults(SurfacePipe* aPipe, - Decoder* aDecoder, - const IntRect& aRect - = IntRect(0, 0, 100, 100)) -{ - // Check that the pipeline ended up in the state we expect. Note that we're - // explicitly testing the SurfacePipe versions of these methods, so we don't - // want to use AssertCorrectPipelineFinalState() here. - EXPECT_TRUE(aPipe->IsSurfaceFinished()); - Maybe<SurfaceInvalidRect> invalidRect = aPipe->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isSome()); - EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect); - EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect); - - // Check the generated image. - CheckGeneratedPalettedImage(aDecoder, aRect); - - // Reset and clear the image before the next test. - aPipe->ResetToFirstRow(); - EXPECT_FALSE(aPipe->IsSurfaceFinished()); - invalidRect = aPipe->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - uint32_t count = 0; - auto result = aPipe->WritePixels<uint8_t>([&]() { - ++count; - return AsVariant(uint8_t(0)); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u * 100u, count); - - EXPECT_TRUE(aPipe->IsSurfaceFinished()); - invalidRect = aPipe->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isSome()); - EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mInputSpaceRect); - EXPECT_EQ(IntRect(0, 0, 100, 100), invalidRect->mOutputSpaceRect); - - aPipe->ResetToFirstRow(); - EXPECT_FALSE(aPipe->IsSurfaceFinished()); - invalidRect = aPipe->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); -} - -class ImageSurfacePipeIntegration : public ::testing::Test -{ -protected: - AutoInitializeImageLib mInit; -}; - -TEST_F(ImageSurfacePipeIntegration, SurfacePipe) -{ - // Test that SurfacePipe objects can be initialized and move constructed. - SurfacePipe pipe = TestSurfacePipeFactory::SimpleSurfacePipe(); - - // Test that SurfacePipe objects can be move assigned. - pipe = TestSurfacePipeFactory::SimpleSurfacePipe(); - - // Test that SurfacePipe objects can be initialized with a pipeline. - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - auto sink = MakeUnique<SurfaceSink>(); - nsresult rv = - sink->Configure(SurfaceConfig { decoder, IntSize(100, 100), - SurfaceFormat::B8G8R8A8, false }); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink); - - // Test that WritePixels() gets passed through to the underlying pipeline. - { - uint32_t count = 0; - auto result = pipe.WritePixels<uint32_t>([&]() { - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u * 100u, count); - CheckSurfacePipeMethodResults(&pipe, decoder); - } - - // Create a buffer the same size as one row of the surface, containing all - // green pixels. We'll use this for the WriteBuffer() tests. - uint32_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = BGRAColor::Green().AsPixel(); - } - - // Test that WriteBuffer() gets passed through to the underlying pipeline. - { - uint32_t count = 0; - WriteState result = WriteState::NEED_MORE_DATA; - while (result == WriteState::NEED_MORE_DATA) { - result = pipe.WriteBuffer(buffer); - ++count; - } - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, count); - CheckSurfacePipeMethodResults(&pipe, decoder); - } - - // Test that the 3 argument version of WriteBuffer() gets passed through to - // the underlying pipeline. - { - uint32_t count = 0; - WriteState result = WriteState::NEED_MORE_DATA; - while (result == WriteState::NEED_MORE_DATA) { - result = pipe.WriteBuffer(buffer, 0, 100); - ++count; - } - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, count); - CheckSurfacePipeMethodResults(&pipe, decoder); - } - - // Test that WriteEmptyRow() gets passed through to the underlying pipeline. - { - uint32_t count = 0; - WriteState result = WriteState::NEED_MORE_DATA; - while (result == WriteState::NEED_MORE_DATA) { - result = pipe.WriteEmptyRow(); - ++count; - } - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, count); - CheckSurfacePipeMethodResults(&pipe, decoder, IntRect(0, 0, 0, 0)); - } - - // Mark the frame as finished so we don't get an assertion. - RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef(); - currentFrame->Finish(); -} - -TEST_F(ImageSurfacePipeIntegration, PalettedSurfacePipe) -{ - // Create a SurfacePipe containing a PalettedSurfaceSink. - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - auto sink = MakeUnique<PalettedSurfaceSink>(); - nsresult rv = - sink->Configure(PalettedSurfaceConfig { decoder, IntSize(100, 100), - IntRect(0, 0, 100, 100), - SurfaceFormat::B8G8R8A8, - 8, false }); - ASSERT_TRUE(NS_SUCCEEDED(rv)); - - SurfacePipe pipe = TestSurfacePipeFactory::SurfacePipeFromPipeline(sink); - - // Test that WritePixels() gets passed through to the underlying pipeline. - { - uint32_t count = 0; - auto result = pipe.WritePixels<uint8_t>([&]() { - ++count; - return AsVariant(uint8_t(255)); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u * 100u, count); - CheckPalettedSurfacePipeMethodResults(&pipe, decoder); - } - - // Create a buffer the same size as one row of the surface, containing all - // 255 pixels. We'll use this for the WriteBuffer() tests. - uint8_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = 255; - } - - // Test that WriteBuffer() gets passed through to the underlying pipeline. - { - uint32_t count = 0; - WriteState result = WriteState::NEED_MORE_DATA; - while (result == WriteState::NEED_MORE_DATA) { - result = pipe.WriteBuffer(buffer); - ++count; - } - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, count); - CheckPalettedSurfacePipeMethodResults(&pipe, decoder); - } - - // Test that the 3 argument version of WriteBuffer() gets passed through to - // the underlying pipeline. - { - uint32_t count = 0; - WriteState result = WriteState::NEED_MORE_DATA; - while (result == WriteState::NEED_MORE_DATA) { - result = pipe.WriteBuffer(buffer, 0, 100); - ++count; - } - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, count); - CheckPalettedSurfacePipeMethodResults(&pipe, decoder); - } - - // Test that WriteEmptyRow() gets passed through to the underlying pipeline. - { - uint32_t count = 0; - WriteState result = WriteState::NEED_MORE_DATA; - while (result == WriteState::NEED_MORE_DATA) { - result = pipe.WriteEmptyRow(); - ++count; - } - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, count); - CheckPalettedSurfacePipeMethodResults(&pipe, decoder, IntRect(0, 0, 0, 0)); - } - - // Mark the frame as finished so we don't get an assertion. - RawAccessFrameRef currentFrame = decoder->GetCurrentFrameRef(); - currentFrame->Finish(); -} - -TEST_F(ImageSurfacePipeIntegration, DeinterlaceDownscaleWritePixels) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { - CheckWritePixels(aDecoder, aFilter, - /* aOutputRect = */ Some(IntRect(0, 0, 25, 25))); - }; - - WithFilterPipeline(decoder, test, - DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true }, - DownscalingConfig { IntSize(100, 100), - SurfaceFormat::B8G8R8A8 }, - SurfaceConfig { decoder, IntSize(25, 25), - SurfaceFormat::B8G8R8A8, false }); -} - -TEST_F(ImageSurfacePipeIntegration, RemoveFrameRectBottomRightDownscaleWritePixels) -{ - // This test case uses a frame rect that extends beyond the borders of the - // image to the bottom and to the right. It looks roughly like this (with the - // box made of '#'s representing the frame rect): - // - // +------------+ - // + + - // + +------------+ - // + +############+ - // +------+############+ - // +############+ - // +------------+ - - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - // Note that aInputWriteRect is 100x50 because RemoveFrameRectFilter ignores - // trailing rows that don't show up in the output. (Leading rows unfortunately - // can't be ignored.) So the action of the pipeline is as follows: - // - // (1) RemoveFrameRectFilter reads a 100x50 region of the input. - // (aInputWriteRect captures this fact.) The remaining 50 rows are ignored - // because they extend off the bottom of the image due to the frame rect's - // (50, 50) offset. The 50 columns on the right also don't end up in the - // output, so ultimately only a 50x50 region in the output contains data - // from the input. The filter's output is not 50x50, though, but 100x100, - // because what RemoveFrameRectFilter does is introduce blank rows or - // columns as necessary to transform an image that needs a frame rect into - // an image that doesn't. - // - // (2) DownscalingFilter reads the output of RemoveFrameRectFilter (100x100) - // and downscales it to 20x20. - // - // (3) The surface owned by SurfaceSink logically has only a 10x10 region - // region in it that's non-blank; this is the downscaled version of the - // 50x50 region discussed in (1). (aOutputWriteRect captures this fact.) - // Some fuzz, as usual, is necessary when dealing with Lanczos downscaling. - - auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { - CheckWritePixels(aDecoder, aFilter, - /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)), - /* aInputRect = */ Some(IntRect(0, 0, 100, 100)), - /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 50)), - /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)), - /* aFuzz = */ 0x33); - }; - - WithFilterPipeline(decoder, test, - RemoveFrameRectConfig { IntRect(50, 50, 100, 100) }, - DownscalingConfig { IntSize(100, 100), - SurfaceFormat::B8G8R8A8 }, - SurfaceConfig { decoder, IntSize(20, 20), - SurfaceFormat::B8G8R8A8, false }); -} - -TEST_F(ImageSurfacePipeIntegration, RemoveFrameRectTopLeftDownscaleWritePixels) -{ - // This test case uses a frame rect that extends beyond the borders of the - // image to the top and to the left. It looks roughly like this (with the - // box made of '#'s representing the frame rect): - // - // +------------+ - // +############+ - // +############+------+ - // +############+ + - // +------------+ + - // + + - // +------------+ - - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { - CheckWritePixels(aDecoder, aFilter, - /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)), - /* aInputRect = */ Some(IntRect(0, 0, 100, 100)), - /* aInputWriteRect = */ Some(IntRect(0, 0, 100, 100)), - /* aOutputWriteRect = */ Some(IntRect(0, 0, 10, 10)), - /* aFuzz = */ 0x21); - }; - - WithFilterPipeline(decoder, test, - RemoveFrameRectConfig { IntRect(-50, -50, 100, 100) }, - DownscalingConfig { IntSize(100, 100), - SurfaceFormat::B8G8R8A8 }, - SurfaceConfig { decoder, IntSize(20, 20), - SurfaceFormat::B8G8R8A8, false }); -} - -TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectWritePixels) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - // Note that aInputRect is the full 100x100 size even though - // RemoveFrameRectFilter is part of this pipeline, because deinterlacing - // requires reading every row. - - auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { - CheckWritePixels(aDecoder, aFilter, - /* aOutputRect = */ Some(IntRect(0, 0, 100, 100)), - /* aInputRect = */ Some(IntRect(0, 0, 100, 100)), - /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)), - /* aOutputWriteRect = */ Some(IntRect(50, 50, 50, 50))); - }; - - WithFilterPipeline(decoder, test, - DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true }, - RemoveFrameRectConfig { IntRect(50, 50, 100, 100) }, - SurfaceConfig { decoder, IntSize(100, 100), - SurfaceFormat::B8G8R8A8, false }); -} - -TEST_F(ImageSurfacePipeIntegration, DeinterlaceRemoveFrameRectDownscaleWritePixels) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - auto test = [](Decoder* aDecoder, SurfaceFilter* aFilter) { - CheckWritePixels(aDecoder, aFilter, - /* aOutputRect = */ Some(IntRect(0, 0, 20, 20)), - /* aInputRect = */ Some(IntRect(0, 0, 100, 100)), - /* aInputWriteRect = */ Some(IntRect(50, 50, 100, 100)), - /* aOutputWriteRect = */ Some(IntRect(10, 10, 10, 10)), - /* aFuzz = */ 33); - }; - - WithFilterPipeline(decoder, test, - DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true }, - RemoveFrameRectConfig { IntRect(50, 50, 100, 100) }, - DownscalingConfig { IntSize(100, 100), - SurfaceFormat::B8G8R8A8 }, - SurfaceConfig { decoder, IntSize(20, 20), - SurfaceFormat::B8G8R8A8, false }); -} - -TEST_F(ImageSurfacePipeIntegration, ConfiguringPalettedRemoveFrameRectDownscaleFails) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - // This is an invalid pipeline for paletted images, so configuration should - // fail. - AssertConfiguringPipelineFails(decoder, - RemoveFrameRectConfig { IntRect(0, 0, 50, 50) }, - DownscalingConfig { IntSize(100, 100), - SurfaceFormat::B8G8R8A8 }, - PalettedSurfaceConfig { decoder, IntSize(100, 100), - IntRect(0, 0, 50, 50), - SurfaceFormat::B8G8R8A8, 8, - false }); -} - -TEST_F(ImageSurfacePipeIntegration, ConfiguringPalettedDeinterlaceDownscaleFails) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - // This is an invalid pipeline for paletted images, so configuration should - // fail. - AssertConfiguringPipelineFails(decoder, - DeinterlacingConfig<uint8_t> { /* mProgressiveDisplay = */ true}, - DownscalingConfig { IntSize(100, 100), - SurfaceFormat::B8G8R8A8 }, - PalettedSurfaceConfig { decoder, IntSize(100, 100), - IntRect(0, 0, 20, 20), - SurfaceFormat::B8G8R8A8, 8, - false }); -} - -TEST_F(ImageSurfacePipeIntegration, ConfiguringHugeDeinterlacingBufferFails) -{ - RefPtr<Decoder> decoder = CreateTrivialDecoder(); - ASSERT_TRUE(decoder != nullptr); - - // When DownscalingFilter is used, we may succeed in allocating an output - // surface for huge images, because we only need to store the scaled-down - // version of the image. However, regardless of downscaling, - // DeinterlacingFilter needs to allocate a buffer as large as the size of the - // input. This can cause OOMs on operating systems that allow overcommit. This - // test makes sure that we reject such allocations. - AssertConfiguringPipelineFails(decoder, - DeinterlacingConfig<uint32_t> { /* mProgressiveDisplay = */ true}, - DownscalingConfig { IntSize(60000, 60000), - SurfaceFormat::B8G8R8A8 }, - SurfaceConfig { decoder, IntSize(600, 600), - SurfaceFormat::B8G8R8A8, false }); -} diff --git a/image/test/gtest/TestSurfaceSink.cpp b/image/test/gtest/TestSurfaceSink.cpp deleted file mode 100644 index 3a1c74d12e..0000000000 --- a/image/test/gtest/TestSurfaceSink.cpp +++ /dev/null @@ -1,1491 +0,0 @@ -/* -*- 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, 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, IntSize(100, 100), - aFrameRect, SurfaceFormat::B8G8R8A8, - 8, false }); -} - -void -ResetForNextPass(SurfaceFilter* aSink) -{ - aSink->ResetToFirstRow(); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); -} - -template <typename WriteFunc, typename CheckFunc> void -DoCheckIterativeWrite(SurfaceFilter* aSink, - WriteFunc aWriteFunc, - CheckFunc aCheckFunc) -{ - // Write the buffer to successive rows until every row of the surface - // has been written. - uint32_t row = 0; - WriteState result = WriteState::NEED_MORE_DATA; - while (result == WriteState::NEED_MORE_DATA) { - result = aWriteFunc(row); - ++row; - } - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, row); - - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Check that the generated image is correct. - aCheckFunc(); -} - -template <typename WriteFunc> void -CheckIterativeWrite(Decoder* aDecoder, - SurfaceSink* aSink, - const IntRect& aOutputRect, - WriteFunc aWriteFunc) -{ - // Ignore the row passed to WriteFunc, since no callers use it. - auto writeFunc = [&](uint32_t) { - return aWriteFunc(); - }; - - DoCheckIterativeWrite(aSink, writeFunc, [&]{ - CheckGeneratedImage(aDecoder, aOutputRect); - }); -} - -template <typename WriteFunc> void -CheckPalettedIterativeWrite(Decoder* aDecoder, - PalettedSurfaceSink* aSink, - const IntRect& aOutputRect, - WriteFunc aWriteFunc) -{ - // Ignore the row passed to WriteFunc, since no callers use it. - auto writeFunc = [&](uint32_t) { - return aWriteFunc(); - }; - - DoCheckIterativeWrite(aSink, writeFunc, [&]{ - CheckGeneratedPalettedImage(aDecoder, aOutputRect); - }); -} - -TEST(ImageSurfaceSink, NullSurfaceSink) -{ - // Create the NullSurfaceSink. - 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()); - - uint32_t source = BGRAColor::Red().AsPixel(); - result = sink.WriteBuffer(&source); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_TRUE(sink.IsSurfaceFinished()); - invalidRect = sink.TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - result = sink.WriteBuffer(&source, 0, 1); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_TRUE(sink.IsSurfaceFinished()); - invalidRect = sink.TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - result = sink.WriteEmptyRow(); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_TRUE(sink.IsSurfaceFinished()); - invalidRect = sink.TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - result = sink.WriteUnsafeComputedRow<uint32_t>([&](uint32_t* aRow, - uint32_t aLength) { - gotCalled = true; - for (uint32_t col = 0; col < aLength; ++col, ++aRow) { - *aRow = BGRAColor::Red().AsPixel(); - } - }); - 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, SurfaceSinkInitialization) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - // Check initial state. - EXPECT_FALSE(aSink->IsSurfaceFinished()); - Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - // Check that the surface is zero-initialized. We verify this by calling - // CheckGeneratedImage() and telling it that we didn't write to the surface - // anyway (i.e., we wrote to the empty rect); it will then expect the entire - // surface to be transparent, which is what it should be if it was - // zero-initialied. - CheckGeneratedImage(aDecoder, IntRect(0, 0, 0, 0)); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWritePixels) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - CheckWritePixels(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); - - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Attempt to write more and make sure that nothing gets written. - count = 0; - result = aSink->WritePixels<uint32_t>([&]() { - count++; - return AsVariant(BGRAColor::Red().AsPixel()); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(0u, count); - EXPECT_TRUE(aSink->IsSurfaceFinished()); - - // Check that the generated image is correct. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Transparent())); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWritePixelsEarlyExit) -{ - auto checkEarlyExit = - [](Decoder* aDecoder, SurfaceSink* aSink, WriteState aState) { - // Write half a row of green pixels and then exit early with |aState|. If - // the lambda keeps getting called, we'll write red pixels, which will cause - // the test to fail. - uint32_t count = 0; - auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> { - if (count == 50) { - return AsVariant(aState); - } - return count++ < 50 ? AsVariant(BGRAColor::Green().AsPixel()) - : AsVariant(BGRAColor::Red().AsPixel()); - }); - - EXPECT_EQ(aState, result); - EXPECT_EQ(50u, count); - CheckGeneratedImage(aDecoder, IntRect(0, 0, 50, 1)); - - if (aState != WriteState::FINISHED) { - // We should still be able to write more at this point. - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - // Verify that we can resume writing. We'll finish up the same row. - count = 0; - result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> { - if (count == 50) { - return AsVariant(WriteState::NEED_MORE_DATA); - } - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(50u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, 1)); - - return; - } - - // We should've finished the surface at this point. - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Attempt to write more and make sure that nothing gets written. - count = 0; - result = aSink->WritePixels<uint32_t>([&]{ - count++; - return AsVariant(BGRAColor::Red().AsPixel()); - }); - - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(0u, count); - EXPECT_TRUE(aSink->IsSurfaceFinished()); - - // Check that the generated image is still correct. - CheckGeneratedImage(aDecoder, IntRect(0, 0, 50, 1)); - }; - - WithSurfaceSink<Orient::NORMAL>([&](Decoder* aDecoder, SurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::NEED_MORE_DATA); - }); - - WithSurfaceSink<Orient::NORMAL>([&](Decoder* aDecoder, SurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::FAILURE); - }); - - WithSurfaceSink<Orient::NORMAL>([&](Decoder* aDecoder, SurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::FINISHED); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWritePixelsToRow) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - // Write the first 99 rows of our 100x100 surface and verify that even - // though our lambda will yield pixels forever, only one row is written per - // call to WritePixelsToRow(). - for (int row = 0; row < 99; ++row) { - uint32_t count = 0; - WriteState result = aSink->WritePixelsToRow<uint32_t>([&]{ - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(100u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isSome()); - EXPECT_EQ(IntRect(0, row, 100, 1), invalidRect->mInputSpaceRect); - EXPECT_EQ(IntRect(0, row, 100, 1), invalidRect->mOutputSpaceRect); - - CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, row + 1)); - } - - // Write the final line, which should finish the surface. - uint32_t count = 0; - WriteState result = aSink->WritePixelsToRow<uint32_t>([&]{ - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, count); - - // Note that the final invalid rect we expect here is only the last row; - // that's because we called TakeInvalidRect() repeatedly in the loop above. - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 99, 100, 1), - IntRect(0, 99, 100, 1)); - - // Check that the generated image is correct. - CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, 100)); - - // Attempt to write more and make sure that nothing gets written. - count = 0; - result = aSink->WritePixelsToRow<uint32_t>([&]{ - count++; - return AsVariant(BGRAColor::Red().AsPixel()); - }); - - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(0u, count); - EXPECT_TRUE(aSink->IsSurfaceFinished()); - - // Check that the generated image is still correct. - CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, 100)); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWritePixelsToRowEarlyExit) -{ - auto checkEarlyExit = - [](Decoder* aDecoder, SurfaceSink* aSink, WriteState aState) { - // Write half a row of green pixels and then exit early with |aState|. If - // the lambda keeps getting called, we'll write red pixels, which will cause - // the test to fail. - uint32_t count = 0; - auto result = aSink->WritePixelsToRow<uint32_t>([&]() -> NextPixel<uint32_t> { - if (count == 50) { - return AsVariant(aState); - } - return count++ < 50 ? AsVariant(BGRAColor::Green().AsPixel()) - : AsVariant(BGRAColor::Red().AsPixel()); - }); - - EXPECT_EQ(aState, result); - EXPECT_EQ(50u, count); - CheckGeneratedImage(aDecoder, IntRect(0, 0, 50, 1)); - - if (aState != WriteState::FINISHED) { - // We should still be able to write more at this point. - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - // Verify that we can resume the same row and still stop at the end. - count = 0; - WriteState result = aSink->WritePixelsToRow<uint32_t>([&]{ - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(50u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - CheckGeneratedImage(aDecoder, IntRect(0, 0, 100, 1)); - - return; - } - - // We should've finished the surface at this point. - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Attempt to write more and make sure that nothing gets written. - count = 0; - result = aSink->WritePixelsToRow<uint32_t>([&]{ - count++; - return AsVariant(BGRAColor::Red().AsPixel()); - }); - - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(0u, count); - EXPECT_TRUE(aSink->IsSurfaceFinished()); - - // Check that the generated image is still correct. - CheckGeneratedImage(aDecoder, IntRect(0, 0, 50, 1)); - }; - - WithSurfaceSink<Orient::NORMAL>([&](Decoder* aDecoder, SurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::NEED_MORE_DATA); - }); - - WithSurfaceSink<Orient::NORMAL>([&](Decoder* aDecoder, SurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::FAILURE); - }); - - WithSurfaceSink<Orient::NORMAL>([&](Decoder* aDecoder, SurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::FINISHED); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWriteBuffer) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - // Create a green buffer the same size as one row of the surface (which is 100x100), - // containing 60 pixels of green in the middle and 20 transparent pixels on - // either side. - uint32_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = 20 <= i && i < 80 ? BGRAColor::Green().AsPixel() - : BGRAColor::Transparent().AsPixel(); - } - - // Write the buffer to every row of the surface and check that the generated - // image is correct. - CheckIterativeWrite(aDecoder, aSink, IntRect(20, 0, 60, 100), [&]{ - return aSink->WriteBuffer(buffer); - }); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWriteBufferPartialRow) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - // Create a buffer the same size as one row of the surface, containing all - // green pixels. - uint32_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = BGRAColor::Green().AsPixel(); - } - - // Write the buffer to the middle 60 pixels of every row of the surface and - // check that the generated image is correct. - CheckIterativeWrite(aDecoder, aSink, IntRect(20, 0, 60, 100), [&]{ - return aSink->WriteBuffer(buffer, 20, 60); - }); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWriteBufferPartialRowStartColOverflow) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - // Create a buffer the same size as one row of the surface, containing all - // green pixels. - uint32_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = BGRAColor::Green().AsPixel(); - } - - { - // Write the buffer to successive rows until every row of the surface - // has been written. We place the start column beyond the end of the row, - // which will prevent us from writing anything, so we check that the - // generated image is entirely transparent. - CheckIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0), [&]{ - return aSink->WriteBuffer(buffer, 100, 100); - }); - } - - ResetForNextPass(aSink); - - { - // Write the buffer to successive rows until every row of the surface - // has been written. We use column 50 as the start column, but we still - // write the buffer, which means we overflow the right edge of the surface - // by 50 pixels. We check that the left half of the generated image is - // transparent and the right half is green. - CheckIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{ - return aSink->WriteBuffer(buffer, 50, 100); - }); - } - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWriteBufferPartialRowBufferOverflow) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - // Create a buffer twice as large as a row of the surface. The first half - // (which is as large as a row of the image) will contain green pixels, - // while the second half will contain red pixels. - uint32_t buffer[200]; - for (int i = 0; i < 200; ++i) { - buffer[i] = i < 100 ? BGRAColor::Green().AsPixel() - : BGRAColor::Red().AsPixel(); - } - - { - // Write the buffer to successive rows until every row of the surface has - // been written. The buffer extends 100 pixels to the right of a row of - // the surface, but bounds checking will prevent us from overflowing the - // buffer. We check that the generated image is entirely green since the - // pixels on the right side of the buffer shouldn't have been written to - // the surface. - CheckIterativeWrite(aDecoder, aSink, IntRect(0, 0, 100, 100), [&]{ - return aSink->WriteBuffer(buffer, 0, 200); - }); - } - - ResetForNextPass(aSink); - - { - // Write from the buffer to the middle of each row of the surface. That - // means that the left side of each row should be transparent, since we - // didn't write anything there. A buffer overflow would cause us to write - // buffer contents into the left side of each row. We check that the - // generated image is transparent on the left side and green on the right. - CheckIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{ - return aSink->WriteBuffer(buffer, 50, 200); - }); - } - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWriteBufferFromNullSource) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - // Calling WriteBuffer() with a null pointer should fail without making any - // changes to the surface. - uint32_t* nullBuffer = nullptr; - WriteState result = aSink->WriteBuffer(nullBuffer); - - EXPECT_EQ(WriteState::FAILURE, result); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - // Check that nothing got written to the surface. - CheckGeneratedImage(aDecoder, IntRect(0, 0, 0, 0)); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWriteEmptyRow) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - { - // Write an empty row to each row of the surface. We check that the - // generated image is entirely transparent. - CheckIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0), [&]{ - return aSink->WriteEmptyRow(); - }); - } - - ResetForNextPass(aSink); - - { - // Write a partial row before we begin calling WriteEmptyRow(). We check - // that the generated image is entirely transparent, which is to be - // expected since WriteEmptyRow() overwrites the current row even if some - // data has already been written to it. - uint32_t count = 0; - auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> { - if (count == 50) { - return AsVariant(WriteState::NEED_MORE_DATA); - } - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(50u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - CheckIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0), [&]{ - return aSink->WriteEmptyRow(); - }); - } - - ResetForNextPass(aSink); - - { - // Create a buffer the same size as one row of the surface, containing all - // green pixels. - uint32_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = BGRAColor::Green().AsPixel(); - } - - // Write an empty row to the middle 60 rows of the surface. The first 20 - // and last 20 rows will be green. (We need to use DoCheckIterativeWrite() - // here because we need a custom function to check the output, since it - // can't be described by a simple rect.) - auto writeFunc = [&](uint32_t aRow) { - if (aRow < 20 || aRow >= 80) { - return aSink->WriteBuffer(buffer); - } else { - return aSink->WriteEmptyRow(); - } - }; - - auto checkFunc = [&]{ - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - - EXPECT_TRUE(RowsAreSolidColor(surface, 0, 20, BGRAColor::Green())); - EXPECT_TRUE(RowsAreSolidColor(surface, 20, 60, BGRAColor::Transparent())); - EXPECT_TRUE(RowsAreSolidColor(surface, 80, 20, BGRAColor::Green())); - }; - - DoCheckIterativeWrite(aSink, writeFunc, checkFunc); - } - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkWriteUnsafeComputedRow) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - // Create a green buffer the same size as one row of the surface. - uint32_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = BGRAColor::Green().AsPixel(); - } - - // Write the buffer to successive rows until every row of the surface - // has been written. We only write to the right half of each row, so we - // check that the left side of the generated image is transparent and the - // right side is green. - CheckIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{ - return aSink->WriteUnsafeComputedRow<uint32_t>([&](uint32_t* aRow, - uint32_t aLength) { - EXPECT_EQ(100u, aLength ); - memcpy(aRow + 50, buffer, 50 * sizeof(uint32_t)); - }); - }); - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkProgressivePasses) -{ - WithSurfaceSink<Orient::NORMAL>([](Decoder* aDecoder, SurfaceSink* aSink) { - { - // Fill the image with a first pass of red. - uint32_t count = 0; - auto result = aSink->WritePixels<uint32_t>([&]() { - ++count; - return AsVariant(BGRAColor::Red().AsPixel()); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u * 100u, count); - - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Check that the generated image is correct. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red())); - } - - { - ResetForNextPass(aSink); - - // Check that the generated image is still the first pass image. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red())); - } - - { - // Fill the image with a second pass of green. - uint32_t count = 0; - auto result = aSink->WritePixels<uint32_t>([&]() { - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u * 100u, count); - - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Check that the generated image is correct. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green())); - } - }); -} - -TEST(ImageSurfaceSink, SurfaceSinkInvalidRect) -{ - WithSurfaceSink<Orient::NORMAL>([](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->GetSourceSurface(); - 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->GetSourceSurface(); - EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red())); - } - - { - ResetForNextPass(aSink); - - // Check that the generated image is still the first pass image. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Red())); - } - - { - // Fill 25 rows of the image with green and make sure everything is OK. - uint32_t count = 0; - auto result = aSink->WritePixels<uint32_t>([&]() -> NextPixel<uint32_t> { - if (count == 25 * 100) { - return AsVariant(WriteState::NEED_MORE_DATA); - } - count++; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(25u * 100u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - // Assert that we have the right invalid rect, which should include the - // *bottom* (since we're flipping vertically) 25 rows of the image. - Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isSome()); - EXPECT_EQ(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->GetSourceSurface(); - EXPECT_TRUE(RowsAreSolidColor(surface, 0, 75, BGRAColor::Red())); - EXPECT_TRUE(RowsAreSolidColor(surface, 75, 25, BGRAColor::Green())); - } - - { - // Fill the rest of the image with a second pass of green. - uint32_t count = 0; - auto result = aSink->WritePixels<uint32_t>([&]() { - ++count; - return AsVariant(BGRAColor::Green().AsPixel()); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(75u * 100u, count); - - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 75), - IntRect(0, 0, 100, 75)); - - // Check that the generated image is correct. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface(); - EXPECT_TRUE(IsSolidColor(surface, BGRAColor::Green())); - } - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkInitialization) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Check initial state. - EXPECT_FALSE(aSink->IsSurfaceFinished()); - Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - // Check that the paletted image data is zero-initialized. - RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef(); - uint8_t* imageData = nullptr; - uint32_t imageLength = 0; - currentFrame->GetImageData(&imageData, &imageLength); - ASSERT_TRUE(imageData != nullptr); - ASSERT_EQ(100u * 100u, imageLength); - for (uint32_t i = 0; i < imageLength; ++i) { - ASSERT_EQ(uint8_t(0), imageData[i]); - } - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsFor0_0_100_100) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - CheckPalettedWritePixels(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, 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, 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, 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, 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, PalettedSurfaceSinkWritePixelsFinish) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Write nothing into the surface; just finish immediately. - uint32_t count = 0; - auto result = aSink->WritePixels<uint8_t>([&]{ - count++; - return AsVariant(WriteState::FINISHED); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(1u, count); - - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Attempt to write more and make sure that nothing gets written. - count = 0; - result = aSink->WritePixels<uint8_t>([&]() { - count++; - return AsVariant(uint8_t(128)); - }); - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(0u, count); - EXPECT_TRUE(aSink->IsSurfaceFinished()); - - // Check that the generated image is correct. - EXPECT_TRUE(IsSolidPalettedColor(aDecoder, 0)); - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsEarlyExit) -{ - auto checkEarlyExit = - [](Decoder* aDecoder, PalettedSurfaceSink* aSink, WriteState aState) { - // Write half a row of green pixels and then exit early with |aState|. If - // the lambda keeps getting called, we'll write red pixels, which will cause - // the test to fail. - uint32_t count = 0; - auto result = aSink->WritePixels<uint8_t>([&]() -> NextPixel<uint8_t> { - if (count == 50) { - return AsVariant(aState); - } - return count++ < 50 ? AsVariant(uint8_t(255)) : AsVariant(uint8_t(128)); - }); - - EXPECT_EQ(aState, result); - EXPECT_EQ(50u, count); - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 50, 1)); - - if (aState != WriteState::FINISHED) { - // We should still be able to write more at this point. - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - // Verify that we can resume writing. We'll finish up the same row. - count = 0; - result = aSink->WritePixels<uint8_t>([&]() -> NextPixel<uint8_t> { - if (count == 50) { - return AsVariant(WriteState::NEED_MORE_DATA); - } - ++count; - return AsVariant(uint8_t(255)); - }); - - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(50u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 100, 1)); - - return; - } - - // We should've finished the surface at this point. - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Attempt to write more and make sure that nothing gets written. - count = 0; - result = aSink->WritePixels<uint8_t>([&]{ - count++; - return AsVariant(uint8_t(128)); - }); - - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(0u, count); - EXPECT_TRUE(aSink->IsSurfaceFinished()); - - // Check that the generated image is still correct. - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 50, 1)); - }; - - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [&](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::NEED_MORE_DATA); - }); - - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [&](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::FAILURE); - }); - - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [&](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::FINISHED); - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsToRow) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Write the first 99 rows of our 100x100 surface and verify that even - // though our lambda will yield pixels forever, only one row is written per - // call to WritePixelsToRow(). - for (int row = 0; row < 99; ++row) { - uint32_t count = 0; - WriteState result = aSink->WritePixelsToRow<uint8_t>([&]{ - ++count; - return AsVariant(uint8_t(255)); - }); - - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(100u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isSome()); - EXPECT_EQ(IntRect(0, row, 100, 1), invalidRect->mInputSpaceRect); - EXPECT_EQ(IntRect(0, row, 100, 1), invalidRect->mOutputSpaceRect); - - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 100, row + 1)); - } - - // Write the final line, which should finish the surface. - uint32_t count = 0; - WriteState result = aSink->WritePixelsToRow<uint8_t>([&]{ - ++count; - return AsVariant(uint8_t(255)); - }); - - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(100u, count); - - // Note that the final invalid rect we expect here is only the last row; - // that's because we called TakeInvalidRect() repeatedly in the loop above. - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 99, 100, 1), - IntRect(0, 99, 100, 1)); - - // Check that the generated image is correct. - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 100, 100)); - - // Attempt to write more and make sure that nothing gets written. - count = 0; - result = aSink->WritePixelsToRow<uint8_t>([&]{ - count++; - return AsVariant(uint8_t(128)); - }); - - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(0u, count); - EXPECT_TRUE(aSink->IsSurfaceFinished()); - - // Check that the generated image is still correct. - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 100, 100)); - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWritePixelsToRowEarlyExit) -{ - auto checkEarlyExit = - [](Decoder* aDecoder, PalettedSurfaceSink* aSink, WriteState aState) { - // Write half a row of 255s and then exit early with |aState|. If the lambda - // keeps getting called, we'll write 128s, which will cause the test to - // fail. - uint32_t count = 0; - auto result = aSink->WritePixelsToRow<uint8_t>([&]() -> NextPixel<uint8_t> { - if (count == 50) { - return AsVariant(aState); - } - return count++ < 50 ? AsVariant(uint8_t(255)) - : AsVariant(uint8_t(128)); - }); - - EXPECT_EQ(aState, result); - EXPECT_EQ(50u, count); - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 50, 1)); - - if (aState != WriteState::FINISHED) { - // We should still be able to write more at this point. - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - // Verify that we can resume the same row and still stop at the end. - count = 0; - WriteState result = aSink->WritePixelsToRow<uint8_t>([&]{ - ++count; - return AsVariant(uint8_t(255)); - }); - - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(50u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 100, 1)); - - return; - } - - // We should've finished the surface at this point. - AssertCorrectPipelineFinalState(aSink, - IntRect(0, 0, 100, 100), - IntRect(0, 0, 100, 100)); - - // Attempt to write more and make sure that nothing gets written. - count = 0; - result = aSink->WritePixelsToRow<uint8_t>([&]{ - count++; - return AsVariant(uint8_t(128)); - }); - - EXPECT_EQ(WriteState::FINISHED, result); - EXPECT_EQ(0u, count); - EXPECT_TRUE(aSink->IsSurfaceFinished()); - - // Check that the generated image is still correct. - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 50, 1)); - }; - - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [&](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::NEED_MORE_DATA); - }); - - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [&](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::FAILURE); - }); - - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [&](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - checkEarlyExit(aDecoder, aSink, WriteState::FINISHED); - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBuffer) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Create a buffer the same size as one row of the surface (which is 100x100), - // containing 60 pixels of 255 in the middle and 20 transparent pixels of 0 on - // either side. - uint8_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = 20 <= i && i < 80 ? 255 : 0; - } - - // Write the buffer to every row of the surface and check that the generated - // image is correct. - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(20, 0, 60, 100), [&]{ - return aSink->WriteBuffer(buffer); - }); - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBufferPartialRow) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Create a buffer the same size as one row of the surface, containing all - // 255 pixels. - uint8_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = 255; - } - - // Write the buffer to the middle 60 pixels of every row of the surface and - // check that the generated image is correct. - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(20, 0, 60, 100), [&]{ - return aSink->WriteBuffer(buffer, 20, 60); - }); - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBufferPartialRowStartColOverflow) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Create a buffer the same size as one row of the surface, containing all - // 255 pixels. - uint8_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = 255; - } - - { - // Write the buffer to successive rows until every row of the surface - // has been written. We place the start column beyond the end of the row, - // which will prevent us from writing anything, so we check that the - // generated image is entirely 0. - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0), [&]{ - return aSink->WriteBuffer(buffer, 100, 100); - }); - } - - ResetForNextPass(aSink); - - { - // Write the buffer to successive rows until every row of the surface - // has been written. We use column 50 as the start column, but we still - // write the buffer, which means we overflow the right edge of the surface - // by 50 pixels. We check that the left half of the generated image is - // 0 and the right half is 255. - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{ - return aSink->WriteBuffer(buffer, 50, 100); - }); - } - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBufferPartialRowBufferOverflow) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Create a buffer twice as large as a row of the surface. The first half - // (which is as large as a row of the image) will contain 255 pixels, - // while the second half will contain 128 pixels. - uint8_t buffer[200]; - for (int i = 0; i < 200; ++i) { - buffer[i] = i < 100 ? 255 : 128; - } - - { - // Write the buffer to successive rows until every row of the surface has - // been written. The buffer extends 100 pixels to the right of a row of - // the surface, but bounds checking will prevent us from overflowing the - // buffer. We check that the generated image is entirely 255 since the - // pixels on the right side of the buffer shouldn't have been written to - // the surface. - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(0, 0, 100, 100), [&]{ - return aSink->WriteBuffer(buffer, 0, 200); - }); - } - - ResetForNextPass(aSink); - - { - // Write from the buffer to the middle of each row of the surface. That - // means that the left side of each row should be 0, since we didn't write - // anything there. A buffer overflow would cause us to write buffer - // contents into the left side of each row. We check that the generated - // image is 0 on the left side and 255 on the right. - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{ - return aSink->WriteBuffer(buffer, 50, 200); - }); - } - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteBufferFromNullSource) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Calling WriteBuffer() with a null pointer should fail without making any - // changes to the surface. - uint8_t* nullBuffer = nullptr; - WriteState result = aSink->WriteBuffer(nullBuffer); - - EXPECT_EQ(WriteState::FAILURE, result); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - Maybe<SurfaceInvalidRect> invalidRect = aSink->TakeInvalidRect(); - EXPECT_TRUE(invalidRect.isNothing()); - - // Check that nothing got written to the surface. - CheckGeneratedPalettedImage(aDecoder, IntRect(0, 0, 0, 0)); - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteEmptyRow) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - { - // Write an empty row to each row of the surface. We check that the - // generated image is entirely 0. - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0), [&]{ - return aSink->WriteEmptyRow(); - }); - } - - ResetForNextPass(aSink); - - { - // Write a partial row before we begin calling WriteEmptyRow(). We check - // that the generated image is entirely 0, which is to be expected since - // WriteEmptyRow() overwrites the current row even if some data has - // already been written to it. - uint32_t count = 0; - auto result = aSink->WritePixels<uint8_t>([&]() -> NextPixel<uint8_t> { - if (count == 50) { - return AsVariant(WriteState::NEED_MORE_DATA); - } - ++count; - return AsVariant(uint8_t(255)); - }); - - EXPECT_EQ(WriteState::NEED_MORE_DATA, result); - EXPECT_EQ(50u, count); - EXPECT_FALSE(aSink->IsSurfaceFinished()); - - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(0, 0, 0, 0), [&]{ - return aSink->WriteEmptyRow(); - }); - } - - ResetForNextPass(aSink); - - { - // Create a buffer the same size as one row of the surface, containing all - // 255 pixels. - uint8_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = 255; - } - - // Write an empty row to the middle 60 rows of the surface. The first 20 - // and last 20 rows will be 255. (We need to use DoCheckIterativeWrite() - // here because we need a custom function to check the output, since it - // can't be described by a simple rect.) - auto writeFunc = [&](uint32_t aRow) { - if (aRow < 20 || aRow >= 80) { - return aSink->WriteBuffer(buffer); - } else { - return aSink->WriteEmptyRow(); - } - }; - - auto checkFunc = [&]{ - EXPECT_TRUE(PalettedRowsAreSolidColor(aDecoder, 0, 20, 255)); - EXPECT_TRUE(PalettedRowsAreSolidColor(aDecoder, 20, 60, 0)); - EXPECT_TRUE(PalettedRowsAreSolidColor(aDecoder, 80, 20, 255)); - }; - - DoCheckIterativeWrite(aSink, writeFunc, checkFunc); - } - }); -} - -TEST(ImageSurfaceSink, PalettedSurfaceSinkWriteUnsafeComputedRow) -{ - WithPalettedSurfaceSink(IntRect(0, 0, 100, 100), - [](Decoder* aDecoder, PalettedSurfaceSink* aSink) { - // Create an all-255 buffer the same size as one row of the surface. - uint8_t buffer[100]; - for (int i = 0; i < 100; ++i) { - buffer[i] = 255; - } - - // Write the buffer to successive rows until every row of the surface has - // been written. We only write to the right half of each row, so we check - // that the left side of the generated image is 0 and the right side is 255. - CheckPalettedIterativeWrite(aDecoder, aSink, IntRect(50, 0, 50, 100), [&]{ - return aSink->WriteUnsafeComputedRow<uint8_t>([&](uint8_t* aRow, - uint32_t aLength) { - EXPECT_EQ(100u, aLength ); - memcpy(aRow + 50, buffer, 50 * sizeof(uint8_t)); - }); - }); - }); -} diff --git a/image/test/gtest/animated-with-extra-image-sub-blocks.gif b/image/test/gtest/animated-with-extra-image-sub-blocks.gif Binary files differdeleted file mode 100644 index a145c814a6..0000000000 --- a/image/test/gtest/animated-with-extra-image-sub-blocks.gif +++ /dev/null diff --git a/image/test/gtest/corrupt-with-bad-bmp-height.ico b/image/test/gtest/corrupt-with-bad-bmp-height.ico Binary files differdeleted file mode 100644 index ee4a90fcd7..0000000000 --- a/image/test/gtest/corrupt-with-bad-bmp-height.ico +++ /dev/null diff --git a/image/test/gtest/corrupt-with-bad-bmp-width.ico b/image/test/gtest/corrupt-with-bad-bmp-width.ico Binary files differdeleted file mode 100644 index aa4051cd07..0000000000 --- a/image/test/gtest/corrupt-with-bad-bmp-width.ico +++ /dev/null diff --git a/image/test/gtest/corrupt.jpg b/image/test/gtest/corrupt.jpg Binary files differdeleted file mode 100644 index 555a416d7d..0000000000 --- a/image/test/gtest/corrupt.jpg +++ /dev/null diff --git a/image/test/gtest/downscaled.bmp b/image/test/gtest/downscaled.bmp Binary files differdeleted file mode 100644 index 9e6a29e62b..0000000000 --- a/image/test/gtest/downscaled.bmp +++ /dev/null diff --git a/image/test/gtest/downscaled.gif b/image/test/gtest/downscaled.gif Binary files differdeleted file mode 100644 index ff9a20bcdb..0000000000 --- a/image/test/gtest/downscaled.gif +++ /dev/null diff --git a/image/test/gtest/downscaled.ico b/image/test/gtest/downscaled.ico Binary files differdeleted file mode 100644 index ee112af0a9..0000000000 --- a/image/test/gtest/downscaled.ico +++ /dev/null diff --git a/image/test/gtest/downscaled.icon b/image/test/gtest/downscaled.icon Binary files differdeleted file mode 100644 index 19785f5dcb..0000000000 --- a/image/test/gtest/downscaled.icon +++ /dev/null diff --git a/image/test/gtest/downscaled.jpg b/image/test/gtest/downscaled.jpg Binary files differdeleted file mode 100644 index 5a4b3cd036..0000000000 --- a/image/test/gtest/downscaled.jpg +++ /dev/null diff --git a/image/test/gtest/downscaled.png b/image/test/gtest/downscaled.png Binary files differdeleted file mode 100644 index b71b4652d5..0000000000 --- a/image/test/gtest/downscaled.png +++ /dev/null diff --git a/image/test/gtest/first-frame-green.gif b/image/test/gtest/first-frame-green.gif Binary files differdeleted file mode 100644 index cd3c7d3db8..0000000000 --- a/image/test/gtest/first-frame-green.gif +++ /dev/null diff --git a/image/test/gtest/first-frame-green.png b/image/test/gtest/first-frame-green.png Binary files differdeleted file mode 100644 index 115f035d89..0000000000 --- a/image/test/gtest/first-frame-green.png +++ /dev/null diff --git a/image/test/gtest/first-frame-padding.gif b/image/test/gtest/first-frame-padding.gif Binary files differdeleted file mode 100644 index e6d7c49322..0000000000 --- a/image/test/gtest/first-frame-padding.gif +++ /dev/null diff --git a/image/test/gtest/green-1x1-truncated.gif b/image/test/gtest/green-1x1-truncated.gif Binary files differdeleted file mode 100644 index 0829f9694d..0000000000 --- a/image/test/gtest/green-1x1-truncated.gif +++ /dev/null diff --git a/image/test/gtest/green.bmp b/image/test/gtest/green.bmp Binary files differdeleted file mode 100644 index f79dd672ad..0000000000 --- a/image/test/gtest/green.bmp +++ /dev/null diff --git a/image/test/gtest/green.gif b/image/test/gtest/green.gif Binary files differdeleted file mode 100644 index ef215dfc94..0000000000 --- a/image/test/gtest/green.gif +++ /dev/null diff --git a/image/test/gtest/green.ico b/image/test/gtest/green.ico Binary files differdeleted file mode 100644 index c5dfa8b538..0000000000 --- a/image/test/gtest/green.ico +++ /dev/null diff --git a/image/test/gtest/green.icon b/image/test/gtest/green.icon Binary files differdeleted file mode 100644 index c74e62fee5..0000000000 --- a/image/test/gtest/green.icon +++ /dev/null diff --git a/image/test/gtest/green.jpg b/image/test/gtest/green.jpg Binary files differdeleted file mode 100644 index 48c454d27c..0000000000 --- a/image/test/gtest/green.jpg +++ /dev/null diff --git a/image/test/gtest/green.png b/image/test/gtest/green.png Binary files differdeleted file mode 100644 index 7df25f33bd..0000000000 --- a/image/test/gtest/green.png +++ /dev/null diff --git a/image/test/gtest/invalid-truncated-metadata.bmp b/image/test/gtest/invalid-truncated-metadata.bmp Binary files differdeleted file mode 100644 index 228c5c9992..0000000000 --- a/image/test/gtest/invalid-truncated-metadata.bmp +++ /dev/null diff --git a/image/test/gtest/moz.build b/image/test/gtest/moz.build deleted file mode 100644 index 3548eaecc0..0000000000 --- a/image/test/gtest/moz.build +++ /dev/null @@ -1,79 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -Library('imagetest') - -UNIFIED_SOURCES = [ - 'Common.cpp', - 'TestADAM7InterpolatingFilter.cpp', - 'TestCopyOnWrite.cpp', - 'TestDecoders.cpp', - 'TestDecodeToSurface.cpp', - 'TestDeinterlacingFilter.cpp', - 'TestLoader.cpp', - 'TestMetadata.cpp', - 'TestRemoveFrameRectFilter.cpp', - 'TestSourceBuffer.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 += [ - 'animated-with-extra-image-sub-blocks.gif', - 'corrupt-with-bad-bmp-height.ico', - 'corrupt-with-bad-bmp-width.ico', - 'corrupt.jpg', - 'downscaled.bmp', - 'downscaled.gif', - 'downscaled.ico', - 'downscaled.icon', - 'downscaled.jpg', - 'downscaled.png', - 'first-frame-green.gif', - 'first-frame-green.png', - 'first-frame-padding.gif', - 'green-1x1-truncated.gif', - 'green.bmp', - 'green.gif', - 'green.ico', - 'green.icon', - 'green.jpg', - 'green.png', - 'invalid-truncated-metadata.bmp', - 'no-frame-delay.gif', - 'rle4.bmp', - 'rle8.bmp', - 'transparent-ico-with-and-mask.ico', - 'transparent-if-within-ico.bmp', - 'transparent.gif', - 'transparent.png', -] - -include('/ipc/chromium/chromium-config.mozbuild') - -LOCAL_INCLUDES += [ - '/dom/base', - '/gfx/2d', - '/image', -] - -LOCAL_INCLUDES += CONFIG['SKIA_INCLUDES'] - -FINAL_LIBRARY = 'xul-gtest' - -if CONFIG['GNU_CXX']: - CXXFLAGS += ['-Wno-error=shadow'] diff --git a/image/test/gtest/no-frame-delay.gif b/image/test/gtest/no-frame-delay.gif Binary files differdeleted file mode 100644 index 1c50b67431..0000000000 --- a/image/test/gtest/no-frame-delay.gif +++ /dev/null diff --git a/image/test/gtest/rle4.bmp b/image/test/gtest/rle4.bmp Binary files differdeleted file mode 100644 index 78a0927870..0000000000 --- a/image/test/gtest/rle4.bmp +++ /dev/null diff --git a/image/test/gtest/rle8.bmp b/image/test/gtest/rle8.bmp Binary files differdeleted file mode 100644 index bd793b6b66..0000000000 --- a/image/test/gtest/rle8.bmp +++ /dev/null diff --git a/image/test/gtest/transparent-ico-with-and-mask.ico b/image/test/gtest/transparent-ico-with-and-mask.ico Binary files differdeleted file mode 100644 index ab0dc4bce1..0000000000 --- a/image/test/gtest/transparent-ico-with-and-mask.ico +++ /dev/null diff --git a/image/test/gtest/transparent-if-within-ico.bmp b/image/test/gtest/transparent-if-within-ico.bmp Binary files differdeleted file mode 100644 index 4dc04c181b..0000000000 --- a/image/test/gtest/transparent-if-within-ico.bmp +++ /dev/null diff --git a/image/test/gtest/transparent.gif b/image/test/gtest/transparent.gif Binary files differdeleted file mode 100644 index 48f5c7caf1..0000000000 --- a/image/test/gtest/transparent.gif +++ /dev/null diff --git a/image/test/gtest/transparent.png b/image/test/gtest/transparent.png Binary files differdeleted file mode 100644 index fc8002053a..0000000000 --- a/image/test/gtest/transparent.png +++ /dev/null |