1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-11 17:29:29 +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:
Andy McFadden 2022-01-12 11:48:20 -08:00
parent 92f304a030
commit 75ccffe393
6 changed files with 85 additions and 40 deletions

View File

@ -25,6 +25,10 @@ namespace CommonUtil {
/// <remarks>
/// This has only been tested with the GIF images created by the Windows Media GifEncoder,
/// 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>
public class UnpackedGif {
//

View File

@ -308,20 +308,23 @@ namespace PluginCommon {
/// </summary>
int Height { get; }
//void SetPixelIndex(int x, int y, byte colorIndex);
//int GetPixel(int x, int y); // returns ARGB value
/// <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.
/// </summary>
byte[] GetPixels();
/// <summary>
/// Returns the color palette as a series of 32-bit ARGB values. Will be null for
/// direct-color images.
/// Do not modify.
/// Returns the color palette as a series of 32-bit ARGB values. Duplicate entries
/// may have been merged to improve compression.
///
/// Will be null for direct-color images. Do not modify.
/// </summary>
/// <remarks>
/// It's possible, but weird, for the array to have a length of zero.
/// </remarks>
int[] GetPalette();
// TODO(maybe): report pixel aspect ratio?

View File

@ -22,6 +22,9 @@ namespace PluginCommon {
/// <summary>
/// Bitmap with 8-bit palette indices, for use with visualization generators.
/// </summary>
/// <remarks>
/// The bitmap is initially filled with color index 0.
/// </remarks>
[Serializable]
public class VisBitmap8 : IVisualization2d {
public const int MAX_DIMENSION = 4096;
@ -34,7 +37,7 @@ namespace PluginCommon {
private byte[] mData;
private int[] mPalette;
private int mNextColorIdx;
private int mMaxColorIndex;
/// <summary>
@ -51,10 +54,11 @@ namespace PluginCommon {
Height = height;
mData = new byte[width * height];
mPalette = new int[256];
mNextColorIdx = 0;
mPalette = new int[256]; // entries initialize to 0, i.e. transparent black
mMaxColorIndex = 0;
}
//[Obsolete("use GetPixelIndex()")]
public int GetPixel(int x, int y) {
byte pix = mData[x + y * Width];
return mPalette[pix];
@ -75,9 +79,9 @@ namespace PluginCommon {
throw new ArgumentException("Bad x/y: " + x + "," + y + " (width=" + Width +
" height=" + Height + ")");
}
if (colorIndex < 0 || colorIndex >= mNextColorIdx) {
if (colorIndex < 0 || colorIndex >= mMaxColorIndex) {
throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" +
mNextColorIdx + ")");
mMaxColorIndex + ")");
}
mData[x + y * Width] = colorIndex;
}
@ -87,9 +91,9 @@ namespace PluginCommon {
/// </summary>
/// <param name="colorIndex">Color index.</param>
public void SetAllPixelIndices(byte colorIndex) {
if (colorIndex < 0 || colorIndex >= mNextColorIdx) {
if (colorIndex < 0 || colorIndex >= mMaxColorIndex) {
throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" +
mNextColorIdx + ")");
mMaxColorIndex + ")");
}
for (int i = 0; i < mData.Length; i++) {
mData[i] = colorIndex;
@ -98,13 +102,15 @@ namespace PluginCommon {
// IVisualization2d
public byte[] GetPixels() {
// TODO: remap any duplicate colors to reduce size of GIF
return mData;
}
// IVisualization2d
public int[] GetPalette() {
int[] pal = new int[mNextColorIdx];
for (int i = 0; i < mNextColorIdx; i++) {
// TODO: remove any duplicate colors to reduce size of GIF
int[] pal = new int[mMaxColorIndex];
for (int i = 0; i < mMaxColorIndex; i++) {
pal[i] = mPalette[i];
}
return pal;
@ -116,20 +122,21 @@ namespace PluginCommon {
/// effect.
/// </summary>
/// <param name="color">32-bit ARGB color value.</param>
//[Obsolete("use SetColor()")]
public void AddColor(int color) {
if (mNextColorIdx == 256) {
if (mMaxColorIndex == 256) {
Debug.WriteLine("Palette is full");
return;
}
// 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) {
Debug.WriteLine("Color " + color.ToString("x6") +
" already exists in palette (" + i + ")");
return;
}
}
mPalette[mNextColorIdx++] = color;
mPalette[mMaxColorIndex++] = color;
}
/// <summary>
@ -140,10 +147,33 @@ namespace PluginCommon {
/// <param name="r">Red value.</param>
/// <param name="g">Green value.</param>
/// <param name="b">Blue value.</param>
//[Obsolete("use SetColor()")]
public void AddColor(byte a, byte r, byte g, byte 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>
/// Draws an 8x8 character cell on the bitmap.
/// </summary>

View File

@ -886,7 +886,7 @@ namespace SourceGen {
#if false
// try feeding the animated GIF into our GIF unpacker
using (MemoryStream ms = new MemoryStream()) {
encoder.Save(ms);
encoder.Save(ms, out dispWidth, out dispHeight);
Debug.WriteLine("TESTING");
UnpackedGif anim = UnpackedGif.Create(ms.GetBuffer());
anim.DebugDump();

View File

@ -276,29 +276,29 @@ namespace RuntimeData.Nintendo {
}
private void SetPalette(VisBitmap8 vb, Palette pal) {
vb.AddColor(0, 0, 0, 0); // 0=transparent
vb.AddColor(0xff, 0x01, 0x01, 0x01); // 1=near black (so VB doesn't uniquify)
vb.AddColor(0xff, 0xfe, 0xfe, 0xfe); // 2=near white
vb.SetColor((byte)Color.Transparent, 0x00, 0x00, 0x00, 0x00);
vb.SetColor((byte)Color.Black, 0xff, 0x00, 0x00, 0x00);
vb.SetColor((byte)Color.White, 0xff, 0xff, 0xff, 0xff);
switch (pal) {
case Palette.Greyscale:
default:
vb.AddColor(0xff, 0x00, 0x00, 0x00); // black
vb.AddColor(0xff, 0x80, 0x80, 0x80); // dark grey
vb.AddColor(0xff, 0xb0, 0xb0, 0xb0); // medium grey
vb.AddColor(0xff, 0xe0, 0xe0, 0xe0); // light grey
vb.SetColor((byte)Color.Color0, 0xff, 0x00, 0x00, 0x00); // black
vb.SetColor((byte)Color.Color1, 0xff, 0x80, 0x80, 0x80); // dark grey
vb.SetColor((byte)Color.Color2, 0xff, 0xb0, 0xb0, 0xb0); // medium grey
vb.SetColor((byte)Color.Color3, 0xff, 0xe0, 0xe0, 0xe0); // light grey
break;
case Palette.Pinkish:
vb.AddColor(0xff, 0x49, 0x99, 0xfe); // sky blue
vb.AddColor(0xff, 0xff, 0xbd, 0xaf); // pinkish
vb.AddColor(0xff, 0xcd, 0x50, 0x00); // dark orange
vb.AddColor(0xff, 0x00, 0x00, 0x00); // black
vb.SetColor((byte)Color.Color0, 0xff, 0x49, 0x99, 0xfe); // sky blue
vb.SetColor((byte)Color.Color1, 0xff, 0xff, 0xbd, 0xaf); // pinkish
vb.SetColor((byte)Color.Color2, 0xff, 0xcd, 0x50, 0x00); // dark orange
vb.SetColor((byte)Color.Color3, 0xff, 0x00, 0x00, 0x00); // black
break;
case Palette.Greenish:
vb.AddColor(0xff, 0x49, 0x99, 0xfe); // sky blue
vb.AddColor(0xff, 0x00, 0xa4, 0x00); // medium green
vb.AddColor(0xff, 0xfc, 0xfc, 0xfc); // near white
vb.AddColor(0xff, 0xff, 0x99, 0x2b); // orange
vb.SetColor((byte)Color.Color0, 0xff, 0x49, 0x99, 0xfe); // sky blue
vb.SetColor((byte)Color.Color1, 0xff, 0x00, 0xa4, 0x00); // medium green
vb.SetColor((byte)Color.Color2, 0xff, 0xfc, 0xfc, 0xfc); // near white
vb.SetColor((byte)Color.Color3, 0xff, 0xff, 0x99, 0x2b); // orange
break;
}
}

View File

@ -15,19 +15,17 @@
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using CommonWPF;
using Microsoft.Win32;
using CommonUtil;
using CommonWPF;
namespace SourceGen.WpfGui {
/// <summary>
/// Export an image from the visualization editor.
@ -157,9 +155,19 @@ namespace SourceGen.WpfGui {
GifBitmapEncoder encoder = new GifBitmapEncoder();
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)) {
encoder.Save(stream);
}
#endif
}
} catch (Exception ex) {
// Error handling is a little sloppy, but this shouldn't fail often.