blob: 1eb1b335aafe68f1fad787ef8f8e6fdd993f3822 [file] [log] [blame]
// 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/lgif/cgifcontext.h"
#include <algorithm>
#include <utility>
#include "core/fxcodec/codec/ccodec_gifmodule.h"
#include "core/fxcodec/lbmp/fx_bmp.h"
#include "core/fxcodec/lgif/fx_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
CGifContext::CGifContext(CCodec_GifModule* gif_module,
CCodec_GifModule::Delegate* pDelegate)
: m_pModule(gif_module),
m_pDelegate(pDelegate),
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) {
memset(m_szLastError, 0, sizeof(m_szLastError));
}
CGifContext::~CGifContext() {}
void CGifContext::AddError(const char* err_msg) {
strncpy(m_szLastError, err_msg, GIF_MAX_ERROR_SIZE - 1);
}
void CGifContext::RecordCurrentPosition(uint32_t* cur_pos_ptr) {
m_pDelegate->GifRecordCurrentPosition(*cur_pos_ptr);
}
void CGifContext::ReadScanline(int32_t row_num, uint8_t* row_buf) {
m_pDelegate->GifReadScanline(row_num, row_buf);
}
bool CGifContext::GetRecordPosition(uint32_t cur_pos,
int32_t left,
int32_t top,
int32_t width,
int32_t height,
int32_t pal_num,
GifPalette* pal_ptr,
int32_t delay_time,
bool user_input,
int32_t trans_index,
int32_t disposal_method,
bool interlace) {
return m_pDelegate->GifInputRecordPositionBuf(
cur_pos, FX_RECT(left, top, left + width, top + height), pal_num, pal_ptr,
delay_time, user_input, trans_index, disposal_method, interlace);
}
GifDecodeStatus CGifContext::ReadHeader() {
uint32_t skip_size_org = skip_size;
GifHeader* gif_header_ptr = nullptr;
if (!ReadData(reinterpret_cast<uint8_t**>(&gif_header_ptr), 6))
return GifDecodeStatus::Unfinished;
if (strncmp(gif_header_ptr->signature, GIF_SIGNATURE, 3) != 0 ||
gif_header_ptr->version[0] != '8' || gif_header_ptr->version[2] != 'a') {
AddError("Not A Gif Image");
return GifDecodeStatus::Error;
}
GifLSD* gif_lsd_ptr = nullptr;
if (!ReadData(reinterpret_cast<uint8_t**>(&gif_lsd_ptr), 7)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
if (reinterpret_cast<GifGF*>(&gif_lsd_ptr->global_flag)->global_pal) {
global_pal_exp =
reinterpret_cast<GifGF*>(&gif_lsd_ptr->global_flag)->pal_bits;
int32_t global_pal_size = (2 << global_pal_exp) * 3;
uint8_t* global_pal_ptr = nullptr;
if (!ReadData(&global_pal_ptr, global_pal_size)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
global_sort_flag = ((GifGF*)&gif_lsd_ptr->global_flag)->sort_flag;
global_color_resolution =
((GifGF*)&gif_lsd_ptr->global_flag)->color_resolution;
m_GlobalPalette.resize(global_pal_size / 3);
memcpy(m_GlobalPalette.data(), global_pal_ptr, global_pal_size);
}
width =
(int)GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&gif_lsd_ptr->width));
height =
(int)GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&gif_lsd_ptr->height));
bc_index = gif_lsd_ptr->bc_index;
pixel_aspect = gif_lsd_ptr->pixel_aspect;
return GifDecodeStatus::Success;
}
GifDecodeStatus CGifContext::GetFrame() {
GifDecodeStatus ret = GifDecodeStatus::Success;
while (true) {
switch (decode_status) {
case GIF_D_STATUS_TAIL:
return GifDecodeStatus::Success;
case GIF_D_STATUS_SIG: {
uint8_t* sig_ptr = nullptr;
if (!ReadData(&sig_ptr, 1))
return GifDecodeStatus::Unfinished;
switch (*sig_ptr) {
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 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 GifDecodeStatus::Success;
}
}
case GIF_D_STATUS_EXT: {
uint8_t* ext_ptr = nullptr;
if (!ReadData(&ext_ptr, 1))
return GifDecodeStatus::Unfinished;
switch (*ext_ptr) {
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 (*ext_ptr == GIF_BLOCK_PTE) {
status = GIF_D_STATUS_EXT_PTE;
}
SaveDecodingStatus(status);
continue;
}
}
}
case GIF_D_STATUS_IMG_INFO: {
ret = DecodeImageInfo();
if (ret != GifDecodeStatus::Success)
return ret;
continue;
}
case GIF_D_STATUS_IMG_DATA: {
uint8_t* data_size_ptr = nullptr;
uint8_t* data_ptr = nullptr;
uint32_t skip_size_org = skip_size;
if (!ReadData(&data_size_ptr, 1))
return GifDecodeStatus::Unfinished;
while (*data_size_ptr != GIF_BLOCK_TERMINAL) {
if (!ReadData(&data_ptr, *data_size_ptr)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
skip_size_org = skip_size;
if (!ReadData(&data_size_ptr, 1))
return GifDecodeStatus::Unfinished;
}
SaveDecodingStatus(GIF_D_STATUS_SIG);
continue;
}
default: {
ret = DecodeExtension();
if (ret != GifDecodeStatus::Success)
return ret;
break;
}
}
}
return GifDecodeStatus::Success;
}
GifDecodeStatus CGifContext::LoadFrame(int32_t frame_num) {
if (!pdfium::IndexInBounds(m_Images, frame_num))
return GifDecodeStatus::Error;
uint8_t* data_size_ptr = nullptr;
uint8_t* data_ptr = nullptr;
uint32_t skip_size_org = skip_size;
GifImage* gif_image_ptr = m_Images[frame_num].get();
uint32_t gif_img_row_bytes = gif_image_ptr->m_ImageInfo.width;
if (gif_img_row_bytes == 0) {
AddError("Error Invalid Number of Row Bytes");
return GifDecodeStatus::Error;
}
if (decode_status == GIF_D_STATUS_TAIL) {
gif_image_ptr->m_ImageRowBuf.resize(gif_img_row_bytes);
GifGCE* gif_img_gce_ptr = gif_image_ptr->m_ImageGCE.get();
int32_t loc_pal_num =
((GifLF*)&gif_image_ptr->m_ImageInfo.local_flag)->local_pal
? (2 << ((GifLF*)&gif_image_ptr->m_ImageInfo.local_flag)->pal_bits)
: 0;
avail_in = 0;
GifPalette* pLocalPalette = gif_image_ptr->m_LocalPalettes.empty()
? nullptr
: gif_image_ptr->m_LocalPalettes.data();
if (!gif_img_gce_ptr) {
bool bRes = GetRecordPosition(
gif_image_ptr->image_data_pos, gif_image_ptr->m_ImageInfo.left,
gif_image_ptr->m_ImageInfo.top, gif_image_ptr->m_ImageInfo.width,
gif_image_ptr->m_ImageInfo.height, loc_pal_num, pLocalPalette, 0, 0,
-1, 0,
(bool)((GifLF*)&gif_image_ptr->m_ImageInfo.local_flag)->interlace);
if (!bRes) {
gif_image_ptr->m_ImageRowBuf.clear();
AddError("Error Read Record Position Data");
return GifDecodeStatus::Error;
}
} else {
bool bRes = GetRecordPosition(
gif_image_ptr->image_data_pos, gif_image_ptr->m_ImageInfo.left,
gif_image_ptr->m_ImageInfo.top, gif_image_ptr->m_ImageInfo.width,
gif_image_ptr->m_ImageInfo.height, loc_pal_num, pLocalPalette,
(int32_t)gif_image_ptr->m_ImageGCE->delay_time,
(bool)((GifCEF*)&gif_image_ptr->m_ImageGCE->gce_flag)->user_input,
((GifCEF*)&gif_image_ptr->m_ImageGCE->gce_flag)->transparency
? (int32_t)gif_image_ptr->m_ImageGCE->trans_index
: -1,
(int32_t)((GifCEF*)&gif_image_ptr->m_ImageGCE->gce_flag)
->disposal_method,
(bool)((GifLF*)&gif_image_ptr->m_ImageInfo.local_flag)->interlace);
if (!bRes) {
gif_image_ptr->m_ImageRowBuf.clear();
AddError("Error Read Record Position Data");
return GifDecodeStatus::Error;
}
}
if (gif_image_ptr->image_code_exp > GIF_MAX_LZW_EXP) {
gif_image_ptr->m_ImageRowBuf.clear();
AddError("Error Invalid Code Size");
return GifDecodeStatus::Error;
}
if (!m_ImgDecoder.get())
m_ImgDecoder = pdfium::MakeUnique<CGifLZWDecoder>(m_szLastError);
m_ImgDecoder->InitTable(!gif_image_ptr->m_LocalPalettes.empty()
? gif_image_ptr->local_pallette_exp
: global_pal_exp,
gif_image_ptr->image_code_exp);
img_row_offset = 0;
img_row_avail_size = 0;
img_pass_num = 0;
gif_image_ptr->image_row_num = 0;
SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
}
CGifLZWDecoder* img_decoder_ptr = m_ImgDecoder.get();
if (decode_status == GIF_D_STATUS_IMG_DATA) {
if (!ReadData(&data_size_ptr, 1))
return GifDecodeStatus::Unfinished;
if (*data_size_ptr != GIF_BLOCK_TERMINAL) {
if (!ReadData(&data_ptr, *data_size_ptr)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
img_decoder_ptr->Input(data_ptr, *data_size_ptr);
SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
img_row_offset += img_row_avail_size;
img_row_avail_size = gif_img_row_bytes - img_row_offset;
GifDecodeStatus ret = img_decoder_ptr->Decode(
gif_image_ptr->m_ImageRowBuf.data() + img_row_offset,
&img_row_avail_size);
if (ret == GifDecodeStatus::Error) {
DecodingFailureAtTailCleanup(gif_image_ptr);
return GifDecodeStatus::Error;
}
while (ret != GifDecodeStatus::Error) {
if (ret == GifDecodeStatus::Success) {
ReadScanline(gif_image_ptr->image_row_num,
gif_image_ptr->m_ImageRowBuf.data());
gif_image_ptr->m_ImageRowBuf.clear();
SaveDecodingStatus(GIF_D_STATUS_TAIL);
return GifDecodeStatus::Success;
}
if (ret == GifDecodeStatus::Unfinished) {
ASSERT(img_decoder_ptr->GetAvailInput() == 0);
skip_size_org = skip_size;
if (!ReadData(&data_size_ptr, 1))
return GifDecodeStatus::Unfinished;
if (*data_size_ptr != GIF_BLOCK_TERMINAL) {
if (!ReadData(&data_ptr, *data_size_ptr)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
img_decoder_ptr->Input(data_ptr, *data_size_ptr);
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 = img_decoder_ptr->Decode(
gif_image_ptr->m_ImageRowBuf.data() + img_row_offset,
&img_row_avail_size);
}
}
if (ret == GifDecodeStatus::InsufficientDestSize) {
if (((GifLF*)&gif_image_ptr->m_ImageInfo.local_flag)->interlace) {
ReadScanline(gif_image_ptr->image_row_num,
gif_image_ptr->m_ImageRowBuf.data());
gif_image_ptr->image_row_num += s_gif_interlace_step[img_pass_num];
if (gif_image_ptr->image_row_num >=
(int32_t)gif_image_ptr->m_ImageInfo.height) {
img_pass_num++;
if (img_pass_num == FX_ArraySize(s_gif_interlace_step)) {
DecodingFailureAtTailCleanup(gif_image_ptr);
return GifDecodeStatus::Error;
}
gif_image_ptr->image_row_num =
s_gif_interlace_step[img_pass_num] / 2;
}
} else {
ReadScanline(gif_image_ptr->image_row_num++,
gif_image_ptr->m_ImageRowBuf.data());
}
img_row_offset = 0;
img_row_avail_size = gif_img_row_bytes;
ret = img_decoder_ptr->Decode(
gif_image_ptr->m_ImageRowBuf.data() + img_row_offset,
&img_row_avail_size);
}
if (ret == GifDecodeStatus::Error) {
DecodingFailureAtTailCleanup(gif_image_ptr);
return GifDecodeStatus::Error;
}
}
}
SaveDecodingStatus(GIF_D_STATUS_TAIL);
}
AddError("Decode Image Data Error");
return GifDecodeStatus::Error;
}
void CGifContext::SetInputBuffer(uint8_t* src_buf, uint32_t src_size) {
next_in = src_buf;
avail_in = src_size;
skip_size = 0;
}
uint32_t CGifContext::GetAvailInput(uint8_t** avail_buf_ptr) const {
if (avail_buf_ptr) {
*avail_buf_ptr = nullptr;
if (avail_in > 0)
*avail_buf_ptr = next_in;
}
return avail_in;
}
int32_t CGifContext::GetFrameNum() const {
return pdfium::CollectionSize<int32_t>(m_Images);
}
uint8_t* CGifContext::ReadData(uint8_t** des_buf_pp, uint32_t data_size) {
if (avail_in < skip_size + data_size)
return nullptr;
*des_buf_pp = next_in + skip_size;
skip_size += data_size;
return *des_buf_pp;
}
void CGifContext::SaveDecodingStatus(int32_t status) {
decode_status = status;
next_in += skip_size;
avail_in -= skip_size;
skip_size = 0;
}
GifDecodeStatus CGifContext::DecodeExtension() {
uint8_t* data_size_ptr = nullptr;
uint8_t* data_ptr = nullptr;
uint32_t skip_size_org = skip_size;
switch (decode_status) {
case GIF_D_STATUS_EXT_CE: {
if (!ReadData(&data_size_ptr, 1)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
cmt_data.clear();
while (*data_size_ptr != GIF_BLOCK_TERMINAL) {
uint8_t data_size = *data_size_ptr;
if (!ReadData(&data_ptr, *data_size_ptr) ||
!ReadData(&data_size_ptr, 1)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
cmt_data += ByteString(data_ptr, data_size);
}
break;
}
case GIF_D_STATUS_EXT_PTE: {
GifPTE* gif_pte = nullptr;
if (!ReadData(reinterpret_cast<uint8_t**>(&gif_pte), 13))
return GifDecodeStatus::Unfinished;
m_GifGCE = nullptr;
if (!ReadData(&data_size_ptr, 1)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
while (*data_size_ptr != GIF_BLOCK_TERMINAL) {
if (!ReadData(&data_ptr, *data_size_ptr) ||
!ReadData(&data_size_ptr, 1)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
}
break;
}
case GIF_D_STATUS_EXT_GCE: {
GifGCE* gif_gce_ptr = nullptr;
if (!ReadData(reinterpret_cast<uint8_t**>(&gif_gce_ptr), 6))
return GifDecodeStatus::Unfinished;
if (!m_GifGCE.get())
m_GifGCE = pdfium::MakeUnique<GifGCE>();
m_GifGCE->block_size = gif_gce_ptr->block_size;
m_GifGCE->gce_flag = gif_gce_ptr->gce_flag;
m_GifGCE->delay_time = GetWord_LSBFirst(
reinterpret_cast<uint8_t*>(&gif_gce_ptr->delay_time));
m_GifGCE->trans_index = gif_gce_ptr->trans_index;
break;
}
default: {
if (decode_status == GIF_D_STATUS_EXT_PTE)
m_GifGCE = nullptr;
if (!ReadData(&data_size_ptr, 1))
return GifDecodeStatus::Unfinished;
while (*data_size_ptr != GIF_BLOCK_TERMINAL) {
if (!ReadData(&data_ptr, *data_size_ptr) ||
!ReadData(&data_size_ptr, 1)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
}
}
}
SaveDecodingStatus(GIF_D_STATUS_SIG);
return GifDecodeStatus::Success;
}
GifDecodeStatus CGifContext::DecodeImageInfo() {
if (width == 0 || height == 0) {
AddError("No Image Header Info");
return GifDecodeStatus::Error;
}
uint32_t skip_size_org = skip_size;
GifImageInfo* gif_img_info_ptr = nullptr;
if (!ReadData(reinterpret_cast<uint8_t**>(&gif_img_info_ptr), 9))
return GifDecodeStatus::Unfinished;
auto gif_image = pdfium::MakeUnique<GifImage>();
gif_image->m_ImageInfo.left =
GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&gif_img_info_ptr->left));
gif_image->m_ImageInfo.top =
GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&gif_img_info_ptr->top));
gif_image->m_ImageInfo.width =
GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&gif_img_info_ptr->width));
gif_image->m_ImageInfo.height =
GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&gif_img_info_ptr->height));
gif_image->m_ImageInfo.local_flag = gif_img_info_ptr->local_flag;
if (gif_image->m_ImageInfo.left + gif_image->m_ImageInfo.width > width ||
gif_image->m_ImageInfo.top + gif_image->m_ImageInfo.height > height) {
AddError("Image Data Out Of LSD, The File May Be Corrupt");
return GifDecodeStatus::Error;
}
GifLF* gif_img_info_lf_ptr = (GifLF*)&gif_img_info_ptr->local_flag;
if (gif_img_info_lf_ptr->local_pal) {
gif_image->local_pallette_exp = gif_img_info_lf_ptr->pal_bits;
int32_t loc_pal_size = (2 << gif_img_info_lf_ptr->pal_bits) * 3;
uint8_t* loc_pal_ptr = nullptr;
if (!ReadData(&loc_pal_ptr, loc_pal_size)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
gif_image->m_LocalPalettes = std::vector<GifPalette>(loc_pal_size / 3);
std::copy(loc_pal_ptr, loc_pal_ptr + loc_pal_size,
reinterpret_cast<uint8_t*>(gif_image->m_LocalPalettes.data()));
}
uint8_t* code_size_ptr = nullptr;
if (!ReadData(&code_size_ptr, 1)) {
skip_size = skip_size_org;
return GifDecodeStatus::Unfinished;
}
gif_image->image_code_exp = *code_size_ptr;
RecordCurrentPosition(&gif_image->image_data_pos);
gif_image->image_data_pos += skip_size;
gif_image->m_ImageGCE = nullptr;
if (m_GifGCE.get()) {
gif_image->m_ImageGCE = std::move(m_GifGCE);
m_GifGCE = nullptr;
}
m_Images.push_back(std::move(gif_image));
SaveDecodingStatus(GIF_D_STATUS_IMG_DATA);
return GifDecodeStatus::Success;
}
void CGifContext::DecodingFailureAtTailCleanup(GifImage* gif_image_ptr) {
gif_image_ptr->m_ImageRowBuf.clear();
SaveDecodingStatus(GIF_D_STATUS_TAIL);
AddError("Decode Image Data Error");
}