@Override
    public ImageInfo getImageInfo(final ByteSource byteSource, final JpegImagingParameters params) throws ImagingException, IOException {
        // List allSegments = readSegments(byteSource, null, false);

        final List<AbstractSegment> 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<AbstractSegment> 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<AbstractSegment> 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;
        final 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<AbstractSegment> commentSegments = readSegments(byteSource, new int[] { JpegConstants.COM_MARKER }, false);
        final List<String> comments = Allocator.arrayList(commentSegments.size());
        for (final AbstractSegment 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);
    }
Constructs a new instance with the big-endian byte order.
src/main/java/org/apache/commons/imaging/formats/jpeg/JpegImageParser.java