Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cfx_gifcontext.cpp
Go to the documentation of this file.
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 <iterator>
14#include <utility>
15
16#include "core/fxcodec/cfx_codec_memory.h"
17#include "core/fxcrt/data_vector.h"
18#include "core/fxcrt/fx_system.h"
19#include "core/fxcrt/stl_util.h"
20
21namespace fxcodec {
22
23namespace {
24
25constexpr int32_t kGifInterlaceStep[4] = {8, 8, 4, 2};
26
27} // namespace
28
31
32CFX_GifContext::~CFX_GifContext() = default;
33
34void CFX_GifContext::ReadScanline(int32_t row_num,
35 pdfium::span<uint8_t> row_buf) {
36 delegate_->GifReadScanline(row_num, row_buf);
37}
38
39bool CFX_GifContext::GetRecordPosition(uint32_t cur_pos,
40 int32_t left,
41 int32_t top,
42 int32_t width,
43 int32_t height,
44 int32_t pal_num,
45 CFX_GifPalette* pal,
46 int32_t trans_index,
47 bool interlace) {
48 return delegate_->GifInputRecordPositionBuf(
49 cur_pos, FX_RECT(left, top, left + width, top + height), pal_num, pal,
50 trans_index, interlace);
51}
52
59
62 while (true) {
63 switch (decode_status_) {
66 case GIF_D_STATUS_SIG: {
67 uint8_t signature;
68 if (!ReadAllOrNone(&signature, sizeof(signature)))
70
71 switch (signature) {
73 SaveDecodingStatus(GIF_D_STATUS_EXT);
74 continue;
75 case GIF_SIG_IMAGE:
76 SaveDecodingStatus(GIF_D_STATUS_IMG_INFO);
77 continue;
78 case GIF_SIG_TRAILER:
79 SaveDecodingStatus(GIF_D_STATUS_TAIL);
81 default:
82 if (!input_buffer_->IsEOF()) {
83 // The Gif File has non_standard Tag!
84 SaveDecodingStatus(GIF_D_STATUS_SIG);
85 continue;
86 }
87 // The Gif File Doesn't have Trailer Tag!
89 }
90 }
91 case GIF_D_STATUS_EXT: {
92 uint8_t extension;
93 if (!ReadAllOrNone(&extension, sizeof(extension)))
95
96 switch (extension) {
97 case GIF_BLOCK_CE:
98 SaveDecodingStatus(GIF_D_STATUS_EXT_CE);
99 continue;
100 case GIF_BLOCK_GCE:
101 SaveDecodingStatus(GIF_D_STATUS_EXT_GCE);
102 continue;
103 case GIF_BLOCK_PTE:
104 SaveDecodingStatus(GIF_D_STATUS_EXT_PTE);
105 continue;
106 default: {
107 int32_t status = GIF_D_STATUS_EXT_UNE;
108 if (extension == GIF_BLOCK_PTE) {
109 status = GIF_D_STATUS_EXT_PTE;
110 }
111 SaveDecodingStatus(status);
112 continue;
113 }
114 }
115 }
117 ret = DecodeImageInfo();
119 return ret;
120
121 continue;
122 }
124 uint8_t img_data_size;
125 size_t read_marker = input_buffer_->GetPosition();
126
127 if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
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);
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(&img_data_size, sizeof(img_data_size)))
143 }
144
145 SaveDecodingStatus(GIF_D_STATUS_SIG);
146 continue;
147 }
148 default: {
149 ret = DecodeExtension();
151 return ret;
152 break;
153 }
154 }
155 }
156}
157
159 if (frame_num >= images_.size())
161
162 CFX_GifImage* gif_image = images_[frame_num].get();
163 if (gif_image->image_info.height == 0)
165
166 uint32_t gif_img_row_bytes = gif_image->image_info.width;
167 if (gif_img_row_bytes == 0)
169
171 gif_image->row_buffer.resize(gif_img_row_bytes);
172 CFX_GifGraphicControlExtension* gif_img_gce = gif_image->image_GCE.get();
173 int32_t loc_pal_num =
175 ? (2 << gif_image->image_info.local_flags.pal_bits)
176 : 0;
177 CFX_GifPalette* pLocalPalette = gif_image->local_palettes.empty()
178 ? nullptr
179 : gif_image->local_palettes.data();
180 if (!gif_img_gce) {
181 bool bRes = GetRecordPosition(
182 gif_image->data_pos, gif_image->image_info.left,
183 gif_image->image_info.top, gif_image->image_info.width,
184 gif_image->image_info.height, loc_pal_num, pLocalPalette, -1,
186 if (!bRes) {
187 gif_image->row_buffer.clear();
189 }
190 } else {
191 bool bRes = GetRecordPosition(
192 gif_image->data_pos, gif_image->image_info.left,
193 gif_image->image_info.top, gif_image->image_info.width,
194 gif_image->image_info.height, loc_pal_num, pLocalPalette,
195 gif_image->image_GCE->gce_flags.transparency
196 ? static_cast<int32_t>(gif_image->image_GCE->trans_index)
197 : -1,
199 if (!bRes) {
200 gif_image->row_buffer.clear();
202 }
203 }
204
205 if (gif_image->code_exp > GIF_MAX_LZW_EXP) {
206 gif_image->row_buffer.clear();
208 }
209
210 img_row_offset_ = 0;
212 img_pass_num_ = 0;
213 gif_image->row_num = 0;
214 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
215 }
216
217 uint8_t img_data_size;
218 DataVector<uint8_t> img_data;
219 size_t read_marker = input_buffer_->GetPosition();
220
221 // TODO(crbug.com/pdfium/1793): This logic can be simplified a lot, but it
222 // probably makes more sense to switch to a different GIF decoder altogether.
224 if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
226
227 if (img_data_size != GIF_BLOCK_TERMINAL) {
228 img_data.resize(img_data_size);
229 if (!ReadAllOrNone(img_data.data(), img_data_size)) {
230 input_buffer_->Seek(read_marker);
232 }
233
234 if (!lzw_decompressor_) {
235 lzw_decompressor_ = LZWDecompressor::Create(GetPaletteExp(gif_image),
236 gif_image->code_exp);
237 if (!lzw_decompressor_) {
238 DecodingFailureAtTailCleanup(gif_image);
240 }
241 }
242 lzw_decompressor_->SetSource(img_data.data(), img_data_size);
243
244 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
246 img_row_avail_size_ = gif_img_row_bytes - img_row_offset_;
247 LZWDecompressor::Status ret = lzw_decompressor_->Decode(
248 gif_image->row_buffer.data() + img_row_offset_, &img_row_avail_size_);
250 DecodingFailureAtTailCleanup(gif_image);
252 }
253
254 while (ret != LZWDecompressor::Status::kError) {
256 ReadScanline(gif_image->row_num, gif_image->row_buffer);
257 gif_image->row_buffer.clear();
258 SaveDecodingStatus(GIF_D_STATUS_TAIL);
260 }
261
263 read_marker = input_buffer_->GetPosition();
264 if (!ReadAllOrNone(&img_data_size, sizeof(img_data_size)))
266
267 if (img_data_size != GIF_BLOCK_TERMINAL) {
268 img_data.resize(img_data_size);
269 if (!ReadAllOrNone(img_data.data(), img_data_size)) {
270 input_buffer_->Seek(read_marker);
272 }
273
274 lzw_decompressor_->SetSource(img_data.data(), img_data_size);
275
276 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
278 img_row_avail_size_ = gif_img_row_bytes - img_row_offset_;
279 ret = lzw_decompressor_->Decode(
280 gif_image->row_buffer.data() + img_row_offset_,
281 &img_row_avail_size_);
282 }
283 }
284
287 ReadScanline(gif_image->row_num, gif_image->row_buffer);
288 gif_image->row_num += kGifInterlaceStep[img_pass_num_];
289 if (gif_image->row_num >=
290 static_cast<int32_t>(gif_image->image_info.height)) {
292 if (img_pass_num_ == std::size(kGifInterlaceStep)) {
293 DecodingFailureAtTailCleanup(gif_image);
295 }
296 gif_image->row_num = kGifInterlaceStep[img_pass_num_] / 2;
297 }
298 } else {
299 ReadScanline(gif_image->row_num++, gif_image->row_buffer);
300 }
301
302 img_row_offset_ = 0;
303 img_row_avail_size_ = gif_img_row_bytes;
304 ret = lzw_decompressor_->Decode(
305 gif_image->row_buffer.data() + img_row_offset_,
306 &img_row_avail_size_);
307 }
308
310 DecodingFailureAtTailCleanup(gif_image);
312 }
313 }
314 }
315 SaveDecodingStatus(GIF_D_STATUS_TAIL);
316 }
318}
319
320void CFX_GifContext::SetInputBuffer(RetainPtr<CFX_CodecMemory> codec_memory) {
321 input_buffer_ = std::move(codec_memory);
322}
323
324uint32_t CFX_GifContext::GetAvailInput() const {
325 if (!input_buffer_)
326 return 0;
327
328 return pdfium::base::checked_cast<uint32_t>(input_buffer_->GetSize() -
329 input_buffer_->GetPosition());
330}
331
332bool CFX_GifContext::ReadAllOrNone(uint8_t* dest, uint32_t size) {
333 if (!input_buffer_ || !dest)
334 return false;
335
336 size_t read_marker = input_buffer_->GetPosition();
337 size_t read = input_buffer_->ReadBlock({dest, size});
338 if (read < size) {
339 input_buffer_->Seek(read_marker);
340 return false;
341 }
342
343 return true;
344}
345
347 CFX_GifHeader header;
348 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&header), 6))
350
351 if (strncmp(header.signature, kGifSignature87, 6) != 0 &&
352 strncmp(header.signature, kGifSignature89, 6) != 0) {
354 }
355
357}
358
361 size_t read_marker = input_buffer_->GetPosition();
362
363 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&lsd), sizeof(lsd)))
365
367 uint32_t palette_count = unsigned(2 << lsd.global_flags.pal_bits);
368 if (lsd.bc_index >= palette_count)
370 bc_index_ = lsd.bc_index;
371
372 std::vector<CFX_GifPalette> palette(palette_count);
373 auto bytes = pdfium::as_writable_bytes(pdfium::make_span(palette));
374 if (!ReadAllOrNone(bytes.data(),
375 pdfium::base::checked_cast<uint32_t>(bytes.size()))) {
376 // Roll back the read for the LSD
377 input_buffer_->Seek(read_marker);
379 }
380
384 std::swap(global_palette_, palette);
385 }
386
387 width_ = static_cast<int>(
388 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.width)));
389 height_ = static_cast<int>(
390 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd.height)));
391
393}
394
395void CFX_GifContext::SaveDecodingStatus(int32_t status) {
396 decode_status_ = status;
397}
398
399GifDecoder::Status CFX_GifContext::DecodeExtension() {
400 size_t read_marker = input_buffer_->GetPosition();
401
402 switch (decode_status_) {
403 case GIF_D_STATUS_EXT_CE: {
404 if (!ScanForTerminalMarker()) {
405 input_buffer_->Seek(read_marker);
407 }
408 break;
409 }
412 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&gif_pte), sizeof(gif_pte)))
414
415 graphic_control_extension_ = nullptr;
416 if (!ScanForTerminalMarker()) {
417 input_buffer_->Seek(read_marker);
419 }
420 break;
421 }
424 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&gif_gce), sizeof(gif_gce)))
426
427 if (!graphic_control_extension_.get())
428 graphic_control_extension_ =
429 std::make_unique<CFX_GifGraphicControlExtension>();
430 graphic_control_extension_->block_size = gif_gce.block_size;
431 graphic_control_extension_->gce_flags = gif_gce.gce_flags;
432 graphic_control_extension_->delay_time = FXSYS_UINT16_GET_LSBFIRST(
433 reinterpret_cast<uint8_t*>(&gif_gce.delay_time));
434 graphic_control_extension_->trans_index = gif_gce.trans_index;
435 break;
436 }
437 default: {
438 if (decode_status_ == GIF_D_STATUS_EXT_PTE)
439 graphic_control_extension_ = nullptr;
440 if (!ScanForTerminalMarker()) {
441 input_buffer_->Seek(read_marker);
443 }
444 }
445 }
446
447 SaveDecodingStatus(GIF_D_STATUS_SIG);
449}
450
451GifDecoder::Status CFX_GifContext::DecodeImageInfo() {
452 if (width_ <= 0 || height_ <= 0)
454
455 size_t read_marker = input_buffer_->GetPosition();
456 CFX_GifImageInfo img_info;
457 if (!ReadAllOrNone(reinterpret_cast<uint8_t*>(&img_info), sizeof(img_info)))
459
460 auto gif_image = std::make_unique<CFX_GifImage>();
461 gif_image->image_info.left =
462 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.left));
463 gif_image->image_info.top =
464 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.top));
465 gif_image->image_info.width =
466 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.width));
467 gif_image->image_info.height =
468 FXSYS_UINT16_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info.height));
469 gif_image->image_info.local_flags = img_info.local_flags;
470 if (gif_image->image_info.left + gif_image->image_info.width > width_ ||
471 gif_image->image_info.top + gif_image->image_info.height > height_)
473
474 CFX_GifLocalFlags* gif_img_info_lf = &img_info.local_flags;
475 if (gif_img_info_lf->local_pal) {
476 gif_image->local_palette_exp = gif_img_info_lf->pal_bits;
477 uint32_t loc_pal_count = unsigned(2 << gif_img_info_lf->pal_bits);
478 std::vector<CFX_GifPalette> loc_pal(loc_pal_count);
479 auto bytes = pdfium::as_writable_bytes(pdfium::make_span(loc_pal));
480 if (!ReadAllOrNone(bytes.data(),
481 pdfium::base::checked_cast<uint32_t>(bytes.size()))) {
482 input_buffer_->Seek(read_marker);
484 }
485
486 gif_image->local_palettes = std::move(loc_pal);
487 }
488
489 uint8_t code_size;
490 if (!ReadAllOrNone(&code_size, sizeof(code_size))) {
491 input_buffer_->Seek(read_marker);
493 }
494
495 gif_image->code_exp = code_size;
496 gif_image->data_pos = delegate_->GifCurrentPosition();
497 gif_image->image_GCE = nullptr;
498 if (graphic_control_extension_.get()) {
499 if (graphic_control_extension_->gce_flags.transparency) {
500 // Need to test that the color that is going to be transparent is actually
501 // in the palette being used.
502 if (graphic_control_extension_->trans_index >=
503 (2 << GetPaletteExp(gif_image.get()))) {
505 }
506 }
507 gif_image->image_GCE = std::move(graphic_control_extension_);
508 graphic_control_extension_ = nullptr;
509 }
510
511 images_.push_back(std::move(gif_image));
512 SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
514}
515
516void CFX_GifContext::DecodingFailureAtTailCleanup(CFX_GifImage* gif_image) {
517 gif_image->row_buffer.clear();
518 SaveDecodingStatus(GIF_D_STATUS_TAIL);
519}
520
521bool CFX_GifContext::ScanForTerminalMarker() {
522 uint8_t data_size;
523
524 if (!ReadAllOrNone(&data_size, sizeof(data_size)))
525 return false;
526
527 while (data_size != GIF_BLOCK_TERMINAL) {
528 if (!input_buffer_->Seek(input_buffer_->GetPosition() + data_size) ||
529 !ReadAllOrNone(&data_size, sizeof(data_size))) {
530 return false;
531 }
532 }
533
534 return true;
535}
536
537uint8_t CFX_GifContext::GetPaletteExp(CFX_GifImage* gif_image) const {
538 return !gif_image->local_palettes.empty() ? gif_image->local_palette_exp
540}
541
542} // namespace fxcodec
#define GIF_SIG_EXTENSION
Definition cfx_gif.h:20
#define GIF_BLOCK_GCE
Definition cfx_gif.h:23
#define GIF_D_STATUS_TAIL
Definition cfx_gif.h:30
#define GIF_D_STATUS_EXT
Definition cfx_gif.h:31
#define GIF_D_STATUS_EXT_UNE
Definition cfx_gif.h:35
#define GIF_SIG_TRAILER
Definition cfx_gif.h:22
#define GIF_D_STATUS_EXT_GCE
Definition cfx_gif.h:33
#define GIF_SIG_IMAGE
Definition cfx_gif.h:21
#define GIF_D_STATUS_EXT_CE
Definition cfx_gif.h:32
#define GIF_D_STATUS_SIG
Definition cfx_gif.h:29
#define GIF_D_STATUS_IMG_INFO
Definition cfx_gif.h:36
#define GIF_BLOCK_PTE
Definition cfx_gif.h:24
#define GIF_MAX_LZW_EXP
Definition cfx_gif.h:27
#define GIF_BLOCK_TERMINAL
Definition cfx_gif.h:26
#define GIF_BLOCK_CE
Definition cfx_gif.h:25
#define GIF_D_STATUS_IMG_DATA
Definition cfx_gif.h:37
#define GIF_D_STATUS_EXT_PTE
Definition cfx_gif.h:34
CFX_GifContext(GifDecoder::Delegate *delegate)
GifDecoder::Status GetFrame()
void SetInputBuffer(RetainPtr< CFX_CodecMemory > codec_memory)
GifDecoder::Status LoadFrame(size_t frame_num)
uint32_t GetAvailInput() const
void ReadScanline(int32_t row_num, pdfium::span< uint8_t > row_buf)
bool 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 trans_index, bool interlace)
GifDecoder::Status ReadHeader()
GifDecoder::Status ReadLogicalScreenDescriptor()
bool ReadAllOrNone(uint8_t *dest, uint32_t size)
GifDecoder::Status ReadGifSignature()
uint8_t global_pal
Definition cfx_gif.h:44
uint8_t color_resolution
Definition cfx_gif.h:43
uint8_t sort_flag
Definition cfx_gif.h:42
uint8_t pal_bits
Definition cfx_gif.h:41
uint16_t height
Definition cfx_gif.h:71
uint16_t top
Definition cfx_gif.h:69
uint16_t left
Definition cfx_gif.h:68
uint16_t width
Definition cfx_gif.h:70
CFX_GifLocalFlags local_flags
Definition cfx_gif.h:72
uint8_t code_exp
Definition cfx_gif.h:123
uint32_t data_pos
Definition cfx_gif.h:124
CFX_GifImageInfo image_info
Definition cfx_gif.h:121
uint8_t local_palette_exp
Definition cfx_gif.h:122
int32_t row_num
Definition cfx_gif.h:125
uint8_t local_pal
Definition cfx_gif.h:52
uint8_t interlace
Definition cfx_gif.h:51
uint8_t pal_bits
Definition cfx_gif.h:48
CFX_GifGlobalFlags global_flags
Definition cfx_gif.h:62