• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "core/fxcodec/progressive_decoder.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <array>
11 #include <numeric>
12 #include <tuple>
13 #include <utility>
14 
15 #include "core/fxcodec/fx_codec.h"
16 #include "core/fxcodec/fx_codec_def.h"
17 #include "core/fxcodec/jpeg/jpeg_progressive_decoder.h"
18 #include "core/fxcrt/cfx_read_only_span_stream.h"
19 #include "core/fxcrt/cfx_read_only_vector_stream.h"
20 #include "core/fxcrt/data_vector.h"
21 #include "core/fxcrt/retain_ptr.h"
22 #include "core/fxcrt/span.h"
23 #include "core/fxge/dib/cfx_dibitmap.h"
24 #include "core/fxge/dib/fx_dib.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 
28 #ifdef PDF_ENABLE_XFA_BMP
29 #include "core/fxcodec/bmp/bmp_decoder.h"
30 #include "core/fxcodec/bmp/bmp_progressive_decoder.h"
31 #endif  // PDF_ENABLE_XFA_BMP
32 
33 #ifdef PDF_ENABLE_XFA_GIF
34 #include "core/fxcodec/gif/gif_decoder.h"
35 #include "core/fxcodec/gif/gif_progressive_decoder.h"
36 #endif  // PDF_ENABLE_XFA_GIF
37 
38 namespace fxcodec {
39 
40 namespace {
41 
42 using ::testing::ElementsAre;
43 using ::testing::ElementsAreArray;
44 
45 template <size_t Size>
IotaArray(uint8_t start)46 constexpr std::array<uint8_t, Size> IotaArray(uint8_t start) {
47   std::array<uint8_t, Size> result;
48   std::iota(result.begin(), result.end(), start);
49   return result;
50 }
51 
DecodeToBitmap(ProgressiveDecoder & decoder,RetainPtr<CFX_DIBitmap> bitmap)52 FXCODEC_STATUS DecodeToBitmap(ProgressiveDecoder& decoder,
53                               RetainPtr<CFX_DIBitmap> bitmap) {
54   FXCODEC_STATUS status = decoder.StartDecode(std::move(bitmap));
55   while (status == FXCODEC_STATUS::kDecodeToBeContinued) {
56     status = decoder.ContinueDecode();
57   }
58   return status;
59 }
60 
61 class ProgressiveDecoderTest : public testing::Test {
SetUp()62   void SetUp() override {
63 #ifdef PDF_ENABLE_XFA_BMP
64     BmpProgressiveDecoder::InitializeGlobals();
65 #endif
66 #ifdef PDF_ENABLE_XFA_GIF
67     GifProgressiveDecoder::InitializeGlobals();
68 #endif
69     JpegProgressiveDecoder::InitializeGlobals();
70   }
TearDown()71   void TearDown() override {
72     JpegProgressiveDecoder::DestroyGlobals();
73 #ifdef PDF_ENABLE_XFA_GIF
74     GifProgressiveDecoder::DestroyGlobals();
75 #endif
76 #ifdef PDF_ENABLE_XFA_BMP
77     BmpProgressiveDecoder::DestroyGlobals();
78 #endif
79   }
80 };
81 
82 }  // namespace
83 
84 #ifdef PDF_ENABLE_XFA_BMP
TEST_F(ProgressiveDecoderTest,Indexed8Bmp)85 TEST_F(ProgressiveDecoderTest, Indexed8Bmp) {
86   static constexpr uint8_t kInput[] = {
87       0x42, 0x4d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
88       0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
89       0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
90       0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b,
91       0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0,
92       0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
93 
94   ProgressiveDecoder decoder;
95 
96   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
97   CFX_DIBAttribute attr;
98   FXCODEC_STATUS status =
99       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
100   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
101 
102   ASSERT_EQ(1, decoder.GetWidth());
103   ASSERT_EQ(1, decoder.GetHeight());
104   ASSERT_EQ(FXDIB_Format::kBgr, decoder.GetBitmapFormat());
105 
106   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
107   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
108                              decoder.GetBitmapFormat()));
109 
110   size_t frames;
111   std::tie(status, frames) = decoder.GetFrames();
112   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
113   ASSERT_EQ(1u, frames);
114 
115   status = DecodeToBitmap(decoder, bitmap);
116   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
117   EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
118 }
119 
TEST_F(ProgressiveDecoderTest,Indexed8BmpWithInvalidIndex)120 TEST_F(ProgressiveDecoderTest, Indexed8BmpWithInvalidIndex) {
121   static constexpr uint8_t kInput[] = {
122       0x42, 0x4d, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a,
123       0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
124       0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
125       0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b,
126       0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xc0,
127       0x80, 0x40, 0x00, 0x01, 0x00, 0x00, 0x00};
128 
129   ProgressiveDecoder decoder;
130 
131   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
132   CFX_DIBAttribute attr;
133   FXCODEC_STATUS status =
134       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
135   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
136 
137   ASSERT_EQ(1, decoder.GetWidth());
138   ASSERT_EQ(1, decoder.GetHeight());
139   ASSERT_EQ(FXDIB_Format::kBgr, decoder.GetBitmapFormat());
140 
141   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
142   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
143                              decoder.GetBitmapFormat()));
144 
145   size_t frames;
146   std::tie(status, frames) = decoder.GetFrames();
147   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
148   ASSERT_EQ(1u, frames);
149 
150   status = DecodeToBitmap(decoder, bitmap);
151   EXPECT_EQ(FXCODEC_STATUS::kError, status);
152 }
153 
TEST_F(ProgressiveDecoderTest,Direct24Bmp)154 TEST_F(ProgressiveDecoderTest, Direct24Bmp) {
155   static constexpr uint8_t kInput[] = {
156       0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
157       0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
158       0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
159       0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
160       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00};
161 
162   ProgressiveDecoder decoder;
163 
164   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
165   CFX_DIBAttribute attr;
166   FXCODEC_STATUS status =
167       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
168   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
169 
170   ASSERT_EQ(1, decoder.GetWidth());
171   ASSERT_EQ(1, decoder.GetHeight());
172   ASSERT_EQ(FXDIB_Format::kBgr, decoder.GetBitmapFormat());
173 
174   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
175   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
176                              decoder.GetBitmapFormat()));
177 
178   size_t frames;
179   std::tie(status, frames) = decoder.GetFrames();
180   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
181   ASSERT_EQ(1u, frames);
182 
183   status = DecodeToBitmap(decoder, bitmap);
184   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
185   EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
186 }
187 
TEST_F(ProgressiveDecoderTest,Direct32Bmp)188 TEST_F(ProgressiveDecoderTest, Direct32Bmp) {
189   static constexpr uint8_t kInput[] = {
190       0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
191       0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
192       0x00, 0x00, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
193       0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
194       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0xff};
195 
196   ProgressiveDecoder decoder;
197 
198   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
199   CFX_DIBAttribute attr;
200   FXCODEC_STATUS status =
201       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
202   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
203 
204   ASSERT_EQ(1, decoder.GetWidth());
205   ASSERT_EQ(1, decoder.GetHeight());
206   ASSERT_EQ(FXDIB_Format::kBgrx, decoder.GetBitmapFormat());
207 
208   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
209   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
210                              decoder.GetBitmapFormat()));
211 
212   size_t frames;
213   std::tie(status, frames) = decoder.GetFrames();
214   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
215   ASSERT_EQ(1u, frames);
216 
217   status = DecodeToBitmap(decoder, bitmap);
218   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
219   EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
220 }
221 
TEST_F(ProgressiveDecoderTest,BmpWithDataOffsetBeforeEndOfHeader)222 TEST_F(ProgressiveDecoderTest, BmpWithDataOffsetBeforeEndOfHeader) {
223   static constexpr uint8_t kInput[] = {
224       0x42, 0x4d, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x00,
225       0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
226       0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
227       0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
228       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00};
229 
230   ProgressiveDecoder decoder;
231 
232   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
233   CFX_DIBAttribute attr;
234   FXCODEC_STATUS status =
235       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
236   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
237 
238   ASSERT_EQ(1, decoder.GetWidth());
239   ASSERT_EQ(1, decoder.GetHeight());
240   ASSERT_EQ(FXDIB_Format::kBgr, decoder.GetBitmapFormat());
241 
242   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
243   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
244                              decoder.GetBitmapFormat()));
245 
246   size_t frames;
247   std::tie(status, frames) = decoder.GetFrames();
248   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
249   ASSERT_EQ(1u, frames);
250 
251   status = DecodeToBitmap(decoder, bitmap);
252   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
253   EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
254 }
255 
TEST_F(ProgressiveDecoderTest,BmpWithDataOffsetAfterEndOfHeader)256 TEST_F(ProgressiveDecoderTest, BmpWithDataOffsetAfterEndOfHeader) {
257   static constexpr uint8_t kInput[] = {
258       0x42, 0x4d, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00,
259       0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
260       0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
261       0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
262       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x80, 0x40, 0x00};
263 
264   ProgressiveDecoder decoder;
265 
266   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
267   CFX_DIBAttribute attr;
268   FXCODEC_STATUS status =
269       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
270   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
271 
272   ASSERT_EQ(1, decoder.GetWidth());
273   ASSERT_EQ(1, decoder.GetHeight());
274   ASSERT_EQ(FXDIB_Format::kBgr, decoder.GetBitmapFormat());
275 
276   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
277   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
278                              decoder.GetBitmapFormat()));
279 
280   size_t frames;
281   std::tie(status, frames) = decoder.GetFrames();
282   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
283   ASSERT_EQ(1u, frames);
284 
285   status = DecodeToBitmap(decoder, bitmap);
286   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
287   EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0x00));
288 }
289 
TEST_F(ProgressiveDecoderTest,LargeBmp)290 TEST_F(ProgressiveDecoderTest, LargeBmp) {
291   // Construct a 24-bit BMP larger than `kBlockSize` (4096 bytes).
292   static constexpr uint8_t kWidth = 37;
293   static constexpr uint8_t kHeight = 38;
294   static constexpr size_t kScanlineSize = kWidth * 3 + 1;
295   DataVector<uint8_t> input = {
296       0x42,    0x4d, 0xd6, 0x10, 0x00, 0x00, 0x00, 0x00,   0x00, 0x00, 0x36,
297       0x00,    0x00, 0x00, 0x28, 0x00, 0x00, 0x00, kWidth, 0x00, 0x00, 0x00,
298       kHeight, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00,   0x00, 0x00, 0x00,
299       0x00,    0xa0, 0x10, 0x00, 0x00, 0x13, 0x0b, 0x00,   0x00, 0x13, 0x0b,
300       0x00,    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   0x00, 0x00};
301   input.resize(54 + kScanlineSize * kHeight);
302   std::iota(input.begin() + 54, input.end(), 0);
303   ASSERT_EQ(4310u, input.size());
304 
305   ProgressiveDecoder decoder;
306 
307   auto source = pdfium::MakeRetain<CFX_ReadOnlyVectorStream>(std::move(input));
308   CFX_DIBAttribute attr;
309   FXCODEC_STATUS status =
310       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_BMP, &attr, true);
311   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
312 
313   ASSERT_EQ(kWidth, decoder.GetWidth());
314   ASSERT_EQ(kHeight, decoder.GetHeight());
315   ASSERT_EQ(FXDIB_Format::kBgr, decoder.GetBitmapFormat());
316 
317   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
318   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
319                              decoder.GetBitmapFormat()));
320 
321   size_t frames;
322   std::tie(status, frames) = decoder.GetFrames();
323   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
324   ASSERT_EQ(1u, frames);
325 
326   status = DecodeToBitmap(decoder, bitmap);
327   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
328 
329   for (size_t row = 0; row < kHeight; ++row) {
330     // BMP encodes rows from bottom to top by default.
331     pdfium::span<const uint8_t> scanline =
332         bitmap->GetScanline(kHeight - row - 1);
333 
334     EXPECT_THAT(
335         scanline.first(kScanlineSize - 1),
336         ElementsAreArray(IotaArray<kScanlineSize - 1>(row * kScanlineSize)));
337 
338     // Last byte is padding to a 32-bit boundary.
339     EXPECT_EQ(0, scanline[kScanlineSize - 1]);
340   }
341 }
342 #endif  // PDF_ENABLE_XFA_BMP
343 
344 #ifdef PDF_ENABLE_XFA_GIF
TEST_F(ProgressiveDecoderTest,Gif87a)345 TEST_F(ProgressiveDecoderTest, Gif87a) {
346   static constexpr uint8_t kInput[] = {
347       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x01,
348       0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c, 0x00, 0x00, 0x00, 0x00,
349       0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b};
350 
351   ProgressiveDecoder decoder;
352 
353   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
354   CFX_DIBAttribute attr;
355   FXCODEC_STATUS status =
356       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
357   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
358 
359   ASSERT_EQ(1, decoder.GetWidth());
360   ASSERT_EQ(1, decoder.GetHeight());
361   ASSERT_EQ(FXDIB_Format::kBgra, decoder.GetBitmapFormat());
362 
363   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
364   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
365                              decoder.GetBitmapFormat()));
366 
367   size_t frames;
368   std::tie(status, frames) = decoder.GetFrames();
369   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
370   ASSERT_EQ(1u, frames);
371 
372   status = DecodeToBitmap(decoder, bitmap);
373   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
374   EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0xff));
375 }
376 
TEST_F(ProgressiveDecoderTest,Gif89a)377 TEST_F(ProgressiveDecoderTest, Gif89a) {
378   static constexpr uint8_t kInput[] = {
379       0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80,
380       0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x21, 0xf9, 0x04,
381       0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01,
382       0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b};
383 
384   ProgressiveDecoder decoder;
385 
386   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
387   CFX_DIBAttribute attr;
388   FXCODEC_STATUS status =
389       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
390   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
391 
392   ASSERT_EQ(1, decoder.GetWidth());
393   ASSERT_EQ(1, decoder.GetHeight());
394   ASSERT_EQ(FXDIB_Format::kBgra, decoder.GetBitmapFormat());
395 
396   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
397   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
398                              decoder.GetBitmapFormat()));
399 
400   size_t frames;
401   std::tie(status, frames) = decoder.GetFrames();
402   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
403   ASSERT_EQ(1u, frames);
404 
405   status = DecodeToBitmap(decoder, bitmap);
406   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
407   EXPECT_THAT(bitmap->GetScanline(0), ElementsAre(0xc0, 0x80, 0x40, 0xff));
408 }
409 
TEST_F(ProgressiveDecoderTest,GifInsufficientCodeSize)410 TEST_F(ProgressiveDecoderTest, GifInsufficientCodeSize) {
411   // This GIF causes `LZWDecompressor::Create()` to fail because the minimum
412   // code size is too small for the palette.
413   static constexpr uint8_t kInput[] = {
414       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x01, 0x00, 0x01, 0x00, 0x82,
415       0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81,
416       0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85,
417       0x85, 0x86, 0x86, 0x86, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
418       0x01, 0x00, 0x00, 0x02, 0x2,  0x44, 0x01, 0x00, 0x3b};
419 
420   ProgressiveDecoder decoder;
421 
422   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
423   CFX_DIBAttribute attr;
424   FXCODEC_STATUS status =
425       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
426   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
427 
428   ASSERT_EQ(1, decoder.GetWidth());
429   ASSERT_EQ(1, decoder.GetHeight());
430   ASSERT_EQ(FXDIB_Format::kBgra, decoder.GetBitmapFormat());
431 
432   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
433   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
434                              decoder.GetBitmapFormat()));
435 
436   size_t frames;
437   std::tie(status, frames) = decoder.GetFrames();
438   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
439   ASSERT_EQ(1u, frames);
440 
441   status = DecodeToBitmap(decoder, bitmap);
442   EXPECT_EQ(FXCODEC_STATUS::kError, status);
443 }
444 
TEST_F(ProgressiveDecoderTest,GifDecodeAcrossScanlines)445 TEST_F(ProgressiveDecoderTest, GifDecodeAcrossScanlines) {
446   // This GIF contains an LZW code unit split across 2 scanlines. The decoder
447   // must continue decoding the second scanline using the residual data.
448   static constexpr uint8_t kInput[] = {
449       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x02, 0x00, 0x80, 0x01,
450       0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c, 0x00, 0x00, 0x00, 0x00,
451       0x04, 0x00, 0x02, 0x00, 0x00, 0x02, 0x03, 0x84, 0x6f, 0x05, 0x00, 0x3b};
452 
453   ProgressiveDecoder decoder;
454 
455   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
456   CFX_DIBAttribute attr;
457   FXCODEC_STATUS status =
458       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
459   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
460 
461   ASSERT_EQ(4, decoder.GetWidth());
462   ASSERT_EQ(2, decoder.GetHeight());
463   ASSERT_EQ(FXDIB_Format::kBgra, decoder.GetBitmapFormat());
464 
465   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
466   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
467                              decoder.GetBitmapFormat()));
468 
469   size_t frames;
470   std::tie(status, frames) = decoder.GetFrames();
471   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
472   ASSERT_EQ(1u, frames);
473 
474   status = DecodeToBitmap(decoder, bitmap);
475   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
476   EXPECT_THAT(bitmap->GetScanline(0),
477               ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0,
478                           0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff));
479   EXPECT_THAT(bitmap->GetScanline(1),
480               ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0,
481                           0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff));
482 }
483 
TEST_F(ProgressiveDecoderTest,GifDecodeAcrossSubblocks)484 TEST_F(ProgressiveDecoderTest, GifDecodeAcrossSubblocks) {
485   // This GIF contains a scanline split across 2 data sub-blocks. The decoder
486   // must continue decoding in the second sub-block.
487   static constexpr uint8_t kInput[] = {
488       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x02, 0x00,
489       0x80, 0x01, 0x00, 0x40, 0x80, 0xc0, 0x80, 0x80, 0x80, 0x2c,
490       0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x02,
491       0x02, 0x84, 0x6f, 0x01, 0x05, 0x00, 0x3b};
492 
493   ProgressiveDecoder decoder;
494 
495   auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(kInput);
496   CFX_DIBAttribute attr;
497   FXCODEC_STATUS status =
498       decoder.LoadImageInfo(std::move(source), FXCODEC_IMAGE_GIF, &attr, true);
499   ASSERT_EQ(FXCODEC_STATUS::kFrameReady, status);
500 
501   ASSERT_EQ(4, decoder.GetWidth());
502   ASSERT_EQ(2, decoder.GetHeight());
503   ASSERT_EQ(FXDIB_Format::kBgra, decoder.GetBitmapFormat());
504 
505   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
506   ASSERT_TRUE(bitmap->Create(decoder.GetWidth(), decoder.GetHeight(),
507                              decoder.GetBitmapFormat()));
508 
509   size_t frames;
510   std::tie(status, frames) = decoder.GetFrames();
511   ASSERT_EQ(FXCODEC_STATUS::kDecodeReady, status);
512   ASSERT_EQ(1u, frames);
513 
514   status = DecodeToBitmap(decoder, bitmap);
515   EXPECT_EQ(FXCODEC_STATUS::kDecodeFinished, status);
516   EXPECT_THAT(bitmap->GetScanline(0),
517               ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0,
518                           0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff));
519   EXPECT_THAT(bitmap->GetScanline(1),
520               ElementsAre(0xc0, 0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff, 0xc0,
521                           0x80, 0x40, 0xff, 0xc0, 0x80, 0x40, 0xff));
522 }
523 #endif  // PDF_ENABLE_XFA_GIF
524 
525 }  // namespace fxcodec
526