#182 Moved lossless support classes to main package for better encapsulation, sorted out license issues.

This commit is contained in:
Harald Kuhr
2016-09-09 13:13:45 +02:00
parent 673f3e5b53
commit 7d35400595
27 changed files with 612 additions and 1088 deletions
@@ -40,17 +40,17 @@ import java.io.IOException;
* @author last modified by $Author: haraldk$
* @version $Id: AdobeDCTSegment.java,v 1.0 23.04.12 16:55 haraldk Exp$
*/
final class AdobeDCT extends AppSegment {
public static final int Unknown = 0;
public static final int YCC = 1;
public static final int YCCK = 2;
final class AdobeDCT extends Application {
static final int Unknown = 0;
static final int YCC = 1;
static final int YCCK = 2;
final int version;
final int flags0;
final int flags1;
final int transform;
AdobeDCT(int version, int flags0, int flags1, int transform) {
private AdobeDCT(int version, int flags0, int flags1, int transform) {
super(JPEG.APP14, "Adobe", new byte[]{'A', 'd', 'o', 'b', 'e', 0, (byte) version, (byte) (flags0 >> 8), (byte) (flags0 & 0xff), (byte) (flags1 >> 8), (byte) (flags1 & 0xff), (byte) transform});
this.version = version; // 100 or 101
@@ -1,70 +0,0 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.lang.Validate;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
/**
* AppSegment.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: harald.kuhr$
* @version $Id: AppSegment.java,v 1.0 22/08/16 harald.kuhr Exp$
*/
class AppSegment extends Segment {
final String identifier;
final byte[] data;
AppSegment(int marker, final String identifier, final byte[] data) {
super(marker);
this.identifier = Validate.notEmpty(identifier, "identifier");
this.data = data;
}
protected AppSegment(int marker, final String identifier) {
this(marker, identifier, null);
}
InputStream data() {
int offset = identifier.length() + 1;
return new ByteArrayInputStream(data, offset, data.length - offset);
}
public static AppSegment read(final int marker, final String identifier, final DataInput data, final int length) throws IOException {
switch (marker) {
case JPEG.APP0:
// JFIF
if ("JFIF".equals(identifier)) {
return JFIF.read(data, length);
}
case JPEG.APP1:
// JFXX
if ("JFXX".equals(identifier)) {
return JFXX.read(data, length);
}
// TODO: Exif?
case JPEG.APP2:
// ICC_PROFILE
if ("ICC_PROFILE".equals(identifier)) {
return ICCProfile.read(data, length);
}
case JPEG.APP14:
// Adobe
if ("Adobe".equals(identifier)) {
return AdobeDCT.read(data, length);
}
default:
// Generic APPn segment
byte[] bytes = new byte[length - 2];
data.readFully(bytes);
return new AppSegment(marker, identifier, bytes);
}
}
}
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2016, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.lang.Validate;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
/**
* Application.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: harald.kuhr$
* @version $Id: Application.java,v 1.0 22/08/16 harald.kuhr Exp$
*/
class Application extends Segment {
final String identifier;
final byte[] data;
Application(int marker, final String identifier, final byte[] data) {
super(marker);
this.identifier = Validate.notEmpty(identifier, "identifier");
this.data = data;
}
InputStream data() {
int offset = identifier.length() + 1;
return new ByteArrayInputStream(data, offset, data.length - offset);
}
@Override
public String toString() {
return "APP" + (marker & 0x0f) + "/" + identifier + "[length: " + data.length + "]";
}
public static Application read(final int marker, final String identifier, final DataInput data, final int length) throws IOException {
switch (marker) {
case JPEG.APP0:
// JFIF
if ("JFIF".equals(identifier)) {
return JFIF.read(data, length);
}
case JPEG.APP1:
// JFXX
if ("JFXX".equals(identifier)) {
return JFXX.read(data, length);
}
// TODO: Exif?
case JPEG.APP2:
// ICC_PROFILE
if ("ICC_PROFILE".equals(identifier)) {
return ICCProfile.read(data, length);
}
case JPEG.APP14:
// Adobe
if ("Adobe".equals(identifier)) {
return AdobeDCT.read(data, length);
}
default:
// Generic APPn segment
byte[] bytes = new byte[length - 2];
data.readFully(bytes);
return new Application(marker, identifier, bytes);
}
}
}
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2016, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
@@ -21,10 +49,15 @@ class Comment extends Segment {
this.comment = comment;
}
@Override
public String toString() {
return "COM[" + comment + "]";
}
public static Segment read(final DataInput data, final int length) throws IOException {
byte[] ascii = new byte[length];
byte[] ascii = new byte[length - 2];
data.readFully(ascii);
return new Comment(new String(ascii, StandardCharsets.UTF_8));
return new Comment(new String(ascii, StandardCharsets.UTF_8)); // Strictly, it is ASCII, but UTF-8 is compatible
}
}
@@ -51,7 +51,7 @@ final class Frame extends Segment {
final Component[] components; // Components specifications
Frame(final int marker, final int samplePrecision, final int lines, final int samplesPerLine, final Component[] components) {
private Frame(final int marker, final int samplePrecision, final int lines, final int samplesPerLine, final Component[] components) {
super(marker);
this.samplePrecision = samplePrecision;
@@ -116,7 +116,7 @@ final class Frame extends Segment {
return read(marker, new SubImageInputStream(data, length), length);
}
static final class Component {
public static final class Component {
final int id;
final int hSub; // Horizontal sampling factor
final int vSub; // Vertical sampling factor
@@ -1,37 +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.
* Copyright (c) 2016, Harald Kuhr
* Copyright (C) 2015, Michael Martinez
* Copyright (C) 2004, Helmut Dersch
* All rights reserved.
*
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
*/
/*
* Copyright (C) Helmut Dersch
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.io.DataInput;
import java.io.IOException;
@@ -45,7 +46,7 @@ final class HuffmanTable extends Segment {
public static final int MSB = 0x80000000;
public HuffmanTable() {
private HuffmanTable() {
super(JPEG.DHT);
tc[0][0] = 0;
@@ -109,7 +110,7 @@ final class HuffmanTable extends Segment {
}
if (k >= 256) {
if (k > 256) {
throw new IOException("Huffman table error");
throw new IIOException("JPEG Huffman Table error");
}
k = 0;
@@ -119,9 +120,14 @@ final class HuffmanTable extends Segment {
}
}
public static Segment read(DataInput data, int length) throws IOException {
int count = 0;
count += 2;
@Override
public String toString() {
// TODO: Id and class for tables
return "DHT[]";
}
public static Segment read(final DataInput data, final int length) throws IOException {
int count = 2;
HuffmanTable table = new HuffmanTable();
@@ -130,12 +136,12 @@ final class HuffmanTable extends Segment {
count++;
int t = temp & 0x0F;
if (t > 3) {
throw new IOException("Huffman table Id > 3:" + t);
throw new IIOException("Unexpected JPEG Huffman Table Id (> 3):" + t);
}
int c = temp >> 4;
if (c > 2) {
throw new IOException("Huffman table class > 2: " + c);
throw new IIOException("Unexpected JPEG Huffman Table class (> 2): " + c);
}
table.th[t] = 1;
@@ -149,7 +155,7 @@ final class HuffmanTable extends Segment {
for (int i = 0; i < 16; i++) {
for (int j = 0; j < table.l[t][c][i]; j++) {
if (count > length) {
throw new IOException("Huffman table format error [count>Lh]");
throw new IIOException("JPEG Huffman Table format error");
}
table.v[t][c][i][j] = data.readUnsignedByte();
count++;
@@ -158,18 +164,9 @@ final class HuffmanTable extends Segment {
}
if (count != length) {
throw new IOException("Huffman table format error [count!=Lf]");
throw new IIOException("JPEG Huffman Table format error, bad segment length: " + length);
}
// 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;
return table;
}
}
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2016, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
@@ -12,12 +40,18 @@ import java.io.IOException;
* @author last modified by $Author: harald.kuhr$
* @version $Id: ICCProfile.java,v 1.0 22/08/16 harald.kuhr Exp$
*/
final class ICCProfile extends AppSegment {
protected ICCProfile(final byte[] data) {
final class ICCProfile extends Application {
private ICCProfile(final byte[] data) {
super(JPEG.APP2, "ICC_PROFILE", data);
}
// TODO: Create util method to concat all ICC segments to one and return ICC_Profile (move from JPEGImageReader)
// If so, how to deal with warnings from the original code? Throw exceptions instead?
@Override
public String toString() {
return "ICC_PROFILE[" + data[0] + "/" + data[1] + " length: " + data.length + "]";
}
public static ICCProfile read(DataInput data, int length) throws IOException {
byte[] bytes = new byte[length - 2];
@@ -32,7 +32,6 @@ import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* JFIFSegment
@@ -41,7 +40,7 @@ import java.util.Arrays;
* @author last modified by $Author: haraldk$
* @version $Id: JFIFSegment.java,v 1.0 23.04.12 16:52 haraldk Exp$
*/
final class JFIF extends AppSegment {
final class JFIF extends Application {
final int majorVersion;
final int minorVersion;
final int units;
@@ -29,9 +29,7 @@
package com.twelvemonkeys.imageio.plugins.jpeg;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
@@ -41,7 +39,7 @@ import java.util.Arrays;
* @author last modified by $Author: haraldk$
* @version $Id: JFXXSegment.java,v 1.0 23.04.12 16:54 haraldk Exp$
*/
final class JFXX extends AppSegment {
final class JFXX extends Application {
public static final int JPEG = 0x10;
public static final int INDEXED = 0x11;
public static final int RGB = 0x13;
@@ -2,13 +2,9 @@ package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.AbstractMetadata;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEGSegment;
import org.w3c.dom.Node;
import javax.imageio.metadata.IIOMetadataNode;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
@@ -95,10 +91,10 @@ class JPEGImage10Metadata extends AbstractMetadata {
Scan scan = (Scan) segment;
IIOMetadataNode sos = new IIOMetadataNode("sos");
sos.setAttribute("numScanComponents", String.valueOf(scan.components.length));
sos.setAttribute("startSpectralSelection", String.valueOf(scan.selection));
sos.setAttribute("endSpectralSelection", String.valueOf(scan.spectralEnd));
sos.setAttribute("approxHigh", String.valueOf(scan.ah));
sos.setAttribute("approxLow", String.valueOf(scan.al));
sos.setAttribute("startSpectralSelection", String.valueOf(scan.spectralSelStart));
sos.setAttribute("endSpectralSelection", String.valueOf(scan.spectralSelEnd));
sos.setAttribute("approxHigh", String.valueOf(scan.approxHigh));
sos.setAttribute("approxLow", String.valueOf(scan.approxLow));
for (Scan.Component component : scan.components) {
IIOMetadataNode spec = new IIOMetadataNode("scanComponentSpec");
@@ -135,7 +131,7 @@ class JPEGImage10Metadata extends AbstractMetadata {
default:
IIOMetadataNode unknown = new IIOMetadataNode("unknown");
unknown.setAttribute("MarkerTag", String.valueOf(segment.marker & 0xFF));
unknown.setUserObject(((AppSegment) segment).data);
unknown.setUserObject(((Application) segment).data);
markerSequence.appendChild(unknown);
break;
@@ -10,10 +10,7 @@ import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import java.awt.color.ICC_Profile;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
/**
@@ -39,7 +36,7 @@ final class JPEGImage10MetadataCleaner {
IIOMetadata cleanMetadata(final IIOMetadata imageMetadata) throws IOException {
// We filter out pretty much everything from the stream..
// Meaning we have to read get *all APP segments* and re-insert into metadata.
List<AppSegment> appSegments = reader.getAppSegments(JPEGImageReader.ALL_APP_MARKERS, null);
List<Application> appSegments = reader.getAppSegments(JPEGImageReader.ALL_APP_MARKERS, null);
// NOTE: There's a bug in the merging code in JPEGMetadata mergeUnknownNode that makes sure all "unknown" nodes are added twice in certain conditions.... ARGHBL...
// DONE: 1: Work around
@@ -176,7 +173,7 @@ final class JPEGImage10MetadataCleaner {
}
Node next = null;
for (AppSegment segment : appSegments) {
for (Application segment : appSegments) {
// Except real app0JFIF, app0JFXX, app2ICC and app14Adobe, add all the app segments that we filtered away as "unknown" markers
if (segment.marker == JPEG.APP0 && "JFIF".equals(segment.identifier) && hasRealJFIF) {
continue;
@@ -39,7 +39,6 @@ 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.JPEGLosslessDecoderWrapper;
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
import com.twelvemonkeys.lang.Validate;
@@ -110,7 +109,7 @@ public class JPEGImageReader extends ImageReaderBase {
static final int ALL_APP_MARKERS = -1;
/** Segment identifiers for the JPEG segments we care about reading. */
private static final Map<Integer, List<String>> SEGMENT_IDENTIFIERS = createSegmentIds();
private static final Map<Integer, List<String>> SEGMENT_IDENTIFIERS = JPEGSegmentUtil.ALL_SEGMENTS; //createSegmentIds();
private static Map<Integer, List<String>> createSegmentIds() {
Map<Integer, List<String>> map = new LinkedHashMap<>();
@@ -386,7 +385,7 @@ public class JPEGImageReader extends ImageReaderBase {
// TODO: What about stream position?
// TODO: Param handling: Source region, offset, subsampling, destination, destination type, etc....
// Read image as lossless
return new JPEGLosslessDecoderWrapper().readImage(imageInput);
return new JPEGLosslessDecoderWrapper().readImage(segments, imageInput);
}
// We need to apply ICC profile unless the profile is sRGB/default gray (whatever that is)
@@ -592,25 +591,25 @@ public class JPEGImageReader extends ImageReaderBase {
if (adobeDCT != null) {
switch (adobeDCT.transform) {
case AdobeDCT.YCC:
if (startOfFrame.components.length != 3) {
if (startOfFrame.componentsInFrame() != 3) {
// This probably means the Adobe marker is bogus
break;
}
return JPEGColorSpace.YCbCr;
case AdobeDCT.YCCK:
if (startOfFrame.components.length != 4) {
if (startOfFrame.componentsInFrame() != 4) {
// This probably means the Adobe marker is bogus
break;
}
return JPEGColorSpace.YCCK;
case AdobeDCT.Unknown:
if (startOfFrame.components.length == 1) {
if (startOfFrame.componentsInFrame() == 1) {
return JPEGColorSpace.Gray;
}
else if (startOfFrame.components.length == 3) {
else if (startOfFrame.componentsInFrame() == 3) {
return JPEGColorSpace.RGB;
}
else if (startOfFrame.components.length == 4) {
else if (startOfFrame.componentsInFrame() == 4) {
return JPEGColorSpace.CMYK;
}
// Else fall through
@@ -619,7 +618,7 @@ public class JPEGImageReader extends ImageReaderBase {
}
// TODO: We should probably allow component ids out of order (ie. BGR or KMCY)...
switch (startOfFrame.components.length) {
switch (startOfFrame.componentsInFrame()) {
case 1:
return JPEGColorSpace.Gray;
case 2:
@@ -712,7 +711,7 @@ public class JPEGImageReader extends ImageReaderBase {
if (segments == null) {
long start = DEBUG ? System.currentTimeMillis() : 0;
// TODO: Consider just reading the segments here, for better performance...
// TODO: Consider just reading the segments directly, for better performance...
List<JPEGSegment> jpegSegments = readSegments();
List<Segment> segments = new ArrayList<>(jpegSegments.size());
@@ -761,20 +760,20 @@ public class JPEGImageReader extends ImageReaderBase {
return Collections.emptyList();
}
List<AppSegment> getAppSegments(final int marker, final String identifier) throws IOException {
List<Application> getAppSegments(final int marker, final String identifier) throws IOException {
initHeader();
List<AppSegment> appSegments = Collections.emptyList();
List<Application> appSegments = Collections.emptyList();
for (Segment segment : segments) {
if (segment instanceof AppSegment
if (segment instanceof Application
&& (marker == ALL_APP_MARKERS || marker == segment.marker)
&& (identifier == null || identifier.equals(((AppSegment) segment).identifier))) {
&& (identifier == null || identifier.equals(((Application) segment).identifier))) {
if (appSegments == Collections.EMPTY_LIST) {
appSegments = new ArrayList<>(segments.size());
}
appSegments.add((AppSegment) segment);
appSegments.add((Application) segment);
}
}
@@ -794,26 +793,26 @@ public class JPEGImageReader extends ImageReaderBase {
}
AdobeDCT getAdobeDCT() throws IOException {
List<AppSegment> adobe = getAppSegments(JPEG.APP14, "Adobe");
List<Application> adobe = getAppSegments(JPEG.APP14, "Adobe");
return adobe.isEmpty() ? null : (AdobeDCT) adobe.get(0);
}
JFIF getJFIF() throws IOException{
List<AppSegment> jfif = getAppSegments(JPEG.APP0, "JFIF");
List<Application> jfif = getAppSegments(JPEG.APP0, "JFIF");
return jfif.isEmpty() ? null : (JFIF) jfif.get(0);
}
JFXX getJFXX() throws IOException {
List<AppSegment> jfxx = getAppSegments(JPEG.APP0, "JFXX");
List<Application> jfxx = getAppSegments(JPEG.APP0, "JFXX");
return jfxx.isEmpty() ? null : (JFXX) jfxx.get(0);
}
private CompoundDirectory getExif() throws IOException {
List<AppSegment> exifSegments = getAppSegments(JPEG.APP1, "Exif");
List<Application> exifSegments = getAppSegments(JPEG.APP1, "Exif");
if (!exifSegments.isEmpty()) {
AppSegment exif = exifSegments.get(0);
Application exif = exifSegments.get(0);
InputStream data = exif.data();
if (data.read() == -1) { // Read pad
@@ -849,13 +848,13 @@ public class JPEGImageReader extends ImageReaderBase {
// TODO: Allow metadata to contain the wrongly indexed profiles, if readable
// NOTE: We ignore any profile with wrong index for reading and image types, just to be on the safe side
List<AppSegment> segments = getAppSegments(JPEG.APP2, "ICC_PROFILE");
List<Application> segments = getAppSegments(JPEG.APP2, "ICC_PROFILE");
// TODO: Possibly move this logic to the ICCProfile class...
if (segments.size() == 1) {
// Faster code for the common case
AppSegment segment = segments.get(0);
Application segment = segments.get(0);
DataInputStream stream = new DataInputStream(segment.data());
int chunkNumber = stream.readUnsignedByte();
int chunkCount = stream.readUnsignedByte();
@@ -945,7 +944,7 @@ public class JPEGImageReader extends ImageReaderBase {
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 new JPEGLosslessDecoderWrapper().readRaster(segments, imageInput);
}
return delegate.readRaster(imageIndex, param);
@@ -1001,9 +1000,9 @@ public class JPEGImageReader extends ImageReaderBase {
}
// Read Exif thumbnails if present
List<AppSegment> exifSegments = getAppSegments(JPEG.APP1, "Exif");
List<Application> exifSegments = getAppSegments(JPEG.APP1, "Exif");
if (!exifSegments.isEmpty()) {
AppSegment exif = exifSegments.get(0);
Application exif = exifSegments.get(0);
InputStream data = exif.data();
if (data.read() == -1) {
@@ -1,50 +1,51 @@
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.
* Copyright (c) 2016, Harald Kuhr
* Copyright (C) 2015, Michael Martinez
* Copyright (C) 2004, Helmut Dersch
* All rights reserved.
*
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* 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;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import com.twelvemonkeys.lang.Validate;
import javax.imageio.IIOException;
import javax.imageio.stream.ImageInputStream;
import java.io.IOException;
import java.util.List;
public class JPEGLosslessDecoder {
final class JPEGLosslessDecoder {
private final ImageInputStream input;
// TODO: Merge these classes with similar classes from the main package
// (FrameHeader == Frame, ComponentSpec == Frame.Component, ScanHeader == Scan etc)
private final FrameHeader frame;
private final Frame frame;
private final HuffmanTable huffTable;
private final QuantizationTable quantTable;
private final ScanHeader scan;
private Scan scan;
private final int HuffTab[][][] = new int[4][2][MAX_HUFFMAN_SUBTREE * 256];
private final int IDCT_Source[] = new int[64];
@@ -69,36 +70,65 @@ public class JPEGLosslessDecoder {
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
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
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;
private static final int RESTART_MARKER_BEGIN = 0xFFD0;
private static final int RESTART_MARKER_END = 0xFFD7;
private static final int MAX_HUFFMAN_SUBTREE = 50;
private static final int MSB = 0x80000000;
public int getDimX() {
int getDimX() {
return xDim;
}
public int getDimY() {
int getDimY() {
return yDim;
}
public JPEGLosslessDecoder(final ImageInputStream data) {
JPEGLosslessDecoder(final List<Segment> segments, final ImageInputStream data) {
Validate.notNull(segments);
frame = get(segments, Frame.class);
scan = get(segments, Scan.class);
QuantizationTable qt = get(segments, QuantizationTable.class);
quantTable = qt != null ? qt : new QuantizationTable(); // For lossless there are no DQTs
huffTable = get(segments, HuffmanTable.class); // For non-lossless there can be multiple of DHTs
RestartInterval dri = get(segments, RestartInterval.class);
restartInterval = dri != null ? dri.interval : 0;
input = data;
frame = new FrameHeader();
scan = new ScanHeader();
quantTable = new QuantizationTable();
huffTable = new HuffmanTable();
}
public int[][] decode() throws IOException {
private <T> T get(final List<Segment> segments, final Class<T> type) {
for (Segment segment : segments) {
if (type.isInstance(segment)) {
return type.cast(segment);
}
}
return null;
}
int[][] decode() throws IOException {
int current, scanNum = 0;
final int pred[] = new int[10];
int[][] outputRef;
@@ -108,108 +138,22 @@ public class JPEGLosslessDecoder {
current = input.readUnsignedShort();
if (current != JPEG.SOI) { // SOI
throw new IOException("Not a JPEG file");
throw new IIOException("Not a JPEG file");
}
// TODO: Why the two loops?!
huffTable.buildHuffTables(HuffTab);
quantTable.enhanceTables(TABLE);
current = input.readUnsignedShort();
while (((current >> 4) != 0x0FFC) || (current == JPEG.DHT)) { // SOF 0~15
switch (current) {
case JPEG.DHT: // DHT
huffTable.read(input, HuffTab);
break;
case JPEG.DAC: // DAC
throw new IOException("Program doesn't support arithmetic coding.");
case JPEG.DQT:
quantTable.read(input, TABLE);
break;
case JPEG.DRI:
restartInterval = readNumber();
break;
case JPEG.APP0:
case JPEG.APP1:
case JPEG.APP2:
case JPEG.APP3:
case JPEG.APP4:
case JPEG.APP5:
case JPEG.APP6:
case JPEG.APP7:
case JPEG.APP8:
case JPEG.APP9:
case JPEG.APP10:
case JPEG.APP11:
case JPEG.APP12:
case JPEG.APP13:
case JPEG.APP14:
case JPEG.APP15:
readApp();
break;
case JPEG.COM:
readComment();
break;
default:
if ((current >> 8) != 0xFF) {
throw new IOException("JPEG Segment marker expected.");
}
}
current = input.readUnsignedShort();
}
if ((current < 0xFFC0) || (current > 0xFFC7)) {
throw new IOException("ERROR: could not handle arithmetic code!");
}
frame.read(input);
current = input.readUnsignedShort();
do {
while (current != 0x0FFDA) { //SOS
switch (current) {
case 0xFFC4: //DHT
huffTable.read(input, HuffTab);
break;
case 0xFFCC: //DAC
throw new IOException("Program doesn't support arithmetic coding. (format throw new IOException)");
case 0xFFDB:
quantTable.read(input, 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)");
}
}
// Skip until first SOS
while (current != JPEG.SOS) {
input.skipBytes(input.readUnsignedShort() - 2);
current = input.readUnsignedShort();
}
final int precision = frame.getPrecision();
int precision = frame.samplePrecision;
if (precision == 8) {
mask = 0xFF;
@@ -218,28 +162,29 @@ public class JPEGLosslessDecoder {
mask = 0xFFFF;
}
final ComponentSpec[] components = frame.getComponents();
Frame.Component[] components = frame.components;
readScan();
numComp = scan.getNumComponents();
selection = scan.getSelection();
scan = readScan();
numComp = scan.components.length;
selection = scan.spectralSelStart;
final ScanComponent[] scanComps = scan.components;
final Scan.Component[] scanComps = scan.components;
final int[][] quantTables = quantTable.quantTables;
for (int i = 0; i < numComp; i++) {
ComponentSpec component = getComponentSpec(components, scanComps[i].getScanCompSel());
qTab[i] = quantTables[component.quantTableSel];
nBlock[i] = component.vSamp * component.hSamp;
dcTab[i] = HuffTab[scanComps[i].getDcTabSel()][0];
acTab[i] = HuffTab[scanComps[i].getAcTabSel()][1];
Frame.Component component = getComponentSpec(components, scanComps[i].scanCompSel);
qTab[i] = quantTables[component.qtSel];
nBlock[i] = component.vSub * component.hSub;
dcTab[i] = HuffTab[scanComps[i].dcTabSel][0];
acTab[i] = HuffTab[scanComps[i].acTabSel][1];
}
xDim = frame.getDimX();
yDim = frame.getDimY();
xDim = frame.samplesPerLine;
yDim = frame.lines;
outputRef = new int[numComp][];
// TODO: Support 4 components (RGBA/YCCA/CMYK/YCCK), others?
if (numComp == 1) {
outputData = new int[xDim * yDim];
outputRef[0] = outputData;
@@ -314,8 +259,8 @@ public class JPEGLosslessDecoder {
return outputRef;
}
private ComponentSpec getComponentSpec(ComponentSpec[] components, int sel) {
for (ComponentSpec component : components) {
private Frame.Component getComponentSpec(Frame.Component[] components, int sel) {
for (Frame.Component component : components) {
if (component.id == sel) {
return component;
}
@@ -324,8 +269,9 @@ public class JPEGLosslessDecoder {
throw new IllegalArgumentException("No such component id: " + sel);
}
private int readScan() throws IOException {
return scan.read(input);
private Scan readScan() throws IOException {
int length = input.readUnsignedShort();
return Scan.read(input, length);
}
private int decode(final int prev[], final int temp[], final int index[]) throws IOException {
@@ -341,11 +287,11 @@ public class JPEGLosslessDecoder {
}
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.
// 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));
prev[0] = (1 << (frame.samplePrecision - 1));
}
else {
switch (selection) {
@@ -554,7 +500,7 @@ public class JPEGLosslessDecoder {
index[0] += 8 - (code >> 8);
if (index[0] < 0) {
throw new IOException("index=" + index[0] + " temp=" + temp[0] + " code=" + code + " in HuffmanValue()");
throw new IIOException("index=" + index[0] + " temp=" + temp[0] + " code=" + code + " in HuffmanValue()");
}
if (index[0] < markerIndex) {
@@ -566,7 +512,7 @@ public class JPEGLosslessDecoder {
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;
final int one = 1;
final int n_one = -1;
@@ -578,7 +524,7 @@ public class JPEGLosslessDecoder {
}
if (n == 16) {
if (PRED[0] >= 0) {
if (pred[0] >= 0) {
return -32768;
}
else {
@@ -659,7 +605,7 @@ public class JPEGLosslessDecoder {
return getPreviousY(data);
}
else {
return (1 << (frame.getPrecision() - 1));
return (1 << (frame.samplePrecision - 1));
}
}
@@ -685,18 +631,18 @@ public class JPEGLosslessDecoder {
return (xLoc == (xDim - 1)) && (yLoc == (yDim - 1));
}
private void output(final int PRED[]) {
private void output(final int pred[]) {
if (numComp == 1) {
outputSingle(PRED);
outputSingle(pred);
}
else {
outputRGB(PRED);
outputRGB(pred);
}
}
private void outputSingle(final int PRED[]) {
private void outputSingle(final int pred[]) {
if ((xLoc < xDim) && (yLoc < yDim)) {
outputData[(yLoc * xDim) + xLoc] = mask & PRED[0];
outputData[(yLoc * xDim) + xLoc] = mask & pred[0];
xLoc++;
if (xLoc >= xDim) {
@@ -706,11 +652,11 @@ public class JPEGLosslessDecoder {
}
}
private void outputRGB(final int PRED[]) {
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];
outputRedData[(yLoc * xDim) + xLoc] = pred[0];
outputGreenData[(yLoc * xDim) + xLoc] = pred[1];
outputBlueData[(yLoc * xDim) + xLoc] = pred[2];
xLoc++;
if (xLoc >= xDim) {
@@ -720,34 +666,6 @@ public class JPEGLosslessDecoder {
}
}
private int readApp() throws IOException {
int count = 0;
final int length = input.readUnsignedShort();
count += 2;
while (count < length) {
input.readUnsignedByte();
count++;
}
return length;
}
private String readComment() throws IOException {
final StringBuffer sb = new StringBuffer();
int count = 0;
final int length = input.readUnsignedShort();
count += 2;
while (count < length) {
sb.append((char) input.readUnsignedByte());
count++;
}
return sb.toString();
}
private int readNumber() throws IOException {
final int Ld = input.readUnsignedShort();
@@ -758,11 +676,11 @@ public class JPEGLosslessDecoder {
return input.readUnsignedShort();
}
public int getNumComponents() {
int getNumComponents() {
return numComp;
}
public int getPrecision() {
return frame.getPrecision();
int getPrecision() {
return frame.samplePrecision;
}
}
@@ -1,4 +1,33 @@
package com.twelvemonkeys.imageio.plugins.jpeg.lossless;
/*
* Copyright (c) 2016, Harald Kuhr
* Copyright (c) 2016, Herman Kroll
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
@@ -6,6 +35,7 @@ import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferUShort;
import java.awt.image.Raster;
import java.io.IOException;
import java.util.List;
/**
* This class provides the conversion of input data
@@ -22,7 +52,7 @@ import java.io.IOException;
*
* @author Hermann Kroll
*/
public class JPEGLosslessDecoderWrapper {
final class JPEGLosslessDecoderWrapper {
/**
* Decodes a JPEG Lossless stream to a {@code BufferedImage}.
@@ -31,17 +61,19 @@ public class JPEGLosslessDecoderWrapper {
* - 8Bit, Grayscale -> BufferedImage.TYPE_BYTE_GRAY
* - 16Bit, Grayscale -> BufferedImage.TYPE_USHORT_GRAY
*
* @param segments
* @param input input stream which contains a jpeg lossless data
* @return if successfully a BufferedImage is returned
* @throws IOException is thrown if the decoder failed or a conversion is not supported
*/
public BufferedImage readImage(final ImageInputStream input) throws IOException {
JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(input);
BufferedImage readImage(final List<Segment> segments, final ImageInputStream input) throws IOException {
JPEGLosslessDecoder decoder = new JPEGLosslessDecoder(segments, input);
int[][] decoded = decoder.decode();
int width = decoder.getDimX();
int height = decoder.getDimY();
// Single component, assumed to be Gray
if (decoder.getNumComponents() == 1) {
switch (decoder.getPrecision()) {
case 8:
@@ -52,7 +84,8 @@ public class JPEGLosslessDecoderWrapper {
throw new IOException("JPEG Lossless with " + decoder.getPrecision() + " bit precision and 1 component cannot be decoded");
}
}
//rgb
// 3 components, assumed to be RGB
if (decoder.getNumComponents() == 3) {
switch (decoder.getPrecision()) {
case 8:
@@ -66,9 +99,9 @@ public class JPEGLosslessDecoderWrapper {
throw new IOException("JPEG Lossless with " + decoder.getPrecision() + " bit precision and " + decoder.getNumComponents() + " component(s) cannot be decoded");
}
public Raster readRaster(final ImageInputStream input) throws IOException {
Raster readRaster(final List<Segment> segments, final ImageInputStream input) throws IOException {
// TODO: Can perhaps be implemented faster
return readImage(input).getRaster();
return readImage(segments, input).getRaster();
}
/**
@@ -38,7 +38,7 @@ import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
* @version $Id: JPEGProviderInfo.java,v 1.0 20/03/15 harald.kuhr Exp$
*/
final class JPEGProviderInfo extends ReaderWriterProviderInfo {
protected JPEGProviderInfo() {
JPEGProviderInfo() {
super(
JPEGProviderInfo.class,
new String[] {"JPEG", "jpeg", "JPG", "jpg"},
@@ -1,38 +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.
* Copyright (c) 2016, Harald Kuhr
* Copyright (C) 2015, Michael Martinez
* Copyright (C) 2004, Helmut Dersch
* All rights reserved.
*
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
*/
/*
* Copyright (C) Helmut Dersch
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException;
@@ -41,7 +41,7 @@ final class QuantizationTable extends Segment {
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
final int quantTables[][] = new int[4][64]; // Tables
QuantizationTable() {
super(JPEG.DQT);
@@ -53,7 +53,7 @@ final class QuantizationTable extends Segment {
}
// TODO: Get rid of table param, make it a member?
protected void enhanceTables(final int[] table) throws IOException {
void enhanceTables(final int[] table) throws IOException {
for (int t = 0; t < 4; t++) {
if (tq[t] != 0) {
enhanceQuantizationTable(quantTables[t], table);
@@ -63,24 +63,24 @@ final class QuantizationTable extends Segment {
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[ 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[(6 * 8) + i]] *= 49;
qtab[table[(5 * 8) + i]] *= 71;
qtab[table[( 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[( 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[6 + (8 * i)]] *= 49;
qtab[table[5 + (8 * i)]] *= 71;
qtab[table[1 + (8 * i)]] *= 126;
qtab[table[7 + (8 * i)]] *= 25;
qtab[table[7 + (8 * i)]] *= 25;
qtab[table[3 + (8 * i)]] *= 106;
}
@@ -89,8 +89,14 @@ final class QuantizationTable extends Segment {
}
}
@Override
public String toString() {
// TODO: Tables...
return "DQT[]";
}
public static QuantizationTable read(final DataInput data, final int length) throws IOException {
int count = 0; // TODO: Could probably use data.getPosition for this
int count = 2;
QuantizationTable table = new QuantizationTable();
while (count < length) {
@@ -99,7 +105,7 @@ final class QuantizationTable extends Segment {
final int t = temp & 0x0F;
if (t > 3) {
throw new IOException("ERROR: Quantization table ID > 3");
throw new IIOException("Unexpected JPEG Quantization Table Id (> 3): " + t);
}
table.precision[t] = temp >> 4;
@@ -111,7 +117,7 @@ final class QuantizationTable extends Segment {
table.precision[t] = 16;
}
else {
throw new IOException("ERROR: Quantization table precision error");
throw new IIOException("Unexpected JPEG Quantization Table precision: " + table.precision[t]);
}
table.tq[t] = 1;
@@ -119,31 +125,27 @@ final class QuantizationTable extends Segment {
if (table.precision[t] == 8) {
for (int i = 0; i < 64; i++) {
if (count > length) {
throw new IOException("ERROR: Quantization table format error");
throw new IIOException("JPEG Quantization Table format error");
}
table.quantTables[t][i] = data.readUnsignedByte();
count++;
}
// table.enhanceQuantizationTable(table.quantTables[t], table);
}
else {
for (int i = 0; i < 64; i++) {
if (count > length) {
throw new IOException("ERROR: Quantization table format error");
throw new IIOException("JPEG Quantization Table format error");
}
table.quantTables[t][i] = data.readUnsignedShort();
count += 2;
}
// table.enhanceQuantizationTable(table.quantTables[t], table);
}
}
if (count != length) {
throw new IOException("ERROR: Quantization table error [count!=Lq]");
throw new IIOException("JPEG Quantization Table error, bad segment length: " + length);
}
return table;
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2016, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
import javax.imageio.IIOException;
import java.io.DataInput;
import java.io.IOException;
/**
* RestartInterval.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: harald.kuhr$
* @version $Id: RestartInterval.java,v 1.0 24/08/16 harald.kuhr Exp$
*/
class RestartInterval extends Segment {
final int interval;
private RestartInterval(int interval) {
super(JPEG.DRI);
this.interval = interval;
}
@Override
public String toString() {
return "DRI[" + interval + "]";
}
public static RestartInterval read(final DataInput data, final int length) throws IOException {
if (length != 4) {
throw new IIOException("Unexpected length of DRI segment: " + length);
}
return new RestartInterval(data.readUnsignedShort());
}
}
@@ -38,28 +38,28 @@ import java.io.IOException;
import java.util.Arrays;
final class Scan extends Segment {
final int selection; // Start of spectral or predictor selection
final int spectralEnd; // End of spectral selection
final int ah;
final int al;
final int spectralSelStart; // Start of spectral or predictor selection
final int spectralSelEnd; // End of spectral selection
final int approxHigh;
final int approxLow;
final Component[] components;
Scan(final Component[] components, final int selection, final int spectralEnd, final int ah, final int al) {
Scan(final Component[] components, final int spectralStart, final int spectralSelEnd, final int approxHigh, final int approxLow) {
super(JPEG.SOS);
this.components = components;
this.selection = selection;
this.spectralEnd = spectralEnd;
this.ah = ah;
this.al = al;
this.spectralSelStart = spectralStart;
this.spectralSelEnd = spectralSelEnd;
this.approxHigh = approxHigh;
this.approxLow = approxLow;
}
@Override
public String toString() {
return String.format(
"SOS[selection: %d, spectralEnd: %d, ah: %d, al: %d, components: %s]",
selection, spectralEnd, ah, al, Arrays.toString(components)
"SOS[spectralSelStart: %d, spectralSelEnd: %d, approxHigh: %d, approxLow: %d, components: %s]",
spectralSelStart, spectralSelEnd, approxHigh, approxLow, Arrays.toString(components)
);
}
@@ -93,7 +93,7 @@ final class Scan extends Segment {
return new Scan(components, selection, spectralEnd, temp >> 4, temp & 0x0F);
}
final static class Component {
public final static class Component {
final int scanCompSel; // Scan component selector
final int acTabSel; // AC table selector
final int dcTabSel; // DC table selector
@@ -1,3 +1,31 @@
/*
* Copyright (c) 2016, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import com.twelvemonkeys.imageio.metadata.jpeg.JPEG;
@@ -20,15 +48,12 @@ abstract class Segment {
this.marker = Validate.isTrue(marker >> 8 == 0xFF, marker, "Unknown JPEG marker: 0x%04x");
}
public static Segment read(int marker, String identifier, int length, DataInput data) throws IOException {
// TODO: Fix length inconsistencies...
// System.err.print("marker: " + marker);
// System.err.println(" length: " + length);
static Segment read(int marker, String identifier, int length, DataInput data) throws IOException {
switch (marker) {
case JPEG.DHT:
return HuffmanTable.read(data, length);
case JPEG.DQT:
return QuantizationTable.read(data, length - 2);
return QuantizationTable.read(data, length);
case JPEG.SOF0:
case JPEG.SOF1:
case JPEG.SOF2:
@@ -47,6 +72,9 @@ abstract class Segment {
return Scan.read(data, length);
case JPEG.COM:
return Comment.read(data, length);
// TODO: JPEG.DAC
case JPEG.DRI:
return RestartInterval.read(data, length);
case JPEG.APP0:
case JPEG.APP1:
case JPEG.APP2:
@@ -63,8 +91,7 @@ abstract class Segment {
case JPEG.APP13:
case JPEG.APP14:
case JPEG.APP15:
return AppSegment.read(marker, identifier, data, length);
// TODO: JPEG.DRI?
return Application.read(marker, identifier, data, length);
default:
return Unknown.read(marker, length, data);
}
@@ -1,10 +1,38 @@
/*
* Copyright (c) 2016, Harald Kuhr
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name "TwelveMonkeys" nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.twelvemonkeys.imageio.plugins.jpeg;
import java.io.DataInput;
import java.io.IOException;
/**
* Unknown.
* Represents an unknown segment in the JPEG stream.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: harald.kuhr$
@@ -19,9 +47,15 @@ final class Unknown extends Segment {
this.data = data;
}
@Override
public String toString() {
return String.format("Unknown[%04x, length: %d]", marker, data.length);
}
public static Segment read(int marker, int length, DataInput data) throws IOException {
byte[] bytes = new byte[length - 2];
data.readFully(bytes);
return new Unknown(marker, bytes);
}
}
@@ -1,39 +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 class ComponentSpec {
protected int id;
protected int hSamp; // Horizontal sampling factor
protected int quantTableSel; // Quantization table destination selector
protected int vSamp; // Vertical
}
@@ -1,116 +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;
import javax.imageio.stream.ImageInputStream;
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 ImageInputStream data) throws IOException {
int count = 0;
int length = data.readUnsignedShort();
count += 2;
precision = data.readUnsignedByte();
count++;
dimY = data.readUnsignedShort();
count += 2;
dimX = data.readUnsignedShort();
count += 2;
numComp = data.readUnsignedByte();
count++;
components = new ComponentSpec[numComp];
for (int i = 0; i < numComp; i++) {
if (count > length) {
throw new IOException("ERROR: frame format error");
}
int cid = data.readUnsignedByte();
count++;
if (count >= length) {
throw new IOException("ERROR: frame format error [c>=Lf]");
}
int temp = data.readUnsignedByte();
count++;
if (components[i] == null) {
components[i] = new ComponentSpec();
}
components[i].id = cid;
components[i].hSamp = temp >> 4;
components[i].vSamp = temp & 0x0F;
components[i].quantTableSel = data.readUnsignedByte();
count++;
}
if (count != length) {
throw new IOException("ERROR: frame format error [Lf!=count]");
}
return 1;
}
}
@@ -1,157 +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;
import javax.imageio.stream.ImageInputStream;
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 ImageInputStream data, final int[][][] HuffTab) throws IOException {
int count = 0;
final int length = data.readUnsignedShort();
count += 2;
while (count < length) {
final int temp = data.readUnsignedByte();
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.readUnsignedByte();
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.readUnsignedByte();
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++;
}
}
}
}
}
@@ -1,138 +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;
import javax.imageio.stream.ImageInputStream;
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 ImageInputStream data, final int[] table) throws IOException {
int count = 0;
final int length = data.readUnsignedShort();
count += 2;
while (count < length) {
final int temp = data.readUnsignedByte();
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.readUnsignedByte();
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.readUnsignedShort();
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;
}
}
}
@@ -1,62 +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 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;
}
}
@@ -1,126 +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;
import javax.imageio.stream.ImageInputStream;
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 ImageInputStream data) throws IOException {
int count = 0;
final int length = data.readUnsignedShort();
count += 2;
numComp = data.readUnsignedByte();
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.readUnsignedByte());
count++;
final int temp = data.readUnsignedByte();
count++;
components[i].setDcTabSel(temp >> 4);
components[i].setAcTabSel(temp & 0x0F);
}
setSelection(data.readUnsignedByte());
count++;
setSpectralEnd(data.readUnsignedByte());
count++;
final int temp = data.readUnsignedByte();
setAh(temp >> 4);
setAl(temp & 0x0F);
count++;
if (count != length) {
throw new IOException("ERROR: scan header format error [count!=Ns]");
}
return 1;
}
}