#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

View File

@ -1,4 +1,6 @@
Copyright (c) 2013, Harald Kuhr
Copyright (c) 2016, Harald Kuhr
Copyright (C) 2015, Michael Martinez (JPEG Lossless decoder)
Copyright (C) 2004, Helmut Dersch (Java JPEG decoder)
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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];

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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();
}
/**

View File

@ -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"},

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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++;
}
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}