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