| /* |
| * Copyright (C)2011-2018 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 java.nio.*; |
| import org.libjpegturbo.turbojpeg.*; |
| |
| @SuppressWarnings("checkstyle:JavadocType") |
| final class TJUnitTest { |
| |
| private TJUnitTest() {} |
| |
| static final String CLASS_NAME = |
| new TJUnitTest().getClass().getName(); |
| |
| static void usage() { |
| System.out.println("\nUSAGE: java " + CLASS_NAME + " [options]\n"); |
| System.out.println("Options:"); |
| System.out.println("-yuv = test YUV encoding/decoding support"); |
| System.out.println("-noyuvpad = do not pad each line of each Y, U, and V plane to the nearest"); |
| System.out.println(" 4-byte boundary"); |
| System.out.println("-bi = test BufferedImage support\n"); |
| System.exit(1); |
| } |
| |
| static final String[] SUBNAME_LONG = { |
| "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" |
| }; |
| static final String[] SUBNAME = { |
| "444", "422", "420", "GRAY", "440", "411" |
| }; |
| |
| static final String[] PIXFORMATSTR = { |
| "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale", |
| "RGBA", "BGRA", "ABGR", "ARGB", "CMYK" |
| }; |
| |
| static final int[] FORMATS_3BYTE = { |
| TJ.PF_RGB, TJ.PF_BGR |
| }; |
| static final int[] FORMATS_3BYTEBI = { |
| BufferedImage.TYPE_3BYTE_BGR |
| }; |
| static final int[] FORMATS_4BYTE = { |
| TJ.PF_RGBX, TJ.PF_BGRX, TJ.PF_XBGR, TJ.PF_XRGB, TJ.PF_CMYK |
| }; |
| static final int[] FORMATS_4BYTEBI = { |
| BufferedImage.TYPE_INT_BGR, BufferedImage.TYPE_INT_RGB, |
| BufferedImage.TYPE_4BYTE_ABGR, BufferedImage.TYPE_4BYTE_ABGR_PRE, |
| BufferedImage.TYPE_INT_ARGB, BufferedImage.TYPE_INT_ARGB_PRE |
| }; |
| static final int[] FORMATS_GRAY = { |
| TJ.PF_GRAY |
| }; |
| static final int[] FORMATS_GRAYBI = { |
| BufferedImage.TYPE_BYTE_GRAY |
| }; |
| static final int[] FORMATS_RGB = { |
| TJ.PF_RGB |
| }; |
| |
| private static boolean doYUV = false; |
| private static int pad = 4; |
| private static boolean bi = false; |
| |
| private static int exitStatus = 0; |
| |
| static int biTypePF(int biType) { |
| ByteOrder byteOrder = ByteOrder.nativeOrder(); |
| switch (biType) { |
| case BufferedImage.TYPE_3BYTE_BGR: |
| return TJ.PF_BGR; |
| case BufferedImage.TYPE_4BYTE_ABGR: |
| case BufferedImage.TYPE_4BYTE_ABGR_PRE: |
| return TJ.PF_ABGR; |
| case BufferedImage.TYPE_BYTE_GRAY: |
| return TJ.PF_GRAY; |
| case BufferedImage.TYPE_INT_BGR: |
| return TJ.PF_RGBX; |
| case BufferedImage.TYPE_INT_RGB: |
| return TJ.PF_BGRX; |
| case BufferedImage.TYPE_INT_ARGB: |
| case BufferedImage.TYPE_INT_ARGB_PRE: |
| return TJ.PF_BGRA; |
| default: |
| return 0; |
| } |
| } |
| |
| static String biTypeStr(int biType) { |
| switch (biType) { |
| case BufferedImage.TYPE_3BYTE_BGR: |
| return "3BYTE_BGR"; |
| case BufferedImage.TYPE_4BYTE_ABGR: |
| return "4BYTE_ABGR"; |
| case BufferedImage.TYPE_4BYTE_ABGR_PRE: |
| return "4BYTE_ABGR_PRE"; |
| case BufferedImage.TYPE_BYTE_GRAY: |
| return "BYTE_GRAY"; |
| case BufferedImage.TYPE_INT_BGR: |
| return "INT_BGR"; |
| case BufferedImage.TYPE_INT_RGB: |
| return "INT_RGB"; |
| case BufferedImage.TYPE_INT_ARGB: |
| return "INT_ARGB"; |
| case BufferedImage.TYPE_INT_ARGB_PRE: |
| return "INT_ARGB_PRE"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| static void initBuf(byte[] buf, int w, int pitch, int h, int pf, int flags) |
| throws Exception { |
| int roffset = TJ.getRedOffset(pf); |
| int goffset = TJ.getGreenOffset(pf); |
| int boffset = TJ.getBlueOffset(pf); |
| int aoffset = TJ.getAlphaOffset(pf); |
| int ps = TJ.getPixelSize(pf); |
| int index, row, col, halfway = 16; |
| |
| if (pf == TJ.PF_GRAY) { |
| Arrays.fill(buf, (byte)0); |
| for (row = 0; row < h; row++) { |
| for (col = 0; col < w; col++) { |
| if ((flags & TJ.FLAG_BOTTOMUP) != 0) |
| index = pitch * (h - row - 1) + col; |
| else |
| index = pitch * row + col; |
| if (((row / 8) + (col / 8)) % 2 == 0) |
| buf[index] = (row < halfway) ? (byte)255 : 0; |
| else |
| buf[index] = (row < halfway) ? 76 : (byte)226; |
| } |
| } |
| return; |
| } |
| if (pf == TJ.PF_CMYK) { |
| Arrays.fill(buf, (byte)255); |
| for (row = 0; row < h; row++) { |
| for (col = 0; col < w; col++) { |
| if ((flags & TJ.FLAG_BOTTOMUP) != 0) |
| index = (h - row - 1) * w + col; |
| else |
| index = row * w + col; |
| if (((row / 8) + (col / 8)) % 2 == 0) { |
| if (row >= halfway) buf[index * ps + 3] = 0; |
| } else { |
| buf[index * ps + 2] = 0; |
| if (row < halfway) |
| buf[index * ps + 1] = 0; |
| } |
| } |
| } |
| return; |
| } |
| |
| Arrays.fill(buf, (byte)0); |
| for (row = 0; row < h; row++) { |
| for (col = 0; col < w; col++) { |
| if ((flags & TJ.FLAG_BOTTOMUP) != 0) |
| index = pitch * (h - row - 1) + col * ps; |
| else |
| index = pitch * row + col * ps; |
| if (((row / 8) + (col / 8)) % 2 == 0) { |
| if (row < halfway) { |
| buf[index + roffset] = (byte)255; |
| buf[index + goffset] = (byte)255; |
| buf[index + boffset] = (byte)255; |
| } |
| } else { |
| buf[index + roffset] = (byte)255; |
| if (row >= halfway) |
| buf[index + goffset] = (byte)255; |
| } |
| if (aoffset >= 0) |
| buf[index + aoffset] = (byte)255; |
| } |
| } |
| } |
| |
| static void initIntBuf(int[] buf, int w, int pitch, int h, int pf, int flags) |
| throws Exception { |
| int rshift = TJ.getRedOffset(pf) * 8; |
| int gshift = TJ.getGreenOffset(pf) * 8; |
| int bshift = TJ.getBlueOffset(pf) * 8; |
| int ashift = TJ.getAlphaOffset(pf) * 8; |
| int index, row, col, halfway = 16; |
| |
| Arrays.fill(buf, 0); |
| for (row = 0; row < h; row++) { |
| for (col = 0; col < w; col++) { |
| if ((flags & TJ.FLAG_BOTTOMUP) != 0) |
| index = pitch * (h - row - 1) + col; |
| else |
| index = pitch * row + col; |
| if (((row / 8) + (col / 8)) % 2 == 0) { |
| if (row < halfway) { |
| buf[index] |= (255 << rshift); |
| buf[index] |= (255 << gshift); |
| buf[index] |= (255 << bshift); |
| } |
| } else { |
| buf[index] |= (255 << rshift); |
| if (row >= halfway) |
| buf[index] |= (255 << gshift); |
| } |
| if (ashift >= 0) |
| buf[index] |= (255 << ashift); |
| } |
| } |
| } |
| |
| static void initImg(BufferedImage img, int pf, int flags) throws Exception { |
| WritableRaster wr = img.getRaster(); |
| int imgType = img.getType(); |
| |
| if (imgType == BufferedImage.TYPE_INT_RGB || |
| imgType == BufferedImage.TYPE_INT_BGR || |
| imgType == BufferedImage.TYPE_INT_ARGB || |
| imgType == BufferedImage.TYPE_INT_ARGB_PRE) { |
| SinglePixelPackedSampleModel sm = |
| (SinglePixelPackedSampleModel)img.getSampleModel(); |
| int pitch = sm.getScanlineStride(); |
| DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); |
| int[] buf = db.getData(); |
| initIntBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags); |
| } else { |
| ComponentSampleModel sm = (ComponentSampleModel)img.getSampleModel(); |
| int pitch = sm.getScanlineStride(); |
| DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); |
| byte[] buf = db.getData(); |
| initBuf(buf, img.getWidth(), pitch, img.getHeight(), pf, flags); |
| } |
| } |
| |
| static void checkVal(int row, int col, int v, String vname, int cv) |
| throws Exception { |
| v = (v < 0) ? v + 256 : v; |
| if (v < cv - 1 || v > cv + 1) { |
| throw new Exception("Comp. " + vname + " at " + row + "," + col + |
| " should be " + cv + ", not " + v); |
| } |
| } |
| |
| static void checkVal0(int row, int col, int v, String vname) |
| throws Exception { |
| v = (v < 0) ? v + 256 : v; |
| if (v > 1) { |
| throw new Exception("Comp. " + vname + " at " + row + "," + col + |
| " should be 0, not " + v); |
| } |
| } |
| |
| static void checkVal255(int row, int col, int v, String vname) |
| throws Exception { |
| v = (v < 0) ? v + 256 : v; |
| if (v < 254) { |
| throw new Exception("Comp. " + vname + " at " + row + "," + col + |
| " should be 255, not " + v); |
| } |
| } |
| |
| static int checkBuf(byte[] buf, int w, int pitch, int h, int pf, int subsamp, |
| TJScalingFactor sf, int flags) throws Exception { |
| int roffset = TJ.getRedOffset(pf); |
| int goffset = TJ.getGreenOffset(pf); |
| int boffset = TJ.getBlueOffset(pf); |
| int aoffset = TJ.getAlphaOffset(pf); |
| int ps = TJ.getPixelSize(pf); |
| int index, row, col, retval = 1; |
| int halfway = 16 * sf.getNum() / sf.getDenom(); |
| int blockSize = 8 * sf.getNum() / sf.getDenom(); |
| |
| try { |
| |
| if (pf == TJ.PF_GRAY) |
| roffset = goffset = boffset = 0; |
| |
| if (pf == TJ.PF_CMYK) { |
| for (row = 0; row < h; row++) { |
| for (col = 0; col < w; col++) { |
| if ((flags & TJ.FLAG_BOTTOMUP) != 0) |
| index = (h - row - 1) * w + col; |
| else |
| index = row * w + col; |
| byte c = buf[index * ps]; |
| byte m = buf[index * ps + 1]; |
| byte y = buf[index * ps + 2]; |
| byte k = buf[index * ps + 3]; |
| checkVal255(row, col, c, "C"); |
| if (((row / blockSize) + (col / blockSize)) % 2 == 0) { |
| checkVal255(row, col, m, "M"); |
| checkVal255(row, col, y, "Y"); |
| if (row < halfway) |
| checkVal255(row, col, k, "K"); |
| else |
| checkVal0(row, col, k, "K"); |
| } else { |
| checkVal0(row, col, y, "Y"); |
| checkVal255(row, col, k, "K"); |
| if (row < halfway) |
| checkVal0(row, col, m, "M"); |
| else |
| checkVal255(row, col, m, "M"); |
| } |
| } |
| } |
| return 1; |
| } |
| |
| for (row = 0; row < halfway; row++) { |
| for (col = 0; col < w; col++) { |
| if ((flags & TJ.FLAG_BOTTOMUP) != 0) |
| index = pitch * (h - row - 1) + col * ps; |
| else |
| index = pitch * row + col * ps; |
| byte r = buf[index + roffset]; |
| byte g = buf[index + goffset]; |
| byte b = buf[index + boffset]; |
| byte a = aoffset >= 0 ? buf[index + aoffset] : (byte)255; |
| if (((row / blockSize) + (col / blockSize)) % 2 == 0) { |
| if (row < halfway) { |
| checkVal255(row, col, r, "R"); |
| checkVal255(row, col, g, "G"); |
| checkVal255(row, col, b, "B"); |
| } else { |
| checkVal0(row, col, r, "R"); |
| checkVal0(row, col, g, "G"); |
| checkVal0(row, col, b, "B"); |
| } |
| } else { |
| if (subsamp == TJ.SAMP_GRAY) { |
| if (row < halfway) { |
| checkVal(row, col, r, "R", 76); |
| checkVal(row, col, g, "G", 76); |
| checkVal(row, col, b, "B", 76); |
| } else { |
| checkVal(row, col, r, "R", 226); |
| checkVal(row, col, g, "G", 226); |
| checkVal(row, col, b, "B", 226); |
| } |
| } else { |
| checkVal255(row, col, r, "R"); |
| if (row < halfway) { |
| checkVal0(row, col, g, "G"); |
| } else { |
| checkVal255(row, col, g, "G"); |
| } |
| checkVal0(row, col, b, "B"); |
| } |
| } |
| checkVal255(row, col, a, "A"); |
| } |
| } |
| } catch (Exception e) { |
| System.out.println("\n" + e.getMessage()); |
| retval = 0; |
| } |
| |
| if (retval == 0) { |
| for (row = 0; row < h; row++) { |
| for (col = 0; col < w; col++) { |
| if (pf == TJ.PF_CMYK) { |
| int c = buf[pitch * row + col * ps]; |
| int m = buf[pitch * row + col * ps + 1]; |
| int y = buf[pitch * row + col * ps + 2]; |
| int k = buf[pitch * row + col * ps + 3]; |
| if (c < 0) c += 256; |
| if (m < 0) m += 256; |
| if (y < 0) y += 256; |
| if (k < 0) k += 256; |
| System.out.format("%3d/%3d/%3d/%3d ", c, m, y, k); |
| } else { |
| int r = buf[pitch * row + col * ps + roffset]; |
| int g = buf[pitch * row + col * ps + goffset]; |
| int b = buf[pitch * row + col * ps + boffset]; |
| 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; |
| } |
| |
| static int checkIntBuf(int[] buf, int w, int pitch, int h, int pf, |
| int subsamp, TJScalingFactor sf, int flags) |
| throws Exception { |
| int rshift = TJ.getRedOffset(pf) * 8; |
| int gshift = TJ.getGreenOffset(pf) * 8; |
| int bshift = TJ.getBlueOffset(pf) * 8; |
| int ashift = TJ.getAlphaOffset(pf) * 8; |
| int index, row, col, retval = 1; |
| int halfway = 16 * sf.getNum() / sf.getDenom(); |
| int blockSize = 8 * sf.getNum() / sf.getDenom(); |
| |
| try { |
| for (row = 0; row < halfway; row++) { |
| for (col = 0; col < w; col++) { |
| if ((flags & TJ.FLAG_BOTTOMUP) != 0) |
| index = pitch * (h - row - 1) + col; |
| else |
| index = pitch * row + col; |
| int r = (buf[index] >> rshift) & 0xFF; |
| int g = (buf[index] >> gshift) & 0xFF; |
| int b = (buf[index] >> bshift) & 0xFF; |
| int a = ashift >= 0 ? (buf[index] >> ashift) & 0xFF : 255; |
| if (((row / blockSize) + (col / blockSize)) % 2 == 0) { |
| if (row < halfway) { |
| checkVal255(row, col, r, "R"); |
| checkVal255(row, col, g, "G"); |
| checkVal255(row, col, b, "B"); |
| } else { |
| checkVal0(row, col, r, "R"); |
| checkVal0(row, col, g, "G"); |
| checkVal0(row, col, b, "B"); |
| } |
| } else { |
| if (subsamp == TJ.SAMP_GRAY) { |
| if (row < halfway) { |
| checkVal(row, col, r, "R", 76); |
| checkVal(row, col, g, "G", 76); |
| checkVal(row, col, b, "B", 76); |
| } else { |
| checkVal(row, col, r, "R", 226); |
| checkVal(row, col, g, "G", 226); |
| checkVal(row, col, b, "B", 226); |
| } |
| } else { |
| checkVal255(row, col, r, "R"); |
| if (row < halfway) { |
| checkVal0(row, col, g, "G"); |
| } else { |
| checkVal255(row, col, g, "G"); |
| } |
| checkVal0(row, col, b, "B"); |
| } |
| } |
| checkVal255(row, col, a, "A"); |
| } |
| } |
| } catch (Exception e) { |
| System.out.println("\n" + e.getMessage()); |
| retval = 0; |
| } |
| |
| if (retval == 0) { |
| for (row = 0; row < h; row++) { |
| for (col = 0; col < w; col++) { |
| int r = (buf[pitch * row + col] >> rshift) & 0xFF; |
| int g = (buf[pitch * row + col] >> gshift) & 0xFF; |
| int b = (buf[pitch * row + col] >> 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; |
| } |
| |
| static int checkImg(BufferedImage img, int pf, int subsamp, |
| TJScalingFactor sf, int flags) throws Exception { |
| WritableRaster wr = img.getRaster(); |
| int imgType = img.getType(); |
| if (imgType == BufferedImage.TYPE_INT_RGB || |
| imgType == BufferedImage.TYPE_INT_BGR || |
| imgType == BufferedImage.TYPE_INT_ARGB || |
| imgType == BufferedImage.TYPE_INT_ARGB_PRE) { |
| 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, sf, 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, |
| sf, flags); |
| } |
| } |
| |
| static int pad(int v, int p) { |
| return ((v + (p) - 1) & (~((p) - 1))); |
| } |
| |
| static int checkBufYUV(byte[] buf, int size, int w, int h, int subsamp, |
| TJScalingFactor sf) throws Exception { |
| int row, col; |
| int hsf = TJ.getMCUWidth(subsamp) / 8, vsf = TJ.getMCUHeight(subsamp) / 8; |
| int pw = pad(w, hsf), ph = pad(h, vsf); |
| int cw = pw / hsf, ch = ph / vsf; |
| int ypitch = pad(pw, pad), uvpitch = pad(cw, pad); |
| int retval = 1; |
| int correctsize = ypitch * ph + |
| (subsamp == TJ.SAMP_GRAY ? 0 : uvpitch * ch * 2); |
| int halfway = 16 * sf.getNum() / sf.getDenom(); |
| int blockSize = 8 * sf.getNum() / sf.getDenom(); |
| |
| try { |
| if (size != correctsize) |
| throw new Exception("Incorrect size " + size + ". Should be " + |
| correctsize); |
| |
| for (row = 0; row < ph; row++) { |
| for (col = 0; col < pw; col++) { |
| byte y = buf[ypitch * row + col]; |
| if (((row / blockSize) + (col / blockSize)) % 2 == 0) { |
| if (row < halfway) |
| checkVal255(row, col, y, "Y"); |
| else |
| checkVal0(row, col, y, "Y"); |
| } else { |
| if (row < halfway) |
| checkVal(row, col, y, "Y", 76); |
| else |
| checkVal(row, col, y, "Y", 226); |
| } |
| } |
| } |
| if (subsamp != TJ.SAMP_GRAY) { |
| halfway = 16 / vsf * sf.getNum() / sf.getDenom(); |
| for (row = 0; row < ch; row++) { |
| for (col = 0; col < cw; col++) { |
| byte u = buf[ypitch * ph + (uvpitch * row + col)], |
| v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)]; |
| if (((row * vsf / blockSize) + (col * hsf / blockSize)) % 2 == 0) { |
| checkVal(row, col, u, "U", 128); |
| checkVal(row, col, v, "V", 128); |
| } else { |
| if (row < halfway) { |
| checkVal(row, col, u, "U", 85); |
| checkVal255(row, col, v, "V"); |
| } else { |
| checkVal0(row, col, u, "U"); |
| checkVal(row, col, v, "V", 149); |
| } |
| } |
| } |
| } |
| } |
| } catch (Exception e) { |
| System.out.println("\n" + e.getMessage()); |
| retval = 0; |
| } |
| |
| if (retval == 0) { |
| for (row = 0; row < ph; row++) { |
| for (col = 0; col < pw; col++) { |
| int y = buf[ypitch * row + col]; |
| if (y < 0) y += 256; |
| System.out.format("%3d ", y); |
| } |
| System.out.print("\n"); |
| } |
| System.out.print("\n"); |
| for (row = 0; row < ch; row++) { |
| for (col = 0; col < cw; col++) { |
| int u = buf[ypitch * ph + (uvpitch * row + col)]; |
| if (u < 0) u += 256; |
| System.out.format("%3d ", u); |
| } |
| System.out.print("\n"); |
| } |
| System.out.print("\n"); |
| for (row = 0; row < ch; row++) { |
| for (col = 0; col < cw; col++) { |
| int v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)]; |
| if (v < 0) v += 256; |
| System.out.format("%3d ", v); |
| } |
| System.out.print("\n"); |
| } |
| } |
| |
| return retval; |
| } |
| |
| static void writeJPEG(byte[] jpegBuf, int jpegBufSize, String filename) |
| throws Exception { |
| File file = new File(filename); |
| FileOutputStream fos = new FileOutputStream(file); |
| fos.write(jpegBuf, 0, jpegBufSize); |
| fos.close(); |
| } |
| |
| static int compTest(TJCompressor tjc, byte[] dstBuf, int w, int h, int pf, |
| String baseName, int subsamp, int jpegQual, int flags) |
| throws Exception { |
| String tempStr; |
| byte[] srcBuf = null; |
| BufferedImage img = null; |
| String pfStr, pfStrLong; |
| String buStr = (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD"; |
| String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? |
| "Bottom-Up" : "Top-Down "; |
| int size = 0, ps, imgType = pf; |
| |
| if (bi) { |
| pf = biTypePF(imgType); |
| pfStr = biTypeStr(imgType); |
| pfStrLong = pfStr + " (" + PIXFORMATSTR[pf] + ")"; |
| } else { |
| pfStr = PIXFORMATSTR[pf]; |
| pfStrLong = pfStr; |
| } |
| ps = TJ.getPixelSize(pf); |
| |
| if (bi) { |
| img = new BufferedImage(w, h, imgType); |
| initImg(img, pf, flags); |
| tempStr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + |
| SUBNAME[subsamp] + "_Q" + jpegQual + ".png"; |
| File file = new File(tempStr); |
| ImageIO.write(img, "png", file); |
| tjc.setSourceImage(img, 0, 0, 0, 0); |
| } else { |
| srcBuf = new byte[w * h * ps + 1]; |
| initBuf(srcBuf, w, w * ps, h, pf, flags); |
| tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, pf); |
| } |
| Arrays.fill(dstBuf, (byte)0); |
| |
| tjc.setSubsamp(subsamp); |
| tjc.setJPEGQuality(jpegQual); |
| if (doYUV) { |
| System.out.format("%s %s -> YUV %s ... ", pfStrLong, buStrLong, |
| SUBNAME_LONG[subsamp]); |
| YUVImage yuvImage = tjc.encodeYUV(pad, flags); |
| if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), w, h, subsamp, |
| new TJScalingFactor(1, 1)) == 1) |
| System.out.print("Passed.\n"); |
| else { |
| System.out.print("FAILED!\n"); |
| exitStatus = -1; |
| } |
| |
| System.out.format("YUV %s %s -> JPEG Q%d ... ", SUBNAME_LONG[subsamp], |
| buStrLong, jpegQual); |
| tjc.setSourceImage(yuvImage); |
| } else { |
| System.out.format("%s %s -> %s Q%d ... ", pfStrLong, buStrLong, |
| SUBNAME_LONG[subsamp], jpegQual); |
| } |
| tjc.compress(dstBuf, flags); |
| size = tjc.getCompressedSize(); |
| |
| tempStr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + |
| SUBNAME[subsamp] + "_Q" + jpegQual + ".jpg"; |
| writeJPEG(dstBuf, size, tempStr); |
| System.out.println("Done.\n Result in " + tempStr); |
| |
| return size; |
| } |
| |
| static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, |
| int w, int h, int pf, String baseName, int subsamp, |
| int flags, TJScalingFactor sf) throws Exception { |
| String pfStr, pfStrLong, tempStr; |
| String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? |
| "Bottom-Up" : "Top-Down "; |
| int scaledWidth = sf.getScaled(w); |
| int scaledHeight = sf.getScaled(h); |
| int temp1, temp2, imgType = pf; |
| BufferedImage img = null; |
| byte[] dstBuf = null; |
| |
| if (bi) { |
| pf = biTypePF(imgType); |
| pfStr = biTypeStr(imgType); |
| pfStrLong = pfStr + " (" + PIXFORMATSTR[pf] + ")"; |
| } else { |
| pfStr = PIXFORMATSTR[pf]; |
| pfStrLong = pfStr; |
| } |
| |
| tjd.setSourceImage(jpegBuf, jpegSize); |
| if (tjd.getWidth() != w || tjd.getHeight() != h || |
| tjd.getSubsamp() != subsamp) |
| throw new Exception("Incorrect JPEG header"); |
| |
| temp1 = scaledWidth; |
| temp2 = scaledHeight; |
| temp1 = tjd.getScaledWidth(temp1, temp2); |
| temp2 = tjd.getScaledHeight(temp1, temp2); |
| if (temp1 != scaledWidth || temp2 != scaledHeight) |
| throw new Exception("Scaled size mismatch"); |
| |
| if (doYUV) { |
| System.out.format("JPEG -> YUV %s ", SUBNAME_LONG[subsamp]); |
| if (!sf.isOne()) |
| System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom()); |
| else System.out.print("... "); |
| YUVImage yuvImage = tjd.decompressToYUV(scaledWidth, pad, scaledHeight, |
| flags); |
| if (checkBufYUV(yuvImage.getBuf(), yuvImage.getSize(), scaledWidth, |
| scaledHeight, subsamp, sf) == 1) |
| System.out.print("Passed.\n"); |
| else { |
| System.out.print("FAILED!\n"); exitStatus = -1; |
| } |
| |
| System.out.format("YUV %s -> %s %s ... ", SUBNAME_LONG[subsamp], |
| pfStrLong, buStrLong); |
| tjd.setSourceImage(yuvImage); |
| } else { |
| System.out.format("JPEG -> %s %s ", pfStrLong, buStrLong); |
| if (!sf.isOne()) |
| System.out.format("%d/%d ... ", sf.getNum(), sf.getDenom()); |
| else System.out.print("... "); |
| } |
| if (bi) |
| img = tjd.decompress(scaledWidth, scaledHeight, imgType, flags); |
| else |
| dstBuf = tjd.decompress(scaledWidth, 0, scaledHeight, pf, flags); |
| |
| if (bi) { |
| tempStr = baseName + "_dec_" + pfStr + "_" + |
| (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + |
| SUBNAME[subsamp] + "_" + |
| (double)sf.getNum() / (double)sf.getDenom() + "x" + ".png"; |
| File file = new File(tempStr); |
| ImageIO.write(img, "png", file); |
| } |
| |
| if ((bi && checkImg(img, pf, subsamp, sf, flags) == 1) || |
| (!bi && checkBuf(dstBuf, scaledWidth, |
| scaledWidth * TJ.getPixelSize(pf), scaledHeight, pf, |
| subsamp, sf, flags) == 1)) |
| System.out.print("Passed.\n"); |
| else { |
| System.out.print("FAILED!\n"); |
| exitStatus = -1; |
| } |
| } |
| |
| static void decompTest(TJDecompressor tjd, byte[] jpegBuf, int jpegSize, |
| int w, int h, int pf, String baseName, int subsamp, |
| int flags) throws Exception { |
| int i; |
| TJScalingFactor[] sf = TJ.getScalingFactors(); |
| for (i = 0; i < sf.length; i++) { |
| int num = sf[i].getNum(); |
| int denom = sf[i].getDenom(); |
| if (subsamp == TJ.SAMP_444 || subsamp == TJ.SAMP_GRAY || |
| (subsamp == TJ.SAMP_411 && num == 1 && |
| (denom == 2 || denom == 1)) || |
| (subsamp != TJ.SAMP_411 && num == 1 && |
| (denom == 4 || denom == 2 || denom == 1))) |
| decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, |
| flags, sf[i]); |
| } |
| } |
| |
| static void doTest(int w, int h, int[] formats, int subsamp, String baseName) |
| throws Exception { |
| TJCompressor tjc = null; |
| TJDecompressor tjd = null; |
| int size; |
| byte[] dstBuf; |
| |
| dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; |
| |
| try { |
| tjc = new TJCompressor(); |
| tjd = new TJDecompressor(); |
| |
| for (int pf : formats) { |
| if (pf < 0) continue; |
| for (int i = 0; i < 2; i++) { |
| int flags = 0; |
| if (subsamp == TJ.SAMP_422 || subsamp == TJ.SAMP_420 || |
| subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411) |
| flags |= TJ.FLAG_FASTUPSAMPLE; |
| if (i == 1) |
| flags |= TJ.FLAG_BOTTOMUP; |
| size = compTest(tjc, dstBuf, w, h, pf, baseName, subsamp, 100, |
| flags); |
| decompTest(tjd, dstBuf, size, w, h, pf, baseName, subsamp, flags); |
| if (pf >= TJ.PF_RGBX && pf <= TJ.PF_XRGB && !bi) { |
| System.out.print("\n"); |
| decompTest(tjd, dstBuf, size, w, h, pf + (TJ.PF_RGBA - TJ.PF_RGBX), |
| baseName, subsamp, flags); |
| } |
| System.out.print("\n"); |
| } |
| } |
| System.out.print("--------------------\n\n"); |
| } catch (Exception e) { |
| if (tjc != null) tjc.close(); |
| if (tjd != null) tjd.close(); |
| throw e; |
| } |
| if (tjc != null) tjc.close(); |
| if (tjd != null) tjd.close(); |
| } |
| |
| static void bufSizeTest() throws Exception { |
| int w, h, i, subsamp; |
| byte[] srcBuf, dstBuf = null; |
| YUVImage dstImage = null; |
| TJCompressor tjc = null; |
| Random r = new Random(); |
| |
| try { |
| tjc = new TJCompressor(); |
| System.out.println("Buffer size regression test"); |
| for (subsamp = 0; subsamp < TJ.NUMSAMP; subsamp++) { |
| for (w = 1; w < 48; w++) { |
| int maxh = (w == 1) ? 2048 : 48; |
| for (h = 1; h < maxh; h++) { |
| if (h % 100 == 0) |
| System.out.format("%04d x %04d\b\b\b\b\b\b\b\b\b\b\b", w, h); |
| srcBuf = new byte[w * h * 4]; |
| if (doYUV) |
| dstImage = new YUVImage(w, pad, h, subsamp); |
| else |
| dstBuf = new byte[TJ.bufSize(w, h, subsamp)]; |
| for (i = 0; i < w * h * 4; i++) { |
| srcBuf[i] = (byte)(r.nextInt(2) * 255); |
| } |
| tjc.setSourceImage(srcBuf, 0, 0, w, 0, h, TJ.PF_BGRX); |
| tjc.setSubsamp(subsamp); |
| tjc.setJPEGQuality(100); |
| if (doYUV) |
| tjc.encodeYUV(dstImage, 0); |
| else |
| tjc.compress(dstBuf, 0); |
| |
| srcBuf = new byte[h * w * 4]; |
| if (doYUV) |
| dstImage = new YUVImage(h, pad, w, subsamp); |
| else |
| dstBuf = new byte[TJ.bufSize(h, w, subsamp)]; |
| for (i = 0; i < h * w * 4; i++) { |
| srcBuf[i] = (byte)(r.nextInt(2) * 255); |
| } |
| tjc.setSourceImage(srcBuf, 0, 0, h, 0, w, TJ.PF_BGRX); |
| if (doYUV) |
| tjc.encodeYUV(dstImage, 0); |
| else |
| tjc.compress(dstBuf, 0); |
| } |
| dstImage = null; |
| dstBuf = null; |
| System.gc(); |
| } |
| } |
| System.out.println("Done. "); |
| } catch (Exception e) { |
| if (tjc != null) tjc.close(); |
| throw e; |
| } |
| if (tjc != null) tjc.close(); |
| } |
| |
| public static void main(String[] argv) { |
| try { |
| String testName = "javatest"; |
| for (int i = 0; i < argv.length; i++) { |
| if (argv[i].equalsIgnoreCase("-yuv")) |
| doYUV = true; |
| else if (argv[i].equalsIgnoreCase("-noyuvpad")) |
| pad = 1; |
| else if (argv[i].equalsIgnoreCase("-bi")) { |
| bi = true; |
| testName = "javabitest"; |
| } else |
| usage(); |
| } |
| if (doYUV) |
| FORMATS_4BYTE[4] = -1; |
| doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_444, |
| testName); |
| doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_444, |
| testName); |
| doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_422, |
| testName); |
| doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_422, |
| testName); |
| doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_420, |
| testName); |
| doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_420, |
| testName); |
| doTest(35, 39, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_440, |
| testName); |
| doTest(39, 41, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_440, |
| testName); |
| doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_411, |
| testName); |
| doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_411, |
| testName); |
| doTest(39, 41, bi ? FORMATS_GRAYBI : FORMATS_GRAY, TJ.SAMP_GRAY, |
| testName); |
| doTest(41, 35, bi ? FORMATS_3BYTEBI : FORMATS_3BYTE, TJ.SAMP_GRAY, |
| testName); |
| FORMATS_4BYTE[4] = -1; |
| doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4BYTE, TJ.SAMP_GRAY, |
| testName); |
| if (!bi) |
| bufSizeTest(); |
| if (doYUV && !bi) { |
| System.out.print("\n--------------------\n\n"); |
| doTest(48, 48, FORMATS_RGB, TJ.SAMP_444, "javatest_yuv0"); |
| doTest(48, 48, FORMATS_RGB, TJ.SAMP_422, "javatest_yuv0"); |
| doTest(48, 48, FORMATS_RGB, TJ.SAMP_420, "javatest_yuv0"); |
| doTest(48, 48, FORMATS_RGB, TJ.SAMP_440, "javatest_yuv0"); |
| doTest(48, 48, FORMATS_RGB, TJ.SAMP_411, "javatest_yuv0"); |
| doTest(48, 48, FORMATS_RGB, TJ.SAMP_GRAY, "javatest_yuv0"); |
| doTest(48, 48, FORMATS_GRAY, TJ.SAMP_GRAY, "javatest_yuv0"); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| exitStatus = -1; |
| } |
| System.exit(exitStatus); |
| } |
| } |