| // Copyright 2017 PDFium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
| |
| #include "core/fxcodec/gif/cfx_gifcontext.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "core/fxcodec/codec/ccodec_gifmodule.h" |
| #include "core/fxcodec/gif/cfx_gif.h" |
| #include "third_party/base/ptr_util.h" |
| #include "third_party/base/stl_util.h" |
| |
| namespace { |
| |
| const int32_t s_gif_interlace_step[4] = {8, 8, 4, 2}; |
| |
| } // namespace |
| |
| CFX_GifContext::CFX_GifContext(CCodec_GifModule* gif_module, |
| CCodec_GifModule::Delegate* delegate) |
| : gif_module_(gif_module), |
| delegate_(delegate), |
| global_pal_exp_(0), |
| img_row_offset_(0), |
| img_row_avail_size_(0), |
| avail_in_(0), |
| decode_status_(GIF_D_STATUS_SIG), |
| skip_size_(0), |
| next_in_(nullptr), |
| width_(0), |
| height_(0), |
| bc_index_(0), |
| pixel_aspect_(0), |
| global_sort_flag_(0), |
| global_color_resolution_(0), |
| img_pass_num_(0) {} |
| |
| CFX_GifContext::~CFX_GifContext() {} |
| |
| void CFX_GifContext::RecordCurrentPosition(uint32_t* cur_pos) { |
| delegate_->GifRecordCurrentPosition(*cur_pos); |
| } |
| |
| void CFX_GifContext::ReadScanline(int32_t row_num, uint8_t* row_buf) { |
| delegate_->GifReadScanline(row_num, row_buf); |
| } |
| |
| bool CFX_GifContext::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) { |
| return delegate_->GifInputRecordPositionBuf( |
| cur_pos, FX_RECT(left, top, left + width, top + height), pal_num, pal, |
| delay_time, user_input, trans_index, disposal_method, interlace); |
| } |
| |
| CFX_GifDecodeStatus CFX_GifContext::ReadHeader() { |
| CFX_GifDecodeStatus status = ReadGifSignature(); |
| if (status != CFX_GifDecodeStatus::Success) |
| return status; |
| return ReadLogicalScreenDescriptor(); |
| } |
| |
| CFX_GifDecodeStatus CFX_GifContext::GetFrame() { |
| CFX_GifDecodeStatus ret = CFX_GifDecodeStatus::Success; |
| while (true) { |
| switch (decode_status_) { |
| case GIF_D_STATUS_TAIL: |
| return CFX_GifDecodeStatus::Success; |
| case GIF_D_STATUS_SIG: { |
| uint8_t* signature = nullptr; |
| if (!ReadData(&signature, 1)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| switch (*signature) { |
| case GIF_SIG_EXTENSION: |
| SaveDecodingStatus(GIF_D_STATUS_EXT); |
| continue; |
| case GIF_SIG_IMAGE: |
| SaveDecodingStatus(GIF_D_STATUS_IMG_INFO); |
| continue; |
| case GIF_SIG_TRAILER: |
| SaveDecodingStatus(GIF_D_STATUS_TAIL); |
| return CFX_GifDecodeStatus::Success; |
| default: |
| if (avail_in_) { |
| // The Gif File has non_standard Tag! |
| SaveDecodingStatus(GIF_D_STATUS_SIG); |
| continue; |
| } |
| // The Gif File Doesn't have Trailer Tag! |
| return CFX_GifDecodeStatus::Success; |
| } |
| } |
| case GIF_D_STATUS_EXT: { |
| uint8_t* extension = nullptr; |
| if (!ReadData(&extension, 1)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| switch (*extension) { |
| case GIF_BLOCK_CE: |
| SaveDecodingStatus(GIF_D_STATUS_EXT_CE); |
| continue; |
| case GIF_BLOCK_GCE: |
| SaveDecodingStatus(GIF_D_STATUS_EXT_GCE); |
| continue; |
| case GIF_BLOCK_PTE: |
| SaveDecodingStatus(GIF_D_STATUS_EXT_PTE); |
| continue; |
| default: { |
| int32_t status = GIF_D_STATUS_EXT_UNE; |
| if (*extension == GIF_BLOCK_PTE) { |
| status = GIF_D_STATUS_EXT_PTE; |
| } |
| SaveDecodingStatus(status); |
| continue; |
| } |
| } |
| } |
| case GIF_D_STATUS_IMG_INFO: { |
| ret = DecodeImageInfo(); |
| if (ret != CFX_GifDecodeStatus::Success) |
| return ret; |
| |
| continue; |
| } |
| case GIF_D_STATUS_IMG_DATA: { |
| uint8_t* img_data_size = nullptr; |
| uint8_t* img_data = nullptr; |
| uint32_t skip_size_org = skip_size_; |
| if (!ReadData(&img_data_size, 1)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| while (*img_data_size != GIF_BLOCK_TERMINAL) { |
| if (!ReadData(&img_data, *img_data_size)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); |
| skip_size_org = skip_size_; |
| if (!ReadData(&img_data_size, 1)) |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| SaveDecodingStatus(GIF_D_STATUS_SIG); |
| continue; |
| } |
| default: { |
| ret = DecodeExtension(); |
| if (ret != CFX_GifDecodeStatus::Success) |
| return ret; |
| break; |
| } |
| } |
| } |
| return CFX_GifDecodeStatus::Success; |
| } |
| |
| CFX_GifDecodeStatus CFX_GifContext::LoadFrame(int32_t frame_num) { |
| if (!pdfium::IndexInBounds(images_, frame_num)) |
| return CFX_GifDecodeStatus::Error; |
| |
| uint8_t* img_data_size = nullptr; |
| uint8_t* img_data = nullptr; |
| uint32_t skip_size_org = skip_size_; |
| CFX_GifImage* gif_image = images_[static_cast<size_t>(frame_num)].get(); |
| if (gif_image->image_info.height == 0) |
| return CFX_GifDecodeStatus::Error; |
| |
| uint32_t gif_img_row_bytes = gif_image->image_info.width; |
| if (gif_img_row_bytes == 0) |
| return CFX_GifDecodeStatus::Error; |
| |
| if (decode_status_ == GIF_D_STATUS_TAIL) { |
| gif_image->row_buffer.resize(gif_img_row_bytes); |
| CFX_GifGraphicControlExtension* gif_img_gce = gif_image->image_GCE.get(); |
| int32_t loc_pal_num = |
| gif_image->image_info.local_flags.local_pal |
| ? (2 << gif_image->image_info.local_flags.pal_bits) |
| : 0; |
| avail_in_ = 0; |
| CFX_GifPalette* pLocalPalette = gif_image->local_palettes.empty() |
| ? nullptr |
| : gif_image->local_palettes.data(); |
| if (!gif_img_gce) { |
| bool bRes = GetRecordPosition( |
| gif_image->data_pos, gif_image->image_info.left, |
| gif_image->image_info.top, gif_image->image_info.width, |
| gif_image->image_info.height, loc_pal_num, pLocalPalette, 0, 0, -1, 0, |
| gif_image->image_info.local_flags.interlace); |
| if (!bRes) { |
| gif_image->row_buffer.clear(); |
| return CFX_GifDecodeStatus::Error; |
| } |
| } else { |
| bool bRes = GetRecordPosition( |
| gif_image->data_pos, gif_image->image_info.left, |
| gif_image->image_info.top, gif_image->image_info.width, |
| gif_image->image_info.height, loc_pal_num, pLocalPalette, |
| static_cast<int32_t>(gif_image->image_GCE->delay_time), |
| gif_image->image_GCE->gce_flags.user_input, |
| gif_image->image_GCE->gce_flags.transparency |
| ? static_cast<int32_t>(gif_image->image_GCE->trans_index) |
| : -1, |
| static_cast<int32_t>(gif_image->image_GCE->gce_flags.disposal_method), |
| gif_image->image_info.local_flags.interlace); |
| if (!bRes) { |
| gif_image->row_buffer.clear(); |
| return CFX_GifDecodeStatus::Error; |
| } |
| } |
| |
| if (gif_image->code_exp > GIF_MAX_LZW_EXP) { |
| gif_image->row_buffer.clear(); |
| return CFX_GifDecodeStatus::Error; |
| } |
| |
| img_row_offset_ = 0; |
| img_row_avail_size_ = 0; |
| img_pass_num_ = 0; |
| gif_image->row_num = 0; |
| SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); |
| } |
| |
| if (decode_status_ == GIF_D_STATUS_IMG_DATA) { |
| if (!ReadData(&img_data_size, 1)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| if (*img_data_size != GIF_BLOCK_TERMINAL) { |
| if (!ReadData(&img_data, *img_data_size)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| if (!lzw_decompressor_.get()) |
| lzw_decompressor_ = CFX_LZWDecompressor::Create( |
| !gif_image->local_palettes.empty() ? gif_image->local_pallette_exp |
| : global_pal_exp_, |
| gif_image->code_exp); |
| SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); |
| img_row_offset_ += img_row_avail_size_; |
| img_row_avail_size_ = gif_img_row_bytes - img_row_offset_; |
| CFX_GifDecodeStatus ret = |
| lzw_decompressor_.get() |
| ? lzw_decompressor_->Decode( |
| img_data, *img_data_size, |
| gif_image->row_buffer.data() + img_row_offset_, |
| &img_row_avail_size_) |
| : CFX_GifDecodeStatus::Error; |
| if (ret == CFX_GifDecodeStatus::Error) { |
| DecodingFailureAtTailCleanup(gif_image); |
| return CFX_GifDecodeStatus::Error; |
| } |
| while (ret != CFX_GifDecodeStatus::Error) { |
| if (ret == CFX_GifDecodeStatus::Success) { |
| ReadScanline(gif_image->row_num, gif_image->row_buffer.data()); |
| gif_image->row_buffer.clear(); |
| SaveDecodingStatus(GIF_D_STATUS_TAIL); |
| return CFX_GifDecodeStatus::Success; |
| } |
| if (ret == CFX_GifDecodeStatus::Unfinished) { |
| skip_size_org = skip_size_; |
| if (!ReadData(&img_data_size, 1)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| if (*img_data_size != GIF_BLOCK_TERMINAL) { |
| if (!ReadData(&img_data, *img_data_size)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| if (!lzw_decompressor_.get()) |
| lzw_decompressor_ = CFX_LZWDecompressor::Create( |
| !gif_image->local_palettes.empty() |
| ? gif_image->local_pallette_exp |
| : global_pal_exp_, |
| gif_image->code_exp); |
| SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); |
| img_row_offset_ += img_row_avail_size_; |
| img_row_avail_size_ = gif_img_row_bytes - img_row_offset_; |
| ret = lzw_decompressor_.get() |
| ? lzw_decompressor_->Decode( |
| img_data, *img_data_size, |
| gif_image->row_buffer.data() + img_row_offset_, |
| &img_row_avail_size_) |
| : CFX_GifDecodeStatus::Error; |
| } |
| } |
| if (ret == CFX_GifDecodeStatus::InsufficientDestSize) { |
| if (gif_image->image_info.local_flags.interlace) { |
| ReadScanline(gif_image->row_num, gif_image->row_buffer.data()); |
| gif_image->row_num += s_gif_interlace_step[img_pass_num_]; |
| if (gif_image->row_num >= |
| static_cast<int32_t>(gif_image->image_info.height)) { |
| img_pass_num_++; |
| if (img_pass_num_ == FX_ArraySize(s_gif_interlace_step)) { |
| DecodingFailureAtTailCleanup(gif_image); |
| return CFX_GifDecodeStatus::Error; |
| } |
| gif_image->row_num = s_gif_interlace_step[img_pass_num_] / 2; |
| } |
| } else { |
| ReadScanline(gif_image->row_num++, gif_image->row_buffer.data()); |
| } |
| img_row_offset_ = 0; |
| img_row_avail_size_ = gif_img_row_bytes; |
| ret = lzw_decompressor_.get() |
| ? lzw_decompressor_->Decode( |
| img_data, *img_data_size, |
| gif_image->row_buffer.data() + img_row_offset_, |
| &img_row_avail_size_) |
| : CFX_GifDecodeStatus::Error; |
| } |
| if (ret == CFX_GifDecodeStatus::Error) { |
| DecodingFailureAtTailCleanup(gif_image); |
| return CFX_GifDecodeStatus::Error; |
| } |
| } |
| } |
| SaveDecodingStatus(GIF_D_STATUS_TAIL); |
| } |
| return CFX_GifDecodeStatus::Error; |
| } |
| |
| void CFX_GifContext::SetInputBuffer(uint8_t* src_buf, uint32_t src_size) { |
| next_in_ = src_buf; |
| avail_in_ = src_size; |
| skip_size_ = 0; |
| } |
| |
| uint32_t CFX_GifContext::GetAvailInput(uint8_t** avail_buf) const { |
| if (avail_buf) { |
| *avail_buf = nullptr; |
| if (avail_in_ > 0) |
| *avail_buf = next_in_; |
| } |
| return avail_in_; |
| } |
| |
| uint8_t* CFX_GifContext::ReadData(uint8_t** des_buf_pp, uint32_t data_size) { |
| if (!next_in_) |
| return nullptr; |
| if (avail_in_ <= skip_size_) |
| return nullptr; |
| if (!des_buf_pp) |
| return nullptr; |
| if (data_size == 0) |
| return nullptr; |
| if (avail_in_ - skip_size_ < data_size) |
| return nullptr; |
| |
| *des_buf_pp = next_in_ + skip_size_; |
| skip_size_ += data_size; |
| return *des_buf_pp; |
| } |
| |
| CFX_GifDecodeStatus CFX_GifContext::ReadGifSignature() { |
| CFX_GifHeader* header = nullptr; |
| uint32_t skip_size_org = skip_size_; |
| if (!ReadData(reinterpret_cast<uint8_t**>(&header), 6)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| if (strncmp(header->signature, kGifSignature87, 6) != 0 && |
| strncmp(header->signature, kGifSignature89, 6) != 0) |
| return CFX_GifDecodeStatus::Error; |
| |
| return CFX_GifDecodeStatus::Success; |
| } |
| |
| CFX_GifDecodeStatus CFX_GifContext::ReadLogicalScreenDescriptor() { |
| CFX_GifLocalScreenDescriptor* lsd = nullptr; |
| uint32_t skip_size_org = skip_size_; |
| if (!ReadData(reinterpret_cast<uint8_t**>(&lsd), 7)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| if (lsd->global_flags.global_pal) { |
| uint32_t palette_count = unsigned(2 << lsd->global_flags.pal_bits); |
| if (lsd->bc_index >= palette_count) |
| return CFX_GifDecodeStatus::Error; |
| bc_index_ = lsd->bc_index; |
| |
| uint32_t palette_size = palette_count * 3u; |
| uint8_t* palette = nullptr; |
| if (!ReadData(&palette, palette_size)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| global_pal_exp_ = lsd->global_flags.pal_bits; |
| global_sort_flag_ = lsd->global_flags.sort_flag; |
| global_color_resolution_ = lsd->global_flags.color_resolution; |
| global_palette_.resize(palette_count); |
| memcpy(global_palette_.data(), palette, palette_size); |
| } |
| |
| width_ = static_cast<int>( |
| FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd->width))); |
| height_ = static_cast<int>( |
| FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&lsd->height))); |
| |
| pixel_aspect_ = lsd->pixel_aspect; |
| return CFX_GifDecodeStatus::Success; |
| } |
| |
| void CFX_GifContext::SaveDecodingStatus(int32_t status) { |
| decode_status_ = status; |
| next_in_ += skip_size_; |
| avail_in_ -= skip_size_; |
| skip_size_ = 0; |
| } |
| |
| CFX_GifDecodeStatus CFX_GifContext::DecodeExtension() { |
| uint8_t* data_size = nullptr; |
| uint8_t* data_buf = nullptr; |
| uint32_t skip_size_org = skip_size_; |
| switch (decode_status_) { |
| case GIF_D_STATUS_EXT_CE: { |
| if (!ReadData(&data_size, 1)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| cmt_data_.clear(); |
| while (*data_size != GIF_BLOCK_TERMINAL) { |
| uint8_t block_size = *data_size; |
| if (!ReadData(&data_buf, *data_size) || !ReadData(&data_size, 1)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| cmt_data_ += ByteString(data_buf, block_size); |
| } |
| break; |
| } |
| case GIF_D_STATUS_EXT_PTE: { |
| CFX_GifPlainTextExtension* gif_pte = nullptr; |
| if (!ReadData(reinterpret_cast<uint8_t**>(&gif_pte), 13)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| graphic_control_extension_ = nullptr; |
| if (!ReadData(&data_size, 1)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| while (*data_size != GIF_BLOCK_TERMINAL) { |
| if (!ReadData(&data_buf, *data_size) || !ReadData(&data_size, 1)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| } |
| break; |
| } |
| case GIF_D_STATUS_EXT_GCE: { |
| CFX_GifGraphicControlExtension* gif_gce = nullptr; |
| if (!ReadData(reinterpret_cast<uint8_t**>(&gif_gce), 6)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| if (!graphic_control_extension_.get()) |
| graphic_control_extension_ = |
| pdfium::MakeUnique<CFX_GifGraphicControlExtension>(); |
| graphic_control_extension_->block_size = gif_gce->block_size; |
| graphic_control_extension_->gce_flags = gif_gce->gce_flags; |
| graphic_control_extension_->delay_time = |
| FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&gif_gce->delay_time)); |
| graphic_control_extension_->trans_index = gif_gce->trans_index; |
| break; |
| } |
| default: { |
| if (decode_status_ == GIF_D_STATUS_EXT_PTE) |
| graphic_control_extension_ = nullptr; |
| if (!ReadData(&data_size, 1)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| while (*data_size != GIF_BLOCK_TERMINAL) { |
| if (!ReadData(&data_buf, *data_size) || !ReadData(&data_size, 1)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| } |
| } |
| } |
| SaveDecodingStatus(GIF_D_STATUS_SIG); |
| return CFX_GifDecodeStatus::Success; |
| } |
| |
| CFX_GifDecodeStatus CFX_GifContext::DecodeImageInfo() { |
| if (width_ <= 0 || height_ <= 0) |
| return CFX_GifDecodeStatus::Error; |
| |
| uint32_t skip_size_org = skip_size_; |
| CFX_CFX_GifImageInfo* img_info = nullptr; |
| if (!ReadData(reinterpret_cast<uint8_t**>(&img_info), 9)) |
| return CFX_GifDecodeStatus::Unfinished; |
| |
| auto gif_image = pdfium::MakeUnique<CFX_GifImage>(); |
| gif_image->image_info.left = |
| FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info->left)); |
| gif_image->image_info.top = |
| FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info->top)); |
| gif_image->image_info.width = |
| FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info->width)); |
| gif_image->image_info.height = |
| FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&img_info->height)); |
| gif_image->image_info.local_flags = img_info->local_flags; |
| if (gif_image->image_info.left + gif_image->image_info.width > width_ || |
| gif_image->image_info.top + gif_image->image_info.height > height_) |
| return CFX_GifDecodeStatus::Error; |
| |
| CFX_GifLocalFlags* gif_img_info_lf = &img_info->local_flags; |
| if (gif_img_info_lf->local_pal) { |
| gif_image->local_pallette_exp = gif_img_info_lf->pal_bits; |
| uint32_t loc_pal_size = unsigned(2 << gif_img_info_lf->pal_bits) * 3u; |
| uint8_t* loc_pal = nullptr; |
| if (!ReadData(&loc_pal, loc_pal_size)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| gif_image->local_palettes = std::vector<CFX_GifPalette>(loc_pal_size / 3); |
| std::copy(loc_pal, loc_pal + loc_pal_size, |
| reinterpret_cast<uint8_t*>(gif_image->local_palettes.data())); |
| } |
| |
| uint8_t* code_size = nullptr; |
| if (!ReadData(&code_size, 1)) { |
| skip_size_ = skip_size_org; |
| return CFX_GifDecodeStatus::Unfinished; |
| } |
| |
| gif_image->code_exp = *code_size; |
| RecordCurrentPosition(&gif_image->data_pos); |
| gif_image->data_pos += skip_size_; |
| gif_image->image_GCE = nullptr; |
| if (graphic_control_extension_.get()) { |
| if (graphic_control_extension_->gce_flags.transparency) { |
| // Need to test that the color that is going to be transparent is actually |
| // in the palette being used. |
| if (graphic_control_extension_->trans_index >= |
| 2 << (gif_image->local_palettes.empty() |
| ? global_pal_exp_ |
| : gif_image->local_pallette_exp)) |
| return CFX_GifDecodeStatus::Error; |
| } |
| gif_image->image_GCE = std::move(graphic_control_extension_); |
| graphic_control_extension_ = nullptr; |
| } |
| |
| images_.push_back(std::move(gif_image)); |
| SaveDecodingStatus(GIF_D_STATUS_IMG_DATA); |
| return CFX_GifDecodeStatus::Success; |
| } |
| |
| void CFX_GifContext::DecodingFailureAtTailCleanup(CFX_GifImage* gif_image) { |
| gif_image->row_buffer.clear(); |
| SaveDecodingStatus(GIF_D_STATUS_TAIL); |
| } |