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

org.monte.media.iff.IFFOutputStream Maven / Gradle / Ivy

The newest version!

package org.monte.media.iff;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Stack;
import javax.imageio.stream.ImageOutputStream;


public class IFFOutputStream extends OutputStream {

    private byte[] writeBuffer = new byte[4];
    private Stack stack = new Stack();
    private ImageOutputStream out;
    private long streamOffset;

    public IFFOutputStream(ImageOutputStream out) throws IOException {
        this.out = out;
        streamOffset = out.getStreamPosition();
    }

    public void pushCompositeChunk(String compositeType, String chunkType) throws IOException {
        stack.push(new CompositeChunk(compositeType, chunkType));
    }

    public void pushDataChunk(String chunkType) throws IOException {
        stack.push(new DataChunk(chunkType));
    }

    public void popChunk() throws IOException {
        Chunk chunk = stack.pop();
        chunk.finish();
    }

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

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

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        out.write(b, off, len);
    }

    @Override
    public void write(int b) throws IOException {
        out.write(b);
    }

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

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

    
    private abstract class Chunk {

        
        protected String chunkType;
        
        protected long offset;
        protected boolean finished;

        
        public Chunk(String chunkType) throws IOException {
            this.chunkType = chunkType;
            offset = getStreamPosition();
        }

        
        public abstract void finish() throws IOException;

        public abstract boolean isComposite();
    }

    
    private class CompositeChunk extends Chunk {

        
        protected String compositeType;

        
        public CompositeChunk(String compositeType, String chunkType) throws IOException {
            super(chunkType);
            this.compositeType = compositeType;

            out.writeLong(0);
            out.writeInt(0);
        }

        
        @Override
        public void finish() throws IOException {
            if (!finished) {
                long size = getStreamPosition() - offset;
                if (size > 0xffffffffL) {
                    throw new IOException("CompositeChunk \"" + chunkType + "\" is too large: " + size);
                }

                long pointer = getStreamPosition();
                seek(offset);

                writeTYPE(compositeType);
                writeULONG(size - 8);
                writeTYPE(chunkType);
                seek(pointer);
                if (size % 2 == 1) {
                    out.writeByte(0);
                }
                finished = true;
            }
        }

        @Override
        public boolean isComposite() {
            return true;
        }
    }

    
    private class DataChunk extends Chunk {

        
        public DataChunk(String name) throws IOException {
            super(name);
            out.writeLong(0);
        }

        @Override
        public void finish() throws IOException {
            if (!finished) {
                long size = getStreamPosition() - offset;
                if (size > 0xffffffffL) {
                    throw new IOException("DataChunk \"" + chunkType + "\" is too large: " + size);
                }

                long pointer = getStreamPosition();
                seek(offset);

                writeTYPE(chunkType);
                writeULONG(size - 8);
                seek(pointer);
                if (size % 2 == 1) {
                    out.writeByte(0);
                }
                finished = true;
            }
        }

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

    public void writeLONG(int v) throws IOException {
        writeBuffer[0] = (byte) (v >>> 24);
        writeBuffer[1] = (byte) (v >>> 16);
        writeBuffer[2] = (byte) (v >>> 8);
        writeBuffer[3] = (byte) (v >>> 0);
        out.write(writeBuffer, 0, 4);
    }

    public void writeULONG(long v) throws IOException {
        writeBuffer[0] = (byte) (v >>> 24);
        writeBuffer[1] = (byte) (v >>> 16);
        writeBuffer[2] = (byte) (v >>> 8);
        writeBuffer[3] = (byte) (v >>> 0);
        out.write(writeBuffer, 0, 4);
    }

    public void writeWORD(int v) throws IOException {
        writeBuffer[0] = (byte) (v >>> 8);
        writeBuffer[1] = (byte) (v >>> 0);
        out.write(writeBuffer, 0, 2);
    }

    public void writeUWORD(int v) throws IOException {
        writeBuffer[0] = (byte) (v >>> 8);
        writeBuffer[1] = (byte) (v >>> 0);
        out.write(writeBuffer, 0, 2);
    }

    public void writeUBYTE(int v) throws IOException {
        out.write(v);
    }

    
    public void writeTYPE(String s) throws IOException {
        if (s.length() != 4) {
            throw new IllegalArgumentException("type string must have 4 characters");
        }

        try {
            out.write(s.getBytes("ASCII"), 0, 4);
        } catch (UnsupportedEncodingException e) {
            throw new InternalError(e.toString());
        }
    }

    
    public void writeByteRun1(byte[] data) throws IOException {
        writeByteRun1(data, 0, data.length);
    }

    public void writeByteRun1(byte[] data, int offset, int length) throws IOException {
        int end = offset + length;


        int literalOffset = offset;
        int i;
        for (i = offset; i < end; i++) {

            byte b = data[i];


            int repeatCount = i + 1;
            for (; repeatCount < end; repeatCount++) {
                if (data[repeatCount] != b) {
                    break;
                }
            }
            repeatCount = repeatCount - i;

            if (repeatCount == 1) {

                if (i - literalOffset > 127) {
                    write(i - literalOffset - 1);
                    write(data, literalOffset, i - literalOffset);
                    literalOffset = i;
                }



            } else if (repeatCount == 2
                    && literalOffset < i && i - literalOffset < 127) {
                i++;
            } else {

                if (literalOffset < i) {
                    write(i - literalOffset - 1);
                    write(data, literalOffset, i - literalOffset);
                }

                i += repeatCount - 1;
                literalOffset = i + 1;


                for (; repeatCount > 128; repeatCount -= 128) {
                    write(-127);
                    write(b);
                }
                write(-repeatCount + 1);
                write(b);
            }
        }


        if (literalOffset < end) {
            write(i - literalOffset - 1);
            write(data, literalOffset, i - literalOffset);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy