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

org.monte.media.anim.ANIMDeltaFrame Maven / Gradle / Ivy

The newest version!
/*
 * @(#)Main.java
 * Copyright © 2023 Werner Randelshofer, Switzerland. MIT License.
 */
package org.monte.media.anim;

import org.monte.media.amigabitmap.AmigaBitmapImage;

/**
 * @author Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
 */
public class ANIMDeltaFrame
        extends ANIMFrame {

    private int leftBound, topBound, rightBound, bottomBound;
    private final static int //
            ENCODING_ILBM = 0,
            ENCODING_XOR_ILBM = 1,
            ENCODING_LONG_DELTA = 2,
            ENCODING_SHORT_DELTA = 3,
            ENCODING_GENERALIZED_SHORTLONG_DELTA = 4,
            ENCODING_BYTE_VERTICAL = 5,
            ENCODING_VERTICAL_7_SHORT = 6,
            ENCODING_VERTICAL_7_LONG = 7,
            ENCODING_VERTICAL_8_SHORT = 8,
            ENCODING_VERTICAL_8_LONG = 9,
            ENCODING_J = 74;
    public final static int //
            OP_Direct = 0,
            OP_XOR = 1,
            OP_LongDelta = 2,
            OP_ShortDelta = 3,
            OP_GeneralDelta = 4,
            OP_ByteVertical = 5,
            OP_StereoDelta = 6,
            OP_Vertical7 = 7,
            OP_Vertical8 = 8,
            OP_J = 74;
    /**
     * Wether we already printed a warning about a broken encoding.
     */
    private boolean isWarningPrinted = false;

    public ANIMDeltaFrame() {
    }

    private int getEncoding() {
        switch (getOperation()) {
            case OP_Direct: // Key Frame (Data stored in ILBM BODY Chunk)
                throw new InternalError("Key Frames not yet supported (Anim Op0)");
            case OP_ByteVertical:
                if (getBits() == BIT_XOR) {
                    // okay
                } else if ((getBits() & BadBitsOP_ByteVertical) != 0) {
                    throw new InternalError("Unknown Bits for Anim Op5 in ANHD; Bits:" + getBits());
                }
                return ENCODING_BYTE_VERTICAL;
            case OP_Vertical7:
                if ((getBits() & BIT_LongData) == 0) {
                    return ENCODING_VERTICAL_7_SHORT;
                } else {
                    return ENCODING_VERTICAL_7_LONG;
                }
            case OP_Vertical8:
                if ((getBits() & BIT_LongData) == 0) {
                    return ENCODING_VERTICAL_8_SHORT;
                } else {
                    return ENCODING_VERTICAL_8_LONG;
                }
            case OP_J:
                return ENCODING_J;
            default:
                throw new InternalError("ANIM Op" + getOperation() + " not supported.");
        }
    }

    @Override
    public void decode(AmigaBitmapImage bitmap, ANIMMovieResources track) {
        switch (getEncoding()) {
            case ENCODING_BYTE_VERTICAL:
                decodeByteVertical(bitmap, track);
                break;
            case ENCODING_VERTICAL_7_SHORT:
                decodeVertical7Short(bitmap, track);
                break;
            case ENCODING_VERTICAL_7_LONG:
                decodeVertical7Long(bitmap, track);
                break;
            case ENCODING_VERTICAL_8_SHORT:
                decodeVertical8Short(bitmap, track);
                break;
            case ENCODING_VERTICAL_8_LONG:
                decodeVertical8Long(bitmap, track);
                break;
            case ENCODING_J:
                decodeJ(bitmap, track);
                break;
            default:
                throw new InternalError("Unsupported encoding." + getEncoding());
        }
    }

    /**
     * 2.2.1 Format for methods 2 & 3.
     * 

* This chunk is a basic data chunk used to hold the delta * compression data. The minimum size of this chunk is 32 bytes * as the first 8 long-words are byte pointers into the chunk for * the data for each of up to 8 bitplanes. The pointer for the * plane data starting immediately following these 8 pointers will * have a value of 32 as the data starts in the 33-rd byte of the * chunk (index value of 32 due to zero-base indexing). *

* The data for a given plane consists of groups of data words. In * Long Delta mode, these groups consist of both short and long * words - short words for offsets and numbers, and long words for * the actual data. In Short Delta mode, the groups are identical * except data words are also shorts so all data is short words. * Each group consists of a starting word which is an offset. If * the offset is positive then it indicates the increment in long * or short words (whichever is appropriate) through the bitplane. * In other words, if you were reconstructing the plane, you would * start a pointer (to shorts or longs depending on the mode) to * point to the first word of the bitplane. Then the offset would * be added to it and the following data word would be placed at * that position. Then the next offset would be added to the * pointer and the following data word would be placed at that * position. And so on... The data terminates with an offset * equal to 0xFFFF. *

* A second interpretation is given if the offset is negative. In * that case, the absolute value is the offset+2. Then the * following short-word indicates the number of data words that * follow. Following that is the indicated number of contiguous * data words (longs or shorts depending on mode) which are to * be placed in contiguous locations of the bitplane. *

* If there are no changed words in a given plane, then the pointer * in the first 32 bytes of the chunk is =0. */ private void decodeMethod2(AmigaBitmapImage bitmap, ANIMMovieResources track) { throw new UnsupportedOperationException(); } /** * 2.2.2 Format for method 4. *

* The DLTA chunk is modified slightly to have 16 long pointers at * the start. The first 8 are as before - pointers to the start of * the data for each of the bitplanes (up to a theoretical max of 8 * planes). The next 8 are pointers to the start of the offset/numbers * data list. If there is only one list of offset/numbers for all * planes, then the pointer to that list is repeated in all positions * so the playback code need not even be aware of it. In fact, one * could get fancy and have some bitplanes share lists while others * have different lists, or no lists (the problems in these schemes * lie in the generation, not in the playback). *

* The best way to show the use of this format is in a sample playback * routine. *

     *           SetDLTAshort(bm,deltaword)
     *           struct BitMap *bm;
     *           WORD *deltaword;
     *           {
     *              int i;
     *              LONG *deltadata;
     *              WORD *ptr,*planeptr;
     *              register int s,size,nw;
     *              register WORD *data,*dest;
     *
     *              deltadata = (LONG *)deltaword;
     *              nw = bm->BytesPerRow >>1;
     *
     *              for (i=0;i<bm->Depth;i++) {
     *                 planeptr = (WORD *)(bm->Planes[i]);
     *                 data = deltaword + deltadata[i];
     *                 ptr  = deltaword + deltadata[i+8];
     *                 while (*ptr != 0xFFFF) {
     *                    dest = planeptr + *ptr++;
     *                    size = *ptr++;
     *                    if (size < 0) {
     *                       for (s=size;s<0;s++) {
     *                          *dest = *data;
     *                          dest += nw;
     *                       }
     *                       data++;
     *                    }
     *                    else {
     *                       for (s=0;s<size;s++) {
     *                          *dest = *data++;
     *                          dest += nw;
     *                       }
     *                    }
     *                 }
     *              }
     *              return(0);
     *           }
     * 
* The above routine is for short word vertical compression with * run length compression. The most efficient way to support * the various options is to replicate this routine and make * alterations for, say, long word or XOR. The variable nw * indicates the number of words to skip to go down the vertical * column. This one routine could easily handle horizontal * compression by simply setting nw=1. For ultimate playback * speed, the core, at least, of this routine should be coded in * assembly language. */ private void decodeMethod4(AmigaBitmapImage bitmap, ANIMMovieResources track) { throw new UnsupportedOperationException(); } /** * 2.2.2 Format for method 5. *

* In this method the same 16 pointers are used as in option 4. * The first 8 are pointers to the data for up to 8 planes. * The second set of 8 are not used but were retained for several * reasons. First to be somewhat compatible with code for option * 4 (although this has not proven to be of any benefit) and * second, to allow extending the format for more bitplanes (code * has been written for up to 12 planes). *

* Compression/decompression is performed on a plane-by-plane basis. * For each plane, compression can be handled by the skip.c code * (provided Public Domain by Jim Kent) and decompression can be * handled by unvscomp.asm (also provided Public Domain by Jim Kent). *

* Compression/decompression is performed on a plane-by-plane basis. * The following description of the method is taken directly from * Jim Kent's code with minor re-wording. Please refer to Jim's * code (skip.c and unvscomp.asm) for more details: *

* Each column of the bitplane is compressed separately. * A 320x200 bitplane would have 40 columns of 200 bytes each. * Each column starts with an op-count followed by a number * of ops. If the op-count is zero, that's ok, it just means * there's no change in this column from the last frame. * The ops are of three classes, and followed by a varying * amount of data depending on which class: * 1. Skip ops - this is a byte with the hi bit clear that * says how many rows to move the "dest" pointer forward, * ie to skip. It is non-zero. * 2. Uniq ops - this is a byte with the hi bit set. The hi * bit is masked down and the remainder is a count of the * number of bytes of data to copy literally. It's of * course followed by the data to copy. * 3. Same ops - this is a 0 byte followed by a count byte, * followed by a byte value to repeat count times. * Do bear in mind that the data is compressed vertically rather * than horizontally, so to get to the next byte in the destination * we add the number of bytes per row instead of one! */ private void decodeByteVertical(AmigaBitmapImage bitmap, ANIMMovieResources track) { int columns = 0; int iOp = 0; byte[] planeBytes = bitmap.getBitmap(); int iPl = 0; int widthInBytes = bitmap.getBitplaneStride(); int interleave = track.getNbPlanes() * widthInBytes; int opCode = 0; int opCount = 0; byte copyByte = 0; leftBound = widthInBytes; rightBound = 0; topBound = track.getHeight(); bottomBound = 0; int height = track.getHeight(); boolean isXOR = getBits() == BIT_XOR; // Repeat for each plane. for (int i = 0, n = track.getNbPlanes(); i < n; ++i) { // iOp is the pointer (index) to the op-codes. iOp = ((data[i * 4] & 0xff) << 24) + ((data[i * 4 + 1] & 0xff) << 16) + ((data[i * 4 + 2] & 0xff) << 8) + (data[i * 4 + 3] & 0xff); try { if (iOp > 0) { // Each column of the plane is coded on its own. for (columns = 0; columns < widthInBytes; ++columns) { // Set iPl to the beginning of the column in the plane. iPl = columns + i * widthInBytes; opCount = data[iOp++] & 0xff; if (opCount > 0) { if (columns < leftBound) { leftBound = columns; } if (columns > rightBound) { rightBound = columns; } opCode = data[iOp]; if (opCode <= 0) { topBound = 0; } else { if (opCode < topBound) { topBound = opCode; } } if (isXOR) { for (; opCount > 0; opCount--) { opCode = data[iOp++]; if (opCode > 0) { // Skip ops iPl += opCode * interleave; } else if (opCode < 0) { // Uniq ops opCode &= 0x7f; while (opCode-- > 0) { planeBytes[iPl] ^= data[iOp++]; iPl += interleave; } } else { // Repeat ops opCode = data[iOp++] & 0xff; if (opCode == 0) { return; } //throw new InterpretException("Error in Delta Chunk: copy bytes with count 0."); copyByte = data[iOp++]; while (opCode-- > 0) { planeBytes[iPl] ^= copyByte; iPl += interleave; } } } } else { for (; opCount > 0; opCount--) { opCode = data[iOp++]; if (opCode > 0) { // Skip ops iPl += opCode * interleave; } else if (opCode < 0) { // Uniq ops opCode &= 0x7f; while (opCode-- > 0) { planeBytes[iPl] = data[iOp++]; iPl += interleave; } } else { // Repeat ops opCode = data[iOp++] & 0xff; if (opCode == 0) { return; } //throw new InterpretException("Error in Delta Chunk: copy bytes with count 0."); copyByte = data[iOp++]; while (opCode-- > 0) { planeBytes[iPl] = copyByte; iPl += interleave; } } } } if (opCode <= 0) { int bottom = (iPl - (columns + i * widthInBytes)) / interleave; if (bottom > bottomBound) { bottomBound = bottom; } } else { if (height - opCode > bottomBound) { bottomBound = height - opCode; } } } } } } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); } } if (leftBound <= rightBound) { leftBound *= 8; rightBound = rightBound * 8 + 8; } } private void decodeVertical8Short(AmigaBitmapImage bitmap, ANIMMovieResources track) { int columns = 0; int iOp = 0; byte[] planeBytes = bitmap.getBitmap(); int iPl = 0; int widthInBytes = bitmap.getBitplaneStride(); int interleave = track.getNbPlanes() * widthInBytes; int opCode = 0; int opCount = 0; byte copyByte1 = 0; byte copyByte2 = 0; leftBound = widthInBytes; rightBound = 0; topBound = track.getHeight() - 1; bottomBound = 0; int height = track.getHeight() - 1; // Repeat for each plane. for (int i = 0; i < track.getNbPlanes(); i++) { // iOp points to the Op-Codes. iOp = ((data[i * 4] & 0xff) << 24) + ((data[i * 4 + 1] & 0xff) << 16) + ((data[i * 4 + 2] & 0xff) << 8) + (data[i * 4 + 3] & 0xff); if (iOp > 0) { // Each column has its own Op-codes. for (columns = 0; columns < widthInBytes; columns += 2) { // iPl points to the column in the bitmap. iPl = columns + i * widthInBytes; opCount = ((data[iOp++] & 0xff) << 8) | (data[iOp++] & 0xff); if (opCount > 0) { if (columns < leftBound) { leftBound = columns; } if (columns > rightBound) { rightBound = columns; } opCode = (data[iOp] << 8) | (data[iOp + 1] & 0xff); if (opCode <= 0) { topBound = 0; } else { if (opCode < topBound) { topBound = opCode; } } for (; opCount > 0; opCount--) { opCode = (data[iOp++] << 8) | (data[iOp++] & 0xff); if (opCode > 0) { // Skip ops iPl += opCode * interleave; } else if (opCode < 0) { // Uniq ops opCode &= 0x7fff; while (opCode-- > 0) { planeBytes[iPl] = data[iOp++]; planeBytes[iPl + 1] = data[iOp++]; iPl += interleave; } } else { // Repeat ops opCode = ((data[iOp++] << 8) | (data[iOp++] & 0xff)) & 0xffff; if (opCode == 0) { return; } //throw new InterpretException("Error in Delta Chunk: copy bytes with count 0."); copyByte1 = data[iOp++]; copyByte2 = data[iOp++]; while (opCode-- > 0) { planeBytes[iPl] = copyByte1; planeBytes[iPl + 1] = copyByte2; iPl += interleave; } } } if (opCode <= 0) { int bottom = (iPl - (columns + i * widthInBytes)) / interleave; if (bottom > bottomBound) { bottomBound = bottom; } } else { if (height - opCode > bottomBound) { bottomBound = height - opCode; } } } } } } if (leftBound <= rightBound) { leftBound *= 8; rightBound = rightBound * 8 + 16; } } private void decodeVertical8Long(AmigaBitmapImage bitmap, ANIMMovieResources track) { int columns = 0; int iOp = 0; byte[] planeBytes = bitmap.getBitmap(); int iPl = 0; int widthInBytes = bitmap.getBitplaneStride(); int interleave = track.getNbPlanes() * widthInBytes; int opCode = 0; int opCount = 0; byte copyByte1 = 0; byte copyByte2 = 0; byte copyByte3 = 0; byte copyByte4 = 0; leftBound = widthInBytes; rightBound = 0; topBound = track.getHeight() - 1; bottomBound = 0; int height = track.getHeight() - 1; // Repeat for each plane for (int i = 0; i < track.getNbPlanes(); i++) { // iOp points to the op-codes. iOp = ((data[i * 4] & 0xff) << 24) + ((data[i * 4 + 1] & 0xff) << 16) + ((data[i * 4 + 2] & 0xff) << 8) + (data[i * 4 + 3] & 0xff); if (iOp > 0) { // Decode each column of the plane separately. for (columns = 0; columns < widthInBytes; columns += 4) { // iPl points to the column in the bitmap. iPl = columns + i * widthInBytes; opCount = ((data[iOp++] & 0xff) << 24) + ((data[iOp++] & 0xff) << 16) + ((data[iOp++] & 0xff) << 8) + (data[iOp++] & 0xff); if (opCount > 0) { if (columns < leftBound) { leftBound = columns; } if (columns > rightBound) { rightBound = columns; } opCode = ((data[iOp] & 0xff) << 24) + ((data[iOp + 1] & 0xff) << 16) + ((data[iOp + 2] & 0xff) << 8) + (data[iOp + 3] & 0xff); if (opCode <= 0) { topBound = 0; } else { if (opCode < topBound) { topBound = opCode; } } for (; opCount > 0; opCount--) { opCode = ((data[iOp++] & 0xff) << 24) + ((data[iOp++] & 0xff) << 16) + ((data[iOp++] & 0xff) << 8) + (data[iOp++] & 0xff); if (opCode > 0) { // Skip ops iPl += opCode * interleave; } else if (opCode < 0) { // Uniq ops opCode &= 0x7fffffff; while (opCode-- > 0) { planeBytes[iPl] = data[iOp++]; planeBytes[iPl + 1] = data[iOp++]; planeBytes[iPl + 2] = data[iOp++]; planeBytes[iPl + 3] = data[iOp++]; iPl += interleave; } } else { // Repeat ops opCode = ((data[iOp++] & 0xff) << 24) + ((data[iOp++] & 0xff) << 16) + ((data[iOp++] & 0xff) << 8) + (data[iOp++] & 0xff); if (opCode == 0) { return; } //throw new InterpretException("Error in Delta Chunk: copy bytes with count 0."); copyByte1 = data[iOp++]; copyByte2 = data[iOp++]; copyByte3 = data[iOp++]; copyByte4 = data[iOp++]; while (opCode-- > 0) { planeBytes[iPl] = copyByte1; planeBytes[iPl + 1] = copyByte2; planeBytes[iPl + 2] = copyByte3; planeBytes[iPl + 3] = copyByte4; iPl += interleave; } } } if (opCode <= 0) { int bottom = (iPl - (columns + i * widthInBytes)) / interleave; if (bottom > bottomBound) { bottomBound = bottom; } } else { if (height - opCode > bottomBound) { bottomBound = height - opCode; } } } } } } if (leftBound <= rightBound) { leftBound *= 8; rightBound = rightBound * 8 + 32; } } private void decodeVertical7Short(AmigaBitmapImage bitmap, ANIMMovieResources track) { int columns = 0; int iOp = 0; int iData = 0; byte[] planeBytes = bitmap.getBitmap(); int iPl = 0; int widthInBytes = bitmap.getBitplaneStride(); int interleave = bitmap.getScanlineStride(); int opCode = 0; int opCount = 0; byte copyByte1 = 0; byte copyByte2 = 0; leftBound = widthInBytes; rightBound = 0; topBound = track.getHeight() - 1; bottomBound = 0; int height = track.getHeight() - 1; for (int i = 0; i < track.getNbPlanes(); i++) { iOp = ((data[i * 4] & 0xff) << 24) + ((data[i * 4 + 1] & 0xff) << 16) + ((data[i * 4 + 2] & 0xff) << 8) + (data[i * 4 + 3] & 0xff); iData = ((data[i * 4 + 32] & 0xff) << 24) + ((data[i * 4 + 33] & 0xff) << 16) + ((data[i * 4 + 34] & 0xff) << 8) + (data[i * 4 + 35] & 0xff); if (iOp > 0) { for (columns = 0; columns < widthInBytes; columns += 2) { iPl = columns + i * widthInBytes; opCount = data[iOp++] & 0xff; if (opCount > 0) { if (columns < leftBound) { leftBound = columns; } if (columns > rightBound) { rightBound = columns; } opCode = data[iOp]; if (opCode <= 0) { topBound = 0; } else { if (opCode < topBound) { topBound = opCode; } } for (; opCount > 0; opCount--) { opCode = data[iOp++]; if (opCode > 0) { // Skip ops iPl += opCode * interleave; } else if (opCode < 0) { // Uniq ops opCode &= 0x7f; while (opCode-- > 0) { planeBytes[iPl] = data[iData++]; planeBytes[iPl + 1] = data[iData++]; iPl += interleave; } } else { // Repeat ops opCode = data[iOp++] & 0xff; if (opCode == 0) { return; } //throw new InterpretException("Error in Delta Chunk: copy bytes with count 0."); copyByte1 = data[iData++]; copyByte2 = data[iData++]; while (opCode-- > 0) { planeBytes[iPl] = copyByte1; planeBytes[iPl + 1] = copyByte2; iPl += interleave; } } } if (opCode <= 0) { int bottom = (iPl - (columns + i * widthInBytes)) / interleave; if (bottom > bottomBound) { bottomBound = bottom; } } else { if (height - opCode > bottomBound) { bottomBound = height - opCode; } } } } } } if (leftBound <= rightBound) { leftBound *= 8; rightBound = rightBound * 8 + 32; } } private void decodeVertical7Long(AmigaBitmapImage bitmap, ANIMMovieResources track) { int columns = 0; int iOp = 0; int iData = 0; byte[] planeBytes = bitmap.getBitmap(); int iPl = 0; int widthInBytes = bitmap.getBitplaneStride(); int interleave = track.getNbPlanes() * widthInBytes; int opCode = 0; int opCount = 0; byte copyByte1 = 0; byte copyByte2 = 0; byte copyByte3 = 0; byte copyByte4 = 0; leftBound = widthInBytes; rightBound = 0; topBound = track.getHeight() - 1; bottomBound = 0; int height = track.getHeight() - 1; for (int i = 0; i < track.getNbPlanes(); i++) { iOp = ((data[i * 4] & 0xff) << 24) + ((data[i * 4 + 1] & 0xff) << 16) + ((data[i * 4 + 2] & 0xff) << 8) + (data[i * 4 + 3] & 0xff); iData = ((data[i * 4 + 32] & 0xff) << 24) + ((data[i * 4 + 33] & 0xff) << 16) + ((data[i * 4 + 34] & 0xff) << 8) + (data[i * 4 + 35] & 0xff); if (iOp > 0) { for (columns = 0; columns < widthInBytes; columns += 4) { try { iPl = columns + i * widthInBytes; opCount = data[iOp++] & 0xff; if (opCount > 0) { if (columns < leftBound) { leftBound = columns; } if (columns > rightBound) { rightBound = columns; } opCode = data[iOp]; if (opCode <= 0) { topBound = 0; } else { if (opCode < topBound) { topBound = opCode; } } for (; opCount > 0; opCount--) { opCode = data[iOp++]; if (opCode > 0) { // Skip ops iPl += opCode * interleave; } else if (opCode < 0) { // Uniq ops opCode &= 0x7f; while (opCode-- > 0) { planeBytes[iPl] = data[iData++]; planeBytes[iPl + 1] = data[iData++]; planeBytes[iPl + 2] = data[iData++]; planeBytes[iPl + 3] = data[iData++]; iPl += interleave; } } else { // Repeat ops opCode = data[iOp++] & 0xff; if (opCode == 0) { return; } //throw new InterpretException("Error in Delta Chunk: copy bytes with count 0."); copyByte1 = data[iData++]; copyByte2 = data[iData++]; copyByte3 = data[iData++]; copyByte4 = data[iData++]; while (opCode-- > 0) { planeBytes[iPl] = copyByte1; planeBytes[iPl + 1] = copyByte2; planeBytes[iPl + 2] = copyByte3; planeBytes[iPl + 3] = copyByte4; iPl += interleave; } } } if (opCode <= 0) { int bottom = (iPl - (columns + i * widthInBytes)) / interleave; if (bottom > bottomBound) { bottomBound = bottom; } } else { if (height - opCode > bottomBound) { bottomBound = height - opCode; } } } } catch (IndexOutOfBoundsException e) { // Some delta frames write over the bounds // of the bitmap. if (!isWarningPrinted) { e.printStackTrace(); isWarningPrinted = true; } } } } } if (leftBound <= rightBound) { leftBound *= 8; rightBound = rightBound * 8 + 64; } } /** * Decodes DLTA's in Eric Graham's Compresson mode "J". *

* The following documentation has been taken from Steven Den Beste's docu * for his "unmovie" program. *

* A DLTA appears to have three kinds of items in it, with each type being * indicated by the value of its first byte: *

*

* Type 0: indicates the end of the DLTA. Layout: word: 0 *

* Type 1: indicates a "wall": This is a section of the image which has full * Z-height, is 1 byte wide in X, and has a variable Y size. Layout: word: 1 * word: 0=unidirectional (store value), 1=bidirectional (XOR value) word: * Y-size (number of pixels in Y direction) word: number of blocks to * follow: per block: word: offset in each bitplane (note: NOT in the total * image!) 1-6 bytes: full Z height for first Y 1-6 bytes: full Z height for * second Y etc., extending DOWN. *

* Type 2: indicates a "pile": This is a section of the image which has full * Z-height, and has both variable Y size and X size. Layout: word: 2 word: * 0=unidirectional, 1=bidirectional word: Y size word: X size word: number * of blocks to follow: per block: word: offset in each bitplane (NOT in the * total image) successive bytes: a traversed 3D rectangle, with X varying * within Y within Z. (X moves right, Z moves up, Y moves down) *

* The movie is double-buffered, but you don't have to know about that part. * (Anyway, it is described in the original documentation for "pilbm" if * you're curious. */ private void decodeJ(AmigaBitmapImage bitmap, ANIMMovieResources track) { int nbPlanes = track.getNbPlanes(); int widthInBytes = bitmap.getBitplaneStride(); // Mark all pixels of the delta frame as being changed // XXX - Determine minimal bounds /* rightBound = track.getWidth(); leftBound = 0; bottomBound = track.getHeight(); topBound = 0;*/ leftBound = track.getWidth() - 1; rightBound = 0; topBound = track.getHeight() - 1; bottomBound = 0; // Current reading position; int pos = 0; // Output data goes here: byte[] planeBytes = bitmap.getBitmap(); // Change type, 16 bit short: 0=End of Delta, 1=Wall, 2=Pile. int changeType; try { decodingLoop: while (pos < data.length) { changeType = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); switch (changeType) { case 0: /* End of DELTA */ break decodingLoop; case 1: { /* Wall */ // Read wall header // struct { // short uni_flag; // short y_size; // short num_blocks; } wall; int uniFlag = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); int ySize = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); int numBlocks = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); // Decode wall data for (int b = 0; b < numBlocks; b++) { int offset = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); leftBound = Math.min(leftBound, (offset % widthInBytes) * 8); rightBound = Math.max(rightBound, (offset % widthInBytes) * 8 + 8); topBound = Math.min(topBound, (offset / widthInBytes)); bottomBound = Math.max(bottomBound, (offset / widthInBytes) + ySize); int realOffset = (offset / widthInBytes) * nbPlanes; realOffset *= widthInBytes; realOffset += offset % widthInBytes; if (uniFlag == 1) { for (int z = 0; z < nbPlanes; z++) { for (int y = 0; y < ySize; y++) { int dest = z * widthInBytes * ySize + y * widthInBytes + realOffset; planeBytes[dest] ^= data[pos++]; } } } else { for (int z = 0; z < nbPlanes; z++) { for (int y = 0; y < ySize; y++) { int dest = z * widthInBytes * ySize + y * widthInBytes + realOffset; planeBytes[dest] = data[pos++]; } } } // If we've stopped on an odd boundary, read and throw away // another byte. if (pos % 2 == 1) { pos++; } } break; } case 2: { /* Pile */ // Read Pile header // struct { // short uni_flag; // short y_size; // short x_size; // short num_blocks; } pile; int uniFlag = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); int ySize = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); int xSize = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); int numBlocks = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); // Decode Pile data for (int b = 0; b < numBlocks; b++) { int offset = ((data[pos++] & 0xff) << 8) | ((data[pos++]) & 0xff); leftBound = Math.min(leftBound, (offset % widthInBytes) * 8); rightBound = Math.max(rightBound, (offset % widthInBytes + xSize) * 8 + 8); topBound = Math.min(topBound, (offset / widthInBytes)); bottomBound = Math.max(bottomBound, (offset / widthInBytes) + ySize); int realOffset = (offset / widthInBytes) * nbPlanes; realOffset *= widthInBytes; realOffset += offset % widthInBytes; if (uniFlag == 1) { for (int z = 0; z < nbPlanes; z++) { for (int y = 0; y < ySize; y++) { for (int x = 0; x < xSize; x++) { int dest = z * widthInBytes * ySize + y * widthInBytes + realOffset + x; planeBytes[dest] ^= data[pos++]; } } } } else { for (int z = 0; z < nbPlanes; z++) { for (int y = 0; y < ySize; y++) { for (int x = 0; x < xSize; x++) { int dest = z * widthInBytes * ySize + y * widthInBytes + realOffset + x; planeBytes[dest] = data[pos++]; } } } } // If we've stopped on an odd boundary, read and throw away // another byte. if (pos % 2 == 1) { pos++; } } break; } default: System.out.println("Unsupported changeType in 'J' delta frame:" + changeType); break decodingLoop; //throw new InternalError("Unsupported changeType in 'J' delta frame:"+changeType); } } } catch (IndexOutOfBoundsException e) { // Some delta frames write over the bounds // of the bitmap. if (!isWarningPrinted) { e.printStackTrace(); isWarningPrinted = true; } } } @Override public int getTopBound(ANIMMovieResources track) { return topBound; } @Override public int getBottomBound(ANIMMovieResources track) { return bottomBound; } @Override public int getLeftBound(ANIMMovieResources track) { return leftBound; } @Override public int getRightBound(ANIMMovieResources track) { return rightBound; } /** * Returns true if the frame can be decoded over both the previous frame or * the subsequent frame. Bidirectional frames can be used efficiently for * forward and backward playing a movie. *

* All key frames are bidirectional. Delta frames which use an XOR OP-mode * are bidirectional as well. */ @Override public boolean isBidirectional() { switch (getOperation()) { case OP_Direct: // Key Frame (Data stored in ILBM BODY Chunk) return true; case OP_ByteVertical: if (getBits() == BIT_XOR) { return true; } break; case OP_J: // All J-encoded frames seem to be bidirectional return true; default: break; } return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy