TJUnitTest.java revision f962fbb44ad4d7b8200679ad0ed9263e10252638
1/*
2 * Copyright (C)2011 D. R. Commander.  All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
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.
15 *
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.
27 */
28
29/*
30 * This program tests the various code paths in the TurboJPEG JNI Wrapper
31 */
32
33import java.io.*;
34import java.util.*;
35import java.awt.image.*;
36import javax.imageio.*;
37import org.libjpegturbo.turbojpeg.*;
38
39public class TJUnitTest {
40
41  private static final String classname =
42    new TJUnitTest().getClass().getName();
43
44  private static void usage() {
45    System.out.println("\nUSAGE: java " + classname + " [options]\n");
46    System.out.println("Options:\n");
47    System.out.println("-yuv = test YUV encoding/decoding support\n");
48    System.out.println("-bi = test BufferedImage support\n");
49    System.exit(1);
50  }
51
52  private final static String subNameLong[] = {
53    "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0"
54  };
55  private final static String subName[] = {
56    "444", "422", "420", "GRAY", "440"
57  };
58
59  private final static String pixFormatStr[] = {
60    "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale"
61  };
62  private final static int biType[] = {
63    0, BufferedImage.TYPE_3BYTE_BGR, BufferedImage.TYPE_INT_BGR,
64    BufferedImage.TYPE_INT_RGB, 0, 0, BufferedImage.TYPE_BYTE_GRAY
65  };
66
67  private final static int _3byteFormats[] = {
68    TJ.PF_RGB, TJ.PF_BGR
69  };
70  private final static int _3byteFormatsBI[] = {
71    TJ.PF_BGR
72  };
73  private final static int _4byteFormats[] = {
74    TJ.PF_RGBX, TJ.PF_BGRX, TJ.PF_XBGR, TJ.PF_XRGB
75  };
76  private final static int _4byteFormatsBI[] = {
77    TJ.PF_RGBX, TJ.PF_BGRX
78  };
79  private final static int onlyGray[] = {
80    TJ.PF_GRAY
81  };
82  private final static int onlyRGB[] = {
83    TJ.PF_RGB
84  };
85
86  private final static int YUVENCODE = 1;
87  private final static int YUVDECODE = 2;
88  private static int yuv = 0;
89  private static boolean bi = false;
90
91  private static int exitStatus = 0;
92
93  private static double getTime() {
94    return (double)System.nanoTime() / 1.0e9;
95  }
96
97  private final static byte pixels[][] = {
98    {0, (byte)255, 0},
99    {(byte)255, 0, (byte)255},
100    {0, (byte)255, (byte)255},
101    {(byte)255, 0, 0},
102    {(byte)255, (byte)255, 0},
103    {0, 0, (byte)255},
104    {(byte)255, (byte)255, (byte)255},
105    {0, 0, 0},
106    {0, 0, (byte)255}
107  };
108
109  private static void initBuf(byte[] buf, int w, int pitch, int h, int pf,
110    int flags) throws Exception {
111    int roffset = TJ.getRedOffset(pf);
112    int goffset = TJ.getGreenOffset(pf);
113    int boffset = TJ.getBlueOffset(pf);
114    int ps = TJ.getPixelSize(pf);
115    int index, row, col, halfway = 16;
116
117    Arrays.fill(buf, (byte)0);
118    if(pf == TJ.PF_GRAY) {
119      for(row = 0; row < h; row++) {
120        for(col = 0; col < w; col++) {
121          if((flags & TJ.FLAG_BOTTOMUP) != 0)
122            index = pitch * (h - row - 1) + col;
123          else index = pitch * row + col;
124          if(((row / 8) + (col / 8)) % 2 == 0)
125            buf[index] = (row < halfway) ? (byte)255 : 0;
126          else buf[index] = (row < halfway) ? 76 : (byte)226;
127        }
128      }
129      return;
130    }
131    for(row = 0; row < h; row++) {
132      for(col = 0; col < w; col++) {
133        if((flags & TJ.FLAG_BOTTOMUP) != 0)
134          index = pitch * (h - row - 1) + col * ps;
135        else index = pitch * row + col * ps;
136        if(((row / 8) + (col / 8)) % 2 == 0) {
137          if(row < halfway) {
138            buf[index + roffset] = (byte)255;
139            buf[index + goffset] = (byte)255;
140            buf[index + boffset] = (byte)255;
141          }
142        }
143        else {
144          buf[index + roffset] = (byte)255;
145          if(row >= halfway) buf[index + goffset] = (byte)255;
146        }
147      }
148    }
149  }
150
151  private static void initIntBuf(int[] buf, int w, int pitch, int h, int pf,
152    int flags) throws Exception {
153    int rshift = TJ.getRedOffset(pf) * 8;
154    int gshift = TJ.getGreenOffset(pf) * 8;
155    int bshift = TJ.getBlueOffset(pf) * 8;
156    int index, row, col, halfway = 16;
157
158    Arrays.fill(buf, 0);
159    for(row = 0; row < h; row++) {
160      for(col = 0; col < w; col++) {
161        if((flags & TJ.FLAG_BOTTOMUP) != 0)
162          index = pitch * (h - row - 1) + col;
163        else index = pitch * row + col;
164        if(((row / 8) + (col / 8)) % 2 == 0) {
165          if(row < halfway) {
166            buf[index] |= (255 << rshift);
167            buf[index] |= (255 << gshift);
168            buf[index] |= (255 << bshift);
169          }
170        }
171        else {
172          buf[index] |= (255 << rshift);
173          if(row >= halfway) buf[index] |= (255 << gshift);
174        }
175      }
176    }
177  }
178
179  private static void initImg(BufferedImage img, int pf, int flags)
180    throws Exception {
181    WritableRaster wr = img.getRaster();
182    int imgtype = img.getType();
183    if(imgtype == BufferedImage.TYPE_INT_RGB
184      || imgtype == BufferedImage.TYPE_INT_BGR) {
185      SinglePixelPackedSampleModel sm =
186        (SinglePixelPackedSampleModel)img.getSampleModel();
187      int pitch = sm.getScanlineStride();
188      DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
189      int[] buf = db.getData();
190      initIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags);
191    }
192    else {
193      ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel();
194      int pitch = sm.getScanlineStride();
195      DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
196      byte[] buf = db.getData();
197      initBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags);
198    }
199  }
200
201  private static void checkVal(int row, int col, int v, String vname, int cv)
202    throws Exception {
203    v = (v < 0) ? v + 256 : v;
204    if(v < cv - 1 || v > cv + 1) {
205      throw new Exception("\nComp. " + vname + " at " + row + "," + col
206        + " should be " + cv + ", not " + v + "\n");
207    }
208  }
209
210  private static void checkVal0(int row, int col, int v, String vname)
211    throws Exception {
212    v = (v < 0) ? v + 256 : v;
213    if(v > 1) {
214      throw new Exception("\nComp. " + vname + " at " + row + "," + col
215        + " should be 0, not " + v + "\n");
216    }
217  }
218
219  private static void checkVal255(int row, int col, int v, String vname)
220    throws Exception {
221    v = (v < 0) ? v + 256 : v;
222    if(v < 254) {
223      throw new Exception("\nComp. " + vname + " at " + row + "," + col
224        + " should be 255, not " + v + "\n");
225    }
226  }
227
228  private static int checkBuf(byte[] buf, int w, int pitch, int h, int pf,
229    int subsamp, TJScalingFactor sf, int flags) throws Exception {
230    int roffset = TJ.getRedOffset(pf);
231    int goffset = TJ.getGreenOffset(pf);
232    int boffset = TJ.getBlueOffset(pf);
233    int ps = TJ.getPixelSize(pf);
234    int index, row, col, retval = 1;
235    int halfway = 16 * sf.getNum() / sf.getDenom();
236    int blockSize = 8 * sf.getNum() / sf.getDenom();
237
238    try {
239      for(row = 0; row < halfway; row++) {
240        for(col = 0; col < w; col++) {
241          if((flags & TJ.FLAG_BOTTOMUP) != 0)
242            index = pitch * (h - row - 1) + col * ps;
243          else index = pitch * row + col * ps;
244          byte r = buf[index + roffset];
245          byte g = buf[index + goffset];
246          byte b = buf[index + boffset];
247          if(((row / blockSize) + (col / blockSize)) % 2 == 0) {
248            if(row < halfway) {
249              checkVal255(row, col, r, "R");
250              checkVal255(row, col, g, "G");
251              checkVal255(row, col, b, "B");
252            }
253            else {
254              checkVal0(row, col, r, "R");
255              checkVal0(row, col, g, "G");
256              checkVal0(row, col, b, "B");
257            }
258          }
259          else {
260            if(subsamp == TJ.SAMP_GRAY) {
261              if(row < halfway) {
262                checkVal(row, col, r, "R", 76);
263                checkVal(row, col, g, "G", 76);
264                checkVal(row, col, b, "B", 76);
265              }
266              else {
267                checkVal(row, col, r, "R", 226);
268                checkVal(row, col, g, "G", 226);
269                checkVal(row, col, b, "B", 226);
270              }
271            }
272            else {
273              checkVal255(row, col, r, "R");
274              if(row < halfway) {
275                checkVal0(row, col, g, "G");
276              }
277              else {
278                checkVal255(row, col, g, "G");
279              }
280              checkVal0(row, col, b, "B");
281            }
282          }
283        }
284      }
285    }
286    catch(Exception e) {
287      System.out.println(e);
288      retval = 0;
289    }
290
291    if(retval == 0) {
292      System.out.print("\n");
293      for(row = 0; row < h; row++) {
294        for(col = 0; col < w; col++) {
295          int r = buf[pitch * row + col * ps + roffset];
296          int g = buf[pitch * row + col * ps + goffset];
297          int b = buf[pitch * row + col * ps + boffset];
298          if(r < 0) r += 256;  if(g < 0) g += 256;  if(b < 0) b += 256;
299          System.out.format("%3d/%3d/%3d ", r, g, b);
300        }
301        System.out.print("\n");
302      }
303    }
304    return retval;
305  }
306
307  private static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf,
308    int subsamp, TJScalingFactor sf, int flags) throws Exception {
309    int rshift = TJ.getRedOffset(pf) * 8;
310    int gshift = TJ.getGreenOffset(pf) * 8;
311    int bshift = TJ.getBlueOffset(pf) * 8;
312    int index, row, col, retval = 1;
313    int halfway = 16 * sf.getNum() / sf.getDenom();
314    int blockSize = 8 * sf.getNum() / sf.getDenom();
315
316    try {
317      for(row = 0; row < halfway; row++) {
318        for(col = 0; col < w; col++) {
319          if((flags & TJ.FLAG_BOTTOMUP) != 0)
320            index = pitch * (h - row - 1) + col;
321          else index = pitch * row + col;
322          int r = (buf[index] >> rshift) & 0xFF;
323          int g = (buf[index] >> gshift) & 0xFF;
324          int b = (buf[index] >> bshift) & 0xFF;
325          if(((row / blockSize) + (col / blockSize)) % 2 == 0) {
326            if(row < halfway) {
327              checkVal255(row, col, r, "R");
328              checkVal255(row, col, g, "G");
329              checkVal255(row, col, b, "B");
330            }
331            else {
332              checkVal0(row, col, r, "R");
333              checkVal0(row, col, g, "G");
334              checkVal0(row, col, b, "B");
335            }
336          }
337          else {
338            if(subsamp == TJ.SAMP_GRAY) {
339              if(row < halfway) {
340                checkVal(row, col, r, "R", 76);
341                checkVal(row, col, g, "G", 76);
342                checkVal(row, col, b, "B", 76);
343              }
344              else {
345                checkVal(row, col, r, "R", 226);
346                checkVal(row, col, g, "G", 226);
347                checkVal(row, col, b, "B", 226);
348              }
349            }
350            else {
351              checkVal255(row, col, r, "R");
352              if(row < halfway) {
353                checkVal0(row, col, g, "G");
354              }
355              else {
356                checkVal255(row, col, g, "G");
357              }
358              checkVal0(row, col, b, "B");
359            }
360          }
361        }
362      }
363    }
364    catch(Exception e) {
365      System.out.println(e);
366      retval = 0;
367    }
368
369    if(retval == 0) {
370      System.out.print("\n");
371      for(row = 0; row < h; row++) {
372        for(col = 0; col < w; col++) {
373          int r = (buf[pitch * row + col] >> rshift) & 0xFF;
374          int g = (buf[pitch * row + col] >> gshift) & 0xFF;
375          int b = (buf[pitch * row + col] >> bshift) & 0xFF;
376          if(r < 0) r += 256;  if(g < 0) g += 256;  if(b < 0) b += 256;
377          System.out.format("%3d/%3d/%3d ", r, g, b);
378        }
379        System.out.print("\n");
380      }
381    }
382    return retval;
383  }
384
385  private static int checkImg(BufferedImage img, int pf,
386    int subsamp, TJScalingFactor sf, int flags) throws Exception {
387    WritableRaster wr = img.getRaster();
388    int imgtype = img.getType();
389    if(imgtype == BufferedImage.TYPE_INT_RGB
390      || imgtype == BufferedImage.TYPE_INT_BGR) {
391      SinglePixelPackedSampleModel sm =
392        (SinglePixelPackedSampleModel)img.getSampleModel();
393      int pitch = sm.getScanlineStride();
394      DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
395      int[] buf = db.getData();
396      return checkIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf,
397        subsamp, sf, flags);
398    }
399    else {
400      ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel();
401      int pitch = sm.getScanlineStride();
402      DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
403      byte[] buf = db.getData();
404      return checkBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, subsamp,
405        sf, flags);
406    }
407  }
408
409  private static int PAD(int v, int p) {
410    return ((v + (p) - 1) & (~((p) - 1)));
411  }
412
413  private static int checkBufYUV(byte[] buf, int size, int w, int h,
414    int subsamp) throws Exception {
415    int i, j;
416    int hsf = TJ.getMCUWidth(subsamp)/8, vsf = TJ.getMCUHeight(subsamp)/8;
417    int pw = PAD(w, hsf), ph = PAD(h, vsf);
418    int cw = pw / hsf, ch = ph / vsf;
419    int ypitch = PAD(pw, 4), uvpitch = PAD(cw, 4);
420    int retval = 1;
421    int correctsize = ypitch * ph
422      + (subsamp == TJ.SAMP_GRAY ? 0 : uvpitch * ch * 2);
423
424    try {
425      if(size != correctsize)
426        throw new Exception("\nIncorrect size " + size + ".  Should be "
427          + correctsize);
428
429      for(i = 0; i < 16; i++) {
430        for(j = 0; j < pw; j++) {
431          byte y = buf[ypitch * i + j];
432          if(((i / 8) + (j / 8)) % 2 == 0) checkVal255(i, j, y, "Y");
433          else checkVal(i, j, y, "Y", 76);
434        }
435      }
436      for(i = 16; i < ph; i++) {
437        for(j = 0; j < pw; j++) {
438          byte y = buf[ypitch * i + j];
439          if(((i / 8) + (j / 8)) % 2 == 0) checkVal0(i, j, y, "Y");
440          else checkVal(i, j, y, "Y", 226);
441        }
442      }
443      if(subsamp != TJ.SAMP_GRAY) {
444        for(i = 0; i < 16 / vsf; i++) {
445          for(j = 0; j < cw; j++) {
446            byte u = buf[ypitch * ph + (uvpitch * i + j)],
447              v = buf[ypitch * ph + uvpitch * ch + (uvpitch * i + j)];
448            if(((i * vsf / 8) + (j * hsf / 8)) % 2 == 0) {
449              checkVal(i, j, u, "U", 128);  checkVal(i, j, v, "V", 128);
450            }
451            else {
452              checkVal(i, j, u, "U", 85);  checkVal255(i, j, v, "V");
453            }
454          }
455        }
456        for(i = 16 / vsf; i < ch; i++) {
457          for(j = 0; j < cw; j++) {
458            byte u = buf[ypitch * ph + (uvpitch * i + j)],
459              v = buf[ypitch * ph + uvpitch * ch + (uvpitch * i + j)];
460            if(((i * vsf / 8) + (j * hsf / 8)) % 2 == 0) {
461              checkVal(i, j, u, "U", 128);  checkVal(i, j, v, "V", 128);
462            }
463            else {
464              checkVal0(i, j, u, "U");  checkVal(i, j, v, "V", 149);
465            }
466          }
467        }
468      }
469    }
470    catch(Exception e) {
471      System.out.println(e);
472      retval = 0;
473    }
474
475    if(retval == 0) {
476      for(i = 0; i < ph; i++) {
477        for(j = 0; j < pw; j++) {
478          int y = buf[ypitch * i + j];
479          if(y < 0) y += 256;
480          System.out.format("%3d ", y);
481        }
482        System.out.print("\n");
483      }
484      System.out.print("\n");
485      for(i = 0; i < ch; i++) {
486        for(j = 0; j < cw; j++) {
487          int u = buf[ypitch * ph + (uvpitch * i + j)];
488          if(u < 0) u += 256;
489          System.out.format("%3d ", u);
490        }
491        System.out.print("\n");
492      }
493      System.out.print("\n");
494      for(i = 0; i < ch; i++) {
495        for(j = 0; j < cw; j++) {
496          int v = buf[ypitch * ph + uvpitch * ch + (uvpitch * i + j)];
497          if(v < 0) v += 256;
498          System.out.format("%3d ", v);
499        }
500        System.out.print("\n");
501      }
502      System.out.print("\n");
503    }
504
505    return retval;
506  }
507
508  private static void writeJPEG(byte[] jpegBuf, int jpegBufSize,
509    String filename) throws Exception {
510    File file = new File(filename);
511    FileOutputStream fos = new FileOutputStream(file);
512    fos.write(jpegBuf, 0, jpegBufSize);
513    fos.close();
514  }
515
516  private static int compTest(TJCompressor tjc, byte[] dstBuf, int w,
517    int h, int pf, String baseName, int subsamp, int jpegQual,
518    int flags) throws Exception {
519    String tempstr;
520    byte[] srcBuf = null;
521    BufferedImage img = null;
522    String pfStr;
523    double t;
524    int size = 0, ps = TJ.getPixelSize(pf);
525
526    pfStr = pixFormatStr[pf];
527
528    System.out.print(pfStr + " ");
529    if((flags & TJ.FLAG_BOTTOMUP) != 0) System.out.print("Bottom-Up");
530    else System.out.print("Top-Down ");
531    System.out.print(" -> " + subNameLong[subsamp] + " ");
532    if(yuv == YUVENCODE) System.out.print("YUV ... ");
533    else System.out.print("Q" + jpegQual + " ... ");
534
535    if(bi) {
536      img = new BufferedImage(w, h, biType[pf]);
537      initImg(img, pf, flags);
538      tempstr = baseName + "_enc_" + pfStr + "_"
539        + (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_"
540        + subName[subsamp] + "_Q" + jpegQual + ".png";
541      File file = new File(tempstr);
542      ImageIO.write(img, "png", file);
543    }
544    else {
545      srcBuf = new byte[w * h * ps + 1];
546      initBuf(srcBuf, w, w * ps, h, pf, flags);
547    }
548    Arrays.fill(dstBuf, (byte)0);
549
550    t = getTime();
551    tjc.setSubsamp(subsamp);
552    tjc.setJPEGQuality(jpegQual);
553    if(bi) {
554      if(yuv == YUVENCODE) tjc.encodeYUV(img, dstBuf, flags);
555      else tjc.compress(img, dstBuf, flags);
556    }
557    else {
558      tjc.setSourceImage(srcBuf, w, 0, h, pf);
559      if(yuv == YUVENCODE) tjc.encodeYUV(dstBuf, flags);
560      else tjc.compress(dstBuf, flags);
561    }
562    size = tjc.getCompressedSize();
563    t = getTime() - t;
564
565    if(yuv == YUVENCODE)
566      tempstr = baseName + "_enc_" + pfStr + "_"
567        + (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_"
568        + subName[subsamp] + ".yuv";
569    else
570      tempstr = baseName + "_enc_" + pfStr + "_"
571        + (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_"
572        + subName[subsamp] + "_Q" + jpegQual + ".jpg";
573    writeJPEG(dstBuf, size, tempstr);
574
575    if(yuv == YUVENCODE) {
576      if(checkBufYUV(dstBuf, size, w, h, subsamp) == 1)
577        System.out.print("Passed.");
578      else {
579        System.out.print("FAILED!");  exitStatus = -1;
580      }
581    }
582    else System.out.print("Done.");
583    System.out.format("  %.6f ms\n", t * 1000.);
584    System.out.println("  Result in " + tempstr);
585
586    return size;
587  }
588
589  private static void decompTest(TJDecompressor tjd, byte[] jpegBuf,
590    int jpegSize, int w, int h, int pf, String baseName, int subsamp,
591    int flags, TJScalingFactor sf) throws Exception {
592    String pfStr, tempstr;
593    double t;
594    int scaledWidth = sf.getScaled(w);
595    int scaledHeight = sf.getScaled(h);
596    int temp1, temp2;
597    BufferedImage img = null;
598    byte[] dstBuf = null;
599
600    if(yuv == YUVENCODE) return;
601
602    pfStr = pixFormatStr[pf];
603    System.out.print("JPEG -> ");
604    if(yuv == YUVDECODE)
605      System.out.print("YUV " + subName[subsamp] + " ... ");
606    else {
607      System.out.print(pfStr + " ");
608      if((flags & TJ.FLAG_BOTTOMUP) != 0) System.out.print("Bottom-Up ");
609      else System.out.print("Top-Down  ");
610      if(!sf.isOne())
611        System.out.print(sf.getNum() + "/" + sf.getDenom() + " ... ");
612      else System.out.print("... ");
613    }
614
615    t = getTime();
616    tjd.setJPEGImage(jpegBuf, jpegSize);
617    if(tjd.getWidth() != w || tjd.getHeight() != h
618      || tjd.getSubsamp() != subsamp)
619      throw new Exception("Incorrect JPEG header");
620
621    temp1 = scaledWidth;
622    temp2 = scaledHeight;
623    temp1 = tjd.getScaledWidth(temp1, temp2);
624    temp2 = tjd.getScaledHeight(temp1, temp2);
625    if(temp1 != scaledWidth || temp2 != scaledHeight)
626      throw new Exception("Scaled size mismatch");
627
628    if(yuv == YUVDECODE) dstBuf = tjd.decompressToYUV(flags);
629    else {
630      if(bi)
631        img = tjd.decompress(scaledWidth, scaledHeight, biType[pf], flags);
632      else dstBuf = tjd.decompress(scaledWidth, 0, scaledHeight, pf, flags);
633    }
634    t = getTime() - t;
635
636    if(bi) {
637      tempstr = baseName + "_dec_" + pfStr + "_"
638        + (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_"
639        + subName[subsamp] + "_" + (double)sf.getNum() / (double)sf.getDenom()
640        + "x" + ".png";
641      File file = new File(tempstr);
642      ImageIO.write(img, "png", file);
643    }
644
645    if(yuv == YUVDECODE) {
646      if(checkBufYUV(dstBuf, dstBuf.length, w, h, subsamp) == 1)
647        System.out.print("Passed.");
648      else {
649        System.out.print("FAILED!");  exitStatus = -1;
650      }
651    }
652    else {
653      if((bi && checkImg(img, pf, subsamp, sf, flags) == 1)
654        || (!bi && checkBuf(dstBuf, scaledWidth, scaledWidth
655          * TJ.getPixelSize(pf), scaledHeight, pf, subsamp, sf, flags) == 1))
656        System.out.print("Passed.");
657      else {
658        System.out.print("FAILED!");  exitStatus = -1;
659      }
660    }
661    System.out.format("  %.6f ms\n", t * 1000.);
662  }
663
664  private static void decompTest(TJDecompressor tjd, byte[] jpegBuf,
665    int jpegSize, int w, int h, int pf, String baseName, int subsamp,
666    int flags) throws Exception {
667    int i;
668    if((subsamp == TJ.SAMP_444 || subsamp == TJ.SAMP_GRAY) && yuv == 0) {
669      TJScalingFactor sf[] = TJ.getScalingFactors();
670      for(i = 0; i < sf.length; i++)
671        decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp,
672          flags, sf[i]);
673    }
674    else
675      decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp,
676        flags, new TJScalingFactor(1, 1));
677    System.out.print("\n");
678  }
679
680  private static void doTest(int w, int h, int[] formats, int subsamp,
681    String baseName) throws Exception {
682    TJCompressor tjc = null;
683    TJDecompressor tjd = null;
684    int size;
685    byte[] dstBuf;
686
687    if(yuv == YUVENCODE) dstBuf = new byte[TJ.bufSizeYUV(w, h, subsamp)];
688    else dstBuf = new byte[TJ.bufSize(w, h)];
689
690    try {
691      tjc = new TJCompressor();
692      tjd = new TJDecompressor();
693
694      for(int pf : formats) {
695        for(int i = 0; i < 2; i++) {
696          int flags = 0;
697          if(i == 1) {
698            if(yuv == YUVDECODE) {
699              tjc.close();  tjd.close();  return;
700            }
701            else flags |= TJ.FLAG_BOTTOMUP;
702          }
703          size = compTest(tjc, dstBuf, w, h, pf, baseName, subsamp, 100,
704            flags);
705          decompTest(tjd, dstBuf, size, w, h, pf, baseName, subsamp, flags);
706        }
707      }
708    }
709    catch(Exception e) {
710      if(tjc != null) tjc.close();
711      if(tjd != null) tjd.close();
712      throw e;
713    }
714    if(tjc != null) tjc.close();
715    if(tjd != null) tjd.close();
716  }
717
718  private static void doTest1() throws Exception {
719    int w, h, i;
720    byte[] srcBuf, jpegBuf;
721    TJCompressor tjc = null;
722
723    try {
724      tjc = new TJCompressor();
725      System.out.println("Buffer size regression test");
726      for(w = 1; w < 48; w++) {
727        int maxh = (w == 1) ? 2048 : 48;
728        for(h = 1; h < maxh; h++) {
729          if(h % 100 == 0)
730            System.out.format("%04d x %04d\b\b\b\b\b\b\b\b\b\b\b", w, h);
731          srcBuf = new byte[w * h * 4];
732          jpegBuf = new byte[TJ.bufSize(w, h)];
733          Arrays.fill(srcBuf, (byte)0);
734          for(i = 0; i < w * h; i++) {
735            srcBuf[i * 4] = pixels[i % 9][0];
736            srcBuf[i * 4 + 1] = pixels[i % 9][1];
737            srcBuf[i * 4 + 2] = pixels[i % 9][2];
738          }
739          tjc.setSourceImage(srcBuf, w, 0, h, TJ.PF_BGRX);
740          tjc.setSubsamp(TJ.SAMP_444);
741          tjc.setJPEGQuality(100);
742          tjc.compress(jpegBuf, 0);
743
744          srcBuf = new byte[h * w * 4];
745          jpegBuf = new byte[TJ.bufSize(h, w)];
746          for(i = 0; i < h * w; i++) {
747            if(i % 2 == 0) srcBuf[i * 4] =
748                srcBuf[i * 4 + 1] = srcBuf[i * 4 + 2] = (byte)0xFF;
749            else srcBuf[i * 4] = srcBuf[i * 4 + 1] = srcBuf[i * 4 + 2] = 0;
750          }
751          tjc.setSourceImage(srcBuf, h, 0, w, TJ.PF_BGRX);
752          tjc.compress(jpegBuf, 0);
753        }
754      }
755      System.out.println("Done.      ");
756    }
757    catch(Exception e) {
758      if(tjc != null) tjc.close();
759      throw e;
760    }
761    if(tjc != null) tjc.close();
762  }
763
764  public static void main(String argv[]) {
765    try {
766      String testName = "javatest";
767      boolean doyuv = false;
768      for(int i = 0; i < argv.length; i++) {
769        if(argv[i].equalsIgnoreCase("-yuv")) doyuv = true;
770        if(argv[i].substring(0, 1).equalsIgnoreCase("-h")
771          || argv[i].equalsIgnoreCase("-?"))
772          usage();
773        if(argv[i].equalsIgnoreCase("-bi")) {
774          bi = true;
775          testName = "javabitest";
776        }
777      }
778      if(doyuv) yuv = YUVENCODE;
779      doTest(35, 39, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_444, testName);
780      doTest(39, 41, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_444, testName);
781      if(doyuv) {
782        doTest(41, 35, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_422,
783          testName);
784        doTest(35, 39, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_422,
785          testName);
786        doTest(39, 41, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_420,
787          testName);
788        doTest(41, 35, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_420,
789          testName);
790        doTest(35, 39, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_440,
791          testName);
792        doTest(39, 41, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_440,
793          testName);
794      }
795      doTest(35, 39, onlyGray, TJ.SAMP_GRAY, testName);
796      doTest(39, 41, bi ? _3byteFormatsBI : _3byteFormats, TJ.SAMP_GRAY,
797        testName);
798      doTest(41, 35, bi ? _4byteFormatsBI : _4byteFormats, TJ.SAMP_GRAY,
799        testName);
800      if(!doyuv && !bi) doTest1();
801      if(doyuv && !bi) {
802        yuv = YUVDECODE;
803        doTest(48, 48, onlyRGB, TJ.SAMP_444, "javatest_yuv0");
804        doTest(35, 39, onlyRGB, TJ.SAMP_444, "javatest_yuv1");
805        doTest(48, 48, onlyRGB, TJ.SAMP_422, "javatest_yuv0");
806        doTest(39, 41, onlyRGB, TJ.SAMP_422, "javatest_yuv1");
807        doTest(48, 48, onlyRGB, TJ.SAMP_420, "javatest_yuv0");
808        doTest(41, 35, onlyRGB, TJ.SAMP_420, "javatest_yuv1");
809        doTest(48, 48, onlyRGB, TJ.SAMP_440, "javatest_yuv0");
810        doTest(35, 39, onlyRGB, TJ.SAMP_440, "javatest_yuv1");
811        doTest(48, 48, onlyRGB, TJ.SAMP_GRAY, "javatest_yuv0");
812        doTest(35, 39, onlyRGB, TJ.SAMP_GRAY, "javatest_yuv1");
813        doTest(48, 48, onlyGray, TJ.SAMP_GRAY, "javatest_yuv0");
814        doTest(39, 41, onlyGray, TJ.SAMP_GRAY, "javatest_yuv1");
815      }
816    }
817    catch(Exception e) {
818      e.printStackTrace();
819      exitStatus = -1;
820    }
821    System.exit(exitStatus);
822  }
823}
824