diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs index 16c350c24..fc690c3c8 100644 --- a/UICatalog/Scenarios/Images.cs +++ b/UICatalog/Scenarios/Images.cs @@ -23,13 +23,39 @@ public class Images : Scenario private Point _screenLocationForSixel; private string _encodedSixelData; private Window _win; + + /// + /// Number of sixel pixels per row of characters in the console. + /// private NumericUpDown _pxY; + + /// + /// Number of sixel pixels per column of characters in the console + /// private NumericUpDown _pxX; + /// + /// View shown in sixel tab if sixel is supported + /// + private View _sixelSupported; + + /// + /// View shown in sixel tab if sixel is not supported + /// + private View _sixelNotSupported; + + private Tab _tabSixel; + private TabView _tabView; + + /// + /// The view into which the currently opened sixel image is bounded + /// + private View _sixelView; + public override void Main () { Application.Init (); - _win = new Window { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" }; + _win = new() { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" }; bool canTrueColor = Application.Driver?.SupportsTrueColor ?? false; @@ -38,7 +64,7 @@ public class Images : Scenario DisplayText = "Basic" }; - var tabSixel = new Tab + _tabSixel = new() { DisplayText = "Sixel" }; @@ -61,14 +87,17 @@ public class Images : Scenario X = Pos.Right (lblDriverName) + 2, Y = 1, CheckedState = ConsoleDriver.SupportsSixel - ? CheckState.Checked : CheckState.UnChecked, + ? CheckState.Checked + : CheckState.UnChecked, Text = "Supports Sixel" }; cbSupportsSixel.CheckedStateChanging += (s, e) => { ConsoleDriver.SupportsSixel = e.NewValue == CheckState.Checked; + SetupSixelSupported (e.NewValue == CheckState.Checked); }; + _win.Add (cbSupportsSixel); var cbUseTrueColor = new CheckBox @@ -85,38 +114,41 @@ public class Images : Scenario var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" }; _win.Add (btnOpenImage); - var btnStartFire = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 1, Text = "Start Fire" }; - _win.Add (btnStartFire); - - btnStartFire.Accept += BtnStartFireOnAccept; - - var tv = new TabView + _tabView = new() { - Y = Pos.Bottom (cbSupportsSixel), Width = Dim.Fill (), Height = Dim.Fill () + Y = Pos.Bottom (btnOpenImage), Width = Dim.Fill (), Height = Dim.Fill () }; - tv.AddTab (tabBasic, true); - tv.AddTab (tabSixel, false); + _tabView.AddTab (tabBasic, true); + _tabView.AddTab (_tabSixel, false); BuildBasicTab (tabBasic); - BuildSixelTab (tabSixel); + BuildSixelTab (); + + SetupSixelSupported (cbSupportsSixel.CheckedState == CheckState.Checked); btnOpenImage.Accept += OpenImage; - _win.Add (tv); + _win.Add (_tabView); Application.Run (_win); _win.Dispose (); Application.Shutdown (); } + private void SetupSixelSupported (bool isSupported) + { + _tabSixel.View = isSupported ? _sixelSupported : _sixelNotSupported; + _tabView.SetNeedsDisplay (); + } + private void BtnStartFireOnAccept (object sender, HandledEventArgs e) { - var fire = new DoomFire (_win.Frame.Width * _pxX.Value, _win.Frame.Height * _pxY.Value); var encoder = new SixelEncoder (); encoder.Quantizer.PaletteBuildingAlgorithm = new ConstPalette (fire.Palette); - int counter = 0; + var counter = 0; + Application.AddTimeout ( TimeSpan.FromMilliseconds (30), () => @@ -131,27 +163,31 @@ public class Images : Scenario return true; } - var bmp = fire.GetFirePixels (); + Color [,] bmp = fire.GetFirePixels (); // TODO: Static way of doing this, suboptimal Application.Sixel.Clear (); - Application.Sixel.Add (new SixelToRender - { - SixelData = encoder.EncodeSixel (bmp), - ScreenPosition = new Point (0,0) - }); - _win.SetNeedsDisplay(); + Application.Sixel.Add ( + new() + { + SixelData = encoder.EncodeSixel (bmp), + ScreenPosition = new (0, 0) + }); + + _win.SetNeedsDisplay (); return true; }); } - /// + /// protected override void Dispose (bool disposing) { base.Dispose (disposing); _imageView.Dispose (); + _sixelNotSupported.Dispose (); + _sixelSupported.Dispose (); } private void OpenImage (object sender, HandledEventArgs e) @@ -198,103 +234,138 @@ public class Images : Scenario return; } - _imageView.SetImage (img); Application.Refresh (); } private void BuildBasicTab (Tab tabBasic) { - _imageView = new() + _imageView = new () { Width = Dim.Fill (), Height = Dim.Fill (), CanFocus = true - }; tabBasic.View = _imageView; } - private void BuildSixelTab (Tab tabSixel) + private void BuildSixelTab () { - tabSixel.View = new() + _sixelSupported = new() { Width = Dim.Fill (), Height = Dim.Fill (), CanFocus = true }; - var btnSixel = new Button { X = 0, Y = 0, Text = "Output Sixel", Width = Dim.Auto () }; - tabSixel.View.Add (btnSixel); - - var sixelView = new View + _sixelNotSupported = new() + { + Width = Dim.Fill (), + Height = Dim.Fill (), + CanFocus = true + }; + + _sixelNotSupported.Add ( + new Label + { + Width = Dim.Fill (), + Height = Dim.Fill (), + TextAlignment = Alignment.Center, + Text = "Your driver does not support Sixel image format", + VerticalTextAlignment = Alignment.Center + }); + + _sixelView = new() { - Y = Pos.Bottom (btnSixel), Width = Dim.Percent (50), Height = Dim.Fill (), BorderStyle = LineStyle.Dotted }; - tabSixel.View.Add (sixelView); + _sixelSupported.Add (_sixelView); + + var btnSixel = new Button + { + X = Pos.Right (_sixelView), + Y = 0, + Text = "Output Sixel", Width = Dim.Auto () + }; + btnSixel.Accept += OutputSixelButtonClick; + _sixelSupported.Add (btnSixel); + + var btnStartFire = new Button + { + X = Pos.Right (_sixelView), + Y = Pos.Bottom (btnSixel), + Text = "Start Fire" + }; + btnStartFire.Accept += BtnStartFireOnAccept; + _sixelSupported.Add (btnStartFire); + var lblPxX = new Label { - X = Pos.Right (sixelView), + X = Pos.Right (_sixelView), + Y = Pos.Bottom (btnStartFire) + 1, Text = "Pixels per Col:" }; - _pxX = new NumericUpDown + + _pxX = new() { X = Pos.Right (lblPxX), + Y = Pos.Bottom (btnStartFire) + 1, Value = 10 }; var lblPxY = new Label { X = lblPxX.X, - Y = 1, + Y = Pos.Bottom (_pxX), Text = "Pixels per Row:" }; - _pxY = new NumericUpDown + + _pxY = new() { X = Pos.Right (lblPxY), - Y = 1, + Y = Pos.Bottom (_pxX), Value = 20 }; - tabSixel.View.Add (lblPxX); - tabSixel.View.Add (_pxX); - tabSixel.View.Add (lblPxY); - tabSixel.View.Add (_pxY); + _sixelSupported.Add (lblPxX); + _sixelSupported.Add (_pxX); + _sixelSupported.Add (lblPxY); + _sixelSupported.Add (_pxY); - sixelView.DrawContent += SixelViewOnDrawContent; + _sixelView.DrawContent += SixelViewOnDrawContent; + } + private void OutputSixelButtonClick (object sender, HandledEventArgs e) + { + if (_imageView.FullResImage == null) + { + MessageBox.Query ("No Image Loaded", "You must first open an image. Use the 'Open Image' button above.", "Ok"); + return; + } - btnSixel.Accept += (s, e) => - { + _screenLocationForSixel = _sixelView.FrameToScreen ().Location; - if (_imageView.FullResImage == null) - { - return; - } + _encodedSixelData = GenerateSixelData ( + _imageView.FullResImage, + _sixelView.Frame.Size, + _pxX.Value, + _pxY.Value); - - _screenLocationForSixel = sixelView.FrameToScreen ().Location; - _encodedSixelData = GenerateSixelData( - _imageView.FullResImage, - sixelView.Frame.Size, - _pxX.Value, - _pxY.Value); - - // TODO: Static way of doing this, suboptimal - Application.Sixel.Add (new SixelToRender + // TODO: Static way of doing this, suboptimal + Application.Sixel.Add ( + new() { SixelData = _encodedSixelData, ScreenPosition = _screenLocationForSixel }); - }; } - void SixelViewOnDrawContent (object sender, DrawEventArgs e) + + private void SixelViewOnDrawContent (object sender, DrawEventArgs e) { if (!string.IsNullOrWhiteSpace (_encodedSixelData)) { @@ -308,12 +379,12 @@ public class Images : Scenario } } - public string GenerateSixelData( - Image fullResImage, - Size maxSize, - int pixelsPerCellX, - int pixelsPerCellY - ) + public string GenerateSixelData ( + Image fullResImage, + Size maxSize, + int pixelsPerCellX, + int pixelsPerCellY + ) { var encoder = new SixelEncoder (); @@ -439,8 +510,6 @@ public class Images : Scenario FullResImage = image; SetNeedsDisplay (); } - - } public class PaletteView : View @@ -514,15 +583,12 @@ public class Images : Scenario internal class ConstPalette : IPaletteBuilder { - private readonly List _palette; + private readonly List _palette; public ConstPalette (Color [] palette) { _palette = palette.ToList (); } - /// - public List BuildPalette (List colors, int maxColors) - { - return _palette; - } + /// + public List BuildPalette (List colors, int maxColors) { return _palette; } } public abstract class LabColorDistance : IColorDistance @@ -738,15 +804,14 @@ public class MedianCutPaletteBuilder : IPaletteBuilder } } - public class DoomFire { - private int _width; - private int _height; - private Color [,] _firePixels; + private readonly int _width; + private readonly int _height; + private readonly Color [,] _firePixels; private static Color [] _palette; public Color [] Palette => _palette; - private Random _random = new Random (); + private readonly Random _random = new (); public DoomFire (int width, int height) { @@ -763,30 +828,30 @@ public class DoomFire _palette = new Color [37]; // Using 37 colors as per the original Doom fire palette scale. // First color is transparent black - _palette [0] = new Color (0, 0, 0, 0); // Transparent black (ARGB) + _palette [0] = new (0, 0, 0, 0); // Transparent black (ARGB) // The rest of the palette is fire colors - for (int i = 1; i < 37; i++) + for (var i = 1; i < 37; i++) { - byte r = (byte)Math.Min (255, i * 7); - byte g = (byte)Math.Min (255, i * 5); - byte b = (byte)Math.Min (255, i * 2); - _palette [i] = new Color (r, g, b); // Full opacity + var r = (byte)Math.Min (255, i * 7); + var g = (byte)Math.Min (255, i * 5); + var b = (byte)Math.Min (255, i * 2); + _palette [i] = new (r, g, b); // Full opacity } } public void InitializeFire () { // Set the bottom row to full intensity (simulate the base of the fire). - for (int x = 0; x < _width; x++) + for (var x = 0; x < _width; x++) { _firePixels [x, _height - 1] = _palette [36]; // Max intensity fire. } // Set the rest of the pixels to black (transparent). - for (int y = 0; y < _height - 1; y++) + for (var y = 0; y < _height - 1; y++) { - for (int x = 0; x < _width; x++) + for (var x = 0; x < _width; x++) { _firePixels [x, y] = _palette [0]; // Transparent black } @@ -796,9 +861,9 @@ public class DoomFire public void AdvanceFrame () { // Process every pixel except the bottom row - for (int x = 0; x < _width; x++) + for (var x = 0; x < _width; x++) { - for (int y = 1; y < _height; y++) // Skip the last row (which is always max intensity) + for (var y = 1; y < _height; y++) // Skip the last row (which is always max intensity) { int srcX = x; int srcY = y; @@ -827,9 +892,5 @@ public class DoomFire } } - public Color [,] GetFirePixels () - { - return _firePixels; - } + public Color [,] GetFirePixels () { return _firePixels; } } -