/** * Encode the uncompressed source image stored in <code>srcImage</code> and output a YUV planar * image to the given destination buffer. See {@link #encodeYUV(byte[], int)} for more detail. * * @param srcImage a <code>BufferedImage</code> instance containing RGB or grayscale pixels to be * encoded * @param dstBuf buffer that will receive the YUV planar image. Use {@link TJ#bufSizeYUV} to * determine the appropriate size for this buffer based on the image width, height, and level * of chrominance subsampling. * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} */ public void encodeYUV(BufferedImage srcImage, byte[] dstBuf, int flags) throws Exception { if (srcImage == null || dstBuf == null || flags < 0) throw new Exception("Invalid argument in encodeYUV()"); int width = srcImage.getWidth(); int height = srcImage.getHeight(); int pixelFormat; boolean intPixels = false; if (byteOrder == null) byteOrder = ByteOrder.nativeOrder(); switch (srcImage.getType()) { case BufferedImage.TYPE_3BYTE_BGR: pixelFormat = TJ.PF_BGR; break; case BufferedImage.TYPE_4BYTE_ABGR: case BufferedImage.TYPE_4BYTE_ABGR_PRE: pixelFormat = TJ.PF_XBGR; break; case BufferedImage.TYPE_BYTE_GRAY: pixelFormat = TJ.PF_GRAY; break; case BufferedImage.TYPE_INT_BGR: if (byteOrder == ByteOrder.BIG_ENDIAN) pixelFormat = TJ.PF_XBGR; else pixelFormat = TJ.PF_RGBX; intPixels = true; break; case BufferedImage.TYPE_INT_RGB: case BufferedImage.TYPE_INT_ARGB: case BufferedImage.TYPE_INT_ARGB_PRE: if (byteOrder == ByteOrder.BIG_ENDIAN) pixelFormat = TJ.PF_XRGB; else pixelFormat = TJ.PF_BGRX; intPixels = true; break; default: throw new Exception("Unsupported BufferedImage format"); } WritableRaster wr = srcImage.getRaster(); if (subsamp < 0) throw new Exception("Subsampling level not set"); if (intPixels) { SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel) srcImage.getSampleModel(); int pitch = sm.getScanlineStride(); DataBufferInt db = (DataBufferInt) wr.getDataBuffer(); int[] buf = db.getData(); encodeYUV(buf, width, pitch, height, pixelFormat, dstBuf, subsamp, flags); } else { ComponentSampleModel sm = (ComponentSampleModel) srcImage.getSampleModel(); int pixelSize = sm.getPixelStride(); if (pixelSize != TJ.getPixelSize(pixelFormat)) throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage"); int pitch = sm.getScanlineStride(); DataBufferByte db = (DataBufferByte) wr.getDataBuffer(); byte[] buf = db.getData(); encodeYUV(buf, width, pitch, height, pixelFormat, dstBuf, subsamp, flags); } compressedSize = TJ.bufSizeYUV(width, height, subsamp); }
/** * Decompress the JPEG source image associated with this decompressor instance and return a buffer * containing the decompressed image. * * @param desiredWidth see {@link #decompress(byte[], int, int, int, int, int, int, int)} for * description * @param pitch see {@link #decompress(byte[], int, int, int, int, int, int, int)} for description * @param desiredHeight see {@link #decompress(byte[], int, int, int, int, int, int, int)} for * description * @param pixelFormat pixel format of the decompressed image (one of {@link TJ TJ.PF_*}) * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} * @return a buffer containing the decompressed image */ public byte[] decompress( int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) throws Exception { if (desiredWidth < 0 || pitch < 0 || desiredHeight < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) throw new Exception("Invalid argument in decompress()"); int pixelSize = TJ.getPixelSize(pixelFormat); int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); if (pitch == 0) pitch = scaledWidth * pixelSize; byte[] buf = new byte[pitch * scaledHeight]; decompress(buf, desiredWidth, pitch, desiredHeight, pixelFormat, flags); return buf; }
/** * Associate an uncompressed source image with this compressor instance. * * @param srcImage image buffer containing RGB or grayscale pixels to be compressed * @param x x offset (in pixels) of the region from which the JPEG image should be compressed, * relative to the start of <code>srcImage</code>. * @param y y offset (in pixels) of the region from which the JPEG image should be compressed, * relative to the start of <code>srcImage</code>. * @param width width (in pixels) of the region in the source image from which the JPEG image * should be compressed. * @param pitch bytes per line of the source image. Normally, this should be <code> * width * TJ.pixelSize(pixelFormat)</code> if the source image is unpadded, but you can use * this parameter to, for instance, specify that the scanlines in the source image are padded * to a 4-byte boundary or to compress a JPEG image from a region of a larger source image. * You can also be clever and use this parameter to skip lines, etc. Setting this parameter to * 0 is the equivalent of setting it to <code>width * * TJ.pixelSize(pixelFormat)</code>. * @param height height (in pixels) of the region in the source image from which the JPEG image * should be compressed. * @param pixelFormat pixel format of the source image (one of {@link TJ TJ.PF_*}) */ public void setSourceImage( byte[] srcImage, int x, int y, int width, int pitch, int height, int pixelFormat) throws Exception { if (handle == 0) init(); if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 || pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF) throw new Exception("Invalid argument in setSourceImage()"); srcBuf = srcImage; srcWidth = width; if (pitch == 0) srcPitch = width * TJ.getPixelSize(pixelFormat); else srcPitch = pitch; srcHeight = height; srcPixelFormat = pixelFormat; srcX = x; srcY = y; }
private 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 = alphaOffset[pf]; int ps = TJ.getPixelSize(pf); int index, row, col, halfway = 16; Arrays.fill(buf, (byte) 0); if (pf == TJ.PF_GRAY) { 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; } 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; } } }
private 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, tempstr; double t; int scaledWidth = sf.getScaled(w); int scaledHeight = sf.getScaled(h); int temp1, temp2, imgType = pf; BufferedImage img = null; byte[] dstBuf = null; if (yuv == YUVENCODE) return; if (bi) { pf = biTypePF(imgType); pfStr = biTypeStr(imgType); } else pfStr = pixFormatStr[pf]; System.out.print("JPEG -> "); if (yuv == YUVDECODE) System.out.print("YUV " + subName[subsamp] + " ... "); else { System.out.print(pfStr + " "); if (bi) System.out.print("(" + pixFormatStr[pf] + ") "); if ((flags & TJ.FLAG_BOTTOMUP) != 0) System.out.print("Bottom-Up "); else System.out.print("Top-Down "); if (!sf.isOne()) System.out.print(sf.getNum() + "/" + sf.getDenom() + " ... "); else System.out.print("... "); } t = getTime(); tjd.setJPEGImage(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 (yuv == YUVDECODE) dstBuf = tjd.decompressToYUV(flags); else { if (bi) img = tjd.decompress(scaledWidth, scaledHeight, imgType, flags); else dstBuf = tjd.decompress(scaledWidth, 0, scaledHeight, pf, flags); } t = getTime() - t; 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 (yuv == YUVDECODE) { if (checkBufYUV(dstBuf, dstBuf.length, w, h, subsamp) == 1) System.out.print("Passed."); else { System.out.print("FAILED!"); exitStatus = -1; } } else { 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."); else { System.out.print("FAILED!"); exitStatus = -1; } } System.out.format(" %.6f ms\n", t * 1000.); }
private 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; double t; int size = 0, ps, imgType = pf; if (bi) { pf = biTypePF(imgType); pfStr = biTypeStr(imgType); } else pfStr = pixFormatStr[pf]; ps = TJ.getPixelSize(pf); System.out.print(pfStr + " "); if (bi) System.out.print("(" + pixFormatStr[pf] + ") "); if ((flags & TJ.FLAG_BOTTOMUP) != 0) System.out.print("Bottom-Up"); else System.out.print("Top-Down "); System.out.print(" -> " + subNameLong[subsamp] + " "); if (yuv == YUVENCODE) System.out.print("YUV ... "); else System.out.print("Q" + jpegQual + " ... "); if (bi) { img = new BufferedImage(w, h, imgType); initImg(img, pf, flags); tempstr = baseName + "_enc_" + pfStr + "_" + (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + subName[subsamp] + "_Q" + jpegQual + ".png"; File file = new File(tempstr); ImageIO.write(img, "png", file); } else { srcBuf = new byte[w * h * ps + 1]; initBuf(srcBuf, w, w * ps, h, pf, flags); } Arrays.fill(dstBuf, (byte) 0); t = getTime(); tjc.setSubsamp(subsamp); tjc.setJPEGQuality(jpegQual); if (bi) { if (yuv == YUVENCODE) tjc.encodeYUV(img, dstBuf, flags); else tjc.compress(img, dstBuf, flags); } else { tjc.setSourceImage(srcBuf, w, 0, h, pf); if (yuv == YUVENCODE) tjc.encodeYUV(dstBuf, flags); else tjc.compress(dstBuf, flags); } size = tjc.getCompressedSize(); t = getTime() - t; if (yuv == YUVENCODE) tempstr = baseName + "_enc_" + pfStr + "_" + (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + subName[subsamp] + ".yuv"; else tempstr = baseName + "_enc_" + pfStr + "_" + (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + subName[subsamp] + "_Q" + jpegQual + ".jpg"; writeJPEG(dstBuf, size, tempstr); if (yuv == YUVENCODE) { if (checkBufYUV(dstBuf, 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 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 = alphaOffset[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 { 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(e); retval = 0; } if (retval == 0) { System.out.print("\n"); for (row = 0; row < h; row++) { for (col = 0; col < w; col++) { 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; }
/** * Decompress the JPEG source image associated with this decompressor instance and output a * decompressed image to the given <code>BufferedImage</code> instance. * * @param dstImage a <code>BufferedImage</code> instance that will receive the decompressed image * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*} */ public void decompress(BufferedImage dstImage, int flags) throws Exception { if (dstImage == null || flags < 0) throw new Exception("Invalid argument in decompress()"); int desiredWidth = dstImage.getWidth(); int desiredHeight = dstImage.getHeight(); int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); if (scaledWidth != desiredWidth || scaledHeight != desiredHeight) throw new Exception( "BufferedImage dimensions do not match a scaled image size that TurboJPEG is capable of generating."); int pixelFormat; boolean intPixels = false; if (byteOrder == null) byteOrder = ByteOrder.nativeOrder(); switch (dstImage.getType()) { case BufferedImage.TYPE_3BYTE_BGR: pixelFormat = TJ.PF_BGR; break; case BufferedImage.TYPE_4BYTE_ABGR: case BufferedImage.TYPE_4BYTE_ABGR_PRE: pixelFormat = TJ.PF_XBGR; break; case BufferedImage.TYPE_BYTE_GRAY: pixelFormat = TJ.PF_GRAY; break; case BufferedImage.TYPE_INT_BGR: if (byteOrder == ByteOrder.BIG_ENDIAN) pixelFormat = TJ.PF_XBGR; else pixelFormat = TJ.PF_RGBX; intPixels = true; break; case BufferedImage.TYPE_INT_RGB: if (byteOrder == ByteOrder.BIG_ENDIAN) pixelFormat = TJ.PF_XRGB; else pixelFormat = TJ.PF_BGRX; intPixels = true; break; case BufferedImage.TYPE_INT_ARGB: case BufferedImage.TYPE_INT_ARGB_PRE: if (byteOrder == ByteOrder.BIG_ENDIAN) pixelFormat = TJ.PF_ARGB; else pixelFormat = TJ.PF_BGRA; intPixels = true; break; default: throw new Exception("Unsupported BufferedImage format"); } WritableRaster wr = dstImage.getRaster(); if (intPixels) { SinglePixelPackedSampleModel sm = (SinglePixelPackedSampleModel) dstImage.getSampleModel(); int stride = sm.getScanlineStride(); DataBufferInt db = (DataBufferInt) wr.getDataBuffer(); int[] buf = db.getData(); if (jpegBuf == null) throw new Exception(NO_ASSOC_ERROR); decompress(jpegBuf, jpegBufSize, buf, scaledWidth, stride, scaledHeight, pixelFormat, flags); } else { ComponentSampleModel sm = (ComponentSampleModel) dstImage.getSampleModel(); int pixelSize = sm.getPixelStride(); if (pixelSize != TJ.getPixelSize(pixelFormat)) throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage"); int pitch = sm.getScanlineStride(); DataBufferByte db = (DataBufferByte) wr.getDataBuffer(); byte[] buf = db.getData(); decompress(buf, scaledWidth, pitch, scaledHeight, pixelFormat, flags); } }