All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.monte.media.jpeg.JFIFInputStream Maven / Gradle / Ivy

The newest version!

package org.monte.media.jpeg;

import java.io.*;
import java.util.*;


public class JFIFInputStream extends FilterInputStream {


    private final HashSet standaloneMarkers = new HashSet();

    private final HashSet doubleSegMarkers = new HashSet();


    public static class Segment {


        public final int marker;

        public final long offset;

        public final int length;

        public Segment(int marker, long offset, int length) {
            this.marker = marker;
            this.offset = offset;
            this.length = length;
        }

        public boolean isEntropyCoded() {
            return length == -1;
        }

        @Override
        public String toString() {
            return "Segment marker=0x" + Integer.toHexString(marker) + " offset=" + offset + "=0x" + Long.toHexString(offset);
        }
    }
    private Segment segment;

    private boolean markerFound;
    private int marker = JUNK_MARKER;
    private long offset = 0;
    private boolean isStuffed0xff = false;

    public final static int JUNK_MARKER = -1;

    public final static int SOI_MARKER = 0xffd8;

    public final static int EOI_MARKER = 0xffd9;

    public final static int TEM_MARKER = 0xff01;

    public final static int SOS_MARKER = 0xffda;

    public final static int APP1_MARKER = 0xffe1;

    public final static int APP2_MARKER = 0xffe2;

    public final static int JPG0_MARKER = 0xfff0;
    public final static int JPG1_MARKER = 0xfff1;
    public final static int JPG2_MARKER = 0xfff2;
    public final static int JPG3_MARKER = 0xfff3;
    public final static int JPG4_MARKER = 0xfff4;
    public final static int JPG5_MARKER = 0xfff5;
    public final static int JPG6_MARKER = 0xfff6;
    public final static int JPG7_MARKER = 0xfff7;
    public final static int JPG8_MARKER = 0xfff8;
    public final static int JPG9_MARKER = 0xfff9;
    public final static int JPGA_MARKER = 0xfffA;
    public final static int JPGB_MARKER = 0xfffB;
    public final static int JPGC_MARKER = 0xfffC;
    public final static int JPGD_MARKER = 0xfffD;

    public final static int SOF0_MARKER = 0xffc0;
    public final static int SOF1_MARKER = 0xffc1;
    public final static int SOF2_MARKER = 0xffc2;
    public final static int SOF3_MARKER = 0xffc3;


    public final static int SOF5_MARKER = 0xffc5;
    public final static int SOF6_MARKER = 0xffc6;
    public final static int SOF7_MARKER = 0xffc7;


    public final static int SOF9_MARKER = 0xffc9;
    public final static int SOFA_MARKER = 0xffcA;
    public final static int SOFB_MARKER = 0xffcB;

    public final static int SOFD_MARKER = 0xffcD;
    public final static int SOFE_MARKER = 0xffcE;
    public final static int SOFF_MARKER = 0xffcF;


    public final static int RST0_MARKER = 0xffd0;
    public final static int RST1_MARKER = 0xffd1;
    public final static int RST2_MARKER = 0xffd2;
    public final static int RST3_MARKER = 0xffd3;
    public final static int RST4_MARKER = 0xffd4;
    public final static int RST5_MARKER = 0xffd5;
    public final static int RST6_MARKER = 0xffd6;
    public final static int RST7_MARKER = 0xffd7;

    public JFIFInputStream(File f) throws IOException {
       this(new BufferedInputStream(new FileInputStream(f)));
    }

    public JFIFInputStream(InputStream in) {
        super(in);

        for (int i = RST0_MARKER; i <= RST7_MARKER; i++) {
            standaloneMarkers.add(i);
        }
        standaloneMarkers.add(SOI_MARKER);
        standaloneMarkers.add(EOI_MARKER);
        standaloneMarkers.add(TEM_MARKER);
        standaloneMarkers.add(JPG0_MARKER);
        standaloneMarkers.add(JPG1_MARKER);
        standaloneMarkers.add(JPG2_MARKER);
        standaloneMarkers.add(JPG3_MARKER);
        standaloneMarkers.add(JPG4_MARKER);
        standaloneMarkers.add(JPG5_MARKER);
        standaloneMarkers.add(JPG6_MARKER);
        standaloneMarkers.add(JPG7_MARKER);
        standaloneMarkers.add(JPG8_MARKER);
        standaloneMarkers.add(JPG9_MARKER);
        standaloneMarkers.add(JPGA_MARKER);
        standaloneMarkers.add(JPGB_MARKER);
        standaloneMarkers.add(JPGC_MARKER);
        standaloneMarkers.add(JPGD_MARKER);
        standaloneMarkers.add(0xffff);
        doubleSegMarkers.add(SOS_MARKER);


        segment = new Segment(-1, 0, -1);
    }


