diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html
index 75321f91..42c30095 100644
--- a/doc/html/group___turbo_j_p_e_g.html
+++ b/doc/html/group___turbo_j_p_e_g.html
@@ -3173,7 +3173,7 @@ If you choose option 1, then *jpegSize should be set to the size of
dstBufs[i] will receive a JPEG image that has been transformed using the parameters in transforms[i]. TurboJPEG has the ability to reallocate the JPEG destination buffer to accommodate the size of the transformed JPEG image. Thus, you can choose to:dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer for you, ordstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJPARAM_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed.dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the transformed JPEG image (in bytes.)TJ.bufSize() to determine the maximum size for each
buffer based on the transformed or cropped width and height and the level
- of subsampling used in the destination image.
+ of subsampling used in the destination image (taking into account
+ grayscale conversion and transposition of the width and height.)
transforms - an array of TJTransform instances, each of
which specifies the transform parameters and/or cropping region for the
corresponding transformed JPEG imagetransforms[i]. Use
* {@link TJ#bufSize TJ.bufSize()} to determine the maximum size for each
* buffer based on the transformed or cropped width and height and the level
- * of subsampling used in the destination image.
+ * of subsampling used in the destination image (taking into account
+ * grayscale conversion and transposition of the width and height.)
*
* @param transforms an array of {@link TJTransform} instances, each of
* which specifies the transform parameters and/or cropping region for the
@@ -130,14 +131,33 @@ public class TJTransformer extends TJDecompressor {
byte[][] dstBufs = new byte[transforms.length][];
if (getWidth() < 1 || getHeight() < 1)
throw new IllegalStateException("JPEG buffer not initialized");
- checkSubsampling();
+ int srcSubsamp = get(TJ.PARAM_SUBSAMP);
for (int i = 0; i < transforms.length; i++) {
int w = getWidth(), h = getHeight();
+ int dstSubsamp = srcSubsamp;
+
+ if ((transforms[i].options & TJTransform.OPT_GRAY) != 0)
+ dstSubsamp = TJ.SAMP_GRAY;
+ if (transforms[i].op == TJTransform.OP_TRANSPOSE ||
+ transforms[i].op == TJTransform.OP_TRANSVERSE ||
+ transforms[i].op == TJTransform.OP_ROT90 ||
+ transforms[i].op == TJTransform.OP_ROT270) {
+ w = getHeight(); h = getWidth();
+ if (dstSubsamp == TJ.SAMP_422)
+ dstSubsamp = TJ.SAMP_440;
+ else if (dstSubsamp == TJ.SAMP_440)
+ dstSubsamp = TJ.SAMP_422;
+ else if (dstSubsamp == TJ.SAMP_411)
+ dstSubsamp = TJ.SAMP_441;
+ else if (dstSubsamp == TJ.SAMP_441)
+ dstSubsamp = TJ.SAMP_411;
+ }
+
if ((transforms[i].options & TJTransform.OPT_CROP) != 0) {
if (transforms[i].width != 0) w = transforms[i].width;
if (transforms[i].height != 0) h = transforms[i].height;
}
- dstBufs[i] = new byte[TJ.bufSize(w, h, get(TJ.PARAM_SUBSAMP))];
+ dstBufs[i] = new byte[TJ.bufSize(w, h, dstSubsamp)];
}
TJDecompressor[] tjd = new TJDecompressor[transforms.length];
transform(dstBufs, transforms);
diff --git a/tjbench.c b/tjbench.c
index ccf4a896..b91c36eb 100644
--- a/tjbench.c
+++ b/tjbench.c
@@ -738,6 +738,15 @@ static int decompTest(char *fileName)
THROW_UNIX("allocating JPEG size array");
memset(jpegSizes, 0, sizeof(size_t) * ntilesw * ntilesh);
+ tsubsamp = (xformOpt & TJXOPT_GRAY) ? TJSAMP_GRAY : subsamp;
+ if (xformOp == TJXOP_TRANSPOSE || xformOp == TJXOP_TRANSVERSE ||
+ xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) {
+ if (tsubsamp == TJSAMP_422) tsubsamp = TJSAMP_440;
+ else if (tsubsamp == TJSAMP_440) tsubsamp = TJSAMP_422;
+ else if (tsubsamp == TJSAMP_411) tsubsamp = TJSAMP_441;
+ else if (tsubsamp == TJSAMP_441) tsubsamp = TJSAMP_411;
+ }
+
if (noRealloc &&
(doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter)) {
for (i = 0; i < ntilesw * ntilesh; i++) {
@@ -745,9 +754,9 @@ static int decompTest(char *fileName)
if (xformOp == TJXOP_TRANSPOSE || xformOp == TJXOP_TRANSVERSE ||
xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270)
- jpegBufSize = tj3JPEGBufSize(tileh, tilew, subsamp);
+ jpegBufSize = tj3JPEGBufSize(tileh, tilew, tsubsamp);
else
- jpegBufSize = tj3JPEGBufSize(tilew, tileh, subsamp);
+ jpegBufSize = tj3JPEGBufSize(tilew, tileh, tsubsamp);
if (jpegBufSize == 0)
THROW_TJG();
if ((jpegBufs[i] = tj3Alloc(jpegBufSize)) == NULL)
@@ -767,7 +776,6 @@ static int decompTest(char *fileName)
printf("%-5d %-5d ", CROPPED_WIDTH(tilew), CROPPED_HEIGHT(tileh));
}
- tsubsamp = subsamp;
if (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter) {
if ((t = (tjtransform *)malloc(sizeof(tjtransform) * ntilesw *
ntilesh)) == NULL)
@@ -782,26 +790,15 @@ static int decompTest(char *fileName)
subsamp == TJSAMP_UNKNOWN)
THROW("transforming",
"Could not determine subsampling level of JPEG image");
- if (xformOpt & TJXOPT_GRAY) tsubsamp = TJSAMP_GRAY;
- if (xformOp == TJXOP_HFLIP || xformOp == TJXOP_ROT180)
+ if (xformOp == TJXOP_HFLIP || xformOp == TJXOP_TRANSVERSE ||
+ xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT180)
tw = tw - (tw % tjMCUWidth[tsubsamp]);
- if (xformOp == TJXOP_VFLIP || xformOp == TJXOP_ROT180)
+ if (xformOp == TJXOP_VFLIP || xformOp == TJXOP_TRANSVERSE ||
+ xformOp == TJXOP_ROT180 || xformOp == TJXOP_ROT270)
th = th - (th % tjMCUHeight[tsubsamp]);
- if (xformOp == TJXOP_TRANSVERSE || xformOp == TJXOP_ROT90)
- tw = tw - (tw % tjMCUHeight[tsubsamp]);
- if (xformOp == TJXOP_TRANSVERSE || xformOp == TJXOP_ROT270)
- th = th - (th % tjMCUWidth[tsubsamp]);
tntilesw = (tw + ttilew - 1) / ttilew;
tntilesh = (th + ttileh - 1) / ttileh;
- if (xformOp == TJXOP_TRANSPOSE || xformOp == TJXOP_TRANSVERSE ||
- xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) {
- if (tsubsamp == TJSAMP_422) tsubsamp = TJSAMP_440;
- else if (tsubsamp == TJSAMP_440) tsubsamp = TJSAMP_422;
- else if (tsubsamp == TJSAMP_411) tsubsamp = TJSAMP_441;
- else if (tsubsamp == TJSAMP_441) tsubsamp = TJSAMP_411;
- }
-
for (row = 0, tile = 0; row < tntilesh; row++) {
for (col = 0; col < tntilesw; col++, tile++) {
t[tile].r.w = min(ttilew, tw - col * ttilew);
diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c
index 5c6f6176..68e5739a 100644
--- a/turbojpeg-jni.c
+++ b/turbojpeg-jni.c
@@ -1218,6 +1218,10 @@ JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transf
if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
w = jpegHeight; h = jpegWidth;
+ if (dstSubsamp == TJSAMP_422) dstSubsamp = TJSAMP_440;
+ else if (dstSubsamp == TJSAMP_440) dstSubsamp = TJSAMP_422;
+ else if (dstSubsamp == TJSAMP_411) dstSubsamp = TJSAMP_441;
+ else if (dstSubsamp == TJSAMP_441) dstSubsamp = TJSAMP_411;
}
if (t[i].r.w != 0) w = t[i].r.w;
if (t[i].r.h != 0) h = t[i].r.h;
diff --git a/turbojpeg.c b/turbojpeg.c
index b047db10..9e622ae1 100644
--- a/turbojpeg.c
+++ b/turbojpeg.c
@@ -2740,26 +2740,25 @@ DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf,
for (i = 0; i < n; i++) {
int dstSubsamp = (t[i].options & TJXOPT_GRAY) ? TJSAMP_GRAY : srcSubsamp;
+ if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
+ t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
+ if (dstSubsamp == TJSAMP_422) dstSubsamp = TJSAMP_440;
+ else if (dstSubsamp == TJSAMP_440) dstSubsamp = TJSAMP_422;
+ else if (dstSubsamp == TJSAMP_411) dstSubsamp = TJSAMP_441;
+ else if (dstSubsamp == TJSAMP_441) dstSubsamp = TJSAMP_411;
+ }
+
if (!jtransform_request_workspace(dinfo, &xinfo[i]))
THROW("Transform is not perfect");
if (xinfo[i].crop) {
if (dstSubsamp == TJSAMP_UNKNOWN)
THROW("Could not determine subsampling level of JPEG image");
- if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
- t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
- if ((t[i].r.x % tjMCUHeight[dstSubsamp]) != 0 ||
- (t[i].r.y % tjMCUWidth[dstSubsamp]) != 0)
- THROWI("To crop this JPEG image, x must be a multiple of %d\n"
- "and y must be a multiple of %d.", tjMCUHeight[dstSubsamp],
- tjMCUWidth[dstSubsamp]);
- } else {
- if ((t[i].r.x % tjMCUWidth[dstSubsamp]) != 0 ||
- (t[i].r.y % tjMCUHeight[dstSubsamp]) != 0)
- THROWI("To crop this JPEG image, x must be a multiple of %d\n"
- "and y must be a multiple of %d.", tjMCUWidth[dstSubsamp],
- tjMCUHeight[dstSubsamp]);
- }
+ if ((t[i].r.x % tjMCUWidth[dstSubsamp]) != 0 ||
+ (t[i].r.y % tjMCUHeight[dstSubsamp]) != 0)
+ THROWI("To crop this JPEG image, x must be a multiple of %d\n"
+ "and y must be a multiple of %d.", tjMCUWidth[dstSubsamp],
+ tjMCUHeight[dstSubsamp]);
}
}
@@ -2772,6 +2771,10 @@ DLLEXPORT int tj3Transform(tjhandle handle, const unsigned char *jpegBuf,
if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
dstWidth = dinfo->image_height; dstHeight = dinfo->image_width;
+ if (dstSubsamp == TJSAMP_422) dstSubsamp = TJSAMP_440;
+ else if (dstSubsamp == TJSAMP_440) dstSubsamp = TJSAMP_422;
+ else if (dstSubsamp == TJSAMP_411) dstSubsamp = TJSAMP_441;
+ else if (dstSubsamp == TJSAMP_441) dstSubsamp = TJSAMP_411;
}
if (xinfo[i].crop) {
diff --git a/turbojpeg.h b/turbojpeg.h
index 934e4b05..0d505df9 100644
--- a/turbojpeg.h
+++ b/turbojpeg.h
@@ -2065,13 +2065,15 @@ DLLEXPORT int tj3DecodeYUV8(tjhandle handle, const unsigned char *srcBuf,
* you, or
* -# pre-allocate the buffer to a "worst case" size determined by calling
* #tj3JPEGBufSize() with the transformed or cropped width and height and the
- * level of subsampling used in the destination image. Under normal
- * circumstances, this should ensure that the buffer never has to be
+ * level of subsampling used in the destination image (taking into account
+ * grayscale conversion and transposition of the width and height.) Under
+ * normal circumstances, this should ensure that the buffer never has to be
* re-allocated. (Setting #TJPARAM_NOREALLOC guarantees that it won't be.)
* Note, however, that there are some rare cases (such as transforming images
* with a large amount of embedded Exif or ICC profile data) in which the
* transformed JPEG image will be larger than the worst-case size, and
- * #TJPARAM_NOREALLOC cannot be used in those cases.
+ * #TJPARAM_NOREALLOC cannot be used in those cases unless the embedded data is
+ * discarded using #TJXOPT_COPYNONE.
* .
* If you choose option 1, then `dstSizes[i]` should be set to the size of your
* pre-allocated buffer. In any case, unless you have set #TJPARAM_NOREALLOC,