Home | History | Annotate | Download | only in java
      1 /*
      2  * Copyright (C)2011-2015 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:\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_ABGR;
    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