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