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