summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Harrison <rharrison@chromium.org>2017-10-03 09:32:14 -0400
committerChromium commit bot <commit-bot@chromium.org>2017-10-03 13:53:21 +0000
commit0feba6f9ef721e4927e37da68ac27572ffae1453 (patch)
tree9e5cfef2691bcd8e439bca9175c8197fbd946e33
parentc2ae41abd16aef062fee878160aa18457d2118a7 (diff)
downloadpdfium-0feba6f9ef721e4927e37da68ac27572ffae1453.tar.xz
Rewrite how GIF headers are read
Break up reading the signature and local screen descriptors into seperate functions. Fix a bug in how matching in the signature validation works. Move LSD value assignment to after sufficient data has been confirmed. Convert LSB to MSB methods where they were just wrong. Add unit tests for ReadData, SetInputBuffer, ReadSignature, ReadLocalScreenDescriptor, and ReadHeader. BUG=pdfium:913,chromium:770470 Change-Id: I1683b8aefc11300625b9be8087c6988549308a8f Reviewed-on: https://pdfium-review.googlesource.com/15250 Commit-Queue: dsinclair <dsinclair@chromium.org> Reviewed-by: dsinclair <dsinclair@chromium.org>
-rw-r--r--BUILD.gn1
-rw-r--r--core/fxcodec/gif/cfx_gif.cpp3
-rw-r--r--core/fxcodec/gif/cfx_gif.h9
-rw-r--r--core/fxcodec/gif/cfx_gifcontext.cpp98
-rw-r--r--core/fxcodec/gif/cfx_gifcontext.h6
-rw-r--r--core/fxcodec/gif/cfx_gifcontext_unittest.cpp336
6 files changed, 409 insertions, 44 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 5657ac18f7..0c6ddaefb8 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1950,6 +1950,7 @@ test("pdfium_unittests") {
include_dirs = []
if (pdf_enable_xfa) {
sources += [
+ "core/fxcodec/gif/cfx_gifcontext_unittest.cpp",
"core/fxcodec/gif/cfx_lzwdecompressor_unittest.cpp",
"core/fxcrt/css/cfx_cssdeclaration_unittest.cpp",
"core/fxcrt/css/cfx_cssstylesheet_unittest.cpp",
diff --git a/core/fxcodec/gif/cfx_gif.cpp b/core/fxcodec/gif/cfx_gif.cpp
index c3c5c85323..985a687906 100644
--- a/core/fxcodec/gif/cfx_gif.cpp
+++ b/core/fxcodec/gif/cfx_gif.cpp
@@ -6,6 +6,9 @@
#include "core/fxcodec/gif/cfx_gif.h"
+const char* kGifSignature87 = "GIF87a";
+const char* kGifSignature89 = "GIF89a";
+
static_assert(sizeof(CFX_CFX_GifImageInfo) == 9,
"CFX_CFX_GifImageInfo should have a size of 9");
static_assert(sizeof(CFX_GifPalette) == 3,
diff --git a/core/fxcodec/gif/cfx_gif.h b/core/fxcodec/gif/cfx_gif.h
index e9eb5c4054..ab8278ecd2 100644
--- a/core/fxcodec/gif/cfx_gif.h
+++ b/core/fxcodec/gif/cfx_gif.h
@@ -12,7 +12,9 @@
class CFX_GifContext;
-#define GIF_SIGNATURE "GIF"
+extern const char* kGifSignature87;
+extern const char* kGifSignature89;
+
#define GIF_SIG_EXTENSION 0x21
#define GIF_SIG_IMAGE 0x2C
#define GIF_SIG_TRAILER 0x3B
@@ -49,10 +51,7 @@ typedef struct {
uint8_t local_pal : 1;
} CFX_GifLocalFlags;
-typedef struct {
- char signature[3];
- char version[3];
-} CFX_GifHeader;
+typedef struct { char signature[6]; } CFX_GifHeader;
typedef struct {
uint16_t width;
diff --git a/core/fxcodec/gif/cfx_gifcontext.cpp b/core/fxcodec/gif/cfx_gifcontext.cpp
index 09e8753ca0..28bcad9ceb 100644
--- a/core/fxcodec/gif/cfx_gifcontext.cpp
+++ b/core/fxcodec/gif/cfx_gifcontext.cpp
@@ -67,43 +67,10 @@ bool CFX_GifContext::GetRecordPosition(uint32_t cur_pos,
}
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>(
- FXWORD_GET_LSBFIRST(reinterpret_cast<uint8_t*>(&gif_lsd->width)));
- height_ = static_cast<int>(
- FXWORD_GET_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 status = ReadGifSignature();
+ if (status != CFX_GifDecodeStatus::Success)
+ return status;
+ return ReadLogicalScreenDescriptor();
}
CFX_GifDecodeStatus CFX_GifContext::GetFrame() {
@@ -383,7 +350,15 @@ int32_t CFX_GifContext::GetFrameNum() const {
}
uint8_t* CFX_GifContext::ReadData(uint8_t** des_buf_pp, uint32_t data_size) {
- if (avail_in_ < skip_size_ + 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_;
@@ -391,6 +366,53 @@ uint8_t* CFX_GifContext::ReadData(uint8_t** des_buf_pp, uint32_t 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 global_pal_size = unsigned(2 << lsd->global_flags.pal_bits) * 3u;
+ uint8_t* global_pal = nullptr;
+ if (!ReadData(&global_pal, global_pal_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(global_pal_size / 3);
+ memcpy(global_palette_.data(), global_pal, global_pal_size);
+ }
+
+ width_ = static_cast<int>(
+ FXWORD_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&lsd->width)));
+ height_ = static_cast<int>(
+ FXWORD_GET_MSBFIRST(reinterpret_cast<uint8_t*>(&lsd->height)));
+ bc_index_ = lsd->bc_index;
+ pixel_aspect_ = lsd->pixel_aspect;
+ return CFX_GifDecodeStatus::Success;
+}
+
void CFX_GifContext::SaveDecodingStatus(int32_t status) {
decode_status_ = status;
next_in_ += skip_size_;
diff --git a/core/fxcodec/gif/cfx_gifcontext.h b/core/fxcodec/gif/cfx_gifcontext.h
index a1cffb2746..e0c5900f93 100644
--- a/core/fxcodec/gif/cfx_gifcontext.h
+++ b/core/fxcodec/gif/cfx_gifcontext.h
@@ -65,8 +65,12 @@ class CFX_GifContext : public CCodec_GifModule::Context {
uint8_t global_color_resolution_;
uint8_t img_pass_num_;
- private:
+ protected:
uint8_t* ReadData(uint8_t** des_buf_pp, uint32_t data_size);
+ CFX_GifDecodeStatus ReadGifSignature();
+ CFX_GifDecodeStatus ReadLogicalScreenDescriptor();
+
+ private:
void SaveDecodingStatus(int32_t status);
CFX_GifDecodeStatus DecodeExtension();
CFX_GifDecodeStatus DecodeImageInfo();
diff --git a/core/fxcodec/gif/cfx_gifcontext_unittest.cpp b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp
new file mode 100644
index 0000000000..98b8ec5cd0
--- /dev/null
+++ b/core/fxcodec/gif/cfx_gifcontext_unittest.cpp
@@ -0,0 +1,336 @@
+// 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_gifcontext.h"
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CFX_GifContextForTest : public CFX_GifContext {
+ public:
+ CFX_GifContextForTest(CCodec_GifModule* gif_module,
+ CCodec_GifModule::Delegate* delegate)
+ : CFX_GifContext(gif_module, delegate) {}
+ ~CFX_GifContextForTest() override {}
+
+ using CFX_GifContext::ReadData;
+ using CFX_GifContext::ReadGifSignature;
+ using CFX_GifContext::ReadLogicalScreenDescriptor;
+};
+
+TEST(CFX_GifContext, SetInputBuffer) {
+ CFX_GifContextForTest context(nullptr, nullptr);
+
+ context.SetInputBuffer(nullptr, 0);
+ EXPECT_EQ(nullptr, context.next_in_);
+ EXPECT_EQ(0u, context.avail_in_);
+ EXPECT_EQ(0u, context.skip_size_);
+
+ context.SetInputBuffer(nullptr, 100);
+ EXPECT_EQ(nullptr, context.next_in_);
+ EXPECT_EQ(100u, context.avail_in_);
+ EXPECT_EQ(0u, context.skip_size_);
+
+ uint8_t buffer[] = {0x00, 0x01, 0x02};
+ context.SetInputBuffer(buffer, 0);
+ EXPECT_EQ(buffer, context.next_in_);
+ EXPECT_EQ(0u, context.avail_in_);
+ EXPECT_EQ(0u, context.skip_size_);
+
+ context.SetInputBuffer(buffer, 3);
+ EXPECT_EQ(buffer, context.next_in_);
+ EXPECT_EQ(3u, context.avail_in_);
+ EXPECT_EQ(0u, context.skip_size_);
+
+ context.SetInputBuffer(buffer, 100);
+ EXPECT_EQ(buffer, context.next_in_);
+ EXPECT_EQ(100u, context.avail_in_);
+ EXPECT_EQ(0u, context.skip_size_);
+}
+
+TEST(CFX_GifContext, ReadData) {
+ CFX_GifContextForTest context(nullptr, nullptr);
+
+ context.SetInputBuffer(nullptr, 0);
+ EXPECT_EQ(nullptr, context.ReadData(nullptr, 0));
+ EXPECT_EQ(nullptr, context.ReadData(nullptr, 10));
+
+ uint8_t* dest_buffer = nullptr;
+ EXPECT_EQ(nullptr,
+ context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 0));
+ EXPECT_EQ(nullptr,
+ context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10));
+
+ uint8_t src_buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08, 0x09};
+ context.SetInputBuffer(src_buffer, 0);
+ EXPECT_EQ(nullptr,
+ context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10));
+ EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), context.next_in_);
+ EXPECT_EQ(0u, context.avail_in_);
+ EXPECT_EQ(0u, context.skip_size_);
+
+ dest_buffer = nullptr;
+ context.SetInputBuffer(src_buffer, 10);
+ EXPECT_EQ(src_buffer,
+ context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 10));
+ EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), dest_buffer);
+ EXPECT_EQ(src_buffer, context.next_in_);
+ EXPECT_EQ(10u, context.avail_in_);
+ EXPECT_EQ(10u, context.skip_size_);
+
+ dest_buffer = nullptr;
+ context.SetInputBuffer(src_buffer, 10);
+ EXPECT_EQ(src_buffer,
+ context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5));
+ EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer), dest_buffer);
+ EXPECT_EQ(src_buffer, context.next_in_);
+ EXPECT_EQ(10u, context.avail_in_);
+ EXPECT_EQ(5u, context.skip_size_);
+
+ dest_buffer = nullptr;
+ EXPECT_EQ(src_buffer + 5,
+ context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5));
+ EXPECT_EQ(reinterpret_cast<uint8_t*>(src_buffer + 5), dest_buffer);
+ EXPECT_EQ(src_buffer, context.next_in_);
+ EXPECT_EQ(10u, context.avail_in_);
+ EXPECT_EQ(10u, context.skip_size_);
+
+ dest_buffer = nullptr;
+ EXPECT_EQ(nullptr,
+ context.ReadData(reinterpret_cast<uint8_t**>(&dest_buffer), 5));
+ EXPECT_EQ(nullptr, dest_buffer);
+ EXPECT_EQ(src_buffer, context.next_in_);
+ EXPECT_EQ(10u, context.avail_in_);
+ EXPECT_EQ(10u, context.skip_size_);
+}
+
+TEST(CFX_GifContext, ReadGifSignature) {
+ CFX_GifContextForTest context(nullptr, nullptr);
+
+ {
+ uint8_t data[1];
+ context.SetInputBuffer(data, 0);
+ EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature());
+ EXPECT_EQ(0u, context.skip_size_);
+ }
+ // Make sure testing the entire signature
+ {
+ uint8_t data[] = {'G', 'I', 'F'};
+ context.SetInputBuffer(data, sizeof(data));
+ EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature());
+ EXPECT_EQ(0u, context.skip_size_);
+ }
+ {
+ uint8_t data[] = {'N', 'O', 'T', 'G', 'I', 'F'};
+ context.SetInputBuffer(data, sizeof(data));
+ EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
+ EXPECT_EQ(6u, context.skip_size_);
+ }
+ // Make sure not matching GIF8*a
+ {
+ uint8_t data[] = {'G', 'I', 'F', '8', '0', 'a'};
+ context.SetInputBuffer(data, sizeof(data));
+ EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
+ EXPECT_EQ(6u, context.skip_size_);
+ }
+ // Make sure not matching GIF**a
+ {
+ uint8_t data[] = {'G', 'I', 'F', '9', '2', 'a'};
+ context.SetInputBuffer(data, sizeof(data));
+ EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
+ EXPECT_EQ(6u, context.skip_size_);
+ }
+ // One valid signature
+ {
+ uint8_t data[] = {'G', 'I', 'F', '8', '7', 'a'};
+ context.SetInputBuffer(data, sizeof(data));
+ EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature());
+ EXPECT_EQ(6u, context.skip_size_);
+ }
+ // The other valid signature
+ {
+ uint8_t data[] = {'G', 'I', 'F', '8', '9', 'a'};
+ context.SetInputBuffer(data, sizeof(data));
+ EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature());
+ EXPECT_EQ(6u, context.skip_size_);
+ }
+}
+
+TEST(CFX_GifContext, ReadLocalScreenDescriptor) {
+ CFX_GifContextForTest context(nullptr, nullptr);
+ {
+ uint8_t data[1];
+ context.SetInputBuffer(data, 0);
+ EXPECT_EQ(CFX_GifDecodeStatus::Unfinished,
+ context.ReadLogicalScreenDescriptor());
+ }
+ // LSD with all the values zero'd
+ {
+ CFX_GifLocalScreenDescriptor lsd;
+ memset(&lsd, 0, sizeof(CFX_GifLocalScreenDescriptor));
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&lsd),
+ sizeof(CFX_GifLocalScreenDescriptor));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Success,
+ context.ReadLogicalScreenDescriptor());
+
+ EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor), context.skip_size_);
+ EXPECT_EQ(0, context.width_);
+ EXPECT_EQ(0, context.height_);
+ EXPECT_EQ(0u, context.bc_index_);
+ EXPECT_EQ(0u, context.pixel_aspect_);
+ }
+ // LSD with no global palette
+ {
+ CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 0}, 1, 2};
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&lsd),
+ sizeof(CFX_GifLocalScreenDescriptor));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Success,
+ context.ReadLogicalScreenDescriptor());
+
+ EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor), context.skip_size_);
+ EXPECT_EQ(0x000A, context.width_);
+ EXPECT_EQ(0x0F00, context.height_);
+ EXPECT_EQ(1u, context.bc_index_);
+ EXPECT_EQ(2u, context.pixel_aspect_);
+ }
+ // LSD with global palette bit set, but no global palette
+ {
+ CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 1}, 1, 2};
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&lsd),
+ sizeof(CFX_GifLocalScreenDescriptor));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Unfinished,
+ context.ReadLogicalScreenDescriptor());
+
+ EXPECT_EQ(0u, context.skip_size_);
+ }
+ // LSD with global palette
+ {
+ CFX_GifLocalScreenDescriptor lsd = {0x0A00, 0x000F, {1, 1, 2, 1}, 1, 2};
+ CFX_GifPalette palette[4] = {{0, 0, 0}, {1, 1, 1}, {1, 0, 0}, {0, 0, 1}};
+ struct {
+ CFX_GifLocalScreenDescriptor lsd;
+ CFX_GifPalette palette[4];
+ } data;
+ memcpy(&data.lsd, &lsd, sizeof(data.lsd));
+ memcpy(&data.palette, &palette, sizeof(data.lsd));
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Success,
+ context.ReadLogicalScreenDescriptor());
+
+ EXPECT_EQ(sizeof(data), context.skip_size_);
+ EXPECT_EQ(0x000A, context.width_);
+ EXPECT_EQ(0x0F00, context.height_);
+ EXPECT_EQ(1u, context.bc_index_);
+ EXPECT_EQ(2u, context.pixel_aspect_);
+ EXPECT_EQ(1u, context.global_pal_exp_);
+ EXPECT_EQ(1, context.global_sort_flag_);
+ EXPECT_EQ(2, context.global_color_resolution_);
+ EXPECT_TRUE(0 == memcmp(data.palette, context.global_palette_.data(),
+ sizeof(data.palette)));
+ }
+}
+
+TEST(CFX_GifContext, ReadHeader) {
+ CFX_GifContextForTest context(nullptr, nullptr);
+ // Bad signature
+ {
+ uint8_t signature[] = {'N', 'O', 'T', 'G', 'I', 'F'};
+ CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 0}, 1, 2};
+ struct {
+ uint8_t signature[6];
+ CFX_GifLocalScreenDescriptor lsd;
+ } data;
+ memcpy(&data.signature, signature, sizeof(data.signature));
+ memcpy(&data.lsd, &lsd, sizeof(data.lsd));
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadHeader());
+
+ EXPECT_EQ(sizeof(signature), context.skip_size_);
+ }
+ // Short after signature
+ {
+ uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'};
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&signature),
+ sizeof(signature));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader());
+
+ EXPECT_EQ(sizeof(signature), context.skip_size_);
+ }
+ // Success without global palette
+ {
+ uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'};
+ CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 0}, 1, 2};
+ struct {
+ uint8_t signature[6];
+ CFX_GifLocalScreenDescriptor lsd;
+ } data;
+ memcpy(&data.signature, signature, sizeof(data.signature));
+ memcpy(&data.lsd, &lsd, sizeof(data.lsd));
+
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader());
+
+ EXPECT_EQ(sizeof(data), context.skip_size_);
+ EXPECT_EQ(0x000A, context.width_);
+ EXPECT_EQ(0x0F00, context.height_);
+ EXPECT_EQ(1u, context.bc_index_);
+ EXPECT_EQ(2u, context.pixel_aspect_);
+ }
+ // Missing Global Palette
+ {
+ uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'};
+ CFX_GifLocalScreenDescriptor lsd{0x0A00, 0x000F, {0, 0, 0, 1}, 1, 2};
+
+ struct {
+ uint8_t signature[6];
+ CFX_GifLocalScreenDescriptor lsd;
+ } data;
+ memcpy(&data.signature, signature, sizeof(data.signature));
+ memcpy(&data.lsd, &lsd, sizeof(data.lsd));
+
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader());
+
+ EXPECT_EQ(sizeof(signature), context.skip_size_);
+ }
+ // Success with global palette
+ {
+ uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'};
+ CFX_GifLocalScreenDescriptor lsd = {0x0A00, 0x000F, {1, 1, 2, 1}, 1, 2};
+ CFX_GifPalette palette[4] = {{0, 0, 0}, {1, 1, 1}, {1, 0, 0}, {0, 0, 1}};
+
+ struct {
+ uint8_t signature[6];
+ CFX_GifLocalScreenDescriptor lsd;
+ CFX_GifPalette palette[4];
+ } data;
+ memcpy(&data.signature, signature, sizeof(data.signature));
+ memcpy(&data.lsd, &lsd, sizeof(data.lsd));
+ memcpy(&data.palette, &palette, sizeof(data.lsd));
+
+ context.SetInputBuffer(reinterpret_cast<uint8_t*>(&data), sizeof(data));
+
+ EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader());
+
+ EXPECT_EQ(sizeof(data), context.skip_size_);
+ EXPECT_EQ(0x000A, context.width_);
+ EXPECT_EQ(0x0F00, context.height_);
+ EXPECT_EQ(1u, context.bc_index_);
+ EXPECT_EQ(2u, context.pixel_aspect_);
+ EXPECT_EQ(1u, context.global_pal_exp_);
+ EXPECT_EQ(1, context.global_sort_flag_);
+ EXPECT_EQ(2, context.global_color_resolution_);
+ EXPECT_TRUE(0 == memcmp(data.palette, context.global_palette_.data(),
+ sizeof(data.palette)));
+ }
+}