mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-29 09:18:01 +01:00
Investigate changing sixel to output as part of view render
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user