/* * @(#)ANIMKeyFrame.java 1.1 2010-06-27 * * Copyright (c) 1999-2010 Werner Randelshofer, Goldau, Switzerland. * All rights reserved. * * You may not use, copy or modify this file, except in compliance with the * license agreement you entered into with Werner Randelshofer. * For details see accompanying license terms. */ package org.monte.media.anim; import org.monte.media.image.BitmapImage; import org.monte.media.iff.IFFParser; import org.monte.media.ParseException; /** * @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland * @version 2010-06-27 Support for "vertical" compression added. * <br>1.0 1999-10-19 */ public class ANIMKeyFrame extends ANIMFrame { private int compression; protected final static int VDAT_ID = IFFParser.stringToID("VDAT"); public ANIMKeyFrame() { } @Override public void setData(byte[] data) { this.data = data; } /** For possible values see {@link ANIMMovieTrack}. */ public void setCompression(int compression) { this.compression = compression; } @Override public void decode(BitmapImage bitmap, ANIMMovieTrack track) { switch (compression) { case ANIMMovieTrack.CMP_BYTE_RUN_1: unpackByteRun1(data, bitmap.getBitmap()); break; case ANIMMovieTrack.CMP_VERTICAL: unpackVertical(data, bitmap); break; case ANIMMovieTrack.CMP_NONE: default: System.arraycopy(data, 0, bitmap.getBitmap(), 0, data.length); break; } } /** * ByteRun1 run decoder. * <p> * The run encoding scheme by <em>byteRun1</em> is best described by pseudo * code for the decoder <em>Unpacker</em> (called <em>UnPackBits</em> in * the Macintosh toolbox. * <pre> * UnPacker: * LOOP until produced the desired number of bytes * Read the next source byte into n * SELECT n FROM * [0..127] => copy the next n+1 bytes literally * [-1..-127] => replicate the next byte -n+1 times * -128 => no operation * ENDCASE; * ENDLOOP; * </pre> * */ public static int unpackByteRun1(byte[] in, byte[] out) { int iOut = 0; // output array index int iIn = 0; // input array index int n = 0; // The unpack command byte copyByte; try { while (iOut < out.length) { n = in[iIn++]; if (n >= 0) { // [0..127] => copy the next n+1 bytes literally n = n + 1; System.arraycopy(in, iIn, out, iOut, n); iOut += n; iIn += n; } else { if (n != -128) {//[-1..-127] => replicate the next byte -n+1 times copyByte = in[iIn++]; for (; n < 1; n++) { out[iOut++] = copyByte; } } } } } catch (IndexOutOfBoundsException e) { System.out.println("ANIMKeyFrame.unpackByteRun1(): " + e); System.out.println(" Plane-Index: " + iOut + " Plane size:" + out.length); System.out.println(" Buffer-Index: " + iIn + " Buffer size:" + in.length); System.out.println(" Command: " + n); } return iOut; } /** * Vertical run decoder. * <p> * Each plane is stored in a separate VDAT chunk. * <p> * A VDAT chunk consists of an id, a length, and a body. * <pre> * struct { * uint16 id; // The 4 ASCII characters "VDAT" * uint16 length, * byte[length] body * } * </pre> * The body consists of a command list and a data list. * <pre> * struct { * uint16 cnt; // Command count + 2 * uint8[cnt - 2] cmd; // The commands * uint16[] data; // Data words * } * </pre> * Pseudo code for the unpacker: * <pre> * UnPacker: * Read cnt; * LOOP cnt - 2 TIMES * Read the next command byte into cmd * SELECT cmd FROM * 0 => * Read the next data word into n * Copy the next n data words literally * 1 => * Read the next data word into n * Replicate the next data word n times * [2..127] => * Replicate the next data word cmd times * [-1..-128] => * Copy the next -cmd data words literally * ENDCASE; * IF end of data reached THEN EXIT END; * ENDLOOP; * </pre> * */ public void unpackVertical(byte[] in, BitmapImage bm) { byte[] out = bm.getBitmap(); int iIn = 0; // input index int endOfData = 0; int bmhdWidth = bm.getWidth(); int bmhdHeight = bm.getHeight(); int bmhdNbPlanes = bm.getDepth(); byte buf[] = new byte[bmhdWidth * bmhdHeight / 8]; // temporary bitplane buffer int scanlineStride = bm.getScanlineStride(); int columnCount = (bmhdWidth / 8) * bmhdHeight; int columnStride = bmhdHeight * 2; try { for (int p = 0; p < bmhdNbPlanes; p++) { // Each plane is stored in a separate VDAT chunk. // ---------------------------------------------- int iBuf = 0; iIn = endOfData; // read the "VDAT" chunk id and length int id = (in[iIn++] & 0xff) << 24 | (in[iIn++] & 0xff) << 16 | (in[iIn++] & 0xff) << 8 | (in[iIn++] & 0xff); if (id != VDAT_ID) { throw new ParseException("Illegal VDAT chunk ID:" + IFFParser.idToString(id) + " at " + (iIn - 4)); } long length = (in[iIn++] & 0xffL) << 24 | (in[iIn++] & 0xffL) << 16 | (in[iIn++] & 0xffL) << 8 | (in[iIn++] & 0xffL); if (iIn + length > in.length) { throw new ParseException("Illegal VDAT chunk length:" + length + " at " + (iIn - 4)); } endOfData += length + 8; // The body consists of a command list and a data list. // ---------------------------------------------------- // read the command count, compute the offset to the data list int cnt = (in[iIn++] & 0xff) << 8 | (in[iIn++] & 0xff); int iCmd = iIn; iIn = iIn + cnt - 2; try { // Process the commands until all commands read or end of data reached for (int i = cnt - 2; i > 0 && iIn < endOfData; i--) { int cmd = in[iCmd++]; if (cmd == 0) { // 0 => Read the next data word into n // Copy the next n data words literally int n = (in[iIn++] & 0xff) << 8 | (in[iIn++] & 0xff); for (n *= 2; n > 0; n--) { buf[iBuf++] = in[iIn++]; } } else if (cmd == 1) { // 1 => Read the next data word into n // Replicate the next data word n times int n = (in[iIn++] & 0xff) << 8 | (in[iIn++] & 0xff); byte dhigh = in[iIn++]; // high byte byte dlow = in[iIn++]; // low byte for (; n > 0; n--) { buf[iBuf++] = dhigh; buf[iBuf++] = dlow; } } else if (cmd >= 2) { // [2..127] => Replicate the next data word cmd times byte dhigh = in[iIn++]; // high byte byte dlow = in[iIn++]; // low byte for (int n = cmd; n > 0; n--) { buf[iBuf++] = dhigh; buf[iBuf++] = dlow; } } else { // [-1..-128] => Copy the next -cmd data words literally for (int n = cmd * -2; n > 0; n--) { buf[iBuf++] = in[iIn++]; } } } } catch (IndexOutOfBoundsException e) { System.err.println("IndexOutOfBounds in bitplane " + p); e.printStackTrace(); } // Copy buffer into bitmap int bitplaneOffset = bm.getBitplaneStride() * p; for (int xBuf = 0, xOut = 0; xBuf < columnCount; xBuf += columnStride, xOut += 2) { for (int yBuf = 0, yOut = bitplaneOffset; yBuf < columnStride; yBuf += 2, yOut += scanlineStride) { out[xOut + yOut] = buf[xBuf + yBuf]; out[xOut + 1 + yOut] = buf[xBuf + 1 + yBuf]; } } } } catch (ParseException e) { e.printStackTrace(); } catch (IndexOutOfBoundsException e) { e.printStackTrace(); // System.out.println("ILBMDecoder.unpackVertical(): " + e); } } }