mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-02 01:03:29 +01:00
Output at specific position
This commit is contained in:
@@ -27,5 +27,5 @@ public static partial class Application // Driver abstractions
|
||||
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
|
||||
public static string ForceDriver { get; set; } = string.Empty;
|
||||
|
||||
public static string Sixel;
|
||||
public static List<SixelToRender> Sixel = new List<SixelToRender> ();
|
||||
}
|
||||
|
||||
@@ -1021,10 +1021,13 @@ internal class NetDriver : ConsoleDriver
|
||||
Console.Write (output);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(Application.Sixel))
|
||||
foreach (var s in Application.Sixel)
|
||||
{
|
||||
Console.SetCursorPosition (0,0);
|
||||
Console.Write (Application.Sixel);
|
||||
if (!string.IsNullOrWhiteSpace (s.SixelData))
|
||||
{
|
||||
SetCursorPosition (s.ScreenPosition.X, s.ScreenPosition.Y);
|
||||
Console.Write (s.SixelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ using Terminal.Gui;
|
||||
|
||||
namespace Terminal.Gui;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Encodes a images into the sixel console image output format.
|
||||
/// </summary>
|
||||
|
||||
19
Terminal.Gui/Drawing/SixelToRender.cs
Normal file
19
Terminal.Gui/Drawing/SixelToRender.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>
|
||||
/// Describes a request to render a given <see cref="SixelData"/> at a given <see cref="ScreenPosition"/>.
|
||||
/// Requires that the terminal and <see cref="ConsoleDriver"/> both support sixel.
|
||||
/// </summary>
|
||||
public class SixelToRender
|
||||
{
|
||||
/// <summary>
|
||||
/// gets or sets the encoded sixel data. Use <see cref="SixelEncoder"/> to convert bitmaps
|
||||
/// into encoded sixel data.
|
||||
/// </summary>
|
||||
public string SixelData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// gets or sets where to move the cursor to before outputting the <see cref="SixelData"/>.
|
||||
/// </summary>
|
||||
public Point ScreenPosition { get; set; }
|
||||
}
|
||||
@@ -20,6 +20,7 @@ namespace UICatalog.Scenarios;
|
||||
public class Images : Scenario
|
||||
{
|
||||
private ImageView _imageView;
|
||||
|
||||
public override void Main ()
|
||||
{
|
||||
Application.Init ();
|
||||
@@ -27,18 +28,16 @@ public class Images : Scenario
|
||||
|
||||
bool canTrueColor = Application.Driver?.SupportsTrueColor ?? false;
|
||||
|
||||
|
||||
var tabBasic = new Tab ()
|
||||
var tabBasic = new Tab
|
||||
{
|
||||
DisplayText = "Basic"
|
||||
};
|
||||
|
||||
var tabSixel = new Tab ()
|
||||
var tabSixel = new Tab
|
||||
{
|
||||
DisplayText = "Sixel"
|
||||
};
|
||||
|
||||
|
||||
var lblDriverName = new Label { X = 0, Y = 0, Text = $"Driver is {Application.Driver?.GetType ().Name}" };
|
||||
win.Add (lblDriverName);
|
||||
|
||||
@@ -66,7 +65,6 @@ public class Images : Scenario
|
||||
var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" };
|
||||
win.Add (btnOpenImage);
|
||||
|
||||
|
||||
var tv = new TabView
|
||||
{
|
||||
Y = Pos.Bottom (lblDriverName), Width = Dim.Fill (), Height = Dim.Fill ()
|
||||
@@ -136,10 +134,10 @@ public class Images : Scenario
|
||||
|
||||
private void BuildBasicTab (Tab tabBasic)
|
||||
{
|
||||
_imageView = new ImageView
|
||||
_imageView = new()
|
||||
{
|
||||
Width = Dim.Fill(),
|
||||
Height = Dim.Fill()
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
|
||||
tabBasic.View = _imageView;
|
||||
@@ -147,14 +145,64 @@ public class Images : Scenario
|
||||
|
||||
private void BuildSixelTab (Tab tabSixel)
|
||||
{
|
||||
tabSixel.View = new View ()
|
||||
tabSixel.View = new()
|
||||
{
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill ()
|
||||
};
|
||||
var btnSixel = new Button { X = 0, Y = 0, Text = "Output Sixel" };
|
||||
btnSixel.Accept += (s, e) => { _imageView.OutputSixel (); };
|
||||
|
||||
var btnSixel = new Button { X = 0, Y = 0, Text = "Output Sixel", Width = Dim.Auto () };
|
||||
tabSixel.View.Add (btnSixel);
|
||||
|
||||
var sixelView = new View
|
||||
{
|
||||
Y = Pos.Bottom (btnSixel),
|
||||
Width = Dim.Percent (50),
|
||||
Height = Dim.Fill (),
|
||||
BorderStyle = LineStyle.Dotted
|
||||
};
|
||||
|
||||
tabSixel.View.Add (sixelView);
|
||||
|
||||
var lblPxX = new Label
|
||||
{
|
||||
X = Pos.Right (sixelView),
|
||||
Text = "Pixels per Col:"
|
||||
};
|
||||
|
||||
var pxX = new NumericUpDown
|
||||
{
|
||||
X = Pos.Right (lblPxX),
|
||||
Value = 12
|
||||
};
|
||||
|
||||
var lblPxY = new Label
|
||||
{
|
||||
X = lblPxX.X,
|
||||
Y = 1,
|
||||
Text = "Pixels per Row:"
|
||||
};
|
||||
|
||||
var pxY = new NumericUpDown
|
||||
{
|
||||
X = Pos.Right (lblPxY),
|
||||
Y = 1,
|
||||
Value = 6
|
||||
};
|
||||
|
||||
tabSixel.View.Add (lblPxX);
|
||||
tabSixel.View.Add (pxX);
|
||||
tabSixel.View.Add (lblPxY);
|
||||
tabSixel.View.Add (pxY);
|
||||
|
||||
btnSixel.Accept += (s, e) =>
|
||||
{
|
||||
_imageView.OutputSixel (
|
||||
sixelView.FrameToScreen ().Location,
|
||||
sixelView.Frame.Size,
|
||||
pxX.Value,
|
||||
pxY.Value);
|
||||
};
|
||||
}
|
||||
|
||||
private class ImageView : View
|
||||
@@ -205,7 +253,12 @@ public class Images : Scenario
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
|
||||
public void OutputSixel ()
|
||||
public void OutputSixel (
|
||||
Point screenPosition,
|
||||
Size maxSize,
|
||||
int pixelsPerCellX,
|
||||
int pixelsPerCellY
|
||||
)
|
||||
{
|
||||
if (_fullResImage == null)
|
||||
{
|
||||
@@ -214,7 +267,21 @@ public class Images : Scenario
|
||||
|
||||
var encoder = new SixelEncoder ();
|
||||
|
||||
string encoded = encoder.EncodeSixel (ConvertToColorArray (_fullResImage));
|
||||
// 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 ());
|
||||
|
||||
@@ -235,7 +302,29 @@ public class Images : Scenario
|
||||
dlg.AddButton (btn);
|
||||
Application.Run (dlg);
|
||||
|
||||
Application.Sixel = encoded;
|
||||
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)
|
||||
@@ -260,7 +349,7 @@ public class Images : Scenario
|
||||
|
||||
public class PaletteView : View
|
||||
{
|
||||
private List<Color> _palette;
|
||||
private readonly List<Color> _palette;
|
||||
|
||||
public PaletteView (List<Color> palette)
|
||||
{
|
||||
@@ -324,13 +413,6 @@ public class Images : Scenario
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allows dynamically changing the palette
|
||||
public void SetPalette (List<Color> palette)
|
||||
{
|
||||
_palette = palette ?? new List<Color> ();
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +500,7 @@ public class MedianCutPaletteBuilder : IPaletteBuilder
|
||||
|
||||
private List<Color> MedianCut (List<Color> colors, int maxColors)
|
||||
{
|
||||
List<List<Color>> cubes = new() { colors };
|
||||
List<List<Color>> cubes = new () { colors };
|
||||
|
||||
// Recursively split color regions
|
||||
while (cubes.Count < maxColors)
|
||||
|
||||
Reference in New Issue
Block a user