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