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