xref: /aosp_15_r20/external/pdfium/core/fxcodec/jbig2/JBig2_Image_unittest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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