Investigate changing sixel to output as part of view render

This commit is contained in:
tznind
2024-09-28 10:22:25 +01:00
parent 9322b9af7c
commit 519d8c0fcd

View File

@@ -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<Rgba32> 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<Rgba32> 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<Rgba32> 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<Rgba32, Attribute> _cache = new ();
private Image<Rgba32> _fullResImage;
public Image<Rgba32> FullResImage;
private Image<Rgba32> _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<Rgba32> 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<Rgba32> 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<Rgba32> 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