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