1 // Copyright 2016 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 // TODO(tsepez) this requires a lot more testing.
6
7 #include "core/fxcodec/jbig2/JBig2_Image.h"
8
9 #include <stdint.h>
10
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 namespace {
14
15 const int32_t kWidthPixels = 80;
16 const int32_t kWidthBytes = 10;
17 const int32_t kStrideBytes = kWidthBytes + 2; // For testing stride != width.
18 const int32_t kHeightLines = 20;
19 const int32_t kLargerHeightLines = 100;
20 const int32_t kTooLargeHeightLines = 40000000;
21
CheckImageEq(CJBig2_Image * img1,CJBig2_Image * img2,int line)22 void CheckImageEq(CJBig2_Image* img1, CJBig2_Image* img2, int line) {
23 EXPECT_EQ(img1->width(), img2->width());
24 EXPECT_EQ(img1->height(), img2->height());
25 for (int32_t y = 0; y < img1->height(); ++y) {
26 for (int32_t x = 0; x < img1->width(); ++x) {
27 EXPECT_EQ(img1->GetPixel(x, y), img2->GetPixel(x, y))
28 << " at " << x << " " << y << " actual line " << line;
29 }
30 }
31 }
32
33 } // namespace
34
TEST(fxcodec,EmptyImage)35 TEST(fxcodec, EmptyImage) {
36 CJBig2_Image empty(0, 0);
37 EXPECT_EQ(empty.width(), 0);
38 EXPECT_EQ(empty.height(), 0);
39
40 // Out-of-bounds SetPixel() is silent no-op.
41 empty.SetPixel(0, 0, true);
42 empty.SetPixel(1, 1, true);
43
44 // Out-of-bounds GetPixel returns 0.
45 EXPECT_EQ(empty.GetPixel(0, 0), 0);
46 EXPECT_EQ(empty.GetPixel(1, 1), 0);
47
48 // Out-of-bounds GetLine() returns null.
49 EXPECT_EQ(empty.GetLine(0), nullptr);
50 EXPECT_EQ(empty.GetLine(1), nullptr);
51 }
52
TEST(fxcodec,JBig2ImageCreate)53 TEST(fxcodec, JBig2ImageCreate) {
54 CJBig2_Image img(kWidthPixels, kHeightLines);
55 EXPECT_EQ(kWidthPixels, img.width());
56 EXPECT_EQ(kHeightLines, img.height());
57 EXPECT_EQ(0, img.GetPixel(0, 0));
58 EXPECT_EQ(0, img.GetLine(0)[0]);
59 EXPECT_EQ(0, img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
60 EXPECT_EQ(0, img.GetLine(kHeightLines - 1)[kWidthBytes - 1]);
61
62 img.SetPixel(0, 0, true);
63 img.SetPixel(kWidthPixels - 1, kHeightLines - 1, true);
64 EXPECT_EQ(1, img.GetPixel(0, 0));
65 EXPECT_EQ(1, img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
66 EXPECT_EQ(0x80, img.GetLine(0)[0]);
67 EXPECT_EQ(0x01, img.GetLine(kHeightLines - 1)[kWidthBytes - 1]);
68
69 // Out-of-bounds SetPixel() is silent no-op.
70 img.SetPixel(-1, 1, true);
71 img.SetPixel(kWidthPixels, kHeightLines, true);
72
73 // Out-of-bounds GetPixel returns 0.
74 EXPECT_EQ(0, img.GetPixel(-1, -1));
75 EXPECT_EQ(0, img.GetPixel(kWidthPixels, kHeightLines));
76
77 // Out-of-bounds GetLine() returns null.
78 EXPECT_FALSE(img.GetLine(-1));
79 EXPECT_FALSE(img.GetLine(kHeightLines));
80 }
81
TEST(fxcodec,JBig2ImageCreateTooBig)82 TEST(fxcodec, JBig2ImageCreateTooBig) {
83 CJBig2_Image img(kWidthPixels, kTooLargeHeightLines);
84 EXPECT_EQ(0, img.width());
85 EXPECT_EQ(0, img.height());
86 EXPECT_FALSE(img.data());
87 }
88
TEST(fxcodec,JBig2ImageCreateExternal)89 TEST(fxcodec, JBig2ImageCreateExternal) {
90 uint8_t buf[kHeightLines * kStrideBytes];
91 CJBig2_Image img(kWidthPixels, kHeightLines, kStrideBytes, buf);
92 img.SetPixel(0, 0, true);
93 img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
94 EXPECT_EQ(kWidthPixels, img.width());
95 EXPECT_EQ(kHeightLines, img.height());
96 EXPECT_TRUE(img.GetPixel(0, 0));
97 EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
98 }
99
TEST(fxcodec,JBig2ImageCreateExternalTooBig)100 TEST(fxcodec, JBig2ImageCreateExternalTooBig) {
101 uint8_t buf[kHeightLines * kStrideBytes];
102 CJBig2_Image img(kWidthPixels, kTooLargeHeightLines, kStrideBytes, buf);
103 EXPECT_EQ(0, img.width());
104 EXPECT_EQ(0, img.height());
105 EXPECT_FALSE(img.data());
106 }
107
TEST(fxcodec,JBig2ImageCreateExternalBadStride)108 TEST(fxcodec, JBig2ImageCreateExternalBadStride) {
109 uint8_t buf[kHeightLines * kStrideBytes];
110 CJBig2_Image img(kWidthPixels, kTooLargeHeightLines, kStrideBytes - 1, buf);
111 EXPECT_EQ(0, img.width());
112 EXPECT_EQ(0, img.height());
113 EXPECT_FALSE(img.data());
114 }
115
TEST(fxcodec,JBig2ImageExpand)116 TEST(fxcodec, JBig2ImageExpand) {
117 CJBig2_Image img(kWidthPixels, kHeightLines);
118 img.SetPixel(0, 0, true);
119 img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
120 img.Expand(kLargerHeightLines, true);
121 EXPECT_EQ(kWidthPixels, img.width());
122 EXPECT_EQ(kLargerHeightLines, img.height());
123 EXPECT_TRUE(img.GetPixel(0, 0));
124 EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
125 EXPECT_TRUE(img.GetPixel(kWidthPixels - 1, kLargerHeightLines - 1));
126 }
127
TEST(fxcodec,JBig2ImageExpandTooBig)128 TEST(fxcodec, JBig2ImageExpandTooBig) {
129 CJBig2_Image img(kWidthPixels, kHeightLines);
130 img.SetPixel(0, 0, true);
131 img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
132 img.Expand(kTooLargeHeightLines, true);
133 EXPECT_EQ(kWidthPixels, img.width());
134 EXPECT_EQ(kHeightLines, img.height());
135 EXPECT_TRUE(img.GetPixel(0, 0));
136 EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
137 }
138
TEST(fxcodec,JBig2ImageExpandExternal)139 TEST(fxcodec, JBig2ImageExpandExternal) {
140 uint8_t buf[kHeightLines * kStrideBytes];
141 CJBig2_Image img(kWidthPixels, kHeightLines, kStrideBytes, buf);
142 img.SetPixel(0, 0, true);
143 img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
144 img.Expand(kLargerHeightLines, true);
145 EXPECT_EQ(kWidthPixels, img.width());
146 EXPECT_EQ(kLargerHeightLines, img.height());
147 EXPECT_TRUE(img.GetPixel(0, 0));
148 EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
149 EXPECT_TRUE(img.GetPixel(kWidthPixels - 1, kLargerHeightLines - 1));
150 }
151
TEST(fxcodec,JBig2ImageExpandExternalTooBig)152 TEST(fxcodec, JBig2ImageExpandExternalTooBig) {
153 uint8_t buf[kHeightLines * kStrideBytes];
154 CJBig2_Image img(kWidthPixels, kHeightLines, kStrideBytes, buf);
155 img.SetPixel(0, 0, true);
156 img.SetPixel(kWidthPixels - 1, kHeightLines - 1, false);
157 img.Expand(kTooLargeHeightLines, true);
158 EXPECT_EQ(kWidthPixels, img.width());
159 EXPECT_EQ(kHeightLines, img.height());
160 EXPECT_TRUE(img.GetPixel(0, 0));
161 EXPECT_FALSE(img.GetPixel(kWidthPixels - 1, kHeightLines - 1));
162 }
163
TEST(fxcodec,JBig2EmptyImage)164 TEST(fxcodec, JBig2EmptyImage) {
165 auto empty = std::make_unique<CJBig2_Image>(0, 0);
166
167 // Empty subimage.
168 auto sub1 = empty->SubImage(0, 0, 0, 0);
169 EXPECT_EQ(sub1->width(), 0);
170 EXPECT_EQ(sub1->height(), 0);
171
172 // Larger dimensions are zero-padded.
173 auto sub2 = empty->SubImage(0, 0, 1, 1);
174 EXPECT_EQ(1, sub2->width());
175 EXPECT_EQ(1, sub2->height());
176 EXPECT_EQ(0, sub2->GetPixel(0, 0));
177
178 // Bad dimensions give an empty image.
179 sub2 = empty->SubImage(0, 0, -1, -1);
180 EXPECT_EQ(sub2->width(), 0);
181 EXPECT_EQ(sub2->height(), 0);
182
183 // Bad offsets zero pad the image.
184 auto sub3 = empty->SubImage(-1, -1, 2, 2);
185 EXPECT_EQ(sub3->width(), 2);
186 EXPECT_EQ(sub3->height(), 2);
187
188 // Bad dimensions and bad offsets give an empty image.
189 sub3 = empty->SubImage(-1, -1, -100, -100);
190 EXPECT_EQ(sub3->width(), 0);
191 EXPECT_EQ(sub3->height(), 0);
192 }
193
TEST(fxcodec,JBig2SubImage)194 TEST(fxcodec, JBig2SubImage) {
195 // clang-format off
196 // 1-px wide rectangle in image.
197 uint8_t pattern[40] = {
198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
199 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
200 0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
201 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
202 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
203 };
204
205 // 1-px wide rectangle in image, offset 2 in x.
206 uint8_t pattern20[40] = {
207 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
208 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
209 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
210 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
212 };
213
214 // 1-px wide rectangle in image, offset 2 in x and y, padded.
215 uint8_t pattern22[40] = {
216 0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
217 0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221 };
222
223 // 1-px wide rectangle in image, offset 16 in x, 1 in y, padded.
224 uint8_t pattern161[40] = {
225 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
226 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
227 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
228 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230 };
231 // clang-format on
232
233 // Image size a nice clean power of two.
234 auto img32 = std::make_unique<CJBig2_Image>(32, 5, 8, pattern);
235
236 // Image size not a nice clean value.
237 auto img37 = std::make_unique<CJBig2_Image>(37, 5, 8, pattern);
238
239 // Expected results to check against.
240 auto expected20 = std::make_unique<CJBig2_Image>(30, 5, 8, pattern20);
241 auto expected22 = std::make_unique<CJBig2_Image>(30, 5, 8, pattern22);
242 auto expected161 = std::make_unique<CJBig2_Image>(25, 5, 8, pattern161);
243 auto expected_zeros = std::make_unique<CJBig2_Image>(32, 5);
244
245 // Empty subimage.
246 auto sub = img32->SubImage(0, 0, 0, 0);
247 EXPECT_EQ(sub->width(), 0);
248 EXPECT_EQ(sub->height(), 0);
249
250 // Full sub-image.
251 sub = img32->SubImage(0, 0, 32, 5);
252 EXPECT_EQ(sub->width(), 32);
253 EXPECT_EQ(sub->height(), 5);
254 CheckImageEq(img32.get(), sub.get(), __LINE__);
255
256 sub = img37->SubImage(0, 0, 32, 5);
257 EXPECT_EQ(sub->width(), 32);
258 EXPECT_EQ(sub->height(), 5);
259 CheckImageEq(img32.get(), sub.get(), __LINE__);
260
261 // Actual bit manipulations.
262 sub = img32->SubImage(2, 0, 30, 5);
263 CheckImageEq(expected20.get(), sub.get(), __LINE__);
264
265 sub = img37->SubImage(2, 2, 30, 5);
266 CheckImageEq(expected22.get(), sub.get(), __LINE__);
267
268 // Fast path.
269 sub = img37->SubImage(16, 1, 25, 5);
270 CheckImageEq(expected161.get(), sub.get(), __LINE__);
271
272 // Aligned Sub-image including cruft in stride beyond width.
273 sub = img37->SubImage(32, 0, 32, 5);
274 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
275
276 // Sub-image waaaaay beyond width.
277 sub = img37->SubImage(2000, 0, 32, 5);
278 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
279
280 // Sub-image waaaaay beyond height.
281 sub = img37->SubImage(0, 2000, 32, 5);
282 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
283
284 // Sub-image with negative x offset.
285 sub = img37->SubImage(-1, 0, 32, 5);
286 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
287
288 // Sub-image with negative y offset.
289 sub = img37->SubImage(0, -1, 32, 5);
290 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
291
292 // Sub-image with negative width.
293 sub = img37->SubImage(-1, 0, 32, 5);
294 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
295
296 // Sub-image with negative height.
297 sub = img37->SubImage(0, -1, 32, 5);
298 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
299
300 // Sub-image wider than original.
301 sub = img37->SubImage(0, 0, 128, 5);
302 EXPECT_EQ(128, sub->width());
303 EXPECT_EQ(5, sub->height());
304
305 // Sub-image higher than original.
306 sub = img37->SubImage(0, 0, 32, 40);
307 EXPECT_EQ(32, sub->width());
308 EXPECT_EQ(40, sub->height());
309 }
310
TEST(fxcodec,JBig2CopyLine)311 TEST(fxcodec, JBig2CopyLine) {
312 // clang-format off
313 // Horizontal line in image.
314 uint8_t pattern[24] = {
315 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
316 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
317 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
318 };
319
320 uint8_t expected_pattern[24] = {
321 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
322 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
324 };
325 // clang-format on
326
327 auto img = std::make_unique<CJBig2_Image>(37, 3, 8, pattern);
328 auto expected = std::make_unique<CJBig2_Image>(37, 3, 8, expected_pattern);
329
330 // Shuffle.
331 img->CopyLine(2, 1);
332 img->CopyLine(1, 0);
333 img->CopyLine(0, 2);
334
335 // Clear top line via invalid |from| offset.
336 img->CopyLine(2, 3);
337
338 // Copies with invalid |to|s don't mess with things.
339 img->CopyLine(-1, 0);
340 img->CopyLine(4, 0);
341 img->CopyLine(-1, -1);
342 img->CopyLine(4, 4);
343
344 CheckImageEq(expected.get(), img.get(), __LINE__);
345 }
346