diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java index f0eb81c8..1b7a90ec 100644 --- a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/JPEGImageReader.java @@ -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 || diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ComponentSpec.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ComponentSpec.java new file mode 100644 index 00000000..b517de7a --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ComponentSpec.java @@ -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 +} diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/DataStream.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/DataStream.java new file mode 100644 index 00000000..842493f5 --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/DataStream.java @@ -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(); +} diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/FrameHeader.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/FrameHeader.java new file mode 100644 index 00000000..52dec2ca --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/FrameHeader.java @@ -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; + } +} diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/HuffmanTable.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/HuffmanTable.java new file mode 100644 index 00000000..33579821 --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/HuffmanTable.java @@ -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++; + } + } + } + } +} diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/JPEGLosslessDecoder.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/JPEGLosslessDecoder.java new file mode 100644 index 00000000..0ba8c1fe --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/JPEGLosslessDecoder.java @@ -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(); + } +} diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/JPEGLosslessDecoderWrapper.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/JPEGLosslessDecoderWrapper.java new file mode 100644 index 00000000..fb5f5d91 --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/JPEGLosslessDecoderWrapper.java @@ -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; + } + +} diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/QuantizationTable.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/QuantizationTable.java new file mode 100644 index 00000000..debbd8c4 --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/QuantizationTable.java @@ -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; + } + } +} diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ScanComponent.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ScanComponent.java new file mode 100644 index 00000000..6b9a64ee --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ScanComponent.java @@ -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; + } +} diff --git a/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ScanHeader.java b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ScanHeader.java new file mode 100644 index 00000000..f55217ac --- /dev/null +++ b/imageio/imageio-jpeg/src/main/java/com/twelvemonkeys/imageio/plugins/jpeg/lossless/ScanHeader.java @@ -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; + } +}