in src/main/java/org/apache/commons/imaging/formats/jpeg/JpegImageParser.java [316:648]
public ImageInfo getImageInfo(final ByteSource byteSource, final JpegImagingParameters params)
throws ImagingException, IOException {
// List allSegments = readSegments(byteSource, null, false);
final List<Segment> SOF_segments = readSegments(byteSource, new int[] {
// kJFIFMarker,
JpegConstants.SOF0_MARKER,
JpegConstants.SOF1_MARKER,
JpegConstants.SOF2_MARKER,
JpegConstants.SOF3_MARKER,
JpegConstants.SOF5_MARKER,
JpegConstants.SOF6_MARKER,
JpegConstants.SOF7_MARKER,
JpegConstants.SOF9_MARKER,
JpegConstants.SOF10_MARKER,
JpegConstants.SOF11_MARKER,
JpegConstants.SOF13_MARKER,
JpegConstants.SOF14_MARKER,
JpegConstants.SOF15_MARKER,
}, false);
if (SOF_segments == null) {
throw new ImagingException("No SOFN Data Found.");
}
// if (SOF_segments.size() != 1)
// System.out.println("Incoherent SOFN Data Found: "
// + SOF_segments.size());
final List<Segment> jfifSegments = readSegments(byteSource,
new int[] { JpegConstants.JFIF_MARKER, }, true);
final SofnSegment fSOFNSegment = (SofnSegment) SOF_segments.get(0);
// SofnSegment fSOFNSegment = (SofnSegment) findSegment(segments,
// SOFNmarkers);
if (fSOFNSegment == null) {
throw new ImagingException("No SOFN Data Found.");
}
final int width = fSOFNSegment.width;
final int height = fSOFNSegment.height;
JfifSegment jfifSegment = null;
if ((jfifSegments != null) && (!jfifSegments.isEmpty())) {
jfifSegment = (JfifSegment) jfifSegments.get(0);
}
final List<Segment> app14Segments = readSegments(byteSource, new int[] { JpegConstants.JPEG_APP14_MARKER}, true);
App14Segment app14Segment = null;
if (app14Segments != null && !app14Segments.isEmpty()) {
app14Segment = (App14Segment) app14Segments.get(0);
}
// JfifSegment fTheJFIFSegment = (JfifSegment) findSegment(segments,
// kJFIFMarker);
double xDensity = -1.0;
double yDensity = -1.0;
double unitsPerInch = -1.0;
// int JFIF_major_version;
// int JFIF_minor_version;
String formatDetails;
if (jfifSegment != null) {
xDensity = jfifSegment.xDensity;
yDensity = jfifSegment.yDensity;
final int densityUnits = jfifSegment.densityUnits;
// JFIF_major_version = fTheJFIFSegment.JFIF_major_version;
// JFIF_minor_version = fTheJFIFSegment.JFIF_minor_version;
formatDetails = "Jpeg/JFIF v." + jfifSegment.jfifMajorVersion + "."
+ jfifSegment.jfifMinorVersion;
switch (densityUnits) {
case 0:
break;
case 1: // inches
unitsPerInch = 1.0;
break;
case 2: // cms
unitsPerInch = 2.54;
break;
default:
break;
}
} else {
final JpegImageMetadata metadata = (JpegImageMetadata) getMetadata(
byteSource, params);
if (metadata != null) {
{
final TiffField field = metadata.findExifValue(TiffTagConstants.TIFF_TAG_XRESOLUTION);
if (field != null) {
xDensity = ((Number) field.getValue()).doubleValue();
}
}
{
final TiffField field = metadata.findExifValue(TiffTagConstants.TIFF_TAG_YRESOLUTION);
if (field != null) {
yDensity = ((Number) field.getValue()).doubleValue();
}
}
{
final TiffField field = metadata.findExifValue(TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT);
if (field != null) {
final int densityUnits = ((Number) field.getValue()).intValue();
switch (densityUnits) {
case 1:
break;
case 2: // inches
unitsPerInch = 1.0;
break;
case 3: // cms
unitsPerInch = 2.54;
break;
default:
break;
}
}
}
}
formatDetails = "Jpeg/DCM";
}
int physicalHeightDpi = -1;
float physicalHeightInch = -1;
int physicalWidthDpi = -1;
float physicalWidthInch = -1;
if (unitsPerInch > 0) {
physicalWidthDpi = (int) Math.round(xDensity * unitsPerInch);
physicalWidthInch = (float) (width / (xDensity * unitsPerInch));
physicalHeightDpi = (int) Math.round(yDensity * unitsPerInch);
physicalHeightInch = (float) (height / (yDensity * unitsPerInch));
}
final List<Segment> commentSegments = readSegments(byteSource,
new int[] { JpegConstants.COM_MARKER}, false);
final List<String> comments = Allocator.arrayList(commentSegments.size());
for (final Segment commentSegment : commentSegments) {
final ComSegment comSegment = (ComSegment) commentSegment;
comments.add(new String(comSegment.getComment(), StandardCharsets.UTF_8));
}
final int numberOfComponents = fSOFNSegment.numberOfComponents;
final int precision = fSOFNSegment.precision;
final int bitsPerPixel = numberOfComponents * precision;
final ImageFormat format = ImageFormats.JPEG;
final String formatName = "JPEG (Joint Photographic Experts Group) Format";
final String mimeType = "image/jpeg";
// TODO: we ought to count images, but don't yet.
final int numberOfImages = 1;
// not accurate ... only reflects first
final boolean progressive = fSOFNSegment.marker == JpegConstants.SOF2_MARKER;
boolean transparent = false;
final boolean usesPalette = false; // TODO: inaccurate.
// See https://docs.oracle.com/javase/8/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html#color
ImageInfo.ColorType colorType = ImageInfo.ColorType.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()) {
final int colorTransform = app14Segment.getAdobeColorTransform();
switch (colorTransform) {
case App14Segment.ADOBE_COLOR_TRANSFORM_UNKNOWN:
if (numberOfComponents == 3) {
colorType = ImageInfo.ColorType.RGB;
} else if (numberOfComponents == 4) {
colorType = ImageInfo.ColorType.CMYK;
}
break;
case App14Segment.ADOBE_COLOR_TRANSFORM_YCbCr:
colorType = ImageInfo.ColorType.YCbCr;
break;
case App14Segment.ADOBE_COLOR_TRANSFORM_YCCK:
colorType = ImageInfo.ColorType.YCCK;
break;
default:
break;
}
} else if (jfifSegment != null) {
if (numberOfComponents == 1) {
colorType = ImageInfo.ColorType.GRAYSCALE;
} else if (numberOfComponents == 3) {
colorType = ImageInfo.ColorType.YCbCr;
}
} else {
switch (numberOfComponents) {
case 1:
colorType = ImageInfo.ColorType.GRAYSCALE;
break;
case 2:
colorType = ImageInfo.ColorType.GRAYSCALE;
transparent = true;
break;
case 3:
case 4:
boolean have1 = false;
boolean have2 = false;
boolean have3 = false;
boolean have4 = false;
boolean haveOther = false;
for (final SofnSegment.Component component : fSOFNSegment.getComponents()) {
final int id = component.componentIdentifier;
switch (id) {
case 1:
have1 = true;
break;
case 2:
have2 = true;
break;
case 3:
have3 = true;
break;
case 4:
have4 = true;
break;
default:
haveOther = true;
break;
}
}
if (numberOfComponents == 3 && have1 && have2 && have3 && !have4 && !haveOther) {
colorType = ImageInfo.ColorType.YCbCr;
} else if (numberOfComponents == 4 && have1 && have2 && have3 && have4 && !haveOther) {
colorType = ImageInfo.ColorType.YCbCr;
transparent = true;
} else {
boolean haveR = false;
boolean haveG = false;
boolean haveB = false;
boolean haveA = false;
boolean haveC = false;
boolean havec = false;
boolean haveY = false;
for (final SofnSegment.Component component : fSOFNSegment.getComponents()) {
final int id = component.componentIdentifier;
switch (id) {
case 'R':
haveR = true;
break;
case 'G':
haveG = true;
break;
case 'B':
haveB = true;
break;
case 'A':
haveA = true;
break;
case 'C':
haveC = true;
break;
case 'c':
havec = true;
break;
case 'Y':
haveY = true;
break;
default:
break;
}
}
if (haveR && haveG && haveB && !haveA && !haveC && !havec && !haveY) {
colorType = ImageInfo.ColorType.RGB;
} else if (haveR && haveG && haveB && haveA && !haveC && !havec && !haveY) {
colorType = ImageInfo.ColorType.RGB;
transparent = true;
} else if (haveY && haveC && havec && !haveR && !haveG && !haveB && !haveA) {
colorType = ImageInfo.ColorType.YCC;
} else if (haveY && haveC && havec && haveA && !haveR && !haveG && !haveB) {
colorType = ImageInfo.ColorType.YCC;
transparent = true;
} else {
int minHorizontalSamplingFactor = Integer.MAX_VALUE;
int maxHorizontalSmaplingFactor = Integer.MIN_VALUE;
int minVerticalSamplingFactor = Integer.MAX_VALUE;
int maxVerticalSamplingFactor = Integer.MIN_VALUE;
for (final SofnSegment.Component component : fSOFNSegment.getComponents()) {
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;
}
}
final boolean isSubsampled = (minHorizontalSamplingFactor != maxHorizontalSmaplingFactor)
|| (minVerticalSamplingFactor != maxVerticalSamplingFactor);
if (numberOfComponents == 3) {
if (isSubsampled) {
colorType = ImageInfo.ColorType.YCbCr;
} else {
colorType = ImageInfo.ColorType.RGB;
}
} else if (numberOfComponents == 4) {
if (isSubsampled) {
colorType = ImageInfo.ColorType.YCCK;
} else {
colorType = ImageInfo.ColorType.CMYK;
}
}
}
}
break;
default:
break;
}
}
final ImageInfo.CompressionAlgorithm compressionAlgorithm = ImageInfo.CompressionAlgorithm.JPEG;
return new ImageInfo(formatDetails, bitsPerPixel, comments,
format, formatName, height, mimeType, numberOfImages,
physicalHeightDpi, physicalHeightInch, physicalWidthDpi,
physicalWidthInch, width, progressive, transparent,
usesPalette, colorType, compressionAlgorithm);
}