Author: damjan
Date: Tue Sep 4 19:11:17 2012
New Revision: 1380817
URL: http://svn.apache.org/viewvc?rev=1380817&view=rev
Log:
Add support for the Adobe App14 JPEG segment,
and improve JPEG colorspace detection.
Jira issue key: IMAGING-89
Added:
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App14Segment.java
(with props)
Modified:
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/ImageInfo.java
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/JpegImageParser.java
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java
Modified:
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/ImageInfo.java
URL:
http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/ImageInfo.java?rev=1380817&r1=1380816&r2=1380817&view=diff
==============================================================================
---
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/ImageInfo.java
(original)
+++
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/ImageInfo.java
Tue Sep 4 19:11:17 2012
@@ -51,6 +51,9 @@ public class ImageInfo {
public static final int COLOR_TYPE_GRAYSCALE = 1;
public static final int COLOR_TYPE_RGB = 2;
public static final int COLOR_TYPE_CMYK = 3;
+ public static final int COLOR_TYPE_YCbCr = 4;
+ public static final int COLOR_TYPE_YCCK = 5;
+ public static final int COLOR_TYPE_YCC = 6;
public static final int COLOR_TYPE_OTHER = -1;
public static final int COLOR_TYPE_UNKNOWN = -2;
@@ -260,6 +263,12 @@ public class ImageInfo {
return "RGB";
case COLOR_TYPE_CMYK:
return "CMYK";
+ case COLOR_TYPE_YCbCr:
+ return "YCbCr";
+ case COLOR_TYPE_YCCK:
+ return "YCCK";
+ case COLOR_TYPE_YCC:
+ return "YCC";
case COLOR_TYPE_OTHER:
return "Other";
case COLOR_TYPE_UNKNOWN:
Modified:
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/JpegImageParser.java
URL:
http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/JpegImageParser.java?rev=1380817&r1=1380816&r2=1380817&view=diff
==============================================================================
---
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/JpegImageParser.java
(original)
+++
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/JpegImageParser.java
Tue Sep 4 19:11:17 2012
@@ -40,6 +40,7 @@ import org.apache.commons.imaging.format
import org.apache.commons.imaging.formats.jpeg.iptc.IptcParser;
import org.apache.commons.imaging.formats.jpeg.iptc.PhotoshopApp13Data;
import org.apache.commons.imaging.formats.jpeg.segments.App13Segment;
+import org.apache.commons.imaging.formats.jpeg.segments.App14Segment;
import org.apache.commons.imaging.formats.jpeg.segments.App2Segment;
import org.apache.commons.imaging.formats.jpeg.segments.ComSegment;
import org.apache.commons.imaging.formats.jpeg.segments.DqtSegment;
@@ -147,6 +148,8 @@ public class JpegImageParser extends Ima
if (marker == JPEG_APP13_Marker) {
// Debug.debug("app 13 segment data", segmentData.length);
result.add(new App13Segment(parser, marker, segmentData));
+ } else if (marker == JPEG_APP14_Marker) {
+ result.add(new App14Segment(marker, segmentData));
} else if (marker == JPEG_APP2_Marker) {
result.add(new App2Segment(marker, segmentData));
} else if (marker == JFIFMarker) {
@@ -632,6 +635,11 @@ public class JpegImageParser extends Ima
if ((jfifSegments != null) && (jfifSegments.size() > 0))
jfifSegment = (JfifSegment) jfifSegments.get(0);
+ List<Segment> app14Segments = readSegments(byteSource, new int[] {
JPEG_APP14_Marker }, true);
+ App14Segment app14Segment = null;
+ if (app14Segments != null && !app14Segments.isEmpty())
+ app14Segment = (App14Segment) app14Segments.get(0);
+
// JfifSegment fTheJFIFSegment = (JfifSegment) findSegment(segments,
// kJFIFMarker);
@@ -746,17 +754,128 @@ public class JpegImageParser extends Ima
// not accurate ... only reflects first
boolean isProgressive = fSOFNSegment.marker == SOF2Marker;
- boolean isTransparent = false; // TODO: inaccurate.
+ boolean isTransparent = false;
boolean usesPalette = false; // TODO: inaccurate.
- int ColorType;
- if (Number_of_components == 1)
- ColorType = ImageInfo.COLOR_TYPE_BW;
- else if (Number_of_components == 3)
- ColorType = ImageInfo.COLOR_TYPE_RGB;
- else if (Number_of_components == 4)
- ColorType = ImageInfo.COLOR_TYPE_CMYK;
- else
- ColorType = ImageInfo.COLOR_TYPE_UNKNOWN;
+
+ // See
http://docs.oracle.com/javase/6/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color
+ int colorType = ImageInfo.COLOR_TYPE_UNKNOWN;
+ // Some images have both JFIF/APP0 and APP14.
+ // JFIF is meant to win but in them APP14 is clearly right, so make it
win.
+ if (app14Segment != null && app14Segment.isAdobeJpegSegment()) {
+ int colorTransform = app14Segment.getAdobeColorTransform();
+ if (colorTransform == App14Segment.ADOBE_COLOR_TRANSFORM_UNKNOWN)
{
+ if (Number_of_components == 3) {
+ colorType = ImageInfo.COLOR_TYPE_RGB;
+ } else if (Number_of_components == 4) {
+ colorType = ImageInfo.COLOR_TYPE_CMYK;
+ }
+ } else if (colorTransform ==
App14Segment.ADOBE_COLOR_TRANSFORM_YCbCr) {
+ colorType = ImageInfo.COLOR_TYPE_YCbCr;
+ } else if (colorTransform ==
App14Segment.ADOBE_COLOR_TRANSFORM_YCCK) {
+ colorType = ImageInfo.COLOR_TYPE_YCCK;
+ }
+ } else if (jfifSegment != null) {
+ if (Number_of_components == 1)
+ colorType = ImageInfo.COLOR_TYPE_GRAYSCALE;
+ else if (Number_of_components == 3)
+ colorType = ImageInfo.COLOR_TYPE_YCbCr;
+ } else {
+ if (Number_of_components == 1) {
+ colorType = ImageInfo.COLOR_TYPE_GRAYSCALE;
+ } else if (Number_of_components == 2) {
+ colorType = ImageInfo.COLOR_TYPE_GRAYSCALE;
+ isTransparent = true;
+ } else if (Number_of_components == 3 || Number_of_components == 4)
{
+ boolean have1 = false;
+ boolean have2 = false;
+ boolean have3 = false;
+ boolean have4 = false;
+ boolean haveOther = false;
+ for (SofnSegment.Component component :
fSOFNSegment.components) {
+ final int id = component.componentIdentifier;
+ if (id == 1)
+ have1 = true;
+ else if (id == 2)
+ have2 = true;
+ else if (id == 3)
+ have3 = true;
+ else if (id == 4)
+ have4 = true;
+ else
+ haveOther = true;
+ }
+ if (Number_of_components == 3 && have1 && have2 && have3 &&
!have4 && !haveOther) {
+ colorType = ImageInfo.COLOR_TYPE_YCbCr;
+ } else if (Number_of_components == 4 && have1 && have2 &&
have3 && have4 && !haveOther) {
+ colorType = ImageInfo.COLOR_TYPE_YCbCr;
+ isTransparent = true;
+ } else {
+ boolean haveR = false;
+ boolean haveG = false;
+ boolean haveB = false;
+ boolean haveA = false;
+ boolean haveC = false;
+ boolean havec = false;
+ boolean haveY = false;
+ for (SofnSegment.Component component :
fSOFNSegment.components) {
+ final int id = component.componentIdentifier;
+ if (id == 'R')
+ haveR = true;
+ else if (id == 'G')
+ haveG = true;
+ else if (id == 'B')
+ haveB = true;
+ else if (id == 'A')
+ haveA = true;
+ else if (id == 'C')
+ haveC = true;
+ else if (id == 'c')
+ havec = true;
+ else if (id == 'Y')
+ haveY = true;
+ }
+ if (haveR && haveG && haveB && !haveA && !haveC && !havec
&& !haveY) {
+ colorType = ImageInfo.COLOR_TYPE_RGB;
+ } else if (haveR && haveG && haveB && haveA && !haveC &&
!havec && !haveY) {
+ colorType = ImageInfo.COLOR_TYPE_RGB;
+ isTransparent = true;
+ } else if (haveY && haveC && havec && !haveR && !haveG &&
!haveB && !haveA) {
+ colorType = ImageInfo.COLOR_TYPE_YCC;
+ } else if (haveY && haveC && havec && haveA && !haveR &&
!haveG && !haveB) {
+ colorType = ImageInfo.COLOR_TYPE_YCC;
+ isTransparent = true;
+ } else {
+ int minHorizontalSamplingFactor = Integer.MAX_VALUE;
+ int maxHorizontalSmaplingFactor = Integer.MIN_VALUE;
+ int minVerticalSamplingFactor = Integer.MAX_VALUE;
+ int maxVerticalSamplingFactor = Integer.MIN_VALUE;
+ for (SofnSegment.Component component :
fSOFNSegment.components) {
+ if (minHorizontalSamplingFactor >
component.horizontalSamplingFactor)
+ minHorizontalSamplingFactor =
component.horizontalSamplingFactor;
+ if (maxHorizontalSmaplingFactor <
component.horizontalSamplingFactor)
+ maxHorizontalSmaplingFactor =
component.horizontalSamplingFactor;
+ if (minVerticalSamplingFactor >
component.verticalSamplingFactor)
+ minVerticalSamplingFactor =
component.verticalSamplingFactor;
+ if (maxVerticalSamplingFactor <
component.verticalSamplingFactor)
+ maxVerticalSamplingFactor =
component.verticalSamplingFactor;
+ }
+ boolean isSubsampled = (minHorizontalSamplingFactor !=
maxHorizontalSmaplingFactor) ||
+ (minVerticalSamplingFactor !=
maxVerticalSamplingFactor);
+ if (Number_of_components == 3) {
+ if (isSubsampled)
+ colorType = ImageInfo.COLOR_TYPE_YCbCr;
+ else
+ colorType = ImageInfo.COLOR_TYPE_RGB;
+ } else if (Number_of_components == 4) {
+ if (isSubsampled)
+ colorType = ImageInfo.COLOR_TYPE_YCCK;
+ else
+ colorType = ImageInfo.COLOR_TYPE_CMYK;
+ }
+ }
+ }
+ }
+ }
String compressionAlgorithm = ImageInfo.COMPRESSION_ALGORITHM_JPEG;
@@ -764,7 +883,7 @@ public class JpegImageParser extends Ima
Format, FormatName, Height, MimeType, NumberOfImages,
PhysicalHeightDpi, PhysicalHeightInch, PhysicalWidthDpi,
PhysicalWidthInch, Width, isProgressive, isTransparent,
- usesPalette, ColorType, compressionAlgorithm);
+ usesPalette, colorType, compressionAlgorithm);
return result;
}
Modified:
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java
URL:
http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java?rev=1380817&r1=1380816&r2=1380817&view=diff
==============================================================================
---
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java
(original)
+++
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App13Segment.java
Tue Sep 4 19:11:17 2012
@@ -69,7 +69,7 @@ public class App13Segment extends AppnSe
* metadata. However, we should not treat App13 signatures without
* Photoshop's signature as Photoshop/IPTC segments.
*/
- if (!new IptcParser().isPhotoshopJpegSegment(bytes))
+ if (!isPhotoshopJpegSegment())
return null;
return new IptcParser().parsePhotoshopSegment(bytes, params);
Added:
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App14Segment.java
URL:
http://svn.apache.org/viewvc/commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App14Segment.java?rev=1380817&view=auto
==============================================================================
---
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App14Segment.java
(added)
+++
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App14Segment.java
Tue Sep 4 19:11:17 2012
@@ -0,0 +1,46 @@
+package org.apache.commons.imaging.formats.jpeg.segments;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+import org.apache.commons.imaging.common.BinaryFileParser;
+
+/**
+ *
http://www.aiim.org/documents/standards/PDF-Ref/References/Adobe/5116.DCT_Filter.pdf
+ */
+public class App14Segment extends AppnSegment {
+ private static final byte[] adobePrefix;
+ public static final int ADOBE_COLOR_TRANSFORM_UNKNOWN = 0;
+ public static final int ADOBE_COLOR_TRANSFORM_YCbCr = 1;
+ public static final int ADOBE_COLOR_TRANSFORM_YCCK = 2;
+
+ static {
+ byte[] adobe = null;
+ try {
+ adobe = "Adobe".getBytes("US-ASCII");
+ } catch (UnsupportedEncodingException cannotHappen) {
+ }
+ adobePrefix = adobe;
+ }
+
+ public App14Segment(int marker, byte segmentData[])
+ throws IOException {
+ this(marker, segmentData.length, new ByteArrayInputStream(
+ segmentData));
+ }
+
+ public App14Segment(int marker, int marker_length, InputStream is)
+ throws IOException {
+ super(marker, marker_length, is);
+ }
+
+ public boolean isAdobeJpegSegment() {
+ return BinaryFileParser.byteArrayHasPrefix(bytes, adobePrefix);
+ }
+
+ public int getAdobeColorTransform() {
+ return 0xff & bytes[11];
+ }
+}
Propchange:
commons/proper/imaging/trunk/src/main/java/org/apache/commons/imaging/formats/jpeg/segments/App14Segment.java
------------------------------------------------------------------------------
svn:eol-style = native