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