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