mirror of
https://github.com/fadden/6502bench.git
synced 2025-02-08 05:30:35 +00:00
Implement Apple II hi-res bitmap font and screen image visualizers
Bitmap fonts are a series of (usually) 1x8 bitmaps, which we arrange into a grid of cells. Screen images are useful for embedded screens, or for people who want to display stand-alone image files as disassembly projects.
This commit is contained in:
parent
df04de61e6
commit
4ef1d7e7c7
@ -271,16 +271,34 @@ namespace PluginCommon {
|
||||
/// without scaling or filtering.
|
||||
/// </summary>
|
||||
public interface IVisualization2d {
|
||||
/// <summary>
|
||||
/// Bitmap width, in pixels.
|
||||
/// </summary>
|
||||
int Width { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Bitmap height, in pixels.
|
||||
/// </summary>
|
||||
int Height { get; }
|
||||
|
||||
void SetPixelIndex(int x, int y, byte colorIndex);
|
||||
int GetPixel(int x, int y); // returns ARGB value
|
||||
//void SetPixelIndex(int x, int y, byte colorIndex);
|
||||
//int GetPixel(int x, int y); // returns ARGB value
|
||||
|
||||
byte[] GetPixels(); // densely-packed index or ARGB values
|
||||
int[] GetPalette(); // 32-bit ARGB values; null for direct-color image
|
||||
/// <summary>
|
||||
/// Returns a densely-packed array of color indices or ARGB values.
|
||||
/// Do not modify.
|
||||
/// </summary>
|
||||
byte[] GetPixels();
|
||||
|
||||
// TODO(maybe): pixel aspect ratio
|
||||
/// <summary>
|
||||
/// Returns the color palette as a series of 32-bit ARGB values. Will be null for
|
||||
/// direct-color images.
|
||||
/// Do not modify.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
int[] GetPalette();
|
||||
|
||||
// TODO(maybe): report pixel aspect ratio?
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -26,13 +26,22 @@ namespace PluginCommon {
|
||||
public class VisBitmap8 : IVisualization2d {
|
||||
public const int MAX_DIMENSION = 4096;
|
||||
|
||||
// IVisualization2d
|
||||
public int Width { get; private set; }
|
||||
|
||||
// IVisualization2d
|
||||
public int Height { get; private set; }
|
||||
|
||||
private byte[] mData;
|
||||
private int[] mPalette;
|
||||
private int mNextColor;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="width">Bitmap width, in pixels.</param>
|
||||
/// <param name="height">Bitmap height, in pixels.</param>
|
||||
public VisBitmap8(int width, int height) {
|
||||
Debug.Assert(width > 0 && width <= MAX_DIMENSION);
|
||||
Debug.Assert(height > 0 && height <= MAX_DIMENSION);
|
||||
@ -45,7 +54,6 @@ namespace PluginCommon {
|
||||
mNextColor = 0;
|
||||
}
|
||||
|
||||
// IVisualization2d
|
||||
public int GetPixel(int x, int y) {
|
||||
byte pix = mData[x + y * Width];
|
||||
return mPalette[pix];
|
||||
@ -77,6 +85,12 @@ namespace PluginCommon {
|
||||
return pal;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new color to the palette. If the color already exists, the call has no
|
||||
/// effect.
|
||||
/// </summary>
|
||||
/// <param name="color">32-bit ARGB color value.</param>
|
||||
public void AddColor(int color) {
|
||||
if (mNextColor == 256) {
|
||||
Debug.WriteLine("Palette is full");
|
||||
@ -92,6 +106,14 @@ namespace PluginCommon {
|
||||
mPalette[mNextColor++] = color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new color to the palette. If the color already exists, the call has no
|
||||
/// effect.
|
||||
/// </summary>
|
||||
/// <param name="a">Alpha value.</param>
|
||||
/// <param name="r">Red value.</param>
|
||||
/// <param name="g">Green value.</param>
|
||||
/// <param name="b">Blue value.</param>
|
||||
public void AddColor(byte a, byte r, byte g, byte b) {
|
||||
AddColor(Util.MakeARGB(a, r, g, b));
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ namespace RuntimeData.Apple {
|
||||
// Visualization identifiers; DO NOT change or projects that use them will break.
|
||||
private const string VIS_GEN_BITMAP = "apple2-hi-res-bitmap";
|
||||
private const string VIS_GEN_BITMAP_FONT = "apple2-hi-res-bitmap-font";
|
||||
private const string VIS_GEN_HR_SCREEN = "apple2-hi-res-screen";
|
||||
|
||||
private const string P_OFFSET = "offset";
|
||||
private const string P_BYTE_WIDTH = "byteWidth";
|
||||
@ -78,7 +79,14 @@ namespace RuntimeData.Apple {
|
||||
new VisParamDescr("Item height",
|
||||
P_ITEM_HEIGHT, typeof(int), 1, 192, 0, 8),
|
||||
new VisParamDescr("Number of items",
|
||||
P_COUNT, typeof(int), 1, 256, 0, 1),
|
||||
P_COUNT, typeof(int), 1, 256, 0, 96),
|
||||
}),
|
||||
new VisDescr(VIS_GEN_HR_SCREEN, "Apple II Hi-Res Screen Image", VisDescr.VisType.Bitmap,
|
||||
new VisParamDescr[] {
|
||||
new VisParamDescr("File offset (hex)",
|
||||
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
|
||||
new VisParamDescr("Color",
|
||||
P_IS_COLOR, typeof(bool), 0, 0, 0, true),
|
||||
}),
|
||||
};
|
||||
|
||||
@ -109,8 +117,9 @@ namespace RuntimeData.Apple {
|
||||
case VIS_GEN_BITMAP:
|
||||
return GenerateBitmap(parms);
|
||||
case VIS_GEN_BITMAP_FONT:
|
||||
// TODO (xyzzy)
|
||||
return null;
|
||||
return GenerateBitmapFont(parms);
|
||||
case VIS_GEN_HR_SCREEN:
|
||||
return GenerateScreen(parms);
|
||||
default:
|
||||
mAppRef.DebugLog("Unknown ident " + descr.Ident);
|
||||
return null;
|
||||
@ -118,16 +127,13 @@ namespace RuntimeData.Apple {
|
||||
}
|
||||
|
||||
private IVisualization2d GenerateBitmap(ReadOnlyDictionary<string, object> parms) {
|
||||
int offset, byteWidth, height, colStride, rowStride;
|
||||
bool isColor, isFirstOdd;
|
||||
|
||||
offset = Util.GetFromObjDict(parms, P_OFFSET, 0);
|
||||
byteWidth = Util.GetFromObjDict(parms, P_BYTE_WIDTH, 1); // width ignoring colStride
|
||||
height = Util.GetFromObjDict(parms, P_HEIGHT, 1);
|
||||
colStride = Util.GetFromObjDict(parms, P_COL_STRIDE, 0);
|
||||
rowStride = Util.GetFromObjDict(parms, P_ROW_STRIDE, 0);
|
||||
isColor = Util.GetFromObjDict(parms, P_IS_COLOR, true);
|
||||
isFirstOdd = Util.GetFromObjDict(parms, P_IS_FIRST_ODD, false);
|
||||
int offset = Util.GetFromObjDict(parms, P_OFFSET, 0);
|
||||
int byteWidth = Util.GetFromObjDict(parms, P_BYTE_WIDTH, 1); // width ignoring colStride
|
||||
int height = Util.GetFromObjDict(parms, P_HEIGHT, 1);
|
||||
int colStride = Util.GetFromObjDict(parms, P_COL_STRIDE, 0);
|
||||
int rowStride = Util.GetFromObjDict(parms, P_ROW_STRIDE, 0);
|
||||
bool isColor = Util.GetFromObjDict(parms, P_IS_COLOR, true);
|
||||
bool isFirstOdd = Util.GetFromObjDict(parms, P_IS_FIRST_ODD, false);
|
||||
|
||||
// We allow the stride entries to be zero to indicate a "dense" bitmap.
|
||||
if (colStride == 0) {
|
||||
@ -160,21 +166,129 @@ namespace RuntimeData.Apple {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
VisBitmap8 vb = new VisBitmap8(byteWidth * 7, height);
|
||||
SetHiResPalette(vb);
|
||||
|
||||
RenderBitmap(offset, byteWidth, height, colStride, rowStride,
|
||||
isColor ? ColorMode.SimpleColor : ColorMode.Mono, isFirstOdd, vb);
|
||||
RenderBitmap(mFileData, offset, byteWidth, height, colStride, rowStride,
|
||||
isColor ? ColorMode.SimpleColor : ColorMode.Mono, isFirstOdd,
|
||||
vb, 0, 0);
|
||||
return vb;
|
||||
}
|
||||
|
||||
private IVisualization2d GenerateBitmapFont(ReadOnlyDictionary<string, object> parms) {
|
||||
int offset = Util.GetFromObjDict(parms, P_OFFSET, 0);
|
||||
int itemByteWidth = Util.GetFromObjDict(parms, P_ITEM_BYTE_WIDTH, 1);
|
||||
int itemHeight = Util.GetFromObjDict(parms, P_ITEM_HEIGHT, 8);
|
||||
int count = Util.GetFromObjDict(parms, P_COUNT, 96);
|
||||
|
||||
if (offset < 0 || offset >= mFileData.Length ||
|
||||
itemByteWidth <= 0 || itemByteWidth > MAX_DIM ||
|
||||
itemHeight <= 0 || itemHeight > MAX_DIM) {
|
||||
// should be caught by editor
|
||||
mAppRef.DebugLog("Invalid parameter");
|
||||
return null;
|
||||
}
|
||||
|
||||
int lastOffset = offset + itemByteWidth * itemHeight - 1;
|
||||
if (lastOffset >= mFileData.Length) {
|
||||
mAppRef.DebugLog("Bitmap runs off end of file (last offset +" +
|
||||
lastOffset.ToString("x6") + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set the number of horizontal cells to 16 or 32 based on the number of elements.
|
||||
int hcells;
|
||||
if (count > 128) {
|
||||
hcells = 32;
|
||||
} else {
|
||||
hcells = 16;
|
||||
}
|
||||
|
||||
int vcells = (count + hcells - 1) / hcells;
|
||||
|
||||
// Create a bitmap with room for each cell, plus a 1-pixel transparent boundary
|
||||
// between them and around the edges.
|
||||
VisBitmap8 vb = new VisBitmap8(1 + hcells * itemByteWidth * 7 + hcells,
|
||||
1 + vcells * itemHeight + vcells);
|
||||
SetHiResPalette(vb);
|
||||
|
||||
int cellx = 1;
|
||||
int celly = 1;
|
||||
|
||||
for (int idx = 0; idx < count; idx++) {
|
||||
|
||||
//byte color = (byte)(1 + idx % 6);
|
||||
//for (int y = 0; y < itemHeight; y++) {
|
||||
// for (int x = 0; x < itemByteWidth * 7; x++) {
|
||||
// vb.SetPixelIndex(cellx + x, celly + y, color);
|
||||
// }
|
||||
//}
|
||||
RenderBitmap(mFileData, offset + idx * itemByteWidth * itemHeight,
|
||||
itemByteWidth, itemHeight, 1, itemByteWidth,
|
||||
ColorMode.Mono, false,
|
||||
vb, cellx, celly);
|
||||
|
||||
cellx += itemByteWidth * 7 + 1;
|
||||
if (cellx == vb.Width) {
|
||||
cellx = 1;
|
||||
celly += itemHeight + 1;
|
||||
}
|
||||
}
|
||||
return vb;
|
||||
}
|
||||
|
||||
private IVisualization2d GenerateScreen(ReadOnlyDictionary<string, object> parms) {
|
||||
const int RAW_IMAGE_SIZE = 0x1ff8;
|
||||
const int HR_WIDTH = 280;
|
||||
const int HR_BYTE_WIDTH = HR_WIDTH / 7;
|
||||
const int HR_HEIGHT = 192;
|
||||
|
||||
int offset = Util.GetFromObjDict(parms, P_OFFSET, 0);
|
||||
bool isColor = Util.GetFromObjDict(parms, P_IS_COLOR, true);
|
||||
|
||||
if (offset < 0 || offset >= mFileData.Length) {
|
||||
// should be caught by editor
|
||||
mAppRef.DebugLog("Invalid parameter");
|
||||
return null;
|
||||
}
|
||||
|
||||
int lastOffset = offset + RAW_IMAGE_SIZE - 1;
|
||||
if (lastOffset >= mFileData.Length) {
|
||||
mAppRef.DebugLog("Bitmap runs off end of file (last offset +" +
|
||||
lastOffset.ToString("x6") + ")");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Linearize the data.
|
||||
byte[] buf = new byte[HR_BYTE_WIDTH * HR_HEIGHT];
|
||||
int outIdx = 0;
|
||||
for (int row = 0; row < HR_HEIGHT; row++) {
|
||||
// If row is ABCDEFGH, we want pppFGHCD EABAB000 (where p is zero for us).
|
||||
int low = ((row & 0xc0) >> 1) | ((row & 0xc0) >> 3) | ((row & 0x08) << 4);
|
||||
int high = ((row & 0x07) << 2) | ((row & 0x30) >> 4);
|
||||
int addr = (high << 8) | low;
|
||||
|
||||
for (int col = 0; col < HR_BYTE_WIDTH; col++) {
|
||||
buf[outIdx++] = mFileData[offset + addr + col];
|
||||
}
|
||||
}
|
||||
|
||||
VisBitmap8 vb = new VisBitmap8(HR_WIDTH, HR_HEIGHT);
|
||||
SetHiResPalette(vb);
|
||||
RenderBitmap(buf, 0, HR_BYTE_WIDTH, HR_HEIGHT, 1, HR_BYTE_WIDTH,
|
||||
isColor ? ColorMode.SimpleColor : ColorMode.Mono, false,
|
||||
vb, 0, 0);
|
||||
return vb;
|
||||
}
|
||||
|
||||
|
||||
private enum ColorMode { Mono, ClunkyColor, SimpleColor, IIgsRGB };
|
||||
|
||||
private void RenderBitmap(int offset, int byteWidth, int height, int colStride,
|
||||
int rowStride, ColorMode colorMode, bool isFirstOdd, VisBitmap8 vb) {
|
||||
int bx = 0;
|
||||
int by = 0;
|
||||
private void RenderBitmap(byte[] data, int offset, int byteWidth, int height,
|
||||
int colStride, int rowStride, ColorMode colorMode, bool isFirstOdd,
|
||||
VisBitmap8 vb, int xstart, int ystart) {
|
||||
int bx = xstart;
|
||||
int by = ystart;
|
||||
switch (colorMode) {
|
||||
case ColorMode.Mono: {
|
||||
// Since we're not displaying this we don't need to worry about
|
||||
@ -182,7 +296,7 @@ namespace RuntimeData.Apple {
|
||||
for (int row = 0; row < height; row++) {
|
||||
int colIdx = 0;
|
||||
for (int col = 0; col < byteWidth; col++) {
|
||||
byte val = mFileData[offset + colIdx];
|
||||
byte val = data[offset + colIdx];
|
||||
for (int bit = 0; bit < 7; bit++) {
|
||||
if ((val & 0x01) == 0) {
|
||||
vb.SetPixelIndex(bx, by, (int)HiResColors.Black0);
|
||||
@ -194,7 +308,7 @@ namespace RuntimeData.Apple {
|
||||
}
|
||||
colIdx += colStride;
|
||||
}
|
||||
bx = 0;
|
||||
bx = xstart;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
@ -214,7 +328,7 @@ namespace RuntimeData.Apple {
|
||||
}
|
||||
|
||||
for (int colByte = 0; colByte < byteWidth; colByte += colStride) {
|
||||
byte val = mFileData[offset + colByte];
|
||||
byte val = data[offset + colByte];
|
||||
bool hiBitSet = (val & 0x80) != 0;
|
||||
|
||||
// Grab 3 or 4 pairs of bits.
|
||||
@ -250,7 +364,7 @@ namespace RuntimeData.Apple {
|
||||
lastBit = -1;
|
||||
}
|
||||
}
|
||||
bx = 0;
|
||||
bx = xstart;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
@ -272,7 +386,7 @@ namespace RuntimeData.Apple {
|
||||
int idx = OVER; // start past "fake" bits
|
||||
int colIdx = 0;
|
||||
for (int col = 0; col < byteWidth; col++) {
|
||||
byte val = mFileData[offset + colIdx];
|
||||
byte val = data[offset + colIdx];
|
||||
bool hiBitSet = (val & 0x80) != 0;
|
||||
|
||||
for (int bit = 0; bit < 7; bit++) {
|
||||
@ -318,7 +432,7 @@ namespace RuntimeData.Apple {
|
||||
}
|
||||
|
||||
// move to next row
|
||||
bx = 0;
|
||||
bx = xstart;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
@ -348,7 +462,7 @@ namespace RuntimeData.Apple {
|
||||
int idx = 0;
|
||||
int colIdx = 0;
|
||||
for (int col = 0; col < byteWidth; col++) {
|
||||
byte val = mFileData[offset + colIdx];
|
||||
byte val = data[offset + colIdx];
|
||||
bool hiBitSet = (val & 0x80) != 0;
|
||||
|
||||
for (int bit = 0; bit < 7; bit++) {
|
||||
@ -401,7 +515,7 @@ namespace RuntimeData.Apple {
|
||||
}
|
||||
|
||||
// move to next row
|
||||
bx = 0;
|
||||
bx = xstart;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
|
BIN
SourceGen/SGTestData/Visualization/apple2-screen-img#062000
Normal file
BIN
SourceGen/SGTestData/Visualization/apple2-screen-img#062000
Normal file
Binary file not shown.
@ -0,0 +1,29 @@
|
||||
### 6502bench SourceGen dis65 v1.0 ###
|
||||
{
|
||||
"_ContentVersion":3,"FileDataLength":8184,"FileDataCrc32":-760689174,"ProjectProps":{
|
||||
"CpuName":"65C02","IncludeUndocumentedInstr":false,"TwoByteBrk":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{
|
||||
"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true},
|
||||
"PlatformSymbolFileIdentifiers":["RT:Apple/F8-ROM.sym65","RT:Apple/Cxxx-IO.sym65","RT:Apple/ProDOS8.sym65"],"ExtensionScriptFileIdentifiers":["RT:Apple/ProDOS8.cs","RT:Apple/VisHiRes.cs"],"ProjectSyms":{
|
||||
}},
|
||||
"AddressMap":[{
|
||||
"Offset":0,"Addr":8192}],"TypeHints":[],"StatusFlagOverrides":{
|
||||
},
|
||||
"Comments":{
|
||||
},
|
||||
"LongComments":{
|
||||
"-2147483647":{
|
||||
"Text":"6502bench SourceGen v1.5.0-dev1","BoxMode":false,"MaxWidth":80,"BackgroundColor":0}},
|
||||
"Notes":{
|
||||
},
|
||||
"UserLabels":{
|
||||
},
|
||||
"OperandFormats":{
|
||||
"0":{
|
||||
"Length":8184,"Format":"Dense","SubFormat":"None","SymbolRef":null}},
|
||||
"LvTables":{
|
||||
},
|
||||
"VisualizationSets":{
|
||||
"0":{
|
||||
"Items":[{
|
||||
"Tag":"full screen image","VisGenIdent":"apple2-hi-res-screen","VisGenParams":{
|
||||
"offset":0,"isColor":true}}]}}}
|
@ -47,7 +47,7 @@ limitations under the License.
|
||||
Visibility="{Binding ScriptWarningVisible}"
|
||||
Text="NOTE: no extension scripts with visualization generators are loaded"/>
|
||||
|
||||
<DataGrid Name="visualizationGrid" Grid.Column="0" Grid.Row="1" Margin="4,4,4,0"
|
||||
<DataGrid Name="visualizationGrid" Grid.Column="0" Grid.Row="1" Margin="0,4,4,0"
|
||||
ItemsSource="{Binding VisualizationList}"
|
||||
IsReadOnly="True"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"
|
||||
@ -81,8 +81,8 @@ limitations under the License.
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Header="Tag" Width="170" Binding="{Binding Tag}"/>
|
||||
<DataGridTextColumn Header="Visualization Generator" Width="212" Binding="{Binding VisGenIdent}"/>
|
||||
<DataGridTextColumn Header="Tag" Width="176" Binding="{Binding Tag}"/>
|
||||
<DataGridTextColumn Header="Visualization Generator" Width="210" Binding="{Binding VisGenIdent}"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user