1 // Copyright 2017 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fxcodec/gif/cfx_gifcontext.h"
8
9 #include <stdint.h>
10 #include <string.h>
11
12 #include <algorithm>
13 #include <iterator>
14 #include <utility>
15
16 #include "core/fxcodec/cfx_codec_memory.h"
17 #include "core/fxcrt/data_vector.h"
18 #include "core/fxcrt/fx_system.h"
19 #include "core/fxcrt/stl_util.h"
20
21 namespace fxcodec {
22
23 namespace {
24
25 constexpr int32_t kGifInterlaceStep[4] = {8, 8, 4, 2};
26
27 } // namespace
28
CFX_GifContext(GifDecoder::Delegate * delegate)29 CFX_GifContext::CFX_GifContext(GifDecoder::Delegate* delegate)
30 : delegate_(delegate) {}
31
32 CFX_GifContext::~CFX_GifContext() = default;
33
ReadScanline(int32_t row_num,pdfium::span<uint8_t> row_buf)34 void CFX_GifContext::ReadScanline(int32_t row_num,
35 pdfium::span<uint8_t> row_buf) {
36 delegate_->GifReadScanline(row_num, row_buf);
37 }
38
GetRecordPosition(uint32_t cur_pos,int32_t left,int32_t top,int32_t width,int32_t height,int32_t pal_num,CFX_GifPalette * pal,int32_t trans_index,bool interlace)39 bool CFX_GifContext::GetRecordPosition(uint32_t cur_pos,
40 int32_t left,
41 int32_t top,
42 int32_t width,
43 int32_t height,
44 int32_t pal_num,
45 CFX_GifPalette* pal,
46 int32_t trans_index,
47 bool interlace) {
48 return delegate_->GifInputRecordPositionBuf(
49 cur_pos, FX_RECT(left, top, left + width, top + height), pal_num, pal,
50 trans_index, interlace);
51 }
52
ReadHeader()53 GifDecoder::Status CFX_GifContext::ReadHeader() {
54 GifDecoder::Status status = ReadGifSignature();
55 if (status != GifDecoder::Status::kSuccess)
56 return status;
57 return ReadLogicalScreenDescriptor();
58 }
59
GetFrame()60 GifDecoder::Status CFX_GifContext::GetFrame() {
61 GifDecoder::Status ret = GifDecoder::Status::kSuccess;
62 while (true) {
63 switch (decode_status_) {
64 case GIF_D_STATUS_TAIL:
65 return GifDecoder::Status::kSuccess;
66 case GIF_D_STATUS_SIG: {
67 uint8_t signature;
68 if (!ReadAllOrNone(&signature, sizeof(signature)))
69 return GifDecoder::Status::kUnfinished;
70
71 switch (signature) {
72 case GIF_SIG_EXTENSION:
73 SaveDecodingStatus(GIF_D_STATUS_EXT);
74 continue;
75 case GIF_SIG_IMAGE:
76 SaveDecodingStatus(GIF_D_STATUS_IMG_INFO);
77 continue;
78 case GIF_SIG_TRAILER:
79 SaveDecodingStatus(GIF_D_STATUS_TAIL);
80 return GifDecoder::Status::kSuccess;
81 default:
82 if (!input_buffer_->IsEOF()) {
83 // The Gif File has non_standard Tag!
84 SaveDecodingStatus(GIF_D_STATUS_SIG);
85 continue;
86 }
87 // The Gif File Doesn't have Trailer Tag!
88 return GifDecoder::Status::kSuccess;
89 }
90 }
91 case GIF_D_STATUS_EXT: {
92 uint8_t extension;
93 if (!ReadAllOrNone(&extension, sizeof(extension)))
94 return GifDecoder::Status::kUnfinished;
95
96 switch (extension) {
97 case GIF_BLOCK_CE:
98 SaveDecodingStatus(GIF_D_STATUS_EXT_CE);
99 continue;
100 case GIF_BLOCK_GCE:
101 SaveDecodingStatus(GIF_D_STATUS_EXT_GCE);
102 continue;
103 case GIF_BLOCK_PTE:
104 SaveDecodingStatus(GIF_D_STATUS_EXT_PTE);
105 continue;
106 default: {
107 int32_t status = GIF_D_STATUS_EXT_UNE;
108 if (extension == GIF_BLOCK_PTE) {
109 status = GIF_D_STATUS_EXT_PTE;
110 }
111 SaveDecodingStatus(status);
112 continue;
113 }
114 }
115 }
116 case GIF_D_STATUS_IMG_INFO: {
117 ret = DecodeImageInfo();
118 if (ret != GifDecoder::Status::kSuccess)
119 return ret;
120
121 continue;
122 }
123 case GIF_D_STATUS_IMG_DATA: {
124 uint8_t img_data_size;
125 size_t read_marker = input_buffer_->GetPosition();
126
127 if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
128 return GifDecoder::Status::kUnfinished;
129
130 while (img_data_size != GIF_BLOCK_TERMINAL) {
131 if (!input_buffer_->Seek(input_buffer_->GetPosition() +
132 img_data_size)) {
133 input_buffer_->Seek(read_marker);
134 return GifDecoder::Status::kUnfinished;
135 }
136
137 // This saving of the scan state on partial reads is why
138 // ScanForTerminalMarker() cannot be used here.
139 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
140 read_marker = input_buffer_->GetPosition();
141 if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
142 return GifDecoder::Status::kUnfinished;
143 }
144
145 SaveDecodingStatus(GIF_D_STATUS_SIG);
146 continue;
147 }
148 default: {
149 ret = DecodeExtension();
150 if (ret != GifDecoder::Status::kSuccess)
151 return ret;
152 break;
153 }
154 }
155 }
156 }
157
LoadFrame(size_t frame_num)158 GifDecoder::Status CFX_GifContext::LoadFrame(size_t frame_num) {
159 if (frame_num >= images_.size())
160 return GifDecoder::Status::kError;
161
162 CFX_GifImage* gif_image = images_[frame_num].get();
163 if (gif_image->image_info.height == 0)
164 return GifDecoder::Status::kError;
165
166 uint32_t gif_img_row_bytes = gif_image->image_info.width;
167 if (gif_img_row_bytes == 0)
168 return GifDecoder::Status::kError;
169
170 if (decode_status_ == GIF_D_STATUS_TAIL) {
171 gif_image->row_buffer.resize(gif_img_row_bytes);
172 CFX_GifGraphicControlExtension* gif_img_gce = gif_image->image_GCE.get();
173 int32_t loc_pal_num =
174 gif_image->image_info.local_flags.local_pal
175 ? (2 << gif_image->image_info.local_flags.pal_bits)
176 : 0;
177 CFX_GifPalette* pLocalPalette = gif_image->local_palettes.empty()
178 ? nullptr
179 : gif_image->local_palettes.data();
180 if (!gif_img_gce) {
181 bool bRes = GetRecordPosition(
182 gif_image->data_pos, gif_image->image_info.left,
183 gif_image->image_info.top, gif_image->image_info.width,
184 gif_image->image_info.height, loc_pal_num, pLocalPalette, -1,
185 gif_image->image_info.local_flags.interlace);
186 if (!bRes) {
187 gif_image->row_buffer.clear();
188 return GifDecoder::Status::kError;
189 }
190 } else {
191 bool bRes = GetRecordPosition(
192 gif_image->data_pos, gif_image->image_info.left,
193 gif_image->image_info.top, gif_image->image_info.width,
194 gif_image->image_info.height, loc_pal_num, pLocalPalette,
195 gif_image->image_GCE->gce_flags.transparency
196 ? static_cast<int32_t>(gif_image->image_GCE->trans_index)
197 : -1,
198 gif_image->image_info.local_flags.interlace);
199 if (!bRes) {
200 gif_image->row_buffer.clear();
201 return GifDecoder::Status::kError;
202 }
203 }
204
205 if (gif_image->code_exp > GIF_MAX_LZW_EXP) {
206 gif_image->row_buffer.clear();
207 return GifDecoder::Status::kError;
208 }
209
210 img_row_offset_ = 0;
211 img_row_avail_size_ = 0;
212 img_pass_num_ = 0;
213 gif_image->row_num = 0;
214 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
215 }
216
217 uint8_t img_data_size;
218 DataVector<uint8_t> img_data;
219 size_t read_marker = input_buffer_->GetPosition();
220
221 // TODO(crbug.com/pdfium/1793): This logic can be simplified a lot, but it
222 // probably makes more sense to switch to a different GIF decoder altogether.
223 if (decode_status_ == GIF_D_STATUS_IMG_DATA) {
224 if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
225 return GifDecoder::Status::kUnfinished;
226
227 if (img_data_size != GIF_BLOCK_TERMINAL) {
228 img_data.resize(img_data_size);
229 if (!ReadAllOrNone(img_data.data(), img_data_size)) {
230 input_buffer_->Seek(read_marker);
231 return GifDecoder::Status::kUnfinished;
232 }
233
234 if (!lzw_decompressor_) {
235 lzw_decompressor_ = LZWDecompressor::Create(GetPaletteExp(gif_image),
236 gif_image->code_exp);
237 if (!lzw_decompressor_) {
238 DecodingFailureAtTailCleanup(gif_image);
239 return GifDecoder::Status::kError;
240 }
241 }
242 lzw_decompressor_->SetSource(img_data.data(), img_data_size);
243
244 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
245 img_row_offset_ += img_row_avail_size_;
246 img_row_avail_size_ = gif_img_row_bytes - img_row_offset_;
247 LZWDecompressor::Status ret = lzw_decompressor_->Decode(
248 gif_image->row_buffer.data() + img_row_offset_, &img_row_avail_size_);
249 if (ret == LZWDecompressor::Status::kError) {
250 DecodingFailureAtTailCleanup(gif_image);
251 return GifDecoder::Status::kError;
252 }
253
254 while (ret != LZWDecompressor::Status::kError) {
255 if (ret == LZWDecompressor::Status::kSuccess) {
256 ReadScanline(gif_image->row_num, gif_image->row_buffer);
257 gif_image->row_buffer.clear();
258 SaveDecodingStatus(GIF_D_STATUS_TAIL);
259 return GifDecoder::Status::kSuccess;
260 }
261
262 if (ret == LZWDecompressor::Status::kUnfinished) {
263 read_marker = input_buffer_->GetPosition();
264 if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
265 return GifDecoder::Status::kUnfinished;
266
267 if (img_data_size != GIF_BLOCK_TERMINAL) {
268 img_data.resize(img_data_size);
269 if (!ReadAllOrNone(img_data.data(), img_data_size)) {
270 input_buffer_->Seek(read_marker);
271 return GifDecoder::Status::kUnfinished;
272 }
273
274 lzw_decompressor_->SetSource(img_data.data(), img_data_size);
275
276 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
277 img_row_offset_ += img_row_avail_size_;
278 img_row_avail_size_ = gif_img_row_bytes - img_row_offset_;
279 ret = lzw_decompressor_->Decode(
280 gif_image->row_buffer.data() + img_row_offset_,
281 &img_row_avail_size_);
282 }
283 }
284
285 if (ret == LZWDecompressor::Status::kInsufficientDestSize) {
286 if (gif_image->image_info.local_flags.interlace) {
287 ReadScanline(gif_image->row_num, gif_image->row_buffer);
288 gif_image->row_num += kGifInterlaceStep[img_pass_num_];
289 if (gif_image->row_num >=
290 static_cast<int32_t>(gif_image->image_info.height)) {
291 img_pass_num_++;
292 if (img_pass_num_ == std::size(kGifInterlaceStep)) {
293 DecodingFailureAtTailCleanup(gif_image);
294 return GifDecoder::Status::kError;
295 }
296 gif_image->row_num = kGifInterlaceStep[img_pass_num_] / 2;
297 }
298 } else {
299 ReadScanline(gif_image->row_num++, gif_image->row_buffer);
300 }
301
302 img_row_offset_ = 0;
303 img_row_avail_size_ = gif_img_row_bytes;
304 ret = lzw_decompressor_->Decode(
305 gif_image->row_buffer.data() + img_row_offset_,
306 &img_row_avail_size_);
307 }
308
309 if (ret == LZWDecompressor::Status::kError) {
310 DecodingFailureAtTailCleanup(gif_image);
311 return GifDecoder::Status::kError;
312 }
313 }
314 }
315 SaveDecodingStatus(GIF_D_STATUS_TAIL);
316 }
317 return GifDecoder::Status::kError;
318 }
319
SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory)320 void CFX_GifContext::SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory) {
321 input_buffer_ = std::move(codec_memory);
322 }
323
GetAvailInput() const324 uint32_t CFX_GifContext::GetAvailInput() const {
325 if (!input_buffer_)
326 return 0;
327
328 return pdfium::base::checked_cast<uint32_t>(input_buffer_->GetSize() -
329 input_buffer_->GetPosition());
330 }
331
ReadAllOrNone(uint8_t * dest,uint32_t size)332 bool CFX_GifContext::ReadAllOrNone(uint8_t* dest, uint32_t size) {
333 if (!input_buffer_ || !dest)
334 return false;
335
336 size_t read_marker = input_buffer_->GetPosition();
337 size_t read = input_buffer_->ReadBlock({dest, size});
338 if (read < size) {
339 input_buffer_->Seek(read_marker);
340 return false;
341 }
342
343 return true;
344 }
345
ReadGifSignature()346 GifDecoder::Status CFX_GifContext::ReadGifSignature() {
347 CFX_GifHeader header;
348 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&header), 6))
349 return GifDecoder::Status::kUnfinished;
350
351 if (strncmp(header.signature, kGifSignature87, 6) != 0 &&
352 strncmp(header.signature, kGifSignature89, 6) != 0) {
353 return GifDecoder::Status::kError;
354 }
355
356 return GifDecoder::Status::kSuccess;
357 }
358
ReadLogicalScreenDescriptor()359 GifDecoder::Status CFX_GifContext::ReadLogicalScreenDescriptor() {
360 CFX_GifLocalScreenDescriptor lsd;
361 size_t read_marker = input_buffer_->GetPosition();
362
363 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&lsd), sizeof(lsd)))
364 return GifDecoder::Status::kUnfinished;
365
366 if (lsd.global_flags.global_pal) {
367 uint32_t palette_count = unsigned(2 << lsd.global_flags.pal_bits);
368 if (lsd.bc_index >= palette_count)
369 return GifDecoder::Status::kError;
370 bc_index_ = lsd.bc_index;
371
372 uint32_t palette_size = palette_count * sizeof(CFX_GifPalette);
373 std::vector<CFX_GifPalette> palette(palette_count);
374 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(palette.data()),
375 palette_size)) {
376 // Roll back the read for the LSD
377 input_buffer_->Seek(read_marker);
378 return GifDecoder::Status::kUnfinished;
379 }
380
381 global_palette_exp_ = lsd.global_flags.pal_bits;
382 global_sort_flag_ = lsd.global_flags.sort_flag;
383 global_color_resolution_ = lsd.global_flags.color_resolution;
384 std::swap(global_palette_, palette);
385 }
386
387 width_ = static_cast<int>(
388 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.width)));
389 height_ = static_cast<int>(
390 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.height)));
391
392 return GifDecoder::Status::kSuccess;
393 }
394
SaveDecodingStatus(int32_t status)395 void CFX_GifContext::SaveDecodingStatus(int32_t status) {
396 decode_status_ = status;
397 }
398
DecodeExtension()399 GifDecoder::Status CFX_GifContext::DecodeExtension() {
400 size_t read_marker = input_buffer_->GetPosition();
401
402 switch (decode_status_) {
403 case GIF_D_STATUS_EXT_CE: {
404 if (!ScanForTerminalMarker()) {
405 input_buffer_->Seek(read_marker);
406 return GifDecoder::Status::kUnfinished;
407 }
408 break;
409 }
410 case GIF_D_STATUS_EXT_PTE: {
411 CFX_GifPlainTextExtension gif_pte;
412 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&gif_pte), sizeof(gif_pte)))
413 return GifDecoder::Status::kUnfinished;
414
415 graphic_control_extension_ = nullptr;
416 if (!ScanForTerminalMarker()) {
417 input_buffer_->Seek(read_marker);
418 return GifDecoder::Status::kUnfinished;
419 }
420 break;
421 }
422 case GIF_D_STATUS_EXT_GCE: {
423 CFX_GifGraphicControlExtension gif_gce;
424 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&gif_gce), sizeof(gif_gce)))
425 return GifDecoder::Status::kUnfinished;
426
427 if (!graphic_control_extension_.get())
428 graphic_control_extension_ =
429 std::make_unique<CFX_GifGraphicControlExtension>();
430 graphic_control_extension_->block_size = gif_gce.block_size;
431 graphic_control_extension_->gce_flags = gif_gce.gce_flags;
432 graphic_control_extension_->delay_time = FXSYS_UINT16_GET_LSBFIRST(
433 reinterpret_cast<uint8_t*>(&gif_gce.delay_time));
434 graphic_control_extension_->trans_index = gif_gce.trans_index;
435 break;
436 }
437 default: {
438 if (decode_status_ == GIF_D_STATUS_EXT_PTE)
439 graphic_control_extension_ = nullptr;
440 if (!ScanForTerminalMarker()) {
441 input_buffer_->Seek(read_marker);
442 return GifDecoder::Status::kUnfinished;
443 }
444 }
445 }
446
447 SaveDecodingStatus(GIF_D_STATUS_SIG);
448 return GifDecoder::Status::kSuccess;
449 }
450
DecodeImageInfo()451 GifDecoder::Status CFX_GifContext::DecodeImageInfo() {
452 if (width_ <= 0 || height_ <= 0)
453 return GifDecoder::Status::kError;
454
455 size_t read_marker = input_buffer_->GetPosition();
456 CFX_GifImageInfo img_info;
457 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&img_info), sizeof(img_info)))
458 return GifDecoder::Status::kUnfinished;
459
460 auto gif_image = std::make_unique<CFX_GifImage>();
461 gif_image->image_info.left =
462 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.left));
463 gif_image->image_info.top =
464 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.top));
465 gif_image->image_info.width =
466 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.width));
467 gif_image->image_info.height =
468 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.height));
469 gif_image->image_info.local_flags = img_info.local_flags;
470 if (gif_image->image_info.left + gif_image->image_info.width > width_ ||
471 gif_image->image_info.top + gif_image->image_info.height > height_)
472 return GifDecoder::Status::kError;
473
474 CFX_GifLocalFlags* gif_img_info_lf = &img_info.local_flags;
475 if (gif_img_info_lf->local_pal) {
476 gif_image->local_palette_exp = gif_img_info_lf->pal_bits;
477 uint32_t loc_pal_count = unsigned(2 << gif_img_info_lf->pal_bits);
478 std::vector<CFX_GifPalette> loc_pal(loc_pal_count);
479 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(loc_pal.data()),
480 loc_pal_count * sizeof(CFX_GifPalette))) {
481 input_buffer_->Seek(read_marker);
482 return GifDecoder::Status::kUnfinished;
483 }
484
485 gif_image->local_palettes = std::move(loc_pal);
486 }
487
488 uint8_t code_size;
489 if (!ReadAllOrNone(&code_size, sizeof(code_size))) {
490 input_buffer_->Seek(read_marker);
491 return GifDecoder::Status::kUnfinished;
492 }
493
494 gif_image->code_exp = code_size;
495 gif_image->data_pos = delegate_->GifCurrentPosition();
496 gif_image->image_GCE = nullptr;
497 if (graphic_control_extension_.get()) {
498 if (graphic_control_extension_->gce_flags.transparency) {
499 // Need to test that the color that is going to be transparent is actually
500 // in the palette being used.
501 if (graphic_control_extension_->trans_index >=
502 (2 << GetPaletteExp(gif_image.get()))) {
503 return GifDecoder::Status::kError;
504 }
505 }
506 gif_image->image_GCE = std::move(graphic_control_extension_);
507 graphic_control_extension_ = nullptr;
508 }
509
510 images_.push_back(std::move(gif_image));
511 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
512 return GifDecoder::Status::kSuccess;
513 }
514
DecodingFailureAtTailCleanup(CFX_GifImage * gif_image)515 void CFX_GifContext::DecodingFailureAtTailCleanup(CFX_GifImage* gif_image) {
516 gif_image->row_buffer.clear();
517 SaveDecodingStatus(GIF_D_STATUS_TAIL);
518 }
519
ScanForTerminalMarker()520 bool CFX_GifContext::ScanForTerminalMarker() {
521 uint8_t data_size;
522
523 if (!ReadAllOrNone(&data_size, sizeof(data_size)))
524 return false;
525
526 while (data_size != GIF_BLOCK_TERMINAL) {
527 if (!input_buffer_->Seek(input_buffer_->GetPosition() + data_size) ||
528 !ReadAllOrNone(&data_size, sizeof(data_size))) {
529 return false;
530 }
531 }
532
533 return true;
534 }
535
GetPaletteExp(CFX_GifImage * gif_image) const536 uint8_t CFX_GifContext::GetPaletteExp(CFX_GifImage* gif_image) const {
537 return !gif_image->local_palettes.empty() ? gif_image->local_palette_exp
538 : global_palette_exp_;
539 }
540
541 } // namespace fxcodec
542