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

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

The newest version!

package org.monte.media.jpeg;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteOrder;
import java.util.HashSet;
import java.util.Stack;
import javax.imageio.stream.FileImageOutputStream;
import javax.imageio.stream.ImageOutputStream;


public class JFIFOutputStream extends OutputStream {


    private final HashSet standaloneMarkers = new HashSet();

    private final HashSet doubleSegMarkers = new HashSet();

    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;
    private ImageOutputStream out;
    private long streamOffset;
    private Stack stack = new Stack();

    public JFIFOutputStream(ImageOutputStream out) throws IOException {
        this.out = out;
        out.setByteOrder(ByteOrder.BIG_ENDIAN);
        streamOffset = out.getStreamPosition();

        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(0);

        doubleSegMarkers.add(SOS_MARKER);

    }

    public JFIFOutputStream(File imgFile) throws IOException {
        this(new FileImageOutputStream(imgFile));
    }


    public long getStreamPosition() throws IOException {
        return out.getStreamPosition() - streamOffset;
    }


    public void seek(long newPosition) throws IOException {
        out.seek(newPosition + streamOffset);
    }

    public void pushSegment(int marker) throws IOException {
        stack.push(new Segment(marker));
    }

    public void popSegment() throws IOException {
        Segment seg = stack.pop();
        seg.finish();
    }


    public long getSegmentOffset() throws IOException {
        if (stack.peek() == null) {
            return -1;
        } else {
            return stack.peek().offset;
        }
    }


    public long getSegmentLength() throws IOException {
        if (stack.peek() == null) {
            return -1;
        } else {
            return getStreamPosition() - stack.peek().offset - 2;
        }
    }

    public void finish() throws IOException {
        while (!stack.empty()) {
            popSegment();
        }
    }

    @Override
    public void close() throws IOException {
        try {
            finish();
        } finally {
            out.close();
        }
    }


    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        if (stack.size() == 0 || standaloneMarkers.contains(stack.peek().marker)) {
            writeStuffed(b, off, len);
        } else {
            writeNonstuffed(b, off, len);
        }
    }


    @Override
    public void write(int b) throws IOException {
        if (stack.size() == 0 || standaloneMarkers.contains(stack.peek().marker)) {
            writeStuffed(b);
        } else {
            writeNonstuffed(b);
        }
    }


    private void writeNonstuffed(byte[] b, int off, int len) throws IOException {
        out.write(b, off, len);
    }


    private void writeNonstuffed(int b) throws IOException {
        out.write(b);
    }


    private void writeStuffed(byte[] b, int off, int len) throws IOException {
        int n = off + len;
        for (int i = off; i < n; i++) {
            if (b[i] == -1) {
                out.write(b, off, i - off + 1);
                out.write(0);
                off = i + 1;
            }
        }
        if (n - off > 0) {
            out.write(b, off, n - off);
        }
    }


    private void writeStuffed(int b) throws IOException {
        out.write(0xff);
        if (b == 0xff) {
            out.write(0);
        }
    }


    private class Segment {


        protected int marker;

        protected long offset;
        protected boolean finished;


        public Segment(int marker) throws IOException {
            this.marker = marker;
            if (marker != 0) {
                out.writeShort(marker);
                offset = getStreamPosition();
                if (!standaloneMarkers.contains(marker)) {
                    out.writeShort(0);
                }
            }
        }


        public void finish() throws IOException {
            if (!finished) {
                if (!standaloneMarkers.contains(marker)) {
                    long size = getStreamPosition() - offset;
                    if (size > 0xffffL) {
                        throw new IOException("Segment \"" + marker + "\" is too large: " + size);
                    }

                    long pointer = getStreamPosition();
                    seek(offset);
                    out.writeShort((short) size);

                    seek(pointer);
                }

                finished = true;
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy