package ch.werner_randelshofer.gui.image;/*Copyright (C) 1999 Werner Randelshoferwerner.randelshofer@mythen.chhttp://www.mythen.ch/w.randelshofer/    Permission to use this release of ILBMDecoder is herebygranted without fee provided that the complete copyrightnotice and this permission notice appear in all copiesand in supporting documentation.*/import ch.werner_randelshofer.iff.*;import ch.werner_randelshofer.util.*;import java.io.*;import java.util.*;import java.awt.image.*;import java.net.URL;/**Creates Image objects by reading an IFF ILBM stream.@author	Werner Randelshofer, Staldenmattweg 2, CH-6405 Immensee, Switzerland@version	1999-04-25	Better compatibility with bogus Java VM's:						Always use 8 bits for IndexColorModel.<br>history	1999-01-02	Reworked.<br>history	1997-08-30	Created.*/public class ILBMDecoderimplements IFFVisitor	{	protected final static int ILBM_ID = IFFContext.stringToID("ILBM");	protected final static int BMHD_ID = IFFContext.stringToID("BMHD");	protected final static int CMAP_ID = IFFContext.stringToID("CMAP");	protected final static int CAMG_ID = IFFContext.stringToID("CAMG");	protected final static int BODY_ID = IFFContext.stringToID("BODY");	protected final static int		MODE_INDEXED_COLORS = 0,		MODE_DIRECT_COLORS = 1,		MODE_EHB = 2,		MODE_HAM6 = 3,		MODE_HAM8 = 4;	protected final static int		MSK_NONE = 0,		MSK_HAS_MASK = 1,		MSK_HAS_TRANSPARENT_COLOR = 2,		MSK_LASSO = 3;			protected final static int		CMP_NONE = 0,		CMP_BYTE_RUN_1 = 1;	/** Mask bits for CAMG ModeID. */	protected final static int MONITOR_ID_MASK = 0xffff1000;	protected final static int EXTENDED_MODE = 0x00001000; // does anybody know the extended mode bit?	/* Instance variables */	protected InputStream inputStream_;	protected URL location_;	protected Vector sources_;	/* Properties. */	protected Hashtable properties_;	/* BMHD data */	protected int bmhdWidth_,bmhdHeight_; // Raster width_ and heigth in pixels	protected int bmhdXPosition_,bmhdYPosition_; // pixel position for this image	protected int bmhdNbPlanes_; // number of source bitplanes	protected int bmhdMasking_; 	protected int bmhdCompression_;	protected int bmhdTransparentColor_; // transparent "color nomber" (sort of)	protected int bmhdXAspect_, bmhdYAspect_; // pixel aspect, a ratio width_ : height_	protected int bmhdPageWidth_, bmhdPageHeight_; // source "page" size in pixels		/* CAMG data */	protected int camgMode_;		/* CMAP data */	protected ColorModel cmapColorModel_;	/* BODY data */	protected Bitmap bodyBitmap_;	/* Constructors */	public ILBMDecoder(InputStream in)		{		inputStream_ = in;		}	public ILBMDecoder(URL location)		{		location_ = location;		}	public Vector produce()	throws IOException		{		InputStream in = null;		sources_ = new Vector();		try			{			if (inputStream_ != null)				{				in = inputStream_;				}			else				{				in = location_.openStream();				}			IFFParser iff = new IFFParser();			registerChunks(iff);			iff.parse(in,this);			}		catch (ParseException e1)			{ //System.out.println(e1);			}		catch (AbortException e)			{ //System.out.println(e);			}		finally			{			try				{				if (in != null)					{ in.close(); }				}			catch (IOException e)				{ System.out.println(e); }			}		return sources_;		}	public void registerChunks(IFFParser iff)		{		iff.declareGroupChunk(ILBM_ID,IFFContext.FORM_ID);		iff.declarePropertyChunk(ILBM_ID,BMHD_ID);		iff.declarePropertyChunk(ILBM_ID,CMAP_ID);		iff.declarePropertyChunk(ILBM_ID,CAMG_ID);		iff.declareDataChunk(ILBM_ID,BODY_ID);		}	public void enterGroup(IFFChunk chunk)		{ }	public void leaveGroup(IFFChunk chunk)		{ }	public void visitChunk(IFFChunk group, IFFChunk chunk)	throws ParseException, AbortException		{		decodeBMHD(group.getPropertyChunk(BMHD_ID));		decodeCAMG(group.getPropertyChunk(CAMG_ID));		decodeCMAP(group.getPropertyChunk(CMAP_ID));		decodeBODY(chunk);		Hashtable props = new Hashtable();		double aspect = (double)bmhdXAspect_ / (double)bmhdYAspect_;		if (bmhdXAspect_ == 0 || bmhdYAspect_ == 0)			{ aspect = 1d; }		props.put("aspect",new Double(aspect));				MemoryImageSource mis;		if (bodyBitmap_.convertToChunky() == Bitmap.BYTE_PIXEL)			{			mis = new MemoryImageSource(					bmhdWidth_,bmhdHeight_,					cmapColorModel_,				    bodyBitmap_.getBytePixels(), 0, bmhdWidth_,				    props				    );			}		else			{			mis = new MemoryImageSource(					bmhdWidth_,bmhdHeight_,					cmapColorModel_,				    bodyBitmap_.getIntPixels(), 0, bmhdWidth_,				    props				    );			}		sources_.addElement(mis);			}	protected void decodeBMHD(IFFChunk chunk)	throws ParseException		{		if (chunk == null)			{ throw new ParseException("no BMHD -> no Picture"); }		try {			MC68000InputStream in = new MC68000InputStream( new ByteArrayInputStream( chunk.getData() ) ) ;			bmhdWidth_ = in.readUWORD();			bmhdHeight_ = in.readUWORD();			bmhdXPosition_ = in.readWORD();			bmhdYPosition_ = in.readWORD();			bmhdNbPlanes_ = in.readUBYTE();			bmhdMasking_ = in.readUBYTE();			bmhdCompression_ = in.readUBYTE();			in.skip(1);			bmhdTransparentColor_ = in.readUWORD();			bmhdXAspect_ = in.readUBYTE();			bmhdYAspect_ = in.readUBYTE();			bmhdPageWidth_ = in.readWORD();			bmhdPageHeight_ = in.readWORD();			in.close();			}		catch (IOException e)			{ throw new ParseException(e.toString()); }		}	/**	Decodes the CAMG Chunk.	Requires data from BMHD Chunk.	*/	protected void decodeCAMG(IFFChunk chunk)	throws ParseException		{		int camgMode = 0;				if (chunk != null)			{			try {				MC68000InputStream in = new MC68000InputStream( new ByteArrayInputStream( chunk.getData() ) ) ;				camgMode = in.readLONG();								in.close();				}			catch (IOException e)				{ throw new ParseException(e.toString()); }			}		/*		/*	Knock bad bits out of old-style CAMG modes before checking availability.			(some ILBM CAMG's have these bits set in old 1.3 modes, and should not)			If not an extended monitor ID, or if marked as extended but missing			upper 16 bits, screen out inappropriate bits now.		* /		if (			((camgMode & MONITOR_ID_MASK) == 0) ||			(((camgMode & EXTENDED_MODE) != 0) && ((camgMode & 0xFFFF0000) == 0))			)			{			//camgMode &= !(EXTENDED_MODE|SPRITES|GENLOCK_AUDIO|GENLOCK_VIDEO|VP_HIDE); // does anybody know these bits?			camgMode &= 0xffff8fff; 			}				/*	Check for bogus camgMode like some brushes have, with junk in			upper word and extended bit NOT set in lower word.		* /		if ( ((camgMode & 0xffff0000) != 0) && ((camgMode & EXTENDED_MODE) == 0) )			{			/*	Bad camgMode, so ignore camgMode and determine a mode based on				pagesize or aspect.			* /			camgMode = 0;			}		*/				switch (camgMode & 0x880)			{			case 0x80 :				camgMode_ = MODE_EHB;				break;			case 0x800 :				if (bmhdNbPlanes_ == 6)					{ camgMode_ = MODE_HAM6; }				else					{					if (bmhdNbPlanes_ == 8)						{ camgMode_ = MODE_HAM8; }					else						{ 						throw new ParseException("unsupported Ham Mode with " + bmhdNbPlanes_ + " bitplanes");						}					}				break;			default :				if (bmhdNbPlanes_ <= 8)					{ camgMode_ = MODE_INDEXED_COLORS; }				else					{ camgMode_ = MODE_DIRECT_COLORS; }			}		}	protected void decodeCMAP(IFFChunk chunk)	throws ParseException		{		byte[] red;		byte[] green;		byte[] blue;		int size = 0;		int colorsToRead = 0;				if (chunk == null && camgMode_ != MODE_DIRECT_COLORS)			{ throw new ParseException("No CMAP -> not yet supported"); }		switch (camgMode_)			{			case MODE_EHB :				size = 64;				colorsToRead = Math.min(32,(int)chunk.getSize() / 3);				break;			case MODE_HAM6:			case MODE_HAM8:				size = 1<<(bmhdNbPlanes_ - 2);				colorsToRead = Math.min(size,(int)chunk.getSize() / 3);				break;			case MODE_INDEXED_COLORS :				size = 1<<(bmhdNbPlanes_);				colorsToRead = Math.min(size,(int)chunk.getSize() / 3);				break;			case MODE_DIRECT_COLORS :				cmapColorModel_ = new DirectColorModel(24,0x00000F,0x0000F0,0x000F00);				return;			}		red = new byte[size];		green = new byte[size];		blue = new byte[size];		byte[] data = chunk.getData();		int j = 0;		for (int i = 0;i < colorsToRead; i++)			{			red[i] = data[j++];			green[i] = data[j++];			blue[i] = data[j++];			} 		switch (camgMode_) {			case MODE_EHB :				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);					}				cmapColorModel_ = new IndexColorModel(8,64,red,green,blue,255);//1999-04-25	cmapColorModel_ = new IndexColorModel(bmhdNbPlanes_,64,red,green,blue,255);				break;			case MODE_HAM6 :				cmapColorModel_ = new HAMColorModel(HAMColorModel.HAM6,16,red,green,blue);				break;			case MODE_HAM8 :				cmapColorModel_ = new HAMColorModel(HAMColorModel.HAM8,64,red,green,blue);				break;			case MODE_INDEXED_COLORS :				cmapColorModel_ = new IndexColorModel(8,(int)chunk.getSize() / 3,red,green,blue);//1999-04-25	cmapColorModel_ = new IndexColorModel(bmhdNbPlanes_,(int)chunk.getSize() / 3,red,green,blue);				break;			}		}	protected void decodeBODY(IFFChunk chunk)	throws ParseException		{		if ((bmhdMasking_ & MSK_HAS_MASK) != 0)			{			bodyBitmap_ = new Bitmap(bmhdWidth_,bmhdHeight_,bmhdNbPlanes_ + 1,cmapColorModel_);			}		else			{			bodyBitmap_ = new Bitmap(bmhdWidth_,bmhdHeight_,bmhdNbPlanes_,cmapColorModel_);			}		byte[] data = chunk.getData();		switch (bmhdCompression_)			{			case CMP_NONE :				System.arraycopy(data,0, bodyBitmap_.getBitmap(), 0, data.length);				break;			case CMP_BYTE_RUN_1 :				decodeByteRun1(data,bodyBitmap_.getBitmap());				break;			default :				throw new ParseException("unknown compression method: " + bmhdCompression_);			}		}	public static int decodeByteRun1(byte[] in, byte[] out)	throws ParseException		{		int iOut = 0; 		int iIn = 0;		int command = 0;		int finalIndex = in.length;		byte copyByte;		try			{			while (iOut < out.length)				{ 				command = in[iIn++];				if (command >= 0)					{					for (;command >= 0;command--)						{ out[iOut++] = in[iIn++]; }					}				else					{					if (command != -128)						{						copyByte = in[iIn++];						for (;command < 1;command++)							{ out[iOut++] = copyByte; }						}					}				}			}		catch (IndexOutOfBoundsException e)			{			System.out.println("ILBMDecoder.decodeByteRun1(): " + 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: " + command); 			}		return iOut;		}/*	/* 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 HAM_MASK		= 0x00000800;	public final static int DPF_MASK		= 0x00000400;	public final static int DPF2_MASK	= 0x00000440;	public final static int EHB_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_KEY				= 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 EXTRAHALFBRITE_KEY	= 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 VGA_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;*/	}