mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-29 08:29:52 +00:00
Improve visualization
Various improvements: - Switched to ReadOnlyDictionary in Visualization to make it clear that the parameter dictionary should not be modified. - Added a warning to the Visualization Set editor that appears when there are no plugins that implement a visualizer. - Make sure an item is selected in the set editor after edit/remove. - Replaced the checkerboard background with one that's a little bit more grey, so it's more distinct from white pixel data. - Added a new Apple II hi-res color converter whose output more closely matches KEGS and AppleWin RGB. - Added VisHiRes.cs to some Apple II system definitions. - Added some test bitmaps for Apple II hi-res to the test directory. (These are not part of an automated test.)
This commit is contained in:
parent
2f56c56e5c
commit
df04de61e6
@ -15,6 +15,7 @@
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
|
||||
namespace CommonUtil {
|
||||
@ -78,5 +79,21 @@ namespace CommonUtil {
|
||||
return !dict1.Except(dict2).Any();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool CompareDicts<TKey, TValue>(
|
||||
ReadOnlyDictionary<TKey, TValue> dict1, ReadOnlyDictionary<TKey, TValue> dict2) {
|
||||
if (dict1 == dict2) {
|
||||
return true;
|
||||
}
|
||||
if (dict1 == null || dict2 == null) {
|
||||
return false;
|
||||
}
|
||||
if (dict1.Count != dict2.Count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check to see if there are any elements in the first that are not in the second.
|
||||
return !dict1.Except(dict2).Any();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace PluginCommon {
|
||||
/// <summary>
|
||||
@ -150,13 +151,18 @@ namespace PluginCommon {
|
||||
/// <param name="parms">Parameter set.</param>
|
||||
/// <returns>2D visualization object reference, or null if something went
|
||||
/// wrong (unknown ident, bad parameters, etc).</returns>
|
||||
IVisualization2d Generate2d(VisDescr descr, Dictionary<string, object> parms);
|
||||
IVisualization2d Generate2d(VisDescr descr, ReadOnlyDictionary<string, object> parms);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visualization generator descriptor. IPlugin_Visualizer instances publish a list of
|
||||
/// these to tell the application about the generators it supports.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class VisDescr {
|
||||
/// <summary>
|
||||
/// Unique identifier. This is stored in the project file.
|
||||
/// Unique identifier. This is stored in the project file. Names beginning with
|
||||
/// underscores ('_') are reserved.
|
||||
/// </summary>
|
||||
public string Ident { get; private set; }
|
||||
|
||||
@ -181,6 +187,9 @@ namespace PluginCommon {
|
||||
/// </summary>
|
||||
public VisParamDescr[] VisParamDescrs { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public VisDescr(string ident, string uiName, VisType type, VisParamDescr[] descrs) {
|
||||
Ident = ident;
|
||||
UiName = uiName;
|
||||
@ -192,24 +201,59 @@ namespace PluginCommon {
|
||||
/// <summary>
|
||||
/// Visualization parameter descriptor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We provide min/max for individual numeric fields, but there's no way to check other
|
||||
/// fields to see if e.g. Stride >= Width. We'd need a "verify" function and a way to
|
||||
/// report errors that the GUI could use to point out what was wrong.
|
||||
/// </remarks>
|
||||
[Serializable]
|
||||
public class VisParamDescr {
|
||||
/// <summary>
|
||||
/// Special feature enumeration.
|
||||
/// </summary>
|
||||
public enum SpecialMode {
|
||||
None = 0, Offset
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Label to show in the UI.
|
||||
/// </summary>
|
||||
public string UiLabel { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Name to use internally. Do not use names that start with an underscore ('_'), as
|
||||
/// these are reserved for future internal use.
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parameter data type.
|
||||
/// </summary>
|
||||
public Type CsType { get; private set; }
|
||||
|
||||
// Min/Max values for ints and floats. Could also be length for strings.
|
||||
// NOTE: ideally we'd provide a "verify" function that tested individual fields and
|
||||
// could also see other fields, e.g. to confirm that Stride >= Width.
|
||||
/// <summary>
|
||||
/// Minimum allowable value for int/float (and perhaps string length).
|
||||
/// </summary>
|
||||
public object Min { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum allowable value for int/float (and perhaps string length).
|
||||
/// </summary>
|
||||
public object Max { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to a value if the field requires special treatment.
|
||||
/// </summary>
|
||||
public SpecialMode Special { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default value for this field.
|
||||
/// </summary>
|
||||
public object DefaultValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public VisParamDescr(string uiLabel, string name, Type csType, object min, object max,
|
||||
SpecialMode special, object defVal) {
|
||||
UiLabel = uiLabel;
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using CommonUtil;
|
||||
|
||||
namespace PluginCommon {
|
||||
@ -107,7 +108,8 @@ namespace PluginCommon {
|
||||
/// <param name="defVal">Default value.</param>
|
||||
/// <returns>Value found, or the default if the key doesn't exist or the value has the
|
||||
/// wrong type.</returns>
|
||||
public static T GetFromObjDict<T>(Dictionary<string, object> dict, string key, T defVal) {
|
||||
public static T GetFromObjDict<T>(ReadOnlyDictionary<string, object> dict, string key,
|
||||
T defVal) {
|
||||
if (dict.TryGetValue(key, out object objVal)) {
|
||||
if (objVal is T) {
|
||||
return (T)objVal;
|
||||
|
@ -22,17 +22,33 @@ limitations under the License.
|
||||
<ResourceDictionary>
|
||||
<FontFamily x:Key="GeneralMonoFont">Consolas</FontFamily>
|
||||
|
||||
<!-- gradient background for bitmap images -->
|
||||
<LinearGradientBrush x:Key="BitmapBackground" StartPoint="0,0" EndPoint="1,1">
|
||||
<GradientStop Color="#707070" Offset="0.0"/>
|
||||
<GradientStop Color="#f0f0f0" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
|
||||
<!-- checkerboard background for bitmap images -->
|
||||
<!-- https://stackoverflow.com/a/47049174/294248 -->
|
||||
<DrawingBrush x:Key="CheckerBackground" TileMode="Tile" Viewport="0,0,16,16" ViewportUnits="Absolute">
|
||||
<DrawingBrush x:Key="CheckerBackgroundHG" TileMode="Tile" Viewport="0,0,16,16" ViewportUnits="Absolute">
|
||||
<!-- draw two squares, leaving the other two untouched (probably window bkgnd color) -->
|
||||
<DrawingBrush.Drawing>
|
||||
<GeometryDrawing Geometry="M0,0 H1 V1 H2 V2 H1 V1 H0Z" Brush="LightGray"/>
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
<DrawingBrush x:Key="CheckerBackground" TileMode="Tile" Viewport="0,0,16,16" ViewportUnits="Absolute">
|
||||
<DrawingBrush.Drawing>
|
||||
<DrawingGroup>
|
||||
<DrawingGroup.Children>
|
||||
<!-- not sure if it's better to do this with overdraw (background
|
||||
then two squares) or by explicitly drawing four squares -->
|
||||
<GeometryDrawing Geometry="M0,0 H2 V2 H0Z" Brush="#e8e8e8"/>
|
||||
<GeometryDrawing Geometry="M0,0 H1 V1 H2 V2 H1 V1 H0Z" Brush="#f0f0f0"/>
|
||||
<!--<GeometryDrawing Geometry="M0,2 H1 V1 H2 V0 H1 V1 H0Z" Brush="#e8e8e8"/>-->
|
||||
</DrawingGroup.Children>
|
||||
</DrawingGroup>
|
||||
</DrawingBrush.Drawing>
|
||||
</DrawingBrush>
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Res/Strings.xaml"/>
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
@ -368,7 +369,7 @@ namespace SourceGen {
|
||||
public SerVisualization(Visualization vis) {
|
||||
Tag = vis.Tag;
|
||||
VisGenIdent = vis.VisGenIdent;
|
||||
VisGenParams = vis.VisGenParams; // Dictionary
|
||||
VisGenParams = new Dictionary<string, object>(vis.VisGenParams);
|
||||
}
|
||||
}
|
||||
public class SerVisualizationSet {
|
||||
@ -1002,7 +1003,8 @@ namespace SourceGen {
|
||||
parms.Add(kvp.Key, val);
|
||||
}
|
||||
|
||||
Visualization vis = new Visualization(serVis.Tag, serVis.VisGenIdent, parms);
|
||||
Visualization vis = new Visualization(serVis.Tag, serVis.VisGenIdent,
|
||||
new ReadOnlyDictionary<string, object>(parms));
|
||||
outVisSet.Add(vis);
|
||||
}
|
||||
return true;
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
|
||||
using PluginCommon;
|
||||
@ -65,8 +66,8 @@ namespace RuntimeData.Apple {
|
||||
P_IS_COLOR, typeof(bool), 0, 0, 0, true),
|
||||
new VisParamDescr("First col odd",
|
||||
P_IS_FIRST_ODD, typeof(bool), 0, 0, 0, false),
|
||||
new VisParamDescr("Test Float",
|
||||
"floaty", typeof(float), -5.0f, 5.0f, 0, 0.1f),
|
||||
//new VisParamDescr("Test Float",
|
||||
// "floaty", typeof(float), -5.0f, 5.0f, 0, 0.1f),
|
||||
}),
|
||||
new VisDescr(VIS_GEN_BITMAP_FONT, "Apple II Hi-Res Bitmap Font", VisDescr.VisType.Bitmap,
|
||||
new VisParamDescr[] {
|
||||
@ -103,7 +104,7 @@ namespace RuntimeData.Apple {
|
||||
|
||||
// IPlugin_Visualizer
|
||||
public IVisualization2d Generate2d(VisDescr descr,
|
||||
Dictionary<string, object> parms) {
|
||||
ReadOnlyDictionary<string, object> parms) {
|
||||
switch (descr.Ident) {
|
||||
case VIS_GEN_BITMAP:
|
||||
return GenerateBitmap(parms);
|
||||
@ -116,7 +117,7 @@ namespace RuntimeData.Apple {
|
||||
}
|
||||
}
|
||||
|
||||
private IVisualization2d GenerateBitmap(Dictionary<string, object> parms) {
|
||||
private IVisualization2d GenerateBitmap(ReadOnlyDictionary<string, object> parms) {
|
||||
int offset, byteWidth, height, colStride, rowStride;
|
||||
bool isColor, isFirstOdd;
|
||||
|
||||
@ -147,7 +148,7 @@ namespace RuntimeData.Apple {
|
||||
mAppRef.DebugLog("Invalid column stride");
|
||||
return null;
|
||||
}
|
||||
if (rowStride < byteWidth * colStride - (colStride-1) || rowStride > MAX_DIM) {
|
||||
if (rowStride < byteWidth * colStride - (colStride - 1) || rowStride > MAX_DIM) {
|
||||
mAppRef.DebugLog("Invalid row stride");
|
||||
return null;
|
||||
}
|
||||
@ -159,187 +160,283 @@ namespace RuntimeData.Apple {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
VisBitmap8 vb = new VisBitmap8(byteWidth * 7, height);
|
||||
SetHiResPalette(vb);
|
||||
|
||||
if (!isColor) {
|
||||
// B&W mode. Since we're not displaying this we don't need to worry about
|
||||
// half-pixel shifts, and can just convert 7 bits to pixels.
|
||||
int bx = 0;
|
||||
int by = 0;
|
||||
for (int row = 0; row < height; row++) {
|
||||
int colIdx = 0;
|
||||
for (int col = 0; col < byteWidth; col++) {
|
||||
byte val = mFileData[offset + colIdx];
|
||||
for (int bit = 0; bit < 7; bit++) {
|
||||
vb.SetPixelIndex(bx, by, (byte)((val & 0x01) + 1)); // black or white
|
||||
val >>= 1;
|
||||
bx++;
|
||||
}
|
||||
colIdx += colStride;
|
||||
}
|
||||
bx = 0;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
} else {
|
||||
int bx = 0;
|
||||
int by = 0;
|
||||
|
||||
#if false
|
||||
// Color mode. We treat the data as a strictly 140-mode bitmap, which doesn't
|
||||
// quite match up with how the pixels will be displayed, but does allow a
|
||||
// straightforward conversion between file formats. Color fringing is severe.
|
||||
for (int row = 0; row < height; row++) {
|
||||
int lastBit;
|
||||
if (isFirstOdd) {
|
||||
lastBit = 0; // pretend we already have one bit
|
||||
} else {
|
||||
lastBit = -1;
|
||||
}
|
||||
|
||||
for (int colByte = 0; colByte < byteWidth; colByte += colStride) {
|
||||
byte val = mFileData[offset + colByte];
|
||||
bool hiBitSet = (val & 0x80) != 0;
|
||||
|
||||
// Grab 3 or 4 pairs of bits.
|
||||
int pairCount = (lastBit < 0) ? 3 : 4;
|
||||
while (pairCount-- > 0) {
|
||||
int twoBits;
|
||||
if (lastBit >= 0) {
|
||||
// merge with bit from previous byte
|
||||
twoBits = (lastBit << 1) | (val & 0x01);
|
||||
val >>= 1;
|
||||
lastBit = -1;
|
||||
} else {
|
||||
// grab two bits
|
||||
twoBits = (val & 0x03);
|
||||
val >>= 2;
|
||||
}
|
||||
|
||||
if (hiBitSet) {
|
||||
twoBits += 4;
|
||||
}
|
||||
|
||||
// We're in 140 mode, so set two adjacent pixels.
|
||||
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
|
||||
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
|
||||
}
|
||||
|
||||
bool thisEven = ((colByte & 0x01) == 0) ^ isFirstOdd;
|
||||
if (thisEven) {
|
||||
// started in even column we have one bit left over
|
||||
lastBit = val & 0x01;
|
||||
} else {
|
||||
// started in odd column, all bits consumed
|
||||
lastBit = -1;
|
||||
}
|
||||
}
|
||||
bx = 0;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
#else
|
||||
// Color conversion similar to what CiderPress does, but without the half-pixel
|
||||
// shift (we're trying to create a 1:1 bitmap, not 1:2).
|
||||
bool[] lineBits = new bool[byteWidth * 7];
|
||||
bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simplifies things
|
||||
int[] colorBuf = new int[byteWidth * 7];
|
||||
for (int row = 0; row < height; row++) {
|
||||
// Unravel the bits.
|
||||
int idx = 0;
|
||||
int colIdx = 0;
|
||||
for (int col = 0; col < byteWidth; col++) {
|
||||
byte val = mFileData[offset + colIdx];
|
||||
bool hiBitSet = (val & 0x80) != 0;
|
||||
|
||||
for (int bit = 0; bit < 7; bit++) {
|
||||
hiFlags[idx] = hiBitSet;
|
||||
lineBits[idx] = (val & 0x01) != 0;
|
||||
idx++;
|
||||
val >>= 1;
|
||||
}
|
||||
colIdx += colStride;
|
||||
}
|
||||
|
||||
// Convert to color.
|
||||
int lastBit = byteWidth * 7;
|
||||
for (idx = 0; idx < lastBit; idx++) {
|
||||
int colorShift = hiFlags[idx] ? 4 : 0;
|
||||
if (!lineBits[idx]) {
|
||||
// Bit not set, set pixel to black.
|
||||
colorBuf[idx] = (int)HiResColors.Black0 + colorShift;
|
||||
} else {
|
||||
// Bit set, set pixel to white or color.
|
||||
if (idx > 0 && colorBuf[idx - 1] != (int)HiResColors.Black0 &&
|
||||
colorBuf[idx - 1] != (int)HiResColors.Black1) {
|
||||
// previous bit was also set, this is white
|
||||
colorBuf[idx] = (int)HiResColors.White0 + colorShift;
|
||||
|
||||
// the previous pixel is part of a run of white
|
||||
colorBuf[idx - 1] = (int)HiResColors.White0 + colorShift;
|
||||
} else {
|
||||
// previous bit not set *or* was first pixel in line;
|
||||
// set color based on whether this is even or odd pixel col
|
||||
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
|
||||
if (isOdd) {
|
||||
colorBuf[idx] = (int)HiResColors.Green + colorShift;
|
||||
} else {
|
||||
colorBuf[idx] = (int)HiResColors.Purple + colorShift;
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a run of the same color? If so, smooth the color out.
|
||||
// Note that white blends smoothly with everything.
|
||||
if (idx > 1 && (colorBuf[idx - 2] == colorBuf[idx] ||
|
||||
colorBuf[idx - 2] == (int)HiResColors.White0 ||
|
||||
colorBuf[idx - 2] == (int)HiResColors.White1)) {
|
||||
|
||||
//if (colorBuf[idx - 1] != (int)HiResColors.Black0 &&
|
||||
// colorBuf[idx - 1] != (int) HiResColors.Black1) {
|
||||
// mAppRef.DebugLog("Unexpected color at row=" + by +
|
||||
// " idx=" + idx + ": " + colorBuf[idx - 1]);
|
||||
//}
|
||||
|
||||
colorBuf[idx - 1] = colorBuf[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write to bitmap.
|
||||
for (idx = 0; idx < lastBit; idx++) {
|
||||
vb.SetPixelIndex(bx++, by, sHiResColorMap[colorBuf[idx]]);
|
||||
}
|
||||
|
||||
// move to next row
|
||||
bx = 0;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
RenderBitmap(offset, byteWidth, height, colStride, rowStride,
|
||||
isColor ? ColorMode.SimpleColor : ColorMode.Mono, isFirstOdd, vb);
|
||||
return vb;
|
||||
}
|
||||
|
||||
private enum HiResColors {
|
||||
Black0 = 0,
|
||||
Green = 1,
|
||||
Purple = 2,
|
||||
White0 = 3,
|
||||
Black1 = 4,
|
||||
Orange = 5,
|
||||
Blue = 6,
|
||||
White1 = 7
|
||||
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;
|
||||
switch (colorMode) {
|
||||
case ColorMode.Mono: {
|
||||
// Since we're not displaying this we don't need to worry about
|
||||
// half-pixel shifts, and can just convert 7 bits to pixels.
|
||||
for (int row = 0; row < height; row++) {
|
||||
int colIdx = 0;
|
||||
for (int col = 0; col < byteWidth; col++) {
|
||||
byte val = mFileData[offset + colIdx];
|
||||
for (int bit = 0; bit < 7; bit++) {
|
||||
if ((val & 0x01) == 0) {
|
||||
vb.SetPixelIndex(bx, by, (int)HiResColors.Black0);
|
||||
} else {
|
||||
vb.SetPixelIndex(bx, by, (int)HiResColors.White0);
|
||||
}
|
||||
val >>= 1;
|
||||
bx++;
|
||||
}
|
||||
colIdx += colStride;
|
||||
}
|
||||
bx = 0;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorMode.ClunkyColor: {
|
||||
// We treat the data as a strictly 140-mode bitmap, which doesn't match
|
||||
// up well with how the pixels will be displayed, but does allow a
|
||||
// straightforward conversion between file formats. Color fringing is
|
||||
// severe.
|
||||
for (int row = 0; row < height; row++) {
|
||||
int lastBit;
|
||||
if (isFirstOdd) {
|
||||
lastBit = 0; // pretend we already have one bit
|
||||
} else {
|
||||
lastBit = -1;
|
||||
}
|
||||
|
||||
for (int colByte = 0; colByte < byteWidth; colByte += colStride) {
|
||||
byte val = mFileData[offset + colByte];
|
||||
bool hiBitSet = (val & 0x80) != 0;
|
||||
|
||||
// Grab 3 or 4 pairs of bits.
|
||||
int pairCount = (lastBit < 0) ? 3 : 4;
|
||||
while (pairCount-- > 0) {
|
||||
int twoBits;
|
||||
if (lastBit >= 0) {
|
||||
// merge with bit from previous byte
|
||||
twoBits = (lastBit << 1) | (val & 0x01);
|
||||
val >>= 1;
|
||||
lastBit = -1;
|
||||
} else {
|
||||
// grab two bits
|
||||
twoBits = (val & 0x03);
|
||||
val >>= 2;
|
||||
}
|
||||
|
||||
if (hiBitSet) {
|
||||
twoBits += 4;
|
||||
}
|
||||
|
||||
// We're in 140 mode, so set two adjacent pixels.
|
||||
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
|
||||
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
|
||||
}
|
||||
|
||||
bool thisEven = ((colByte & 0x01) == 0) ^ isFirstOdd;
|
||||
if (thisEven) {
|
||||
// started in even column we have one bit left over
|
||||
lastBit = val & 0x01;
|
||||
} else {
|
||||
// started in odd column, all bits consumed
|
||||
lastBit = -1;
|
||||
}
|
||||
}
|
||||
bx = 0;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorMode.SimpleColor: {
|
||||
// Straightforward conversion, with no funky border effects. This
|
||||
// represents an idealized version of the hardware.
|
||||
|
||||
// Bits for every byte, plus a couple of "fake" bits on the ends so
|
||||
// we don't have to throw range-checks everywhere.
|
||||
const int OVER = 2;
|
||||
bool[] lineBits = new bool[OVER + byteWidth * 7 + OVER];
|
||||
bool[] hiFlags = new bool[OVER + byteWidth * 7 + OVER];
|
||||
for (int row = 0; row < height; row++) {
|
||||
// Unravel the bits. Note we do each byte "backwards", i.e. the
|
||||
// low bit (which is generally considered to be on the right) is
|
||||
// the leftmost pixel.
|
||||
int idx = OVER; // start past "fake" bits
|
||||
int colIdx = 0;
|
||||
for (int col = 0; col < byteWidth; col++) {
|
||||
byte val = mFileData[offset + colIdx];
|
||||
bool hiBitSet = (val & 0x80) != 0;
|
||||
|
||||
for (int bit = 0; bit < 7; bit++) {
|
||||
hiFlags[idx] = hiBitSet;
|
||||
lineBits[idx] = (val & 0x01) != 0;
|
||||
idx++;
|
||||
val >>= 1;
|
||||
}
|
||||
colIdx += colStride;
|
||||
}
|
||||
|
||||
// Convert to color.
|
||||
int lastBit = byteWidth * 7;
|
||||
for (idx = OVER; idx < lastBit + OVER; idx++) {
|
||||
int colorShift = hiFlags[idx] ? 2 : 0;
|
||||
if (lineBits[idx] && (lineBits[idx - 1] || lineBits[idx + 1])) {
|
||||
// [X]11 or [1]1X; two 1s in a row is always white
|
||||
vb.SetPixelIndex(bx++, by, (byte)HiResColors.White0);
|
||||
} else if (lineBits[idx]) {
|
||||
// [0]10, color pixel
|
||||
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
|
||||
if (isOdd) {
|
||||
vb.SetPixelIndex(bx++, by,
|
||||
(byte)((int)HiResColors.Green + colorShift));
|
||||
} else {
|
||||
vb.SetPixelIndex(bx++, by,
|
||||
(byte)((int)HiResColors.Purple + colorShift));
|
||||
}
|
||||
} else if (lineBits[idx - 1] && lineBits[idx + 1]) {
|
||||
// [1]01, keep color going
|
||||
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
|
||||
if (isOdd) {
|
||||
vb.SetPixelIndex(bx++, by,
|
||||
(byte)((int)HiResColors.Purple + colorShift));
|
||||
} else {
|
||||
vb.SetPixelIndex(bx++, by,
|
||||
(byte)((int)HiResColors.Green + colorShift));
|
||||
}
|
||||
} else {
|
||||
// [0]0X or [X]01
|
||||
vb.SetPixelIndex(bx++, by, (byte)HiResColors.Black0);
|
||||
}
|
||||
}
|
||||
|
||||
// move to next row
|
||||
bx = 0;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ColorMode.IIgsRGB: {
|
||||
// Color conversion similar to what CiderPress does, but without the
|
||||
// half-pixel shift (we're trying to create a 1:1 bitmap, not 1:2).
|
||||
//
|
||||
// This replicates some of the oddness in Apple IIgs RGB monitor output,
|
||||
// but it's not quite right though. For example:
|
||||
//
|
||||
// observed generated
|
||||
// d5 2a: blue [dk blue] purple ... black ...
|
||||
// aa 55: orange [yellow] green ... white ...
|
||||
// 55 aa: purple [lt blue] blue ... black ...
|
||||
// 2a d5: green [brown] orange ... black ...
|
||||
//
|
||||
// KEGS doesn't seem to try to model this; it shows solid colors with no
|
||||
// wackiness. AppleWin in "Color TV" mode shows similar effects, but is
|
||||
// much blurrier (by design).
|
||||
bool[] lineBits = new bool[byteWidth * 7];
|
||||
bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simpler
|
||||
int[] colorBuf = new int[byteWidth * 7];
|
||||
for (int row = 0; row < height; row++) {
|
||||
// Unravel the bits.
|
||||
int idx = 0;
|
||||
int colIdx = 0;
|
||||
for (int col = 0; col < byteWidth; col++) {
|
||||
byte val = mFileData[offset + colIdx];
|
||||
bool hiBitSet = (val & 0x80) != 0;
|
||||
|
||||
for (int bit = 0; bit < 7; bit++) {
|
||||
hiFlags[idx] = hiBitSet;
|
||||
lineBits[idx] = (val & 0x01) != 0;
|
||||
idx++;
|
||||
val >>= 1;
|
||||
}
|
||||
colIdx += colStride;
|
||||
}
|
||||
|
||||
// Convert to color.
|
||||
int lastBit = byteWidth * 7;
|
||||
for (idx = 0; idx < lastBit; idx++) {
|
||||
int colorShift = hiFlags[idx] ? 2 : 0;
|
||||
if (!lineBits[idx]) {
|
||||
// Bit not set, set pixel to black.
|
||||
colorBuf[idx] = (int)HiResColors.Black0;
|
||||
} else {
|
||||
// Bit set, set pixel to white or color.
|
||||
if (idx > 0 && colorBuf[idx - 1] != (int)HiResColors.Black0) {
|
||||
// previous bit was also set, this is white
|
||||
colorBuf[idx] = (int)HiResColors.White0;
|
||||
|
||||
// the previous pixel is part of a run of white
|
||||
colorBuf[idx - 1] = (int)HiResColors.White0;
|
||||
} else {
|
||||
// previous bit not set *or* was first pixel in line;
|
||||
// set color based on whether this is even or odd pixel col
|
||||
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
|
||||
if (isOdd) {
|
||||
colorBuf[idx] = (int)HiResColors.Green + colorShift;
|
||||
} else {
|
||||
colorBuf[idx] = (int)HiResColors.Purple + colorShift;
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a run of the same color? If so, smooth the
|
||||
// color out. Note that white blends smoothly with everything.
|
||||
if (idx > 1 && (colorBuf[idx - 2] == colorBuf[idx] ||
|
||||
colorBuf[idx - 2] == (int)HiResColors.White0)) {
|
||||
colorBuf[idx - 1] = colorBuf[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write to bitmap.
|
||||
for (idx = 0; idx < lastBit; idx++) {
|
||||
vb.SetPixelIndex(bx++, by, (byte)colorBuf[idx]);
|
||||
}
|
||||
|
||||
// move to next row
|
||||
bx = 0;
|
||||
by++;
|
||||
offset += rowStride;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// just leave the bitmap empty
|
||||
mAppRef.DebugLog("Unknown ColorMode " + colorMode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Maps HiResColors to the palette entries.
|
||||
/// <summary>
|
||||
/// Map hi-res colors to palette entries.
|
||||
/// </summary>
|
||||
private enum HiResColors : byte {
|
||||
Black0 = 1,
|
||||
Green = 3,
|
||||
Purple = 4,
|
||||
White0 = 2,
|
||||
Black1 = 1,
|
||||
Orange = 5,
|
||||
Blue = 6,
|
||||
White1 = 2
|
||||
}
|
||||
|
||||
// Maps HiResColors to the palette entries. Used for mapping 2-bit values to colors.
|
||||
private static readonly byte[] sHiResColorMap = new byte[8] {
|
||||
1, 3, 4, 2, 1, 5, 6, 2
|
||||
};
|
||||
|
||||
private void SetHiResPalette(VisBitmap8 vb) {
|
||||
// These don't match directly to hi-res color numbers because we want to
|
||||
// avoid adding black/white twice.
|
||||
// avoid adding black/white twice. The colors correspond to Apple IIgs RGB
|
||||
// monitor output.
|
||||
vb.AddColor(0, 0, 0, 0); // 0=transparent
|
||||
vb.AddColor(0xff, 0x00, 0x00, 0x00); // 1=black0/black1
|
||||
vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white0/white1
|
||||
|
@ -15,7 +15,8 @@
|
||||
"RT:Apple/ProDOS8.sym65"
|
||||
],
|
||||
"ExtensionScripts" : [
|
||||
"RT:Apple/ProDOS8.cs"
|
||||
"RT:Apple/ProDOS8.cs",
|
||||
"RT:Apple/VisHiRes.cs"
|
||||
],
|
||||
"Parameters" : {
|
||||
"load-address":"0x2000"
|
||||
@ -33,6 +34,7 @@
|
||||
"RT:Apple/DOS33.sym65"
|
||||
],
|
||||
"ExtensionScripts" : [
|
||||
"RT:Apple/VisHiRes.cs"
|
||||
],
|
||||
"Parameters" : {
|
||||
"load-address":"0x2000"
|
||||
@ -51,7 +53,8 @@
|
||||
"RT:Apple/IIgs-ROM.sym65"
|
||||
],
|
||||
"ExtensionScripts" : [
|
||||
"RT:Apple/ProDOS8.cs"
|
||||
"RT:Apple/ProDOS8.cs",
|
||||
"RT:Apple/VisHiRes.cs"
|
||||
],
|
||||
"Parameters" : {
|
||||
"load-address":"0x2000",
|
||||
|
@ -93,3 +93,9 @@ This is a collection of project files with deliberate errors. These exist
|
||||
to exercise the load-time error reporting. See the README in that directory
|
||||
for a full explanation.
|
||||
|
||||
|
||||
## Visualization ##
|
||||
|
||||
Some test projects and data files for exercising the visualization generators.
|
||||
Not part of a formal test; load the projects and eyeball the results.
|
||||
|
||||
|
BIN
SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000
Normal file
BIN
SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000
Normal file
Binary file not shown.
166
SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000.S
Normal file
166
SourceGen/SGTestData/Visualization/apple2-bitmap-test#061000.S
Normal file
@ -0,0 +1,166 @@
|
||||
; Copyright 2019 faddenSoft. All Rights Reserved.
|
||||
; See the LICENSE.txt file for distribution terms (Apache 2.0).
|
||||
;
|
||||
; Assembler: Merlin 32
|
||||
|
||||
HGR equ $f3e2
|
||||
src_ptr equ $00
|
||||
xoff equ $02
|
||||
width equ $03
|
||||
line equ $04
|
||||
count equ $05
|
||||
|
||||
org $1000
|
||||
|
||||
bit bitmap1 ;give the disassembler a reason to label them
|
||||
bit bitmap2
|
||||
bit bitmap3
|
||||
bit bitmap4
|
||||
bit bitmap5
|
||||
|
||||
jsr HGR ;clear screen, set hi-res mode
|
||||
lda #<bitmap1
|
||||
sta src_ptr
|
||||
lda #>bitmap1
|
||||
sta src_ptr+1
|
||||
lda #$00
|
||||
sta xoff
|
||||
lda #$01 ;1x8
|
||||
sta width
|
||||
jsr draw
|
||||
|
||||
lda #<bitmap2
|
||||
sta src_ptr
|
||||
lda #>bitmap2
|
||||
sta src_ptr+1
|
||||
lda #$03 ;start in odd column
|
||||
sta xoff
|
||||
lda #$01 ;1x8
|
||||
sta width
|
||||
jsr draw
|
||||
|
||||
lda #<bitmap3
|
||||
sta src_ptr
|
||||
lda #>bitmap3
|
||||
sta src_ptr+1
|
||||
lda #$06
|
||||
sta xoff
|
||||
lda #$02 ;2x8
|
||||
sta width
|
||||
jsr draw
|
||||
|
||||
lda #<bitmap4
|
||||
sta src_ptr
|
||||
lda #>bitmap4
|
||||
sta src_ptr+1
|
||||
lda #$0a
|
||||
sta xoff
|
||||
lda #$02 ;2x8
|
||||
sta width
|
||||
jsr draw
|
||||
|
||||
lda #<bitmap5
|
||||
sta src_ptr
|
||||
lda #>bitmap5
|
||||
sta src_ptr+1
|
||||
lda #$0e
|
||||
sta xoff
|
||||
lda #$03 ;3x8
|
||||
sta width
|
||||
jsr draw
|
||||
|
||||
rts
|
||||
|
||||
; Copies an Nx8 bitmap to the hi-res screen.
|
||||
;
|
||||
; Put X offset in "xoff", width-1 in "width_m1", and a pointer to the bitmap
|
||||
; data in "src_ptr".
|
||||
draw
|
||||
ldy #$00 ;index to input byte
|
||||
sty line ;init line index to zero
|
||||
:loop ldx line
|
||||
lda addrs,x
|
||||
beq :done
|
||||
sta _copy+2
|
||||
lda xoff
|
||||
sta _copy+1
|
||||
|
||||
ldx #$00
|
||||
:loop1 lda (src_ptr),y
|
||||
_copy sta $2000,x
|
||||
iny
|
||||
inx
|
||||
cpx width
|
||||
bne :loop1
|
||||
inc line
|
||||
bne :loop ;always (more or less)
|
||||
|
||||
:done rts
|
||||
|
||||
; hi byte of base addresses on hi-res screen; must be 8 of them
|
||||
addrs
|
||||
dfb $20
|
||||
dfb $24
|
||||
dfb $28
|
||||
dfb $2c
|
||||
dfb $30
|
||||
dfb $34
|
||||
dfb $38
|
||||
dfb $3c
|
||||
dfb $00
|
||||
|
||||
; 1x8
|
||||
bitmap1
|
||||
dfb $00
|
||||
dfb $01
|
||||
dfb $02
|
||||
dfb $04
|
||||
dfb $08
|
||||
dfb $10
|
||||
dfb $20
|
||||
dfb $40
|
||||
|
||||
; 1x8 (odd byte)
|
||||
bitmap2
|
||||
dfb $c0
|
||||
dfb $a0
|
||||
dfb $90
|
||||
dfb $88
|
||||
dfb $84
|
||||
dfb $82
|
||||
dfb $81
|
||||
dfb $80
|
||||
|
||||
; 2x8
|
||||
bitmap3
|
||||
dfb $d5,$aa ;pure colors
|
||||
dfb $aa,$d5
|
||||
dfb $55,$2a
|
||||
dfb $2a,$55
|
||||
dfb $d5,$2a ;high bit split
|
||||
dfb $aa,$55
|
||||
dfb $55,$aa
|
||||
dfb $2a,$d5
|
||||
|
||||
; 2x8
|
||||
bitmap4
|
||||
dfb $40,$00 ;bits at the edge
|
||||
dfb $00,$01
|
||||
dfb $40,$01
|
||||
dfb $60,$03
|
||||
dfb $40,$02
|
||||
dfb $d5,$d5 ;same byte odd/even
|
||||
dfb $aa,$aa
|
||||
dfb $d5,$55
|
||||
|
||||
; 3x8 (do in B&W)
|
||||
bitmap5
|
||||
dfb $7f,$7f,$7f
|
||||
dfb $03,$08,$60
|
||||
dfb $03,$1c,$60
|
||||
dfb $03,$3e,$60
|
||||
dfb $03,$7f,$60
|
||||
dfb $43,$77,$61
|
||||
dfb $63,$63,$63
|
||||
dfb $7f,$7f,$7f
|
||||
|
@ -0,0 +1,47 @@
|
||||
### 6502bench SourceGen dis65 v1.0 ###
|
||||
{
|
||||
"_ContentVersion":3,"FileDataLength":232,"FileDataCrc32":-2081570119,"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"],"ExtensionScriptFileIdentifiers":["RT:Apple/VisHiRes.cs"],"ProjectSyms":{
|
||||
}},
|
||||
"AddressMap":[{
|
||||
"Offset":0,"Addr":4096}],"TypeHints":[{
|
||||
"Low":0,"High":0,"Hint":"Code"}],"StatusFlagOverrides":{
|
||||
},
|
||||
"Comments":{
|
||||
},
|
||||
"LongComments":{
|
||||
"-2147483647":{
|
||||
"Text":"6502bench SourceGen v1.5.0-dev1","BoxMode":false,"MaxWidth":80,"BackgroundColor":0}},
|
||||
"Notes":{
|
||||
},
|
||||
"UserLabels":{
|
||||
"114":{
|
||||
"Label":"Draw","Value":4210,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"}},
|
||||
"OperandFormats":{
|
||||
"151":{
|
||||
"Length":9,"Format":"Dense","SubFormat":"None","SymbolRef":null}},
|
||||
"LvTables":{
|
||||
},
|
||||
"VisualizationSets":{
|
||||
"160":{
|
||||
"Items":[{
|
||||
"Tag":"vis0000a0","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||
"offset":160,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
|
||||
"168":{
|
||||
"Items":[{
|
||||
"Tag":"vis0000a8","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||
"offset":168,"byteWidth":1,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":true}}]},
|
||||
"176":{
|
||||
"Items":[{
|
||||
"Tag":"vis0000b0","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||
"offset":176,"byteWidth":2,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
|
||||
"192":{
|
||||
"Items":[{
|
||||
"Tag":"vis0000c0","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||
"offset":192,"byteWidth":2,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]},
|
||||
"208":{
|
||||
"Items":[{
|
||||
"Tag":"vis0000d0","VisGenIdent":"apple2-hi-res-bitmap","VisGenParams":{
|
||||
"offset":208,"byteWidth":3,"height":8,"colStride":0,"rowStride":0,"isColor":true,"isFirstOdd":false}}]}}}
|
BIN
SourceGen/SGTestData/Visualization/apple2-bitmap-test-AW-RGB.png
Normal file
BIN
SourceGen/SGTestData/Visualization/apple2-bitmap-test-AW-RGB.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 713 B |
@ -15,6 +15,7 @@
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
@ -26,12 +27,12 @@ namespace SourceGen {
|
||||
/// <summary>
|
||||
/// Graphical visualization object. Useful for displaying 2D bitmaps and 3D objects.
|
||||
///
|
||||
/// Treat this as generally immutable, i.e. don't modify VisGenParams. The CachedImage
|
||||
/// field is mutable.
|
||||
/// This is generally immutable, except for the CachedImage field.
|
||||
/// </summary>
|
||||
public class Visualization {
|
||||
/// <summary>
|
||||
/// Unique user-specified tag. Contents are arbitrary, but may not be empty.
|
||||
/// Unique user-specified tag. This may be any valid string that is at least two
|
||||
/// characters long after the leading and trailing whitespace have been trimmed.
|
||||
/// </summary>
|
||||
public string Tag { get; private set; }
|
||||
|
||||
@ -41,9 +42,9 @@ namespace SourceGen {
|
||||
public string VisGenIdent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parameters passed to the visualization generator.
|
||||
/// Parameters to be passed to the visualization generator.
|
||||
/// </summary>
|
||||
public Dictionary<string, object> VisGenParams { get; private set; }
|
||||
public ReadOnlyDictionary<string, object> VisGenParams { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Cached reference to 2D image, useful for thumbnails. Not serialized.
|
||||
@ -68,7 +69,7 @@ namespace SourceGen {
|
||||
/// <param name="visGenIdent">Visualization generator identifier.</param>
|
||||
/// <param name="visGenParams">Parameters for visualization generator.</param>
|
||||
public Visualization(string tag, string visGenIdent,
|
||||
Dictionary<string, object> visGenParams) {
|
||||
ReadOnlyDictionary<string, object> visGenParams) {
|
||||
Tag = tag;
|
||||
VisGenIdent = visGenIdent;
|
||||
VisGenParams = visGenParams;
|
||||
|
@ -16,6 +16,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
using PluginCommon;
|
||||
@ -147,7 +148,8 @@ namespace SourceGen {
|
||||
|
||||
IVisualization2d vis2d;
|
||||
try {
|
||||
vis2d = vplug.Generate2d(visDescr, vis.VisGenParams);
|
||||
vis2d = vplug.Generate2d(visDescr,
|
||||
new ReadOnlyDictionary<string, object>(vis.VisGenParams));
|
||||
if (vis2d == null) {
|
||||
Debug.WriteLine("Vis generator returned null");
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ namespace SourceGen.WpfGui {
|
||||
private void OkButton_Click(object sender, RoutedEventArgs e) {
|
||||
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
|
||||
Debug.Assert(item != null);
|
||||
Dictionary<string, object> valueDict = CreateVisGenParams();
|
||||
ReadOnlyDictionary<string, object> valueDict = CreateVisGenParams();
|
||||
string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid);
|
||||
Debug.Assert(isTagValid);
|
||||
NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict);
|
||||
@ -263,7 +263,7 @@ namespace SourceGen.WpfGui {
|
||||
DialogResult = true;
|
||||
}
|
||||
|
||||
private Dictionary<string, object> CreateVisGenParams() {
|
||||
private ReadOnlyDictionary<string, object> CreateVisGenParams() {
|
||||
// Generate value dictionary.
|
||||
Dictionary<string, object> valueDict =
|
||||
new Dictionary<string, object>(ParameterList.Count);
|
||||
@ -285,7 +285,7 @@ namespace SourceGen.WpfGui {
|
||||
}
|
||||
}
|
||||
|
||||
return valueDict;
|
||||
return new ReadOnlyDictionary<string, object>(valueDict);
|
||||
}
|
||||
|
||||
private bool ParseIntObj(object val, VisParamDescr.SpecialMode special, out int intVal) {
|
||||
|
@ -23,7 +23,7 @@ limitations under the License.
|
||||
xmlns:local="clr-namespace:SourceGen.WpfGui"
|
||||
mc:Ignorable="d"
|
||||
Title="Edit Visualization Set"
|
||||
Width="560" Height="400" ResizeMode="NoResize"
|
||||
Width="600" Height="400" ResizeMode="NoResize"
|
||||
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
|
||||
Closing="Window_Closing">
|
||||
|
||||
@ -38,11 +38,16 @@ limitations under the License.
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<DataGrid Name="visualizationGrid" Grid.Column="0" Grid.Row="0" Margin="4,4,4,0"
|
||||
<TextBlock Grid.Row="0" Foreground="Red" Margin="0,0,0,4"
|
||||
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"
|
||||
ItemsSource="{Binding VisualizationList}"
|
||||
IsReadOnly="True"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"
|
||||
@ -76,26 +81,27 @@ limitations under the License.
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
<DataGridTextColumn Header="Tag" Width="155" Binding="{Binding Tag}"/>
|
||||
<DataGridTextColumn Header="Visualization Generator" Width="220" Binding="{Binding VisGenIdent}"/>
|
||||
<DataGridTextColumn Header="Tag" Width="170" Binding="{Binding Tag}"/>
|
||||
<DataGridTextColumn Header="Visualization Generator" Width="212" Binding="{Binding VisGenIdent}"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<StackPanel Grid.Column="1" Grid.Row="0">
|
||||
<Button Width="75" Margin="4" Content="_New..."
|
||||
<StackPanel Grid.Column="1" Grid.Row="1">
|
||||
<Button Width="110" Margin="4" Content="_New Bitmap..."
|
||||
IsEnabled="{Binding HasVisPlugins}" Click="NewButton_Click"/>
|
||||
<Button Width="75" Margin="4" Content="_Edit"
|
||||
|
||||
<Button Width="110" Margin="4,20,4,4" Content="_Edit..."
|
||||
IsEnabled="{Binding IsEditEnabled}" Click="EditButton_Click"/>
|
||||
<Button Width="75" Margin="4" Content="_Remove"
|
||||
<Button Width="110" Margin="4" Content="_Remove"
|
||||
IsEnabled="{Binding IsRemoveEnabled}" Click="RemoveButton_Click"/>
|
||||
|
||||
<Button Width="75" Margin="4,20,4,4" Content="_Up"
|
||||
<Button Width="110" Margin="4,20,4,4" Content="_Up"
|
||||
IsEnabled="{Binding IsUpEnabled}" Click="UpButton_Click"/>
|
||||
<Button Width="75" Margin="4,4" Content="_Down"
|
||||
<Button Width="110" Margin="4,4" Content="_Down"
|
||||
IsEnabled="{Binding IsDownEnabled}" Click="DownButton_Click"/>
|
||||
</StackPanel>
|
||||
|
||||
<DockPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="0,8,0,0" LastChildFill="False">
|
||||
<DockPanel Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Margin="0,8,0,0" LastChildFill="False">
|
||||
<Button DockPanel.Dock="Right" Content="Cancel" Width="70" Margin="8,0,0,0" IsCancel="True"/>
|
||||
<Button DockPanel.Dock="Right" Grid.Column="1" Content="OK" Width="70"
|
||||
IsDefault="True" Click="OkButton_Click"/>
|
||||
|
@ -50,6 +50,11 @@ namespace SourceGen.WpfGui {
|
||||
}
|
||||
private bool mHasVisPlugins;
|
||||
|
||||
public Visibility ScriptWarningVisible {
|
||||
get { return mHasVisPlugins ? Visibility.Collapsed : Visibility.Visible; }
|
||||
// this can't change while the dialog is open, so don't need OnPropertyChanged
|
||||
}
|
||||
|
||||
public bool IsEditEnabled {
|
||||
get { return mIsEditEnabled; }
|
||||
set { mIsEditEnabled = value; OnPropertyChanged(); }
|
||||
@ -92,11 +97,14 @@ namespace SourceGen.WpfGui {
|
||||
mOffset = offset;
|
||||
|
||||
if (curSet != null) {
|
||||
// Populate the ItemsSource.
|
||||
// Populate the data grid ItemsSource.
|
||||
foreach (Visualization vis in curSet) {
|
||||
VisualizationList.Add(vis);
|
||||
}
|
||||
}
|
||||
if (VisualizationList.Count > 0) {
|
||||
visualizationGrid.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
// Check to see if we have any relevant plugins. If not, disable New/Edit.
|
||||
List<IPlugin> plugins = project.GetActivePlugins();
|
||||
@ -163,6 +171,7 @@ namespace SourceGen.WpfGui {
|
||||
return;
|
||||
}
|
||||
VisualizationList.Add(dlg.NewVis);
|
||||
visualizationGrid.SelectedIndex = VisualizationList.Count - 1;
|
||||
}
|
||||
|
||||
private void EditButton_Click(object sender, RoutedEventArgs e) {
|
||||
@ -185,11 +194,19 @@ namespace SourceGen.WpfGui {
|
||||
int index = VisualizationList.IndexOf(item);
|
||||
VisualizationList.Remove(item);
|
||||
VisualizationList.Insert(index, dlg.NewVis);
|
||||
visualizationGrid.SelectedIndex = index;
|
||||
}
|
||||
|
||||
private void RemoveButton_Click(object sender, RoutedEventArgs e) {
|
||||
Visualization item = (Visualization)visualizationGrid.SelectedItem;
|
||||
int index = VisualizationList.IndexOf(item);
|
||||
VisualizationList.Remove(item);
|
||||
if (index == VisualizationList.Count) {
|
||||
index--;
|
||||
}
|
||||
if (index >= 0) {
|
||||
visualizationGrid.SelectedIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpButton_Click(object sender, RoutedEventArgs e) {
|
||||
|
Loading…
Reference in New Issue
Block a user