• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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