Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * @(#)ANIMDecoder.java  2.3  2011-07-21
    *
    * Copyright (c) 1999-2011 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 java.io.*;
  import java.util.*;
  import java.awt.image.*;
  import java.net.URL;
Decodes IFF files and adds the data to an ANIMMovieTrack.

Author(s):
Werner Randelshofer, Hausmatt 10, CH-6405 Goldau, Switzerland
Version:
2.3 2011-07-21 Treats CMAP specially if OCS chip set is detected.
2.1 2010-04-11 Adds support for CCRT color cycling.
2.1 2010-01-22 Adds support for CRNG color cycling.
2.0 2009-12-25 Treat an ILBM file as an animation with a single frame.
1.3 2009-12-24 Added support for CRNG color cycling.
1.2.1 2006-09-30 Decode CMAP even if it is too big or too small for the number of bitplanes used of the animation.
1.2 2003-04-21 Decode ANFI revised.
1.0 2003-04-03 Support for ANIM+SLA (Animations with Statically Loaded Audio) files added.
1.0 1999-10-19
  
  public class ANIMDecoder
          implements IFFVisitor {
  
      private final static int ILBM_ID = IFFParser.stringToID("ILBM");
      private final static int BMHD_ID = IFFParser.stringToID("BMHD");
      private final static int CMAP_ID = IFFParser.stringToID("CMAP");
      private final static int CAMG_ID = IFFParser.stringToID("CAMG");
      private final static int CCRT_ID = IFFParser.stringToID("CCRT");
      private final static int CRNG_ID = IFFParser.stringToID("CRNG");
      private final static int DRNG_ID = IFFParser.stringToID("DRNG");
      private final static int BODY_ID = IFFParser.stringToID("BODY");
      private final static int ANHD_ID = IFFParser.stringToID("ANHD");
      private final static int DLTA_ID = IFFParser.stringToID("DLTA");
      private final static int ANIM_ID = IFFParser.stringToID("ANIM");
      private final static int COPYRIGHT_ID = IFFParser.stringToID("(c) ");
      private final static int AUTH_ID = IFFParser.stringToID("AUTH");
      private final static int ANNO_ID = IFFParser.stringToID("ANNO");
      private final static int ANFI_ID = IFFParser.stringToID("ANFI");
      private final static int SCTL_ID = IFFParser.stringToID("SCTL");
    
CAMG monitor ID mask.
  
      public final static int MONITOR_ID_MASK = 0xffff1000;
    
Default ID chooses a system dependent screen mode. We always fall back to NTSC OCS with 60fps. The default monitor ID triggers OCS mode! OCS stands for "Original Chip Set". The OCS chip set only had 4 bits per color register. All later chip sets hat 8 bits per color register.
  
      public final static int DEFAULT_MONITOR_ID = 0x00000000;
    
NTSC, 60fps, 44:52.
  
      public final static int NTSC_MONITOR_ID = 0x00011000;
    
PAL, 50fps, 44:44.
  
      public final static int PAL_MONITOR_ID = 0x00021000;
    
MULTISCAN (VGA), 58fps, 44:44.
  
      public final static int MULTISCAN_MONITOR_ID = 0x00031000;
    
A2024, 60fps (I don't know the real value).
  
      public final static int A2024_MONITOR_ID = 0x00041000;
    
PROTO, 60fps (I don't know the real value).
  
      public final static int PROTO_MONITOR_ID = 0x00051000;
    
EURO72, 69fps, 44:44.
  
      public final static int EURO72_MONITOR_ID = 0x00061000;
    
EURO36, 73fps, 44:44.
  
      public final static int EURO36_MONITOR_ID = 0x00071000;
    
SUPER72, 71fps, 34:40.
  
      public final static int SUPER72_MONITOR_ID = 0x00081000;
    
DBLNTSC, 58fps, 44:52.
  
      public final static int DBLNTSC_MONITOR_ID = 0x00091000;
    
DBLPAL, 48fps, 44:44.
  
      public final static int DBLPAL_MONITOR_ID = 0x000a1000;
      protected final static int MODE_MASK = 0x00000880;
      protected final static int HAM_MODE = 0x00000800;
      protected final static int EHB_MODE = 0x00000080;
    
Instance variables
  
      private InputStream inputStream_;
      private URL location_;
     private Vector sources_;
    
Properties.
 
     private Hashtable properties_;
    
CMAP data.
 
     private ColorModel cmapColorModel;
    
MovieTrack
 
     private ANIMMovieTrack track;
    
Number of ANIM Chunks found.
 
     private int animCount;
    
Index of ANIM Chunk to load.
 
     private int index;
    
8SVX Decoder
 
     private EightSVXDecoder eightSVXDecoder;
    
Data of previously decoded CMAP.
 
     private byte[] previousCMAPdata_;
    
Count the number of color maps encountered.
 
    
Flag if within ANIM form.
 
     private boolean isInANIM;
    
Flag if within ILBM form.
 
     private boolean isInILBM;
    
The camg.
 
     private int camg = ;
 
     /* Constructors */
     public ANIMDecoder(InputStream inputStream) {
          = inputStream;
     }
 
     public ANIMDecoder(URL location) {
          = location;
     }

    
Decodes the stream and produces animation frames into the specified movie track. Reads the n-th ANIM chunk out of the IFF-file.

Parameters:
track The decoded data is stored in this track.
n The index of the ANIM FORM to be read out of the IFF-File
loadAudio If this is set to false, audio data will be skipped.
 
     public void produce(ANIMMovieTrack trackint nboolean loadAudio)
             throws IOException {
         InputStream in = null;
         this. = track;
          = n;
          = 0;
         if ( != null) {
             in = ;
         } else {
             in = .openStream();
         }
         try {
 
 
             IFFParser iff = new IFFParser();
             registerChunks(iffloadAudio);
             if (loadAudio) {
                  = new EightSVXDecoder() {
 
                     @Override
                     public void addAudioClip(AudioClip clip) {
                         super.addAudioClip(clip);
                         ANIMDecoder.this..addAudioClip(clip);
                     }
                 };
                 .registerChunks(iff);
             }
             iff.parse(inthis);
         } catch (ParseException e) { //System.out.println(e1);
             throw new IOException(e.getMessage());
         } catch (AbortException e) { //System.out.println(e);
             throw new IOException(e.getMessage());
         } finally {
             in.close();
         }
     }
 
     public void registerChunks(IFFParser iffboolean loadAudio) {
         iff.declarePropertyChunk();
         iff.declarePropertyChunk();
         iff.declarePropertyChunk();
         iff.declarePropertyChunk();
         iff.declareCollectionChunk();
         iff.declareCollectionChunk();
         iff.declareCollectionChunk();
         if (loadAudio) {
             iff.declarePropertyChunk();
             iff.declareCollectionChunk();
         }
         iff.declareGroupChunk(.);
         iff.declareGroupChunk(.);
         iff.declareDataChunk();
         iff.declareDataChunk();
         iff.declareCollectionChunk();
         iff.declareCollectionChunk();
     }
 
     public void enterGroup(IFFChunk chunk) {
         // Process chunks only when within ANIM Form or within ILBM Form.
         if (chunk.getType() == ) {
             if (++ == ) {
                  = true;
             }
         } else if (chunk.getType() == ) {
              = true;
         }
 
         // Decode 8SVX Sound data
         if ( &&  != null) {
             .enterGroup(chunk);
         }
     }
 
     public void leaveGroup(IFFChunk chunk) {
         // Decode 8SVX Sound data
         if ( &&  != null) {
             .leaveGroup(chunk);
         }
 
         // Process chunks only when within ANIM Form or within ILBM Form
         if (chunk.getType() == ) {
              = false;
         }
         if (chunk.getType() == ) {
              = false;
         }
     }
 
     public void visitChunk(IFFChunk groupIFFChunk chunk)
             throws ParseExceptionAbortException {
         if (Thread.currentThread().isInterrupted()) {
             throw new AbortException();
         }
         if () {
             // Decode 8SVX data
             if ( != null) {
                 .visitChunk(groupchunk);
             }
 
             // Decode ANIM data
             if (group.getType() == ) {
                 // Init track if not initialized.
                 if (.getWidth() == 0) {
                     decodeBMHD(group.getPropertyChunk(), );
                     decodeCAMG(group.getPropertyChunk(), );
                     decodeColorCycling(//
                             group.getCollectionChunks(),
                             group.getCollectionChunks(),//
                             group.getCollectionChunks(),//
                             );
                     decodeAUTH(group.getCollectionChunks(), );
                     decodeANNO(group.getCollectionChunks(), );
                     decodeCOPYRIGHT(group.getCollectionChunks(), );
                 }
                 boolean is4BitsPerChannel = ( & ) == ;
                 ColorModel cm = decodeCMAP(group.getPropertyChunk(), is4BitsPerChannel);
                 if (cm != null) {
                      = cm;
                 }
 
                 if (chunk.getID() == ) {
                     decodeBODY(groupchunk);
                 } else if (chunk.getID() == ) {
                     decodeDLTA(groupchunk);
                 }
             }
         } else if () {
             // Decode an ILBM image, which is outside of an ANIM Form as
             // a movie with a single video frame
 
             // Init track if not initialized.
             if (.getWidth() == 0) {
                 decodeBMHD(group.getPropertyChunk(), );
                 decodeCAMG(group.getPropertyChunk(), );
                 decodeColorCycling(//
                         group.getCollectionChunks(),//
                         group.getCollectionChunks(),//
                         group.getCollectionChunks(),//
                         );
                 decodeAUTH(group.getCollectionChunks(), );
                 decodeANNO(group.getCollectionChunks(), );
                 decodeCOPYRIGHT(group.getCollectionChunks(), );
             }
             .setPlayWrapupFrames(true);
             boolean is4BitsPerChannel = ( & ) == ;
             ColorModel cm = decodeCMAP(group.getPropertyChunk(), is4BitsPerChannel);
             if (cm != null) {
                  = cm;
             }
 
             if (chunk.getID() == ) {
                 decodeBODY(groupchunk);
             }
         }
     }

    
Decodes the bitmap header (ILBM BMHD).
 typedef UBYTE Masking; // Choice of masking technique

 #define mskNone                 0
 #define mskHasMask              1
 #define mskHasTransparentColor  2
 #define mskLasso                3

 typedef UBYTE Compression; // Choice of compression algorithm
 // applied to the rows of all source and mask planes.
 // "cmpByteRun1" is the byte run encoding. Do not compress
 // accross rows!
 #define cmpNone      0
 #define cmpByteRun1  1

 typedef struct {
 UWORD       w, h; // raster width & height in pixels
 WORD        x, y; // pixel position for this image
 UBYTE       nbPlanes; // # source bitplanes
 Masking     masking;
 Compression compression;
 UBYTE       pad1;     // unused; ignore on read, write as 0
 UWORD       transparentColor; // transparent "color number" (sort of)
 UBYTE       xAspect, yAspect; // pixel aspect, a ratio width : height
 WORD        pageWidth, pageHeight; // source "page" size in pixels
 } BitmapHeader;
 
 
     private void decodeBMHD(IFFChunk chunkANIMMovieTrack track)
             throws ParseException {
         try {
             MC68000InputStream in = new MC68000InputStream(new ByteArrayInputStream(chunk.getData()));
             track.setWidth(in.readUWORD());
             track.setHeight(in.readUWORD());
             track.setXPosition(in.readWORD());
             track.setYPosition(in.readWORD());
             track.setNbPlanes(in.readUBYTE());
             track.setMasking(in.readUBYTE());
             track.setCompression(in.readUBYTE());
             in.skip(1);
             track.setTransparentColor(in.readUWORD());
             track.setXAspect(in.readUBYTE());
             track.setYAspect(in.readUBYTE());
             track.setPageWidth(in.readWORD());
             track.setPageHeight(in.readWORD());
             in.close();
         } catch (IOException e) {
             throw new ParseException(e.toString());
         }
     }

    
Decodes the CAMG Chunk. The required information from the BMHD chunk must be provided by the ANIMMovieTrack.
 
     private void decodeCAMG(IFFChunk chunkANIMMovieTrack track)
             throws ParseException {
 
 
         if (chunk != null) {
             try {
                 MC68000InputStream in = new MC68000InputStream(new ByteArrayInputStream(chunk.getData()));
 
                  = in.readLONG();
 
                 in.close();
             } catch (IOException e) {
                 throw new ParseException(e.toString());
             }
         }
 
         // Extract color mode bits
         switch ( & ( | )) {
             case :
                 track.setScreenMode(.);
                 break;
             case :
                 if (track.getNbPlanes() == 6) {
                     track.setScreenMode(.);
                 } else if (track.getNbPlanes() == 8) {
                     track.setScreenMode(.);
                 } else {
                     throw new ParseException("unsupported Ham Mode with " + track.getNbPlanes() + " bitplanes");
 
                 }
                 break;
             default:
                 if (track.getNbPlanes() <= 8) {
                     track.setScreenMode(.);
                 } else {
                     track.setScreenMode(.);
                 }
         }
 
         // Extract monitor id bits
         int camgJiffies;
         switch ( & ) {
             case :
                 camgJiffies = 60;
                 break;
             case :
                 camgJiffies = 60;
                 break;
             case :
                 camgJiffies = 50;
                 break;
             case :
                 camgJiffies = 58;
                 break;
             case :
                 camgJiffies = 60;
                 break// I don't know the real value
             case :
                 camgJiffies = 60;
                 break// I don't know the real value
             case :
                 camgJiffies = 69;
                 break;
             case :
                 camgJiffies = 73;
                 break;
             case :
                 camgJiffies = 58;
                 break;
             case :
                 camgJiffies = 48;
                 break;
             case :
                 camgJiffies = 71;
                 break;
             default:
                 camgJiffies = 60;
                 break;
         }
         track.setJiffies(camgJiffies);
     }

    
Decodes the color map (ILBM CMAP). The required information from the BMHD chunk and the CAMG chunk must be provided by the ANIMMovieTrack.
 typedef struct {
 UBYTE red, green, blue; // color intesnities 0..255
 } ColorRegister;          // size = 3 bytes

 typedef ColorRegister ColorMap[n]; // size = 3n bytes
 
 
     private ColorModel decodeCMAP(IFFChunk chunkANIMMovieTrack trackboolean is4BitsPerChannel)
             throws ParseException {
         byte[] red;
         byte[] green;
         byte[] blue;
         int size = 0;
         int colorsToRead = 0;
 
         if (chunk == null) {
             return null;
         }
 
         byte[] cmapData = chunk.getData();
         if ( != null && Arrays.equals(cmapData)) {
             return null;
         } else {
              = cmapData;
         }
 
         switch (track.getScreenMode()) {
             case .:
                 size = 64;
                 colorsToRead = Math.min(32, (intchunk.getSize() / 3);
                 break;
             case .:
             case .:
                 size = 1 << (track.getNbPlanes() - 2);
                 colorsToRead = Math.min(size, (intchunk.getSize() / 3);
                 break;
             case .:
                 size = 1 << (track.getNbPlanes());
                 colorsToRead = Math.min(size, (intchunk.getSize() / 3);
                 break;
             case .:
                 return new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
         }
 
         red = new byte[size];
         green = new byte[size];
         blue = new byte[size];
 
         byte[] data = chunk.getData();
         int j = 0;
 
         if (is4BitsPerChannel) {
             for (int i = 0; i < colorsToReadi++) {
                 red[i] = (byte) (data[j] & 0xf0 | ((data[j] & 0xf0) >>> 4));
                 green[i] = (byte) (data[j + 1] & 0xf0 | ((data[j + 1] & 0xf0) >>> 4));
                 blue[i] = (byte) (data[j + 2] & 0xf0 | ((data[j + 2] & 0xf0) >>> 4));
                 j += 3;
             }
         } else {
 
             for (int i = 0; i < colorsToReadi++) {
                 red[i] = data[j++];
                 green[i] = data[j++];
                 blue[i] = data[j++];
             }
         }
 
         switch (track.getScreenMode()) {
 
             case .:
                 j = 32;
                 for (int i = 0; i < 32; i++, j++) {
                     red[j] = (byte) ((red[i] & 255) / 2);
                     green[j] = (byte) ((green[i] & 255) / 2);
                     blue[j] = (byte) ((blue[i] & 255) / 2);
                 }
                 // Should return the effective number of planes, but
                 // runs on more Java VM's when allways returning 8.
                 return new IndexColorModel(8, 64, redgreenblue, -1);
             //return new IndexColorModel(track.getNbPlanes(),64,red,green,blue,-1);
 
             case .:
                 return new HAMColorModel(., 16, redgreenbluefalse);
 
             case .:
                 return new HAMColorModel(., 64, redgreenbluefalse);
 
             case .:
                 // Should return the effective number of planes, but
                 // runs on more Java VM's when allways returning 8.
                 //return new IndexColorModel(8,(int)chunk.getSize() / 3,red,green,blue,-1);
                 return new IndexColorModel(8, Math.min(red.length, (intchunk.getSize() / 3), redgreenblue, -1);
             //return new IndexColorModel(track.getNbPlanes(),(int)chunk.getSize() / 3,red,green,blue);
 
             default: {
                 throw new ParseException("ScreenMode not supported:" + track.getScreenMode());
             }
         }
     }

    
Decodes the color cycling range and timing chunk (ILBM CCRT).
 enum {
     dontCycle = 0, forward = 1, backwards = -1
 } ccrtDirection;
 typedef struct {
   WORD enum ccrtDirection direction;  // 0=don't cycle, 1=forward, -1=backwards
   UBYTE start;      // range lower
   UBYTE end;        // range upper
   ULONG  seconds;    // seconds between cycling
   ULONG  microseconds; // msecs between cycling
   WORD  pad;        // future exp - store 0 here
 } ilbmColorCyclingRangeAndTimingChunk;
 
 
     protected void decodeCCRT(IFFChunk chunkANIMMovieTrack track)
             throws ParseException {
         ColorCycle cc;
         try {
             MC68000InputStream in = new MC68000InputStream(new ByteArrayInputStream(chunk.getData()));
 
             int direction = in.readWORD();
             int start = in.readUBYTE();
             int end = in.readUBYTE();
             long seconds = in.readULONG();
             long microseconds = in.readULONG();
             int pad = in.readWORD();
             cc = new CRNGColorCycle(1000000 / (int) (seconds * 1000 + microseconds / 1000), 1000, startend,//
                     direction == 1 || direction == -1, //
                     direction == 1, track.getScreenMode() == .);
 
             in.close();
         } catch (IOException e) {
             throw new ParseException(e.toString());
         }
         if (cc.isActive()) {
             track.addColorCycle(cc);
         }
     }

    
Decodes the color range cycling (ILBM CRNG).
 #define RNG_NORATE  36   // Dpaint uses this rate to mean non-active
  set {
  active = 1, reverse = 2
  } crngActive;

  // A CRange is store in a CRNG chunk.
  typedef struct {
  WORD  pad1;              // reserved for future use; store 0 here *
  WORD  rate;              // 60/sec=16384, 30/sec=8192, 1/sec=16384/60=273
  WORD set crngActive flags;     // bit0 set = active, bit 1 set = reverse
  UBYTE low; UBYTE high;         // lower and upper color registers selected
  } ilbmColorRegisterRangeChunk;
 
 
     protected void decodeCRNG(IFFChunk chunkANIMMovieTrack track)
             throws ParseException {
         try {
             ColorCycle cc;
             MC68000InputStream in = new MC68000InputStream(new ByteArrayInputStream(chunk.getData()));
 
             int pad1 = in.readUWORD();
             int rate = in.readUWORD();
             int flags = in.readUWORD();
             int low = in.readUBYTE();
             int high = in.readUBYTE();
 //System.out.println("CRNG pad1:"+pad1+" rate:"+rate+" flags:"+flags+" low:"+low+" high:"+high);
             cc = new CRNGColorCycle(rate, 273, //
                     lowhigh//
                     (flags & 1) != 0 && rate > 36 && high > low//
                     (flags & 2) != 0, //
                     track.getScreenMode() == .);
 
             if (cc.isActive()) {
                 track.addColorCycle(cc);
             }
 
             in.close();
         } catch (IOException e) {
             throw new ParseException(e.toString());
         }
     }

    
Decodes the DPaint IV enhanced color cycle chunk (ILBM DRNG)

The RNG_ACTIVE flag is set when the range is cyclable. A range should only have the RNG _ACTIVE if it:

  1. contains at least one color register
  2. has a defined rate
  3. has more than one color and/or color register
 ILBM DRNG DPaint IV enhanced color cycle chunk
 --------------------------------------------

 set {
     RNG_ACTIVE=1,RNG_DP_RESERVED=4
 } drngFlags;

 /* True color cell * /
 typedef struct {
     UBYTE cell;
     UBYTE r;
     UBYTE g;
     UBYTE b;
 } ilbmDRNGDColor;

 /* Color register cell * /
 typedef struct {
     UBYTE cell;
     UBYTE index;
 } ilbmDRNGDIndex;

 /* DRNG chunk. * /
 typedef struct {
     UBYTE min; /* min cell value * /
     UBYTE max; /* max cell value * /
     UWORD rate; /* color cycling rate, 16384 = 60 steps/second * /
     UWORD set drngFlags flags; /* 1=RNG_ACTIVE, 4=RNG_DP_RESERVED * /
     UBYTE ntrue; /* number of DColorCell structs to follow * /
     UBYTE ntregs; /* number of DIndexCell structs to follow * /
     ilbmDRNGDColor[ntrue] trueColorCells;
     ilbmDRNGDIndex[ntregs] colorRegisterCells;
 } ilbmDRangeChunk;
 
 
     protected void decodeDRNG(IFFChunk chunkANIMMovieTrack track)
             throws ParseException {
         ColorCycle cc;
         try {
             MC68000InputStream in = new MC68000InputStream(new ByteArrayInputStream(chunk.getData()));
 
             int min = in.readUBYTE();
             int max = in.readUBYTE();
             int rate = in.readUWORD();
             int flags = in.readUWORD();
             int ntrue = in.readUBYTE();
             int nregs = in.readUBYTE();
             DRNGColorCycle.Cell[] cells = new DRNGColorCycle.Cell[ntrue + nregs];
 
             for (int i = 0; i < ntruei++) {
                 int cell = in.readUBYTE();
                 int rgb = (in.readUBYTE() << 16) | (in.readUBYTE() << 8) | in.readUBYTE();
                 cells[i] = new DRNGColorCycle.DColorCell(cellrgb);
             }
             for (int i = 0; i < nregsi++) {
                 int cell = in.readUBYTE();
                 int index = in.readUBYTE();
                 cells[i + ntrue] = new DRNGColorCycle.DIndexCell(cellindex);
             }
 
 //System.out.println("DRNG min:"+min+" max:"+max+" rate:"+rate+" flags:"+flags+" ntrue:"+ntrue+" nregs:"+nregs);
             cc = new DRNGColorCycle(rate, 273, minmax//
                     (flags & 1) != 0 && rate > 36 && min <= max && ntrue + nregs > 1,//
                     track.getScreenMode() == .cells);
             if (cc.isActive()) {
                 track.addColorCycle(cc);
             }
 
             in.close();
         } catch (IOException e) {
             throw new ParseException(e.toString());
         }
     }

    
Process CRNG and DRNG chunks in the sequence of their location in the file.
 
     protected void decodeColorCycling(IFFChunk[] ccrtChunksIFFChunk[] crngChunksIFFChunk[] drngChunksANIMMovieTrack trackthrows ParseException {
         int activeCycles = 0;
         int j = 0, k = 0, l = 0;
         for (int i = 0, n = ccrtChunks.length + crngChunks.length + drngChunks.lengthi < ni++) {
             if (j < crngChunks.length //
                     && (k >= drngChunks.length || crngChunks[j].getScan() < drngChunks[k].getScan())//
                     && (l >= ccrtChunks.length || crngChunks[j].getScan() < ccrtChunks[l].getScan())) {
                 decodeCRNG(crngChunks[j], track);
                 j++;
             } else if (k < drngChunks.length //
                     && (l >= ccrtChunks.length || drngChunks[k].getScan() < ccrtChunks[l].getScan())) {
                 decodeDRNG(drngChunks[k], track);
                 k++;
             } else {
                 decodeCCRT(ccrtChunks[l], track);
                 l++;
             }
         }
         track.setProperty("colorCycling"track.getColorCyclesCount());
     }
 
     private void decodeBODY(ColorModel colorModelIFFChunk groupIFFChunk bodyANIMMovieTrack track)
             throws ParseException {
         ANIMKeyFrame frame = new ANIMKeyFrame();
         frame.setColorModel(colorModel);
 
         decodeANHD(group.getPropertyChunk(), frame);
         if (group.getPropertyChunk() != null) {
             decodeANFI(group.getPropertyChunk(), frametrack);
         }
         IFFChunk[] sctlChunks = group.getCollectionChunks();
         for (int i = 0; i < sctlChunks.lengthi++) {
             decodeSCTL(sctlChunks[i], frametrack);
         }
         frame.cleanUpAudioCommands();
         frame.setData(body.getData());
         frame.setCompression(track.getCompression());
         // This is not good, because subsequent body frames may be compressed differently.
 
         track.addFrame(frame);
     }
 
     private void decodeDLTA(ColorModel colorModelIFFChunk groupIFFChunk dltaANIMMovieTrack track)
             throws ParseException {
         ANIMDeltaFrame frame = new ANIMDeltaFrame();
         frame.setColorModel(colorModel);
 
         decodeANHD(group.getPropertyChunk(), frame);
         if (group.getPropertyChunk() != null) {
             decodeANFI(group.getPropertyChunk(), frametrack);
         }
         IFFChunk[] sctlChunks = group.getCollectionChunks();
         for (int i = 0; i < sctlChunks.lengthi++) {
             decodeSCTL(sctlChunks[i], frametrack);
         }
 
         frame.cleanUpAudioCommands();
         frame.setData(dlta.getData());
 
         track.addFrame(frame);
     }

    
Decodes the anim header (ILBM ANHD).
 typedef UBYTE Operation; // Choice of compression algorithm.

 #define opDirect        0  // set directly (normal ILBM BODY)
 #define opXOR           1  // XOR ILBM mode
 #define opLongDelta     2  // Long Delta mode
 #define opShortDelta    3  // Short Delta Mode
 #define opGeneralDelta  4  // Generalized short/long Delta mode
 #define opByteVertical  5  // Byte Vertical Delta mode
 #define opStereoDelta   6  // Stereo op 5 (third party)
 #define opVertical7     7  // Short/Long Vertical Delta mode (opcodes and data stored separately)
 #define opVertical8     8  // Short/Long Vertical Delta mode (opcodes and data combined)
 #define opJ            74  // (ascii 'J') reserved for Eric Graham's compression technique

 typedef struct {
 Operation   operation; // The compression method.
 UBYTE       mask;      // XOR mode only - plane mask where each
 // bit is set =1 if there is data and =0
 // if not.
 UWORD       w,h;       // XOR mode only - width and height of the
 // area represented by the BODY to eliminate
 // unnecessary un-changed data.
 UWORD        x,y;       // XOR mode only - position of rectangular
 // area represented by the BODY.
 ULONG       abstime;   // currently unused - timing for a frame
 // relative to the time the first frame
 // was displayed - in jiffies (1/60 sec).
 ULONG       reltime;   // timing for frame relative to time
 // previous frame was displayed - in
 // jiffies (1/60 sec).
 UBYTE       interleave;// unused so far - indicates how many frames
 // back this data is to modify. =0 defaults
 // to indicate two frames back (for double
 // buffering). =n indicates n frames back.
 // The main intent here is to allow values
 // of =1 for special applications where
 // frame data would modify the immediately
 // previous frame.
 UBYTE        pad0;     // Pad byte, not used at present.
 ULONG        bits;     // 32 option bits used by opGeneralDelta,
 // opByteVertical, opVertical7 and opVertical8.
 // At present only 6 are identified, but the
 // rest are set =0 tso they can be used to
 // implement future ideas. These are defined
 // for opGeneralData only at this point. It is
 // recommended that all bits be set =0 for
 // opByteVertical and that any bit settings used in
 // the future (such as for XOR mode) be compatible
 // with the opGeneralData settings. Player code
 // should check undefined bits in opGeneralData and
 // opByteVertical to assure they are zero.
 //
 // The six bits for current use are:
 //
 // bit #    set =0          set =1
 // =======================================
 // 0        short data          long data
 // 1        set                 XOR
 // 2        separate info       one info list
 //          for each plane      for all planes
 // 3        not RLC             RLC (run length coded)
 // 4        horizontal          vertical
 // 5        short info offsets  long info offsets

 UBYTE        pad[16];  // This is a pad for future use for future
 // compression modes.
 } AnimHeader;
 
 
     private void decodeANHD(IFFChunk chunkANIMFrame frame)
             throws ParseException {
         if (chunk != null) {
             try {
                 MC68000InputStream in = new MC68000InputStream(new ByteArrayInputStream(chunk.getData()));
                 frame.setOperation(in.readUBYTE());
                 frame.setMask(in.readUBYTE());
                 frame.setWidth(in.readUWORD());
                 frame.setHeight(in.readUWORD());
                 frame.setY(in.readUWORD());
                 frame.setX(in.readUWORD());
                 frame.setAbsTime(in.readULONG());
                 frame.setRelTime(in.readULONG());
                 frame.setInterleave(in.readUBYTE());
                 in.skip(1);
                 frame.setBits((intin.readULONG());
                 //    in.skip(16);
                 in.close();
             } catch (IOException e) {
                 throw new ParseException(e.toString());
             }
         }
     }

    
Decodes the anim frame info (ILBM ANFI).
 enum {
 play = 0x28,
 doNothing = 0x0
 } anfiCommand;

 typedef struct {
 USHORT enum anfiCommand command;   // What to do, see above
 USHORT frequencyDivider; // frequency divider (Amiga's audio.device)
 UBYTE  sound; // Sound Number (starting at 1).
 UBYTE  channel;    // Channel number (1 to 4, 0=invalid)
 UBYTE  repeats; // repeat count (0..2 = play once, 3..? = play 2 or more times)
 UBYTE  volume;   // volume 00 to 40 (max should be 3F weird, perhaps volum 01 is 00 and 00 is channel off)
 } anfiCommandInfo;

 typedef struct {
 anfiCommandInfo[4] commandInfo;
 UBYTE[4] pad4;       // For future use
 } animANFIChunk;
 
 
     private void decodeANFI(IFFChunk chunkANIMFrame frameANIMMovieTrack track)
             throws ParseException {
         try {
             MC68000InputStream in = new MC68000InputStream(new ByteArrayInputStream(chunk.getData()));
 
             for (int i = 0; i < 4; i++) {
                 int command = in.readUWORD();
                 int frequency = in.readUWORD();
                 int sound = in.readUBYTE();
                 int channel = in.readUBYTE();
                 int repeats = in.readUBYTE();
                 if (repeats > 2) {
                     repeats -= 1;
                 } else {
                     repeats = 1;
                 }
                 int volume = in.readUBYTE();
                 //if (command == 0x28 || command == 0x81f) {
                 if (command != 0) {
                     ANIMAudioCommand audioCommand = new ANIMAudioCommand(.volume - 1, soundrepeats, 1 << channel, 0 /*frequency*/, 0);
                     frame.addAudioCommand(audioCommand);
                     audioCommand.prepare(track);
                 }
             }
             //int pad = in.readULONG();
             in.close();
         } catch (IOException e) {
             throw new ParseException(e.toString());
         }
 
         /*
         if (chunk != null) {
         try {
         MC68000InputStream in = new MC68000InputStream( new ByteArrayInputStream( chunk.getData() ) ) ;
         for (int i = 0; i < 4; i ++) {
         //in.skip(4); // skip pad bytes
         long pad1a = in.readWORD();
         long pad1b = in.readWORD();
         int audioclip = in.readUBYTE();
         //in.skip(3); // skip pad bytes
         int repeatCount = in.readUBYTE();
         int pad3 = in.readWORD();
         if (audioclip != 0) {
         frame.setAudioClip(audioclip - 1, i, repeatCount);
         //            System.out.println("channel:"+i+" clip:"+audioclip+ " pad1a:"+pad1a+" pad1b:"+pad1b +" repeat:"+repeatCount+" pad3:"+pad3);
         }
         }
         }
         catch (IOException e) {
         throw new ParseException(e.toString());
         }
         }*/
     }

    
Decodes the ANIM+SLA Sound Control collection chunk (ILBM SCTL).
 typedef UBYTE Command; // Choice of commands
 #define cmdPlaySound 1 // Start playing a sound
 #define cmdStopSound 2 // Stop the sound in a given channel
 #define cmdSetFreqvol 3 // Change frequency/volume for a channel

 typedef USHORT Flags; // Choice of flags
 #define flagNoInterrupt 1 // Play the sound, but only if
                           // the channel isn't in use

 typedef struct {
 Command  command;   // What to do, see above
 UBYTE    volume;    // Volume 0..64
 UWORD    sound,     // Sound number (one based)
          repeats,   // Number of times to play the sound
          channel,   // Channel(s) to use for playing (bit mask)
          frequency; // If non-zero, overrides the VHDR value
 Flags    flags;     // Flags, see above
 UBYTE    pad[4];       // For future use
 } SoundControl;

 
 
     private void decodeSCTL(IFFChunk chunkANIMFrame frameANIMMovieTrack track)
             throws ParseException {
         try {
             MC68000InputStream in = new MC68000InputStream(new ByteArrayInputStream(chunk.getData()));
             int command = in.readUBYTE();
             int volume = in.readUBYTE();
             int sound = in.readUWORD();
             int repeats = in.readUWORD();
             int channel = in.readUWORD();
             int frequency = in.readUWORD();
             int flags = in.readUWORD();
             //int pad = in.readULONG();
             in.close();
            ANIMAudioCommand audioCommand = new ANIMAudioCommand(commandvolumesoundrepeatschannelfrequencyflags);
            frame.addAudioCommand(audioCommand);
            audioCommand.prepare(track);
        } catch (IOException e) {
            throw new ParseException(e.toString());
        }
    }
    protected void decodeCOPYRIGHT(IFFChunk[] chunksANIMMovieTrack track)
            throws ParseException {
        for (int i = 0; i < chunks.lengthi++) {
            String copyright = new String(chunks[i].getData());
            appendProperty("copyright"copyright);
            appendProperty("comment""� " + copyright);
        }
    }
    protected void decodeAUTH(IFFChunk[] chunksANIMMovieTrack track)
            throws ParseException {
        for (int i = 0; i < chunks.lengthi++) {
            String author = new String(chunks[i].getData());
            appendProperty("author"author);
            appendProperty("comment""Author " + author);
        }
    }
    protected void decodeANNO(IFFChunk[] chunksANIMMovieTrack track)
            throws ParseException {
        for (int i = 0; i < chunks.lengthi++) {
            String anno = new String(chunks[i].getData());
            appendProperty("annotation"anno);
            appendProperty("comment"anno);
        }
    }
    private void appendProperty(String nameString value) {
        String oldValue = (String.getProperty(name);
        if (oldValue == null) {
            .setProperty(namevalue);
        } else {
            .setProperty(nameoldValue + "\n" + value);
        }
    }
    /*
    /* Normal identifiers. * /
    public final static int DEFAULT_MONITOR_ID = 0x00000000;
    public final static int NTSC_MONITOR_ID = 0x00011000;
    public final static int PAL_MONITOR_ID = 0x00021000;
    public final static int LORES_MASK  = 0x00000000;
    public final static int LACE_MASK  = 0x00000004;
    public final static int HIRES_MASK  = 0x00008000;
    public final static int SUPER_MASK  = 0x00008020;
    public final static int MODE_MASK    = 0x00000800;
    public final static int DPF_MASK    = 0x00000400;
    public final static int DPF2_MASK  = 0x00000440;
    public final static int MODE_MASK    = 0x00000080;
    
    /*
    The following 20 composite keys are for Modes on the default Monitor.
    NTSC & PAL "flavours" of these particular keys may be made by or'ing
    the NTSC or PAL MONITOR_ID with the desired MODE_KEY.
     * /
    public final static int LORES_KEY      = 0x00000000; // NTSC:320*200,44x52  PAL:320x256,44x44
    public final static int HIRES_KEY      = 0x00008000; // NTSC:640*200,22x52  PAL:640*256,22x44
    public final static int SUPER_KEY      = 0x00008020; // NTSC:1280*200,11x52  PAL:1280x256,11x44
    public final static int HAM_MODE        = 0x00000800; // NTSC:320*200,44x52  PAL:320x256,44x44
    public final static int LORESLACE_KEY    = 0x00000004; // NTSC:320*400,44x26 PAL:320x512,44x22
    public final static int HIRESLACE_KEY    = 0x00008004; // NTSC:640*400,22x26  PAL:640x512,22x22
    public final static int SUPERLACE_KEY    = 0x00008024; // NTSC:1280*400,11x26  PAL:1280x512,11x22
    public final static int HAMLACE_KEY      = 0x00000804; // NTSC:320*400,44x26  PAL:320x512,44x22
    public final static int LORESDPF_KEY      = 0x00000400; // 320*240,256
    public final static int HIRESDPF_KEY      = 0x00008400; // 640*240,256
    public final static int SUPERDPF_KEY      = 0x00008420; // 1280*240,256
    public final static int LORESLACEDPF_KEY    = 0x00000404; // 320*480,512
    public final static int HIRESLACEDPF_KEY    = 0x00008404; // 640*480,512
    public final static int SUPERLACEDPF_KEY    = 0x00008424; // 1280*480,512
    public final static int LORESDPF2_KEY    = 0x00000440; // 320*240,256
    public final static int HIRESDPF2_KEY    = 0x00008440; // 640*240,256
    public final static int SUPERDPF2_KEY    = 0x00008460; // 1280*240,256
    public final static int LORESLACEDPF2_KEY  = 0x00000444; // 320*480,512
    public final static int HIRESLACEDPF2_KEY  = 0x00008444; // 640*480,512
    public final static int SUPERLACEDPF2_KEY  = 0x00008464; // 1280*480,512
    public final static int EHB_MODE  = 0x00000080; // NTSC:320*200,44x52  PAL:320*256,44x44
    public final static int EXTRAHALFBRITELACE_KEY  = 0x00000084; // NTSC:320*400,44x26  PAL:320*512,44x22
    
    /* VGA identifiers. * /
    public final static int MULTISCAN_MONITOR_ID  = 0x00031000;
    public final static int VGALACE_MASK    = 0x00000001;
    public final static int VGALORES_MASK  = 0x00008000;
    
    public final static int VGAEXTRALORES_KEY    = 0x00031004; // 160*480 v 88x22
    public final static int VGALORES_KEY        = 0x00039004; // 320*480 v 44x22
    public final static int VGAPRODUCT_KEY      = 0x00039024; // 640*480 v 22x22
    public final static int VGAHAM_KEY        = 0x00031804; //
    public final static int VGAEXTRALORESLACE_KEY  = 0x00031005; // 160*960 v 88x11
    public final static int VGALORESLACE_KEY      = 0x00039005; // 320*960 v 44x11
    public final static int VGAPRODUCTLACE_KEY    = 0x00039025; // 640*960 v 22x11
    public final static int VGAHAMLACE_KEY      = 0x00031805; //
    public final static int VGAEXTRALORESDPF_KEY    = 0x00031404; //
    public final static int VGALORESDPF_KEY      = 0x00039404;
    public final static int VGAPRODUCTDPF_KEY    = 0x00039424;
    public final static int VGAEXTRALORESLACEDPF_KEY  = 0x00031405;
    public final static int VGALORESLACEDPF_KEY    = 0x00039405;
    public final static int VGAPRODUCTLACEDPF_KEY  = 0x00039425; //
    public final static int VGAEXTRALORESDPF2_KEY  = 0x00031444;
    public final static int VGALORESDPF2_KEY      = 0x00039444;
    public final static int VGAPRODUCTDPF2_KEY    = 0x00039464;
    public final static int VGAEXTRALORESLACEDPF2_KEY  = 0x00031445;
    public final static int VGALORESLACEDPF2_KEY    = 0x00039445;
    public final static int VGAPRODUCTLACEDPF2_KEY  = 0x00039465; // 640*960
    public final static int VGAEXTRAHALFBRITE_KEY  = 0x00031084;
    public final static int VGAEXTRAHALFBRITELACE_KEY = 0x00031085;
    
    /* A2024 identifiers. * /
    public final static int A2024_MONITOR_ID = 0x00041000;
    
    public final static int A2024TENHERTZ_KEY    = 0x00041000;
    public final static int A2024FIFTEENHERTZ_KEY  = 0x00049000;
    
    /* Proto identifiers. * /
    public final static int PROTO_MONITOR_ID = 0x00051000;
    
    /* Euro identifiers * /
    public final static int EURO36_MONITOR_ID = 0x00071000;
    
    public final static int EURO36EXTRAHALFBRITE_KEY  = 0x00071080; // 320*200 v 44x44
    public final static int EURO36EXTRAHALFBRITELACE_KEY = 0x00071084; // 320*400 v 44x22
    public final static int EURO36HAM_KEY      = 0x00071800; // 320*200 v 44x44
    public final static int EURO36HAMLACE_KEY    = 0x00071804; // 320*400 v 44x22
    public final static int EURO36HIRES_KEY      = 0x00079000; // 640*200 v 22x44
    public final static int EURO36HIRESLACE_KEY    = 0x00079004; // 640*400 v 22x22
    public final static int EURO36LORES_KEY      = 0x00071000; // 320*200 v 44x44
    public final static int EURO36LORESLACE_KEY    = 0x00071004; // 320*400 v 44x22
    public final static int EURO36SUPERHIRES_KEY    = 0x00079020; // 1280*200 v 11x44
    public final static int EURO36SUPERHIRESLACE_KEY  = 0x00079024; // 1280*400 v 11x44
    public final static int EURO72ECS_KEY      = 0x00069004; // 320*400 v 44x22
    public final static int EURO72ECSLACE_KEY    = 0x00069005; // 320*800 v 44x11
    public final static int EURO72PRODUCT_KEY    = 0x00069024; // 640*400 v 22x22
    public final static int EURO72PRODUCTLACE_KEY  = 0x00069025; // 640*800 v 22x11
    
    
    public final static int EURO_KEY = 0x00071000;
     */
New to GrepCode? Check out our FAQ X