mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
WIP: trying to get fully transparent alpha to not render
This commit is contained in:
@@ -113,13 +113,24 @@ public class SixelEncoder
|
||||
for (int x = 0; x < width; ++x)
|
||||
{
|
||||
Array.Clear (code, 0, usedColorIdx.Count);
|
||||
bool anyNonTransparentPixel = false; // Track if any non-transparent pixels are found in this column
|
||||
|
||||
// Process each row in the 6-pixel high band
|
||||
for (int row = 0; row < bandHeight; ++row)
|
||||
{
|
||||
var color = pixels [x, startY + row];
|
||||
|
||||
int colorIndex = Quantizer.GetNearestColor (color);
|
||||
|
||||
if (color.A == 0) // Skip fully transparent pixels
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
anyNonTransparentPixel = true;
|
||||
}
|
||||
|
||||
if (slots [colorIndex] == -1)
|
||||
{
|
||||
targets.Add (new List<string> ());
|
||||
@@ -135,6 +146,8 @@ public class SixelEncoder
|
||||
code [slots [colorIndex]] |= (byte)(1 << row); // Accumulate SIXEL data
|
||||
}
|
||||
|
||||
// TODO: Handle fully empty rows better
|
||||
|
||||
// Handle transitions between columns
|
||||
for (int j = 0; j < usedColorIdx.Count; ++j)
|
||||
{
|
||||
|
||||
@@ -22,11 +22,12 @@ public class Images : Scenario
|
||||
private ImageView _imageView;
|
||||
private Point _screenLocationForSixel;
|
||||
private string _encodedSixelData;
|
||||
private Window _win;
|
||||
|
||||
public override void Main ()
|
||||
{
|
||||
Application.Init ();
|
||||
var win = new Window { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" };
|
||||
_win = new Window { Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}" };
|
||||
|
||||
bool canTrueColor = Application.Driver?.SupportsTrueColor ?? false;
|
||||
|
||||
@@ -41,7 +42,7 @@ public class Images : Scenario
|
||||
};
|
||||
|
||||
var lblDriverName = new Label { X = 0, Y = 0, Text = $"Driver is {Application.Driver?.GetType ().Name}" };
|
||||
win.Add (lblDriverName);
|
||||
_win.Add (lblDriverName);
|
||||
|
||||
var cbSupportsTrueColor = new CheckBox
|
||||
{
|
||||
@@ -51,7 +52,7 @@ public class Images : Scenario
|
||||
CanFocus = false,
|
||||
Text = "supports true color "
|
||||
};
|
||||
win.Add (cbSupportsTrueColor);
|
||||
_win.Add (cbSupportsTrueColor);
|
||||
|
||||
var cbSupportsSixel = new CheckBox
|
||||
{
|
||||
@@ -63,7 +64,7 @@ public class Images : Scenario
|
||||
Enabled = false,
|
||||
Text = "Supports Sixel"
|
||||
};
|
||||
win.Add (cbSupportsSixel);
|
||||
_win.Add (cbSupportsSixel);
|
||||
|
||||
var cbUseTrueColor = new CheckBox
|
||||
{
|
||||
@@ -74,10 +75,15 @@ public class Images : Scenario
|
||||
Text = "Use true color"
|
||||
};
|
||||
cbUseTrueColor.CheckedStateChanging += (_, evt) => Application.Force16Colors = evt.NewValue == CheckState.UnChecked;
|
||||
win.Add (cbUseTrueColor);
|
||||
_win.Add (cbUseTrueColor);
|
||||
|
||||
var btnOpenImage = new Button { X = Pos.Right (cbUseTrueColor) + 2, Y = 0, Text = "Open Image" };
|
||||
win.Add (btnOpenImage);
|
||||
_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
|
||||
{
|
||||
@@ -92,11 +98,38 @@ public class Images : Scenario
|
||||
|
||||
btnOpenImage.Accept += OpenImage;
|
||||
|
||||
win.Add (tv);
|
||||
Application.Run (win);
|
||||
win.Dispose ();
|
||||
_win.Add (tv);
|
||||
Application.Run (_win);
|
||||
_win.Dispose ();
|
||||
Application.Shutdown ();
|
||||
}
|
||||
|
||||
private void BtnStartFireOnAccept (object sender, HandledEventArgs e)
|
||||
{
|
||||
var fire = new DoomFire (_win.Frame.Width, _win.Frame.Height);
|
||||
var encoder = new SixelEncoder ();
|
||||
encoder.Quantizer.PaletteBuildingAlgorithm = new ConstPalette (fire.Palette);
|
||||
|
||||
Application.AddTimeout (
|
||||
TimeSpan.FromMilliseconds (500),
|
||||
() =>
|
||||
{
|
||||
fire.AdvanceFrame ();
|
||||
|
||||
var 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();
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -150,6 +183,7 @@ public class Images : Scenario
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_imageView.SetImage (img);
|
||||
Application.Refresh ();
|
||||
}
|
||||
@@ -465,6 +499,19 @@ public class Images : Scenario
|
||||
}
|
||||
}
|
||||
|
||||
internal class ConstPalette : IPaletteBuilder
|
||||
{
|
||||
private readonly List<Color> _palette;
|
||||
|
||||
public ConstPalette (Color [] palette) { _palette = palette.ToList (); }
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<Color> BuildPalette (List<Color> colors, int maxColors)
|
||||
{
|
||||
return _palette;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class LabColorDistance : IColorDistance
|
||||
{
|
||||
// Reference white point for D65 illuminant (can be moved to constants)
|
||||
@@ -677,3 +724,93 @@ public class MedianCutPaletteBuilder : IPaletteBuilder
|
||||
return (maxR - minR) * (maxG - minG) * (maxB - minB);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DoomFire
|
||||
{
|
||||
private int _width;
|
||||
private int _height;
|
||||
private Color [,] _firePixels;
|
||||
private static Color [] _palette;
|
||||
public Color [] Palette => _palette;
|
||||
|
||||
public DoomFire (int width, int height)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_firePixels = new Color [width, height];
|
||||
InitializePalette ();
|
||||
InitializeFire ();
|
||||
}
|
||||
|
||||
private void InitializePalette ()
|
||||
{
|
||||
// Initialize a basic fire palette. You can modify these colors as needed.
|
||||
_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)
|
||||
|
||||
// The rest of the palette is fire colors
|
||||
for (int 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
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeFire ()
|
||||
{
|
||||
// Set the bottom row to full intensity (simulate the base of the fire).
|
||||
for (int 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 (int x = 0; x < _width; x++)
|
||||
{
|
||||
_firePixels [x, y] = _palette [0]; // Transparent black
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AdvanceFrame ()
|
||||
{
|
||||
// Process every pixel except the bottom row
|
||||
for (int x = 0; x < _width; x++)
|
||||
{
|
||||
for (int y = 1; y < _height; y++) // Skip the last row (which is always max intensity)
|
||||
{
|
||||
int srcX = x;
|
||||
int srcY = y;
|
||||
int dstY = y - 1;
|
||||
|
||||
// Spread fire upwards with randomness
|
||||
int decay = new Random ().Next (0, 3);
|
||||
int dstX = Math.Max (0, srcX - decay);
|
||||
|
||||
// Get the fire color from below and reduce its intensity
|
||||
Color srcColor = _firePixels [srcX, srcY];
|
||||
int intensity = Array.IndexOf (_palette, srcColor) - decay;
|
||||
|
||||
if (intensity < 0)
|
||||
{
|
||||
intensity = 0;
|
||||
}
|
||||
|
||||
_firePixels [dstX, dstY] = _palette [intensity];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Color [,] GetFirePixels ()
|
||||
{
|
||||
return _firePixels;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,4 +149,86 @@ public class SixelEncoderTests
|
||||
// Compare the generated SIXEL string with the expected one
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EncodeSixel_Transparent12x12_ReturnsExpectedSixel ()
|
||||
{
|
||||
string expected = "\u001bP" // Start sixel sequence
|
||||
+ "0;0;0" // Defaults for aspect ratio and grid size
|
||||
+ "q" // Signals beginning of sixel image data
|
||||
+ "\"1;1;12;12" // no scaling factors (1x1) and filling 12x12 pixel area
|
||||
+ "#0;2;0;0;0" // Black transparent (TODO: Shouldn't really be output this if it is transparent)
|
||||
// Since all pixels are transparent, the data should just be filled with '?'
|
||||
+ "#0!12?$-" // Fills the transparent line with byte 0 which maps to '?'
|
||||
+ "#0!12?$" // Second band, same fully transparent pixels
|
||||
+ "\u001b\\"; // End sixel sequence
|
||||
|
||||
// Arrange: Create a 12x12 bitmap filled with fully transparent pixels
|
||||
Color [,] pixels = new Color [12, 12];
|
||||
|
||||
for (var x = 0; x < 12; x++)
|
||||
{
|
||||
for (var y = 0; y < 12; y++)
|
||||
{
|
||||
pixels [x, y] = new (0, 0, 0, 0); // Fully transparent
|
||||
}
|
||||
}
|
||||
|
||||
// Act: Encode the image
|
||||
var encoder = new SixelEncoder ();
|
||||
string result = encoder.EncodeSixel (pixels);
|
||||
|
||||
// Assert: Expect the result to be fully transparent encoded output
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
[Fact]
|
||||
public void EncodeSixel_VerticalMix_TransparentAndColor_ReturnsExpectedSixel ()
|
||||
{
|
||||
string expected = "\u001bP" // Start sixel sequence
|
||||
+ "0;0;0" // Defaults for aspect ratio and grid size
|
||||
+ "q" // Signals beginning of sixel image data
|
||||
+ "\"1;1;12;12" // No scaling factors (1x1) and filling 12x12 pixel area
|
||||
/*
|
||||
* Define the color palette:
|
||||
* We'll use one color (Red) for the colored pixels.
|
||||
*/
|
||||
+ "#0;2;100;0;0" // Red color definition (index 0: RGB 100,0,0)
|
||||
+ "#1;2;0;0;0" // Black transparent (TODO: Shouldn't really be output this if it is transparent)
|
||||
/*
|
||||
* Start of the Pixel data
|
||||
* We have alternating transparent (0) and colored (red) pixels in a vertical band.
|
||||
* The pattern for each sixel byte is 101010, which in binary (+63) converts to ASCII character 'T'.
|
||||
* Since we have 12 pixels horizontally, we'll see this pattern repeat across the row so we see
|
||||
* the 'sequence repeat' 12 times i.e. !12 (do the next letter 'T' 12 times).
|
||||
*/
|
||||
+ "#0!12T$-" // First band of alternating red and transparent pixels
|
||||
+ "#0!12T$" // Second band, same alternating red and transparent pixels
|
||||
+ "\u001b\\"; // End sixel sequence
|
||||
|
||||
// Arrange: Create a 12x12 bitmap with alternating transparent and red pixels in a vertical band
|
||||
Color [,] pixels = new Color [12, 12];
|
||||
|
||||
for (var x = 0; x < 12; x++)
|
||||
{
|
||||
for (var y = 0; y < 12; y++)
|
||||
{
|
||||
// For simplicity, we'll make every other row transparent
|
||||
if (y % 2 == 0)
|
||||
{
|
||||
pixels [x, y] = new (255, 0, 0); // Red pixel
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels [x, y] = new (0, 0, 0, 0); // Transparent pixel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Act: Encode the image
|
||||
var encoder = new SixelEncoder ();
|
||||
string result = encoder.EncodeSixel (pixels);
|
||||
|
||||
// Assert: Expect the result to match the expected sixel output
|
||||
Assert.Equal (expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user