From 9244ceda7c0c3c0bad11f8e1c4b4ed1d877aa4f3 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Wed, 27 Nov 2019 17:12:26 -0800 Subject: [PATCH] More progress on visualization Added some rudimentary bitmap creation code. Got a test pattern generated by the plugin to display in the app. (Most of the time required for this was spent figuring out how bitmaps are handled in WPF.) --- PluginCommon/Interfaces.cs | 9 ++- PluginCommon/Util.cs | 13 ++++ PluginCommon/VisBitmap8.cs | 88 ++++++++++++++++++++++ SourceGen/RuntimeData/Apple/VisHiRes.cs | 14 +++- SourceGen/Visualization.cs | 27 ++++++- SourceGen/WpfGui/EditVisualization.xaml | 8 +- SourceGen/WpfGui/EditVisualization.xaml.cs | 63 +++++++++++----- 7 files changed, 195 insertions(+), 27 deletions(-) create mode 100644 PluginCommon/VisBitmap8.cs diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs index ac3aecb..a4c9e12 100644 --- a/PluginCommon/Interfaces.cs +++ b/PluginCommon/Interfaces.cs @@ -216,10 +216,15 @@ namespace PluginCommon { /// without scaling or filtering. /// public interface IVisualization2d { - int GetWidth(); - int GetHeight(); + int Width { get; } + int Height { get; } + + 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 + // TODO(maybe): pixel aspect ratio } diff --git a/PluginCommon/Util.cs b/PluginCommon/Util.cs index d8f2161..3a67915 100644 --- a/PluginCommon/Util.cs +++ b/PluginCommon/Util.cs @@ -84,5 +84,18 @@ namespace PluginCommon { appRef.SetInlineDataFormat(brkOffset + 1, 1, DataType.NumericLE, subType, label); } } + + /// + /// Converts four 8-bit color values to a single 32-bit ARGB value. Values should + /// not be pre-multiplied. + /// + /// Alpha channel. + /// Red. + /// Green. + /// Blue. + /// Combined value. + public static int MakeARGB(int a, int r, int g, int b) { + return (a << 24) | (r << 16) | (g << 8) | b; + } } } diff --git a/PluginCommon/VisBitmap8.cs b/PluginCommon/VisBitmap8.cs new file mode 100644 index 0000000..711c17b --- /dev/null +++ b/PluginCommon/VisBitmap8.cs @@ -0,0 +1,88 @@ +/* + * Copyright 2019 faddenSoft + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; + +namespace PluginCommon { + /// + /// Bitmap with 8-bit palette indices, for use with plugin visualizers. + /// + [Serializable] + public class VisBitmap8 : IVisualization2d { + public const int MAX_DIMENSION = 4096; + + public int Width { get; private set; } + public int Height { get; private set; } + + private byte[] mData; + private int[] mPalette; + private int mNextColor; + + public VisBitmap8(int width, int height) { + Debug.Assert(width > 0 && width <= MAX_DIMENSION); + Debug.Assert(height > 0 && height <= MAX_DIMENSION); + + Width = width; + Height = height; + + mData = new byte[width * height]; + mPalette = new int[256]; + mNextColor = 0; + } + + // IVisualization2d + public int GetPixel(int x, int y) { + byte pix = mData[x + y * Width]; + return mPalette[pix]; + } + + public void SetPixelIndex(int x, int y, byte colorIndex) { + if (x < 0 || x >= Width || y < 0 || y >= Width) { + throw new ArgumentException("Bad x/y: " + x + "," + y + " (width=" + Width + + " height=" + Height + ")"); + } + if (colorIndex < 0 || colorIndex >= mNextColor) { + throw new ArgumentException("Bad color: " + colorIndex + " (nextCol=" + + mNextColor + ")"); + } + mData[x + y * Width] = colorIndex; + } + + // IVisualization2d + public byte[] GetPixels() { + return mData; + } + + // IVisualization2d + public int[] GetPalette() { + int[] pal = new int[mNextColor]; + for (int i = 0; i < mNextColor; i++) { + pal[i] = mPalette[i]; + } + return pal; + } + + public void AddColor(int color) { + if (mNextColor == 256) { + Debug.WriteLine("Palette is full"); + return; + } + mPalette[mNextColor++] = color; + } + } +} diff --git a/SourceGen/RuntimeData/Apple/VisHiRes.cs b/SourceGen/RuntimeData/Apple/VisHiRes.cs index bc2d205..3b187b9 100644 --- a/SourceGen/RuntimeData/Apple/VisHiRes.cs +++ b/SourceGen/RuntimeData/Apple/VisHiRes.cs @@ -62,7 +62,7 @@ namespace RuntimeData.Apple { new VisParamDescr("Item width (in bytes)", "itemByteWidth", typeof(int), 1, 40, 0, 1), new VisParamDescr("Item height", - "itemHeight", typeof(int), 8, 192, 0, 1), + "itemHeight", typeof(int), 1, 192, 0, 8), new VisParamDescr("Number of items", "count", typeof(int), 1, 256, 0, 1), }), @@ -83,7 +83,17 @@ namespace RuntimeData.Apple { // IPlugin_Visualizer public IVisualization2d Generate2d(VisDescr descr, Dictionary parms) { - throw new NotImplementedException(); + // TODO: replace with actual + VisBitmap8 vb = new VisBitmap8(16, 16); + vb.AddColor(Util.MakeARGB(0xff, 0x40, 0x40, 0x40)); + vb.AddColor(Util.MakeARGB(0xff, 0xff, 0x00, 0x00)); + vb.AddColor(Util.MakeARGB(0xff, 0x00, 0xff, 0x80)); + + for (int i = 0; i < 16; i++) { + vb.SetPixelIndex(i, i, 1); + vb.SetPixelIndex(15 - i, i, 2); + } + return vb; } } } diff --git a/SourceGen/Visualization.cs b/SourceGen/Visualization.cs index aaed483..4968aa8 100644 --- a/SourceGen/Visualization.cs +++ b/SourceGen/Visualization.cs @@ -16,7 +16,8 @@ using System; using System.Collections.Generic; using System.Text; - +using System.Windows.Media; +using System.Windows.Media.Imaging; using CommonUtil; using PluginCommon; @@ -53,6 +54,30 @@ namespace SourceGen { VisGenParams = visGenParams; } + public static BitmapSource CreateBitmapSource(IVisualization2d vis2d) { + // Create indexed color palette. + int[] intPal = vis2d.GetPalette(); + List colors = new List(intPal.Length); + foreach (int argb in intPal) { + Color col = Color.FromArgb((byte)(argb >> 24), (byte)(argb >> 16), + (byte)(argb >> 8), (byte)argb); + colors.Add(col); + } + BitmapPalette palette = new BitmapPalette(colors); + + // indexed-color; see https://stackoverflow.com/a/15272528/294248 for direct color + BitmapSource image = BitmapSource.Create( + vis2d.Width, + vis2d.Height, + 96.0, + 96.0, + PixelFormats.Indexed8, + palette, + vis2d.GetPixels(), + vis2d.Width); + + return image; + } /// /// Finds a plugin that provides the named visualization generator. diff --git a/SourceGen/WpfGui/EditVisualization.xaml b/SourceGen/WpfGui/EditVisualization.xaml index 7ac24e0..edbf83e 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml +++ b/SourceGen/WpfGui/EditVisualization.xaml @@ -110,7 +110,7 @@ limitations under the License. - + + + diff --git a/SourceGen/WpfGui/EditVisualization.xaml.cs b/SourceGen/WpfGui/EditVisualization.xaml.cs index c77ee5a..396e4b9 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml.cs +++ b/SourceGen/WpfGui/EditVisualization.xaml.cs @@ -25,7 +25,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media; - +using System.Windows.Media.Imaging; using Asm65; using PluginCommon; @@ -74,17 +74,26 @@ namespace SourceGen.WpfGui { } private Brush mTagLabelBrush; + public class VisualizationItem { + public IPlugin_Visualizer Plugin { get; private set; } + public VisDescr VisDescriptor { get; private set; } + public VisualizationItem(IPlugin_Visualizer plugin, VisDescr descr) { + Plugin = plugin; + VisDescriptor = descr; + } + } + + /// + /// List of visualizers, for combo box. + /// + public List VisualizationList { get; private set; } + /// /// ItemsSource for the ItemsControl with the generated parameter controls. /// public ObservableCollection ParameterList { get; private set; } = new ObservableCollection(); - /// - /// List of visualizers, for combo box. - /// - public List VisualizationList { get; private set; } - // INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { @@ -114,7 +123,7 @@ namespace SourceGen.WpfGui { } int visSelection = 0; - VisualizationList = new List(); + VisualizationList = new List(); List plugins = proj.GetActivePlugins(); foreach (IPlugin chkPlug in plugins) { if (!(chkPlug is IPlugin_Visualizer)) { @@ -125,7 +134,7 @@ namespace SourceGen.WpfGui { if (vis != null && vis.VisGenIdent == descr.Ident) { visSelection = VisualizationList.Count; } - VisualizationList.Add(descr); + VisualizationList.Add(new VisualizationItem(vplug, descr)); } } @@ -180,6 +189,15 @@ namespace SourceGen.WpfGui { } private void OkButton_Click(object sender, RoutedEventArgs e) { + VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem; + Debug.Assert(item != null); + Dictionary valueDict = CreateVisGenParams(); + NewVis = new Visualization(TagString, item.VisDescriptor.Ident, valueDict); + + DialogResult = true; + } + + private Dictionary CreateVisGenParams() { // Generate value dictionary. Dictionary valueDict = new Dictionary(ParameterList.Count); @@ -211,11 +229,7 @@ namespace SourceGen.WpfGui { } } - VisDescr item = (VisDescr)visComboBox.SelectedItem; - Debug.Assert(item != null); - - NewVis = new Visualization(TagString, item.Ident, valueDict); - DialogResult = true; + return valueDict; } private bool ParseInt(string str, VisParamDescr.SpecialMode special, out int intVal) { @@ -247,7 +261,7 @@ namespace SourceGen.WpfGui { } } - private void CheckValid() { + private void UpdateControls() { IsValid = true; string trimTag = TagString.Trim(); @@ -289,31 +303,40 @@ namespace SourceGen.WpfGui { Debug.Assert(false); } } + + if (!IsValid) { + previewImage.Source = new BitmapImage(new Uri("pack://application:,,,/Res/Logo.png")); + } else { + VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem; + IVisualization2d vis2d = item.Plugin.Generate2d(item.VisDescriptor, + CreateVisGenParams()); + previewImage.Source = Visualization.CreateBitmapSource(vis2d); + } } private void VisComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { - VisDescr item = (VisDescr)visComboBox.SelectedItem; + VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem; if (item == null) { Debug.Assert(false); // not expected return; } - Debug.WriteLine("VisComboBox sel change: " + item.Ident); - GenerateParamControls(item); - CheckValid(); + Debug.WriteLine("VisComboBox sel change: " + item.VisDescriptor.Ident); + GenerateParamControls(item.VisDescriptor); + UpdateControls(); } private void TextBox_TextChanged(object sender, TextChangedEventArgs e) { TextBox src = (TextBox)sender; ParameterValue pv = (ParameterValue)src.DataContext; //Debug.WriteLine("TEXT CHANGE " + pv + ": " + src.Text); - CheckValid(); + UpdateControls(); } private void CheckBox_Changed(object sender, RoutedEventArgs e) { CheckBox src = (CheckBox)sender; ParameterValue pv = (ParameterValue)src.DataContext; //Debug.WriteLine("CHECK CHANGE" + pv); - CheckValid(); + UpdateControls(); } }