1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "gtest/gtest.h"
6
7 #include "Common.h"
8 #include "Decoder.h"
9 #include "DecoderFactory.h"
10 #include "decoders/nsBMPDecoder.h"
11 #include "IDecodingTask.h"
12 #include "imgIContainer.h"
13 #include "ImageFactory.h"
14 #include "mozilla/gfx/2D.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsCOMPtr.h"
17 #include "nsIInputStream.h"
18 #include "mozilla/RefPtr.h"
19 #include "nsStreamUtils.h"
20 #include "nsString.h"
21 #include "nsThreadUtils.h"
22 #include "ProgressTracker.h"
23 #include "SourceBuffer.h"
24
25 using namespace mozilla;
26 using namespace mozilla::gfx;
27 using namespace mozilla::image;
28
29 enum class BMPWithinICO { NO, YES };
30
CheckMetadata(const ImageTestCase & aTestCase,BMPWithinICO aBMPWithinICO=BMPWithinICO::NO)31 static void CheckMetadata(const ImageTestCase& aTestCase,
32 BMPWithinICO aBMPWithinICO = BMPWithinICO::NO) {
33 nsCOMPtr<nsIInputStream> inputStream = LoadFile(aTestCase.mPath);
34 ASSERT_TRUE(inputStream != nullptr);
35
36 // Figure out how much data we have.
37 uint64_t length;
38 nsresult rv = inputStream->Available(&length);
39 ASSERT_TRUE(NS_SUCCEEDED(rv));
40
41 // Write the data into a SourceBuffer.
42 auto sourceBuffer = MakeNotNull<RefPtr<SourceBuffer>>();
43 sourceBuffer->ExpectLength(length);
44 rv = sourceBuffer->AppendFromInputStream(inputStream, length);
45 ASSERT_TRUE(NS_SUCCEEDED(rv));
46 sourceBuffer->Complete(NS_OK);
47
48 // Create a metadata decoder.
49 DecoderType decoderType = DecoderFactory::GetDecoderType(aTestCase.mMimeType);
50 RefPtr<image::Decoder> decoder =
51 DecoderFactory::CreateAnonymousMetadataDecoder(decoderType, sourceBuffer);
52 ASSERT_TRUE(decoder != nullptr);
53 RefPtr<IDecodingTask> task =
54 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
55
56 if (aBMPWithinICO == BMPWithinICO::YES) {
57 static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO();
58 }
59
60 // Run the metadata decoder synchronously.
61 task->Run();
62
63 // Ensure that the metadata decoder didn't make progress it shouldn't have
64 // (which would indicate that it decoded past the header of the image).
65 Progress metadataProgress = decoder->TakeProgress();
66 EXPECT_TRUE(
67 0 == (metadataProgress &
68 ~(FLAG_SIZE_AVAILABLE | FLAG_HAS_TRANSPARENCY | FLAG_IS_ANIMATED)));
69
70 // If the test case is corrupt, assert what we can and return early.
71 if (aTestCase.mFlags & TEST_CASE_HAS_ERROR) {
72 EXPECT_TRUE(decoder->GetDecodeDone());
73 EXPECT_TRUE(decoder->HasError());
74 return;
75 }
76
77 EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError());
78
79 // Check that we got the expected metadata.
80 EXPECT_TRUE(metadataProgress & FLAG_SIZE_AVAILABLE);
81
82 OrientedIntSize metadataSize = decoder->Size();
83 EXPECT_EQ(aTestCase.mSize.width, metadataSize.width);
84 if (aBMPWithinICO == BMPWithinICO::YES) {
85 // Half the data is considered to be part of the AND mask if embedded
86 EXPECT_EQ(aTestCase.mSize.height / 2, metadataSize.height);
87 } else {
88 EXPECT_EQ(aTestCase.mSize.height, metadataSize.height);
89 }
90
91 bool expectTransparency =
92 aBMPWithinICO == BMPWithinICO::YES
93 ? true
94 : bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT);
95 EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY));
96
97 EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
98 bool(metadataProgress & FLAG_IS_ANIMATED));
99
100 // Create a full decoder, so we can compare the result.
101 decoder = DecoderFactory::CreateAnonymousDecoder(
102 decoderType, sourceBuffer, Nothing(), DecoderFlags::FIRST_FRAME_ONLY,
103 aTestCase.mSurfaceFlags);
104 ASSERT_TRUE(decoder != nullptr);
105 task =
106 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
107
108 if (aBMPWithinICO == BMPWithinICO::YES) {
109 static_cast<nsBMPDecoder*>(decoder.get())->SetIsWithinICO();
110 }
111
112 // Run the full decoder synchronously.
113 task->Run();
114
115 EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError());
116 Progress fullProgress = decoder->TakeProgress();
117
118 // If the metadata decoder set a progress bit, the full decoder should also
119 // have set the same bit.
120 EXPECT_EQ(fullProgress, metadataProgress | fullProgress);
121
122 // The full decoder and the metadata decoder should agree on the image's size.
123 OrientedIntSize fullSize = decoder->Size();
124 EXPECT_EQ(metadataSize.width, fullSize.width);
125 EXPECT_EQ(metadataSize.height, fullSize.height);
126
127 // We should not discover transparency during the full decode that we didn't
128 // discover during the metadata decode, unless the image is animated.
129 EXPECT_TRUE(!(fullProgress & FLAG_HAS_TRANSPARENCY) ||
130 (metadataProgress & FLAG_HAS_TRANSPARENCY) ||
131 (fullProgress & FLAG_IS_ANIMATED));
132 }
133
134 class ImageDecoderMetadata : public ::testing::Test {
135 protected:
136 AutoInitializeImageLib mInit;
137 };
138
TEST_F(ImageDecoderMetadata,TransparentAVIF)139 TEST_F(ImageDecoderMetadata, TransparentAVIF) {
140 CheckMetadata(TransparentAVIFTestCase());
141 }
142
TEST_F(ImageDecoderMetadata,PNG)143 TEST_F(ImageDecoderMetadata, PNG) { CheckMetadata(GreenPNGTestCase()); }
TEST_F(ImageDecoderMetadata,TransparentPNG)144 TEST_F(ImageDecoderMetadata, TransparentPNG) {
145 CheckMetadata(TransparentPNGTestCase());
146 }
TEST_F(ImageDecoderMetadata,GIF)147 TEST_F(ImageDecoderMetadata, GIF) { CheckMetadata(GreenGIFTestCase()); }
TEST_F(ImageDecoderMetadata,TransparentGIF)148 TEST_F(ImageDecoderMetadata, TransparentGIF) {
149 CheckMetadata(TransparentGIFTestCase());
150 }
TEST_F(ImageDecoderMetadata,JPG)151 TEST_F(ImageDecoderMetadata, JPG) { CheckMetadata(GreenJPGTestCase()); }
TEST_F(ImageDecoderMetadata,BMP)152 TEST_F(ImageDecoderMetadata, BMP) { CheckMetadata(GreenBMPTestCase()); }
TEST_F(ImageDecoderMetadata,ICO)153 TEST_F(ImageDecoderMetadata, ICO) { CheckMetadata(GreenICOTestCase()); }
TEST_F(ImageDecoderMetadata,Icon)154 TEST_F(ImageDecoderMetadata, Icon) { CheckMetadata(GreenIconTestCase()); }
TEST_F(ImageDecoderMetadata,WebP)155 TEST_F(ImageDecoderMetadata, WebP) { CheckMetadata(GreenWebPTestCase()); }
156
157 #ifdef MOZ_JXL
TEST_F(ImageDecoderMetadata,JXL)158 TEST_F(ImageDecoderMetadata, JXL) { CheckMetadata(GreenJXLTestCase()); }
TEST_F(ImageDecoderMetadata,TransparentJXL)159 TEST_F(ImageDecoderMetadata, TransparentJXL) {
160 CheckMetadata(TransparentJXLTestCase());
161 }
162 #endif
163
TEST_F(ImageDecoderMetadata,AnimatedGIF)164 TEST_F(ImageDecoderMetadata, AnimatedGIF) {
165 CheckMetadata(GreenFirstFrameAnimatedGIFTestCase());
166 }
167
TEST_F(ImageDecoderMetadata,AnimatedPNG)168 TEST_F(ImageDecoderMetadata, AnimatedPNG) {
169 CheckMetadata(GreenFirstFrameAnimatedPNGTestCase());
170 }
171
TEST_F(ImageDecoderMetadata,FirstFramePaddingGIF)172 TEST_F(ImageDecoderMetadata, FirstFramePaddingGIF) {
173 CheckMetadata(FirstFramePaddingGIFTestCase());
174 }
175
TEST_F(ImageDecoderMetadata,TransparentIfWithinICOBMPNotWithinICO)176 TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPNotWithinICO) {
177 CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_DEFAULT_FLAGS),
178 BMPWithinICO::NO);
179 }
180
TEST_F(ImageDecoderMetadata,TransparentIfWithinICOBMPWithinICO)181 TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPWithinICO) {
182 CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_IS_TRANSPARENT),
183 BMPWithinICO::YES);
184 }
185
TEST_F(ImageDecoderMetadata,RLE4BMP)186 TEST_F(ImageDecoderMetadata, RLE4BMP) { CheckMetadata(RLE4BMPTestCase()); }
TEST_F(ImageDecoderMetadata,RLE8BMP)187 TEST_F(ImageDecoderMetadata, RLE8BMP) { CheckMetadata(RLE8BMPTestCase()); }
188
TEST_F(ImageDecoderMetadata,Corrupt)189 TEST_F(ImageDecoderMetadata, Corrupt) { CheckMetadata(CorruptTestCase()); }
190
TEST_F(ImageDecoderMetadata,NoFrameDelayGIF)191 TEST_F(ImageDecoderMetadata, NoFrameDelayGIF) {
192 CheckMetadata(NoFrameDelayGIFTestCase());
193 }
194
TEST_F(ImageDecoderMetadata,NoFrameDelayGIFFullDecode)195 TEST_F(ImageDecoderMetadata, NoFrameDelayGIFFullDecode) {
196 ImageTestCase testCase = NoFrameDelayGIFTestCase();
197
198 // The previous test (NoFrameDelayGIF) verifies that we *don't* detect that
199 // this test case is animated, because it has a zero frame delay for the first
200 // frame. This test verifies that when we do a full decode, we detect the
201 // animation at that point and successfully decode all the frames.
202
203 // Create an image.
204 RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
205 nsDependentCString(testCase.mMimeType));
206 ASSERT_TRUE(!image->HasError());
207
208 nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
209 ASSERT_TRUE(inputStream != nullptr);
210
211 // Figure out how much data we have.
212 uint64_t length;
213 nsresult rv = inputStream->Available(&length);
214 ASSERT_TRUE(NS_SUCCEEDED(rv));
215
216 // Write the data into the image.
217 rv = image->OnImageDataAvailable(nullptr, inputStream, 0,
218 static_cast<uint32_t>(length));
219 ASSERT_TRUE(NS_SUCCEEDED(rv));
220
221 // Let the image know we've sent all the data.
222 rv = image->OnImageDataComplete(nullptr, NS_OK, true);
223 ASSERT_TRUE(NS_SUCCEEDED(rv));
224
225 RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
226 tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
227
228 // Use GetFrame() to force a sync decode of the image.
229 RefPtr<SourceSurface> surface = image->GetFrame(
230 imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
231
232 // Ensure that the image's metadata meets our expectations.
233 IntSize imageSize(0, 0);
234 rv = image->GetWidth(&imageSize.width);
235 EXPECT_TRUE(NS_SUCCEEDED(rv));
236 rv = image->GetHeight(&imageSize.height);
237 EXPECT_TRUE(NS_SUCCEEDED(rv));
238
239 EXPECT_EQ(testCase.mSize.width, imageSize.width);
240 EXPECT_EQ(testCase.mSize.height, imageSize.height);
241
242 Progress imageProgress = tracker->GetProgress();
243
244 EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
245 EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
246
247 // Ensure that we decoded both frames of the image.
248 LookupResult result =
249 SurfaceCache::Lookup(ImageKey(image.get()),
250 RasterSurfaceKey(imageSize, testCase.mSurfaceFlags,
251 PlaybackType::eAnimated),
252 /* aMarkUsed = */ true);
253 ASSERT_EQ(MatchType::EXACT, result.Type());
254
255 EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
256 EXPECT_TRUE(bool(result.Surface()));
257
258 RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
259 EXPECT_TRUE(bool(partialFrame));
260 }
261