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