dmolony-DiskBrowser/src/com/bytezone/diskbrowser/applefile/HiResImage.java

850 lines
29 KiB
Java
Raw Normal View History

2016-12-31 09:34:15 +00:00
package com.bytezone.diskbrowser.applefile;
2017-01-26 04:19:23 +00:00
import java.awt.Color;
2019-11-19 19:40:23 +00:00
import java.awt.image.DataBuffer;
2016-12-31 09:34:15 +00:00
import java.io.ByteArrayInputStream;
import java.io.IOException;
2017-01-14 01:40:58 +00:00
import java.util.List;
2016-12-31 09:34:15 +00:00
import javax.imageio.ImageIO;
import com.bytezone.diskbrowser.prodos.ProdosConstants;
2017-01-26 04:19:23 +00:00
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
2016-12-31 09:34:15 +00:00
2019-11-21 08:13:41 +00:00
// -----------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public abstract class HiResImage extends AbstractFile
2019-11-21 08:13:41 +00:00
// -----------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
2024-03-12 07:37:13 +00:00
static final String[] auxTypes =
{ "Paintworks Packed SHR Image", "Packed Super Hi-Res Image",
"Super Hi-Res Image (Apple Preferred Format)", "Packed QuickDraw II PICT File",
"Packed Super Hi-Res 3200 color image", "DreamGraphix" };
2019-11-22 08:33:59 +00:00
static final int COLOR_TABLE_SIZE = 32;
2020-03-30 04:57:07 +00:00
static final int COLOR_TABLE_OFFSET_AUX_0 = 32_256;
static final int COLOR_TABLE_OFFSET_AUX_2 = 32_000;
2019-11-28 04:36:49 +00:00
public static final int FADDEN_AUX = 0x8066;
2020-03-30 04:57:07 +00:00
private byte[] fourBuf = new byte[4];
2020-03-31 11:00:40 +00:00
private ColorTable defaultColorTable320 = new ColorTable (0, 0x00);
private ColorTable defaultColorTable640 = new ColorTable (0, 0x80);
2019-11-21 08:13:41 +00:00
2021-09-15 00:51:45 +00:00
// ---- ---- ------ -------------------------------------- ------------------------
// File Type Aux Name Description
// ---- ---- ------ -------------------------------------- ------------------------
// $06 BIN isGif() OriginalHiResImage
// $06 BIN isPng() OriginalHiResImage
// $06 BIN .BMP isBmp() OriginalHiResImage
// $06 BIN .AUX DoubleHiResImage
// $06 BIN .PAC DoubleHiResImage
// $06 BIN .A2FC DoubleHiResImage
// $06 BIN $2000 eof $4000 DoubleHiResImage
// $06 BIN $1FFF eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
// $06 BIN $2000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage
// $06 BIN $4000 eof $1FF8/$1FFF/$2000/$4000 OriginalHiResImage (?)
// $06 BIN $4000 eof $4000 DoubleHiResImage (?)
// $06 BIN .3200 SHRPictureFile2
// $06 BIN .3201 SHRPictureFile2 packed
// ---- ---- ------ -------------------------------------- ------------------------
// $08 FOT <$4000 Apple II Graphics File OriginalHiResImage
// $08 FOT $4000 Packed Hi-Res file ???
// $08 FOT $4001 Packed Double Hi-Res file ???
// $08 FOT $8066 Fadden Hi-res FaddenHiResImage
// ---- ---- ------ -------------------------------------- ------------------------
// * $C0 PNT $0000 Paintworks Packed Super Hi-Res SHRPictureFile2
// * $C0 PNT $0001 Packed IIGS Super Hi-Res Image SHRPictureFile2
// * $C0 PNT $0002 IIGS Super Hi-Res Picture File (APF) SHRPictureFile1
// $C0 PNT $0003 Packed IIGS QuickDraw II PICT File SHRPictureFile2 *
// * $C0 PNT $0004 Packed Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3201
// $C0 PNT $1000
// $C0 PNT $8000 Drawplus? Paintworks Gold?
// $C0 PNT $8001 GTv background picture
// $C0 PNT $8005 DreamGraphix document SHRPictureFile2
// $C0 PNT $8006 GIF
// ---- ---- ------ -------------------------------------- ------------------------
// * $C1 PIC $0000 IIGS Super Hi-Res Image SHRPictureFile2
// $C1 PIC $0001 IIGS QuickDraw II PICT File SHRPictureFile2 *
// * $C1 PIC $0002 Super Hi-Res 3200 (Brooks) SHRPictureFile2 .3200
// $C1 PIC $2000 = $C1/0000
// $C1 PIC $4100 = $C1/0000
// $C1 PIC $4950 = $C1/0000
// $C1 PIC $8001 Allison raw image
// $C1 PIC $8002 Thunderscan
// $C1 PIC $8003 DreamGraphix
// ---- ---- ------ -------------------------------------- ------------------------
// $C2 ANI Paintworks animation
// $C3 PAL Paintworks palette
// ---- ---- ------ -------------------------------------- ------------------------
// packed unpacked
// $06.3200 1
// $06.3201 .
// $08 0000 .
// $08 4000 .
// $08 4001 .
// $08 8066 3
// $C0 0000 1
// $C0 0001 $C1 0000 2 1
// $C0 0002 1,5
// $C0 0003 $C1 0001 . .
// $C0 0004 $C1 0002 . 1
// $C0 1000 .
// $C0 8000 .
// $C0 8001 .
// $C0 8005 6
// $C0 8006 .
// $C1 0042 4
// $C1 0043 4
// $C1 2000 .
// $C1 4100 1
// $C1 4950 .
// $C1 8001 .
// $C1 8002 .
// $C1 8003 .
2019-11-22 05:42:07 +00:00
// 1 Graphics & Animation.2mg
2019-11-22 10:17:58 +00:00
// 2 0603 Katie's Farm - Disk 2.po
2019-11-28 04:36:49 +00:00
// 3 CompressedSlides.do
// 4 System Addons.hdv
2020-03-26 09:55:29 +00:00
// 5 gfx.po
2021-08-20 10:04:53 +00:00
// 6 Dream Grafix v1.02.po
2019-11-22 05:42:07 +00:00
2020-02-07 11:44:51 +00:00
// see also - https://docs.google.com/spreadsheets/d
2021-09-15 00:51:45 +00:00
// . /1rKR6A_bVniSCtIP_rrv8QLWJdj4h6jEU1jJj0AebWwg/edit#gid=0
// also - http://lukazi.blogspot.com/2017/03/double-high-resolution-graphics-dhgr.html
2020-02-07 11:44:51 +00:00
2018-07-31 07:02:19 +00:00
static PaletteFactory paletteFactory = new PaletteFactory ();
2017-01-12 22:11:05 +00:00
2024-03-12 07:37:13 +00:00
static final byte[] pngHeader =
{ (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
2016-12-31 09:34:15 +00:00
2018-07-31 07:02:19 +00:00
static boolean colourQuirks;
static boolean monochrome;
2016-12-31 09:34:15 +00:00
2018-07-31 07:02:19 +00:00
int fileType;
int auxType;
int eof;
2017-01-25 11:02:50 +00:00
2018-07-31 07:02:19 +00:00
int paletteIndex;
String failureReason = "";
2016-12-31 09:34:15 +00:00
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public HiResImage (String name, byte[] buffer)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
super (name, buffer);
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public HiResImage (String name, byte[] buffer, int loadAddress)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
this (name, buffer, loadAddress, false);
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public HiResImage (String name, byte[] buffer, int loadAddress, boolean scrunched)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
super (name, buffer);
this.loadAddress = loadAddress; // for the disassembly listing
if (scrunched)
this.buffer = unscrunch (buffer);
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 11:02:50 +00:00
public HiResImage (String name, byte[] buffer, int fileType, int auxType, int eof)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
super (name, buffer);
this.fileType = fileType;
this.auxType = auxType;
2017-01-25 11:02:50 +00:00
this.eof = eof;
2016-12-31 09:34:15 +00:00
}
2020-05-16 07:45:58 +00:00
// ---------------------------------------------------------------------------------//
public boolean isAnimation ()
// ---------------------------------------------------------------------------------//
{
return fileType == ProdosConstants.FILE_TYPE_ANI;
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
protected void createImage ()
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
2019-11-27 04:00:02 +00:00
if (!failureReason.isEmpty ())
return;
if (isGif (buffer) || isPng (buffer) || isBmp (buffer) || isTiff (buffer))
makeImage ();
else if (monochrome)
createMonochromeImage ();
else
createColourImage ();
2016-12-31 09:34:15 +00:00
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2018-07-30 08:44:29 +00:00
abstract void createMonochromeImage ();
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2018-07-30 08:44:29 +00:00
abstract void createColourImage ();
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-14 20:58:07 +00:00
public void checkPalette ()
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-14 20:58:07 +00:00
{
if (!monochrome && paletteIndex != paletteFactory.getCurrentPaletteIndex ())
createImage ();
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-14 11:11:07 +00:00
public void setPalette ()
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-11 21:26:28 +00:00
{
if (!monochrome)
createImage ();
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public void setColourQuirks (boolean value)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
if (colourQuirks == value)
return;
colourQuirks = value;
if (!monochrome)
createImage ();
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public void setMonochrome (boolean value)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
if (monochrome == value)
return;
monochrome = value;
createImage ();
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public static void setDefaultColourQuirks (boolean value)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
colourQuirks = value;
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public static void setDefaultMonochrome (boolean value)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
monochrome = value;
}
2020-05-15 01:14:01 +00:00
// byte +120 is the first screen hole
2016-12-31 09:34:15 +00:00
/*-
2019-07-28 06:24:52 +00:00
* Files of type $08 and any auxiliary type less than or equal to $3FFF contain a
* standard Apple II graphics file in one of several modes. After determining that
* the auxiliary type is not $4000 or $4001 (which have been defined for high-resolution
* and double high-resolution pictures packed with the Apple IIGS PackBytes routine),
* you can determine the mode of the file by examining byte +120 (+$78). The value of
2018-08-15 02:32:11 +00:00
* this byte, which ranges from zero to seven, is interpreted as follows:
2019-07-28 06:24:52 +00:00
*
2016-12-31 09:34:15 +00:00
Mode Page 1 Page 2
280 x 192 Black & White 0 4
280 x 192 Limited Color 1 5
560 x 192 Black & White 2 6
140 x 192 Full Color 3 7
*/
// SHR see - http://noboot.com/charlie/cb2e_p3.htm
2019-07-28 06:24:52 +00:00
// also: https://groups.google.com/forum/#!topic/comp.sys.apple2/zYhZ5YdNNxQ
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
@Override
public String getText ()
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
String auxText = "";
2018-08-01 07:23:12 +00:00
StringBuilder text = new StringBuilder ();
2024-03-12 07:37:13 +00:00
text.append (String.format ("Image File : %s%nFile type : $%02X %s%n", name,
fileType, ProdosConstants.fileTypes[fileType]));
2016-12-31 09:34:15 +00:00
switch (fileType)
{
2018-08-15 02:32:11 +00:00
case ProdosConstants.FILE_TYPE_FOT: // 0x08
2016-12-31 09:34:15 +00:00
if (auxType < 0x4000)
{
2017-01-26 04:19:23 +00:00
auxText = "Apple II Graphics File";
2016-12-31 09:34:15 +00:00
byte mode = buffer[0x78]; // 0-7
2017-01-26 04:19:23 +00:00
System.out.println ("Prodos PICT, mode=" + mode); // see mode table above
2016-12-31 09:34:15 +00:00
}
else if (auxType == 0x4000)
auxText = "Packed Hi-Res File";
else if (auxType == 0x4001)
auxText = "Packed Double Hi-Res File";
2019-11-28 04:36:49 +00:00
else if (auxType == FADDEN_AUX)
2018-08-15 02:32:11 +00:00
auxText = "Fadden Hi-Res File";
2017-01-27 07:11:00 +00:00
else
auxText = "Unknown aux: " + auxType;
2016-12-31 09:34:15 +00:00
break;
2017-01-26 04:19:23 +00:00
case ProdosConstants.FILE_TYPE_PNT: // 0xC0
2021-08-30 06:56:43 +00:00
if (auxType == 0x8005)
auxText = auxTypes[5];
else
auxText = auxType > 4 ? "Unknown aux: " + auxType : auxTypes[auxType];
2016-12-31 09:34:15 +00:00
break;
2017-01-26 04:19:23 +00:00
case ProdosConstants.FILE_TYPE_PIC: // 0xC1
2021-03-28 06:44:36 +00:00
auxText = switch (auxType)
2018-07-30 08:44:29 +00:00
{
2021-03-28 06:44:36 +00:00
case 0, 0x2000, 0x0042, 0x0043 -> "Super Hi-res Screen Image";
case 1 -> "QuickDraw PICT File";
case 2 -> "Super Hi-Res 3200 color image";
default -> "Unknown aux: " + auxType;
};
2016-12-31 09:34:15 +00:00
}
if (!auxText.isEmpty ())
2018-08-01 07:23:12 +00:00
text.append (String.format ("Aux type : $%04X %s%n", auxType, auxText));
2016-12-31 09:34:15 +00:00
2018-08-01 07:23:12 +00:00
text.append (String.format ("File size : %,d%n", buffer.length));
text.append (String.format ("EOF : %,d%n", eof));
2018-07-31 07:02:19 +00:00
if (!failureReason.isEmpty ())
2018-08-01 07:23:12 +00:00
text.append (String.format ("Failure : %s%n", failureReason));
2016-12-31 09:34:15 +00:00
2018-08-01 07:23:12 +00:00
text.deleteCharAt (text.length () - 1);
2016-12-31 09:34:15 +00:00
return text.toString ();
}
2019-11-20 10:25:16 +00:00
// ---------------------------------------------------------------------------------//
2020-03-31 11:00:40 +00:00
int mode320Line (int ptr, int element, int dataWidth, ColorTable colorTable,
2019-11-20 10:25:16 +00:00
DataBuffer dataBuffer, int imageWidth)
// ---------------------------------------------------------------------------------//
{
2020-03-31 11:00:40 +00:00
if (colorTable == null)
colorTable = defaultColorTable320;
for (int i = 0; i < dataWidth; i++)
2019-11-20 10:25:16 +00:00
{
2020-03-29 03:07:59 +00:00
if (ptr >= buffer.length)
{
System.out.printf ("too big: %d %d%n", ptr, buffer.length);
return ptr;
}
2019-11-24 22:37:41 +00:00
// get two pixels from this byte
2020-03-30 04:57:07 +00:00
int left = (buffer[ptr] & 0xF0) >>> 4;
2019-11-20 10:25:16 +00:00
int right = buffer[ptr++] & 0x0F;
2019-11-24 22:32:15 +00:00
// get pixel colors
2019-11-20 10:25:16 +00:00
int rgbLeft = colorTable.entries[left].color.getRGB ();
int rgbRight = colorTable.entries[right].color.getRGB ();
2020-04-02 07:04:34 +00:00
// draw two pixels (twice each) on two lines
2019-11-20 10:25:16 +00:00
draw (dataBuffer, element + imageWidth, rgbLeft, rgbLeft, rgbRight, rgbRight);
element = draw (dataBuffer, element, rgbLeft, rgbLeft, rgbRight, rgbRight);
}
2020-03-30 04:57:07 +00:00
2019-11-20 10:25:16 +00:00
return ptr;
}
// ---------------------------------------------------------------------------------//
2020-03-31 11:00:40 +00:00
int mode640Line (int ptr, int element, int dataWidth, ColorTable colorTable,
2019-11-20 10:25:16 +00:00
DataBuffer dataBuffer, int imageWidth)
// ---------------------------------------------------------------------------------//
{
2020-03-31 11:00:40 +00:00
if (colorTable == null)
colorTable = defaultColorTable640;
for (int i = 0; i < dataWidth; i++)
2019-11-20 10:25:16 +00:00
{
2019-11-24 22:37:41 +00:00
// get four pixels from this byte
2020-03-30 04:57:07 +00:00
int p1 = (buffer[ptr] & 0xC0) >>> 6;
2019-11-20 10:25:16 +00:00
int p2 = (buffer[ptr] & 0x30) >> 4;
int p3 = (buffer[ptr] & 0x0C) >> 2;
int p4 = (buffer[ptr++] & 0x03);
// get pixel colors
int rgb1 = colorTable.entries[p1 + 8].color.getRGB ();
int rgb2 = colorTable.entries[p2 + 12].color.getRGB ();
int rgb3 = colorTable.entries[p3].color.getRGB ();
int rgb4 = colorTable.entries[p4 + 4].color.getRGB ();
2020-03-26 09:55:29 +00:00
// draw four pixels on two lines
2019-11-25 07:57:14 +00:00
draw (dataBuffer, element + imageWidth, rgb1, rgb2, rgb3, rgb4); // 2nd line
element = draw (dataBuffer, element, rgb1, rgb2, rgb3, rgb4); // 1st line
2019-11-20 10:25:16 +00:00
}
2020-03-30 04:57:07 +00:00
2019-11-20 10:25:16 +00:00
return ptr;
}
2019-11-19 19:40:23 +00:00
// ---------------------------------------------------------------------------------//
2020-03-30 04:57:07 +00:00
int draw (DataBuffer dataBuffer, int element, int... rgbList)
2019-11-19 19:40:23 +00:00
// ---------------------------------------------------------------------------------//
{
2020-03-30 04:57:07 +00:00
if (dataBuffer.getSize () < rgbList.length + element)
2020-03-26 09:55:29 +00:00
{
2024-03-12 07:37:13 +00:00
System.out.printf ("Bollocks: %d %d %d%n", dataBuffer.getSize (), rgbList.length,
element);
2020-03-26 09:55:29 +00:00
return element;
}
2020-03-30 04:57:07 +00:00
for (int rgb : rgbList)
dataBuffer.setElem (element++, rgb);
2019-11-19 19:40:23 +00:00
return element;
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2020-04-03 07:38:14 +00:00
int unpack (byte[] buffer, int ptr, int max, byte[] newBuf, int newPtr)
2020-04-01 05:47:36 +00:00
// ---------------------------------------------------------------------------------//
{
int savePtr = newPtr;
while (ptr < max - 1) // minimum 2 bytes needed
{
int type = (buffer[ptr] & 0xC0) >>> 6; // 0-3
int count = (buffer[ptr++] & 0x3F) + 1; // 1-64
switch (type)
{
case 0: // 2-65 bytes
while (count-- != 0 && newPtr < newBuf.length && ptr < max)
newBuf[newPtr++] = buffer[ptr++];
break;
case 1: // 2 bytes
byte b = buffer[ptr++];
while (count-- != 0 && newPtr < newBuf.length)
newBuf[newPtr++] = b;
break;
case 2: // 5 bytes
for (int i = 0; i < 4; i++)
fourBuf[i] = ptr < max ? buffer[ptr++] : 0;
while (count-- != 0)
for (int i = 0; i < 4; i++)
if (newPtr < newBuf.length)
newBuf[newPtr++] = fourBuf[i];
break;
case 3: // 2 bytes
b = buffer[ptr++];
count *= 4;
while (count-- != 0 && newPtr < newBuf.length)
newBuf[newPtr++] = b;
break;
}
}
return newPtr - savePtr; // bytes unpacked
}
2020-03-31 11:00:40 +00:00
// ---------------------------------------------------------------------------------//
2020-04-08 02:17:08 +00:00
String debug (byte[] buffer, int ptr, int length)
2020-03-31 11:00:40 +00:00
// ---------------------------------------------------------------------------------//
{
int size = 0;
int max = ptr + length;
2020-04-08 02:17:08 +00:00
StringBuffer text = new StringBuffer ();
2020-03-31 11:00:40 +00:00
while (ptr < max)
{
int type = (buffer[ptr] & 0xC0) >>> 6; // 0-3
int count = (buffer[ptr++] & 0x3F) + 1; // 1-64
2024-03-12 07:37:13 +00:00
text.append (String.format ("%04X/%04d: %02X (%d,%2d) ", ptr - 1, size,
buffer[ptr - 1], type, count));
2020-03-31 11:00:40 +00:00
if (type == 0)
{
2024-03-12 07:37:13 +00:00
text.append (
String.format ("%s%n", HexFormatter.getHexString (buffer, ptr, count)));
2020-03-31 11:00:40 +00:00
ptr += count;
size += count;
}
else if (type == 1)
{
2020-04-08 02:17:08 +00:00
text.append (String.format ("%s%n", HexFormatter.getHexString (buffer, ptr, 1)));
2020-03-31 11:00:40 +00:00
ptr++;
size += count;
}
else if (type == 2)
{
2020-04-08 02:17:08 +00:00
text.append (String.format ("%s%n", HexFormatter.getHexString (buffer, ptr, 4)));
2020-03-31 11:00:40 +00:00
ptr += 4;
size += count * 4;
}
else
{
2020-04-08 02:17:08 +00:00
text.append (String.format ("%s%n", HexFormatter.getHexString (buffer, ptr, 1)));
2020-03-31 11:00:40 +00:00
ptr++;
size += count * 4;
}
}
2020-04-08 02:17:08 +00:00
return text.toString ();
2017-01-26 04:19:23 +00:00
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2020-04-03 07:38:14 +00:00
int calculateBufferSize (byte[] buffer, int ptr)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2019-08-21 00:31:11 +00:00
{
2021-09-15 00:51:45 +00:00
// int ptr = 0;
2019-08-21 00:31:11 +00:00
int size = 0;
while (ptr < buffer.length)
{
2019-11-19 05:25:10 +00:00
int type = (buffer[ptr] & 0xC0) >>> 6; // 0-3
2019-08-21 00:31:11 +00:00
int count = (buffer[ptr++] & 0x3F) + 1; // 1-64
if (type == 0)
{
ptr += count;
size += count;
}
else if (type == 1)
{
ptr++;
size += count;
}
else if (type == 2)
{
ptr += 4;
size += count * 4;
}
else
{
ptr++;
size += count * 4;
}
}
return size;
}
2016-12-31 09:34:15 +00:00
// Beagle Bros routine to expand a hi-res screen
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
private byte[] unscrunch (byte[] src)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
byte[] dst = new byte[0x2000];
int p1 = 0;
int p2 = 0;
while (p1 < dst.length)
{
byte b = src[p2++];
if ((b == (byte) 0x80) || (b == (byte) 0xFF))
{
b &= 0x7F;
int rpt = src[p2++];
for (int i = 0; i < rpt; i++)
dst[p1++] = b;
}
else
dst[p1++] = b;
}
return dst;
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
protected void makeImage ()
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
try
{
image = ImageIO.read (new ByteArrayInputStream (buffer));
}
catch (IOException e)
{
e.printStackTrace ();
}
2017-01-25 05:26:37 +00:00
catch (IndexOutOfBoundsException e) // some BMP files cause this
{
System.out.println ("Error in makeImage()");
System.out.println (e.getMessage ());
}
2016-12-31 09:34:15 +00:00
}
2018-10-01 09:34:38 +00:00
// http://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art011
// https://www.w3.org/Graphics/GIF/spec-gif89a.txt
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public static boolean isGif (byte[] buffer)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
if (buffer.length < 6)
return false;
String text = new String (buffer, 0, 6);
return text.equals ("GIF89a") || text.equals ("GIF87a");
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
public static boolean isPng (byte[] buffer)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2016-12-31 09:34:15 +00:00
{
if (buffer.length < pngHeader.length)
return false;
for (int i = 0; i < pngHeader.length; i++)
if (pngHeader[i] != buffer[i])
return false;
return true;
}
2017-01-12 22:11:05 +00:00
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2019-08-09 23:45:49 +00:00
public static boolean isTiff (byte[] buffer)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2019-08-09 23:45:49 +00:00
{
if (buffer.length < 3)
return false;
String text = new String (buffer, 0, 2);
if (!"II".equals (text) && !"MM".equals (text))
return false;
if (buffer[2] != 0x2A)
return false;
return true;
}
2017-02-01 21:15:30 +00:00
// http://www.daubnet.com/en/file-format-bmp
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 05:26:37 +00:00
public static boolean isBmp (byte[] buffer)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 05:26:37 +00:00
{
2017-02-01 21:15:30 +00:00
if (buffer.length < 26)
2017-01-25 05:26:37 +00:00
return false;
String text = new String (buffer, 0, 2);
int size = Utility.getLong (buffer, 2);
2017-02-01 21:15:30 +00:00
if (false)
{
int empty = Utility.getLong (buffer, 6);
int offset = Utility.getLong (buffer, 10);
int header = Utility.getLong (buffer, 14);
int width = Utility.getLong (buffer, 18);
int height = Utility.getLong (buffer, 22);
2017-02-01 21:15:30 +00:00
System.out.println (buffer.length);
System.out.println (size);
System.out.println (empty);
System.out.println (offset);
System.out.println (header);
System.out.println (width);
System.out.println (height);
}
return text.equals ("BM") && size <= buffer.length;
2017-01-25 05:26:37 +00:00
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2018-08-02 22:13:49 +00:00
public static boolean isAPP (byte[] buffer)
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2018-08-02 22:13:49 +00:00
{
2018-08-17 11:46:47 +00:00
if (buffer.length < 4)
return false;
2019-11-28 04:36:49 +00:00
2024-03-12 07:37:13 +00:00
return buffer[0] == (byte) 0xC1 && buffer[1] == (byte) 0xD0
&& buffer[2] == (byte) 0xD0 && buffer[3] == 0;
2018-08-02 22:13:49 +00:00
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-14 01:40:58 +00:00
public static PaletteFactory getPaletteFactory ()
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-12 22:11:05 +00:00
{
2017-01-14 01:40:58 +00:00
return paletteFactory;
2017-01-12 22:11:05 +00:00
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-14 01:40:58 +00:00
public static List<Palette> getPalettes ()
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-12 22:11:05 +00:00
{
2017-01-14 01:40:58 +00:00
return paletteFactory.getPalettes ();
2017-01-12 22:11:05 +00:00
}
2017-01-26 04:19:23 +00:00
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
class ColorTable
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
2019-11-22 10:17:58 +00:00
private int id;
2017-01-26 04:19:23 +00:00
ColorEntry[] entries = new ColorEntry[16];
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2020-03-26 09:55:29 +00:00
public ColorTable (int id, int mode)
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
// default empty table
2020-03-26 09:55:29 +00:00
this.id = id;
if ((mode & 0x80) == 0)
{
entries[0] = new ColorEntry (0x00, 0x00, 0x00);
entries[1] = new ColorEntry (0x07, 0x07, 0x07);
entries[2] = new ColorEntry (0x08, 0x04, 0x01);
entries[3] = new ColorEntry (0x07, 0x02, 0x0C);
entries[4] = new ColorEntry (0x00, 0x00, 0x0F);
entries[5] = new ColorEntry (0x00, 0x08, 0x00);
entries[6] = new ColorEntry (0x0F, 0x07, 0x00);
entries[7] = new ColorEntry (0x0D, 0x00, 0x00);
entries[8] = new ColorEntry (0x0F, 0x0A, 0x09);
entries[9] = new ColorEntry (0x0F, 0x0F, 0x00);
entries[10] = new ColorEntry (0x00, 0x0E, 0x00);
entries[11] = new ColorEntry (0x04, 0x0D, 0x0F);
entries[12] = new ColorEntry (0x0D, 0x0A, 0x0F);
entries[13] = new ColorEntry (0x07, 0x08, 0x0F);
entries[14] = new ColorEntry (0x0C, 0x0C, 0x0C);
entries[15] = new ColorEntry (0x0F, 0x0F, 0x0F);
}
else
{
entries[0] = new ColorEntry (0x00, 0x00, 0x00);
entries[1] = new ColorEntry (0x00, 0x00, 0x0F);
entries[2] = new ColorEntry (0x0F, 0x0F, 0x00);
entries[3] = new ColorEntry (0x0F, 0x0F, 0x0F);
entries[4] = new ColorEntry (0x00, 0x00, 0x00);
entries[5] = new ColorEntry (0x0D, 0x00, 0x00);
entries[6] = new ColorEntry (0x00, 0x0E, 0x00);
entries[7] = new ColorEntry (0x0F, 0x0F, 0x0F);
entries[0] = new ColorEntry (0x00, 0x00, 0x00);
entries[1] = new ColorEntry (0x00, 0x00, 0x0F);
entries[2] = new ColorEntry (0x0F, 0x0F, 0x00);
entries[3] = new ColorEntry (0x0F, 0x0F, 0x0F);
entries[4] = new ColorEntry (0x00, 0x00, 0x00);
entries[5] = new ColorEntry (0x0D, 0x00, 0x00);
entries[6] = new ColorEntry (0x00, 0x0E, 0x00);
entries[7] = new ColorEntry (0x0F, 0x0F, 0x0F);
}
2017-01-26 04:19:23 +00:00
}
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
public ColorTable (int id, byte[] data, int offset)
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
this.id = id;
for (int i = 0; i < 16; i++)
{
entries[i] = new ColorEntry (data, offset);
offset += 2;
}
}
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
String toLine ()
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
StringBuilder text = new StringBuilder ();
2017-02-01 21:15:30 +00:00
text.append (String.format ("%02X", id));
2017-01-26 04:19:23 +00:00
for (int i = 0; i < 16; i++)
text.append (String.format (" %04X", entries[i].value));
return text.toString ();
}
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
void reverse ()
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
for (int i = 0; i < 8; i++)
{
ColorEntry temp = entries[i];
entries[i] = entries[15 - i];
entries[15 - i] = temp;
}
}
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
@Override
public String toString ()
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
StringBuilder text = new StringBuilder ();
2020-03-26 09:55:29 +00:00
text.append (String.format ("%3d ColorTable%n", id));
2017-01-26 04:19:23 +00:00
for (int i = 0; i < 8; i++)
text.append (String.format (" %2d: %04X", i, entries[i].value));
text.append ("\n");
for (int i = 8; i < 16; i++)
text.append (String.format (" %2d: %04X", i, entries[i].value));
return text.toString ();
}
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
class ColorEntry
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
int value; // 0RGB
Color color;
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2020-03-26 09:55:29 +00:00
public ColorEntry (int red, int green, int blue)
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
2020-03-26 09:55:29 +00:00
value = (red << 8) | (green << 4) | blue;
color = new Color (red, green, blue);
2017-01-26 04:19:23 +00:00
}
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
public ColorEntry (byte[] data, int offset)
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
value = Utility.getShort (data, offset);
2017-01-26 04:19:23 +00:00
int red = ((value >> 8) & 0x0f) * 17;
int green = ((value >> 4) & 0x0f) * 17;
int blue = (value & 0x0f) * 17;
2017-01-27 07:11:00 +00:00
2017-01-26 04:19:23 +00:00
color = new Color (red, green, blue);
}
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
@Override
public String toString ()
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
return String.format ("ColorEntry: %04X", value);
}
}
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
class DirEntry
2019-11-21 08:13:41 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
int numBytes;
int mode;
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
public DirEntry (byte[] data, int offset)
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
numBytes = Utility.getShort (data, offset);
mode = Utility.getShort (data, offset + 2);
2017-01-26 04:19:23 +00:00
}
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
@Override
public String toString ()
2019-11-21 08:13:41 +00:00
// -------------------------------------------------------------------------------//
2017-01-26 04:19:23 +00:00
{
return String.format ("Bytes: %5d, mode: %02X", numBytes, mode);
}
}
2015-06-01 09:35:51 +00:00
}