#182 Clean up after merge of #215

This commit is contained in:
Harald Kuhr 2016-08-10 15:36:41 +02:00
parent b1ac99ba1a
commit 15ce9d6b64
14 changed files with 1347 additions and 1358 deletions

View File

@ -28,6 +28,7 @@
package com.twelvemonkeys.imageio.plugins.jpeg; package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.AbstractMetadata;
import com.twelvemonkeys.imageio.ImageReaderBase; import com.twelvemonkeys.imageio.ImageReaderBase;
import com.twelvemonkeys.imageio.color.ColorSpaces; import com.twelvemonkeys.imageio.color.ColorSpaces;
import com.twelvemonkeys.imageio.color.YCbCrConverter; import com.twelvemonkeys.imageio.color.YCbCrConverter;
@ -39,16 +40,17 @@ import com.twelvemonkeys.imageio.metadata.exif.TIFF;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG; import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment; import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegmentUtil; 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.plugins.jpeg.lossless.JPEGLosslessDecoderWrapper;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers; import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ProgressListenerBase; import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import com.twelvemonkeys.xml.XMLSerializer; import com.twelvemonkeys.xml.XMLSerializer;
import org.w3c.dom.Node;
import javax.imageio.*; import javax.imageio.*;
import javax.imageio.event.IIOReadUpdateListener; import javax.imageio.event.IIOReadUpdateListener;
import javax.imageio.event.IIOReadWarningListener; import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata; import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataFormatImpl; import javax.imageio.metadata.IIOMetadataFormatImpl;
import javax.imageio.metadata.IIOMetadataNode; import javax.imageio.metadata.IIOMetadataNode;
@ -136,6 +138,10 @@ public class JPEGImageReader extends ImageReaderBase {
map.put(JPEG.SOF14, null); map.put(JPEG.SOF14, null);
map.put(JPEG.SOF15, null); map.put(JPEG.SOF15, null);
map.put(JPEG.DQT, null);
map.put(JPEG.DHT, null);
map.put(JPEG.SOS, null);
return Collections.unmodifiableMap(map); return Collections.unmodifiableMap(map);
} }
@ -201,6 +207,12 @@ public class JPEGImageReader extends ImageReaderBase {
@Override @Override
public int getNumImages(boolean allowSearch) throws IOException { public int getNumImages(boolean allowSearch) throws IOException {
if (allowSearch) {
if (isLossless()) {
return 1;
}
}
try { try {
return delegate.getNumImages(allowSearch); return delegate.getNumImages(allowSearch);
} }
@ -210,13 +222,43 @@ public class JPEGImageReader extends ImageReaderBase {
} }
} }
private boolean isLossless() throws IOException {
assertInput();
try {
SOFSegment sof = getSOF();
if (sof.marker == JPEG.SOF3) {
return true;
}
}
catch (IIOException ignore) {
// May happen if no SOF is found, in case we'll just fall through
}
return false;
}
@Override @Override
public int getWidth(int imageIndex) throws IOException { public int getWidth(int imageIndex) throws IOException {
checkBounds(imageIndex);
SOFSegment sof = getSOF();
if (sof.marker == JPEG.SOF3) {
return sof.samplesPerLine;
}
return delegate.getWidth(imageIndex); return delegate.getWidth(imageIndex);
} }
@Override @Override
public int getHeight(int imageIndex) throws IOException { public int getHeight(int imageIndex) throws IOException {
checkBounds(imageIndex);
SOFSegment sof = getSOF();
if (sof.marker == JPEG.SOF3) {
return sof.lines;
}
return delegate.getHeight(imageIndex); return delegate.getHeight(imageIndex);
} }
@ -277,7 +319,7 @@ public class JPEGImageReader extends ImageReaderBase {
return rawType; return rawType;
} }
} }
catch (NullPointerException ignore) { catch (IIOException | NullPointerException ignore) {
// Fall through // Fall through
} }
@ -340,33 +382,11 @@ public class JPEGImageReader extends ImageReaderBase {
JPEGColorSpace sourceCSType = getSourceCSType(getJFIF(), adobeDCT, sof); JPEGColorSpace sourceCSType = getSourceCSType(getJFIF(), adobeDCT, sof);
//try to read an JPEG Lossless image
if (sof.marker == JPEG.SOF3) { if (sof.marker == JPEG.SOF3) {
JPEGLosslessDecoderWrapper decoder = new JPEGLosslessDecoderWrapper(); // TODO: What about stream position?
ImageInputStream inputStream = (ImageInputStream) this.getInput(); // TODO: Param handling: Source region, offset, subsampling, destination, destination type, etc....
// Read image as lossless
/* try to read the input stream as once. If length is not supported return new JPEGLosslessDecoderWrapper().readImage(imageInput);
* 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) // We need to apply ICC profile unless the profile is sRGB/default gray (whatever that is)
@ -746,34 +766,35 @@ public class JPEGImageReader extends ImageReaderBase {
initHeader(); initHeader();
for (JPEGSegment segment : segments) { for (JPEGSegment segment : segments) {
if (JPEG.SOF0 >= segment.marker() && segment.marker() <= JPEG.SOF3 || if (JPEG.SOF0 <= segment.marker() && segment.marker() <= JPEG.SOF3 ||
JPEG.SOF5 >= segment.marker() && segment.marker() <= JPEG.SOF7 || JPEG.SOF5 <= segment.marker() && segment.marker() <= JPEG.SOF7 ||
JPEG.SOF9 >= segment.marker() && segment.marker() <= JPEG.SOF11 || JPEG.SOF9 <= segment.marker() && segment.marker() <= JPEG.SOF11 ||
JPEG.SOF13 >= segment.marker() && segment.marker() <= JPEG.SOF15) { JPEG.SOF13 <= segment.marker() && segment.marker() <= JPEG.SOF15) {
DataInputStream data = new DataInputStream(segment.data()); try (DataInputStream data = new DataInputStream(segment.data())) {
return SOFSegment.read(segment.marker(), data);
try {
int samplePrecision = data.readUnsignedByte();
int lines = data.readUnsignedShort();
int samplesPerLine = data.readUnsignedShort();
int componentsInFrame = data.readUnsignedByte();
SOFComponent[] components = new SOFComponent[componentsInFrame];
for (int i = 0; i < componentsInFrame; i++) {
int id = data.readUnsignedByte();
int sub = data.readUnsignedByte();
int qtSel = data.readUnsignedByte();
components[i] = new SOFComponent(id, ((sub & 0xF0) >> 4), (sub & 0xF), qtSel);
}
return new SOFSegment(segment.marker(), samplePrecision, lines, samplesPerLine, components);
}
finally {
data.close();
} }
// try {
// int samplePrecision = data.readUnsignedByte();
// int lines = data.readUnsignedShort();
// int samplesPerLine = data.readUnsignedShort();
// int componentsInFrame = data.readUnsignedByte();
//
// SOFComponent[] components = new SOFComponent[componentsInFrame];
//
// for (int i = 0; i < componentsInFrame; i++) {
// int id = data.readUnsignedByte();
// int sub = data.readUnsignedByte();
// int qtSel = data.readUnsignedByte();
//
// components[i] = new SOFComponent(id, ((sub & 0xF0) >> 4), (sub & 0xF), qtSel);
// }
//
// return new SOFSegment(segment.marker(), samplePrecision, lines, samplesPerLine, components);
// }
// finally {
// data.close();
// }
} }
} }
@ -960,7 +981,15 @@ public class JPEGImageReader extends ImageReaderBase {
} }
@Override @Override
public Raster readRaster(int imageIndex, ImageReadParam param) throws IOException { public Raster readRaster(final int imageIndex, final ImageReadParam param) throws IOException {
checkBounds(imageIndex);
if (isLossless()) {
// TODO: What about stream position?
// TODO: Param handling: Reading as raster should support source region, subsampling etc.
return new JPEGLosslessDecoderWrapper().readRaster(imageInput);
}
return delegate.readRaster(imageIndex, param); return delegate.readRaster(imageIndex, param);
} }
@ -1101,6 +1130,39 @@ public class JPEGImageReader extends ImageReaderBase {
IIOMetadata imageMetadata; IIOMetadata imageMetadata;
if (isLossless()) {
return new AbstractMetadata(true, JPEGImage10MetadataCleaner.JAVAX_IMAGEIO_JPEG_IMAGE_1_0, null, null, null) {
@Override
protected Node getNativeTree() {
IIOMetadataNode root = new IIOMetadataNode(JPEGImage10MetadataCleaner.JAVAX_IMAGEIO_JPEG_IMAGE_1_0);
root.appendChild(new IIOMetadataNode("JPEGvariety"));
IIOMetadataNode markerSequence = new IIOMetadataNode("markerSequence");
root.appendChild(markerSequence);
for (JPEGSegment segment : segments) {
switch (segment.marker()) {
// SOF3 is the only one supported by now
case JPEG.SOF3:
markerSequence.appendChild(new IIOMetadataNode("sof"));
break;
case JPEG.DHT:
markerSequence.appendChild(new IIOMetadataNode("dht"));
break;
case JPEG.DQT:
markerSequence.appendChild(new IIOMetadataNode("dqt"));
break;
case JPEG.SOS:
markerSequence.appendChild(new IIOMetadataNode("sos"));
break;
}
}
return root;
}
};
}
else {
try { try {
imageMetadata = delegate.getImageMetadata(imageIndex); imageMetadata = delegate.getImageMetadata(imageIndex);
} }
@ -1120,6 +1182,7 @@ public class JPEGImageReader extends ImageReaderBase {
return metadataCleaner.cleanMetadata(imageMetadata); return metadataCleaner.cleanMetadata(imageMetadata);
} }
}
return imageMetadata; return imageMetadata;
} }
@ -1402,7 +1465,8 @@ public class JPEGImageReader extends ImageReaderBase {
image = reader.getImageTypes(0).next().createBufferedImage((reader.getWidth(0) + subX - 1)/ subX, (reader.getHeight(0) + subY - 1) / subY); image = reader.getImageTypes(0).next().createBufferedImage((reader.getWidth(0) + subX - 1)/ subX, (reader.getHeight(0) + subY - 1) / subY);
} }
else { else {
image = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0)); // image = reader.getImageTypes(0).next().createBufferedImage(reader.getWidth(0), reader.getHeight(0));
image = null;
} }
param.setDestination(image); param.setDestination(image);

View File

@ -28,6 +28,8 @@
package com.twelvemonkeys.imageio.plugins.jpeg; package com.twelvemonkeys.imageio.plugins.jpeg;
import java.io.DataInput;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
/** /**
@ -63,4 +65,23 @@ final class SOFSegment {
marker & 0xff - 0xc0, marker, samplePrecision, lines, samplesPerLine, Arrays.toString(components) marker & 0xff - 0xc0, marker, samplePrecision, lines, samplesPerLine, Arrays.toString(components)
); );
} }
public static SOFSegment read(final int marker, final DataInput data) throws IOException {
int samplePrecision = data.readUnsignedByte();
int lines = data.readUnsignedShort();
int samplesPerLine = data.readUnsignedShort();
int componentsInFrame = data.readUnsignedByte();
SOFComponent[] components = new SOFComponent[componentsInFrame];
for (int i = 0; i < componentsInFrame; i++) {
int id = data.readUnsignedByte();
int sub = data.readUnsignedByte();
int qtSel = data.readUnsignedByte();
components[i] = new SOFComponent(id, ((sub & 0xF0) >> 4), (sub & 0xF), qtSel);
}
return new SOFSegment(marker, samplePrecision, lines, samplesPerLine, components);
}
} }

View File

@ -1,40 +0,0 @@
/*
* 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();
}

View File

@ -30,9 +30,9 @@
package com.twelvemonkeys.imageio.plugins.jpeg.lossless; package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException; import java.io.IOException;
public class FrameHeader { public class FrameHeader {
private ComponentSpec components[]; // Components private ComponentSpec components[]; // Components
@ -41,54 +41,42 @@ public class FrameHeader {
private int numComp; // Number of component in the frame private int numComp; // Number of component in the frame
private int precision; // Sample Precision (from the original image) private int precision; // Sample Precision (from the original image)
public ComponentSpec[] getComponents() { public ComponentSpec[] getComponents() {
return components.clone(); return components.clone();
} }
public int getDimX() { public int getDimX() {
return dimX; return dimX;
} }
public int getDimY() { public int getDimY() {
return dimY; return dimY;
} }
public int getNumComponents() { public int getNumComponents() {
return numComp; return numComp;
} }
public int getPrecision() { public int getPrecision() {
return precision; return precision;
} }
protected int read(final ImageInputStream data) throws IOException {
protected int read(final DataStream data) throws IOException {
int count = 0; int count = 0;
final int length = data.get16(); final int length = data.readUnsignedShort();
count += 2; count += 2;
precision = data.get8(); precision = data.readUnsignedByte();
count++; count++;
dimY = data.get16(); dimY = data.readUnsignedShort();
count += 2; count += 2;
dimX = data.get16(); dimX = data.readUnsignedShort();
count += 2; count += 2;
numComp = data.get8(); numComp = data.readUnsignedByte();
count++; count++;
//components = new ComponentSpec[numComp]; // some image exceed this range... //components = new ComponentSpec[numComp]; // some image exceed this range...
@ -99,14 +87,14 @@ public class FrameHeader {
throw new IOException("ERROR: frame format error"); throw new IOException("ERROR: frame format error");
} }
final int c = data.get8(); final int c = data.readUnsignedByte();
count++; count++;
if (count >= length) { if (count >= length) {
throw new IOException("ERROR: frame format error [c>=Lf]"); throw new IOException("ERROR: frame format error [c>=Lf]");
} }
final int temp = data.get8(); final int temp = data.readUnsignedByte();
count++; count++;
if (components[c] == null) { if (components[c] == null) {
@ -115,7 +103,7 @@ public class FrameHeader {
components[c].hSamp = temp >> 4; components[c].hSamp = temp >> 4;
components[c].vSamp = temp & 0x0F; components[c].vSamp = temp & 0x0F;
components[c].quantTableSel = data.get8(); components[c].quantTableSel = data.readUnsignedByte();
count++; count++;
} }

View File

@ -30,9 +30,9 @@
package com.twelvemonkeys.imageio.plugins.jpeg.lossless; package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException; import java.io.IOException;
public class HuffmanTable { public class HuffmanTable {
private final int l[][][] = new int[4][2][16]; private final int l[][][] = new int[4][2][16];
@ -42,8 +42,6 @@ public class HuffmanTable {
public static final int MSB = 0x80000000; public static final int MSB = 0x80000000;
public HuffmanTable() { public HuffmanTable() {
tc[0][0] = 0; tc[0][0] = 0;
tc[1][0] = 0; tc[1][0] = 0;
@ -59,15 +57,13 @@ public class HuffmanTable {
th[3] = 0; th[3] = 0;
} }
protected int read(final ImageInputStream data, final int[][][] HuffTab) throws IOException {
protected int read(final DataStream data, final int[][][] HuffTab) throws IOException {
int count = 0; int count = 0;
final int length = data.get16(); final int length = data.readUnsignedShort();
count += 2; count += 2;
while (count < length) { while (count < length) {
final int temp = data.get8(); final int temp = data.readUnsignedByte();
count++; count++;
final int t = temp & 0x0F; final int t = temp & 0x0F;
if (t > 3) { if (t > 3) {
@ -83,7 +79,7 @@ public class HuffmanTable {
tc[t][c] = 1; tc[t][c] = 1;
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
l[t][c][i] = data.get8(); l[t][c][i] = data.readUnsignedByte();
count++; count++;
} }
@ -92,7 +88,7 @@ public class HuffmanTable {
if (count > length) { if (count > length) {
throw new IOException("ERROR: Huffman table format error [count>Lh]"); throw new IOException("ERROR: Huffman table format error [count>Lh]");
} }
v[t][c][i][j] = data.get8(); v[t][c][i][j] = data.readUnsignedByte();
count++; count++;
} }
} }
@ -113,8 +109,6 @@ public class HuffmanTable {
return 1; return 1;
} }
// Build_HuffTab() // Build_HuffTab()
// Parameter: t table ID // Parameter: t table ID
// c table class ( 0 for DC, 1 for AC ) // c table class ( 0 for DC, 1 for AC )

View File

@ -30,13 +30,14 @@ package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
* THE SOFTWARE. * THE SOFTWARE.
*/ */
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
public class JPEGLosslessDecoder {
public class JPEGLosslessDecoder implements DataStream { private final ImageInputStream input;
private final ByteBuffer buffer;
private final FrameHeader frame; private final FrameHeader frame;
private final HuffmanTable huffTable; private final HuffmanTable huffTable;
private final QuantizationTable quantTable; private final QuantizationTable quantTable;
@ -49,7 +50,6 @@ public class JPEGLosslessDecoder implements DataStream {
private final int[] qTab[] = new int[10][]; // quantization table 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 boolean restarting;
private int dataBufferIndex;
private int marker; private int marker;
private int markerIndex; private int markerIndex;
private int numComp; private int numComp;
@ -64,109 +64,113 @@ public class JPEGLosslessDecoder implements DataStream {
private int[] outputGreenData; private int[] outputGreenData;
private int[] outputBlueData; 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, private static final int IDCT_P[] = {
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 }; 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,
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, 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
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 }; };
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_BEGIN = 0xFFD0;
public static final int RESTART_MARKER_END = 0xFFD7; public static final int RESTART_MARKER_END = 0xFFD7;
public static final int MAX_HUFFMAN_SUBTREE = 50; public static final int MAX_HUFFMAN_SUBTREE = 50;
public static final int MSB = 0x80000000; public static final int MSB = 0x80000000;
public int getDimX() { public int getDimX() {
return xDim; return xDim;
} }
public int getDimY() { public int getDimY() {
return yDim; return yDim;
} }
public JPEGLosslessDecoder(final byte[] data) { public JPEGLosslessDecoder(final ImageInputStream data) {
buffer = ByteBuffer.wrap(data); input = data;
frame = new FrameHeader(); frame = new FrameHeader();
scan = new ScanHeader(); scan = new ScanHeader();
quantTable = new QuantizationTable(); quantTable = new QuantizationTable();
huffTable = new HuffmanTable(); huffTable = new HuffmanTable();
} }
public int[][] decode() throws IOException { public int[][] decode() throws IOException {
int current, scanNum = 0; int current, scanNum = 0;
final int pred[] = new int[10]; final int pred[] = new int[10];
int[][] outputRef = null; int[][] outputRef;
xLoc = 0; xLoc = 0;
yLoc = 0; yLoc = 0;
current = get16(); current = input.readUnsignedShort();
if (current != 0xFFD8) { // SOI if (current != JPEG.SOI) { // SOI
throw new IOException("Not a JPEG file"); throw new IOException("Not a JPEG file");
} }
current = get16(); // TODO: Why the two loops?!
while (((current >> 4) != 0x0FFC) || (current == 0xFFC4)) { // SOF 0~15 current = input.readUnsignedShort();
while (((current >> 4) != 0x0FFC) || (current == JPEG.DHT)) { // SOF 0~15
switch (current) { switch (current) {
case 0xFFC4: // DHT case JPEG.DHT: // DHT
huffTable.read(this, HuffTab); huffTable.read(input, HuffTab);
break; break;
case 0xFFCC: // DAC case JPEG.DAC: // DAC
throw new IOException("Program doesn't support arithmetic coding. (format throw new IOException)"); throw new IOException("Program doesn't support arithmetic coding.");
case 0xFFDB: case JPEG.DQT:
quantTable.read(this, TABLE); quantTable.read(input, TABLE);
break; break;
case 0xFFDD: case JPEG.DRI:
restartInterval = readNumber(); restartInterval = readNumber();
break; break;
case 0xFFE0: case JPEG.APP0:
case 0xFFE1: case JPEG.APP1:
case 0xFFE2: case JPEG.APP2:
case 0xFFE3: case JPEG.APP3:
case 0xFFE4: case JPEG.APP4:
case 0xFFE5: case JPEG.APP5:
case 0xFFE6: case JPEG.APP6:
case 0xFFE7: case JPEG.APP7:
case 0xFFE8: case JPEG.APP8:
case 0xFFE9: case JPEG.APP9:
case 0xFFEA: case JPEG.APP10:
case 0xFFEB: case JPEG.APP11:
case 0xFFEC: case JPEG.APP12:
case 0xFFED: case JPEG.APP13:
case 0xFFEE: case JPEG.APP14:
case 0xFFEF: case JPEG.APP15:
readApp(); readApp();
break; break;
case 0xFFFE: case JPEG.COM:
readComment(); readComment();
break; break;
default: default:
if ((current >> 8) != 0xFF) { if ((current >> 8) != 0xFF) {
throw new IOException("ERROR: format throw new IOException! (decode)"); throw new IOException("JPEG Segment marker expected.");
} }
} }
current = get16(); current = input.readUnsignedShort();
} }
if ((current < 0xFFC0) || (current > 0xFFC7)) { if ((current < 0xFFC0) || (current > 0xFFC7)) {
throw new IOException("ERROR: could not handle arithmetic code!"); throw new IOException("ERROR: could not handle arithmetic code!");
} }
frame.read(this); frame.read(input);
current = get16(); current = input.readUnsignedShort();
do { do {
while (current != 0x0FFDA) { //SOS while (current != 0x0FFDA) { //SOS
switch (current) { switch (current) {
case 0xFFC4: //DHT case 0xFFC4: //DHT
huffTable.read(this, HuffTab); huffTable.read(input, HuffTab);
break; break;
case 0xFFCC: //DAC case 0xFFCC: //DAC
throw new IOException("Program doesn't support arithmetic coding. (format throw new IOException)"); throw new IOException("Program doesn't support arithmetic coding. (format throw new IOException)");
case 0xFFDB: case 0xFFDB:
quantTable.read(this, TABLE); quantTable.read(input, TABLE);
break; break;
case 0xFFDD: case 0xFFDD:
restartInterval = readNumber(); restartInterval = readNumber();
@ -198,20 +202,21 @@ public class JPEGLosslessDecoder implements DataStream {
} }
} }
current = get16(); current = input.readUnsignedShort();
} }
final int precision = frame.getPrecision(); final int precision = frame.getPrecision();
if (precision == 8) { if (precision == 8) {
mask = 0xFF; mask = 0xFF;
} else { }
else {
mask = 0xFFFF; mask = 0xFFFF;
} }
final ComponentSpec[] components = frame.getComponents(); final ComponentSpec[] components = frame.getComponents();
scan.read(this); readScan();
numComp = scan.getNumComponents(); numComp = scan.getNumComponents();
selection = scan.getSelection(); selection = scan.getSelection();
@ -234,7 +239,8 @@ public class JPEGLosslessDecoder implements DataStream {
if (numComp == 1) { if (numComp == 1) {
outputData = new int[xDim * yDim]; outputData = new int[xDim * yDim];
outputRef[0] = outputData; outputRef[0] = outputData;
} else { }
else {
outputRedData = new int[xDim * yDim]; // not a good use of memory, but I had trouble packing bytes into int. some values exceeded 255. 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]; outputGreenData = new int[xDim * yDim];
outputBlueData = new int[xDim * yDim]; outputBlueData = new int[xDim * yDim];
@ -281,64 +287,53 @@ public class JPEGLosslessDecoder implements DataStream {
if (markerIndex != 0) { if (markerIndex != 0) {
current = (0xFF00 | marker); current = (0xFF00 | marker);
markerIndex = 0; markerIndex = 0;
} else { }
current = get16(); else {
current = input.readUnsignedShort();
} }
} }
if ((current >= RESTART_MARKER_BEGIN) && (current <= RESTART_MARKER_END)) { if ((current >= RESTART_MARKER_BEGIN) && (current <= RESTART_MARKER_END)) {
//empty //empty
} else { }
else {
break; //current=MARKER break; //current=MARKER
} }
} }
if ((current == 0xFFDC) && (scanNum == 1)) { //DNL if ((current == JPEG.DNL) && (scanNum == 1)) { //DNL
readNumber(); readNumber();
current = get16(); current = input.readUnsignedShort();
} }
} while ((current != 0xFFD9) && ((xLoc < xDim) && (yLoc < yDim)) && (scanNum == 0)); } while ((current != JPEG.EOI) && ((xLoc < xDim) && (yLoc < yDim)) && (scanNum == 0));
return outputRef; return outputRef;
} }
private int readScan() throws IOException {
return scan.read(input);
@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 { private int decode(final int prev[], final int temp[], final int index[]) throws IOException {
if (numComp == 1) { if (numComp == 1) {
return decodeSingle(prev, temp, index); return decodeSingle(prev, temp, index);
} else if (numComp == 3) { }
else if (numComp == 3) {
return decodeRGB(prev, temp, index); return decodeRGB(prev, temp, index);
} else { }
else {
return -1; return -1;
} }
} }
private int decodeSingle(final int prev[], final int temp[], final int index[]) throws IOException { 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 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. // at the beginning of each restart interval the prediction value of 2P 1 is used, where P is the input precision.
if (restarting) { if (restarting) {
restarting = false; restarting = false;
prev[0] = (1 << (frame.getPrecision() - 1)); prev[0] = (1 << (frame.getPrecision() - 1));
} else { }
else {
switch (selection) { switch (selection) {
case 2: case 2:
prev[0] = getPreviousY(outputData); prev[0] = getPreviousY(outputData);
@ -384,8 +379,6 @@ public class JPEGLosslessDecoder implements DataStream {
return 0; return 0;
} }
private int decodeRGB(final int prev[], final int temp[], final int index[]) throws IOException { private int decodeRGB(final int prev[], final int temp[], final int index[]) throws IOException {
switch (selection) { switch (selection) {
case 2: case 2:
@ -459,7 +452,8 @@ public class JPEGLosslessDecoder implements DataStream {
if ((value >> 4) == 0) { if ((value >> 4) == 0) {
break; break;
} }
} else { }
else {
IDCT_Source[IDCT_P[j]] = getn(index, value & 0x0F, temp, index) * qtab[j]; IDCT_Source[IDCT_P[j]] = getn(index, value & 0x0F, temp, index) * qtab[j];
} }
} }
@ -469,8 +463,6 @@ public class JPEGLosslessDecoder implements DataStream {
return 0; 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 // 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. // 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. // HuffTab[*][*][0-256] is always the only 1st-layer table.
@ -508,15 +500,16 @@ public class JPEGLosslessDecoder implements DataStream {
if (index[0] < 8) { if (index[0] < 8) {
temp[0] <<= 8; temp[0] <<= 8;
input = get8(); input = this.input.readUnsignedByte();
if (input == 0xFF) { if (input == 0xFF) {
marker = get8(); marker = this.input.readUnsignedByte();
if (marker != 0) { if (marker != 0) {
markerIndex = 9; markerIndex = 9;
} }
} }
temp[0] |= input; temp[0] |= input;
} else { }
else {
index[0] -= 8; index[0] -= 8;
} }
@ -530,10 +523,10 @@ public class JPEGLosslessDecoder implements DataStream {
temp[0] &= (mask >> (16 - index[0])); temp[0] &= (mask >> (16 - index[0]));
temp[0] <<= 8; temp[0] <<= 8;
input = get8(); input = this.input.readUnsignedByte();
if (input == 0xFF) { if (input == 0xFF) {
marker = get8(); marker = this.input.readUnsignedByte();
if (marker != 0) { if (marker != 0) {
markerIndex = 9; markerIndex = 9;
} }
@ -559,8 +552,6 @@ public class JPEGLosslessDecoder implements DataStream {
return code & 0xFF; return code & 0xFF;
} }
private int getn(final int[] PRED, final int n, final int temp[], final int index[]) throws IOException { private int getn(final int[] PRED, final int n, final int temp[], final int index[]) throws IOException {
int result; int result;
final int one = 1; final int one = 1;
@ -575,7 +566,8 @@ public class JPEGLosslessDecoder implements DataStream {
if (n == 16) { if (n == 16) {
if (PRED[0] >= 0) { if (PRED[0] >= 0) {
return -32768; return -32768;
} else { }
else {
return 32768; return 32768;
} }
} }
@ -590,12 +582,13 @@ public class JPEGLosslessDecoder implements DataStream {
result = temp[0] >> index[0]; result = temp[0] >> index[0];
temp[0] &= (mask >> (16 - index[0])); temp[0] &= (mask >> (16 - index[0]));
} else { }
else {
temp[0] <<= 8; temp[0] <<= 8;
input = get8(); input = this.input.readUnsignedByte();
if (input == 0xFF) { if (input == 0xFF) {
marker = get8(); marker = this.input.readUnsignedByte();
if (marker != 0) { if (marker != 0) {
markerIndex = 9; markerIndex = 9;
} }
@ -611,10 +604,10 @@ public class JPEGLosslessDecoder implements DataStream {
} }
temp[0] <<= 8; temp[0] <<= 8;
input = get8(); input = this.input.readUnsignedByte();
if (input == 0xFF) { if (input == 0xFF) {
marker = get8(); marker = this.input.readUnsignedByte();
if (marker != 0) { if (marker != 0) {
markerIndex = 9; markerIndex = 9;
} }
@ -644,56 +637,49 @@ public class JPEGLosslessDecoder implements DataStream {
return result; return result;
} }
private int getPreviousX(final int data[]) { private int getPreviousX(final int data[]) {
if (xLoc > 0) { if (xLoc > 0) {
return data[((yLoc * xDim) + xLoc) - 1]; return data[((yLoc * xDim) + xLoc) - 1];
} else if (yLoc > 0) { }
else if (yLoc > 0) {
return getPreviousY(data); return getPreviousY(data);
} else { }
else {
return (1 << (frame.getPrecision() - 1)); return (1 << (frame.getPrecision() - 1));
} }
} }
private int getPreviousXY(final int data[]) { private int getPreviousXY(final int data[]) {
if ((xLoc > 0) && (yLoc > 0)) { if ((xLoc > 0) && (yLoc > 0)) {
return data[(((yLoc - 1) * xDim) + xLoc) - 1]; return data[(((yLoc - 1) * xDim) + xLoc) - 1];
} else { }
else {
return getPreviousY(data); return getPreviousY(data);
} }
} }
private int getPreviousY(final int data[]) { private int getPreviousY(final int data[]) {
if (yLoc > 0) { if (yLoc > 0) {
return data[((yLoc - 1) * xDim) + xLoc]; return data[((yLoc - 1) * xDim) + xLoc];
} else { }
else {
return getPreviousX(data); return getPreviousX(data);
} }
} }
private boolean isLastPixel() { private boolean isLastPixel() {
return (xLoc == (xDim - 1)) && (yLoc == (yDim - 1)); return (xLoc == (xDim - 1)) && (yLoc == (yDim - 1));
} }
private void output(final int PRED[]) { private void output(final int PRED[]) {
if (numComp == 1) { if (numComp == 1) {
outputSingle(PRED); outputSingle(PRED);
} else { }
else {
outputRGB(PRED); outputRGB(PRED);
} }
} }
private void outputSingle(final int PRED[]) { private void outputSingle(final int PRED[]) {
if ((xLoc < xDim) && (yLoc < yDim)) { if ((xLoc < xDim) && (yLoc < yDim)) {
outputData[(yLoc * xDim) + xLoc] = mask & PRED[0]; outputData[(yLoc * xDim) + xLoc] = mask & PRED[0];
@ -706,8 +692,6 @@ public class JPEGLosslessDecoder implements DataStream {
} }
} }
private void outputRGB(final int PRED[]) { private void outputRGB(final int PRED[]) {
if ((xLoc < xDim) && (yLoc < yDim)) { if ((xLoc < xDim) && (yLoc < yDim)) {
outputRedData[(yLoc * xDim) + xLoc] = PRED[0]; outputRedData[(yLoc * xDim) + xLoc] = PRED[0];
@ -722,58 +706,48 @@ public class JPEGLosslessDecoder implements DataStream {
} }
} }
private int readApp() throws IOException { private int readApp() throws IOException {
int count = 0; int count = 0;
final int length = get16(); final int length = input.readUnsignedShort();
count += 2; count += 2;
while (count < length) { while (count < length) {
get8(); input.readUnsignedByte();
count++; count++;
} }
return length; return length;
} }
private String readComment() throws IOException { private String readComment() throws IOException {
final StringBuffer sb = new StringBuffer(); final StringBuffer sb = new StringBuffer();
int count = 0; int count = 0;
final int length = get16(); final int length = input.readUnsignedShort();
count += 2; count += 2;
while (count < length) { while (count < length) {
sb.append((char) get8()); sb.append((char) input.readUnsignedByte());
count++; count++;
} }
return sb.toString(); return sb.toString();
} }
private int readNumber() throws IOException { private int readNumber() throws IOException {
final int Ld = get16(); final int Ld = input.readUnsignedShort();
if (Ld != 4) { if (Ld != 4) {
throw new IOException("ERROR: Define number format throw new IOException [Ld!=4]"); throw new IOException("ERROR: Define number format throw new IOException [Ld!=4]");
} }
return get16(); return input.readUnsignedShort();
} }
public int getNumComponents() { public int getNumComponents() {
return numComp; return numComp;
} }
public int getPrecision() { public int getPrecision() {
return frame.getPrecision(); return frame.getPrecision();
} }

View File

@ -1,9 +1,9 @@
package com.twelvemonkeys.imageio.plugins.jpeg.lossless; package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
import java.awt.image.BufferedImage; import javax.imageio.stream.ImageInputStream;
import java.awt.image.DataBufferByte; import java.awt.image.*;
import java.awt.image.DataBufferInt; import java.io.ByteArrayOutputStream;
import java.awt.image.DataBufferUShort; import java.io.DataInput;
import java.io.IOException; import java.io.IOException;
/** /**
@ -11,18 +11,17 @@ import java.io.IOException;
* containing a JPEGLossless to an BufferedImage. * containing a JPEGLossless to an BufferedImage.
* Therefore it uses the rii-mango JPEGLosslessDecoder * Therefore it uses the rii-mango JPEGLosslessDecoder
* Library ( https://github.com/rii-mango/JPEGLosslessDecoder ) * Library ( https://github.com/rii-mango/JPEGLosslessDecoder )
* * <p>
* Take care, that only the following lossless formats are supported * 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.57 JPEG Lossless, Nonhierarchical (Processes 14)
* 1.2.840.10008.1.2.4.70 JPEG Lossless, Nonhierarchical (Processes 14 [Selection 1]) * 1.2.840.10008.1.2.4.70 JPEG Lossless, Nonhierarchical (Processes 14 [Selection 1])
* * <p>
* Currently the following conversions are supported * Currently the following conversions are supported
* - 24Bit, RGB -> BufferedImage.TYPE_INT_RGB * - 24Bit, RGB -> BufferedImage.TYPE_INT_RGB
* - 8Bit, Grayscale -> BufferedImage.TYPE_BYTE_GRAY * - 8Bit, Grayscale -> BufferedImage.TYPE_BYTE_GRAY
* - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY * - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY
* *
* @author Hermann Kroll * @author Hermann Kroll
*
*/ */
public class JPEGLosslessDecoderWrapper { public class JPEGLosslessDecoderWrapper {
@ -34,21 +33,19 @@ public class JPEGLosslessDecoderWrapper {
* - 8Bit, Grayscale -> BufferedImage.TYPE_BYTE_GRAY * - 8Bit, Grayscale -> BufferedImage.TYPE_BYTE_GRAY
* - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY * - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY
* *
* @param data byte buffer which contains a jpeg lossless * @param input input stream which contains a jpeg lossless data
* @return if successfully a BufferedImage is returned * @return if successfully a BufferedImage is returned
* @throws IOException is thrown if the decoder failed or a conversion is not supported * @throws IOException is thrown if the decoder failed or a conversion is not supported
*/ */
public BufferedImage readImage(byte[] data) throws IOException{ public BufferedImage readImage(final ImageInputStream input) throws IOException {
JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(data); JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(input);
int[][] decoded = decoder.decode(); int[][] decoded = decoder.decode();
int width = decoder.getDimX(); int width = decoder.getDimX();
int height = decoder.getDimY(); int height = decoder.getDimY();
if (decoder.getNumComponents() == 1) { if (decoder.getNumComponents() == 1) {
switch(decoder.getPrecision()) switch (decoder.getPrecision()) {
{
case 8: case 8:
return read8Bit1ComponentGrayScale(decoded, width, height); return read8Bit1ComponentGrayScale(decoded, width, height);
case 16: case 16:
@ -59,9 +56,8 @@ public class JPEGLosslessDecoderWrapper {
} }
//rgb //rgb
if (decoder.getNumComponents() == 3) { if (decoder.getNumComponents() == 3) {
switch(decoder.getPrecision()) switch (decoder.getPrecision()) {
{ case 8:
case 24:
return read24Bit3ComponentRGB(decoded, width, height); return read24Bit3ComponentRGB(decoded, width, height);
default: default:
@ -73,9 +69,14 @@ public class JPEGLosslessDecoderWrapper {
} }
public Raster readRaster(final ImageInputStream input) throws IOException {
return readImage(input).getRaster();
}
/** /**
* converts the decoded buffer into a BufferedImage * converts the decoded buffer into a BufferedImage
* precision: 16 bit, componentCount = 1 * precision: 16 bit, componentCount = 1
*
* @param decoded data buffer * @param decoded data buffer
* @param width of the image * @param width of the image
* @param height of the image * @param height of the image
@ -90,9 +91,11 @@ public class JPEGLosslessDecoderWrapper {
} }
return image; return image;
} }
/** /**
* converts the decoded buffer into a BufferedImage * converts the decoded buffer into a BufferedImage
* precision: 8 bit, componentCount = 1 * precision: 8 bit, componentCount = 1
*
* @param decoded data buffer * @param decoded data buffer
* @param width of the image * @param width of the image
* @param height of the image * @param height of the image
@ -111,12 +114,14 @@ public class JPEGLosslessDecoderWrapper {
/** /**
* converts the decoded buffer into a BufferedImage * converts the decoded buffer into a BufferedImage
* precision: 24 bit, componentCount = 3 * precision: 24 bit, componentCount = 3
*
* @param decoded data buffer * @param decoded data buffer
* @param width of the image * @param width of the image
* @param height of the image * @param height of the image
* @return a BufferedImage.TYPE_INT_RGB * @return a BufferedImage.TYPE_INT_RGB
*/ */
private BufferedImage read24Bit3ComponentRGB(int[][] decoded, int width, int height) { private BufferedImage read24Bit3ComponentRGB(int[][] decoded, int width, int height) {
// TODO: Consider 3_BYTE_BGR as this is more common?
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int[] imageBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); int[] imageBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

View File

@ -30,9 +30,9 @@
package com.twelvemonkeys.imageio.plugins.jpeg.lossless; package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException; import java.io.IOException;
public class QuantizationTable { public class QuantizationTable {
private final int precision[] = new int[4]; // Quantization precision 8 or 16 private final int precision[] = new int[4]; // Quantization precision 8 or 16
@ -40,8 +40,6 @@ public class QuantizationTable {
protected final int quantTables[][] = new int[4][64]; // Tables protected final int quantTables[][] = new int[4][64]; // Tables
public QuantizationTable() { public QuantizationTable() {
tq[0] = 0; tq[0] = 0;
tq[1] = 0; tq[1] = 0;
@ -49,15 +47,13 @@ public class QuantizationTable {
tq[3] = 0; tq[3] = 0;
} }
protected int read(final ImageInputStream data, final int[] table) throws IOException {
protected int read(final DataStream data, final int[] table) throws IOException {
int count = 0; int count = 0;
final int length = data.get16(); final int length = data.readUnsignedShort();
count += 2; count += 2;
while (count < length) { while (count < length) {
final int temp = data.get8(); final int temp = data.readUnsignedByte();
count++; count++;
final int t = temp & 0x0F; final int t = temp & 0x0F;
@ -69,9 +65,11 @@ public class QuantizationTable {
if (precision[t] == 0) { if (precision[t] == 0) {
precision[t] = 8; precision[t] = 8;
} else if (precision[t] == 1) { }
else if (precision[t] == 1) {
precision[t] = 16; precision[t] = 16;
} else { }
else {
throw new IOException("ERROR: Quantization table precision error"); throw new IOException("ERROR: Quantization table precision error");
} }
@ -83,18 +81,19 @@ public class QuantizationTable {
throw new IOException("ERROR: Quantization table format error"); throw new IOException("ERROR: Quantization table format error");
} }
quantTables[t][i] = data.get8(); quantTables[t][i] = data.readUnsignedByte();
count++; count++;
} }
enhanceQuantizationTable(quantTables[t], table); enhanceQuantizationTable(quantTables[t], table);
} else { }
else {
for (int i = 0; i < 64; i++) { for (int i = 0; i < 64; i++) {
if (count > length) { if (count > length) {
throw new IOException("ERROR: Quantization table format error"); throw new IOException("ERROR: Quantization table format error");
} }
quantTables[t][i] = data.get16(); quantTables[t][i] = data.readUnsignedShort();
count += 2; count += 2;
} }
@ -109,8 +108,6 @@ public class QuantizationTable {
return 1; return 1;
} }
private void enhanceQuantizationTable(final int qtab[], final int[] table) { private void enhanceQuantizationTable(final int qtab[], final int[] table) {
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
qtab[table[(0 * 8) + i]] *= 90; qtab[table[(0 * 8) + i]] *= 90;

View File

@ -36,38 +36,26 @@ public class ScanComponent {
private int dcTabSel; // DC table selector private int dcTabSel; // DC table selector
private int scanCompSel; // Scan component selector private int scanCompSel; // Scan component selector
public int getAcTabSel() { public int getAcTabSel() {
return acTabSel; return acTabSel;
} }
public int getDcTabSel() { public int getDcTabSel() {
return dcTabSel; return dcTabSel;
} }
public int getScanCompSel() { public int getScanCompSel() {
return scanCompSel; return scanCompSel;
} }
public void setAcTabSel(final int acTabSel) { public void setAcTabSel(final int acTabSel) {
this.acTabSel = acTabSel; this.acTabSel = acTabSel;
} }
public void setDcTabSel(final int dcTabSel) { public void setDcTabSel(final int dcTabSel) {
this.dcTabSel = dcTabSel; this.dcTabSel = dcTabSel;
} }
public void setScanCompSel(final int scanCompSel) { public void setScanCompSel(final int scanCompSel) {
this.scanCompSel = scanCompSel; this.scanCompSel = scanCompSel;
} }

View File

@ -30,9 +30,9 @@
package com.twelvemonkeys.imageio.plugins.jpeg.lossless; package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException; import java.io.IOException;
public class ScanHeader { public class ScanHeader {
private int ah; private int ah;
@ -43,68 +43,48 @@ public class ScanHeader {
protected ScanComponent components[]; protected ScanComponent components[];
public int getAh() { public int getAh() {
return ah; return ah;
} }
public int getAl() { public int getAl() {
return al; return al;
} }
public int getNumComponents() { public int getNumComponents() {
return numComp; return numComp;
} }
public int getSelection() { public int getSelection() {
return selection; return selection;
} }
public int getSpectralEnd() { public int getSpectralEnd() {
return spectralEnd; return spectralEnd;
} }
public void setAh(final int ah) { public void setAh(final int ah) {
this.ah = ah; this.ah = ah;
} }
public void setAl(final int al) { public void setAl(final int al) {
this.al = al; this.al = al;
} }
public void setSelection(final int selection) { public void setSelection(final int selection) {
this.selection = selection; this.selection = selection;
} }
public void setSpectralEnd(final int spectralEnd) { public void setSpectralEnd(final int spectralEnd) {
this.spectralEnd = spectralEnd; this.spectralEnd = spectralEnd;
} }
protected int read(final ImageInputStream data) throws IOException {
protected int read(final DataStream data) throws IOException {
int count = 0; int count = 0;
final int length = data.get16(); final int length = data.readUnsignedShort();
count += 2; count += 2;
numComp = data.get8(); numComp = data.readUnsignedByte();
count++; count++;
components = new ScanComponent[numComp]; components = new ScanComponent[numComp];
@ -116,23 +96,23 @@ public class ScanHeader {
throw new IOException("ERROR: scan header format error"); throw new IOException("ERROR: scan header format error");
} }
components[i].setScanCompSel(data.get8()); components[i].setScanCompSel(data.readUnsignedByte());
count++; count++;
final int temp = data.get8(); final int temp = data.readUnsignedByte();
count++; count++;
components[i].setDcTabSel(temp >> 4); components[i].setDcTabSel(temp >> 4);
components[i].setAcTabSel(temp & 0x0F); components[i].setAcTabSel(temp & 0x0F);
} }
setSelection(data.get8()); setSelection(data.readUnsignedByte());
count++; count++;
setSpectralEnd(data.get8()); setSpectralEnd(data.readUnsignedByte());
count++; count++;
final int temp = data.get8(); final int temp = data.readUnsignedByte();
setAh(temp >> 4); setAh(temp >> 4);
setAl(temp & 0x0F); setAl(temp & 0x0F);
count++; count++;

View File

@ -94,7 +94,8 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
new TestData(getClassLoaderResource("/jpeg/jfif-padded-segments.jpg"), new Dimension(20, 45)), new TestData(getClassLoaderResource("/jpeg/jfif-padded-segments.jpg"), new Dimension(20, 45)),
new TestData(getClassLoaderResource("/jpeg/0x00-to-0xFF-between-segments.jpg"), new Dimension(16, 16)), new TestData(getClassLoaderResource("/jpeg/0x00-to-0xFF-between-segments.jpg"), new Dimension(16, 16)),
new TestData(getClassLoaderResource("/jpeg/jfif-bogus-empty-jfif-segment.jpg"), new Dimension(942, 714)), new TestData(getClassLoaderResource("/jpeg/jfif-bogus-empty-jfif-segment.jpg"), new Dimension(942, 714)),
new TestData(getClassLoaderResource("/jpeg/jfif-16bit-dqt.jpg"), new Dimension(204, 131)) new TestData(getClassLoaderResource("/jpeg/jfif-16bit-dqt.jpg"), new Dimension(204, 131)),
new TestData(getClassLoaderResource("/jpeg-lossless/testimgl.jpg"), new Dimension(227, 149))
); );
// More test data in specific tests below // More test data in specific tests below
@ -1175,6 +1176,7 @@ public class JPEGImageReaderTest extends ImageReaderAbstractTest<JPEGImageReader
} }
} }
catch (IIOException e) { catch (IIOException e) {
e.printStackTrace();
fail(String.format("Reading metadata failed for %s image %s: %s", testData, i, e.getMessage())); fail(String.format("Reading metadata failed for %s image %s: %s", testData, i, e.getMessage()));
} }
} }

View File

@ -49,6 +49,22 @@ public interface JPEG {
/** Define Huffman Tables segment marker (DHT). */ /** Define Huffman Tables segment marker (DHT). */
int DHT = 0xFFC4; int DHT = 0xFFC4;
/** Comment (COM) */
int COM = 0xFFFE;
/** Define Number of Lines (DNL). */
int DNL = 0xFFDC;
/** Define Restart Interval (DRI). */
int DRI = 0xFFDD;
/** Define Hierarchical Progression (DHP). */
int DHP = 0xFFDE;
/** Expand reference components (EXP). */
int EXP = 0xFFDF;
/** Temporary use in arithmetic coding (TEM). */
int TEM = 0xFF01;
/** Define Define Arithmetic Coding conditioning (DAC). */
int DAC = 0xFFCC;
// App segment markers (APPn). // App segment markers (APPn).
int APP0 = 0xFFE0; int APP0 = 0xFFE0;
int APP1 = 0xFFE1; int APP1 = 0xFFE1;

View File

@ -43,7 +43,7 @@ import java.util.Arrays;
public final class JPEGSegment implements Serializable { public final class JPEGSegment implements Serializable {
final int marker; final int marker;
final byte[] data; final byte[] data;
final int length; private final int length;
private transient String id; private transient String id;