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

org.monte.media.avi.ZMBVCodecCore Maven / Gradle / Ivy

The newest version!

package org.monte.media.avi;


import java.util.zip.InflaterInputStream;
import java.io.IOException;
import org.monte.media.io.AppendableByteArrayInputStream;
import org.monte.media.io.ByteArrayImageInputStream;
import org.monte.media.io.UncachedImageInputStream;
import java.nio.ByteOrder;
import javax.imageio.stream.ImageInputStream;
import static java.lang.Math.*;

public class ZMBVCodecCore {

    public final static int VIDEOMODE_NONE = 0;
    public final static int VIDEOMODE_1_BIT_PALETTIZED = 1;
    public final static int VIDEOMODE_2_BIT_PALETTIZED = 2;
    public final static int VIDEOMODE_4_BIT_PALETTIZED = 3;
    public final static int VIDEOMODE_8_BIT_PALETTIZED = 4;
    public final static int VIDEOMODE_15_BIT_BGR = 5;
    public final static int VIDEOMODE_16_BIT_BGR = 6;
    public final static int VIDEOMODE_24_BIT_BGR = 7;
    public final static int VIDEOMODE_32_BIT_BGR = 8;
    public final static int COMPRESSION_NONE = 0;
    public final static int COMPRESSION_ZLIB = 1;
    private int majorVersion;
    private int minorVersion;
    private int compressionType;
    private int videoFormat;
    private int blockWidth, blockHeight;
    private InflaterInputStream inflaterInputStream;
    private AppendableByteArrayInputStream byteArrayInputStream;
    private int[] palette;
    private byte[] blockDataBuf;
    private byte[] blockHeaderBuf;

    public boolean decode(byte[] inDat, int off, int length, int[] outDat, int[] prevDat, int width, int height, boolean onlyDecodeIfKeyframe) {
        boolean isKeyframe = false;
        try {
            ImageInputStream in = new ByteArrayImageInputStream(inDat, off, length, ByteOrder.LITTLE_ENDIAN);

            int flags = in.readUnsignedByte();
            isKeyframe = (flags & 1) != 0;

            if (onlyDecodeIfKeyframe && !isKeyframe) {
                System.out.println("ZMBVCodec cannot decode delta without preceeding keyframe.");
                return false;
            }

            if (isKeyframe) {
                // => Key frame
                //System.out.println("ZMBVCode Keyframe w,h=" + width + "," + height);
                majorVersion = in.readUnsignedByte();
                minorVersion = in.readUnsignedByte();
                compressionType = in.readUnsignedByte();
                videoFormat = in.readUnsignedByte();
                blockWidth = in.readUnsignedByte();
                blockHeight = in.readUnsignedByte();
            }
            if (majorVersion != 0 || minorVersion != 1) {
                System.err.println("unsupported version " + majorVersion + "." + minorVersion);
                return isKeyframe;
            }


            switch (compressionType) {
                case COMPRESSION_ZLIB:

                    if (!isKeyframe && inflaterInputStream != null) {
                        // => streams are present.
                        //    Append new data.
                        AppendableByteArrayInputStream bais = byteArrayInputStream;
                        bais.appendBuffer(inDat, (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()), true);
                    } else {
                        // => Keyframe or no Inflater Stream present. Create new one, and ensure
                        //    that we can append new data to it later on.
                        if (byteArrayInputStream != null) {
                            byteArrayInputStream.setBuffer(inDat, (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()));
                        } else {
                            byteArrayInputStream = new AppendableByteArrayInputStream(inDat.clone(), (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()));
                        }
                        if (inflaterInputStream != null) {
                            inflaterInputStream.close();
                        }
                        inflaterInputStream = new InflaterInputStream(byteArrayInputStream);
                    }
                    in = new UncachedImageInputStream(inflaterInputStream, ByteOrder.LITTLE_ENDIAN);
                    break;
                case COMPRESSION_NONE:
                    System.out.println(" NO COMPRESSION");
                    return isKeyframe;
                default:
                    System.err.println("unsupported compression type " + compressionType);
                    return isKeyframe;

            }

            switch (videoFormat) {
                case VIDEOMODE_8_BIT_PALETTIZED:
                    decode8to32(in, outDat, prevDat, flags, width, height);
                    break;

                case VIDEOMODE_15_BIT_BGR:
                    decode15to32(in, outDat, prevDat, flags, width, height);
                    break;
                case VIDEOMODE_16_BIT_BGR:
                    decode16to32(in, outDat, prevDat, flags, width, height);
                    break;
                case VIDEOMODE_32_BIT_BGR:
                    decode32to32(in, outDat, prevDat, flags, width, height);
                    break;

                default:
                    throw new UnsupportedOperationException("Unsupported video format " + videoFormat);
            }
        } catch (IOException ex) {
            //System.out.println("ZMBVCodecCore "+ex);
            System.err.println("ZMBVCodecCore decoding, isKeyframe=" + isKeyframe);
            ex.printStackTrace();
        }
        return isKeyframe;
    }

    public boolean decode(byte[] inDat, int off, int length, byte[] outDat, byte[] prevDat, int width, int height, boolean onlyDecodeIfKeyframe) {
        boolean isKeyframe = false;
        try {
            ImageInputStream in = new ByteArrayImageInputStream(inDat, off, length, ByteOrder.LITTLE_ENDIAN);

            int flags = in.readUnsignedByte();
            isKeyframe = (flags & 1) != 0;

            if (onlyDecodeIfKeyframe && !isKeyframe) {
                System.out.println("ZMBVCodec cannot decode delta without preceeding keyframe.");
                return false;
            }

            if (isKeyframe) {
                // => Key frame
                //System.out.println("ZMBVCode Keyframe w,h=" + width + "," + height);
                majorVersion = in.readUnsignedByte();
                minorVersion = in.readUnsignedByte();
                compressionType = in.readUnsignedByte();
                videoFormat = in.readUnsignedByte();
                blockWidth = in.readUnsignedByte();
                blockHeight = in.readUnsignedByte();
            }
            if (majorVersion != 0 || minorVersion != 1) {
                System.err.println("unsupported version " + majorVersion + "." + minorVersion);
                return isKeyframe;
            }


            switch (compressionType) {
                case COMPRESSION_ZLIB:

                    if (!isKeyframe && inflaterInputStream != null) {
                        // => streams are present.
                        //    Append new data.
                        AppendableByteArrayInputStream bais = byteArrayInputStream;
                        bais.appendBuffer(inDat, (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()), true);
                    } else {
                        // => Keyframe or no Inflater Stream present. Create new one, and ensure
                        //    that we can append new data to it later on.
                        if (byteArrayInputStream != null) {
                            byteArrayInputStream.setBuffer(inDat, (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()));
                        } else {
                            byteArrayInputStream = new AppendableByteArrayInputStream(inDat.clone(), (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()));
                        }
                        if (inflaterInputStream != null) {
                            inflaterInputStream.close();
                        }
                        inflaterInputStream = new InflaterInputStream(byteArrayInputStream);
                    }
                    in = new UncachedImageInputStream(inflaterInputStream, ByteOrder.LITTLE_ENDIAN);
                    break;
                case COMPRESSION_NONE:
                    System.out.println(" NO COMPRESSION");
                    return isKeyframe;
                default:
                    System.err.println("unsupported compression type " + compressionType);
                    return isKeyframe;

            }

            switch (videoFormat) {
                case VIDEOMODE_8_BIT_PALETTIZED:
                    decode8to8(in, outDat, prevDat, flags, width, height);
                    break;
                /*
                case VIDEOMODE_15_BIT_BGR:
                decode15BitBGR(in, outDat, prevDat, flags, width, height);
                break;
                case VIDEOMODE_16_BIT_BGR:
                decode16BitBGR(in, outDat, prevDat, flags, width, height);
                break;
                case VIDEOMODE_32_BIT_BGR:
                decode32BitBGR(in, outDat, prevDat, flags, width, height);
                break;
                 * */
                default:
                    throw new UnsupportedOperationException("Unsupported video format " + videoFormat);
            }
        } catch (IOException ex) {
            //System.out.println("ZMBVCodecCore "+ex);
            System.err.println("ZMBVCodecCore decoding, isKeyframe=" + isKeyframe);
            ex.printStackTrace();
        }
        return isKeyframe;
    }

