/* * Copyright (C)2011 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the libjpeg-turbo Project nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * This program tests the various code paths in the TurboJPEG JNI Wrapper */ import java.io.*; import java.util.*; import java.awt.image.*; import javax.imageio.*; import org.libjpegturbo.turbojpeg.*; public class TJUnitTest { private static final String classname=new TJUnitTest().getClass().getName(); private static void usage() { System.out.println("\nUSAGE: java "+classname+" [options]\n"); System.out.println("Options:\n"); System.out.println("-yuv = test YUV encoding/decoding support\n"); System.out.println("-bi = test BufferedImage support\n"); System.exit(1); } private final static String _subnamel[]= {"4:4:4", "4:2:2", "4:2:0", "GRAY"}; private final static String _subnames[]= {"444", "422", "420", "GRAY"}; private final static int _hsf[]={1, 2, 2, 1}; private final static int _vsf[]={1, 1, 2, 1}; private final static String _pixformatstr[]= {"RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale"}; private final static int biType[]= {0, BufferedImage.TYPE_3BYTE_BGR, BufferedImage.TYPE_INT_BGR, BufferedImage.TYPE_INT_RGB, 0, 0, BufferedImage.TYPE_BYTE_GRAY}; private final static int _3byteFormats[]= {TJ.PF_RGB, TJ.PF_BGR}; private final static int _3byteFormatsBI[]= {TJ.PF_BGR}; private final static int _4byteFormats[]= {TJ.PF_RGBX, TJ.PF_BGRX, TJ.PF_XBGR, TJ.PF_XRGB}; private final static int _4byteFormatsBI[]= {TJ.PF_RGBX, TJ.PF_BGRX}; private final static int _onlyGray[]= {TJ.PF_GRAY}; private final static int _onlyRGB[]= {TJ.PF_RGB}; private final static int YUVENCODE=1, YUVDECODE=2; private static int yuv=0; private static boolean bi=false; private static int exitstatus=0; private static double gettime() { return (double)System.nanoTime()/1.0e9; } private final static byte pixels[][]= { {0, (byte)255, 0}, {(byte)255, 0, (byte)255}, {(byte)255, (byte)255, 0}, {0, 0, (byte)255}, {0, (byte)255, (byte)255}, {(byte)255, 0, 0}, {(byte)255, (byte)255, (byte)255}, {0, 0, 0}, {(byte)255, 0, 0} }; private static void initbuf(byte [] buf, int w, int pitch, int h, int pf, int flags) throws Exception { int roffset=TJ.getRedShift(pf)/8; int goffset=TJ.getGreenShift(pf)/8; int boffset=TJ.getBlueShift(pf)/8; int ps=TJ.getPixelSize(pf); int i, _i, j; Arrays.fill(buf, (byte)0); if(pf==TJ.PF_GRAY) { for(_i=0; _i<16; _i++) { if((flags&TJ.BOTTOMUP)!=0) i=h-_i-1; else i=_i; for(j=0; jcv+1) { throw new Exception("\nComp. "+vname+" at "+i+","+j+" should be "+cv +", not "+v+"\n"); } } private static void checkval0(int i, int j, int v, String vname) throws Exception { v=(v<0)? v+256:v; if(v>1) { throw new Exception("\nComp. "+vname+" at "+i+","+j+" should be 0, not " +v+"\n"); } } private static void checkval255(int i, int j, int v, String vname) throws Exception { v=(v<0)? v+256:v; if(v<254 && !(v==217 && i==0 && j==21)) { throw new Exception("\nComp. "+vname+" at "+i+","+j+" should be 255, not " +v+"\n"); } } private static int checkbuf(byte [] buf, int w, int pitch, int h, int pf, int subsamp, int scale_num, int scale_denom, int flags) throws Exception { int roffset=TJ.getRedShift(pf)/8; int goffset=TJ.getGreenShift(pf)/8; int boffset=TJ.getBlueShift(pf)/8; int ps=TJ.getPixelSize(pf); int i, _i, j, retval=1; int halfway=16*scale_num/scale_denom, blocksize=8*scale_num/scale_denom; try { for(_i=0; _i> rshift) & 0xFF; int g=(buf[pitch*i+j] >> gshift) & 0xFF; int b=(buf[pitch*i+j] >> bshift) & 0xFF; if(((_i/blocksize)+(j/blocksize))%2==0) { checkval255(_i, j, r, "R"); checkval255(_i, j, g, "G"); checkval255(_i, j, b, "B"); } else { if(subsamp==TJ.SAMP_GRAY) { checkval(_i, j, r, "R", 76); checkval(_i, j, g, "G", 76); checkval(_i, j, b, "B", 76); } else { checkval255(_i, j, r, "R"); checkval0(_i, j, g, "G"); checkval0(_i, j, b, "B"); } } } } for(_i=halfway; _i> rshift) & 0xFF; int g=(buf[pitch*i+j] >> gshift) & 0xFF; int b=(buf[pitch*i+j] >> bshift) & 0xFF; if(((_i/blocksize)+(j/blocksize))%2==0) { checkval0(_i, j, r, "R"); checkval0(_i, j, g, "G"); } else { if(subsamp==TJ.SAMP_GRAY) { checkval(_i, j, r, "R", 226); checkval(_i, j, g, "G", 226); checkval(_i, j, b, "B", 226); } else { checkval255(_i, j, r, "R"); checkval255(_i, j, g, "G"); checkval0(_i, j, b, "B"); } } } } } catch(Exception e) { System.out.println(e); retval=0; } if(retval==0) { System.out.print("\n"); for(i=0; i> rshift) & 0xFF; int g=(buf[pitch*i+j] >> gshift) & 0xFF; int b=(buf[pitch*i+j] >> bshift) & 0xFF; if(r<0) r+=256; if(g<0) g+=256; if(b<0) b+=256; System.out.format("%3d/%3d/%3d ", r, g, b); } System.out.print("\n"); } } return retval; } private static int checkimg(BufferedImage img, int pf, int subsamp, int scale_num, int scale_denom, int flags) throws Exception { WritableRaster wr=img.getRaster(); int imgtype=img.getType(); if(imgtype==BufferedImage.TYPE_INT_RGB || imgtype==BufferedImage.TYPE_INT_BGR) { SinglePixelPackedSampleModel sm= (SinglePixelPackedSampleModel)img.getSampleModel(); int pitch=sm.getScanlineStride(); DataBufferInt db=(DataBufferInt)wr.getDataBuffer(); int [] buf = db.getData(); return checkintbuf(buf, img.getWidth(), pitch, img.getHeight(), pf, subsamp, scale_num, scale_denom, flags); } else { ComponentSampleModel sm= (ComponentSampleModel)img.getSampleModel(); int pitch=sm.getScanlineStride(); DataBufferByte db=(DataBufferByte)wr.getDataBuffer(); byte [] buf = db.getData(); return checkbuf(buf, img.getWidth(), pitch, img.getHeight(), pf, subsamp, scale_num, scale_denom, flags); } } private static int PAD(int v, int p) { return ((v+(p)-1)&(~((p)-1))); } private static int checkbufyuv(byte [] buf, int size, int w, int h, int subsamp) { int i, j; int hsf=_hsf[subsamp], vsf=_vsf[subsamp]; int pw=PAD(w, hsf), ph=PAD(h, vsf); int cw=pw/hsf, ch=ph/vsf; int ypitch=PAD(pw, 4), uvpitch=PAD(cw, 4); int retval=1; int correctsize=ypitch*ph + (subsamp==TJ.SAMP_GRAY? 0:uvpitch*ch*2); try { if(size!=correctsize) throw new Exception("\nIncorrect size "+size+". Should be " +correctsize); for(i=0; i<16; i++) { for(j=0; j "+_subnamel[subsamp]+" "); if(yuv==YUVENCODE) System.out.print("YUV ... "); else System.out.print("Q"+qual+" ... "); if(bi) { img=new BufferedImage(w, h, biType[pf]); initimg(img, pf, flags); tempstr=basefilename+"_enc_"+pixformat+"_" +(((flags&TJ.BOTTOMUP)!=0)? "BU":"TD")+"_"+_subnames[subsamp] +"_Q"+qual+".png"; File file=new File(tempstr); ImageIO.write(img, "png", file); } else { bmpbuf=new byte[w*h*ps+1]; initbuf(bmpbuf, w, w*ps, h, pf, flags); } Arrays.fill(jpegbuf, (byte)0); t=gettime(); tjc.setSubsamp(subsamp); tjc.setJPEGQuality(qual); if(bi) { if(yuv==YUVENCODE) tjc.encodeYUV(img, jpegbuf, flags); else tjc.compress(img, jpegbuf, flags); } else { tjc.setBitmapBuffer(bmpbuf, w, 0, h, pf); if(yuv==YUVENCODE) tjc.encodeYUV(jpegbuf, flags); else tjc.compress(jpegbuf, flags); } size=tjc.getCompressedSize(); t=gettime()-t; if(yuv==YUVENCODE) tempstr=basefilename+"_enc_"+pixformat+"_" +(((flags&TJ.BOTTOMUP)!=0)? "BU":"TD")+"_"+_subnames[subsamp]+".yuv"; else tempstr=basefilename+"_enc_"+pixformat+"_" +(((flags&TJ.BOTTOMUP)!=0)? "BU":"TD")+"_"+_subnames[subsamp] +"_Q"+qual+".jpg"; writejpeg(jpegbuf, size, tempstr); if(yuv==YUVENCODE) { if(checkbufyuv(jpegbuf, size, w, h, subsamp)==1) System.out.print("Passed."); else {System.out.print("FAILED!"); exitstatus=-1;} } else System.out.print("Done."); System.out.format(" %.6f ms\n", t*1000.); System.out.println(" Result in "+tempstr); return size; } private static void _gentestbmp(TJDecompressor tjd, byte [] jpegbuf, int jpegsize, int w, int h, int pf, String basefilename, int subsamp, int flags, int scale_num, int scale_denom) throws Exception { String pixformat, tempstr; int _hdrw=0, _hdrh=0, _hdrsubsamp=-1; double t; int scaledw=(w*scale_num+scale_denom-1)/scale_denom; int scaledh=(h*scale_num+scale_denom-1)/scale_denom; int temp1, temp2; BufferedImage img=null; byte [] bmpbuf=null; if(yuv==YUVENCODE) return; pixformat=_pixformatstr[pf]; System.out.print("JPEG -> "); if(yuv==YUVDECODE) System.out.print("YUV "+_subnames[subsamp]+" ... "); else { System.out.print(pixformat+" "); if((flags&TJ.BOTTOMUP)!=0) System.out.print("Bottom-Up "); else System.out.print("Top-Down "); if(scale_num!=1 || scale_denom!=1) System.out.print(scale_num+"/"+scale_denom+" ... "); else System.out.print("... "); } t=gettime(); tjd.setJPEGBuffer(jpegbuf, jpegsize); if(tjd.getWidth()!=w || tjd.getHeight()!=h || tjd.getSubsamp()!=subsamp) throw new Exception("Incorrect JPEG header"); temp1=scaledw; temp2=scaledh; temp1=tjd.getScaledWidth(temp1, temp2); temp2=tjd.getScaledHeight(temp1, temp2); if(temp1!=scaledw || temp2!=scaledh) throw new Exception("Scaled size mismatch"); if(yuv==YUVDECODE) bmpbuf=tjd.decompressToYUV(flags); else { if(bi) img=tjd.decompress(scaledw, scaledh, biType[pf], flags); else bmpbuf=tjd.decompress(scaledw, 0, scaledh, pf, flags); } t=gettime()-t; if(bi) { tempstr=basefilename+"_dec_"+pixformat+"_" +(((flags&TJ.BOTTOMUP)!=0)? "BU":"TD")+"_"+_subnames[subsamp] +"_"+(double)scale_num/(double)scale_denom+"x"+".png"; File file=new File(tempstr); ImageIO.write(img, "png", file); } if(yuv==YUVDECODE) { if(checkbufyuv(bmpbuf, bmpbuf.length, w, h, subsamp)==1) System.out.print("Passed."); else {System.out.print("FAILED!"); exitstatus=-1;} } else { if((bi && checkimg(img, pf, subsamp, scale_num, scale_denom, flags)==1) || (!bi && checkbuf(bmpbuf, scaledw, scaledw*TJ.getPixelSize(pf), scaledh, pf, subsamp, scale_num, scale_denom, flags)==1)) System.out.print("Passed."); else { System.out.print("FAILED!"); exitstatus=-1; } } System.out.format(" %.6f ms\n", t*1000.); } private static void gentestbmp(TJDecompressor tjd, byte [] jpegbuf, int jpegsize, int w, int h, int pf, String basefilename, int subsamp, int flags) throws Exception { int i; if((subsamp==TJ.SAMP_444 || subsamp==TJ.SAMP_GRAY) && yuv==0) { TJ.ScalingFactor sf []=TJ.getScalingFactors(); for(i=0; i