From 519d8c0fcda2efb67dca2132001de1ac3c1989d5 Mon Sep 17 00:00:00 2001 From: tznind Date: Sat, 28 Sep 2024 10:22:25 +0100 Subject: [PATCH] Investigate changing sixel to output as part of view render --- UICatalog/Scenarios/Images.cs | 220 ++++++++++++++++++---------------- 1 file changed, 119 insertions(+), 101 deletions(-) diff --git a/UICatalog/Scenarios/Images.cs b/UICatalog/Scenarios/Images.cs index d8681610b..9669e4c2c 100644 --- a/UICatalog/Scenarios/Images.cs +++ b/UICatalog/Scenarios/Images.cs @@ -20,6 +20,8 @@ namespace UICatalog.Scenarios; public class Images : Scenario { private ImageView _imageView; + private Point _screenLocationForSixel; + private string _encodedSixelData; public override void Main () { @@ -195,27 +197,134 @@ public class Images : Scenario tabSixel.View.Add (lblPxY); tabSixel.View.Add (pxY); + sixelView.DrawContent += SixelViewOnDrawContent; + + btnSixel.Accept += (s, e) => { - _imageView.OutputSixel ( - sixelView.FrameToScreen ().Location, - sixelView.Frame.Size, - pxX.Value, - pxY.Value); + + if (_imageView.FullResImage == null) + { + return; + } + + + _screenLocationForSixel = sixelView.FrameToScreen ().Location; + _encodedSixelData = GenerateSixelData( + _imageView.FullResImage, + sixelView.Frame.Size, + pxX.Value, + pxY.Value); }; } + void SixelViewOnDrawContent (object sender, DrawEventArgs e) + { + if (!string.IsNullOrWhiteSpace (_encodedSixelData)) + { + // Does not work + Application.Driver?.Move (_screenLocationForSixel.X, _screenLocationForSixel.Y); + Application.Driver?.AddStr (_encodedSixelData); + + // Works in NetDriver but results in screen flicker when moving mouse but vanish instantly + // Console.SetCursorPosition (_screenLocationForSixel.X, _screenLocationForSixel.Y); + // Console.Write (_encodedSixelData); + } + } + + public string GenerateSixelData( + Image fullResImage, + Size maxSize, + int pixelsPerCellX, + int pixelsPerCellY + ) + { + var encoder = new SixelEncoder (); + + // Calculate the target size in pixels based on console units + int targetWidthInPixels = maxSize.Width * pixelsPerCellX; + int targetHeightInPixels = maxSize.Height * pixelsPerCellY; + + // Get the original image dimensions + int originalWidth = fullResImage.Width; + int originalHeight = fullResImage.Height; + + // Use the helper function to get the resized dimensions while maintaining the aspect ratio + Size newSize = CalculateAspectRatioFit (originalWidth, originalHeight, targetWidthInPixels, targetHeightInPixels); + + // Resize the image to match the console size + Image resizedImage = fullResImage.Clone (x => x.Resize (newSize.Width, newSize.Height)); + + string encoded = encoder.EncodeSixel (ConvertToColorArray (resizedImage)); + + var pv = new PaletteView (encoder.Quantizer.Palette.ToList ()); + + var dlg = new Dialog + { + Title = "Palette (Esc to close)", + Width = Dim.Fill (2), + Height = Dim.Fill (1) + }; + + var btn = new Button + { + Text = "Ok" + }; + + btn.Accept += (s, e) => Application.RequestStop (); + dlg.Add (pv); + dlg.AddButton (btn); + Application.Run (dlg); + + return encoded; + } + + private Size CalculateAspectRatioFit (int originalWidth, int originalHeight, int targetWidth, int targetHeight) + { + // Calculate the scaling factor for width and height + double widthScale = (double)targetWidth / originalWidth; + double heightScale = (double)targetHeight / originalHeight; + + // Use the smaller scaling factor to maintain the aspect ratio + double scale = Math.Min (widthScale, heightScale); + + // Calculate the new width and height while keeping the aspect ratio + var newWidth = (int)(originalWidth * scale); + var newHeight = (int)(originalHeight * scale); + + // Return the new size as a Size object + return new (newWidth, newHeight); + } + + public static Color [,] ConvertToColorArray (Image image) + { + int width = image.Width; + int height = image.Height; + Color [,] colors = new Color [width, height]; + + // Loop through each pixel and convert Rgba32 to Terminal.Gui color + for (var x = 0; x < width; x++) + { + for (var y = 0; y < height; y++) + { + Rgba32 pixel = image [x, y]; + colors [x, y] = new (pixel.R, pixel.G, pixel.B); // Convert Rgba32 to Terminal.Gui color + } + } + + return colors; + } private class ImageView : View { private readonly ConcurrentDictionary _cache = new (); - private Image _fullResImage; + public Image FullResImage; private Image _matchSize; public override void OnDrawContent (Rectangle bounds) { base.OnDrawContent (bounds); - if (_fullResImage == null) + if (FullResImage == null) { return; } @@ -224,7 +333,7 @@ public class Images : Scenario if (_matchSize == null || bounds.Width != _matchSize.Width || bounds.Height != _matchSize.Height) { // generate one - _matchSize = _fullResImage.Clone (x => x.Resize (bounds.Width, bounds.Height)); + _matchSize = FullResImage.Clone (x => x.Resize (bounds.Width, bounds.Height)); } for (var y = 0; y < bounds.Height; y++) @@ -249,102 +358,11 @@ public class Images : Scenario internal void SetImage (Image image) { - _fullResImage = image; + FullResImage = image; SetNeedsDisplay (); } - public void OutputSixel ( - Point screenPosition, - Size maxSize, - int pixelsPerCellX, - int pixelsPerCellY - ) - { - if (_fullResImage == null) - { - return; - } - - var encoder = new SixelEncoder (); - - // Calculate the target size in pixels based on console units - int targetWidthInPixels = maxSize.Width * pixelsPerCellX; - int targetHeightInPixels = maxSize.Height * pixelsPerCellY; - - // Get the original image dimensions - int originalWidth = _fullResImage.Width; - int originalHeight = _fullResImage.Height; - - // Use the helper function to get the resized dimensions while maintaining the aspect ratio - Size newSize = CalculateAspectRatioFit (originalWidth, originalHeight, targetWidthInPixels, targetHeightInPixels); - - // Resize the image to match the console size - Image resizedImage = _fullResImage.Clone (x => x.Resize (newSize.Width, newSize.Height)); - - string encoded = encoder.EncodeSixel (ConvertToColorArray (resizedImage)); - - var pv = new PaletteView (encoder.Quantizer.Palette.ToList ()); - - var dlg = new Dialog - { - Title = "Palette (Esc to close)", - Width = Dim.Fill (2), - Height = Dim.Fill (1) - }; - - var btn = new Button - { - Text = "Ok" - }; - - btn.Accept += (s, e) => Application.RequestStop (); - dlg.Add (pv); - dlg.AddButton (btn); - Application.Run (dlg); - - Application.Sixel.Add ( - new() - { - ScreenPosition = screenPosition, - SixelData = encoded - }); - } - - private Size CalculateAspectRatioFit (int originalWidth, int originalHeight, int targetWidth, int targetHeight) - { - // Calculate the scaling factor for width and height - double widthScale = (double)targetWidth / originalWidth; - double heightScale = (double)targetHeight / originalHeight; - - // Use the smaller scaling factor to maintain the aspect ratio - double scale = Math.Min (widthScale, heightScale); - - // Calculate the new width and height while keeping the aspect ratio - var newWidth = (int)(originalWidth * scale); - var newHeight = (int)(originalHeight * scale); - - // Return the new size as a Size object - return new (newWidth, newHeight); - } - - public static Color [,] ConvertToColorArray (Image image) - { - int width = image.Width; - int height = image.Height; - Color [,] colors = new Color [width, height]; - - // Loop through each pixel and convert Rgba32 to Terminal.Gui color - for (var x = 0; x < width; x++) - { - for (var y = 0; y < height; y++) - { - Rgba32 pixel = image [x, y]; - colors [x, y] = new (pixel.R, pixel.G, pixel.B); // Convert Rgba32 to Terminal.Gui color - } - } - - return colors; - } + } public class PaletteView : View