From 0feba6f9ef721e4927e37da68ac27572ffae1453 Mon Sep 17 00:00:00 2001 From: Ryan Harrison Date: Tue, 3 Oct 2017 09:32:14 -0400 Subject: 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 Reviewed-by: dsinclair --- BUILD.gn | 1 + core/fxcodec/gif/cfx_gif.cpp | 3 + core/fxcodec/gif/cfx_gif.h | 9 +- core/fxcodec/gif/cfx_gifcontext.cpp | 98 +++++--- core/fxcodec/gif/cfx_gifcontext.h | 6 +- core/fxcodec/gif/cfx_gifcontext_unittest.cpp | 336 +++++++++++++++++++++++++++ 6 files changed, 409 insertions(+), 44 deletions(-) create mode 100644 core/fxcodec/gif/cfx_gifcontext_unittest.cpp 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(&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(&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( - FXWORD_GET_LSBFIRST(reinterpret_cast(&gif_lsd->width))); - height_ = static_cast( - FXWORD_GET_LSBFIRST(reinterpret_cast(&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(&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(&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( + FXWORD_GET_MSBFIRST(reinterpret_cast(&lsd->width))); + height_ = static_cast( + FXWORD_GET_MSBFIRST(reinterpret_cast(&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(&dest_buffer), 0)); + EXPECT_EQ(nullptr, + context.ReadData(reinterpret_cast(&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(&dest_buffer), 10)); + EXPECT_EQ(reinterpret_cast(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(&dest_buffer), 10)); + EXPECT_EQ(reinterpret_cast(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(&dest_buffer), 5)); + EXPECT_EQ(reinterpret_cast(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(&dest_buffer), 5)); + EXPECT_EQ(reinterpret_cast(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(&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(&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(&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(&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(&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(&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(&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(&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(&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(&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))); + } +} -- cgit v1.2.3