summaryrefslogtreecommitdiff
path: root/core/fxcodec/gif
diff options
context:
space:
mode:
authorRyan Harrison <rharrison@chromium.org>2017-09-27 15:39:26 -0400
committerChromium commit bot <commit-bot@chromium.org>2017-09-27 19:56:53 +0000
commit36a155d1f3a9d9f315655a20d583c13644ef1f3e (patch)
tree66e8a87c0b35f3d6c71460f3a0c4ce68a15b51f0 /core/fxcodec/gif
parentdd17a14f060c39f50ca24522b202ffef5436dd43 (diff)
downloadpdfium-36a155d1f3a9d9f315655a20d583c13644ef1f3e.tar.xz
Cleaning up naming of GIF files/classes/variables
Moved everything from core/fxcodec/lgif to core/fxcodec/gif Converted CGifContext -> CFX_GifContext Removed _ptr suffixes from CXF_GifContext Movef fx_gif.* -> cfx_gif.* Renamed structs in cfx_gif.h Renamed members of CFX_GifImage Renamed members of CFX_GifContext Renamed CFX_LZWDecoder -> CFX_LZWDecompressor BUG=pdfium:903 Change-Id: I537e905e935da26832e6bbdc03e0373ed5500bcb Reviewed-on: https://pdfium-review.googlesource.com/14990 Commit-Queue: Ryan Harrison <rharrison@chromium.org> Reviewed-by: dsinclair <dsinclair@chromium.org>
Diffstat (limited to 'core/fxcodec/gif')
-rw-r--r--core/fxcodec/gif/cfx_gif.cpp20
-rw-r--r--core/fxcodec/gif/cfx_gif.h131
-rw-r--r--core/fxcodec/gif/cfx_gifcontext.cpp537
-rw-r--r--core/fxcodec/gif/cfx_gifcontext.h76
-rw-r--r--core/fxcodec/gif/cfx_lzwdecompressor.cpp185
-rw-r--r--core/fxcodec/gif/cfx_lzwdecompressor.h55
-rw-r--r--core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp147
7 files changed, 1151 insertions, 0 deletions
diff --git a/core/fxcodec/gif/cfx_gif.cpp b/core/fxcodec/gif/cfx_gif.cpp
new file mode 100644
index 0000000000..c3c5c85323
--- /dev/null
+++ b/core/fxcodec/gif/cfx_gif.cpp
@@ -0,0 +1,20 @@
+// Copyright 2014 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_gif.h"
+
+static_assert(sizeof(CFX_CFX_GifImageInfo) == 9,
+ "CFX_CFX_GifImageInfo should have a size of 9");
+static_assert(sizeof(CFX_GifPalette) == 3,
+ "CFX_GifPalette should have a size of 3");
+static_assert(sizeof(CFX_GifPlainTextExtension) == 13,
+ "CFX_GifPlainTextExtension should have a size of 13");
+static_assert(sizeof(CFX_GifGraphicControlExtension) == 5,
+ "CFX_GifGraphicControlExtension should have a size of 5");
+static_assert(sizeof(CFX_GifHeader) == 6,
+ "CFX_GifHeader should have a size of 6");
+static_assert(sizeof(CFX_GifLocalScreenDescriptor) == 7,
+ "CFX_GifLocalScreenDescriptor should have a size of 7");
diff --git a/core/fxcodec/gif/cfx_gif.h b/core/fxcodec/gif/cfx_gif.h
new file mode 100644
index 0000000000..02a4862017
--- /dev/null
+++ b/core/fxcodec/gif/cfx_gif.h
@@ -0,0 +1,131 @@
+// Copyright 2014 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
+
+#ifndef CORE_FXCODEC_GIF_CFX_GIF_H_
+#define CORE_FXCODEC_GIF_CFX_GIF_H_
+
+#include <memory>
+#include <vector>
+
+class CFX_GifContext;
+
+#define GIF_SIGNATURE "GIF"
+#define GIF_SIG_EXTENSION 0x21
+#define GIF_SIG_IMAGE 0x2C
+#define GIF_SIG_TRAILER 0x3B
+#define GIF_BLOCK_GCE 0xF9
+#define GIF_BLOCK_PTE 0x01
+#define GIF_BLOCK_CE 0xFE
+#define GIF_BLOCK_AE 0xFF
+#define GIF_BLOCK_TERMINAL 0x00
+#define GIF_MAX_LZW_EXP 12
+#define GIF_MAX_LZW_CODE 4096
+#define GIF_DATA_BLOCK 255
+#define GIF_MAX_ERROR_SIZE 256
+#define GIF_D_STATUS_SIG 0x01
+#define GIF_D_STATUS_TAIL 0x02
+#define GIF_D_STATUS_EXT 0x03
+#define GIF_D_STATUS_EXT_AE 0x04
+#define GIF_D_STATUS_EXT_CE 0x05
+#define GIF_D_STATUS_EXT_GCE 0x06
+#define GIF_D_STATUS_EXT_PTE 0x07
+#define GIF_D_STATUS_EXT_UNE 0x08
+#define GIF_D_STATUS_IMG_INFO 0x09
+#define GIF_D_STATUS_IMG_DATA 0x0A
+
+#pragma pack(1)
+typedef struct {
+ uint8_t pal_bits : 3;
+ uint8_t sort_flag : 1;
+ uint8_t color_resolution : 3;
+ uint8_t global_pal : 1;
+} CFX_GifGlobalFlags;
+
+typedef struct {
+ uint8_t pal_bits : 3;
+ uint8_t reserved : 2;
+ uint8_t sort_flag : 1;
+ uint8_t interlace : 1;
+ uint8_t local_pal : 1;
+} CFX_GifLocalFlags;
+
+typedef struct {
+ char signature[3];
+ char version[3];
+} CFX_GifHeader;
+
+typedef struct {
+ uint16_t width;
+ uint16_t height;
+ CFX_GifGlobalFlags global_flags;
+ uint8_t bc_index;
+ uint8_t pixel_aspect;
+} CFX_GifLocalScreenDescriptor;
+
+typedef struct {
+ uint16_t left;
+ uint16_t top;
+ uint16_t width;
+ uint16_t height;
+ CFX_GifLocalFlags local_flags;
+} CFX_CFX_GifImageInfo;
+
+typedef struct {
+ uint8_t transparency : 1;
+ uint8_t user_input : 1;
+ uint8_t disposal_method : 3;
+ uint8_t reserved : 3;
+} CFX_GifControlExtensionFlags;
+
+typedef struct {
+ uint8_t block_size;
+ CFX_GifControlExtensionFlags gce_flags;
+ uint16_t delay_time;
+ uint8_t trans_index;
+} CFX_GifGraphicControlExtension;
+
+typedef struct {
+ uint8_t block_size;
+ uint16_t grid_left;
+ uint16_t grid_top;
+ uint16_t grid_width;
+ uint16_t grid_height;
+
+ uint8_t char_width;
+ uint8_t char_height;
+
+ uint8_t fc_index;
+ uint8_t bc_index;
+} CFX_GifPlainTextExtension;
+
+typedef struct {
+ uint8_t block_size;
+ uint8_t app_identify[8];
+ uint8_t app_authentication[3];
+} GifApplicationExtension;
+
+typedef struct { uint8_t r, g, b; } CFX_GifPalette;
+#pragma pack()
+
+enum class CFX_GifDecodeStatus {
+ Error,
+ Success,
+ Unfinished,
+ InsufficientDestSize, // Only used internally by CGifLZWDecoder::Decode()
+};
+
+typedef struct {
+ std::unique_ptr<CFX_GifGraphicControlExtension> image_GCE;
+ std::vector<CFX_GifPalette> local_palettes;
+ std::vector<uint8_t> row_buffer;
+ CFX_CFX_GifImageInfo image_info;
+ uint8_t local_pallette_exp;
+ uint8_t code_exp;
+ uint32_t data_pos;
+ int32_t row_num;
+} CFX_GifImage;
+
+#endif // CORE_FXCODEC_GIF_CFX_GIF_H_
diff --git a/core/fxcodec/gif/cfx_gifcontext.cpp b/core/fxcodec/gif/cfx_gifcontext.cpp
new file mode 100644
index 0000000000..a882a58bd2
--- /dev/null
+++ b/core/fxcodec/gif/cfx_gifcontext.cpp
@@ -0,0 +1,537 @@
+// 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 "core/fxcodec/lbmp/fx_bmp.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() {
+ uint32_t skip_size_org = skip_size_;
+ CFX_GifHeader* gif_header = nullptr;
+ if (!ReadData(reinterpret_cast<uint8_t**>(&gif_header), 6))
+ return CFX_GifDecodeStatus::Unfinished;
+
+ if (strncmp(gif_header->signature, GIF_SIGNATURE, 3) != 0 ||
+ gif_header->version[0] != '8' || gif_header->version[2] != 'a')
+ return CFX_GifDecodeStatus::Error;
+
+ CFX_GifLocalScreenDescriptor* gif_lsd = nullptr;
+ if (!ReadData(reinterpret_cast<uint8_t**>(&gif_lsd), 7)) {
+ skip_size_ = skip_size_org;
+ return CFX_GifDecodeStatus::Unfinished;
+ }
+
+ if (gif_lsd->global_flags.global_pal) {
+ global_pal_exp_ = gif_lsd->global_flags.pal_bits;
+ uint32_t global_pal_size = unsigned(2 << global_pal_exp_) * 3u;
+ uint8_t* global_pal = nullptr;
+ if (!ReadData(&global_pal, global_pal_size)) {
+ skip_size_ = skip_size_org;
+ return CFX_GifDecodeStatus::Unfinished;
+ }
+
+ global_sort_flag_ = gif_lsd->global_flags.sort_flag;
+ global_color_resolution_ = gif_lsd->global_flags.color_resolution;
+ global_palette_.resize(global_pal_size / 3);
+ memcpy(global_palette_.data(), global_pal, global_pal_size);
+ }
+
+ width_ = static_cast<int>(
+ GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&gif_lsd->width)));
+ height_ = static_cast<int>(
+ GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&gif_lsd->height)));
+ bc_index_ = gif_lsd->bc_index;
+ pixel_aspect_ = gif_lsd->pixel_aspect;
+ return CFX_GifDecodeStatus::Success;
+}
+
+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_[frame_num].get();
+ 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_;
+}
+
+int32_t CFX_GifContext::GetFrameNum() const {
+ return pdfium::CollectionSize<int32_t>(images_);
+}
+
+uint8_t* CFX_GifContext::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 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 =
+ GetWord_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 =
+ GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&img_info->left));
+ gif_image->image_info.top =
+ GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&img_info->top));
+ gif_image->image_info.width =
+ GetWord_LSBFirst(reinterpret_cast<uint8_t*>(&img_info->width));
+ gif_image->image_info.height =
+ GetWord_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()) {
+ 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);
+}
diff --git a/core/fxcodec/gif/cfx_gifcontext.h b/core/fxcodec/gif/cfx_gifcontext.h
new file mode 100644
index 0000000000..a1cffb2746
--- /dev/null
+++ b/core/fxcodec/gif/cfx_gifcontext.h
@@ -0,0 +1,76 @@
+// 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
+
+#ifndef CORE_FXCODEC_GIF_CFX_GIFCONTEXT_H_
+#define CORE_FXCODEC_GIF_CFX_GIFCONTEXT_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcodec/codec/ccodec_gifmodule.h"
+#include "core/fxcodec/gif/cfx_gif.h"
+#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
+#include "core/fxcrt/fx_string.h"
+#include "core/fxcrt/unowned_ptr.h"
+
+class CFX_GifContext : public CCodec_GifModule::Context {
+ public:
+ CFX_GifContext(CCodec_GifModule* gif_module,
+ CCodec_GifModule::Delegate* delegate);
+ ~CFX_GifContext() override;
+
+ void RecordCurrentPosition(uint32_t* cur_pos);
+ void ReadScanline(int32_t row_num, 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 delay_time,
+ bool user_input,
+ int32_t trans_index,
+ int32_t disposal_method,
+ bool interlace);
+ CFX_GifDecodeStatus ReadHeader();
+ CFX_GifDecodeStatus GetFrame();
+ CFX_GifDecodeStatus LoadFrame(int32_t frame_num);
+ void SetInputBuffer(uint8_t* src_buf, uint32_t src_size);
+ uint32_t GetAvailInput(uint8_t** avail_buf) const;
+ int32_t GetFrameNum() const;
+
+ UnownedPtr<CCodec_GifModule> gif_module_;
+ UnownedPtr<CCodec_GifModule::Delegate> delegate_;
+ std::vector<CFX_GifPalette> global_palette_;
+ uint8_t global_pal_exp_;
+ uint32_t img_row_offset_;
+ uint32_t img_row_avail_size_;
+ uint32_t avail_in_;
+ int32_t decode_status_;
+ uint32_t skip_size_;
+ ByteString cmt_data_;
+ std::unique_ptr<CFX_GifGraphicControlExtension> graphic_control_extension_;
+ uint8_t* next_in_;
+ std::vector<std::unique_ptr<CFX_GifImage>> images_;
+ std::unique_ptr<CFX_LZWDecompressor> lzw_decompressor_;
+ int width_;
+ int height_;
+ uint8_t bc_index_;
+ uint8_t pixel_aspect_;
+ uint8_t global_sort_flag_;
+ uint8_t global_color_resolution_;
+ uint8_t img_pass_num_;
+
+ private:
+ uint8_t* ReadData(uint8_t** des_buf_pp, uint32_t data_size);
+ void SaveDecodingStatus(int32_t status);
+ CFX_GifDecodeStatus DecodeExtension();
+ CFX_GifDecodeStatus DecodeImageInfo();
+ void DecodingFailureAtTailCleanup(CFX_GifImage* gif_image);
+};
+
+#endif // CORE_FXCODEC_GIF_CFX_GIFCONTEXT_H_
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor.cpp b/core/fxcodec/gif/cfx_lzwdecompressor.cpp
new file mode 100644
index 0000000000..27e36b12e9
--- /dev/null
+++ b/core/fxcodec/gif/cfx_lzwdecompressor.cpp
@@ -0,0 +1,185 @@
+// 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_lzwdecompressor.h"
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+#include "core/fxcodec/lbmp/fx_bmp.h"
+#include "third_party/base/numerics/safe_math.h"
+#include "third_party/base/ptr_util.h"
+#include "third_party/base/stl_util.h"
+
+std::unique_ptr<CFX_LZWDecompressor> CFX_LZWDecompressor::Create(
+ uint8_t color_exp,
+ uint8_t code_exp) {
+ if (code_exp > GIF_MAX_LZW_EXP || code_exp + 1 < color_exp)
+ return nullptr;
+ return std::unique_ptr<CFX_LZWDecompressor>(
+ new CFX_LZWDecompressor(color_exp, code_exp));
+}
+
+CFX_LZWDecompressor::CFX_LZWDecompressor(uint8_t color_exp, uint8_t code_exp)
+ : code_size_(code_exp),
+ code_size_cur_(0),
+ code_color_end_(static_cast<uint16_t>(2 << color_exp)),
+ code_clear_(static_cast<uint16_t>(1 << code_exp)),
+ code_end_(static_cast<uint16_t>((1 << code_exp) + 1)),
+ code_next_(0),
+ code_first_(0),
+ stack_size_(0),
+ code_old_(0),
+ next_in_(nullptr),
+ avail_in_(0),
+ bits_left_(0),
+ code_store_(0) {}
+
+CFX_LZWDecompressor::~CFX_LZWDecompressor() {}
+
+CFX_GifDecodeStatus CFX_LZWDecompressor::Decode(uint8_t* src_buf,
+ uint32_t src_size,
+ uint8_t* des_buf,
+ uint32_t* des_size) {
+ if (!src_buf || src_size == 0 || !des_buf || !des_size)
+ return CFX_GifDecodeStatus::Error;
+
+ if (*des_size == 0)
+ return CFX_GifDecodeStatus::InsufficientDestSize;
+
+ next_in_ = src_buf;
+ avail_in_ = src_size;
+
+ ClearTable();
+
+ uint32_t i = 0;
+ if (stack_size_ != 0) {
+ if (*des_size < stack_size_) {
+ memcpy(des_buf, &stack_[GIF_MAX_LZW_CODE - stack_size_], *des_size);
+ stack_size_ -= static_cast<uint16_t>(*des_size);
+ return CFX_GifDecodeStatus::InsufficientDestSize;
+ }
+
+ memcpy(des_buf, &stack_[GIF_MAX_LZW_CODE - stack_size_], stack_size_);
+ des_buf += stack_size_;
+ i += stack_size_;
+ stack_size_ = 0;
+ }
+
+ while (i <= *des_size && (avail_in_ > 0 || bits_left_ >= code_size_cur_)) {
+ if (code_size_cur_ > GIF_MAX_LZW_EXP)
+ return CFX_GifDecodeStatus::Error;
+
+ if (avail_in_ > 0) {
+ if (bits_left_ > 31)
+ return CFX_GifDecodeStatus::Error;
+
+ pdfium::base::CheckedNumeric<uint32_t> safe_code = *next_in_++;
+ safe_code <<= bits_left_;
+ safe_code |= code_store_;
+ if (!safe_code.IsValid())
+ return CFX_GifDecodeStatus::Error;
+
+ code_store_ = safe_code.ValueOrDie();
+ --avail_in_;
+ bits_left_ += 8;
+ }
+
+ while (bits_left_ >= code_size_cur_) {
+ uint16_t code =
+ static_cast<uint16_t>(code_store_) & ((1 << code_size_cur_) - 1);
+ code_store_ >>= code_size_cur_;
+ bits_left_ -= code_size_cur_;
+ if (code == code_clear_) {
+ ClearTable();
+ continue;
+ }
+ if (code == code_end_) {
+ *des_size = i;
+ return CFX_GifDecodeStatus::Success;
+ }
+
+ if (code_old_ != static_cast<uint16_t>(-1)) {
+ if (code_next_ < GIF_MAX_LZW_CODE) {
+ if (code == code_next_) {
+ AddCode(code_old_, code_first_);
+ if (!DecodeString(code))
+ return CFX_GifDecodeStatus::Error;
+ } else if (code > code_next_) {
+ return CFX_GifDecodeStatus::Error;
+ } else {
+ if (!DecodeString(code))
+ return CFX_GifDecodeStatus::Error;
+
+ uint8_t append_char = stack_[GIF_MAX_LZW_CODE - stack_size_];
+ AddCode(code_old_, append_char);
+ }
+ }
+ } else {
+ if (!DecodeString(code))
+ return CFX_GifDecodeStatus::Error;
+ }
+
+ code_old_ = code;
+ if (i + stack_size_ > *des_size) {
+ memcpy(des_buf, &stack_[GIF_MAX_LZW_CODE - stack_size_], *des_size - i);
+ stack_size_ -= static_cast<uint16_t>(*des_size - i);
+ return CFX_GifDecodeStatus::InsufficientDestSize;
+ }
+
+ memcpy(des_buf, &stack_[GIF_MAX_LZW_CODE - stack_size_], stack_size_);
+ des_buf += stack_size_;
+ i += stack_size_;
+ stack_size_ = 0;
+ }
+ }
+
+ if (avail_in_ != 0)
+ return CFX_GifDecodeStatus::Error;
+
+ *des_size = i;
+ return CFX_GifDecodeStatus::Unfinished;
+}
+
+void CFX_LZWDecompressor::ClearTable() {
+ code_size_cur_ = code_size_ + 1;
+ code_next_ = code_end_ + 1;
+ code_old_ = static_cast<uint16_t>(-1);
+ memset(code_table_, 0, sizeof(code_table_));
+ memset(stack_, 0, sizeof(stack_));
+ for (uint16_t i = 0; i < code_clear_; i++)
+ code_table_[i].suffix = static_cast<uint8_t>(i);
+}
+
+void CFX_LZWDecompressor::AddCode(uint16_t prefix_code, uint8_t append_char) {
+ if (code_next_ == GIF_MAX_LZW_CODE)
+ return;
+
+ code_table_[code_next_].prefix = prefix_code;
+ code_table_[code_next_].suffix = append_char;
+ if (++code_next_ < GIF_MAX_LZW_CODE) {
+ if (code_next_ >> code_size_cur_)
+ code_size_cur_++;
+ }
+}
+
+bool CFX_LZWDecompressor::DecodeString(uint16_t code) {
+ stack_size_ = 0;
+ while (code >= code_clear_ && code <= code_next_) {
+ if (code == code_table_[code].prefix || stack_size_ == GIF_MAX_LZW_CODE - 1)
+ return false;
+
+ stack_[GIF_MAX_LZW_CODE - 1 - stack_size_++] = code_table_[code].suffix;
+ code = code_table_[code].prefix;
+ }
+ if (code >= code_color_end_)
+ return false;
+
+ stack_[GIF_MAX_LZW_CODE - 1 - stack_size_++] = static_cast<uint8_t>(code);
+ code_first_ = static_cast<uint8_t>(code);
+ return true;
+}
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor.h b/core/fxcodec/gif/cfx_lzwdecompressor.h
new file mode 100644
index 0000000000..10c0a566d7
--- /dev/null
+++ b/core/fxcodec/gif/cfx_lzwdecompressor.h
@@ -0,0 +1,55 @@
+// 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
+
+#ifndef CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_
+#define CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "core/fxcodec/gif/cfx_gif.h"
+
+class CFX_LZWDecompressor {
+ public:
+ typedef struct {
+ uint16_t prefix;
+ uint8_t suffix;
+ } CodeEntry;
+
+ // Returns nullptr on error
+ static std::unique_ptr<CFX_LZWDecompressor> Create(uint8_t color_exp,
+ uint8_t code_exp);
+ ~CFX_LZWDecompressor();
+
+ CFX_GifDecodeStatus Decode(uint8_t* src_buf,
+ uint32_t src_size,
+ uint8_t* des_buf,
+ uint32_t* des_size);
+
+ private:
+ CFX_LZWDecompressor(uint8_t color_exp, uint8_t code_exp);
+ void ClearTable();
+ void AddCode(uint16_t prefix_code, uint8_t append_char);
+ bool DecodeString(uint16_t code);
+
+ uint8_t code_size_;
+ uint8_t code_size_cur_;
+ uint16_t code_color_end_;
+ uint16_t code_clear_;
+ uint16_t code_end_;
+ uint16_t code_next_;
+ uint8_t code_first_;
+ uint8_t stack_[GIF_MAX_LZW_CODE];
+ size_t stack_size_;
+ CodeEntry code_table_[GIF_MAX_LZW_CODE];
+ uint16_t code_old_;
+ uint8_t* next_in_;
+ uint32_t avail_in_;
+ uint8_t bits_left_;
+ uint32_t code_store_;
+};
+
+#endif // CORE_FXCODEC_GIF_CFX_LZWDECOMPRESSOR_H_
diff --git a/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp b/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp
new file mode 100644
index 0000000000..cc3ce6367d
--- /dev/null
+++ b/core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp
@@ -0,0 +1,147 @@
+// 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.
+
+#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
+
+#include "core/fxcrt/fx_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(CFX_LZWDecompressor, CreateBadParams) {
+ EXPECT_EQ(nullptr, CFX_LZWDecompressor::Create(0x10, 0x2));
+ EXPECT_EQ(nullptr, CFX_LZWDecompressor::Create(0x4, 0x0F));
+}
+
+TEST(CFX_LZWDecompressor, DecodeBadParams) {
+ uint8_t palette_exp = 0x0;
+ uint8_t code_exp = 0x2;
+
+ auto decoder = CFX_LZWDecompressor::Create(palette_exp, code_exp);
+ ASSERT_NE(nullptr, decoder);
+
+ uint8_t image_data[10];
+ uint32_t image_size = FX_ArraySize(image_data);
+
+ uint8_t output_data[10];
+ uint32_t output_size = FX_ArraySize(output_data);
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Error,
+ decoder->Decode(nullptr, image_size, output_data, &output_size));
+ EXPECT_EQ(CFX_GifDecodeStatus::Error,
+ decoder->Decode(image_data, 0, output_data, &output_size));
+ EXPECT_EQ(CFX_GifDecodeStatus::Error,
+ decoder->Decode(image_data, image_size, nullptr, &output_size));
+ EXPECT_EQ(CFX_GifDecodeStatus::Error,
+ decoder->Decode(image_data, image_size, output_data, nullptr));
+
+ output_size = 0;
+ EXPECT_EQ(CFX_GifDecodeStatus::InsufficientDestSize,
+ decoder->Decode(image_data, image_size, output_data, &output_size));
+}
+
+TEST(CFX_LZWDecompressor, Decode1x1SingleColour) {
+ uint8_t palette_exp = 0x0;
+ uint8_t code_exp = 0x2;
+
+ auto decoder = CFX_LZWDecompressor::Create(palette_exp, code_exp);
+ ASSERT_NE(nullptr, decoder);
+
+ uint8_t image_data[] = {0x44, 0x01};
+ uint32_t image_size = FX_ArraySize(image_data);
+
+ uint8_t output_data[1];
+ uint32_t output_size = FX_ArraySize(output_data);
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Success,
+ decoder->Decode(image_data, image_size, output_data, &output_size));
+ uint8_t expected_data[] = {0x00};
+
+ EXPECT_EQ(FX_ArraySize(output_data), output_size);
+ EXPECT_TRUE(0 == memcmp(expected_data, output_data, sizeof(expected_data)));
+}
+
+TEST(CFX_LZWDecompressor, Decode10x10SingleColour) {
+ uint8_t palette_exp = 0x0;
+ uint8_t code_exp = 0x2;
+
+ auto decoder = CFX_LZWDecompressor::Create(palette_exp, code_exp);
+ ASSERT_NE(nullptr, decoder);
+
+ uint8_t image_data[] = {0x84, 0x8F, 0xA9, 0xCB, 0xED, 0x0F, 0x63, 0x2B};
+ uint32_t image_size = FX_ArraySize(image_data);
+
+ uint8_t output_data[100];
+ memset(output_data, 0, sizeof(output_data));
+ uint32_t output_size = FX_ArraySize(output_data);
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Success,
+ decoder->Decode(image_data, image_size, output_data, &output_size));
+ uint8_t expected_data[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+
+ EXPECT_EQ(FX_ArraySize(output_data), output_size);
+ EXPECT_TRUE(0 == memcmp(expected_data, output_data, sizeof(expected_data)));
+}
+
+TEST(CFX_LZWDecompressor, Decode10x10MultipleColour) {
+ uint8_t palette_exp = 0x1;
+ uint8_t code_exp = 0x2;
+
+ auto decoder = CFX_LZWDecompressor::Create(palette_exp, code_exp);
+ ASSERT_NE(nullptr, decoder);
+
+ uint8_t image_data[] = {0x8C, 0x2D, 0x99, 0x87, 0x2A, 0x1C, 0xDC, 0x33,
+ 0xA0, 0x02, 0x75, 0xEC, 0x95, 0xFA, 0xA8, 0xDE,
+ 0x60, 0x8C, 0x04, 0x91, 0x4C, 0x01};
+ uint32_t image_size = FX_ArraySize(image_data);
+
+ uint8_t output_data[100];
+ memset(output_data, 0, sizeof(output_data));
+ uint32_t output_size = FX_ArraySize(output_data);
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Success,
+ decoder->Decode(image_data, image_size, output_data, &output_size));
+ uint8_t expected_data[] = {
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01,
+ 0x01, 0x01, 0x01, 0x01};
+
+ EXPECT_EQ(FX_ArraySize(output_data), output_size);
+ EXPECT_TRUE(0 == memcmp(expected_data, output_data, sizeof(expected_data)));
+}
+
+TEST(CFX_LZWDecompressor, HandleColourCodeOutOfPalette) {
+ uint8_t palette_exp = 0x2; // Image uses 10 colours, so the palette exp
+ // should be 3, 2^(3+1) = 16 colours.
+ uint8_t code_exp = 0x4;
+
+ auto decoder = CFX_LZWDecompressor::Create(palette_exp, code_exp);
+ ASSERT_NE(nullptr, decoder);
+
+ uint8_t image_data[] = {0x30, 0xC9, 0x49, 0x81, 0xBD, 0x78, 0xE8, 0xCD,
+ 0x89, 0xFF, 0x60, 0x20, 0x8E, 0xE4, 0x61, 0x9E,
+ 0xA8, 0xA1, 0xAE, 0x2C, 0xE2, 0xBE, 0xB0, 0x20,
+ 0xCF, 0x74, 0x61, 0xDF, 0x78, 0x04};
+ uint32_t image_size = FX_ArraySize(image_data);
+
+ uint8_t output_data[100];
+ memset(output_data, 0, sizeof(output_data));
+ uint32_t output_size = FX_ArraySize(output_data);
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Error,
+ decoder->Decode(image_data, image_size, output_data, &output_size));
+}