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