1 // Copyright 2017 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "core/fxcodec/gif/cfx_gifcontext.h"
6
7 #include <utility>
8
9 #include "core/fxcodec/cfx_codec_memory.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11
12 namespace fxcodec {
13
14 class CFX_GifContextForTest final : public CFX_GifContext {
15 public:
CFX_GifContextForTest(GifModule * gif_module,GifModule::Delegate * delegate)16 CFX_GifContextForTest(GifModule* gif_module, GifModule::Delegate* delegate)
17 : CFX_GifContext(gif_module, delegate) {}
~CFX_GifContextForTest()18 ~CFX_GifContextForTest() override {}
19
20 using CFX_GifContext::ReadAllOrNone;
21 using CFX_GifContext::ReadGifSignature;
22 using CFX_GifContext::ReadLogicalScreenDescriptor;
23
InputBuffer() const24 CFX_CodecMemory* InputBuffer() const { return input_buffer_.Get(); }
SetTestInputBuffer(pdfium::span<uint8_t> input)25 void SetTestInputBuffer(pdfium::span<uint8_t> input) {
26 auto pMemory = pdfium::MakeRetain<CFX_CodecMemory>(input.size());
27 memcpy(pMemory->GetBuffer(), input.data(), input.size());
28 SetInputBuffer(std::move(pMemory));
29 }
30 };
31
TEST(CFX_GifContext,SetInputBuffer)32 TEST(CFX_GifContext, SetInputBuffer) {
33 uint8_t buffer[] = {0x00, 0x01, 0x02};
34 CFX_GifContextForTest context(nullptr, nullptr);
35
36 context.SetTestInputBuffer({nullptr, 0});
37 EXPECT_EQ(0u, context.InputBuffer()->GetSize());
38 EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
39
40 context.SetTestInputBuffer({buffer, 0});
41 EXPECT_EQ(0u, context.InputBuffer()->GetSize());
42 EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
43
44 context.SetTestInputBuffer({buffer, 3});
45 EXPECT_EQ(3u, context.InputBuffer()->GetSize());
46 EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
47 }
48
TEST(CFX_GifContext,ReadAllOrNone)49 TEST(CFX_GifContext, ReadAllOrNone) {
50 std::vector<uint8_t> dest_buffer;
51 uint8_t src_buffer[] = {0x00, 0x01, 0x02, 0x03, 0x04,
52 0x05, 0x06, 0x07, 0x08, 0x09};
53 CFX_GifContextForTest context(nullptr, nullptr);
54
55 context.SetTestInputBuffer({nullptr, 0});
56 EXPECT_FALSE(context.ReadAllOrNone(nullptr, 0));
57 EXPECT_FALSE(context.ReadAllOrNone(nullptr, 10));
58
59 EXPECT_FALSE(context.ReadAllOrNone(dest_buffer.data(), 0));
60 EXPECT_FALSE(context.ReadAllOrNone(dest_buffer.data(), 10));
61
62 context.SetTestInputBuffer({src_buffer, 0});
63 dest_buffer.resize(sizeof(src_buffer));
64 EXPECT_FALSE(context.ReadAllOrNone(dest_buffer.data(), sizeof(src_buffer)));
65
66 context.SetTestInputBuffer({src_buffer, 1});
67 EXPECT_FALSE(context.ReadAllOrNone(dest_buffer.data(), sizeof(src_buffer)));
68 EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
69 EXPECT_FALSE(context.ReadAllOrNone(nullptr, sizeof(src_buffer)));
70 EXPECT_FALSE(context.ReadAllOrNone(nullptr, 1));
71 EXPECT_TRUE(context.ReadAllOrNone(dest_buffer.data(), 1));
72 EXPECT_EQ(src_buffer[0], dest_buffer[0]);
73
74 context.SetTestInputBuffer(src_buffer);
75 EXPECT_FALSE(context.ReadAllOrNone(nullptr, sizeof(src_buffer)));
76 EXPECT_TRUE(context.ReadAllOrNone(dest_buffer.data(), sizeof(src_buffer)));
77 for (size_t i = 0; i < sizeof(src_buffer); i++)
78 EXPECT_EQ(src_buffer[i], dest_buffer[i]);
79
80 context.SetTestInputBuffer(src_buffer);
81 for (size_t i = 0; i < sizeof(src_buffer); i++) {
82 EXPECT_TRUE(context.ReadAllOrNone(dest_buffer.data(), 1));
83 EXPECT_EQ(src_buffer[i], dest_buffer[0]);
84 }
85 }
86
TEST(CFX_GifContext,ReadGifSignature)87 TEST(CFX_GifContext, ReadGifSignature) {
88 CFX_GifContextForTest context(nullptr, nullptr);
89 {
90 uint8_t data[1];
91 context.SetTestInputBuffer({data, 0});
92 EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature());
93 EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
94 context.SetTestInputBuffer({});
95 }
96 // Make sure testing the entire signature
97 {
98 uint8_t data[] = {'G', 'I', 'F'};
99 context.SetTestInputBuffer(data);
100 EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadGifSignature());
101 EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
102 context.SetTestInputBuffer({});
103 }
104 {
105 uint8_t data[] = {'N', 'O', 'T', 'G', 'I', 'F'};
106 context.SetTestInputBuffer(data);
107 EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
108 EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
109 context.SetTestInputBuffer({});
110 }
111 // Make sure not matching GIF8*a
112 {
113 uint8_t data[] = {'G', 'I', 'F', '8', '0', 'a'};
114 context.SetTestInputBuffer(data);
115 EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
116 EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
117 context.SetTestInputBuffer({});
118 }
119 // Make sure not matching GIF**a
120 {
121 uint8_t data[] = {'G', 'I', 'F', '9', '2', 'a'};
122 context.SetTestInputBuffer(data);
123 EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadGifSignature());
124 EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
125 context.SetTestInputBuffer({});
126 }
127 // One valid signature
128 {
129 uint8_t data[] = {'G', 'I', 'F', '8', '7', 'a'};
130 context.SetTestInputBuffer(data);
131 EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature());
132 EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
133 context.SetTestInputBuffer({});
134 }
135 // The other valid signature
136 {
137 uint8_t data[] = {'G', 'I', 'F', '8', '9', 'a'};
138 context.SetTestInputBuffer(data);
139 EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadGifSignature());
140 EXPECT_EQ(6u, context.InputBuffer()->GetPosition());
141 context.SetTestInputBuffer({});
142 }
143 }
144
TEST(CFX_GifContext,ReadLocalScreenDescriptor)145 TEST(CFX_GifContext, ReadLocalScreenDescriptor) {
146 CFX_GifContextForTest context(nullptr, nullptr);
147 {
148 uint8_t data[1];
149 context.SetTestInputBuffer({data, 0});
150 EXPECT_EQ(CFX_GifDecodeStatus::Unfinished,
151 context.ReadLogicalScreenDescriptor());
152 context.SetTestInputBuffer({});
153 }
154 // LSD with all the values zero'd
155 {
156 uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
157 memset(&lsd, 0, sizeof(CFX_GifLocalScreenDescriptor));
158 context.SetTestInputBuffer(lsd);
159
160 EXPECT_EQ(CFX_GifDecodeStatus::Success,
161 context.ReadLogicalScreenDescriptor());
162
163 EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor),
164 static_cast<size_t>(context.InputBuffer()->GetPosition()));
165 EXPECT_EQ(0, context.width_);
166 EXPECT_EQ(0, context.height_);
167 EXPECT_EQ(0u, context.bc_index_);
168 context.SetTestInputBuffer({});
169 }
170 // LSD with no global palette
171 {
172 uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)] = {0x0A, 0x00, 0x00, 0x0F,
173 0x00, 0x01, 0x02};
174 context.SetTestInputBuffer(lsd);
175
176 EXPECT_EQ(CFX_GifDecodeStatus::Success,
177 context.ReadLogicalScreenDescriptor());
178
179 EXPECT_EQ(sizeof(CFX_GifLocalScreenDescriptor),
180 static_cast<size_t>(context.InputBuffer()->GetPosition()));
181 EXPECT_EQ(0x000A, context.width_);
182 EXPECT_EQ(0x0F00, context.height_);
183 EXPECT_EQ(0u, context.bc_index_); // bc_index_ is 0 if no global palette
184 context.SetTestInputBuffer({});
185 }
186 // LSD with global palette bit set, but no global palette
187 {
188 uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)] = {0x0A, 0x00, 0x00, 0x0F,
189 0x80, 0x01, 0x02};
190 context.SetTestInputBuffer(lsd);
191
192 EXPECT_EQ(CFX_GifDecodeStatus::Unfinished,
193 context.ReadLogicalScreenDescriptor());
194
195 EXPECT_EQ(0u, context.InputBuffer()->GetPosition());
196 context.SetTestInputBuffer({});
197 }
198 // LSD with global palette
199 {
200 struct {
201 uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
202 uint8_t palette[4 * sizeof(CFX_GifPalette)];
203 } data = {{0x0A, 0x00, 0x00, 0x0F, 0xA9, 0x01, 0x02},
204 {0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1}};
205 context.SetTestInputBuffer(
206 {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
207
208 EXPECT_EQ(CFX_GifDecodeStatus::Success,
209 context.ReadLogicalScreenDescriptor());
210
211 EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
212 EXPECT_EQ(0x000A, context.width_);
213 EXPECT_EQ(0x0F00, context.height_);
214 EXPECT_EQ(1u, context.bc_index_);
215 EXPECT_EQ(1u, context.global_pal_exp_);
216 EXPECT_EQ(1, context.global_sort_flag_);
217 EXPECT_EQ(2, context.global_color_resolution_);
218 EXPECT_EQ(0, memcmp(data.palette, context.global_palette_.data(),
219 sizeof(data.palette)));
220 context.SetTestInputBuffer({});
221 }
222 }
223
TEST(CFX_GifContext,ReadHeader)224 TEST(CFX_GifContext, ReadHeader) {
225 CFX_GifContextForTest context(nullptr, nullptr);
226 // Bad signature
227 {
228 struct {
229 uint8_t signature[6];
230 uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
231 } data = {{'N', 'O', 'T', 'G', 'I', 'F'},
232 {0x0A, 0x00, 0x00, 0x0F, 0x00, 0x01, 0x02}};
233 context.SetTestInputBuffer(
234 {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
235
236 EXPECT_EQ(CFX_GifDecodeStatus::Error, context.ReadHeader());
237 EXPECT_EQ(sizeof(data.signature), context.InputBuffer()->GetPosition());
238 context.SetTestInputBuffer({});
239 }
240 // Short after signature
241 {
242 uint8_t signature[] = {'G', 'I', 'F', '8', '7', 'a'};
243 context.SetTestInputBuffer(
244 {reinterpret_cast<uint8_t*>(&signature), sizeof(signature)});
245
246 EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader());
247 EXPECT_EQ(sizeof(signature), context.InputBuffer()->GetPosition());
248 context.SetTestInputBuffer({});
249 }
250 // Success without global palette
251 {
252 struct {
253 uint8_t signature[6];
254 uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
255 } data = {{'G', 'I', 'F', '8', '7', 'a'},
256 {0x0A, 0x00, 0x00, 0x0F, 0x00, 0x01, 0x02}};
257 context.SetTestInputBuffer(
258 {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
259
260 EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader());
261 EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
262 EXPECT_EQ(0x000A, context.width_);
263 EXPECT_EQ(0x0F00, context.height_);
264 EXPECT_EQ(0u, context.bc_index_); // bc_index_ is 0 if no global palette
265 context.SetTestInputBuffer({});
266 }
267 // Missing Global Palette
268 {
269 struct {
270 uint8_t signature[6];
271 uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
272 } data = {{'G', 'I', 'F', '8', '7', 'a'},
273 {0x0A, 0x00, 0x00, 0x0F, 0x80, 0x01, 0x02}};
274 context.SetTestInputBuffer(
275 {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
276
277 EXPECT_EQ(CFX_GifDecodeStatus::Unfinished, context.ReadHeader());
278 EXPECT_EQ(sizeof(data.signature), context.InputBuffer()->GetPosition());
279 context.SetTestInputBuffer({});
280 }
281 // Success with global palette
282 {
283 struct {
284 uint8_t signature[6];
285 uint8_t lsd[sizeof(CFX_GifLocalScreenDescriptor)];
286 uint8_t palette[4 * sizeof(CFX_GifPalette)];
287 } data = {{'G', 'I', 'F', '8', '7', 'a'},
288 {0x0A, 0x00, 0x00, 0x0F, 0xA9, 0x01, 0x02},
289 {0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1}};
290 context.SetTestInputBuffer(
291 {reinterpret_cast<uint8_t*>(&data), sizeof(data)});
292
293 EXPECT_EQ(CFX_GifDecodeStatus::Success, context.ReadHeader());
294 EXPECT_EQ(sizeof(data), context.InputBuffer()->GetPosition());
295 EXPECT_EQ(0x000A, context.width_);
296 EXPECT_EQ(0x0F00, context.height_);
297 EXPECT_EQ(1u, context.bc_index_);
298 EXPECT_EQ(1u, context.global_pal_exp_);
299 EXPECT_EQ(1, context.global_sort_flag_);
300 EXPECT_EQ(2, context.global_color_resolution_);
301 EXPECT_EQ(0, memcmp(data.palette, context.global_palette_.data(),
302 sizeof(data.palette)));
303 context.SetTestInputBuffer({});
304 }
305 }
306
307 } // namespace fxcodec
308