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