diff options
Diffstat (limited to 'image/test/gtest/Common.cpp')
-rw-r--r-- | image/test/gtest/Common.cpp | 673 |
1 files changed, 673 insertions, 0 deletions
diff --git a/image/test/gtest/Common.cpp b/image/test/gtest/Common.cpp new file mode 100644 index 000000000..5a24bbb14 --- /dev/null +++ b/image/test/gtest/Common.cpp @@ -0,0 +1,673 @@ +/* -*- 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 |