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