mirror of
https://github.com/fadden/6502bench.git
synced 2025-08-10 12:24:58 +00:00
Update visualization bitmap API
The existing API was better suited to direct color than indexed color. The NES visualizer was using a slightly silly hack to avoid duplicate colors; this has been removed.
This commit is contained in:
@@ -25,6 +25,10 @@ namespace CommonUtil {
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This has only been tested with the GIF images created by the Windows Media GifEncoder,
|
/// This has only been tested with the GIF images created by the Windows Media GifEncoder,
|
||||||
/// which are GIF89a with no global color table.
|
/// which are GIF89a with no global color table.
|
||||||
|
///
|
||||||
|
/// References:
|
||||||
|
/// https://www.w3.org/Graphics/GIF/spec-gif87.txt
|
||||||
|
/// https://www.w3.org/Graphics/GIF/spec-gif89a.txt
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class UnpackedGif {
|
public class UnpackedGif {
|
||||||
//
|
//
|
||||||
|
@@ -308,20 +308,23 @@ namespace PluginCommon {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
int Height { get; }
|
int Height { get; }
|
||||||
|
|
||||||
//void SetPixelIndex(int x, int y, byte colorIndex);
|
|
||||||
//int GetPixel(int x, int y); // returns ARGB value
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a densely-packed array of color indices or ARGB values.
|
/// Returns a densely-packed array of color indices or ARGB values. Color index
|
||||||
|
/// values may be remapped from what was originally set to improve compression.
|
||||||
|
///
|
||||||
/// Do not modify.
|
/// Do not modify.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte[] GetPixels();
|
byte[] GetPixels();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the color palette as a series of 32-bit ARGB values. Will be null for
|
/// Returns the color palette as a series of 32-bit ARGB values. Duplicate entries
|
||||||
/// direct-color images.
|
/// may have been merged to improve compression.
|
||||||
/// Do not modify.
|
///
|
||||||
|
/// Will be null for direct-color images. Do not modify.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// It's possible, but weird, for the array to have a length of zero.
|
||||||
|
/// </remarks>
|
||||||
int[] GetPalette();
|
int[] GetPalette();
|
||||||
|
|
||||||
// TODO(maybe): report pixel aspect ratio?
|
// TODO(maybe): report pixel aspect ratio?
|
||||||
|
@@ -22,6 +22,9 @@ namespace PluginCommon {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bitmap with 8-bit palette indices, for use with visualization generators.
|
/// Bitmap with 8-bit palette indices, for use with visualization generators.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The bitmap is initially filled with color index 0.
|
||||||
|
/// </remarks>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class VisBitmap8 : IVisualization2d {
|
public class VisBitmap8 : IVisualization2d {
|
||||||
public const int MAX_DIMENSION = 4096;
|
public const int MAX_DIMENSION = 4096;
|
||||||
@@ -34,7 +37,7 @@ namespace PluginCommon {
|
|||||||
|
|
||||||
private byte[] mData;
|
private byte[] mData;
|
||||||
private int[] mPalette;
|
private int[] mPalette;
|
||||||
private int mNextColorIdx;
|
private int mMaxColorIndex;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -51,10 +54,11 @@ namespace PluginCommon {
|
|||||||
Height = height;
|
Height = height;
|
||||||
|
|
||||||
mData = new byte[width * height];
|
mData = new byte[width * height];
|
||||||
mPalette = new int[256];
|
mPalette = new int[256]; // entries initialize to 0, i.e. transparent black
|
||||||
mNextColorIdx = 0;
|
mMaxColorIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//[Obsolete("use GetPixelIndex()")]
|
||||||
public int GetPixel(int x, int y) {
|
public int GetPixel(int x, int y) {
|
||||||
byte pix = mData[x + y * Width];
|
byte pix = mData[x + y * Width];
|
||||||
return mPalette[pix];
|
return mPalette[pix];
|
||||||
@@ -75,9 +79,9 @@ namespace PluginCommon {
|
|||||||
throw new ArgumentException("Bad x/y: " + x + "," + y + " (width=" + Width +
|
throw new ArgumentException("Bad x/y: " + x + "," + y + " (width=" + Width +
|
||||||
" height=" + Height + ")");
|
" height=" + Height + ")");
|
||||||
}
|
}
|
||||||
if (colorIndex < 0 || colorIndex >= mNextColorIdx) {
|
if (colorIndex < 0 || colorIndex >= mMaxColorIndex) {
|
||||||
throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" +
|
throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" +
|
||||||
mNextColorIdx + ")");
|
mMaxColorIndex + ")");
|
||||||
}
|
}
|
||||||
mData[x + y * Width] = colorIndex;
|
mData[x + y * Width] = colorIndex;
|
||||||
}
|
}
|
||||||
@@ -87,9 +91,9 @@ namespace PluginCommon {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="colorIndex">Color index.</param>
|
/// <param name="colorIndex">Color index.</param>
|
||||||
public void SetAllPixelIndices(byte colorIndex) {
|
public void SetAllPixelIndices(byte colorIndex) {
|
||||||
if (colorIndex < 0 || colorIndex >= mNextColorIdx) {
|
if (colorIndex < 0 || colorIndex >= mMaxColorIndex) {
|
||||||
throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" +
|
throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" +
|
||||||
mNextColorIdx + ")");
|
mMaxColorIndex + ")");
|
||||||
}
|
}
|
||||||
for (int i = 0; i < mData.Length; i++) {
|
for (int i = 0; i < mData.Length; i++) {
|
||||||
mData[i] = colorIndex;
|
mData[i] = colorIndex;
|
||||||
@@ -98,13 +102,15 @@ namespace PluginCommon {
|
|||||||
|
|
||||||
// IVisualization2d
|
// IVisualization2d
|
||||||
public byte[] GetPixels() {
|
public byte[] GetPixels() {
|
||||||
|
// TODO: remap any duplicate colors to reduce size of GIF
|
||||||
return mData;
|
return mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// IVisualization2d
|
// IVisualization2d
|
||||||
public int[] GetPalette() {
|
public int[] GetPalette() {
|
||||||
int[] pal = new int[mNextColorIdx];
|
// TODO: remove any duplicate colors to reduce size of GIF
|
||||||
for (int i = 0; i < mNextColorIdx; i++) {
|
int[] pal = new int[mMaxColorIndex];
|
||||||
|
for (int i = 0; i < mMaxColorIndex; i++) {
|
||||||
pal[i] = mPalette[i];
|
pal[i] = mPalette[i];
|
||||||
}
|
}
|
||||||
return pal;
|
return pal;
|
||||||
@@ -116,20 +122,21 @@ namespace PluginCommon {
|
|||||||
/// effect.
|
/// effect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="color">32-bit ARGB color value.</param>
|
/// <param name="color">32-bit ARGB color value.</param>
|
||||||
|
//[Obsolete("use SetColor()")]
|
||||||
public void AddColor(int color) {
|
public void AddColor(int color) {
|
||||||
if (mNextColorIdx == 256) {
|
if (mMaxColorIndex == 256) {
|
||||||
Debug.WriteLine("Palette is full");
|
Debug.WriteLine("Palette is full");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// I'm expecting palettes to only have a few colors, so O(n^2) is fine for now.
|
// I'm expecting palettes to only have a few colors, so O(n^2) is fine for now.
|
||||||
for (int i = 0; i < mNextColorIdx; i++) {
|
for (int i = 0; i < mMaxColorIndex; i++) {
|
||||||
if (mPalette[i] == color) {
|
if (mPalette[i] == color) {
|
||||||
Debug.WriteLine("Color " + color.ToString("x6") +
|
Debug.WriteLine("Color " + color.ToString("x6") +
|
||||||
" already exists in palette (" + i + ")");
|
" already exists in palette (" + i + ")");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mPalette[mNextColorIdx++] = color;
|
mPalette[mMaxColorIndex++] = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -140,10 +147,33 @@ namespace PluginCommon {
|
|||||||
/// <param name="r">Red value.</param>
|
/// <param name="r">Red value.</param>
|
||||||
/// <param name="g">Green value.</param>
|
/// <param name="g">Green value.</param>
|
||||||
/// <param name="b">Blue value.</param>
|
/// <param name="b">Blue value.</param>
|
||||||
|
//[Obsolete("use SetColor()")]
|
||||||
public void AddColor(byte a, byte r, byte g, byte b) {
|
public void AddColor(byte a, byte r, byte g, byte b) {
|
||||||
AddColor(Util.MakeARGB(a, r, g, b));
|
AddColor(Util.MakeARGB(a, r, g, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the Nth entry in the color palette.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The size of the color palette will expand to hold the largest index. For best
|
||||||
|
/// results, start with index 0 and count up, avoiding duplicates.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="index">Palette index, 0-255.</param>
|
||||||
|
/// <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 SetColor(int index, byte a, byte r, byte g, byte b) {
|
||||||
|
if (index < 0 || index > 255) {
|
||||||
|
throw new ArgumentException("Invalid index: " + index);
|
||||||
|
}
|
||||||
|
mPalette[index] = Util.MakeARGB(a, r, g, b);
|
||||||
|
if (index >= mMaxColorIndex) {
|
||||||
|
mMaxColorIndex = index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws an 8x8 character cell on the bitmap.
|
/// Draws an 8x8 character cell on the bitmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -886,7 +886,7 @@ namespace SourceGen {
|
|||||||
#if false
|
#if false
|
||||||
// try feeding the animated GIF into our GIF unpacker
|
// try feeding the animated GIF into our GIF unpacker
|
||||||
using (MemoryStream ms = new MemoryStream()) {
|
using (MemoryStream ms = new MemoryStream()) {
|
||||||
encoder.Save(ms);
|
encoder.Save(ms, out dispWidth, out dispHeight);
|
||||||
Debug.WriteLine("TESTING");
|
Debug.WriteLine("TESTING");
|
||||||
UnpackedGif anim = UnpackedGif.Create(ms.GetBuffer());
|
UnpackedGif anim = UnpackedGif.Create(ms.GetBuffer());
|
||||||
anim.DebugDump();
|
anim.DebugDump();
|
||||||
|
@@ -276,29 +276,29 @@ namespace RuntimeData.Nintendo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void SetPalette(VisBitmap8 vb, Palette pal) {
|
private void SetPalette(VisBitmap8 vb, Palette pal) {
|
||||||
vb.AddColor(0, 0, 0, 0); // 0=transparent
|
vb.SetColor((byte)Color.Transparent, 0x00, 0x00, 0x00, 0x00);
|
||||||
vb.AddColor(0xff, 0x01, 0x01, 0x01); // 1=near black (so VB doesn't uniquify)
|
vb.SetColor((byte)Color.Black, 0xff, 0x00, 0x00, 0x00);
|
||||||
vb.AddColor(0xff, 0xfe, 0xfe, 0xfe); // 2=near white
|
vb.SetColor((byte)Color.White, 0xff, 0xff, 0xff, 0xff);
|
||||||
|
|
||||||
switch (pal) {
|
switch (pal) {
|
||||||
case Palette.Greyscale:
|
case Palette.Greyscale:
|
||||||
default:
|
default:
|
||||||
vb.AddColor(0xff, 0x00, 0x00, 0x00); // black
|
vb.SetColor((byte)Color.Color0, 0xff, 0x00, 0x00, 0x00); // black
|
||||||
vb.AddColor(0xff, 0x80, 0x80, 0x80); // dark grey
|
vb.SetColor((byte)Color.Color1, 0xff, 0x80, 0x80, 0x80); // dark grey
|
||||||
vb.AddColor(0xff, 0xb0, 0xb0, 0xb0); // medium grey
|
vb.SetColor((byte)Color.Color2, 0xff, 0xb0, 0xb0, 0xb0); // medium grey
|
||||||
vb.AddColor(0xff, 0xe0, 0xe0, 0xe0); // light grey
|
vb.SetColor((byte)Color.Color3, 0xff, 0xe0, 0xe0, 0xe0); // light grey
|
||||||
break;
|
break;
|
||||||
case Palette.Pinkish:
|
case Palette.Pinkish:
|
||||||
vb.AddColor(0xff, 0x49, 0x99, 0xfe); // sky blue
|
vb.SetColor((byte)Color.Color0, 0xff, 0x49, 0x99, 0xfe); // sky blue
|
||||||
vb.AddColor(0xff, 0xff, 0xbd, 0xaf); // pinkish
|
vb.SetColor((byte)Color.Color1, 0xff, 0xff, 0xbd, 0xaf); // pinkish
|
||||||
vb.AddColor(0xff, 0xcd, 0x50, 0x00); // dark orange
|
vb.SetColor((byte)Color.Color2, 0xff, 0xcd, 0x50, 0x00); // dark orange
|
||||||
vb.AddColor(0xff, 0x00, 0x00, 0x00); // black
|
vb.SetColor((byte)Color.Color3, 0xff, 0x00, 0x00, 0x00); // black
|
||||||
break;
|
break;
|
||||||
case Palette.Greenish:
|
case Palette.Greenish:
|
||||||
vb.AddColor(0xff, 0x49, 0x99, 0xfe); // sky blue
|
vb.SetColor((byte)Color.Color0, 0xff, 0x49, 0x99, 0xfe); // sky blue
|
||||||
vb.AddColor(0xff, 0x00, 0xa4, 0x00); // medium green
|
vb.SetColor((byte)Color.Color1, 0xff, 0x00, 0xa4, 0x00); // medium green
|
||||||
vb.AddColor(0xff, 0xfc, 0xfc, 0xfc); // near white
|
vb.SetColor((byte)Color.Color2, 0xff, 0xfc, 0xfc, 0xfc); // near white
|
||||||
vb.AddColor(0xff, 0xff, 0x99, 0x2b); // orange
|
vb.SetColor((byte)Color.Color3, 0xff, 0xff, 0x99, 0x2b); // orange
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,19 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
|
|
||||||
using CommonWPF;
|
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
using CommonUtil;
|
||||||
|
using CommonWPF;
|
||||||
|
|
||||||
namespace SourceGen.WpfGui {
|
namespace SourceGen.WpfGui {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Export an image from the visualization editor.
|
/// Export an image from the visualization editor.
|
||||||
@@ -157,9 +155,19 @@ namespace SourceGen.WpfGui {
|
|||||||
GifBitmapEncoder encoder = new GifBitmapEncoder();
|
GifBitmapEncoder encoder = new GifBitmapEncoder();
|
||||||
encoder.Frames.Add(BitmapFrame.Create(outImage));
|
encoder.Frames.Add(BitmapFrame.Create(outImage));
|
||||||
|
|
||||||
|
#if false
|
||||||
|
// try feeding the GIF into our GIF unpacker
|
||||||
|
using (MemoryStream ms = new MemoryStream()) {
|
||||||
|
encoder.Save(ms);
|
||||||
|
Debug.WriteLine("TESTING");
|
||||||
|
UnpackedGif anim = UnpackedGif.Create(ms.GetBuffer());
|
||||||
|
anim.DebugDump();
|
||||||
|
}
|
||||||
|
#else
|
||||||
using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
|
using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
|
||||||
encoder.Save(stream);
|
encoder.Save(stream);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// Error handling is a little sloppy, but this shouldn't fail often.
|
// Error handling is a little sloppy, but this shouldn't fail often.
|
||||||
|
Reference in New Issue
Block a user