1 // Copyright 2016 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 // TODO(tsepez) this requires a lot more testing.
6
7 #include <stdint.h>
8
9 #include "core/fxcodec/jbig2/JBig2_Image.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/base/ptr_util.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_EQ(nullptr, img.GetLine(-1));
79 EXPECT_EQ(nullptr, 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_EQ(nullptr, 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_EQ(nullptr, 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_EQ(nullptr, 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 = pdfium::MakeUnique<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 // 1-px wide rectangle in image.
196 uint8_t pattern[5][8] = {
197 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
198 {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
199 {0x00, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00},
200 {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
201 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
202 };
203
204 // 1-px wide rectangle in image, offset 2 in x.
205 uint8_t pattern20[5][8] = {
206 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
207 {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
208 {0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00},
209 {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
210 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
211 };
212
213 // 1-px wide rectangle in image, offset 2 in x and y, padded.
214 uint8_t pattern22[5][8] = {
215 {0x00, 0x04, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00},
216 {0x00, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00},
217 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
218 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
219 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
220 };
221
222 // 1-px wide rectangle in image, offset 16 in x, 1 in y, padded.
223 uint8_t pattern161[5][8] = {
224 {0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
225 {0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
226 {0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
227 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
228 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
229 };
230
231 // Image size a nice clean power of two.
232 auto img32 = pdfium::MakeUnique<CJBig2_Image>(
233 32, 5, 8, reinterpret_cast<uint8_t*>(pattern));
234
235 // Image size not a nice clean value.
236 auto img37 = pdfium::MakeUnique<CJBig2_Image>(
237 37, 5, 8, reinterpret_cast<uint8_t*>(pattern));
238
239 // Expected results to check against.
240 auto expected20 = pdfium::MakeUnique<CJBig2_Image>(
241 30, 5, 8, reinterpret_cast<uint8_t*>(pattern20));
242
243 auto expected22 = pdfium::MakeUnique<CJBig2_Image>(
244 30, 5, 8, reinterpret_cast<uint8_t*>(pattern22));
245
246 auto expected161 = pdfium::MakeUnique<CJBig2_Image>(
247 25, 5, 8, reinterpret_cast<uint8_t*>(pattern161));
248
249 auto expected_zeros = pdfium::MakeUnique<CJBig2_Image>(32, 5);
250
251 // Empty subimage.
252 auto sub = img32->SubImage(0, 0, 0, 0);
253 EXPECT_EQ(sub->width(), 0);
254 EXPECT_EQ(sub->height(), 0);
255
256 // Full sub-image.
257 sub = img32->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 sub = img37->SubImage(0, 0, 32, 5);
263 EXPECT_EQ(sub->width(), 32);
264 EXPECT_EQ(sub->height(), 5);
265 CheckImageEq(img32.get(), sub.get(), __LINE__);
266
267 // Actual bit manipulations.
268 sub = img32->SubImage(2, 0, 30, 5);
269 CheckImageEq(expected20.get(), sub.get(), __LINE__);
270
271 sub = img37->SubImage(2, 2, 30, 5);
272 CheckImageEq(expected22.get(), sub.get(), __LINE__);
273
274 // Fast path.
275 sub = img37->SubImage(16, 1, 25, 5);
276 CheckImageEq(expected161.get(), sub.get(), __LINE__);
277
278 // Aligned Sub-image including cruft in stride beyond width.
279 sub = img37->SubImage(32, 0, 32, 5);
280 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
281
282 // Sub-image waaaaay beyond width.
283 sub = img37->SubImage(2000, 0, 32, 5);
284 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
285
286 // Sub-image waaaaay beyond height.
287 sub = img37->SubImage(0, 2000, 32, 5);
288 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
289
290 // Sub-image with negative x offset.
291 sub = img37->SubImage(-1, 0, 32, 5);
292 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
293
294 // Sub-image with negative y offset.
295 sub = img37->SubImage(0, -1, 32, 5);
296 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
297
298 // Sub-image with negative width.
299 sub = img37->SubImage(-1, 0, 32, 5);
300 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
301
302 // Sub-image with negative height.
303 sub = img37->SubImage(0, -1, 32, 5);
304 CheckImageEq(expected_zeros.get(), sub.get(), __LINE__);
305
306 // Sub-image wider than original.
307 sub = img37->SubImage(0, 0, 128, 5);
308 EXPECT_EQ(128, sub->width());
309 EXPECT_EQ(5, sub->height());
310
311 // Sub-image higher than original.
312 sub = img37->SubImage(0, 0, 32, 40);
313 EXPECT_EQ(32, sub->width());
314 EXPECT_EQ(40, sub->height());
315 }
316
TEST(fxcodec,JBig2CopyLine)317 TEST(fxcodec, JBig2CopyLine) {
318 // Horizontal line in image.
319 uint8_t pattern[3][8] = {
320 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
321 {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
322 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
323 };
324
325 uint8_t expected_pattern[3][8] = {
326 {0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00},
327 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
328 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
329 };
330
331 auto img = pdfium::MakeUnique<CJBig2_Image>(
332 37, 3, 8, reinterpret_cast<uint8_t*>(pattern));
333
334 auto expected = pdfium::MakeUnique<CJBig2_Image>(
335 37, 3, 8, reinterpret_cast<uint8_t*>(expected_pattern));
336
337 // Shuffle.
338 img->CopyLine(2, 1);
339 img->CopyLine(1, 0);
340 img->CopyLine(0, 2);
341
342 // Clear top line via invalid |from| offset.
343 img->CopyLine(2, 3);
344
345 // Copies with invalid |to|s don't mess with things.
346 img->CopyLine(-1, 0);
347 img->CopyLine(4, 0);
348 img->CopyLine(-1, -1);
349 img->CopyLine(4, 4);
350
351 CheckImageEq(expected.get(), img.get(), __LINE__);
352 }
353