    public int decode(byte[] inDat, int off, int length, Object[] outDatHolder, Object[] prevDatHolder, int width, int height, boolean onlyDecodeIfKeyframe) {
        boolean isKeyframe = false;
        int depth = 0;
        try {
            ImageInputStream in = new ByteArrayImageInputStream(inDat, off, length, ByteOrder.LITTLE_ENDIAN);

            int flags = in.readUnsignedByte();
            isKeyframe = (flags & 1) != 0;

            if (onlyDecodeIfKeyframe && !isKeyframe) {
                System.err.println("ZMBVCodec cannot decode delta without preceeding keyframe.");
                return 0;
            }

            if (isKeyframe) {
                // => Key frame
                //System.out.println("ZMBVCode Keyframe w,h=" + width + "," + height);
                majorVersion = in.readUnsignedByte();
                minorVersion = in.readUnsignedByte();
                compressionType = in.readUnsignedByte();
                videoFormat = in.readUnsignedByte();
                blockWidth = in.readUnsignedByte();
                blockHeight = in.readUnsignedByte();
            }
            if (majorVersion != 0 || minorVersion != 1) {
                System.err.println("unsupported version " + majorVersion + "." + minorVersion);
                return 0;
            }


            switch (compressionType) {
                case COMPRESSION_ZLIB:

                    if (!isKeyframe && inflaterInputStream != null) {
                        // => streams are present.
                        //    Append new data.
                        AppendableByteArrayInputStream bais = byteArrayInputStream;
                        bais.appendBuffer(inDat, (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()), true);
                    } else {
                        // => Keyframe or no Inflater Stream present. Create new one, and ensure
                        //    that we can append new data to it later on.
                        if (byteArrayInputStream != null) {
                            byteArrayInputStream.setBuffer(inDat, (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()));
                        } else {
                            byteArrayInputStream = new AppendableByteArrayInputStream(inDat.clone(), (int) in.getStreamPosition() + off, (int) (length - in.getStreamPosition()));
                        }
                        if (inflaterInputStream != null) {
                            inflaterInputStream.close();
                        }
                        inflaterInputStream = new InflaterInputStream(byteArrayInputStream);
                    }
                    in = new UncachedImageInputStream(inflaterInputStream, ByteOrder.LITTLE_ENDIAN);
                    break;
                case COMPRESSION_NONE:
                    System.err.println(" NO COMPRESSION");
                    return 0;
                default:
                    System.err.println("unsupported compression type " + compressionType);
                    return 0;

            }

            switch (videoFormat) {
                case VIDEOMODE_8_BIT_PALETTIZED:
                    depth = 8;
                    if (!(outDatHolder[0] instanceof byte[])) {
                        outDatHolder[0] = new byte[width * height];
                    }
                    if (!(prevDatHolder[0] instanceof byte[])) {
                        prevDatHolder[0] = new byte[width * height];
                    }
                    decode8to8(in, (byte[]) outDatHolder[0], (byte[]) prevDatHolder[0], flags, width, height);
                    break;

                case VIDEOMODE_15_BIT_BGR:
                    depth = 15;
                    if (!(outDatHolder[0] instanceof short[])) {
                        outDatHolder[0] = new short[width * height];
                    }
                    if (!(prevDatHolder[0] instanceof short[])) {
                        prevDatHolder[0] = new short[width * height];
                    }
                    decode15to15(in, (short[]) outDatHolder[0], (short[]) prevDatHolder[0], flags, width, height);
                    break;
                case VIDEOMODE_16_BIT_BGR:
                    depth = 16;
                    if (!(outDatHolder[0] instanceof short[])) {
                        outDatHolder[0] = new short[width * height];
                    }
                    if (!(prevDatHolder[0] instanceof short[])) {
                        prevDatHolder[0] = new short[width * height];
                    }
                    decode16to16(in, (short[]) outDatHolder[0], (short[]) prevDatHolder[0], flags, width, height);
                    break;
                case VIDEOMODE_32_BIT_BGR:
                    depth = 32;
                    if (!(outDatHolder[0] instanceof int[])) {
                        outDatHolder[0] = new short[width * height];
                    }
                    if (!(prevDatHolder[0] instanceof int[])) {
                        prevDatHolder[0] = new short[width * height];
                    }
                    decode32to32(in, (int[]) outDatHolder[0], (int[]) prevDatHolder[0], flags, width, height);
                    break;

                default:
                    throw new UnsupportedOperationException("Unsupported video format " + videoFormat);
            }
        } catch (IOException ex) {
            //System.out.println("ZMBVCodecCore "+ex);
            System.err.println("ZMBVCodecCore decoding, isKeyframe=" + isKeyframe);
            ex.printStackTrace();
        }
        return isKeyframe ? -depth : depth;
    }

