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