• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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