    private void decode8to32(ImageInputStream in, int[] outDat, int[] prevDat, int flags, int width, int height) throws IOException {
        boolean isKeyframe = (flags & 1) != 0;
        boolean isPaletteChange = (flags & 2) != 0;

        // palette each entry contains a 32-bit entry constisting of:
        // {palette index, red, green, blue}.
        if (palette == null) {
            palette = new int[256];
        }
        int blockSize = blockWidth * blockHeight;

        if (blockDataBuf == null || blockDataBuf.length < max(3, blockSize)) {
            blockDataBuf = new byte[max(3, blockSize)];
        }

        byte[] buf = blockDataBuf;
        if (isKeyframe) {
            // => Key frame.

            // Read palette
            for (int i = 0; i < 256; i++) {
                in.readFully(buf, 0, 3);
                palette[i] = ((buf[2] & 0xff)) | ((buf[1] & 0xff) << 8) | ((buf[0] & 0xff) << 16) | (i << 24);
            }

            // Process raw pixels
            for (int i = 0, n = width * height; i < n; i++) {
                outDat[i] = palette[in.readUnsignedByte()];
            }

        } else {
            // => Delta frame.

            // Optionally update palette
            if (isPaletteChange) {
                for (int i = 0; i < 256; i++) {
                    in.readFully(buf, 0, 3);
                    palette[i] ^= ((buf[2] & 0xff)) | ((buf[1] & 0xff) << 8) | ((buf[0] & 0xff) << 16);
                }
            }

            // Read block headers
            int nbx = (width + blockWidth - 1) / blockWidth;
            int nby = (height + blockHeight - 1) / blockHeight;
            int blockHeaderSize = ((nbx * nby * 2 + 3) & ~3);
            if (blockHeaderBuf == null || blockHeaderBuf.length < blockHeaderSize) {
                blockHeaderBuf = new byte[blockHeaderSize];
            }
            byte[] blocks = blockHeaderBuf;
            in.readFully(blocks, 0, blockHeaderSize);

            // Process block data
            int block = 0;
            for (int by = 0; by < height; by += blockHeight) {
                int bh2 = min(height - by, blockHeight);
                for (int bx = 0; bx < width; bx += blockWidth) {
                    int bw2 = min(width - bx, blockWidth);
                    int a = blocks[block++];
                    int b = blocks[block++];
                    int dx = a >> 1;
                    int dy = b >> 1;
                    int flag = a & 1;

                    if (flag == 0) {
                        // => copy block from offset dx,dy from previous frame
                        //    motion vectors out of bounds are used to zero blocks.
                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            if (py < 0 || height <= py) {
                                for (int x = 0; x < bw2; x++) {
                                    outDat[iout++] = palette[0];
                                }
                            } else {
                                for (int x = 0; x < bw2; x++) {
                                    int px = bx + x + dx;
                                    if (0 <= px && px < width) {
                                        outDat[iout++] = palette[prevDat[px + py * width] >>> 24];
                                    } else {
                                        outDat[iout++] = palette[0];
                                    }
                                }
                            }

                        }
                    } else {
                        // => XOR block with data read from stream
                        in.readFully(buf, 0, bw2 * bh2);
                        int iblock = 0;

                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                int paletteIndex = buf[iblock++] & 0xff;
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    paletteIndex ^= prevDat[px + py * width] >>> 24;
                                }
                                outDat[iout] = palette[paletteIndex];
                                iout++;
                            }


                        }
                    }
                }
            }
        }
    }

    private void decode8to8(ImageInputStream in, byte[] outDat, byte[] prevDat, int flags, int width, int height) throws IOException {
        boolean isKeyframe = (flags & 1) != 0;
        boolean isPaletteChange = (flags & 2) != 0;

        // palette each entry contains a 32-bit entry constisting of:
        // {palette index, red, green, blue}.
        if (palette == null) {
            palette = new int[256];
        }
        int blockSize = blockWidth * blockHeight;

        if (blockDataBuf == null || blockDataBuf.length < max(3, blockSize)) {
            blockDataBuf = new byte[max(3, blockSize)];
        }

        byte[] buf = blockDataBuf;
        if (isKeyframe) {
            // => Key frame.

            // Read palette
            for (int i = 0; i < 256; i++) {
                in.readFully(buf, 0, 3);
                palette[i] = ((buf[2] & 0xff)) | ((buf[1] & 0xff) << 8) | ((buf[0] & 0xff) << 16) | (i << 24);
            }

            // Process raw pixels
            for (int i = 0, n = width * height; i < n; i++) {
                outDat[i] = in.readByte();
            }

        } else {
            // => Delta frame.

            // Optionally update palette
            if (isPaletteChange) {
                for (int i = 0; i < 256; i++) {
                    in.readFully(buf, 0, 3);
                    palette[i] ^= ((buf[2] & 0xff)) | ((buf[1] & 0xff) << 8) | ((buf[0] & 0xff) << 16);
                }
            }

            // Read block headers
            int nbx = (width + blockWidth - 1) / blockWidth;
            int nby = (height + blockHeight - 1) / blockHeight;
            int blockHeaderSize = ((nbx * nby * 2 + 3) & ~3);
            if (blockHeaderBuf == null || blockHeaderBuf.length < blockHeaderSize) {
                blockHeaderBuf = new byte[blockHeaderSize];
            }
            byte[] blocks = blockHeaderBuf;
            in.readFully(blocks, 0, blockHeaderSize);

            // Process block data
            int block = 0;
            for (int by = 0; by < height; by += blockHeight) {
                int bh2 = min(height - by, blockHeight);
                for (int bx = 0; bx < width; bx += blockWidth) {
                    int bw2 = min(width - bx, blockWidth);
                    int a = blocks[block++];
                    int b = blocks[block++];
                    int dx = a >> 1;
                    int dy = b >> 1;
                    int flag = a & 1;

                    if (flag == 0) {
                        // => copy block from offset dx,dy from previous frame
                        //    motion vectors out of bounds are used to zero blocks.
                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            if (py < 0 || height <= py) {
                                for (int x = 0; x < bw2; x++) {
                                    outDat[iout++] = 0;
                                }
                            } else {
                                for (int x = 0; x < bw2; x++) {
                                    int px = bx + x + dx;
                                    if (0 <= px && px < width) {
                                        outDat[iout++] = prevDat[px + py * width];
                                    } else {
                                        outDat[iout++] = 0;
                                    }
                                }
                            }

                        }
                    } else {
                        // => XOR block with data read from stream
                        in.readFully(buf, 0, bw2 * bh2);
                        int iblock = 0;

                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                byte paletteIndex = buf[iblock++];
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    paletteIndex ^= prevDat[px + py * width];
                                }
                                outDat[iout] = paletteIndex;
                                iout++;
                            }


                        }
                    }
                }
            }
        }
    }

    private void decode15to32(ImageInputStream in, int[] outDat, int[] prevDat, int flags, int width, int height) throws IOException {
        boolean isKeyframe = (flags & 1) != 0;

        int blockSize = blockWidth * blockHeight;
        if (blockDataBuf == null || blockDataBuf.length < max(3, blockSize * 2)) {
            blockDataBuf = new byte[max(3, blockSize * 2)];
        }

        byte[] buf = blockDataBuf;
        if (isKeyframe) {
            // => Key frame.

            // Process raw pixels
            for (int i = 0, n = width * height; i < n; i++) {
                int bgr = in.readUnsignedShort();
                outDat[i] = ((bgr & (0x1f << 5)) << 6) | ((bgr & (0x1c << 5)) << 1)//green
                        | ((bgr & (0x1f << 10)) << 9) | ((bgr & (0x1c << 10)) << 4) // red
                        | ((bgr & (0x1f << 0)) << 3) | ((bgr & (0x1c << 0)) >>> 2) // blue
                        ;
            }

        } else {
            // => Delta frame.

            // Read block headers
            int nbx = (width + blockWidth - 1) / blockWidth;
            int nby = (height + blockHeight - 1) / blockHeight;
            int blockHeaderSize = ((nbx * nby * 2 + 3) & ~3);
            //System.out.println("blockHeaderSize=" + blockHeaderSize + " blockSize x,y=" + blockWidth + "," + blockHeight);
            if (blockHeaderBuf == null || blockHeaderBuf.length < blockHeaderSize) {
                blockHeaderBuf = new byte[blockHeaderSize];
            }
            byte[] blocks = blockHeaderBuf;
            in.readFully(blocks, 0, blockHeaderSize);

            // Process block data
            int block = 0;
            for (int by = 0; by < height; by += blockHeight) {
                int bh2 = min(height - by, blockHeight);
                for (int bx = 0; bx < width; bx += blockWidth) {
                    int bw2 = min(width - bx, blockWidth);
                    int a = blocks[block++];
                    int b = blocks[block++];
                    int dx = a >> 1;
                    int dy = b >> 1;
                    int flag = a & 1;

                    if (flag == 0) {
                        // => copy block from offset dx,dy from previous frame
                        //    motion vectors out of bounds are used to zero blocks.
                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            if (py < 0 || height <= py) {
                                for (int x = 0; x < bw2; x++) {
                                    outDat[iout++] = 0;
                                }
                            } else {
                                for (int x = 0; x < bw2; x++) {
                                    int px = bx + x + dx;
                                    if (0 <= px && px < width) {
                                        outDat[iout++] = prevDat[px + py * width];
                                    } else {
                                        outDat[iout++] = 0;
                                    }
                                }
                            }

                        }
                    } else {
                        // => XOR block with data read from stream
                        in.readFully(buf, 0, bw2 * bh2 * 2);
                        int iblock = 0;

                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                int bgr = ((buf[iblock++] & 0xff)) | ((buf[iblock++] & 0xff) << 8);
                                int rgb = ((bgr & (0x1f << 5)) << 6) | ((bgr & (0x1c << 5)) << 1)//green
                                        | ((bgr & (0x1f << 10)) << 9) | ((bgr & (0x1c << 10)) << 4) // red
                                        | ((bgr & (0x1f << 0)) << 3) | ((bgr & (0x1c << 0)) >>> 2) // blue
                                        ;
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    rgb ^= prevDat[px + py * width];
                                }
                                outDat[iout] = rgb;
                                iout++;
                            }
                        }
                    }
                }
            }
        }
    }

    private void decode15to15(ImageInputStream in, short[] outDat, short[] prevDat, int flags, int width, int height) throws IOException {
        boolean isKeyframe = (flags & 1) != 0;

        int blockSize = blockWidth * blockHeight;
        if (blockDataBuf == null || blockDataBuf.length < max(3, blockSize * 2)) {
            blockDataBuf = new byte[max(3, blockSize * 2)];
        }

        byte[] buf = blockDataBuf;
        if (isKeyframe) {
            // => Key frame.

            // Process raw pixels
            for (int i = 0, n = width * height; i < n; i++) {
                int bgr = in.readUnsignedShort();
                outDat[i] = (short) bgr;
            }

        } else {
            // => Delta frame.

            // Read block headers
            int nbx = (width + blockWidth - 1) / blockWidth;
            int nby = (height + blockHeight - 1) / blockHeight;
            int blockHeaderSize = ((nbx * nby * 2 + 3) & ~3);
            //System.out.println("blockHeaderSize=" + blockHeaderSize + " blockSize x,y=" + blockWidth + "," + blockHeight);
            if (blockHeaderBuf == null || blockHeaderBuf.length < blockHeaderSize) {
                blockHeaderBuf = new byte[blockHeaderSize];
            }
            byte[] blocks = blockHeaderBuf;
            in.readFully(blocks, 0, blockHeaderSize);

            // Process block data
            int block = 0;
            for (int by = 0; by < height; by += blockHeight) {
                int bh2 = min(height - by, blockHeight);
                for (int bx = 0; bx < width; bx += blockWidth) {
                    int bw2 = min(width - bx, blockWidth);
                    int a = blocks[block++];
                    int b = blocks[block++];
                    int dx = a >> 1;
                    int dy = b >> 1;
                    int flag = a & 1;

                    if (flag == 0) {
                        // => copy block from offset dx,dy from previous frame
                        //    motion vectors out of bounds are used to zero blocks.
                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            if (py < 0 || height <= py) {
                                for (int x = 0; x < bw2; x++) {
                                    outDat[iout++] = 0;
                                }
                            } else {
                                for (int x = 0; x < bw2; x++) {
                                    int px = bx + x + dx;
                                    if (0 <= px && px < width) {
                                        outDat[iout++] = prevDat[px + py * width];
                                    } else {
                                        outDat[iout++] = 0;
                                    }
                                }
                            }

                        }
                    } else {
                        // => XOR block with data read from stream
                        in.readFully(buf, 0, bw2 * bh2 * 2);
                        int iblock = 0;

                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                int bgr = ((buf[iblock++] & 0xff)) | ((buf[iblock++] & 0xff) << 8);
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    bgr ^= prevDat[px + py * width];
                                }
                                outDat[iout] = (short) bgr;
                                iout++;
                            }
                        }
                    }
                }
            }
        }
    }

    private void decode16to32(ImageInputStream in, int[] outDat, int[] prevDat, int flags, int width, int height) throws IOException {
        boolean isKeyframe = (flags & 1) != 0;

        int blockSize = blockWidth * blockHeight;
        if (blockDataBuf == null || blockDataBuf.length < max(3, blockSize * 2)) {
            blockDataBuf = new byte[max(3, blockSize * 2)];
        }

        byte[] buf = blockDataBuf;
        if (isKeyframe) {
            // => Key frame.

            // Process raw pixels
            for (int i = 0, n = width * height; i < n; i++) {
                int bgr = in.readUnsignedShort();
                outDat[i] = ((bgr & (0x3f << 5)) << 5) | ((bgr & (0x30 << 5)) >> 1)//green
                        | ((bgr & (0x1f << 11)) << 8) | ((bgr & (0x1c << 11)) << 3) // red
                        | ((bgr & (0x1f << 0)) << 3) | ((bgr & (0x1c << 0)) >>> 2) // blue
                        ;
            }

        } else {
            // => Delta frame.

            // Read block headers
            int nbx = (width + blockWidth - 1) / blockWidth;
            int nby = (height + blockHeight - 1) / blockHeight;
            int blockHeaderSize = ((nbx * nby * 2 + 3) & ~3);
            if (blockHeaderBuf == null || blockHeaderBuf.length < blockHeaderSize) {
                blockHeaderBuf = new byte[blockHeaderSize];
            }
            byte[] blocks = blockHeaderBuf;
            in.readFully(blocks, 0, blockHeaderSize);

            // Process block data
            int block = 0;
            for (int by = 0; by < height; by += blockHeight) {
                int bh2 = min(height - by, blockHeight);
                for (int bx = 0; bx < width; bx += blockWidth) {
                    int bw2 = min(width - bx, blockWidth);
                    int a = blocks[block++];
                    int b = blocks[block++];
                    int dx = a >> 1;
                    int dy = b >> 1;
                    int flag = a & 1;

                    if (flag == 0) {
                        // => copy block from offset dx,dy from previous frame
                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    outDat[iout] = prevDat[px + py * width];
                                } else {
                                    outDat[iout] = 0;
                                }
                                iout++;
                            }

                        }
                    } else {
                        // => XOR block with data read from stream
                        in.readFully(buf, 0, bw2 * bh2 * 2);
                        int iblock = 0;

                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                int bgr = ((buf[iblock++] & 0xff)) | ((buf[iblock++] & 0xff) << 8);
                                int rgb = ((bgr & (0x3f << 5)) << 5) | ((bgr & (0x30 << 5)) >> 1)//green
                                        | ((bgr & (0x1f << 11)) << 8) | ((bgr & (0x1c << 11)) << 3) // red
                                        | ((bgr & (0x1f << 0)) << 3) | ((bgr & (0x1c << 0)) >>> 2) // blue
                                        ;
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    rgb ^= prevDat[px + py * width];
                                }
                                outDat[iout] = (short) bgr;
                                iout++;
                            }


                        }
                    }
                }
            }
        }
    }

    private void decode16to16(ImageInputStream in, short[] outDat, short[] prevDat, int flags, int width, int height) throws IOException {
        boolean isKeyframe = (flags & 1) != 0;

        int blockSize = blockWidth * blockHeight;
        if (blockDataBuf == null || blockDataBuf.length < max(3, blockSize * 2)) {
            blockDataBuf = new byte[max(3, blockSize * 2)];
        }

        byte[] buf = blockDataBuf;
        if (isKeyframe) {
            // => Key frame.

            // Process raw pixels
            for (int i = 0, n = width * height; i < n; i++) {
                int bgr = in.readUnsignedShort();
                outDat[i] = (short) bgr;
            }

        } else {
            // => Delta frame.

            // Read block headers
            int nbx = (width + blockWidth - 1) / blockWidth;
            int nby = (height + blockHeight - 1) / blockHeight;
            int blockHeaderSize = ((nbx * nby * 2 + 3) & ~3);
            if (blockHeaderBuf == null || blockHeaderBuf.length < blockHeaderSize) {
                blockHeaderBuf = new byte[blockHeaderSize];
            }
            byte[] blocks = blockHeaderBuf;
            in.readFully(blocks, 0, blockHeaderSize);

            // Process block data
            int block = 0;
            for (int by = 0; by < height; by += blockHeight) {
                int bh2 = min(height - by, blockHeight);
                for (int bx = 0; bx < width; bx += blockWidth) {
                    int bw2 = min(width - bx, blockWidth);
                    int a = blocks[block++];
                    int b = blocks[block++];
                    int dx = a >> 1;
                    int dy = b >> 1;
                    int flag = a & 1;

                    if (flag == 0) {
                        // => copy block from offset dx,dy from previous frame
                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    outDat[iout] = prevDat[px + py * width];
                                } else {
                                    outDat[iout] = 0;
                                }
                                iout++;
                            }

                        }
                    } else {
                        // => XOR block with data read from stream
                        in.readFully(buf, 0, bw2 * bh2 * 2);
                        int iblock = 0;

                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                int bgr = ((buf[iblock++] & 0xff)) | ((buf[iblock++] & 0xff) << 8);
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    bgr ^= prevDat[px + py * width];
                                }
                                outDat[iout] = (short) bgr;
                                iout++;
                            }


                        }
                    }
                }
            }
        }
    }

    private void decode32to32(ImageInputStream in, int[] outDat, int[] prevDat, int flags, int width, int height) throws IOException {
        boolean isKeyframe = (flags & 1) != 0;

        int blockSize = blockWidth * blockHeight;
        if (blockDataBuf == null || blockDataBuf.length < max(3, blockSize * 4)) {
            blockDataBuf = new byte[max(3, blockSize * 4)];
        }

        byte[] buf = blockDataBuf;
        if (isKeyframe) {
            // => Key frame.

            // Process raw pixels
            for (int i = 0, n = width * height; i < n; i++) {
                int bgr = in.readInt();
                outDat[i] = bgr;
                /*((bgr & (0x3f << 5))<<5)| ((bgr & (0x30 << 5)) >>1)//green
                |((bgr & (0x1f << 11)) << 8) | ((bgr & (0x1c << 11)) << 3) // red
                |((bgr & (0x1f << 0)) << 3) | ((bgr & (0x1c << 0)) >>> 2) // blue
                ; */
            }

        } else {
            // => Delta frame.

            // Read block headers
            int nbx = (width + blockWidth - 1) / blockWidth;
            int nby = (height + blockHeight - 1) / blockHeight;
            int blockHeaderSize = ((nbx * nby * 2 + 3) & ~3);
            if (blockHeaderBuf == null || blockHeaderBuf.length < blockHeaderSize) {
                blockHeaderBuf = new byte[blockHeaderSize];
            }
            byte[] blocks = blockHeaderBuf;
            in.readFully(blocks, 0, blockHeaderSize);

            // Process block data
            int block = 0;
            for (int by = 0; by < height; by += blockHeight) {
                int bh2 = min(height - by, blockHeight);
                for (int bx = 0; bx < width; bx += blockWidth) {
                    int bw2 = min(width - bx, blockWidth);
                    int a = blocks[block++];
                    int b = blocks[block++];
                    int dx = a >> 1;
                    int dy = b >> 1;
                    int flag = a & 1;

                    if (flag == 0) {
                        // => copy block from offset dx,dy from previous frame
                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                int rgb;
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    rgb = prevDat[px + py * width];
                                } else {
                                    rgb = 0;
                                }
                                outDat[iout] = rgb;
                                iout++;
                            }

                        }
                    } else {
                        // => XOR block with data read from stream
                        in.readFully(buf, 0, bw2 * bh2 * 4);
                        int iblock = 0;

                        for (int y = 0; y < bh2; y++) {
                            int py = by + y + dy;
                            int iout = bx + (by + y) * width;
                            for (int x = 0; x < bw2; x++) {
                                int px = bx + x + dx;
                                int bgr = ((buf[iblock] & 0xff)) | ((buf[iblock + 1] & 0xff) << 8) | ((buf[iblock + 2] & 0xff) << 16) | ((buf[iblock + 3] & 0xff) << 24);
                                iblock += 4;
                                int rgb = bgr;
                                /*((bgr & (0x3f << 5))<<5)| ((bgr & (0x30 << 5)) >>1)//green
                                |((bgr & (0x1f << 11)) << 8) | ((bgr & (0x1c << 11)) << 3) // red
                                |((bgr & (0x1f << 0)) << 3) | ((bgr & (0x1c << 0)) >>> 2) // blue
                                ; */
                                if (0 <= py && py < height && 0 <= px && px < width) {
                                    rgb ^= prevDat[px + py * width];
                                }
                                outDat[iout] = rgb;
                                iout++;
                            }


                        }
                    }
                }
            }
        }
    }

    public int[] getPalette() {
        if (palette == null) {
            palette = new int[256];
            // initalize palette with grayscale colors
            for (int i = 0; i < palette.length; i++) {
                palette[i] = (i) | (i << 8) | (i << 16);
            }
        }
        return palette;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy