Extend the TurboJPEG Java API to support compressing JPEG images from YUV planar images

git-svn-id: svn+ssh://svn.code.sf.net/p/libjpeg-turbo/code/trunk@1071 632fc199-4ca6-4c93-a231-07263d6284db
This commit is contained in:
DRC
2013-10-31 05:04:51 +00:00
parent faa868bbb6
commit 1e67274bd7
8 changed files with 282 additions and 44 deletions

View File

@@ -538,6 +538,54 @@ public class TJUnitTest {
return ((v + (p) - 1) & (~((p) - 1)));
}
private static void initBufYUV(byte[] buf, int w, int pad, int h,
int subsamp) 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 halfway = 16, blockSize = 8;
Arrays.fill(buf, (byte)0);
for (row = 0; row < ph; row++) {
for (col = 0; col < pw; col++) {
int index = ypitch * row + col;
if (((row / blockSize) + (col / blockSize)) % 2 == 0) {
if (row < halfway)
buf[index] = (byte)255;
else
buf[index] = 0;
} else {
if (row < halfway)
buf[index] = 76;
else
buf[index] = (byte)226;
}
}
}
if (subsamp != TJ.SAMP_GRAY) {
halfway = 16 / vsf;
for (row = 0; row < ch; row++) {
for (col = 0; col < cw; col++) {
int uindex = ypitch * ph + (uvpitch * row + col),
vindex = ypitch * ph + uvpitch * ch + (uvpitch * row + col);
if (((row * vsf / blockSize) + (col * hsf / blockSize)) % 2 == 0) {
buf[uindex] = buf[vindex] = (byte)128;
} else {
if (row < halfway) {
buf[uindex] = 85;
buf[vindex] = (byte)255;
} else {
buf[uindex] = 0;
buf[vindex] = (byte)149;
}
}
}
}
}
}
private static int checkBufYUV(byte[] buf, int size, int w, int h,
int subsamp, TJScalingFactor sf)
throws Exception {
@@ -646,40 +694,47 @@ public class TJUnitTest {
byte[] srcBuf = null;
BufferedImage img = null;
String pfStr;
String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ?
"Bottom-Up" : "Top-Down ";
String buStr = (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD";
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);
if (yuv == YUVDECODE) {
System.out.format("YUV %s %s --> JPEG Q%d ... ", subNameLong[subsamp],
buStrLong, jpegQual);
srcBuf = new byte[TJ.bufSizeYUV(w, pad, h, subsamp)];
initBufYUV(srcBuf, w, pad, h, subsamp);
pfStr = "YUV";
} else {
srcBuf = new byte[w * h * ps + 1];
initBuf(srcBuf, w, w * ps, h, pf, flags);
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 (yuv == YUVENCODE)
System.out.format("%s -> %s YUV ... ", buStrLong,
subNameLong[subsamp]);
else
System.out.format("%s -> %s Q%d ... ", buStrLong, subNameLong[subsamp],
jpegQual);
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);
} else {
srcBuf = new byte[w * h * ps + 1];
initBuf(srcBuf, w, w * ps, h, pf, flags);
}
}
Arrays.fill(dstBuf, (byte)0);
@@ -693,7 +748,10 @@ public class TJUnitTest {
else
tjc.compress(img, dstBuf, flags);
} else {
tjc.setSourceImage(srcBuf, w, 0, h, pf);
if (yuv == YUVDECODE)
tjc.setSourceImageYUV(srcBuf, w, pad, h);
else
tjc.setSourceImage(srcBuf, w, 0, h, pf);
if (yuv == YUVENCODE)
tjc.encodeYUV(dstBuf, flags);
else
@@ -703,12 +761,10 @@ public class TJUnitTest {
t = getTime() - t;
if (yuv == YUVENCODE)
tempstr = baseName + "_enc_" + pfStr + "_" +
(((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" +
tempstr = baseName + "_enc_" + pfStr + "_" + buStr + "_" +
subName[subsamp] + ".yuv";
else
tempstr = baseName + "_enc_" + pfStr + "_" +
(((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" +
tempstr = baseName + "_enc_" + pfStr + "_" + buStr + "_" +
subName[subsamp] + "_Q" + jpegQual + ".jpg";
writeJPEG(dstBuf, size, tempstr);

View File

@@ -525,6 +525,10 @@ Method in class org.libjpegturbo.turbojpeg.<A HREF="./org/libjpegturbo/turbojpeg
Method in class org.libjpegturbo.turbojpeg.<A HREF="./org/libjpegturbo/turbojpeg/TJCompressor.html" title="class in org.libjpegturbo.turbojpeg">TJCompressor</A>
<DD><B>Deprecated.</B>&nbsp;<I>Use
<A HREF="./org/libjpegturbo/turbojpeg/TJCompressor.html#setSourceImage(byte[], int, int, int, int, int, int)"><CODE>TJCompressor.setSourceImage(byte[], int, int, int, int, int, int)</CODE></A> instead.</I>
<DT><A HREF="./org/libjpegturbo/turbojpeg/TJCompressor.html#setSourceImageYUV(byte[], int, int, int)"><B>setSourceImageYUV(byte[], int, int, int)</B></A> -
Method in class org.libjpegturbo.turbojpeg.<A HREF="./org/libjpegturbo/turbojpeg/TJCompressor.html" title="class in org.libjpegturbo.turbojpeg">TJCompressor</A>
<DD>Associate an uncompressed YUV planar source image with this compressor
instance.
<DT><A HREF="./org/libjpegturbo/turbojpeg/TJCompressor.html#setSubsamp(int)"><B>setSubsamp(int)</B></A> -
Method in class org.libjpegturbo.turbojpeg.<A HREF="./org/libjpegturbo/turbojpeg/TJCompressor.html" title="class in org.libjpegturbo.turbojpeg">TJCompressor</A>
<DD>Set the level of chrominance subsampling for subsequent compress/encode

View File

@@ -9,8 +9,42 @@ Generated Documentation (Untitled)
targetPage = "" + window.location.search;
if (targetPage != "" && targetPage != "undefined")
targetPage = targetPage.substring(1);
if (targetPage.indexOf(":") != -1)
if (targetPage.indexOf(":") != -1 || (targetPage != "" && !validURL(targetPage)))
targetPage = "undefined";
function validURL(url) {
var pos = url.indexOf(".html");
if (pos == -1 || pos != url.length - 5)
return false;
var allowNumber = false;
var allowSep = false;
var seenDot = false;
for (var i = 0; i < url.length - 5; i++) {
var ch = url.charAt(i);
if ('a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
ch == '$' ||
ch == '_') {
allowNumber = true;
allowSep = true;
} else if ('0' <= ch && ch <= '9'
|| ch == '-') {
if (!allowNumber)
return false;
} else if (ch == '/' || ch == '.') {
if (!allowSep)
return false;
allowNumber = false;
allowSep = false;
if (ch == '.')
seenDot = true;
if (ch == '/' && seenDot)
return false;
} else {
return false;
}
}
return true;
}
function loadFrames() {
if (targetPage != "" && targetPage != "undefined")
top.classFrame.location = top.targetPage;

View File

@@ -298,6 +298,18 @@ TurboJPEG compressor
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/libjpegturbo/turbojpeg/TJCompressor.html#setSourceImageYUV(byte[], int, int, int)">setSourceImageYUV</A></B>(byte[]&nbsp;srcImage,
int&nbsp;width,
int&nbsp;pad,
int&nbsp;height)</CODE>
<BR>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Associate an uncompressed YUV planar source image with this compressor
instance.</TD>
</TR>
<TR BGCOLOR="white" CLASS="TableRowColor">
<TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1">
<CODE>&nbsp;void</CODE></FONT></TD>
<TD><CODE><B><A HREF="../../../org/libjpegturbo/turbojpeg/TJCompressor.html#setSubsamp(int)">setSubsamp</A></B>(int&nbsp;newSubsamp)</CODE>
<BR>
@@ -462,6 +474,38 @@ setSourceImage</H3>
</DL>
<HR>
<A NAME="setSourceImageYUV(byte[], int, int, int)"><!-- --></A><H3>
setSourceImageYUV</H3>
<PRE>
public void <B>setSourceImageYUV</B>(byte[]&nbsp;srcImage,
int&nbsp;width,
int&nbsp;pad,
int&nbsp;height)
throws java.lang.Exception</PRE>
<DL>
<DD>Associate an uncompressed YUV planar source image with this compressor
instance.
<P>
<DD><DL>
<DT><B>Parameters:</B><DD><CODE>srcImage</CODE> - image buffer containing a YUV planar image to be
compressed. The Y, U (Cb), and V (Cr) image planes should be stored
sequentially in the buffer, and the size of each plane is determined by
the specified width, height, and padding, as well as the level of
chrominance subsampling (specified using <A HREF="../../../org/libjpegturbo/turbojpeg/TJCompressor.html#setSubsamp(int)"><CODE>setSubsamp(int)</CODE></A>.) If the
chrominance components are subsampled along the horizontal dimension, then
the width of the luminance plane should be padded to the nearest multiple
of 2 (same goes for the height of the luminance plane, if the chrominance
components are subsampled along the vertical dimension.) This is
irrespective of any additional padding specified in the <code>pad</code>
parameter.<DD><CODE>width</CODE> - width (in pixels) of the source image<DD><CODE>pad</CODE> - the line padding used in the source image. For instance, if
each line in each plane of the YUV image is padded to the nearest multiple
of 4 bytes, then <code>pad</code> should be set to 4.<DD><CODE>height</CODE> - height (in pixels) of the source image
<DT><B>Throws:</B>
<DD><CODE>java.lang.Exception</CODE></DL>
</DD>
</DL>
<HR>
<A NAME="setSubsamp(int)"><!-- --></A><H3>
setSubsamp</H3>
<PRE>
@@ -476,6 +520,10 @@ public void <B>setSubsamp</B>(int&nbsp;newSubsamp)
image with little perceptible loss of image clarity (the human eye is more
sensitive to small changes in brightness than to small changes in color.)
This is called "chrominance subsampling".
<p>
NOTE: When compressing a YUV planar image into a JPEG image, this method
also specifies the level of chrominance subsampling used in the source
image.
<P>
<DD><DL>
<DT><B>Parameters:</B><DD><CODE>newSubsamp</CODE> - the new level of chrominance subsampling (one of

View File

@@ -139,6 +139,7 @@ public class TJCompressor {
srcPixelFormat = pixelFormat;
srcX = x;
srcY = y;
srcIsYUV = false;
}
/**
@@ -152,6 +153,41 @@ public class TJCompressor {
srcX = srcY = -1;
}
/**
* Associate an uncompressed YUV planar source image with this compressor
* instance.
*
* @param srcImage image buffer containing a YUV planar image to be
* compressed. The Y, U (Cb), and V (Cr) image planes should be stored
* sequentially in the buffer, and the size of each plane is determined by
* the specified width, height, and padding, as well as the level of
* chrominance subsampling (specified using {@link #setSubsamp}.) If the
* chrominance components are subsampled along the horizontal dimension, then
* the width of the luminance plane should be padded to the nearest multiple
* of 2 (same goes for the height of the luminance plane, if the chrominance
* components are subsampled along the vertical dimension.) This is
* irrespective of any additional padding specified in the <code>pad</code>
* parameter.
*
* @param width width (in pixels) of the source image
*
* @param pad the line padding used in the source image. For instance, if
* each line in each plane of the YUV image is padded to the nearest multiple
* of 4 bytes, then <code>pad</code> should be set to 4.
*
* @param height height (in pixels) of the source image
*/
public void setSourceImageYUV(byte[] srcImage, int width, int pad,
int height) throws Exception {
if (handle == 0) init();
if (srcImage == null || width < 1 || pad < 1 || height < 1)
throw new Exception("Invalid argument in setSourceImageYUV()");
srcBuf = srcImage;
srcWidth = width;
srcYUVPad = pad;
srcHeight = height;
srcIsYUV = true;
}
/**
* Set the level of chrominance subsampling for subsequent compress/encode
@@ -162,6 +198,10 @@ public class TJCompressor {
* image with little perceptible loss of image clarity (the human eye is more
* sensitive to small changes in brightness than to small changes in color.)
* This is called "chrominance subsampling".
* <p>
* NOTE: When compressing a YUV planar image into a JPEG image, this method
* also specifies the level of chrominance subsampling used in the source
* image.
*
* @param newSubsamp the new level of chrominance subsampling (one of
* {@link TJ TJ.SAMP_*})
@@ -203,14 +243,19 @@ public class TJCompressor {
throw new Exception("JPEG Quality not set");
if (subsamp < 0)
throw new Exception("Subsampling level not set");
if (srcX >= 0 && srcY >= 0)
compressedSize = compress(srcBuf, srcX, srcY, srcWidth, srcPitch,
srcHeight, srcPixelFormat, dstBuf, subsamp,
jpegQuality, flags);
else
compressedSize = compress(srcBuf, srcWidth, srcPitch, srcHeight,
srcPixelFormat, dstBuf, subsamp, jpegQuality,
flags);
if (srcIsYUV)
compressedSize = compressFromYUV(srcBuf, srcWidth, srcYUVPad, srcHeight,
subsamp, dstBuf, jpegQuality, flags);
else {
if (srcX >= 0 && srcY >= 0)
compressedSize = compress(srcBuf, srcX, srcY, srcWidth, srcPitch,
srcHeight, srcPixelFormat, dstBuf, subsamp,
jpegQuality, flags);
else
compressedSize = compress(srcBuf, srcWidth, srcPitch, srcHeight,
srcPixelFormat, dstBuf, subsamp, jpegQuality,
flags);
}
}
/**
@@ -550,6 +595,10 @@ public class TJCompressor {
int stride, int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp,
int jpegQual, int flags) throws Exception;
private native int compressFromYUV(byte[] srcBuf, int width, int pad,
int height, int subsamp, byte[] dstBuf, int jpegQual, int flags)
throws Exception;
private native void encodeYUV(byte[] srcBuf, int width, int pitch,
int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags)
throws Exception; // deprecated
@@ -578,6 +627,8 @@ public class TJCompressor {
private int srcY = -1;
private int srcPitch = 0;
private int srcPixelFormat = -1;
private int srcYUVPad = -1;
private boolean srcIsYUV;
private int subsamp = -1;
private int jpegQuality = -1;
private int compressedSize = 0;

View File

@@ -55,6 +55,14 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIIIII_3BIII
(JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jbyteArray, jint, jint, jint);
/*
* Class: org_libjpegturbo_turbojpeg_TJCompressor
* Method: compressFromYUV
* Signature: ([BIIII[BII)I
*/
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3BIIII_3BII
(JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jbyteArray, jint, jint);
/*
* Class: org_libjpegturbo_turbojpeg_TJCompressor
* Method: encodeYUV

View File

@@ -214,6 +214,42 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3
flags);
}
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3BIIII_3BII
(JNIEnv *env, jobject obj, jbyteArray src, jint width, jint pad, jint height,
jint subsamp, jbyteArray dst, jint jpegQual, jint flags)
{
tjhandle handle=0;
unsigned long jpegSize=0;
jsize arraySize=0;
unsigned char *srcBuf=NULL, *jpegBuf=NULL;
gethandle();
arraySize=tjBufSizeYUV2(width, pad, height, subsamp);
if((*env)->GetArrayLength(env, src)<arraySize)
_throw("Source buffer is not large enough");
jpegSize=tjBufSize(width, height, subsamp);
if((*env)->GetArrayLength(env, dst)<(jsize)jpegSize)
_throw("Destination buffer is not large enough");
bailif0(srcBuf=(*env)->GetPrimitiveArrayCritical(env, src, 0));
bailif0(jpegBuf=(*env)->GetPrimitiveArrayCritical(env, dst, 0));
if(tjCompressFromYUV(handle, srcBuf, width, pad, height, subsamp, &jpegBuf,
&jpegSize, jpegQual, flags|TJFLAG_NOREALLOC)==-1)
{
(*env)->ReleasePrimitiveArrayCritical(env, dst, jpegBuf, 0);
(*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0);
jpegBuf=srcBuf=NULL;
_throw(tjGetErrorStr());
}
bailout:
if(jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, jpegBuf, 0);
if(srcBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0);
return (jint)jpegSize;
}
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BIII
(JNIEnv *env, jobject obj, jbyteArray src, jint width, jint pitch,
jint height, jint pf, jbyteArray dst, jint pad, jint subsamp, jint flags)

View File

@@ -72,6 +72,7 @@ TURBOJPEG_1.4
tjDecompressToYUV2;
tjEncodeYUV3;
Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII;
Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3BIIII_3BII;
Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BIII;
Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIII_3BIII;
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3BIIII;