2 * Copyright (C)2011-2018, 2022-2023 D. R. Commander. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * - Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * - Redistributions in binary form must reproduce the above copyright notice,
10 * this list of conditions and the following disclaimer in the documentation
11 * and/or other materials provided with the distribution.
12 * - Neither the name of the libjpeg-turbo Project nor the names of its
13 * contributors may be used to endorse or promote products derived from this
14 * software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
30 * This program tests the various code paths in the TurboJPEG JNI Wrapper
35 import java.awt.image.*;
36 import javax.imageio.*;
38 import org.libjpegturbo.turbojpeg.*;
40 @SuppressWarnings("checkstyle:JavadocType")
41 final class TJUnitTest {
43 private TJUnitTest() {}
45 static final String CLASS_NAME =
46 new TJUnitTest().getClass().getName();
49 System.out.println("\nUSAGE: java " + CLASS_NAME + " [options]\n");
50 System.out.println("Options:");
51 System.out.println("-yuv = test YUV encoding/compression/decompression/decoding");
52 System.out.println(" (8-bit data precision only)");
53 System.out.println("-noyuvpad = do not pad each row in each Y, U, and V plane to the nearest");
54 System.out.println(" multiple of 4 bytes");
55 System.out.println("-precision N = test N-bit data precision (N is 8, 12, or 16; default is 8; if N");
56 System.out.println(" is 16, then -lossless is implied)");
57 System.out.println("-lossless = test lossless JPEG compression/decompression");
58 System.out.println("-bi = test BufferedImage I/O (8-bit data precision only)\n");
62 static final String[] SUBNAME_LONG = {
63 "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1"
65 static final String[] SUBNAME = {
66 "444", "422", "420", "GRAY", "440", "411", "441"
69 static final String[] PIXFORMATSTR = {
70 "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale",
71 "RGBA", "BGRA", "ABGR", "ARGB", "CMYK"
74 static final int[] FORMATS_3SAMPLE = {
77 static final int[] FORMATS_3BYTEBI = {
78 BufferedImage.TYPE_3BYTE_BGR
80 static final int[] FORMATS_4SAMPLE = {
81 TJ.PF_RGBX, TJ.PF_BGRX, TJ.PF_XBGR, TJ.PF_XRGB, TJ.PF_CMYK
83 static final int[] FORMATS_4BYTEBI = {
84 BufferedImage.TYPE_INT_BGR, BufferedImage.TYPE_INT_RGB,
85 BufferedImage.TYPE_4BYTE_ABGR, BufferedImage.TYPE_4BYTE_ABGR_PRE,
86 BufferedImage.TYPE_INT_ARGB, BufferedImage.TYPE_INT_ARGB_PRE
88 static final int[] FORMATS_GRAY = {
91 static final int[] FORMATS_GRAYBI = {
92 BufferedImage.TYPE_BYTE_GRAY
94 static final int[] FORMATS_RGB = {
98 private static boolean doYUV = false;
99 private static boolean lossless = false;
100 private static int psv = 1;
101 private static int yuvAlign = 4;
102 private static int precision = 8;
103 private static int sampleSize, maxSample, tolerance, redToY, yellowToY;
104 private static boolean bi = false;
106 private static int exitStatus = 0;
108 static int biTypePF(int biType) {
109 ByteOrder byteOrder = ByteOrder.nativeOrder();
111 case BufferedImage.TYPE_3BYTE_BGR:
113 case BufferedImage.TYPE_4BYTE_ABGR:
114 case BufferedImage.TYPE_4BYTE_ABGR_PRE:
116 case BufferedImage.TYPE_BYTE_GRAY:
118 case BufferedImage.TYPE_INT_BGR:
120 case BufferedImage.TYPE_INT_RGB:
122 case BufferedImage.TYPE_INT_ARGB:
123 case BufferedImage.TYPE_INT_ARGB_PRE:
130 static String biTypeStr(int biType) {
132 case BufferedImage.TYPE_3BYTE_BGR:
134 case BufferedImage.TYPE_4BYTE_ABGR:
136 case BufferedImage.TYPE_4BYTE_ABGR_PRE:
137 return "4BYTE_ABGR_PRE";
138 case BufferedImage.TYPE_BYTE_GRAY:
140 case BufferedImage.TYPE_INT_BGR:
142 case BufferedImage.TYPE_INT_RGB:
144 case BufferedImage.TYPE_INT_ARGB:
146 case BufferedImage.TYPE_INT_ARGB_PRE:
147 return "INT_ARGB_PRE";
153 static void fillArray(Object buf, int val) {
155 Arrays.fill((byte[])buf, (byte)val);
157 Arrays.fill((short[])buf, (short)val);
160 static void setVal(Object buf, int index, int value) {
162 ((byte[])buf)[index] = (byte)value;
164 ((short[])buf)[index] = (short)value;
167 static void initBuf(Object buf, int w, int pitch, int h, int pf,
168 boolean bottomUp) throws Exception {
169 int roffset = TJ.getRedOffset(pf);
170 int goffset = TJ.getGreenOffset(pf);
171 int boffset = TJ.getBlueOffset(pf);
172 int aoffset = TJ.getAlphaOffset(pf);
173 int ps = TJ.getPixelSize(pf);
174 int index, row, col, halfway = 16;
176 if (pf == TJ.PF_GRAY) {
178 for (row = 0; row < h; row++) {
179 for (col = 0; col < w; col++) {
181 index = pitch * (h - row - 1) + col;
183 index = pitch * row + col;
184 if (((row / 8) + (col / 8)) % 2 == 0)
185 setVal(buf, index, (row < halfway) ? maxSample : 0);
187 setVal(buf, index, (row < halfway) ? redToY : yellowToY);
192 if (pf == TJ.PF_CMYK) {
193 fillArray(buf, maxSample);
194 for (row = 0; row < h; row++) {
195 for (col = 0; col < w; col++) {
197 index = (h - row - 1) * w + col;
199 index = row * w + col;
200 if (((row / 8) + (col / 8)) % 2 == 0) {
201 if (row >= halfway) setVal(buf, index * ps + 3, 0);
203 setVal(buf, index * ps + 2, 0);
205 setVal(buf, index * ps + 1, 0);
213 for (row = 0; row < h; row++) {
214 for (col = 0; col < w; col++) {
216 index = pitch * (h - row - 1) + col * ps;
218 index = pitch * row + col * ps;
219 if (((row / 8) + (col / 8)) % 2 == 0) {
221 setVal(buf, index + roffset, maxSample);
222 setVal(buf, index + goffset, maxSample);
223 setVal(buf, index + boffset, maxSample);
226 setVal(buf, index + roffset, maxSample);
228 setVal(buf, index + goffset, maxSample);
231 setVal(buf, index + aoffset, maxSample);
236 static void initIntBuf(int[] buf, int w, int pitch, int h, int pf,
237 boolean bottomUp) throws Exception {
238 int rshift = TJ.getRedOffset(pf) * 8;
239 int gshift = TJ.getGreenOffset(pf) * 8;
240 int bshift = TJ.getBlueOffset(pf) * 8;
241 int ashift = TJ.getAlphaOffset(pf) * 8;
242 int index, row, col, halfway = 16;
245 for (row = 0; row < h; row++) {
246 for (col = 0; col < w; col++) {
248 index = pitch * (h - row - 1) + col;
250 index = pitch * row + col;
251 if (((row / 8) + (col / 8)) % 2 == 0) {
253 buf[index] |= (255 << rshift);
254 buf[index] |= (255 << gshift);
255 buf[index] |= (255 << bshift);
258 buf[index] |= (255 << rshift);
260 buf[index] |= (255 << gshift);
263 buf[index] |= (255 << ashift);
268 static void initImg(BufferedImage img, int pf, boolean bottomUp)
270 WritableRaster wr = img.getRaster();
271 int imgType = img.getType();
273 if (imgType == BufferedImage.TYPE_INT_RGB ||
274 imgType == BufferedImage.TYPE_INT_BGR ||
275 imgType == BufferedImage.TYPE_INT_ARGB ||
276 imgType == BufferedImage.TYPE_INT_ARGB_PRE) {
277 SinglePixelPackedSampleModel sm =
278 (SinglePixelPackedSampleModel)img.getSampleModel();
279 int pitch = sm.getScanlineStride();
280 DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
281 int[] buf = db.getData();
282 initIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, bottomUp);
284 ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel();
285 int pitch = sm.getScanlineStride();
286 DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
287 byte[] buf = db.getData();
288 initBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, bottomUp);
292 static void checkVal(int row, int col, int v, String vname, int cv)
294 v = (v < 0) ? v + 256 : v;
295 if (v < cv - tolerance || v > cv + tolerance) {
296 throw new Exception("Comp. " + vname + " at " + row + "," + col +
297 " should be " + cv + ", not " + v);
301 static void checkVal0(int row, int col, int v, String vname)
303 v = (v < 0) ? v + 256 : v;
305 throw new Exception("Comp. " + vname + " at " + row + "," + col +
306 " should be 0, not " + v);
310 static void checkValMax(int row, int col, int v, String vname)
312 v = (v < 0) ? v + 256 : v;
313 if (v < maxSample - tolerance) {
314 throw new Exception("Comp. " + vname + " at " + row + "," + col +
315 " should be " + maxSample + ", not " + v);
319 static int getVal(Object buf, int index) {
322 v = (int)(((byte[])buf)[index]);
324 v = (int)(((short[])buf)[index]);
330 static int checkBuf(Object buf, int w, int pitch, int h, int pf, int subsamp,
331 TJScalingFactor sf, boolean bottomUp) throws Exception {
332 int roffset = TJ.getRedOffset(pf);
333 int goffset = TJ.getGreenOffset(pf);
334 int boffset = TJ.getBlueOffset(pf);
335 int aoffset = TJ.getAlphaOffset(pf);
336 int ps = TJ.getPixelSize(pf);
337 int index, row, col, retval = 1;
338 int halfway = 16 * sf.getNum() / sf.getDenom();
339 int blockSize = 8 * sf.getNum() / sf.getDenom();
343 if (pf == TJ.PF_GRAY)
344 roffset = goffset = boffset = 0;
346 if (pf == TJ.PF_CMYK) {
347 for (row = 0; row < h; row++) {
348 for (col = 0; col < w; col++) {
350 index = (h - row - 1) * w + col;
352 index = row * w + col;
353 int c = getVal(buf, index * ps);
354 int m = getVal(buf, index * ps + 1);
355 int y = getVal(buf, index * ps + 2);
356 int k = getVal(buf, index * ps + 3);
357 checkValMax(row, col, c, "C");
358 if (((row / blockSize) + (col / blockSize)) % 2 == 0) {
359 checkValMax(row, col, m, "M");
360 checkValMax(row, col, y, "Y");
362 checkValMax(row, col, k, "K");
364 checkVal0(row, col, k, "K");
366 checkVal0(row, col, y, "Y");
367 checkValMax(row, col, k, "K");
369 checkVal0(row, col, m, "M");
371 checkValMax(row, col, m, "M");
378 for (row = 0; row < halfway; row++) {
379 for (col = 0; col < w; col++) {
381 index = pitch * (h - row - 1) + col * ps;
383 index = pitch * row + col * ps;
384 int r = getVal(buf, index + roffset);
385 int g = getVal(buf, index + goffset);
386 int b = getVal(buf, index + boffset);
387 int a = aoffset >= 0 ? getVal(buf, index + aoffset) : maxSample;
388 if (((row / blockSize) + (col / blockSize)) % 2 == 0) {
390 checkValMax(row, col, r, "R");
391 checkValMax(row, col, g, "G");
392 checkValMax(row, col, b, "B");
394 checkVal0(row, col, r, "R");
395 checkVal0(row, col, g, "G");
396 checkVal0(row, col, b, "B");
399 if (subsamp == TJ.SAMP_GRAY) {
401 checkVal(row, col, r, "R", redToY);
402 checkVal(row, col, g, "G", redToY);
403 checkVal(row, col, b, "B", redToY);
405 checkVal(row, col, r, "R", yellowToY);
406 checkVal(row, col, g, "G", yellowToY);
407 checkVal(row, col, b, "B", yellowToY);
410 checkValMax(row, col, r, "R");
412 checkVal0(row, col, g, "G");
414 checkValMax(row, col, g, "G");
416 checkVal0(row, col, b, "B");
419 checkValMax(row, col, a, "A");
422 } catch (Exception e) {
423 System.out.println("\n" + e.getMessage());
428 for (row = 0; row < h; row++) {
429 for (col = 0; col < w; col++) {
430 if (pf == TJ.PF_CMYK) {
431 int c = getVal(buf, pitch * row + col * ps);
432 int m = getVal(buf, pitch * row + col * ps + 1);
433 int y = getVal(buf, pitch * row + col * ps + 2);
434 int k = getVal(buf, pitch * row + col * ps + 3);
435 System.out.format("%3d/%3d/%3d/%3d ", c, m, y, k);
437 int r = getVal(buf, pitch * row + col * ps + roffset);
438 int g = getVal(buf, pitch * row + col * ps + goffset);
439 int b = getVal(buf, pitch * row + col * ps + boffset);
440 System.out.format("%3d/%3d/%3d ", r, g, b);
443 System.out.print("\n");
449 static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf,
450 int subsamp, TJScalingFactor sf, boolean bottomUp)
452 int rshift = TJ.getRedOffset(pf) * 8;
453 int gshift = TJ.getGreenOffset(pf) * 8;
454 int bshift = TJ.getBlueOffset(pf) * 8;
455 int ashift = TJ.getAlphaOffset(pf) * 8;
456 int index, row, col, retval = 1;
457 int halfway = 16 * sf.getNum() / sf.getDenom();
458 int blockSize = 8 * sf.getNum() / sf.getDenom();
461 for (row = 0; row < halfway; row++) {
462 for (col = 0; col < w; col++) {
464 index = pitch * (h - row - 1) + col;
466 index = pitch * row + col;
467 int r = (buf[index] >> rshift) & 0xFF;
468 int g = (buf[index] >> gshift) & 0xFF;
469 int b = (buf[index] >> bshift) & 0xFF;
470 int a = ashift >= 0 ? (buf[index] >> ashift) & 0xFF : 255;
471 if (((row / blockSize) + (col / blockSize)) % 2 == 0) {
473 checkValMax(row, col, r, "R");
474 checkValMax(row, col, g, "G");
475 checkValMax(row, col, b, "B");
477 checkVal0(row, col, r, "R");
478 checkVal0(row, col, g, "G");
479 checkVal0(row, col, b, "B");
482 if (subsamp == TJ.SAMP_GRAY) {
484 checkVal(row, col, r, "R", 76);
485 checkVal(row, col, g, "G", 76);
486 checkVal(row, col, b, "B", 76);
488 checkVal(row, col, r, "R", 226);
489 checkVal(row, col, g, "G", 226);
490 checkVal(row, col, b, "B", 226);
493 checkValMax(row, col, r, "R");
495 checkVal0(row, col, g, "G");
497 checkValMax(row, col, g, "G");
499 checkVal0(row, col, b, "B");
502 checkValMax(row, col, a, "A");
505 } catch (Exception e) {
506 System.out.println("\n" + e.getMessage());
511 for (row = 0; row < h; row++) {
512 for (col = 0; col < w; col++) {
513 int r = (buf[pitch * row + col] >> rshift) & 0xFF;
514 int g = (buf[pitch * row + col] >> gshift) & 0xFF;
515 int b = (buf[pitch * row + col] >> bshift) & 0xFF;
519 System.out.format("%3d/%3d/%3d ", r, g, b);
521 System.out.print("\n");
527 static int checkImg(BufferedImage img, int pf, int subsamp,
528 TJScalingFactor sf, boolean bottomUp) throws Exception {
529 WritableRaster wr = img.getRaster();
530 int imgType = img.getType();
531 if (imgType == BufferedImage.TYPE_INT_RGB ||
532 imgType == BufferedImage.TYPE_INT_BGR ||
533 imgType == BufferedImage.TYPE_INT_ARGB ||
534 imgType == BufferedImage.TYPE_INT_ARGB_PRE) {
535 SinglePixelPackedSampleModel sm =
536 (SinglePixelPackedSampleModel)img.getSampleModel();
537 int pitch = sm.getScanlineStride();
538 DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
539 int[] buf = db.getData();
540 return checkIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf,
541 subsamp, sf, bottomUp);
543 ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel();
544 int pitch = sm.getScanlineStride();
545 DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
546 byte[] buf = db.getData();
547 return checkBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, subsamp,
552 static int pad(int v, int p) {
553 return ((v + (p) - 1) & (~((p) - 1)));
556 static int checkBufYUV(byte[] buf, int size, int w, int h, int subsamp,
557 TJScalingFactor sf) throws Exception {
559 int hsf = TJ.getMCUWidth(subsamp) / 8, vsf = TJ.getMCUHeight(subsamp) / 8;
560 int pw = pad(w, hsf), ph = pad(h, vsf);
561 int cw = pw / hsf, ch = ph / vsf;
562 int ypitch = pad(pw, yuvAlign), uvpitch = pad(cw, yuvAlign);
564 int correctsize = ypitch * ph +
565 (subsamp == TJ.SAMP_GRAY ? 0 : uvpitch * ch * 2);
566 int halfway = 16 * sf.getNum() / sf.getDenom();
567 int blockSize = 8 * sf.getNum() / sf.getDenom();
570 if (size != correctsize)
571 throw new Exception("Incorrect size " + size + ". Should be " +
574 for (row = 0; row < ph; row++) {
575 for (col = 0; col < pw; col++) {
576 byte y = buf[ypitch * row + col];
577 if (((row / blockSize) + (col / blockSize)) % 2 == 0) {
579 checkValMax(row, col, y, "Y");
581 checkVal0(row, col, y, "Y");
584 checkVal(row, col, y, "Y", 76);
586 checkVal(row, col, y, "Y", 226);
590 if (subsamp != TJ.SAMP_GRAY) {
591 halfway = 16 / vsf * sf.getNum() / sf.getDenom();
592 for (row = 0; row < ch; row++) {
593 for (col = 0; col < cw; col++) {
594 byte u = buf[ypitch * ph + (uvpitch * row + col)],
595 v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)];
596 if (((row * vsf / blockSize) + (col * hsf / blockSize)) % 2 == 0) {
597 checkVal(row, col, u, "U", 128);
598 checkVal(row, col, v, "V", 128);
601 checkVal(row, col, u, "U", 85);
602 checkValMax(row, col, v, "V");
604 checkVal0(row, col, u, "U");
605 checkVal(row, col, v, "V", 149);
611 } catch (Exception e) {
612 System.out.println("\n" + e.getMessage());
617 for (row = 0; row < ph; row++) {
618 for (col = 0; col < pw; col++) {
619 int y = buf[ypitch * row + col];
621 System.out.format("%3d ", y);
623 System.out.print("\n");
625 System.out.print("\n");
626 for (row = 0; row < ch; row++) {
627 for (col = 0; col < cw; col++) {
628 int u = buf[ypitch * ph + (uvpitch * row + col)];
630 System.out.format("%3d ", u);
632 System.out.print("\n");
634 System.out.print("\n");
635 for (row = 0; row < ch; row++) {
636 for (col = 0; col < cw; col++) {
637 int v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)];
639 System.out.format("%3d ", v);
641 System.out.print("\n");
648 static void writeJPEG(byte[] jpegBuf, int jpegBufSize, String filename)
650 File file = new File(filename);
651 FileOutputStream fos = new FileOutputStream(file);
652 fos.write(jpegBuf, 0, jpegBufSize);
656 static int compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf,
657 String baseName) throws Exception {
659 Object srcBuf = null;
660 BufferedImage img = null;
661 String pfStr, pfStrLong;
662 boolean bottomUp = (tjc.get(TJ.PARAM_BOTTOMUP) == 1);
663 int subsamp = tjc.get(TJ.PARAM_SUBSAMP);
664 int jpegQual = tjc.get(TJ.PARAM_QUALITY);
665 int jpegPSV = tjc.get(TJ.PARAM_LOSSLESSPSV);
666 String buStr = bottomUp ? "BU" : "TD";
667 String buStrLong = bottomUp ? "Bottom-Up" : "Top-Down ";
668 int size = 0, ps, imgType = pf;
671 pf = biTypePF(imgType);
672 pfStr = biTypeStr(imgType);
673 pfStrLong = pfStr + " (" + PIXFORMATSTR[pf] + ")";
675 pfStr = PIXFORMATSTR[pf];
678 ps = TJ.getPixelSize(pf);
681 img = new BufferedImage(w, h, imgType);
682 initImg(img, pf, bottomUp);
683 tempStr = baseName + "_enc" + precision + "_" + pfStr + "_" + buStr +
684 "_" + SUBNAME[subsamp] + "_Q" + jpegQual + ".png";
685 File file = new File(tempStr);
686 ImageIO.write(img, "png", file);
687 tjc.setSourceImage(img, 0, 0, 0, 0);
690 srcBuf = new byte[w * h * ps + 1];
692 srcBuf = new short[w * h * ps + 1];
693 initBuf(srcBuf, w, w * ps, h, pf, bottomUp);
695 tjc.setSourceImage((byte[])srcBuf, 0, 0, w, 0, h, pf);
696 else if (precision == 12)
697 tjc.setSourceImage12((short[])srcBuf, 0, 0, w, 0, h, pf);
699 tjc.setSourceImage16((short[])srcBuf, 0, 0, w, 0, h, pf);
701 Arrays.fill(dstBuf, (byte)0);
704 System.out.format("%s %s -> YUV %s ... ", pfStrLong, buStrLong,
705 SUBNAME_LONG[subsamp]);
706 YUVImage yuvImage = tjc.encodeYUV(yuvAlign);
707 if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), w, h, subsamp,
708 new TJScalingFactor(1, 1)) == 1)
709 System.out.print("Passed.\n");
711 System.out.print("FAILED!\n");
715 System.out.format("YUV %s %s -> JPEG Q%d ... ", SUBNAME_LONG[subsamp],
716 buStrLong, jpegQual);
717 tjc.setSourceImage(yuvImage);
720 System.out.format("%s %s -> LOSSLESS PSV%d ... ", pfStrLong, buStrLong,
723 System.out.format("%s %s -> %s Q%d ... ", pfStrLong, buStrLong,
724 SUBNAME_LONG[subsamp], jpegQual);
726 tjc.compress(dstBuf);
727 size = tjc.getCompressedSize();
730 tempStr = baseName + "_enc" + precision + "_" + pfStr + "_" + buStr +
731 "_LOSSLESS_PSV" + jpegPSV + ".jpg";
733 tempStr = baseName + "_enc" + precision + "_" + pfStr + "_" + buStr +
734 "_" + SUBNAME[subsamp] + "_Q" + jpegQual + ".jpg";
735 writeJPEG(dstBuf, size, tempStr);
736 System.out.println("Done.\n Result in " + tempStr);
741 static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize,
742 int w, int h, int pf, String baseName, int subsamp,
743 TJScalingFactor sf) throws Exception {
744 String pfStr, pfStrLong, tempStr;
745 boolean bottomUp = (tjd.get(TJ.PARAM_BOTTOMUP) == 1);
746 String buStrLong = bottomUp ? "Bottom-Up" : "Top-Down ";
747 int scaledWidth = sf.getScaled(w);
748 int scaledHeight = sf.getScaled(h);
749 int temp1, temp2, imgType = pf;
750 BufferedImage img = null;
751 Object dstBuf = null;
754 pf = biTypePF(imgType);
755 pfStr = biTypeStr(imgType);
756 pfStrLong = pfStr + " (" + PIXFORMATSTR[pf] + ")";
758 pfStr = PIXFORMATSTR[pf];
762 tjd.setSourceImage(jpegBuf, jpegSize);
763 tjd.setScalingFactor(sf);
764 if (lossless && subsamp != TJ.SAMP_444 && subsamp != TJ.SAMP_GRAY)
765 subsamp = TJ.SAMP_444;
766 if (tjd.getWidth() != w || tjd.getHeight() != h ||
767 tjd.get(TJ.PARAM_SUBSAMP) != subsamp)
768 throw new Exception("Incorrect JPEG header");
771 System.out.format("JPEG -> YUV %s ", SUBNAME_LONG[subsamp]);
773 System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom());
774 else System.out.print("... ");
775 YUVImage yuvImage = tjd.decompressToYUV(yuvAlign);
776 if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), scaledWidth,
777 scaledHeight, subsamp, sf) == 1)
778 System.out.print("Passed.\n");
780 System.out.print("FAILED!\n"); exitStatus = -1;
783 System.out.format("YUV %s -> %s %s ... ", SUBNAME_LONG[subsamp],
784 pfStrLong, buStrLong);
785 tjd.setSourceImage(yuvImage);
787 System.out.format("JPEG -> %s %s ", pfStrLong, buStrLong);
789 System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom());
790 else System.out.print("... ");
793 img = tjd.decompress8(imgType);
796 dstBuf = tjd.decompress8(0, pf);
797 else if (precision == 12)
798 dstBuf = tjd.decompress12(0, pf);
800 dstBuf = tjd.decompress16(0, pf);
804 tempStr = baseName + "_dec_" + pfStr + "_" + (bottomUp ? "BU" : "TD") +
805 "_" + SUBNAME[subsamp] + "_" +
806 (double)sf.getNum() / (double)sf.getDenom() + "x" + ".png";
807 File file = new File(tempStr);
808 ImageIO.write(img, "png", file);
811 if ((bi && checkImg(img, pf, subsamp, sf, bottomUp) == 1) ||
812 (!bi && checkBuf(dstBuf, scaledWidth,
813 scaledWidth * TJ.getPixelSize(pf), scaledHeight, pf,
814 subsamp, sf, bottomUp) == 1))
815 System.out.print("Passed.\n");
817 System.out.print("FAILED!\n");
822 static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize,
823 int w, int h, int pf, String baseName, int subsamp)
828 decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp,
833 TJScalingFactor[] sf = TJ.getScalingFactors();
834 for (i = 0; i < sf.length; i++) {
835 int num = sf[i].getNum();
836 int denom = sf[i].getDenom();
837 if (subsamp == TJ.SAMP_444 || subsamp == TJ.SAMP_GRAY ||
838 ((subsamp == TJ.SAMP_411 || subsamp == TJ.SAMP_441) && num == 1 &&
839 (denom == 2 || denom == 1)) ||
840 (subsamp != TJ.SAMP_411 && subsamp != TJ.SAMP_441 && num == 1 &&
841 (denom == 4 || denom == 2 || denom == 1)))
842 decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, sf[i]);
846 static void doTest(int w, int h, int[] formats, int subsamp, String baseName)
848 TJCompressor tjc = null;
849 TJDecompressor tjd = null;
853 if (lossless && subsamp != TJ.SAMP_GRAY)
854 subsamp = TJ.SAMP_444;
856 dstBuf = new byte[TJ.bufSize(w, h, subsamp)];
859 tjc = new TJCompressor();
860 tjd = new TJDecompressor();
863 tjc.set(TJ.PARAM_LOSSLESS, 1);
864 tjc.set(TJ.PARAM_LOSSLESSPSV, ((psv++ - 1) % 7) + 1);
866 tjc.set(TJ.PARAM_QUALITY, 100);
867 if (subsamp == TJ.SAMP_422 || subsamp == TJ.SAMP_420 ||
868 subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411 ||
869 subsamp == TJ.SAMP_441)
870 tjd.set(TJ.PARAM_FASTUPSAMPLE, 1);
872 tjc.set(TJ.PARAM_SUBSAMP, subsamp);
874 for (int pf : formats) {
875 if (pf < 0) continue;
876 for (int i = 0; i < 2; i++) {
877 tjc.set(TJ.PARAM_BOTTOMUP, i == 1 ? 1 : 0);
878 tjd.set(TJ.PARAM_BOTTOMUP, i == 1 ? 1 : 0);
879 size = compTest(tjc, dstBuf, w, h, pf, baseName);
880 decompTest(tjd, dstBuf, size, w, h, pf, baseName, subsamp);
881 if (pf >= TJ.PF_RGBX && pf <= TJ.PF_XRGB && !bi) {
882 System.out.print("\n");
883 decompTest(tjd, dstBuf, size, w, h, pf + (TJ.PF_RGBA - TJ.PF_RGBX),
886 System.out.print("\n");
889 System.out.print("--------------------\n\n");
890 } catch (Exception e) {
891 if (tjc != null) tjc.close();
892 if (tjd != null) tjd.close();
895 if (tjc != null) tjc.close();
896 if (tjd != null) tjd.close();
899 static void overflowTest() throws Exception {
900 /* Ensure that the various buffer size methods don't overflow */
902 boolean exception = false;
906 size = TJ.bufSize(18919, 18919, TJ.SAMP_444);
907 } catch (Exception e) { exception = true; }
908 if (!exception || size != 0)
909 throw new Exception("TJ.bufSize() overflow");
912 size = TJ.bufSizeYUV(26755, 1, 26755, TJ.SAMP_444);
913 } catch (Exception e) { exception = true; }
914 if (!exception || size != 0)
915 throw new Exception("TJ.bufSizeYUV() overflow");
918 size = TJ.bufSizeYUV(26754, 3, 26754, TJ.SAMP_444);
919 } catch (Exception e) { exception = true; }
920 if (!exception || size != 0)
921 throw new Exception("TJ.bufSizeYUV() overflow");
924 size = TJ.bufSizeYUV(26754, -1, 26754, TJ.SAMP_444);
925 } catch (Exception e) { exception = true; }
926 if (!exception || size != 0)
927 throw new Exception("TJ.bufSizeYUV() overflow");
930 size = TJ.planeSizeYUV(0, 46341, 0, 46341, TJ.SAMP_444);
931 } catch (Exception e) { exception = true; }
932 if (!exception || size != 0)
933 throw new Exception("TJ.planeSizeYUV() overflow");
936 size = TJ.planeWidth(0, Integer.MAX_VALUE, TJ.SAMP_420);
937 } catch (Exception e) { exception = true; }
938 if (!exception || size != 0)
939 throw new Exception("TJ.planeWidth() overflow");
942 size = TJ.planeHeight(0, Integer.MAX_VALUE, TJ.SAMP_420);
943 } catch (Exception e) { exception = true; }
944 if (!exception || size != 0)
945 throw new Exception("TJ.planeHeight() overflow");
948 static void bufSizeTest() throws Exception {
949 int w, h, i, subsamp, numSamp = TJ.NUMSAMP;
950 byte[] srcBuf, dstBuf = null;
951 YUVImage dstImage = null;
952 TJCompressor tjc = null;
953 Random r = new Random();
956 tjc = new TJCompressor();
959 tjc.set(TJ.PARAM_LOSSLESS, 1);
960 tjc.set(TJ.PARAM_LOSSLESSPSV, ((psv++ - 1) % 7) + 1);
963 tjc.set(TJ.PARAM_QUALITY, 100);
965 System.out.println("Buffer size regression test");
966 for (subsamp = 0; subsamp < numSamp; subsamp++) {
967 tjc.set(TJ.PARAM_SUBSAMP, subsamp);
968 for (w = 1; w < 48; w++) {
969 int maxh = (w == 1) ? 2048 : 48;
970 for (h = 1; h < maxh; h++) {
972 System.out.format("%04d x %04d\b\b\b\b\b\b\b\b\b\b\b", w, h);
973 srcBuf = new byte[w * h * 4];
975 dstImage = new YUVImage(w, yuvAlign, h, subsamp);
977 dstBuf = new byte[TJ.bufSize(w, h, subsamp)];
978 for (i = 0; i < w * h * 4; i++) {
979 srcBuf[i] = (byte)(r.nextInt(2) * 255);
981 tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, TJ.PF_BGRX);
983 tjc.encodeYUV(dstImage);
985 tjc.compress(dstBuf);
987 srcBuf = new byte[h * w * 4];
989 dstImage = new YUVImage(h, yuvAlign, w, subsamp);
991 dstBuf = new byte[TJ.bufSize(h, w, subsamp)];
992 for (i = 0; i < h * w * 4; i++) {
993 srcBuf[i] = (byte)(r.nextInt(2) * 255);
995 tjc.setSourceImage(srcBuf, 0, 0, h, 0, w, TJ.PF_BGRX);
997 tjc.encodeYUV(dstImage);
999 tjc.compress(dstBuf);
1006 System.out.println("Done. ");
1007 } catch (Exception e) {
1008 if (tjc != null) tjc.close();
1011 if (tjc != null) tjc.close();
1014 public static void main(String[] argv) {
1016 String testName = "javatest";
1017 for (int i = 0; i < argv.length; i++) {
1018 if (argv[i].equalsIgnoreCase("-yuv"))
1020 else if (argv[i].equalsIgnoreCase("-noyuvpad"))
1022 else if (argv[i].equalsIgnoreCase("-lossless"))
1024 else if (argv[i].equalsIgnoreCase("-bi")) {
1026 testName = "javabitest";
1027 } else if (argv[i].equalsIgnoreCase("-precision") &&
1028 i < argv.length - 1) {
1032 tempi = Integer.parseInt(argv[++i]);
1033 } catch (NumberFormatException e) {}
1034 if (tempi != 8 && tempi != 12 && tempi != 16)
1037 if (precision == 16)
1042 if (lossless && doYUV)
1043 throw new Exception("Lossless JPEG and YUV encoding/decoding are incompatible.");
1044 if (precision != 8 && doYUV)
1045 throw new Exception("YUV encoding/decoding requires 8-bit data precision.");
1046 if (precision != 8 && bi)
1047 throw new Exception("BufferedImage support requires 8-bit data precision.");
1049 System.out.format("Testing %d-bit precision\n", precision);
1050 sampleSize = (precision == 8 ? 1 : 2);
1051 maxSample = (1 << precision) - 1;
1052 tolerance = (lossless ? 0 : (precision > 8 ? 2 : 1));
1053 redToY = (19595 * maxSample) >> 16;
1054 yellowToY = (58065 * maxSample) >> 16;
1057 FORMATS_4SAMPLE[4] = -1;
1059 doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_444,
1061 doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_444,
1063 doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_422,
1066 doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_422,
1068 doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_420,
1070 doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_420,
1072 doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_440,
1074 doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_440,
1076 doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_411,
1078 doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_411,
1080 doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_441,
1082 doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_441,
1085 doTest(39, 41, bi ? FORMATS_GRAYBI : FORMATS_GRAY, TJ.SAMP_GRAY,
1088 doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_GRAY,
1090 FORMATS_4SAMPLE[4] = -1;
1091 doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_GRAY,
1097 System.out.print("\n--------------------\n\n");
1098 doTest(48, 48, FORMATS_RGB, TJ.SAMP_444, "javatest_yuv0");
1099 doTest(48, 48, FORMATS_RGB, TJ.SAMP_422, "javatest_yuv0");
1100 doTest(48, 48, FORMATS_RGB, TJ.SAMP_420, "javatest_yuv0");
1101 doTest(48, 48, FORMATS_RGB, TJ.SAMP_440, "javatest_yuv0");
1102 doTest(48, 48, FORMATS_RGB, TJ.SAMP_411, "javatest_yuv0");
1103 doTest(48, 48, FORMATS_RGB, TJ.SAMP_441, "javatest_yuv0");
1104 doTest(48, 48, FORMATS_RGB, TJ.SAMP_GRAY, "javatest_yuv0");
1105 doTest(48, 48, FORMATS_GRAY, TJ.SAMP_GRAY, "javatest_yuv0");
1107 } catch (Exception e) {
1108 e.printStackTrace();
1111 System.exit(exitStatus);