TurboJPEG: Implement 4:4:1 chrominance subsampling

This allows losslessly transposed or rotated 4:1:1 JPEG images to be
losslessly cropped, partially decompressed, or decompressed to planar
YUV images.

Because tj3Transform() allows multiple lossless transformations to be
chained together, all subsampling options need to have a corresponding
transposed subsampling option.  (This is why 4:4:0 was originally
implemented as well.)  Otherwise, the documentation would be technically
incorrect.  It says that images with unknown subsampling types cannot be
losslessly cropped, partially decompressed, or decompressed to planar
YUV images, but it doesn't say anything about images with known
subsampling types whose subsampling type becomes unknown if the image is
rotated or transposed.  This is one of those situations in which it is
easier to implement a feature that works around the problem than to
document the problem.

Closes #659
This commit is contained in:
DRC
2023-03-09 20:55:43 -06:00
parent 58a3427ffc
commit fc881ebb21
42 changed files with 335 additions and 229 deletions

View File

@@ -52,11 +52,11 @@ final class TJBench {
};
static final String[] SUBNAME_LONG = {
"4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1"
"4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1"
};
static final String[] SUBNAME = {
"444", "422", "420", "GRAY", "440", "411"
"444", "422", "420", "GRAY", "440", "411", "441"
};
static final String[] CSNAME = {
@@ -826,7 +826,7 @@ final class TJBench {
}
System.out.println(")");
System.out.println("-subsamp S = When compressing, use the specified level of chrominance");
System.out.println(" subsampling (S = 444, 422, 440, 420, 411, or GRAY) [default = test");
System.out.println(" subsampling (S = 444, 422, 440, 420, 411, 441, or GRAY) [default = test");
System.out.println(" Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]");
System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =");
System.out.println(" Perform the specified lossless transform operation on the input image");
@@ -1055,6 +1055,8 @@ final class TJBench {
subsamp = TJ.SAMP_420;
else if (argv[i].equals("411"))
subsamp = TJ.SAMP_411;
else if (argv[i].equals("441"))
subsamp = TJ.SAMP_441;
else
usage();
} else if (argv[i].equalsIgnoreCase("-componly"))

View File

@@ -52,7 +52,7 @@ class TJExample implements TJCustomFilter {
static final String[] SUBSAMP_NAME = {
"4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1"
"4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1", "4:4:1"
};
static final String[] COLORSPACE_NAME = {

View File

@@ -60,10 +60,10 @@ final class TJUnitTest {
}
static final String[] SUBNAME_LONG = {
"4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1"
"4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1"
};
static final String[] SUBNAME = {
"444", "422", "420", "GRAY", "440", "411"
"444", "422", "420", "GRAY", "440", "411", "441"
};
static final String[] PIXFORMATSTR = {
@@ -835,9 +835,9 @@ final class TJUnitTest {
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 &&
((subsamp == TJ.SAMP_411 || subsamp == TJ.SAMP_441) && num == 1 &&
(denom == 2 || denom == 1)) ||
(subsamp != TJ.SAMP_411 && num == 1 &&
(subsamp != TJ.SAMP_411 && subsamp != TJ.SAMP_441 && num == 1 &&
(denom == 4 || denom == 2 || denom == 1)))
decompTest(tjd, jpegBuf, jpegSize, w, h, pf, baseName, subsamp, sf[i]);
}
@@ -865,7 +865,8 @@ final class TJUnitTest {
} else {
tjc.set(TJ.PARAM_QUALITY, 100);
if (subsamp == TJ.SAMP_422 || subsamp == TJ.SAMP_420 ||
subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411)
subsamp == TJ.SAMP_440 || subsamp == TJ.SAMP_411 ||
subsamp == TJ.SAMP_441)
tjd.set(TJ.PARAM_FASTUPSAMPLE, 1);
}
tjc.set(TJ.PARAM_SUBSAMP, subsamp);
@@ -1076,6 +1077,10 @@ final class TJUnitTest {
testName);
doTest(35, 39, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_411,
testName);
doTest(39, 41, bi ? FORMATS_3BYTEBI : FORMATS_3SAMPLE, TJ.SAMP_441,
testName);
doTest(41, 35, bi ? FORMATS_4BYTEBI : FORMATS_4SAMPLE, TJ.SAMP_441,
testName);
}
doTest(39, 41, bi ? FORMATS_GRAYBI : FORMATS_GRAY, TJ.SAMP_GRAY,
testName);
@@ -1095,6 +1100,7 @@ final class TJUnitTest {
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_441, "javatest_yuv0");
doTest(48, 48, FORMATS_RGB, TJ.SAMP_GRAY, "javatest_yuv0");
doTest(48, 48, FORMATS_GRAY, TJ.SAMP_GRAY, "javatest_yuv0");
}

View File

@@ -18,8 +18,9 @@
<script type="text/javascript" src="jquery/jquery-ui.min.js"></script>
</head>
<body>
<main role="main">
<h1 class="bar">All&nbsp;Classes</h1>
<main role="main" class="indexContainer">
<div class="indexContainer">
<ul>
<li><a href="org/libjpegturbo/turbojpeg/TJ.html" title="class in org.libjpegturbo.turbojpeg">TJ</a></li>
<li><a href="org/libjpegturbo/turbojpeg/TJCompressor.html" title="class in org.libjpegturbo.turbojpeg">TJCompressor</a></li>
@@ -31,6 +32,7 @@
<li><a href="org/libjpegturbo/turbojpeg/TJTransformer.html" title="class in org.libjpegturbo.turbojpeg">TJTransformer</a></li>
<li><a href="org/libjpegturbo/turbojpeg/YUVImage.html" title="class in org.libjpegturbo.turbojpeg">YUVImage</a></li>
</ul>
</div>
</main>
</body>
</html>

View File

@@ -239,7 +239,7 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
<!-- -->
</a><code>public&nbsp;static&nbsp;final&nbsp;int</code></td>
<th class="colSecond" scope="row"><code><a href="org/libjpegturbo/turbojpeg/TJ.html#NUMSAMP">NUMSAMP</a></code></th>
<td class="colLast"><code>6</code></td>
<td class="colLast"><code>7</code></td>
</tr>
<tr class="altColor">
<td class="colFirst"><a id="org.libjpegturbo.turbojpeg.TJ.PARAM_ARITHMETIC">
@@ -508,20 +508,27 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
<td class="colLast"><code>4</code></td>
</tr>
<tr class="altColor">
<td class="colFirst"><a id="org.libjpegturbo.turbojpeg.TJ.SAMP_441">
<!-- -->
</a><code>public&nbsp;static&nbsp;final&nbsp;int</code></td>
<th class="colSecond" scope="row"><code><a href="org/libjpegturbo/turbojpeg/TJ.html#SAMP_441">SAMP_441</a></code></th>
<td class="colLast"><code>6</code></td>
</tr>
<tr class="rowColor">
<td class="colFirst"><a id="org.libjpegturbo.turbojpeg.TJ.SAMP_444">
<!-- -->
</a><code>public&nbsp;static&nbsp;final&nbsp;int</code></td>
<th class="colSecond" scope="row"><code><a href="org/libjpegturbo/turbojpeg/TJ.html#SAMP_444">SAMP_444</a></code></th>
<td class="colLast"><code>0</code></td>
</tr>
<tr class="rowColor">
<tr class="altColor">
<td class="colFirst"><a id="org.libjpegturbo.turbojpeg.TJ.SAMP_GRAY">
<!-- -->
</a><code>public&nbsp;static&nbsp;final&nbsp;int</code></td>
<th class="colSecond" scope="row"><code><a href="org/libjpegturbo/turbojpeg/TJ.html#SAMP_GRAY">SAMP_GRAY</a></code></th>
<td class="colLast"><code>3</code></td>
</tr>
<tr class="altColor">
<tr class="rowColor">
<td class="colFirst"><a id="org.libjpegturbo.turbojpeg.TJ.SAMP_UNKNOWN">
<!-- -->
</a><code>public&nbsp;static&nbsp;final&nbsp;int</code></td>

View File

@@ -899,6 +899,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
<dd>
<div class="block">4:4:0 chrominance subsampling.</div>
</dd>
<dt><span class="memberNameLink"><a href="org/libjpegturbo/turbojpeg/TJ.html#SAMP_441">SAMP_441</a></span> - Static variable in class org.libjpegturbo.turbojpeg.<a href="org/libjpegturbo/turbojpeg/TJ.html" title="class in org.libjpegturbo.turbojpeg">TJ</a></dt>
<dd>
<div class="block">4:4:1 chrominance subsampling.</div>
</dd>
<dt><span class="memberNameLink"><a href="org/libjpegturbo/turbojpeg/TJ.html#SAMP_444">SAMP_444</a></span> - Static variable in class org.libjpegturbo.turbojpeg.<a href="org/libjpegturbo/turbojpeg/TJ.html" title="class in org.libjpegturbo.turbojpeg">TJ</a></dt>
<dd>
<div class="block">4:4:4 chrominance subsampling (no chrominance subsampling).</div>

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -565,33 +565,40 @@ extends java.lang.Object</pre>
</tr>
<tr class="altColor">
<td class="colFirst"><code>static int</code></td>
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#SAMP_441">SAMP_441</a></span></code></th>
<td class="colLast">
<div class="block">4:4:1 chrominance subsampling.</div>
</td>
</tr>
<tr class="rowColor">
<td class="colFirst"><code>static int</code></td>
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#SAMP_444">SAMP_444</a></span></code></th>
<td class="colLast">
<div class="block">4:4:4 chrominance subsampling (no chrominance subsampling).</div>
</td>
</tr>
<tr class="rowColor">
<tr class="altColor">
<td class="colFirst"><code>static int</code></td>
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#SAMP_GRAY">SAMP_GRAY</a></span></code></th>
<td class="colLast">
<div class="block">Grayscale.</div>
</td>
</tr>
<tr class="altColor">
<tr class="rowColor">
<td class="colFirst"><code>static int</code></td>
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#SAMP_UNKNOWN">SAMP_UNKNOWN</a></span></code></th>
<td class="colLast">
<div class="block">Unknown subsampling.</div>
</td>
</tr>
<tr class="rowColor">
<tr class="altColor">
<td class="colFirst"><code>static java.awt.Rectangle</code></td>
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#UNCROPPED">UNCROPPED</a></span></code></th>
<td class="colLast">
<div class="block">A <code>java.awt.Rectangle</code> instance that specifies no cropping</div>
</td>
</tr>
<tr class="altColor">
<tr class="rowColor">
<td class="colFirst"><code>static <a href="TJScalingFactor.html" title="class in org.libjpegturbo.turbojpeg">TJScalingFactor</a></code></td>
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#UNSCALED">UNSCALED</a></span></code></th>
<td class="colLast">
@@ -867,6 +874,27 @@ extends java.lang.Object</pre>
</dl>
</li>
</ul>
<a id="SAMP_441">
<!-- -->
</a>
<ul class="blockList">
<li class="blockList">
<h4>SAMP_441</h4>
<pre>public static final&nbsp;int SAMP_441</pre>
<div class="block">4:4:1 chrominance subsampling. The JPEG or YUV image will contain one
chrominance component for every 1x4 block of pixels in the source image.
JPEG images compressed with 4:4:1 subsampling will be almost exactly the
same size as those compressed with 4:2:0 subsampling, and in the
aggregate, both subsampling methods produce approximately the same
perceptual quality. However, 4:4:1 is better able to reproduce sharp
vertical features. Note that 4:4:1 subsampling is not fully accelerated
in libjpeg-turbo.</div>
<dl>
<dt><span class="seeLabel">See Also:</span></dt>
<dd><a href="../../../constant-values.html#org.libjpegturbo.turbojpeg.TJ.SAMP_441">Constant Field Values</a></dd>
</dl>
</li>
</ul>
<a id="SAMP_UNKNOWN">
<!-- -->
</a>

View File

@@ -150,16 +150,16 @@ extends java.lang.Object</pre>
image. The width and height of each plane are determined by the image
width, height, and level of chrominance subsampling. The luminance plane
width is the image width padded to the nearest multiple of the horizontal
subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the
case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance
plane height is the image height padded to the nearest multiple of the
vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or
4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any
additional padding that may be specified as an argument to the various
YUVImage methods. The chrominance plane width is equal to the luminance
plane width divided by the horizontal subsampling factor, and the
chrominance plane height is equal to the luminance plane height divided by
the vertical subsampling factor.
subsampling factor (1 in the case of 4:4:4, grayscale, 4:4:0, or 4:4:1; 2 in
the case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the
luminance plane height is the image height padded to the nearest multiple of
the vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale,
or 4:1:1; 2 in the case of 4:2:0 or 4:4:0; 4 in the case of 4:4:1.) This is
irrespective of any additional padding that may be specified as an argument
to the various YUVImage methods. The chrominance plane width is equal to
the luminance plane width divided by the horizontal subsampling factor, and
the chrominance plane height is equal to the luminance plane height divided
by the vertical subsampling factor.
<p>
For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is
used, then the luminance plane would be 36 x 35 bytes, and each of the

Binary file not shown.

Binary file not shown.

View File

@@ -42,7 +42,7 @@ public final class TJ {
/**
* The number of chrominance subsampling options
*/
public static final int NUMSAMP = 6;
public static final int NUMSAMP = 7;
/**
* 4:4:4 chrominance subsampling (no chrominance subsampling). The JPEG
* or YUV image will contain one chrominance component for every pixel in the
@@ -80,6 +80,17 @@ public final class TJ {
* in libjpeg-turbo.
*/
public static final int SAMP_411 = 5;
/**
* 4:4:1 chrominance subsampling. The JPEG or YUV image will contain one
* chrominance component for every 1x4 block of pixels in the source image.
* JPEG images compressed with 4:4:1 subsampling will be almost exactly the
* same size as those compressed with 4:2:0 subsampling, and in the
* aggregate, both subsampling methods produce approximately the same
* perceptual quality. However, 4:4:1 is better able to reproduce sharp
* vertical features. Note that 4:4:1 subsampling is not fully accelerated
* in libjpeg-turbo.
*/
public static final int SAMP_441 = 6;
/**
* Unknown subsampling. The JPEG image uses an unusual type of chrominance
* subsampling. Such images can be decompressed into packed-pixel images,
@@ -109,7 +120,7 @@ public final class TJ {
}
private static final int[] MCU_WIDTH = {
8, 16, 16, 8, 8, 32
8, 16, 16, 8, 8, 32, 8
};
@@ -129,7 +140,7 @@ public final class TJ {
}
private static final int[] MCU_HEIGHT = {
8, 8, 16, 8, 16, 8
8, 8, 16, 8, 16, 8, 32
};

View File

@@ -48,16 +48,16 @@ package org.libjpegturbo.turbojpeg;
* image. The width and height of each plane are determined by the image
* width, height, and level of chrominance subsampling. The luminance plane
* width is the image width padded to the nearest multiple of the horizontal
* subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the
* case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance
* plane height is the image height padded to the nearest multiple of the
* vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or
* 4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any
* additional padding that may be specified as an argument to the various
* YUVImage methods. The chrominance plane width is equal to the luminance
* plane width divided by the horizontal subsampling factor, and the
* chrominance plane height is equal to the luminance plane height divided by
* the vertical subsampling factor.
* subsampling factor (1 in the case of 4:4:4, grayscale, 4:4:0, or 4:4:1; 2 in
* the case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the
* luminance plane height is the image height padded to the nearest multiple of
* the vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale,
* or 4:1:1; 2 in the case of 4:2:0 or 4:4:0; 4 in the case of 4:4:1.) This is
* irrespective of any additional padding that may be specified as an argument
* to the various YUVImage methods. The chrominance plane width is equal to
* the luminance plane width divided by the horizontal subsampling factor, and
* the chrominance plane height is equal to the luminance plane height divided
* by the vertical subsampling factor.
* <p>
* For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is
* used, then the luminance plane would be 36 x 35 bytes, and each of the

View File

@@ -8,7 +8,7 @@
extern "C" {
#endif
#undef org_libjpegturbo_turbojpeg_TJ_NUMSAMP
#define org_libjpegturbo_turbojpeg_TJ_NUMSAMP 6L
#define org_libjpegturbo_turbojpeg_TJ_NUMSAMP 7L
#undef org_libjpegturbo_turbojpeg_TJ_SAMP_444
#define org_libjpegturbo_turbojpeg_TJ_SAMP_444 0L
#undef org_libjpegturbo_turbojpeg_TJ_SAMP_422
@@ -21,6 +21,8 @@ extern "C" {
#define org_libjpegturbo_turbojpeg_TJ_SAMP_440 4L
#undef org_libjpegturbo_turbojpeg_TJ_SAMP_411
#define org_libjpegturbo_turbojpeg_TJ_SAMP_411 5L
#undef org_libjpegturbo_turbojpeg_TJ_SAMP_441
#define org_libjpegturbo_turbojpeg_TJ_SAMP_441 6L
#undef org_libjpegturbo_turbojpeg_TJ_SAMP_UNKNOWN
#define org_libjpegturbo_turbojpeg_TJ_SAMP_UNKNOWN -1L
#undef org_libjpegturbo_turbojpeg_TJ_NUMPF