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   IntSize 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   IntSize 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,PNG)139 TEST_F(ImageDecoderMetadata, PNG) { CheckMetadata(GreenPNGTestCase()); }
TEST_F(ImageDecoderMetadata,TransparentPNG)140 TEST_F(ImageDecoderMetadata, TransparentPNG) {
141   CheckMetadata(TransparentPNGTestCase());
142 }
TEST_F(ImageDecoderMetadata,GIF)143 TEST_F(ImageDecoderMetadata, GIF) { CheckMetadata(GreenGIFTestCase()); }
TEST_F(ImageDecoderMetadata,TransparentGIF)144 TEST_F(ImageDecoderMetadata, TransparentGIF) {
145   CheckMetadata(TransparentGIFTestCase());
146 }
TEST_F(ImageDecoderMetadata,JPG)147 TEST_F(ImageDecoderMetadata, JPG) { CheckMetadata(GreenJPGTestCase()); }
TEST_F(ImageDecoderMetadata,BMP)148 TEST_F(ImageDecoderMetadata, BMP) { CheckMetadata(GreenBMPTestCase()); }
TEST_F(ImageDecoderMetadata,ICO)149 TEST_F(ImageDecoderMetadata, ICO) { CheckMetadata(GreenICOTestCase()); }
TEST_F(ImageDecoderMetadata,Icon)150 TEST_F(ImageDecoderMetadata, Icon) { CheckMetadata(GreenIconTestCase()); }
TEST_F(ImageDecoderMetadata,WebP)151 TEST_F(ImageDecoderMetadata, WebP) { CheckMetadata(GreenWebPTestCase()); }
152 
TEST_F(ImageDecoderMetadata,AnimatedGIF)153 TEST_F(ImageDecoderMetadata, AnimatedGIF) {
154   CheckMetadata(GreenFirstFrameAnimatedGIFTestCase());
155 }
156 
TEST_F(ImageDecoderMetadata,AnimatedPNG)157 TEST_F(ImageDecoderMetadata, AnimatedPNG) {
158   CheckMetadata(GreenFirstFrameAnimatedPNGTestCase());
159 }
160 
TEST_F(ImageDecoderMetadata,FirstFramePaddingGIF)161 TEST_F(ImageDecoderMetadata, FirstFramePaddingGIF) {
162   CheckMetadata(FirstFramePaddingGIFTestCase());
163 }
164 
TEST_F(ImageDecoderMetadata,TransparentIfWithinICOBMPNotWithinICO)165 TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPNotWithinICO) {
166   CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_DEFAULT_FLAGS),
167                 BMPWithinICO::NO);
168 }
169 
TEST_F(ImageDecoderMetadata,TransparentIfWithinICOBMPWithinICO)170 TEST_F(ImageDecoderMetadata, TransparentIfWithinICOBMPWithinICO) {
171   CheckMetadata(TransparentIfWithinICOBMPTestCase(TEST_CASE_IS_TRANSPARENT),
172                 BMPWithinICO::YES);
173 }
174 
TEST_F(ImageDecoderMetadata,RLE4BMP)175 TEST_F(ImageDecoderMetadata, RLE4BMP) { CheckMetadata(RLE4BMPTestCase()); }
TEST_F(ImageDecoderMetadata,RLE8BMP)176 TEST_F(ImageDecoderMetadata, RLE8BMP) { CheckMetadata(RLE8BMPTestCase()); }
177 
TEST_F(ImageDecoderMetadata,Corrupt)178 TEST_F(ImageDecoderMetadata, Corrupt) { CheckMetadata(CorruptTestCase()); }
179 
TEST_F(ImageDecoderMetadata,NoFrameDelayGIF)180 TEST_F(ImageDecoderMetadata, NoFrameDelayGIF) {
181   CheckMetadata(NoFrameDelayGIFTestCase());
182 }
183 
TEST_F(ImageDecoderMetadata,NoFrameDelayGIFFullDecode)184 TEST_F(ImageDecoderMetadata, NoFrameDelayGIFFullDecode) {
185   ImageTestCase testCase = NoFrameDelayGIFTestCase();
186 
187   // The previous test (NoFrameDelayGIF) verifies that we *don't* detect that
188   // this test case is animated, because it has a zero frame delay for the first
189   // frame. This test verifies that when we do a full decode, we detect the
190   // animation at that point and successfully decode all the frames.
191 
192   // Create an image.
193   RefPtr<Image> image = ImageFactory::CreateAnonymousImage(
194       nsDependentCString(testCase.mMimeType));
195   ASSERT_TRUE(!image->HasError());
196 
197   nsCOMPtr<nsIInputStream> inputStream = LoadFile(testCase.mPath);
198   ASSERT_TRUE(inputStream != nullptr);
199 
200   // Figure out how much data we have.
201   uint64_t length;
202   nsresult rv = inputStream->Available(&length);
203   ASSERT_TRUE(NS_SUCCEEDED(rv));
204 
205   // Write the data into the image.
206   rv = image->OnImageDataAvailable(nullptr, nullptr, inputStream, 0,
207                                    static_cast<uint32_t>(length));
208   ASSERT_TRUE(NS_SUCCEEDED(rv));
209 
210   // Let the image know we've sent all the data.
211   rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
212   ASSERT_TRUE(NS_SUCCEEDED(rv));
213 
214   RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
215   tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
216 
217   // Use GetFrame() to force a sync decode of the image.
218   RefPtr<SourceSurface> surface = image->GetFrame(
219       imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
220 
221   // Ensure that the image's metadata meets our expectations.
222   IntSize imageSize(0, 0);
223   rv = image->GetWidth(&imageSize.width);
224   EXPECT_TRUE(NS_SUCCEEDED(rv));
225   rv = image->GetHeight(&imageSize.height);
226   EXPECT_TRUE(NS_SUCCEEDED(rv));
227 
228   EXPECT_EQ(testCase.mSize.width, imageSize.width);
229   EXPECT_EQ(testCase.mSize.height, imageSize.height);
230 
231   Progress imageProgress = tracker->GetProgress();
232 
233   EXPECT_TRUE(bool(imageProgress & FLAG_HAS_TRANSPARENCY) == false);
234   EXPECT_TRUE(bool(imageProgress & FLAG_IS_ANIMATED) == true);
235 
236   // Ensure that we decoded both frames of the image.
237   LookupResult result =
238       SurfaceCache::Lookup(ImageKey(image.get()),
239                            RasterSurfaceKey(imageSize, testCase.mSurfaceFlags,
240                                             PlaybackType::eAnimated),
241                            /* aMarkUsed = */ true);
242   ASSERT_EQ(MatchType::EXACT, result.Type());
243 
244   EXPECT_TRUE(NS_SUCCEEDED(result.Surface().Seek(0)));
245   EXPECT_TRUE(bool(result.Surface()));
246 
247   RefPtr<imgFrame> partialFrame = result.Surface().GetFrame(1);
248   EXPECT_TRUE(bool(partialFrame));
249 }
250