-
-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
190-strange-coloring-ega-mode #233
Changes from all commits
e06c08b
5de905a
8390c50
7887e43
58e4f42
6e693a4
275069e
5c54ff9
49a312a
8a04477
07b0469
fab6f72
52c55b2
0fc7c74
99959c8
f8adc28
1c32687
3bfff06
66e72ea
beb2fbf
387ca1a
bf44216
5fb6096
25384c6
febcb3b
1bedda1
f105ff3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using System; | ||
using Avalonia.Markup.Xaml; | ||
using Consolonia.Core.Drawing.PixelBufferImplementation.EgaConsoleColor; | ||
|
||
// ReSharper disable once CheckNamespace | ||
namespace Consolonia | ||
{ | ||
//todo: define xaml namespace for out classes | ||
/// <summary> | ||
/// Avalonia axaml extension which consumes ConsoleColor and produces AvaloniaColor | ||
/// </summary> | ||
public class EgaColorExtension : MarkupExtension | ||
{ | ||
public EgaColorExtension() | ||
{ | ||
Color = ConsoleColor.Black; | ||
} | ||
|
||
public EgaColorExtension(ConsoleColor color) | ||
{ | ||
Color = color; | ||
} | ||
|
||
public EgaColorExtension(EgaColorMode mode) | ||
{ | ||
Color = ConsoleColor.Black; | ||
Mode = mode; | ||
} | ||
|
||
public ConsoleColor Color { get; set; } | ||
Check notice on line 30 in src/Consolonia.Core/Drawing/PixelBufferImplementation/EgaConsoleColor/EgaColor.cs GitHub Actions / build
|
||
public EgaColorMode Mode { get; set; } | ||
|
||
public override object ProvideValue(IServiceProvider serviceProvider) | ||
{ | ||
return EgaConsoleColorMode.ConvertToAvaloniaColor(Color, Mode); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// ReSharper disable once CheckNamespace | ||
|
||
namespace Consolonia | ||
{ | ||
public enum EgaColorMode : byte | ||
{ | ||
Colored, | ||
Transparent, | ||
Shaded | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Text; | ||
using Avalonia.Media; | ||
using Consolonia.Core.Infrastructure; | ||
|
||
namespace Consolonia.Core.Drawing.PixelBufferImplementation.EgaConsoleColor | ||
{ | ||
public class EgaConsoleColorMode : IConsoleColorMode | ||
{ | ||
private static readonly (ConsoleColor Color, (int R, int G, int B) Rgb)[] ConsoleColorMap = | ||
[ | ||
(ConsoleColor.Black, (0, 0, 0)), | ||
(ConsoleColor.DarkBlue, (0, 0, 128)), | ||
(ConsoleColor.DarkGreen, (0, 128, 0)), | ||
(ConsoleColor.DarkCyan, (0, 128, 128)), | ||
(ConsoleColor.DarkRed, (128, 0, 0)), | ||
(ConsoleColor.DarkMagenta, (128, 0, 128)), | ||
(ConsoleColor.DarkYellow, (128, 128, 0)), | ||
(ConsoleColor.Gray, (192, 192, 192)), | ||
(ConsoleColor.DarkGray, (128, 128, 128)), | ||
(ConsoleColor.Blue, (0, 0, 255)), | ||
(ConsoleColor.Green, (0, 255, 0)), | ||
(ConsoleColor.Cyan, (0, 255, 255)), | ||
(ConsoleColor.Red, (255, 0, 0)), | ||
(ConsoleColor.Magenta, (255, 0, 255)), | ||
(ConsoleColor.Yellow, (255, 255, 0)), | ||
(ConsoleColor.White, (255, 255, 255)) | ||
]; | ||
|
||
public Color Blend(Color color1, Color color2) | ||
{ | ||
(ConsoleColor consoleColor1, EgaColorMode mode1) = ConvertToConsoleColorMode(color1); | ||
(ConsoleColor consoleColor2, EgaColorMode mode2) = ConvertToConsoleColorMode(color2); | ||
|
||
switch (mode2) | ||
{ | ||
case EgaColorMode.Transparent: | ||
return color1; | ||
|
||
case EgaColorMode.Shaded when mode1 == EgaColorMode.Shaded: | ||
{ | ||
ConsoleColor doubleShadedColor = Shade(Shade(consoleColor1)); | ||
return ConvertToAvaloniaColor(doubleShadedColor); | ||
} | ||
case EgaColorMode.Shaded: | ||
{ | ||
ConsoleColor shadedColor = Shade(consoleColor1); | ||
return ConvertToAvaloniaColor(shadedColor); | ||
} | ||
case EgaColorMode.Colored: | ||
default: | ||
return ConvertToAvaloniaColor(consoleColor2); | ||
} | ||
} | ||
|
||
public void SetAttributes(InputLessDefaultNetConsole console, Color background, Color foreground, | ||
FontWeight? weight) | ||
{ | ||
(ConsoleColor backgroundConsoleColor, EgaColorMode mode) = ConvertToConsoleColorMode(background); | ||
if (mode is not EgaColorMode.Colored) | ||
ConsoloniaPlatform.RaiseNotSupported(62144, foreground); | ||
|
||
|
||
(ConsoleColor foregroundConsoleColor, _) = ConvertToConsoleColorMode(foreground); | ||
//todo: if mode is transparent, don't print foreground. if shaded - shade it | ||
|
||
var sb = new StringBuilder(); | ||
|
||
// Append ANSI escape sequence for background color | ||
sb.Append(GetAnsiCode(backgroundConsoleColor, true)); | ||
|
||
// Append ANSI escape sequence for foreground color | ||
sb.Append(GetAnsiCode(foregroundConsoleColor, false)); | ||
console.WriteText(sb.ToString()); | ||
return; | ||
|
||
// Function to map ConsoleColor to ANSI code | ||
static string GetAnsiCode(ConsoleColor color, bool isBackground) | ||
{ | ||
int ansiCode = color switch | ||
{ | ||
ConsoleColor.Black => 0, | ||
ConsoleColor.DarkRed => 1, | ||
ConsoleColor.DarkGreen => 2, | ||
ConsoleColor.DarkYellow => 3, | ||
ConsoleColor.DarkBlue => 4, | ||
ConsoleColor.DarkMagenta => 5, | ||
ConsoleColor.DarkCyan => 6, | ||
ConsoleColor.Gray => 7, | ||
ConsoleColor.DarkGray => 8, | ||
ConsoleColor.Red => 9, | ||
ConsoleColor.Green => 10, | ||
ConsoleColor.Yellow => 11, | ||
ConsoleColor.Blue => 12, | ||
ConsoleColor.Magenta => 13, | ||
ConsoleColor.Cyan => 14, | ||
ConsoleColor.White => 15, | ||
_ => 7 // Default to white if unknown | ||
}; | ||
|
||
return ansiCode < 8 | ||
? | ||
// Standard colors | ||
$"\x1b[{(isBackground ? 40 + ansiCode : 30 + ansiCode)}m" | ||
: | ||
// Bright colors | ||
$"\x1b[{(isBackground ? 100 + (ansiCode - 8) : 90 + (ansiCode - 8))}m"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add to Esc.cs ? |
||
} | ||
} | ||
|
||
public static (ConsoleColor, EgaColorMode) ConvertToConsoleColorMode(Color color) | ||
{ | ||
ConsoleColor consoleColor = MapToConsoleColor(color); | ||
|
||
EgaColorMode mode = color.A switch | ||
{ | ||
<= 63 => EgaColorMode.Transparent, | ||
<= 191 => EgaColorMode.Shaded, | ||
_ => EgaColorMode.Colored | ||
}; | ||
|
||
return (consoleColor, mode); | ||
} | ||
|
||
private static ConsoleColor MapToConsoleColor(Color color) | ||
{ | ||
int r = color.R, g = color.G, b = color.B; | ||
|
||
// Find the nearest ConsoleColor by RGB distance | ||
return ConsoleColorMap | ||
.OrderBy(c => Math.Pow(c.Rgb.R - r, 2) + Math.Pow(c.Rgb.G - g, 2) + Math.Pow(c.Rgb.B - b, 2)) | ||
.First().Color; | ||
} | ||
|
||
private static ConsoleColor Shade(ConsoleColor color) | ||
{ | ||
return color switch | ||
{ | ||
ConsoleColor.White => ConsoleColor.Gray, | ||
ConsoleColor.Gray => ConsoleColor.DarkGray, | ||
ConsoleColor.Blue => ConsoleColor.DarkBlue, | ||
ConsoleColor.Green => ConsoleColor.DarkGreen, | ||
ConsoleColor.Cyan => ConsoleColor.DarkCyan, | ||
ConsoleColor.Red => ConsoleColor.DarkRed, | ||
ConsoleColor.Magenta => ConsoleColor.DarkMagenta, | ||
ConsoleColor.Yellow => ConsoleColor.DarkYellow, | ||
ConsoleColor.DarkBlue or | ||
ConsoleColor.DarkGreen or | ||
ConsoleColor.DarkCyan or | ||
ConsoleColor.DarkRed or | ||
ConsoleColor.DarkMagenta or | ||
ConsoleColor.DarkYellow or | ||
ConsoleColor.DarkGray or | ||
ConsoleColor.Black => ConsoleColor.Black, | ||
_ => ConsoleColor.Black | ||
}; | ||
} | ||
|
||
public static Color ConvertToAvaloniaColor(ConsoleColor consoleColor, | ||
EgaColorMode mode = EgaColorMode.Colored) | ||
{ | ||
switch (mode) | ||
{ | ||
case EgaColorMode.Transparent: | ||
return Color.FromArgb(0, 0, 0, 0); | ||
case EgaColorMode.Shaded: | ||
return Color.FromArgb(127, 0, 0, 0); | ||
case EgaColorMode.Colored: | ||
(ConsoleColor _, (int R, int G, int B) rgb) = ConsoleColorMap.First(c => c.Color == consoleColor); | ||
return Color.FromRgb((byte)rgb.R, (byte)rgb.G, (byte)rgb.B); | ||
default: | ||
throw new ArgumentOutOfRangeException(nameof(mode), mode, null); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using Avalonia.Media; | ||
using Consolonia.Core.Infrastructure; | ||
|
||
namespace Consolonia.Core.Drawing.PixelBufferImplementation | ||
{ | ||
public interface IConsoleColorMode | ||
{ | ||
Color Blend(Color color1, Color color2); | ||
void SetAttributes(InputLessDefaultNetConsole console, Color background, Color foreground, FontWeight? weight); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Diagnostics.CodeAnalysis; | ||
using Avalonia; | ||
using Avalonia.Media; | ||
using Newtonsoft.Json; | ||
|
||
|
@@ -82,7 +83,7 @@ | |
|
||
public PixelBackground Background { get; init; } | ||
|
||
public bool IsCaret { get; init; } | ||
Check notice on line 86 in src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs GitHub Actions / build
|
||
|
||
[JsonIgnore] public ushort Width => Foreground.Symbol.Width; | ||
|
||
|
@@ -149,29 +150,8 @@ | |
/// <returns>source blended into target</returns> | ||
private static Color MergeColors(Color target, Color source) | ||
{ | ||
// by chatGPT o1 | ||
// Convert alpha from [0..255] to [0..1] | ||
float fgAlpha = source.A / 255f; | ||
float bgAlpha = target.A / 255f; | ||
|
||
// Compute output alpha | ||
float outAlpha = fgAlpha + bgAlpha * (1 - fgAlpha); | ||
|
||
// If there's no alpha in the result, return transparent | ||
if (outAlpha <= 0f) return Color.FromArgb(0, 0, 0, 0); | ||
|
||
// Calculate the composited color channels, also converting channels to [0..1] | ||
float outR = (source.R / 255f * fgAlpha + target.R / 255f * bgAlpha * (1 - fgAlpha)) / outAlpha; | ||
float outG = (source.G / 255f * fgAlpha + target.G / 255f * bgAlpha * (1 - fgAlpha)) / outAlpha; | ||
float outB = (source.B / 255f * fgAlpha + target.B / 255f * bgAlpha * (1 - fgAlpha)) / outAlpha; | ||
|
||
// Convert back to [0..255] | ||
byte a = (byte)(outAlpha * 255f); | ||
byte r = (byte)(outR * 255f); | ||
byte g = (byte)(outG * 255f); | ||
byte b = (byte)(outB * 255f); | ||
|
||
return Color.FromArgb(a, r, g, b); | ||
var consoleColorMode = AvaloniaLocator.Current.GetRequiredService<IConsoleColorMode>(); | ||
return consoleColorMode.Blend(target, source); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ooh, sliick |
||
} | ||
|
||
public override bool Equals([NotNullWhen(true)] object obj) | ||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
using System; | ||
using Avalonia; | ||
using Avalonia.Media; | ||
using Newtonsoft.Json; | ||
|
||
// ReSharper disable UnusedMember.Global | ||
|
@@ -26,7 +27,7 @@ public PixelBuffer(ushort width, ushort height) | |
// blended into it. | ||
for (ushort y = 0; y < height; y++) | ||
for (ushort x = 0; x < width; x++) | ||
_buffer[x, y] = Pixel.Space; | ||
_buffer[x, y] = new Pixel(new PixelBackground(Colors.Black)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not transparent background? |
||
} | ||
|
||
public ushort Width { get; } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it possible to detect default color when app starts? Maybe future enhancement.