    public Segment getSegment() throws IOException {
        return segment;
    }


    public Segment getNextSegment() throws IOException {


        if (!segment.isEntropyCoded()) {
            markerFound = false;
            do {
                long skipped = in.skip(segment.length - offset + segment.offset);
                if (skipped == -1) {
                    segment = new Segment(0, offset, -1);
                    return null;
                }
                offset += skipped;
            } while (offset < segment.length + segment.offset);

            if (doubleSegMarkers.contains(segment.marker)) {
                segment = new Segment(0, offset, -1);
                return segment;
            }
        }


        while (!markerFound) {
            while (true) {
                int b;
                if (isStuffed0xff) {
                    b = 0xff;
                    isStuffed0xff = false;
                } else {
                    b = read0();
                }
                if (b == -1) {
                    return null;
                }
                if (b == 0xff) {
                    markerFound = true;
                    break;
                }
            }
            int b = read0();
            if (b == -1) {
                return null;
            }
            if (b == 0x00) {
                markerFound = false;
            } else if (b == 0xff) {
                isStuffed0xff = true;
                markerFound = false;
            } else {
                marker = 0xff00 | b;
            }
        }
        markerFound = false;




        if (standaloneMarkers.contains(marker)) {
            segment = new Segment(0xff00 | marker, offset, -1);
        } else {
            int length = (read0() << 8) | read0();
            if (length < 2) {
                throw new IOException("JFIFInputStream found illegal segment length " + length + " after marker " + Integer.toHexString(marker) + " at offset " + offset + ".");
            }
            segment = new Segment(0xff00 | marker, offset, length - 2);
        }
        return segment;
    }

    public long getStreamPosition() {
        return offset;
    }

    private int read0() throws IOException {
        int b = in.read();
        if (b != -1) {
            offset++;
        }
        return b;
    }


    @Override
    public int read() throws IOException {
        if (markerFound) {
            return -1;
        }

        int b;
        if (isStuffed0xff) {
            isStuffed0xff = false;
            b = 0xff;
        } else {
            b = read0();
        }

        if (segment.isEntropyCoded()) {
            if (b == 0xff) {
                b = read0();
                if (b == 0x00) {

                    return 0xff;
                } else if (b == 0xff) {

                    isStuffed0xff = true;
                    return 0xff;
                }
                markerFound = true;
                marker = 0xff00 | b;
                return -1;
            }
        }
        return b;
    }


    @Override
    public int read(byte b[], int off, int len) throws IOException {
        if (markerFound) {
            return -1;
        }

        int count = 0;
        if (segment.isEntropyCoded()) {
            for (; count < len; count++) {
                int data = read();
                if (data == -1) {
                    if (count==0) return -1;
                    break;
                }

                b[off + count] = (byte) data;
            }
        } else {
            long available = segment.length - offset + segment.offset;
            if (available <= 0) {
                return -1;
            }
            if (available < len) {
                len = (int) available;
            }
            count = in.read(b, off, len);
            if (count != -1) {
                offset += count;
            }
        }
        return count;
    }


    public final void skipFully(long n) throws IOException {
        long total = 0;
        long cur = 0;

        while ((total < n) && ((cur = (int) in.skip(n - total)) > 0)) {
            total += cur;
        }
        offset+=total;
        if (total < n) {
            throw new EOFException();
        }
    }


    @Override
    public long skip(long n) throws IOException {
        if (markerFound) {
            return -1;
        }

        long count = 0;
        if (segment.isEntropyCoded()) {
            for (; count < n; count++) {
                int data = read();
                if (data == -1) {
                    break;
                }
            }
        } else {
            long available = segment.length - offset + segment.offset;
            if (available < n) {
                n = (int) available;
            }
            count = in.skip(n);
            if (count != -1) {
                offset += count;
            }
        }
        return count;
    }


    @Override
    public synchronized void mark(int readlimit) {

    }


    @Override
    public synchronized void reset() throws IOException {
        throw new IOException("Reset not supported");
    }


    @Override
    public boolean markSupported() {
        return false;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy