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