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

479 lines
15 KiB
Java
Raw Normal View History

2017-01-24 08:59:40 +00:00
package com.bytezone.diskbrowser.applefile;
2017-01-25 01:38:00 +00:00
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
2017-01-24 08:59:40 +00:00
import java.util.ArrayList;
import java.util.List;
import com.bytezone.diskbrowser.utilities.HexFormatter;
import com.bytezone.diskbrowser.utilities.Utility;
2017-01-24 08:59:40 +00:00
2019-11-19 05:25:10 +00:00
// -----------------------------------------------------------------------------------//
2018-07-25 05:48:14 +00:00
public class SHRPictureFile1 extends HiResImage
2019-11-19 05:25:10 +00:00
// -----------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
2020-02-02 10:17:49 +00:00
private final List<Block> blocks = new ArrayList<> ();
2017-01-26 11:30:16 +00:00
private Main mainBlock;
private Multipal multipalBlock;
2018-08-02 22:13:49 +00:00
private final boolean debug = false;
2017-01-24 08:59:40 +00:00
2019-11-19 05:25:10 +00:00
// PNT - 0xC0/02 - Apple IIGS Super Hi-Res Picture File (APF)
// ---------------------------------------------------------------------------------//
2018-07-25 05:48:14 +00:00
public SHRPictureFile1 (String name, byte[] buffer, int fileType, int auxType, int eof)
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
2017-01-25 11:02:50 +00:00
super (name, buffer, fileType, auxType, eof);
2017-01-24 08:59:40 +00:00
int ptr = 0;
while (ptr < buffer.length)
{
int len = Utility.getLong (buffer, ptr);
2019-11-20 10:25:16 +00:00
if (len == 0 || len > buffer.length)
{
System.out.printf ("Block length: %d%n", len);
2017-01-26 23:59:47 +00:00
break;
2019-11-20 10:25:16 +00:00
}
2018-07-26 03:43:07 +00:00
2017-01-24 08:59:40 +00:00
String kind = HexFormatter.getPascalString (buffer, ptr + 4);
2017-01-26 11:30:16 +00:00
byte[] data = new byte[Math.min (len, buffer.length - ptr)];
2017-01-24 08:59:40 +00:00
System.arraycopy (buffer, ptr, data, 0, data.length);
2018-07-26 03:43:07 +00:00
switch (kind)
2017-01-26 11:30:16 +00:00
{
2018-07-26 03:43:07 +00:00
case "MAIN":
mainBlock = new Main (kind, data);
blocks.add (mainBlock);
break;
case "MULTIPAL":
multipalBlock = new Multipal (kind, data);
blocks.add (multipalBlock);
break;
2018-07-28 12:00:02 +00:00
case "PALETTES":
case "MASK":
case "PATS":
2018-07-31 07:02:19 +00:00
case "SCIB":
2018-08-02 22:13:49 +00:00
if (debug)
System.out.println (kind + " not written");
2018-07-28 12:00:02 +00:00
blocks.add (new Block (kind, data));
break;
2018-07-31 07:02:19 +00:00
case "NOTE": // Convert 3200
case "SuperConvert":
case "EOA ": // DeluxePaint
case "Platinum Paint":
2018-08-07 08:40:06 +00:00
case "VSDV":
case "VSMK":
2019-11-22 10:17:58 +00:00
case "816/Paint":
case "SHRConvert":
2018-07-28 12:00:02 +00:00
blocks.add (new Block (kind, data));
break;
2019-11-23 11:00:15 +00:00
case "Nseq":
blocks.add (new Nseq (kind, data));
break;
2018-07-26 03:43:07 +00:00
default:
blocks.add (new Block (kind, data));
System.out.println ("Unknown block type: " + kind + " in " + name);
break;
2017-01-26 11:30:16 +00:00
}
2017-01-24 08:59:40 +00:00
ptr += len;
}
2019-11-23 11:00:15 +00:00
2017-01-25 01:38:00 +00:00
createImage ();
2017-01-24 08:59:40 +00:00
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
@Override
2018-07-30 08:44:29 +00:00
void createMonochromeImage ()
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
2017-01-26 04:19:23 +00:00
image = new BufferedImage (320, 200, BufferedImage.TYPE_BYTE_GRAY);
DataBuffer db = image.getRaster ().getDataBuffer ();
int element = 0;
int ptr = 0;
for (int row = 0; row < 200; row++)
for (int col = 0; col < 160; col++)
{
2018-08-02 05:13:09 +00:00
int pix1 = (buffer[ptr] & 0xF0) >> 4;
int pix2 = buffer[ptr] & 0x0F;
2017-01-26 04:19:23 +00:00
if (pix1 > 0)
db.setElem (element, 255);
if (pix2 > 0)
db.setElem (element + 1, 255);
element += 2;
ptr++;
}
2017-01-24 08:59:40 +00:00
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
@Override
2018-07-30 08:44:29 +00:00
void createColourImage ()
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
2019-11-20 10:25:16 +00:00
if (mainBlock == null)
2019-11-23 11:00:15 +00:00
{
System.out.println ("No MAIN block in image file");
2019-11-20 10:25:16 +00:00
return;
2019-11-23 11:00:15 +00:00
}
2019-11-20 10:25:16 +00:00
2020-03-26 09:55:29 +00:00
boolean mode320 = (mainBlock.masterMode & 0x80) == 0;
2020-03-30 04:57:07 +00:00
int imageWidth = mainBlock.pixelsPerScanLine;
if (mode320)
2020-03-31 11:00:40 +00:00
imageWidth *= 2; // every horizontal pixel is drawn twice
2020-03-26 09:55:29 +00:00
2019-11-20 10:25:16 +00:00
image = new BufferedImage (imageWidth, mainBlock.numScanLines * 2,
BufferedImage.TYPE_INT_RGB);
2017-01-25 01:38:00 +00:00
DataBuffer dataBuffer = image.getRaster ().getDataBuffer ();
2019-11-20 10:25:16 +00:00
int element = 0;
int ptr = 0;
2018-07-31 07:02:19 +00:00
for (int line = 0; line < mainBlock.numScanLines; line++)
2017-01-25 01:38:00 +00:00
{
2018-07-31 07:02:19 +00:00
DirEntry dirEntry = mainBlock.scanLineDirectory[line];
2018-07-30 08:44:29 +00:00
int hi = dirEntry.mode & 0xFF00; // always 0
int lo = dirEntry.mode & 0x00FF; // mode bit if hi == 0
2020-03-26 09:55:29 +00:00
boolean fillMode = (dirEntry.mode & 0x20) != 0;
2021-09-16 08:24:13 +00:00
// assert fillMode == false;
2020-03-26 09:55:29 +00:00
2018-07-30 08:44:29 +00:00
if (hi != 0)
System.out.println ("hi not zero");
2017-01-25 04:40:15 +00:00
2020-03-26 09:55:29 +00:00
ColorTable colorTable = //
multipalBlock != null ? multipalBlock.colorTables[line]
: mainBlock.colorTables[lo & 0x0F];
2017-01-25 01:38:00 +00:00
2020-03-31 11:00:40 +00:00
int dataWidth = mainBlock.pixelsPerScanLine / (mode320 ? 2 : 4);
2017-01-25 01:38:00 +00:00
2020-03-31 11:00:40 +00:00
if (mode320) // two pixels per byte, each shown twice
ptr = mode320Line (ptr, element, dataWidth, colorTable, dataBuffer, imageWidth);
2020-03-26 09:55:29 +00:00
else // four pixels per byte
2020-03-31 11:00:40 +00:00
ptr = mode640Line (ptr, element, dataWidth, colorTable, dataBuffer, imageWidth);
2019-11-20 10:25:16 +00:00
2019-11-23 11:00:15 +00:00
element += imageWidth * 2; // drawing two lines at a time
2017-01-25 01:38:00 +00:00
}
2017-01-24 08:59:40 +00:00
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
@Override
public String getText ()
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
StringBuilder text = new StringBuilder (super.getText ());
2019-11-23 11:00:15 +00:00
if (mainBlock == null)
text.append ("\nFailure : No MAIN block\n");
2018-08-07 08:40:06 +00:00
text.append ("\n\n");
2017-01-24 08:59:40 +00:00
for (Block block : blocks)
{
text.append (block);
text.append ("\n\n");
}
2019-11-23 11:00:15 +00:00
text.deleteCharAt (text.length () - 1);
text.deleteCharAt (text.length () - 1);
2017-01-24 08:59:40 +00:00
return text.toString ();
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 11:30:16 +00:00
private class Block
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
String kind;
byte[] data;
2019-11-23 11:00:15 +00:00
int size;
2017-01-24 08:59:40 +00:00
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
public Block (String kind, byte[] data)
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
this.kind = kind;
this.data = data;
2019-11-23 11:00:15 +00:00
size = Utility.getLong (data, 0);
2017-01-24 08:59:40 +00:00
}
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
@Override
public String toString ()
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
StringBuilder text = new StringBuilder ();
2019-11-23 11:00:15 +00:00
text.append (String.format ("Block ..... %s%n", kind));
text.append (String.format ("Size ...... %04X %<d%n%n", size));
int headerSize = 5 + kind.length ();
text.append (HexFormatter.format (data, headerSize, data.length - headerSize));
2017-01-24 08:59:40 +00:00
return text.toString ();
}
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 11:30:16 +00:00
private class Multipal extends Block
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-25 04:40:15 +00:00
{
2020-03-26 09:55:29 +00:00
int numColorTables;
2017-01-25 04:40:15 +00:00
ColorTable[] colorTables;
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2017-01-25 04:40:15 +00:00
public Multipal (String kind, byte[] data)
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2017-01-25 04:40:15 +00:00
{
super (kind, data);
int ptr = 5 + kind.length ();
numColorTables = Utility.getShort (data, ptr);
2017-01-25 04:40:15 +00:00
ptr += 2;
2020-03-26 09:55:29 +00:00
colorTables = new ColorTable[numColorTables];
for (int i = 0; i < numColorTables; i++)
2017-01-25 04:40:15 +00:00
{
if (ptr < data.length - 32)
colorTables[i] = new ColorTable (i, data, ptr);
else
2021-09-16 08:24:13 +00:00
colorTables[i] = new ColorTable (i, 0x00); // default empty table !! not
2023-09-15 20:51:31 +00:00
// finished
2017-01-25 04:40:15 +00:00
ptr += 32;
}
}
2020-03-26 09:55:29 +00:00
// ---------------------------------------------------------------------------------//
@Override
public String toString ()
// ---------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Kind ................. %s%n", kind));
text.append (String.format ("NumColorTables ....... %d%n%n", numColorTables));
for (int line = 0; line < numColorTables; line++)
{
text.append (colorTables[line]);
text.append ("\n\n");
}
return text.toString ();
}
2017-01-25 04:40:15 +00:00
}
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-26 11:30:16 +00:00
private class Main extends Block
2019-11-19 05:25:10 +00:00
// ---------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
2018-07-25 05:48:14 +00:00
int masterMode; // 0 = Brooks, 0 = PNT 320 80 = PNT 640
2020-04-03 07:38:14 +00:00
int pixelsPerScanLine; // image width in pixels
2018-07-30 08:44:29 +00:00
int numColorTables; // 1 = Brooks, 16 = Other (may be zero)
ColorTable[] colorTables; // [numColorTables]
2020-04-03 07:38:14 +00:00
int numScanLines; // image height in pixels
2018-07-30 08:44:29 +00:00
DirEntry[] scanLineDirectory; // [numScanLines]
2017-01-24 08:59:40 +00:00
byte[][] packedScanLines;
2020-03-29 03:07:59 +00:00
boolean mode640;
2020-03-31 11:00:40 +00:00
int dataWidth;
2019-11-19 05:25:10 +00:00
2017-01-24 08:59:40 +00:00
public Main (String kind, byte[] data)
{
super (kind, data);
int ptr = 5 + kind.length ();
masterMode = Utility.getShort (data, ptr);
pixelsPerScanLine = Utility.getShort (data, ptr + 2);
numColorTables = Utility.getShort (data, ptr + 4);
2020-03-29 03:07:59 +00:00
mode640 = (masterMode & 0x80) != 0;
2017-01-24 08:59:40 +00:00
ptr += 6;
colorTables = new ColorTable[numColorTables];
for (int i = 0; i < numColorTables; i++)
{
colorTables[i] = new ColorTable (i, data, ptr);
ptr += 32;
}
numScanLines = Utility.getShort (data, ptr);
2018-08-02 22:13:49 +00:00
ptr += 2;
2017-01-24 08:59:40 +00:00
scanLineDirectory = new DirEntry[numScanLines];
packedScanLines = new byte[numScanLines][];
2020-03-26 09:55:29 +00:00
for (int line = 0; line < numScanLines; line++)
2017-01-24 08:59:40 +00:00
{
DirEntry dirEntry = new DirEntry (data, ptr);
2020-03-26 09:55:29 +00:00
scanLineDirectory[line] = dirEntry;
packedScanLines[line] = new byte[dirEntry.numBytes];
2017-01-24 08:59:40 +00:00
ptr += 4;
}
2020-03-26 09:55:29 +00:00
for (int line = 0; line < numScanLines; line++)
2017-01-24 08:59:40 +00:00
{
2020-03-26 09:55:29 +00:00
int numBytes = scanLineDirectory[line].numBytes;
if (ptr + numBytes > data.length)
2019-11-19 05:25:10 +00:00
{
System.out.println ("breaking early");
2017-01-24 08:59:40 +00:00
break;
2019-11-19 05:25:10 +00:00
}
2017-01-24 08:59:40 +00:00
2020-03-26 09:55:29 +00:00
System.arraycopy (data, ptr, packedScanLines[line], 0, numBytes);
ptr += numBytes;
2017-01-24 08:59:40 +00:00
}
2017-01-24 21:53:34 +00:00
2020-03-31 11:00:40 +00:00
dataWidth = pixelsPerScanLine / (mode640 ? 4 : 2);
2020-03-29 03:07:59 +00:00
2020-03-31 11:00:40 +00:00
byte[] unpackedBuffer = new byte[numScanLines * dataWidth];
2018-08-02 05:13:09 +00:00
ptr = 0;
for (int line = 0; line < numScanLines; line++)
2017-01-24 21:53:34 +00:00
{
2021-09-16 08:24:13 +00:00
// if (isOddAndEmpty (packedScanLines[line]))
// {
// System.out.println ("Odd number of bytes in empty buffer in " + name);
// break;
// }
2019-08-21 00:31:11 +00:00
2020-04-03 07:38:14 +00:00
int bytesUnpacked = unpack (packedScanLines[line], 0,
2020-04-01 05:47:36 +00:00
packedScanLines[line].length, unpackedBuffer, ptr);
2020-03-31 11:00:40 +00:00
2020-04-03 07:38:14 +00:00
if (bytesUnpacked != dataWidth && false)
2020-03-31 11:00:40 +00:00
System.out.printf ("Unexpected line width %3d %5d %3d %3d%n", line, ptr,
bytesUnpacked, dataWidth);
ptr += dataWidth;
2017-01-24 21:53:34 +00:00
}
2018-08-02 22:13:49 +00:00
2018-08-02 05:13:09 +00:00
SHRPictureFile1.this.buffer = unpackedBuffer;
2017-01-24 21:53:34 +00:00
}
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2021-09-16 08:24:13 +00:00
// private boolean isOddAndEmpty (byte[] buffer)
// //
// -------------------------------------------------------------------------------//
// {
// if (buffer.length % 2 == 0)
// return false;
// for (byte b : buffer)
// if (b != 0)
// return false;
// return true;
// }
2017-01-24 08:59:40 +00:00
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
@Override
public String toString ()
2019-11-23 11:00:15 +00:00
// -------------------------------------------------------------------------------//
2017-01-24 08:59:40 +00:00
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Kind ................. %s%n", kind));
text.append (String.format ("MasterMode ........... %04X%n", masterMode));
2020-03-31 11:00:40 +00:00
text.append (String.format ("PixelsPerScanLine .... %d / %d = %d bytes%n",
pixelsPerScanLine, (mode640 ? 4 : 2), dataWidth));
2017-01-24 08:59:40 +00:00
text.append (String.format ("NumColorTables ....... %d%n", numColorTables));
text.append (String.format ("NumScanLines ......... %d%n%n", numScanLines));
text.append ("Color Tables\n");
text.append ("------------\n\n");
text.append (" # ");
for (int i = 0; i < 16; i++)
text.append (String.format (" %02X ", i));
text.deleteCharAt (text.length () - 1);
text.deleteCharAt (text.length () - 1);
text.append ("\n---");
for (int i = 0; i < 16; i++)
text.append (" ---- ");
text.deleteCharAt (text.length () - 1);
text.append ("\n");
2017-01-25 11:02:50 +00:00
2017-01-24 08:59:40 +00:00
for (ColorTable colorTable : colorTables)
{
text.append (colorTable.toLine ());
text.append ("\n");
}
text.append ("\nScan Lines\n");
text.append ("----------\n\n");
2020-03-29 03:07:59 +00:00
text.append (" # Mode Len Packed Data\n");
2017-01-24 21:53:34 +00:00
text.append ("--- ---- --- ---------------------------------------");
2020-03-29 03:07:59 +00:00
text.append ("--------------------------------\n");
2017-01-24 21:53:34 +00:00
int lineSize = 24;
2017-01-24 08:59:40 +00:00
for (int i = 0; i < scanLineDirectory.length; i++)
{
DirEntry dirEntry = scanLineDirectory[i];
byte[] packedScanLine = packedScanLines[i];
2017-01-25 11:02:50 +00:00
text.append (
2020-03-26 09:55:29 +00:00
String.format ("%3d %04X %3d ", i, dirEntry.mode, packedScanLine.length));
2017-01-24 21:53:34 +00:00
int ptr = 0;
while (true)
{
2019-11-19 06:48:27 +00:00
String hex = HexFormatter.getHexString (packedScanLine, ptr, lineSize);
text.append (hex);
if (ptr == 0)
{
if (hex.length () < 71)
text.append ((" "
+ " ").substring (hex.length ()));
}
2017-01-24 21:53:34 +00:00
ptr += lineSize;
if (ptr >= packedScanLine.length)
break;
text.append ("\n ");
}
2017-01-24 08:59:40 +00:00
text.append ("\n");
2020-04-08 02:17:08 +00:00
if (true)
{
text.append ("\n");
text.append (debug (packedScanLine, 0, packedScanLine.length));
text.append ("\n");
}
2017-01-24 08:59:40 +00:00
}
return text.toString ();
}
}
2019-11-23 11:00:15 +00:00
// ---------------------------------------------------------------------------------//
private class Nseq extends Block
// ---------------------------------------------------------------------------------//
{
// -------------------------------------------------------------------------------//
public Nseq (String kind, byte[] data)
// -------------------------------------------------------------------------------//
{
super (kind, data);
}
// -------------------------------------------------------------------------------//
@Override
public String toString ()
// -------------------------------------------------------------------------------//
{
StringBuilder text = new StringBuilder ();
text.append (String.format ("Block ..... %s%n", kind));
text.append (String.format ("Size ...... %04X %<d%n%n", size));
int ptr = 5 + kind.length ();
while (ptr < data.length)
{
text.append (HexFormatter.format (data, ptr, 4) + "\n");
2019-11-23 11:00:15 +00:00
ptr += 4;
}
text.deleteCharAt (text.length () - 1);
return text.toString ();
}
}
2017-01-24 08:59:40 +00:00
}