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