mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-05 04:25:29 -04:00
add support for JPEG Lossless
JPEG Lossless files which are supported https://github.com/rii-mango/JPEGLosslessDecoder can be read. Careful: currently only supports 16, 8-bit grayscale and 24 bit rgb conversion for BufferedImages
This commit is contained in:
parent
13bea23550
commit
ad269053ed
@ -39,6 +39,8 @@ import com.twelvemonkeys.imageio.metadata.exif.TIFF;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
|
||||
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.lossless.DataStream;
|
||||
import com.twelvemonkeys.imageio.plugins.jpeg.lossless.JPEGLosslessDecoderWrapper;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
@ -338,9 +340,38 @@ public class JPEGImageReader extends ImageReaderBase {
|
||||
|
||||
JPEGColorSpace sourceCSType = getSourceCSType(getJFIF(), adobeDCT, sof);
|
||||
|
||||
//try to read an JPEG Lossless image
|
||||
if(sof.marker == JPEG.SOF3){
|
||||
JPEGLosslessDecoderWrapper decoder = new JPEGLosslessDecoderWrapper();
|
||||
ImageInputStream inputStream = (ImageInputStream) this.getInput();
|
||||
|
||||
/* try to read the input stream as once. If length is not supported
|
||||
* the image is read blockwise
|
||||
*/
|
||||
byte[] rawByteData;
|
||||
long byteLength = inputStream.length();
|
||||
if(byteLength > 0 && byteLength < Integer.MAX_VALUE){
|
||||
rawByteData = new byte[(int)byteLength];
|
||||
inputStream.read(rawByteData);
|
||||
} else {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024 * 1024 /* Provide a size close to what is likely the case for better performance*/);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
while ((count = inputStream.read(buffer)) > 0) {
|
||||
bytes.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
rawByteData = bytes.toByteArray();
|
||||
}
|
||||
|
||||
//try do read the image, IOException can be thrown
|
||||
return decoder.readImage(rawByteData);
|
||||
}
|
||||
|
||||
// We need to apply ICC profile unless the profile is sRGB/default gray (whatever that is)
|
||||
// - or only filter out the bad ICC profiles in the JPEGSegmentImageInputStream.
|
||||
if (delegate.canReadRaster() && (
|
||||
else if (delegate.canReadRaster() && (
|
||||
bogusAdobeDCT ||
|
||||
sourceCSType == JPEGColorSpace.CMYK ||
|
||||
sourceCSType == JPEGColorSpace.YCCK ||
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Martinez
|
||||
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
||||
* warnings, split into multiple class files, and general clean up.
|
||||
*
|
||||
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Helmut Dersch
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
public class ComponentSpec {
|
||||
|
||||
protected int hSamp; // Horizontal sampling factor
|
||||
protected int quantTableSel; // Quantization table destination selector
|
||||
protected int vSamp; // Vertical
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Martinez
|
||||
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
||||
* warnings, split into multiple class files, and general clean up.
|
||||
*
|
||||
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Helmut Dersch
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
public interface DataStream {
|
||||
|
||||
int get16();
|
||||
|
||||
|
||||
|
||||
int get8();
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Martinez
|
||||
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
||||
* warnings, split into multiple class files, and general clean up.
|
||||
*
|
||||
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Helmut Dersch
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class FrameHeader {
|
||||
|
||||
private ComponentSpec components[]; // Components
|
||||
private int dimX; // Number of samples per line
|
||||
private int dimY; // Number of lines
|
||||
private int numComp; // Number of component in the frame
|
||||
private int precision; // Sample Precision (from the original image)
|
||||
|
||||
|
||||
|
||||
public ComponentSpec[] getComponents() {
|
||||
return components.clone();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getDimX() {
|
||||
return dimX;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getDimY() {
|
||||
return dimY;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getNumComponents() {
|
||||
return numComp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getPrecision() {
|
||||
return precision;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected int read(final DataStream data) throws IOException {
|
||||
int count = 0;
|
||||
|
||||
final int length = data.get16();
|
||||
count += 2;
|
||||
|
||||
precision = data.get8();
|
||||
count++;
|
||||
|
||||
dimY = data.get16();
|
||||
count += 2;
|
||||
|
||||
dimX = data.get16();
|
||||
count += 2;
|
||||
|
||||
numComp = data.get8();
|
||||
count++;
|
||||
|
||||
//components = new ComponentSpec[numComp]; // some image exceed this range...
|
||||
components = new ComponentSpec[256]; // setting to 256 -- not sure what it should be.
|
||||
|
||||
for (int i = 1; i <= numComp; i++) {
|
||||
if (count > length) {
|
||||
throw new IOException("ERROR: frame format error");
|
||||
}
|
||||
|
||||
final int c = data.get8();
|
||||
count++;
|
||||
|
||||
if (count >= length) {
|
||||
throw new IOException("ERROR: frame format error [c>=Lf]");
|
||||
}
|
||||
|
||||
final int temp = data.get8();
|
||||
count++;
|
||||
|
||||
if (components[c] == null) {
|
||||
components[c] = new ComponentSpec();
|
||||
}
|
||||
|
||||
components[c].hSamp = temp >> 4;
|
||||
components[c].vSamp = temp & 0x0F;
|
||||
components[c].quantTableSel = data.get8();
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count != length) {
|
||||
throw new IOException("ERROR: frame format error [Lf!=count]");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Martinez
|
||||
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
||||
* warnings, split into multiple class files, and general clean up.
|
||||
*
|
||||
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Helmut Dersch
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class HuffmanTable {
|
||||
|
||||
private final int l[][][] = new int[4][2][16];
|
||||
private final int th[] = new int[4]; // 1: this table is presented
|
||||
private final int v[][][][] = new int[4][2][16][200]; // tables
|
||||
private final int[][] tc = new int[4][2]; // 1: this table is presented
|
||||
|
||||
public static final int MSB = 0x80000000;
|
||||
|
||||
|
||||
|
||||
public HuffmanTable() {
|
||||
tc[0][0] = 0;
|
||||
tc[1][0] = 0;
|
||||
tc[2][0] = 0;
|
||||
tc[3][0] = 0;
|
||||
tc[0][1] = 0;
|
||||
tc[1][1] = 0;
|
||||
tc[2][1] = 0;
|
||||
tc[3][1] = 0;
|
||||
th[0] = 0;
|
||||
th[1] = 0;
|
||||
th[2] = 0;
|
||||
th[3] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected int read(final DataStream data, final int[][][] HuffTab) throws IOException {
|
||||
int count = 0;
|
||||
final int length = data.get16();
|
||||
count += 2;
|
||||
|
||||
while (count < length) {
|
||||
final int temp = data.get8();
|
||||
count++;
|
||||
final int t = temp & 0x0F;
|
||||
if (t > 3) {
|
||||
throw new IOException("ERROR: Huffman table ID > 3");
|
||||
}
|
||||
|
||||
final int c = temp >> 4;
|
||||
if (c > 2) {
|
||||
throw new IOException("ERROR: Huffman table [Table class > 2 ]");
|
||||
}
|
||||
|
||||
th[t] = 1;
|
||||
tc[t][c] = 1;
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
l[t][c][i] = data.get8();
|
||||
count++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
for (int j = 0; j < l[t][c][i]; j++) {
|
||||
if (count > length) {
|
||||
throw new IOException("ERROR: Huffman table format error [count>Lh]");
|
||||
}
|
||||
v[t][c][i][j] = data.get8();
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count != length) {
|
||||
throw new IOException("ERROR: Huffman table format error [count!=Lf]");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
if (tc[i][j] != 0) {
|
||||
buildHuffTable(HuffTab[i][j], l[i][j], v[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Build_HuffTab()
|
||||
// Parameter: t table ID
|
||||
// c table class ( 0 for DC, 1 for AC )
|
||||
// L[i] # of codewords which length is i
|
||||
// V[i][j] Huffman Value (length=i)
|
||||
// Effect:
|
||||
// build up HuffTab[t][c] using L and V.
|
||||
private void buildHuffTable(final int tab[], final int L[], final int V[][]) throws IOException {
|
||||
int currentTable, temp;
|
||||
int k;
|
||||
temp = 256;
|
||||
k = 0;
|
||||
|
||||
for (int i = 0; i < 8; i++) { // i+1 is Code length
|
||||
for (int j = 0; j < L[i]; j++) {
|
||||
for (int n = 0; n < (temp >> (i + 1)); n++) {
|
||||
tab[k] = V[i][j] | ((i + 1) << 8);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 1; k < 256; i++, k++) {
|
||||
tab[k] = i | MSB;
|
||||
}
|
||||
|
||||
currentTable = 1;
|
||||
k = 0;
|
||||
|
||||
for (int i = 8; i < 16; i++) { // i+1 is Code length
|
||||
for (int j = 0; j < L[i]; j++) {
|
||||
for (int n = 0; n < (temp >> (i - 7)); n++) {
|
||||
tab[(currentTable * 256) + k] = V[i][j] | ((i + 1) << 8);
|
||||
k++;
|
||||
}
|
||||
if (k >= 256) {
|
||||
if (k > 256) {
|
||||
throw new IOException("ERROR: Huffman table error(1)!");
|
||||
}
|
||||
k = 0;
|
||||
currentTable++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,780 @@
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Martinez
|
||||
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
||||
* warnings, split into multiple class files, and general clean up.
|
||||
*
|
||||
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Helmut Dersch
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
||||
public class JPEGLosslessDecoder implements DataStream {
|
||||
|
||||
private final ByteBuffer buffer;
|
||||
private final FrameHeader frame;
|
||||
private final HuffmanTable huffTable;
|
||||
private final QuantizationTable quantTable;
|
||||
private final ScanHeader scan;
|
||||
private final int HuffTab[][][] = new int[4][2][MAX_HUFFMAN_SUBTREE * 256];
|
||||
private final int IDCT_Source[] = new int[64];
|
||||
private final int nBlock[] = new int[10]; // number of blocks in the i-th Comp in a scan
|
||||
private final int[] acTab[] = new int[10][]; // ac HuffTab for the i-th Comp in a scan
|
||||
private final int[] dcTab[] = new int[10][]; // dc HuffTab for the i-th Comp in a scan
|
||||
private final int[] qTab[] = new int[10][]; // quantization table for the i-th Comp in a scan
|
||||
|
||||
private boolean restarting;
|
||||
private int dataBufferIndex;
|
||||
private int marker;
|
||||
private int markerIndex;
|
||||
private int numComp;
|
||||
private int restartInterval;
|
||||
private int selection;
|
||||
private int xDim, yDim;
|
||||
private int xLoc;
|
||||
private int yLoc;
|
||||
private int mask;
|
||||
private int[] outputData;
|
||||
private int[] outputRedData;
|
||||
private int[] outputGreenData;
|
||||
private int[] outputBlueData;
|
||||
|
||||
private static final int IDCT_P[] = { 0, 5, 40, 16, 45, 2, 7, 42, 21, 56, 8, 61, 18, 47, 1, 4, 41, 23, 58, 13, 32, 24, 37, 10, 63, 17, 44, 3, 6, 43, 20,
|
||||
57, 15, 34, 29, 48, 53, 26, 39, 9, 60, 19, 46, 22, 59, 12, 33, 31, 50, 55, 25, 36, 11, 62, 14, 35, 28, 49, 52, 27, 38, 30, 51, 54 };
|
||||
private static final int TABLE[] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53,
|
||||
10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 };
|
||||
|
||||
public static final int RESTART_MARKER_BEGIN = 0xFFD0;
|
||||
public static final int RESTART_MARKER_END = 0xFFD7;
|
||||
public static final int MAX_HUFFMAN_SUBTREE = 50;
|
||||
public static final int MSB = 0x80000000;
|
||||
|
||||
|
||||
public int getDimX(){
|
||||
return xDim;
|
||||
}
|
||||
public int getDimY(){
|
||||
return yDim;
|
||||
}
|
||||
|
||||
public JPEGLosslessDecoder(final byte[] data) {
|
||||
buffer = ByteBuffer.wrap(data);
|
||||
frame = new FrameHeader();
|
||||
scan = new ScanHeader();
|
||||
quantTable = new QuantizationTable();
|
||||
huffTable = new HuffmanTable();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int[][] decode() throws IOException {
|
||||
int current, scanNum = 0;
|
||||
final int pred[] = new int[10];
|
||||
int[][] outputRef = null;
|
||||
|
||||
xLoc = 0;
|
||||
yLoc = 0;
|
||||
current = get16();
|
||||
|
||||
if (current != 0xFFD8) { // SOI
|
||||
throw new IOException("Not a JPEG file");
|
||||
}
|
||||
|
||||
current = get16();
|
||||
|
||||
while (((current >> 4) != 0x0FFC) || (current == 0xFFC4)) { // SOF 0~15
|
||||
switch (current) {
|
||||
case 0xFFC4: // DHT
|
||||
huffTable.read(this, HuffTab);
|
||||
break;
|
||||
case 0xFFCC: // DAC
|
||||
throw new IOException("Program doesn't support arithmetic coding. (format throw new IOException)");
|
||||
case 0xFFDB:
|
||||
quantTable.read(this, TABLE);
|
||||
break;
|
||||
case 0xFFDD:
|
||||
restartInterval = readNumber();
|
||||
break;
|
||||
case 0xFFE0:
|
||||
case 0xFFE1:
|
||||
case 0xFFE2:
|
||||
case 0xFFE3:
|
||||
case 0xFFE4:
|
||||
case 0xFFE5:
|
||||
case 0xFFE6:
|
||||
case 0xFFE7:
|
||||
case 0xFFE8:
|
||||
case 0xFFE9:
|
||||
case 0xFFEA:
|
||||
case 0xFFEB:
|
||||
case 0xFFEC:
|
||||
case 0xFFED:
|
||||
case 0xFFEE:
|
||||
case 0xFFEF:
|
||||
readApp();
|
||||
break;
|
||||
case 0xFFFE:
|
||||
readComment();
|
||||
break;
|
||||
default:
|
||||
if ((current >> 8) != 0xFF) {
|
||||
throw new IOException("ERROR: format throw new IOException! (decode)");
|
||||
}
|
||||
}
|
||||
|
||||
current = get16();
|
||||
}
|
||||
|
||||
if ((current < 0xFFC0) || (current > 0xFFC7)) {
|
||||
throw new IOException("ERROR: could not handle arithmetic code!");
|
||||
}
|
||||
|
||||
frame.read(this);
|
||||
current = get16();
|
||||
|
||||
do {
|
||||
while (current != 0x0FFDA) { //SOS
|
||||
switch (current) {
|
||||
case 0xFFC4: //DHT
|
||||
huffTable.read(this, HuffTab);
|
||||
break;
|
||||
case 0xFFCC: //DAC
|
||||
throw new IOException("Program doesn't support arithmetic coding. (format throw new IOException)");
|
||||
case 0xFFDB:
|
||||
quantTable.read(this, TABLE);
|
||||
break;
|
||||
case 0xFFDD:
|
||||
restartInterval = readNumber();
|
||||
break;
|
||||
case 0xFFE0:
|
||||
case 0xFFE1:
|
||||
case 0xFFE2:
|
||||
case 0xFFE3:
|
||||
case 0xFFE4:
|
||||
case 0xFFE5:
|
||||
case 0xFFE6:
|
||||
case 0xFFE7:
|
||||
case 0xFFE8:
|
||||
case 0xFFE9:
|
||||
case 0xFFEA:
|
||||
case 0xFFEB:
|
||||
case 0xFFEC:
|
||||
case 0xFFED:
|
||||
case 0xFFEE:
|
||||
case 0xFFEF:
|
||||
readApp();
|
||||
break;
|
||||
case 0xFFFE:
|
||||
readComment();
|
||||
break;
|
||||
default:
|
||||
if ((current >> 8) != 0xFF) {
|
||||
throw new IOException("ERROR: format throw new IOException! (Parser.decode)");
|
||||
}
|
||||
}
|
||||
|
||||
current = get16();
|
||||
}
|
||||
|
||||
final int precision = frame.getPrecision();
|
||||
|
||||
if (precision == 8) {
|
||||
mask = 0xFF;
|
||||
} else {
|
||||
mask = 0xFFFF;
|
||||
}
|
||||
|
||||
final ComponentSpec[] components = frame.getComponents();
|
||||
|
||||
scan.read(this);
|
||||
numComp = scan.getNumComponents();
|
||||
selection = scan.getSelection();
|
||||
|
||||
final ScanComponent[] scanComps = scan.components;
|
||||
final int[][] quantTables = quantTable.quantTables;
|
||||
|
||||
for (int i = 0; i < numComp; i++) {
|
||||
final int compN = scanComps[i].getScanCompSel();
|
||||
qTab[i] = quantTables[components[compN].quantTableSel];
|
||||
nBlock[i] = components[compN].vSamp * components[compN].hSamp;
|
||||
dcTab[i] = HuffTab[scanComps[i].getDcTabSel()][0];
|
||||
acTab[i] = HuffTab[scanComps[i].getAcTabSel()][1];
|
||||
}
|
||||
|
||||
xDim = frame.getDimX();
|
||||
yDim = frame.getDimY();
|
||||
|
||||
outputRef = new int[numComp][];
|
||||
|
||||
if (numComp == 1) {
|
||||
outputData = new int[xDim * yDim];
|
||||
outputRef[0] = outputData;
|
||||
} else {
|
||||
outputRedData = new int[xDim * yDim]; // not a good use of memory, but I had trouble packing bytes into int. some values exceeded 255.
|
||||
outputGreenData = new int[xDim * yDim];
|
||||
outputBlueData = new int[xDim * yDim];
|
||||
|
||||
outputRef[0] = outputRedData;
|
||||
outputRef[1] = outputGreenData;
|
||||
outputRef[2] = outputBlueData;
|
||||
}
|
||||
|
||||
scanNum++;
|
||||
|
||||
while (true) { // Decode one scan
|
||||
final int temp[] = new int[1]; // to store remainder bits
|
||||
final int index[] = new int[1];
|
||||
temp[0] = 0;
|
||||
index[0] = 0;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
pred[i] = (1 << (precision - 1));
|
||||
}
|
||||
|
||||
if (restartInterval == 0) {
|
||||
current = decode(pred, temp, index);
|
||||
|
||||
while ((current == 0) && ((xLoc < xDim) && (yLoc < yDim))) {
|
||||
output(pred);
|
||||
current = decode(pred, temp, index);
|
||||
}
|
||||
|
||||
break; //current=MARKER
|
||||
}
|
||||
|
||||
for (int mcuNum = 0; mcuNum < restartInterval; mcuNum++) {
|
||||
restarting = (mcuNum == 0);
|
||||
current = decode(pred, temp, index);
|
||||
output(pred);
|
||||
|
||||
if (current != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (current == 0) {
|
||||
if (markerIndex != 0) {
|
||||
current = (0xFF00 | marker);
|
||||
markerIndex = 0;
|
||||
} else {
|
||||
current = get16();
|
||||
}
|
||||
}
|
||||
|
||||
if ((current >= RESTART_MARKER_BEGIN) && (current <= RESTART_MARKER_END)) {
|
||||
//empty
|
||||
} else {
|
||||
break; //current=MARKER
|
||||
}
|
||||
}
|
||||
|
||||
if ((current == 0xFFDC) && (scanNum == 1)) { //DNL
|
||||
readNumber();
|
||||
current = get16();
|
||||
}
|
||||
} while ((current != 0xFFD9) && ((xLoc < xDim) && (yLoc < yDim)) && (scanNum == 0));
|
||||
|
||||
return outputRef;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public final int get16() {
|
||||
final int value = (buffer.getShort(dataBufferIndex) & 0xFFFF);
|
||||
dataBufferIndex += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public final int get8() {
|
||||
return buffer.get(dataBufferIndex++) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int decode(final int prev[], final int temp[], final int index[]) throws IOException {
|
||||
if (numComp == 1) {
|
||||
return decodeSingle(prev, temp, index);
|
||||
} else if (numComp == 3) {
|
||||
return decodeRGB(prev, temp, index);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int decodeSingle(final int prev[], final int temp[], final int index[]) throws IOException {
|
||||
// At the beginning of the first line and
|
||||
// at the beginning of each restart interval the prediction value of 2P – 1 is used, where P is the input precision.
|
||||
if (restarting) {
|
||||
restarting = false;
|
||||
prev[0] = (1 << (frame.getPrecision() - 1));
|
||||
} else {
|
||||
switch (selection) {
|
||||
case 2:
|
||||
prev[0] = getPreviousY(outputData);
|
||||
break;
|
||||
case 3:
|
||||
prev[0] = getPreviousXY(outputData);
|
||||
break;
|
||||
case 4:
|
||||
prev[0] = (getPreviousX(outputData) + getPreviousY(outputData)) - getPreviousXY(outputData);
|
||||
break;
|
||||
case 5:
|
||||
prev[0] = getPreviousX(outputData) + ((getPreviousY(outputData) - getPreviousXY(outputData)) >> 1);
|
||||
break;
|
||||
case 6:
|
||||
prev[0] = getPreviousY(outputData) + ((getPreviousX(outputData) - getPreviousXY(outputData)) >> 1);
|
||||
break;
|
||||
case 7:
|
||||
prev[0] = (int) (((long) getPreviousX(outputData) + getPreviousY(outputData)) / 2);
|
||||
break;
|
||||
default:
|
||||
prev[0] = getPreviousX(outputData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < nBlock[0]; i++) {
|
||||
final int value = getHuffmanValue(dcTab[0], temp, index);
|
||||
|
||||
if (value >= 0xFF00) {
|
||||
return value;
|
||||
}
|
||||
|
||||
final int n = getn(prev, value, temp, index);
|
||||
|
||||
final int nRestart = (n >> 8);
|
||||
if ((nRestart >= RESTART_MARKER_BEGIN) && (nRestart <= RESTART_MARKER_END)) {
|
||||
return nRestart;
|
||||
}
|
||||
|
||||
prev[0] += n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int decodeRGB(final int prev[], final int temp[], final int index[]) throws IOException {
|
||||
switch (selection) {
|
||||
case 2:
|
||||
prev[0] = getPreviousY(outputRedData);
|
||||
prev[1] = getPreviousY(outputGreenData);
|
||||
prev[2] = getPreviousY(outputBlueData);
|
||||
break;
|
||||
case 3:
|
||||
prev[0] = getPreviousXY(outputRedData);
|
||||
prev[1] = getPreviousXY(outputGreenData);
|
||||
prev[2] = getPreviousXY(outputBlueData);
|
||||
break;
|
||||
case 4:
|
||||
prev[0] = (getPreviousX(outputRedData) + getPreviousY(outputRedData)) - getPreviousXY(outputRedData);
|
||||
prev[1] = (getPreviousX(outputGreenData) + getPreviousY(outputGreenData)) - getPreviousXY(outputGreenData);
|
||||
prev[2] = (getPreviousX(outputBlueData) + getPreviousY(outputBlueData)) - getPreviousXY(outputBlueData);
|
||||
break;
|
||||
case 5:
|
||||
prev[0] = getPreviousX(outputRedData) + ((getPreviousY(outputRedData) - getPreviousXY(outputRedData)) >> 1);
|
||||
prev[1] = getPreviousX(outputGreenData) + ((getPreviousY(outputGreenData) - getPreviousXY(outputGreenData)) >> 1);
|
||||
prev[2] = getPreviousX(outputBlueData) + ((getPreviousY(outputBlueData) - getPreviousXY(outputBlueData)) >> 1);
|
||||
break;
|
||||
case 6:
|
||||
prev[0] = getPreviousY(outputRedData) + ((getPreviousX(outputRedData) - getPreviousXY(outputRedData)) >> 1);
|
||||
prev[1] = getPreviousY(outputGreenData) + ((getPreviousX(outputGreenData) - getPreviousXY(outputGreenData)) >> 1);
|
||||
prev[2] = getPreviousY(outputBlueData) + ((getPreviousX(outputBlueData) - getPreviousXY(outputBlueData)) >> 1);
|
||||
break;
|
||||
case 7:
|
||||
prev[0] = (int) (((long) getPreviousX(outputRedData) + getPreviousY(outputRedData)) / 2);
|
||||
prev[1] = (int) (((long) getPreviousX(outputGreenData) + getPreviousY(outputGreenData)) / 2);
|
||||
prev[2] = (int) (((long) getPreviousX(outputBlueData) + getPreviousY(outputBlueData)) / 2);
|
||||
break;
|
||||
default:
|
||||
prev[0] = getPreviousX(outputRedData);
|
||||
prev[1] = getPreviousX(outputGreenData);
|
||||
prev[2] = getPreviousX(outputBlueData);
|
||||
break;
|
||||
}
|
||||
|
||||
int value, actab[], dctab[];
|
||||
int qtab[];
|
||||
|
||||
for (int ctrC = 0; ctrC < numComp; ctrC++) {
|
||||
qtab = qTab[ctrC];
|
||||
actab = acTab[ctrC];
|
||||
dctab = dcTab[ctrC];
|
||||
for (int i = 0; i < nBlock[ctrC]; i++) {
|
||||
for (int k = 0; k < IDCT_Source.length; k++) {
|
||||
IDCT_Source[k] = 0;
|
||||
}
|
||||
|
||||
value = getHuffmanValue(dctab, temp, index);
|
||||
|
||||
if (value >= 0xFF00) {
|
||||
return value;
|
||||
}
|
||||
|
||||
prev[ctrC] = IDCT_Source[0] = prev[ctrC] + getn(index, value, temp, index);
|
||||
IDCT_Source[0] *= qtab[0];
|
||||
|
||||
for (int j = 1; j < 64; j++) {
|
||||
value = getHuffmanValue(actab, temp, index);
|
||||
|
||||
if (value >= 0xFF00) {
|
||||
return value;
|
||||
}
|
||||
|
||||
j += (value >> 4);
|
||||
|
||||
if ((value & 0x0F) == 0) {
|
||||
if ((value >> 4) == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
IDCT_Source[IDCT_P[j]] = getn(index, value & 0x0F, temp, index) * qtab[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Huffman table for fast search: (HuffTab) 8-bit Look up table 2-layer search architecture, 1st-layer represent 256 node (8 bits) if codeword-length > 8
|
||||
// bits, then the entry of 1st-layer = (# of 2nd-layer table) | MSB and it is stored in the 2nd-layer Size of tables in each layer are 256.
|
||||
// HuffTab[*][*][0-256] is always the only 1st-layer table.
|
||||
//
|
||||
// An entry can be: (1) (# of 2nd-layer table) | MSB , for code length > 8 in 1st-layer (2) (Code length) << 8 | HuffVal
|
||||
//
|
||||
// HuffmanValue(table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...)
|
||||
// ):
|
||||
// return: Huffman Value of table
|
||||
// 0xFF?? if it receives a MARKER
|
||||
// Parameter: table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...)
|
||||
// temp temp storage for remainded bits
|
||||
// index index to bit of temp
|
||||
// in FILE pointer
|
||||
// Effect:
|
||||
// temp store new remainded bits
|
||||
// index change to new index
|
||||
// in change to new position
|
||||
// NOTE:
|
||||
// Initial by temp=0; index=0;
|
||||
// NOTE: (explain temp and index)
|
||||
// temp: is always in the form at calling time or returning time
|
||||
// | byte 4 | byte 3 | byte 2 | byte 1 |
|
||||
// | 0 | 0 | 00000000 | 00000??? | if not a MARKER
|
||||
// ^index=3 (from 0 to 15)
|
||||
// 321
|
||||
// NOTE (marker and marker_index):
|
||||
// If get a MARKER from 'in', marker=the low-byte of the MARKER
|
||||
// and marker_index=9
|
||||
// If marker_index=9 then index is always > 8, or HuffmanValue()
|
||||
// will not be called
|
||||
private int getHuffmanValue(final int table[], final int temp[], final int index[]) throws IOException {
|
||||
int code, input;
|
||||
final int mask = 0xFFFF;
|
||||
|
||||
if (index[0] < 8) {
|
||||
temp[0] <<= 8;
|
||||
input = get8();
|
||||
if (input == 0xFF) {
|
||||
marker = get8();
|
||||
if (marker != 0) {
|
||||
markerIndex = 9;
|
||||
}
|
||||
}
|
||||
temp[0] |= input;
|
||||
} else {
|
||||
index[0] -= 8;
|
||||
}
|
||||
|
||||
code = table[temp[0] >> index[0]];
|
||||
|
||||
if ((code & MSB) != 0) {
|
||||
if (markerIndex != 0) {
|
||||
markerIndex = 0;
|
||||
return 0xFF00 | marker;
|
||||
}
|
||||
|
||||
temp[0] &= (mask >> (16 - index[0]));
|
||||
temp[0] <<= 8;
|
||||
input = get8();
|
||||
|
||||
if (input == 0xFF) {
|
||||
marker = get8();
|
||||
if (marker != 0) {
|
||||
markerIndex = 9;
|
||||
}
|
||||
}
|
||||
|
||||
temp[0] |= input;
|
||||
code = table[((code & 0xFF) * 256) + (temp[0] >> index[0])];
|
||||
index[0] += 8;
|
||||
}
|
||||
|
||||
index[0] += 8 - (code >> 8);
|
||||
|
||||
if (index[0] < 0) {
|
||||
throw new IOException("index=" + index[0] + " temp=" + temp[0] + " code=" + code + " in HuffmanValue()");
|
||||
}
|
||||
|
||||
if (index[0] < markerIndex) {
|
||||
markerIndex = 0;
|
||||
return 0xFF00 | marker;
|
||||
}
|
||||
|
||||
temp[0] &= (mask >> (16 - index[0]));
|
||||
return code & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int getn(final int[] PRED, final int n, final int temp[], final int index[]) throws IOException {
|
||||
int result;
|
||||
final int one = 1;
|
||||
final int n_one = -1;
|
||||
final int mask = 0xFFFF;
|
||||
int input;
|
||||
|
||||
if (n == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n == 16) {
|
||||
if (PRED[0] >= 0) {
|
||||
return -32768;
|
||||
} else {
|
||||
return 32768;
|
||||
}
|
||||
}
|
||||
|
||||
index[0] -= n;
|
||||
|
||||
if (index[0] >= 0) {
|
||||
if ((index[0] < markerIndex) && !isLastPixel()) { // this was corrupting the last pixel in some cases
|
||||
markerIndex = 0;
|
||||
return (0xFF00 | marker) << 8;
|
||||
}
|
||||
|
||||
result = temp[0] >> index[0];
|
||||
temp[0] &= (mask >> (16 - index[0]));
|
||||
} else {
|
||||
temp[0] <<= 8;
|
||||
input = get8();
|
||||
|
||||
if (input == 0xFF) {
|
||||
marker = get8();
|
||||
if (marker != 0) {
|
||||
markerIndex = 9;
|
||||
}
|
||||
}
|
||||
|
||||
temp[0] |= input;
|
||||
index[0] += 8;
|
||||
|
||||
if (index[0] < 0) {
|
||||
if (markerIndex != 0) {
|
||||
markerIndex = 0;
|
||||
return (0xFF00 | marker) << 8;
|
||||
}
|
||||
|
||||
temp[0] <<= 8;
|
||||
input = get8();
|
||||
|
||||
if (input == 0xFF) {
|
||||
marker = get8();
|
||||
if (marker != 0) {
|
||||
markerIndex = 9;
|
||||
}
|
||||
}
|
||||
|
||||
temp[0] |= input;
|
||||
index[0] += 8;
|
||||
}
|
||||
|
||||
if (index[0] < 0) {
|
||||
throw new IOException("index=" + index[0] + " in getn()");
|
||||
}
|
||||
|
||||
if (index[0] < markerIndex) {
|
||||
markerIndex = 0;
|
||||
return (0xFF00 | marker) << 8;
|
||||
}
|
||||
|
||||
result = temp[0] >> index[0];
|
||||
temp[0] &= (mask >> (16 - index[0]));
|
||||
}
|
||||
|
||||
if (result < (one << (n - 1))) {
|
||||
result += (n_one << n) + 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int getPreviousX(final int data[]) {
|
||||
if (xLoc > 0) {
|
||||
return data[((yLoc * xDim) + xLoc) - 1];
|
||||
} else if (yLoc > 0) {
|
||||
return getPreviousY(data);
|
||||
} else {
|
||||
return (1 << (frame.getPrecision() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int getPreviousXY(final int data[]) {
|
||||
if ((xLoc > 0) && (yLoc > 0)) {
|
||||
return data[(((yLoc - 1) * xDim) + xLoc) - 1];
|
||||
} else {
|
||||
return getPreviousY(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int getPreviousY(final int data[]) {
|
||||
if (yLoc > 0) {
|
||||
return data[((yLoc - 1) * xDim) + xLoc];
|
||||
} else {
|
||||
return getPreviousX(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean isLastPixel() {
|
||||
return (xLoc == (xDim - 1)) && (yLoc == (yDim - 1));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void output(final int PRED[]) {
|
||||
if (numComp == 1) {
|
||||
outputSingle(PRED);
|
||||
} else {
|
||||
outputRGB(PRED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void outputSingle(final int PRED[]) {
|
||||
if ((xLoc < xDim) && (yLoc < yDim)) {
|
||||
outputData[(yLoc * xDim) + xLoc] = mask & PRED[0];
|
||||
xLoc++;
|
||||
|
||||
if (xLoc >= xDim) {
|
||||
yLoc++;
|
||||
xLoc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void outputRGB(final int PRED[]) {
|
||||
if ((xLoc < xDim) && (yLoc < yDim)) {
|
||||
outputRedData[(yLoc * xDim) + xLoc] = PRED[0];
|
||||
outputGreenData[(yLoc * xDim) + xLoc] = PRED[1];
|
||||
outputBlueData[(yLoc * xDim) + xLoc] = PRED[2];
|
||||
xLoc++;
|
||||
|
||||
if (xLoc >= xDim) {
|
||||
yLoc++;
|
||||
xLoc = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int readApp() throws IOException {
|
||||
int count = 0;
|
||||
final int length = get16();
|
||||
count += 2;
|
||||
|
||||
while (count < length) {
|
||||
get8();
|
||||
count++;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String readComment() throws IOException {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
int count = 0;
|
||||
|
||||
final int length = get16();
|
||||
count += 2;
|
||||
|
||||
while (count < length) {
|
||||
sb.append((char) get8());
|
||||
count++;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private int readNumber() throws IOException {
|
||||
final int Ld = get16();
|
||||
|
||||
if (Ld != 4) {
|
||||
throw new IOException("ERROR: Define number format throw new IOException [Ld!=4]");
|
||||
}
|
||||
|
||||
return get16();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getNumComponents() {
|
||||
return numComp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getPrecision() {
|
||||
return frame.getPrecision();
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.awt.image.DataBufferUShort;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class provides the conversion of a byte buffer
|
||||
* containing a JPEGLossless to an BufferedImage.
|
||||
* Therefore it uses the rii-mango JPEGLosslessDecoder
|
||||
* Library ( https://github.com/rii-mango/JPEGLosslessDecoder )
|
||||
*
|
||||
* Take care, that only the following lossless formats are supported
|
||||
* 1.2.840.10008.1.2.4.57 JPEG Lossless, Nonhierarchical (Processes 14)
|
||||
* 1.2.840.10008.1.2.4.70 JPEG Lossless, Nonhierarchical (Processes 14 [Selection 1])
|
||||
*
|
||||
* Currently the following conversions are supported
|
||||
* - 24Bit, RGB -> BufferedImage.TYPE_INT_RGB
|
||||
* - 8Bit, Grayscale -> BufferedImage.TYPE_BYTE_GRAY
|
||||
* - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY
|
||||
*
|
||||
* @author Hermann Kroll
|
||||
*
|
||||
*/
|
||||
public class JPEGLosslessDecoderWrapper {
|
||||
|
||||
/**
|
||||
* Converts a byte buffer (containing a jpeg lossless)
|
||||
* to an Java BufferedImage
|
||||
* Currently the following conversions are supported
|
||||
* - 24Bit, RGB -> BufferedImage.TYPE_INT_RGB
|
||||
* - 8Bit, Grayscale -> BufferedImage.TYPE_BYTE_GRAY
|
||||
* - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY
|
||||
*
|
||||
* @param data byte buffer which contains a jpeg lossless
|
||||
* @return if successfully a BufferedImage is returned
|
||||
* @throws IOException is thrown if the decoder failed or a conversion is not supported
|
||||
*/
|
||||
public BufferedImage readImage(byte[] data) throws IOException{
|
||||
JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(data);
|
||||
|
||||
|
||||
int[][] decoded = decoder.decode();
|
||||
int width = decoder.getDimX();
|
||||
int height = decoder.getDimY();
|
||||
|
||||
if(decoder.getNumComponents() == 1){
|
||||
switch(decoder.getPrecision())
|
||||
{
|
||||
case 8:
|
||||
return read8Bit1ComponentGrayScale(decoded, width, height);
|
||||
case 16:
|
||||
return read16Bit1ComponentGrayScale(decoded, width, height);
|
||||
default:
|
||||
throw new IOException("JPEG Lossless with " + decoder.getPrecision() + " bit precision and 1 component cannot be decoded");
|
||||
}
|
||||
}
|
||||
//rgb
|
||||
if(decoder.getNumComponents() == 3){
|
||||
switch(decoder.getPrecision())
|
||||
{
|
||||
case 24:
|
||||
return read24Bit3ComponentRGB(decoded, width, height);
|
||||
|
||||
default:
|
||||
throw new IOException("JPEG Lossless with " + decoder.getPrecision() + " bit precision and 3 components cannot be decoded");
|
||||
}
|
||||
}
|
||||
|
||||
throw new IOException("JPEG Lossless with " + decoder.getPrecision() + " bit precision and " + decoder.getNumComponents() + " component(s) cannot be decoded");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* converts the decoded buffer into a BufferedImage
|
||||
* precision: 16 bit, componentCount = 1
|
||||
* @param decoded data buffer
|
||||
* @param width of the image
|
||||
* @param height of the image
|
||||
* @return a BufferedImage.TYPE_USHORT_GRAY
|
||||
*/
|
||||
private BufferedImage read16Bit1ComponentGrayScale(int[][] decoded, int width, int height){
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
|
||||
short[] imageBuffer = ((DataBufferUShort) image.getRaster().getDataBuffer()).getData();
|
||||
|
||||
for(int i = 0; i < imageBuffer.length; i++){
|
||||
imageBuffer[i] = (short)decoded[0][i];
|
||||
}
|
||||
return image;
|
||||
}
|
||||
/**
|
||||
* converts the decoded buffer into a BufferedImage
|
||||
* precision: 8 bit, componentCount = 1
|
||||
* @param decoded data buffer
|
||||
* @param width of the image
|
||||
* @param height of the image
|
||||
* @return a BufferedImage.TYPE_BYTE_GRAY
|
||||
*/
|
||||
private BufferedImage read8Bit1ComponentGrayScale(int[][] decoded, int width, int height){
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
|
||||
byte[] imageBuffer = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
||||
|
||||
for(int i = 0; i < imageBuffer.length; i++){
|
||||
imageBuffer[i] = (byte)decoded[0][i];
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* converts the decoded buffer into a BufferedImage
|
||||
* precision: 24 bit, componentCount = 3
|
||||
* @param decoded data buffer
|
||||
* @param width of the image
|
||||
* @param height of the image
|
||||
* @return a BufferedImage.TYPE_INT_RGB
|
||||
*/
|
||||
private BufferedImage read24Bit3ComponentRGB(int[][] decoded, int width, int height){
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
int[] imageBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
|
||||
|
||||
for(int i = 0; i < imageBuffer.length; i++){
|
||||
//convert to RGB
|
||||
imageBuffer[i] = (decoded[0][i] << 16) | (decoded[1][i] << 8) | (decoded[2][i]);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Martinez
|
||||
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
||||
* warnings, split into multiple class files, and general clean up.
|
||||
*
|
||||
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Helmut Dersch
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class QuantizationTable {
|
||||
|
||||
private final int precision[] = new int[4]; // Quantization precision 8 or 16
|
||||
private final int[] tq = new int[4]; // 1: this table is presented
|
||||
|
||||
protected final int quantTables[][] = new int[4][64]; // Tables
|
||||
|
||||
|
||||
|
||||
public QuantizationTable() {
|
||||
tq[0] = 0;
|
||||
tq[1] = 0;
|
||||
tq[2] = 0;
|
||||
tq[3] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected int read(final DataStream data, final int[] table) throws IOException {
|
||||
int count = 0;
|
||||
final int length = data.get16();
|
||||
count += 2;
|
||||
|
||||
while (count < length) {
|
||||
final int temp = data.get8();
|
||||
count++;
|
||||
final int t = temp & 0x0F;
|
||||
|
||||
if (t > 3) {
|
||||
throw new IOException("ERROR: Quantization table ID > 3");
|
||||
}
|
||||
|
||||
precision[t] = temp >> 4;
|
||||
|
||||
if (precision[t] == 0) {
|
||||
precision[t] = 8;
|
||||
} else if (precision[t] == 1) {
|
||||
precision[t] = 16;
|
||||
} else {
|
||||
throw new IOException("ERROR: Quantization table precision error");
|
||||
}
|
||||
|
||||
tq[t] = 1;
|
||||
|
||||
if (precision[t] == 8) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (count > length) {
|
||||
throw new IOException("ERROR: Quantization table format error");
|
||||
}
|
||||
|
||||
quantTables[t][i] = data.get8();
|
||||
count++;
|
||||
}
|
||||
|
||||
enhanceQuantizationTable(quantTables[t], table);
|
||||
} else {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (count > length) {
|
||||
throw new IOException("ERROR: Quantization table format error");
|
||||
}
|
||||
|
||||
quantTables[t][i] = data.get16();
|
||||
count += 2;
|
||||
}
|
||||
|
||||
enhanceQuantizationTable(quantTables[t], table);
|
||||
}
|
||||
}
|
||||
|
||||
if (count != length) {
|
||||
throw new IOException("ERROR: Quantization table error [count!=Lq]");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void enhanceQuantizationTable(final int qtab[], final int[] table) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
qtab[table[(0 * 8) + i]] *= 90;
|
||||
qtab[table[(4 * 8) + i]] *= 90;
|
||||
qtab[table[(2 * 8) + i]] *= 118;
|
||||
qtab[table[(6 * 8) + i]] *= 49;
|
||||
qtab[table[(5 * 8) + i]] *= 71;
|
||||
qtab[table[(1 * 8) + i]] *= 126;
|
||||
qtab[table[(7 * 8) + i]] *= 25;
|
||||
qtab[table[(3 * 8) + i]] *= 106;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
qtab[table[0 + (8 * i)]] *= 90;
|
||||
qtab[table[4 + (8 * i)]] *= 90;
|
||||
qtab[table[2 + (8 * i)]] *= 118;
|
||||
qtab[table[6 + (8 * i)]] *= 49;
|
||||
qtab[table[5 + (8 * i)]] *= 71;
|
||||
qtab[table[1 + (8 * i)]] *= 126;
|
||||
qtab[table[7 + (8 * i)]] *= 25;
|
||||
qtab[table[3 + (8 * i)]] *= 106;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 64; i++) {
|
||||
qtab[i] >>= 6;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Martinez
|
||||
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
||||
* warnings, split into multiple class files, and general clean up.
|
||||
*
|
||||
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Helmut Dersch
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
public class ScanComponent {
|
||||
|
||||
private int acTabSel; // AC table selector
|
||||
private int dcTabSel; // DC table selector
|
||||
private int scanCompSel; // Scan component selector
|
||||
|
||||
|
||||
|
||||
public int getAcTabSel() {
|
||||
return acTabSel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getDcTabSel() {
|
||||
return dcTabSel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getScanCompSel() {
|
||||
return scanCompSel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setAcTabSel(final int acTabSel) {
|
||||
this.acTabSel = acTabSel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setDcTabSel(final int dcTabSel) {
|
||||
this.dcTabSel = dcTabSel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setScanCompSel(final int scanCompSel) {
|
||||
this.scanCompSel = scanCompSel;
|
||||
}
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Martinez
|
||||
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
||||
* warnings, split into multiple class files, and general clean up.
|
||||
*
|
||||
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) Helmut Dersch
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
public class ScanHeader {
|
||||
|
||||
private int ah;
|
||||
private int al;
|
||||
private int numComp; // Number of components in the scan
|
||||
private int selection; // Start of spectral or predictor selection
|
||||
private int spectralEnd; // End of spectral selection
|
||||
|
||||
protected ScanComponent components[];
|
||||
|
||||
|
||||
|
||||
public int getAh() {
|
||||
return ah;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getAl() {
|
||||
return al;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getNumComponents() {
|
||||
return numComp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getSelection() {
|
||||
return selection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getSpectralEnd() {
|
||||
return spectralEnd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setAh(final int ah) {
|
||||
this.ah = ah;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setAl(final int al) {
|
||||
this.al = al;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setSelection(final int selection) {
|
||||
this.selection = selection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setSpectralEnd(final int spectralEnd) {
|
||||
this.spectralEnd = spectralEnd;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected int read(final DataStream data) throws IOException {
|
||||
int count = 0;
|
||||
final int length = data.get16();
|
||||
count += 2;
|
||||
|
||||
numComp = data.get8();
|
||||
count++;
|
||||
|
||||
components = new ScanComponent[numComp];
|
||||
|
||||
for (int i = 0; i < numComp; i++) {
|
||||
components[i] = new ScanComponent();
|
||||
|
||||
if (count > length) {
|
||||
throw new IOException("ERROR: scan header format error");
|
||||
}
|
||||
|
||||
components[i].setScanCompSel(data.get8());
|
||||
count++;
|
||||
|
||||
final int temp = data.get8();
|
||||
count++;
|
||||
|
||||
components[i].setDcTabSel(temp >> 4);
|
||||
components[i].setAcTabSel(temp & 0x0F);
|
||||
}
|
||||
|
||||
setSelection(data.get8());
|
||||
count++;
|
||||
|
||||
setSpectralEnd(data.get8());
|
||||
count++;
|
||||
|
||||
final int temp = data.get8();
|
||||
setAh(temp >> 4);
|
||||
setAl(temp & 0x0F);
|
||||
count++;
|
||||
|
||||
if (count != length) {
|
||||
throw new IOException("ERROR: scan header format error [count!=Ns]");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user