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