diff --git a/src/Consolonia.Core/Drawing/DrawingContextImpl.cs b/src/Consolonia.Core/Drawing/DrawingContextImpl.cs index 279c09d..4778509 100644 --- a/src/Consolonia.Core/Drawing/DrawingContextImpl.cs +++ b/src/Consolonia.Core/Drawing/DrawingContextImpl.cs @@ -70,36 +70,37 @@ public void DrawBitmap(IBitmapImpl source, double opacity, Rect sourceRect, Rect new SKPaint { FilterQuality = SKFilterQuality.Medium }); for (int y = 0; y < bitmap.Info.Height; y += 2) - for (int x = 0; x < bitmap.Info.Width; x += 2) - { - // NOTE: we divide by 2 because we are working with quad pixels, - // // the bitmap has twice the horizontal and twice the vertical of the target rect. - int px = (int)targetRect.TopLeft.X + x / 2; - int py = (int)targetRect.TopLeft.Y + y / 2; + for (int x = 0; x < bitmap.Info.Width; x += 2) + { + // NOTE: we divide by 2 because we are working with quad pixels, + // // the bitmap has twice the horizontal and twice the vertical of the target rect. + int px = (int)targetRect.TopLeft.X + x / 2; + int py = (int)targetRect.TopLeft.Y + y / 2; - // get the quad pixel the bitmap - var quadColors = new[] - { + // get the quad pixel the bitmap + var quadColors = new[] + { bitmap.GetPixel(x, y), bitmap.GetPixel(x + 1, y), bitmap.GetPixel(x, y + 1), bitmap.GetPixel(x + 1, y + 1) }; - // map it to a single char to represet the 4 pixels - char quadPixel = GetQuadPixelCharacter(quadColors); + // map it to a single char to represet the 4 pixels + char quadPixel = GetQuadPixelCharacter(quadColors); - // get the combined colors for the quad pixel - Color foreground = GetForegroundColorForQuadPixel(quadColors, quadPixel); - Color background = GetBackgroundColorForQuadPixel(quadColors, quadPixel); + // get the combined colors for the quad pixel + Color foreground = GetForegroundColorForQuadPixel(quadColors, quadPixel); + Color background = GetBackgroundColorForQuadPixel(quadColors, quadPixel); - var imagePixel = new Pixel(new PixelForeground(new SimpleSymbol(new Rune(quadPixel)), color: foreground), - new PixelBackground(background)); - CurrentClip.ExecuteWithClipping(new Point(px, py), - () => - { - _pixelBuffer.Set(new PixelBufferCoordinate((ushort)px, (ushort)py), - (existingPixel, _) => existingPixel.Blend(imagePixel), imagePixel.Background.Color); - }); - } + var imagePixel = new Pixel( + new PixelForeground(new SimpleSymbol(new Rune(quadPixel)), color: foreground), + new PixelBackground(background)); + CurrentClip.ExecuteWithClipping(new Point(px, py), + () => + { + _pixelBuffer.Set(new PixelBufferCoordinate((ushort)px, (ushort)py), + (existingPixel, _) => existingPixel.Blend(imagePixel), imagePixel.Background.Color); + }); + } } public void DrawBitmap(IBitmapImpl source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect) @@ -150,11 +151,11 @@ public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry) case VisualBrush: throw new NotImplementedException(); case ISceneBrush sceneBrush: - { - ISceneBrushContent sceneBrushContent = sceneBrush.CreateContent(); - if (sceneBrushContent != null) sceneBrushContent.Render(this, Matrix.Identity); - return; - } + { + ISceneBrushContent sceneBrushContent = sceneBrush.CreateContent(); + if (sceneBrushContent != null) sceneBrushContent.Render(this, Matrix.Identity); + return; + } } Rect r2 = r.TransformToAABB(Transform); @@ -162,19 +163,19 @@ public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry) double width = r2.Width + (pen?.Thickness ?? 0); double height = r2.Height + (pen?.Thickness ?? 0); for (int x = 0; x < width; x++) - for (int y = 0; y < height; y++) - { - int px = (int)(r2.TopLeft.X + x); - int py = (int)(r2.TopLeft.Y + y); + for (int y = 0; y < height; y++) + { + int px = (int)(r2.TopLeft.X + x); + int py = (int)(r2.TopLeft.Y + y); - ConsoleBrush backgroundBrush = ConsoleBrush.FromPosition(brush, x, y, (int)width, (int)height); - CurrentClip.ExecuteWithClipping(new Point(px, py), () => - { - _pixelBuffer.Set(new PixelBufferCoordinate((ushort)px, (ushort)py), - (pixel, bb) => pixel.Blend(new Pixel(new PixelBackground(bb.Mode, bb.Color))), - backgroundBrush); - }); - } + ConsoleBrush backgroundBrush = ConsoleBrush.FromPosition(brush, x, y, (int)width, (int)height); + CurrentClip.ExecuteWithClipping(new Point(px, py), () => + { + _pixelBuffer.Set(new PixelBufferCoordinate((ushort)px, (ushort)py), + (pixel, bb) => pixel.Blend(new Pixel(new PixelBackground(bb.Mode, bb.Color))), + backgroundBrush); + }); + } } if (pen is null or { Thickness: 0 } @@ -207,7 +208,7 @@ public void DrawGlyphRun(IBrush foreground, IGlyphRunImpl glyphRun) } string charactersDoDraw = - string.Concat(glyphRunImpl.GlyphIndices.Select(us => (char)us).ToArray()); + string.Concat(glyphRunImpl.GlyphIndices.Select(us => (char)us).ToArray()); DrawStringInternal(foreground, charactersDoDraw, glyphRun.GlyphTypeface); } @@ -500,7 +501,7 @@ private void DrawPixelAndMoveHead(ref Point head, Line line, LineStyle? lineStyl int currentXPosition = 0; // Each rune maps to a pixel - foreach(var rune in text.EnumerateRunes()) + foreach (Rune rune in text.EnumerateRunes()) { Point characterPoint = whereToDraw.Transform(Matrix.CreateTranslation(currentXPosition++, 0)); Color foregroundColor = consoleBrush.Color; @@ -508,43 +509,43 @@ private void DrawPixelAndMoveHead(ref Point head, Line line, LineStyle? lineStyl switch (rune.Value) { case '\t': + { + const int tabSize = 8; + var consolePixel = new Pixel(new Rune(' '), foregroundColor); + for (int j = 0; j < tabSize; j++) { - const int tabSize = 8; - var consolePixel = new Pixel(new Rune(' '), foregroundColor); - for (int j = 0; j < tabSize; j++) + Point newCharacterPoint = characterPoint.WithX(characterPoint.X + j); + CurrentClip.ExecuteWithClipping(newCharacterPoint, () => { - Point newCharacterPoint = characterPoint.WithX(characterPoint.X + j); - CurrentClip.ExecuteWithClipping(newCharacterPoint, () => - { - _pixelBuffer.Set((PixelBufferCoordinate)newCharacterPoint, - (oldPixel, cp) => oldPixel.Blend(cp), consolePixel); - }); - } - - currentXPosition += tabSize - 1; + _pixelBuffer.Set((PixelBufferCoordinate)newCharacterPoint, + (oldPixel, cp) => oldPixel.Blend(cp), consolePixel); + }); } + + currentXPosition += tabSize - 1; + } break; case '\n': - { - /* it's not clear if we need to draw anything. Cursor can be placed at the end of the line - var consolePixel = new Pixel(' ', foregroundColor); + { + /* it's not clear if we need to draw anything. Cursor can be placed at the end of the line + var consolePixel = new Pixel(' ', foregroundColor); - _pixelBuffer.Set((PixelBufferCoordinate)characterPoint, - (oldPixel, cp) => oldPixel.Blend(cp), consolePixel);*/ - } + _pixelBuffer.Set((PixelBufferCoordinate)characterPoint, + (oldPixel, cp) => oldPixel.Blend(cp), consolePixel);*/ + } break; case '\u200B': currentXPosition--; break; default: + { + var consolePixel = new Pixel(rune, foregroundColor, typeface.Style, typeface.Weight); + CurrentClip.ExecuteWithClipping(characterPoint, () => { - var consolePixel = new Pixel(rune, foregroundColor, typeface.Style, typeface.Weight); - CurrentClip.ExecuteWithClipping(characterPoint, () => - { - _pixelBuffer.Set((PixelBufferCoordinate)characterPoint, - (oldPixel, cp) => oldPixel.Blend(cp), consolePixel); - }); - } + _pixelBuffer.Set((PixelBufferCoordinate)characterPoint, + (oldPixel, cp) => oldPixel.Blend(cp), consolePixel); + }); + } break; } } diff --git a/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs b/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs index c248754..bdc834e 100644 --- a/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs +++ b/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs @@ -21,7 +21,7 @@ public DrawingBoxSymbol(byte upRightDownLeft) private byte _upRightDownLeft; - public Rune Rune => new Rune(GetBoxSymbol()); + public Rune Rune => new(GetBoxSymbol()); /// /// https://en.wikipedia.org/wiki/Code_page_437 @@ -54,46 +54,46 @@ private char GetBoxSymbol() return horizontal ? '╪' : '╫'; default: + { + return _upRightDownLeft switch { - return _upRightDownLeft switch - { - EmptySymbol => char.MinValue, - BoldSymbol => '█', - 0b0000_1001 => '┘', - 0b1000_1001 => '╜', - 0b0001_1001 => '╛', - 0b1001_1001 => '╝', - 0b0000_0011 => '┐', - 0b0010_0011 => '╖', - 0b0001_0011 => '╕', - 0b0011_0011 => '╗', - 0b0000_0110 => '┌', - 0b0100_0110 => '╒', - 0b0010_0110 => '╓', - 0b0110_0110 => '╔', - 0b0000_1100 => '└', - 0b0100_1100 => '╘', - 0b1000_1100 => '╙', - 0b1100_1100 => '╚', - 0b0000_1110 => '├', - 0b1000_1110 or 0b0010_1110 or 0b1010_1110 => '╟', - 0b0100_1110 => '╞', - 0b1100_1110 or 0b0110_1110 or 0b1110_1110 => '╠', - 0b0000_1011 => '┤', - 0b1000_1011 or 0b0010_1011 or 0b1010_1011 => '╢', - 0b0001_1011 => '╡', - 0b1001_1011 or 0b0011_1011 or 0b1011_1011 => '╣', - 0b0000_1101 => '┴', - 0b1000_1101 => '╨', - 0b0100_1101 or 0b0001_1101 or 0b0101_1101 => '╧', - 0b1100_1101 or 0b1001_1101 or 0b1101_1101 => '╩', - 0b0000_0111 => '┬', - 0b0010_0111 => '╥', - 0b0100_0111 or 0b0001_0111 or 0b0101_0111 => '╤', - 0b0110_0111 or 0b0011_0111 or 0b0111_0111 => '╦', - _ => throw new InvalidOperationException() - }; - } + EmptySymbol => char.MinValue, + BoldSymbol => '█', + 0b0000_1001 => '┘', + 0b1000_1001 => '╜', + 0b0001_1001 => '╛', + 0b1001_1001 => '╝', + 0b0000_0011 => '┐', + 0b0010_0011 => '╖', + 0b0001_0011 => '╕', + 0b0011_0011 => '╗', + 0b0000_0110 => '┌', + 0b0100_0110 => '╒', + 0b0010_0110 => '╓', + 0b0110_0110 => '╔', + 0b0000_1100 => '└', + 0b0100_1100 => '╘', + 0b1000_1100 => '╙', + 0b1100_1100 => '╚', + 0b0000_1110 => '├', + 0b1000_1110 or 0b0010_1110 or 0b1010_1110 => '╟', + 0b0100_1110 => '╞', + 0b1100_1110 or 0b0110_1110 or 0b1110_1110 => '╠', + 0b0000_1011 => '┤', + 0b1000_1011 or 0b0010_1011 or 0b1010_1011 => '╢', + 0b0001_1011 => '╡', + 0b1001_1011 or 0b0011_1011 or 0b1011_1011 => '╣', + 0b0000_1101 => '┴', + 0b1000_1101 => '╨', + 0b0100_1101 or 0b0001_1101 or 0b0101_1101 => '╧', + 0b1100_1101 or 0b1001_1101 or 0b1101_1101 => '╩', + 0b0000_0111 => '┬', + 0b0010_0111 => '╥', + 0b0100_0111 or 0b0001_0111 or 0b0101_0111 => '╤', + 0b0110_0111 or 0b0011_0111 or 0b0111_0111 => '╦', + _ => throw new InvalidOperationException() + }; + } } } diff --git a/src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs b/src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs index 40e37e9..03d11c9 100644 --- a/src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs +++ b/src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs @@ -5,14 +5,14 @@ namespace Consolonia.Core.Drawing.PixelBufferImplementation public interface ISymbol { /// - /// The rune for the symbol + /// The rune for the symbol /// Rune Rune { get; } bool IsWhiteSpace(); /// - /// Blend 2 symbols together + /// Blend 2 symbols together /// /// /// diff --git a/src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs b/src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs index 638d3e0..f093689 100644 --- a/src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs +++ b/src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs @@ -67,10 +67,12 @@ public Pixel Blend(Pixel pixelAbove) case PixelBackgroundMode.Transparent: // if the foreground is transparent, ignore pixelAbove foreground. - newForeground = pixelAbove.Foreground.Color != Colors.Transparent ? Foreground.Blend(pixelAbove.Foreground) : Foreground; - + newForeground = pixelAbove.Foreground.Color != Colors.Transparent + ? Foreground.Blend(pixelAbove.Foreground) + : Foreground; + // background is transparent, ignore pixelAbove background. - newBackground = Background; + newBackground = Background; break; case PixelBackgroundMode.Shaded: (newForeground, newBackground) = Shade(); diff --git a/src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs b/src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs index 1cedb8b..b11eac6 100644 --- a/src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs +++ b/src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs @@ -71,17 +71,19 @@ public void Set(PixelBufferCoordinate point, Func - /// Clears old pixel caret position and sets new caret position + /// Clears old pixel caret position and sets new caret position /// /// public void SetCaretPosition(PixelBufferCoordinate point) { - var oldCaretPixel = _buffer[_caretPosition.X, _caretPosition.Y]; - _buffer[_caretPosition.X, _caretPosition.Y] = new Pixel(oldCaretPixel.Foreground, oldCaretPixel.Background, isCaret: false); + Pixel oldCaretPixel = _buffer[_caretPosition.X, _caretPosition.Y]; + _buffer[_caretPosition.X, _caretPosition.Y] = + new Pixel(oldCaretPixel.Foreground, oldCaretPixel.Background, false); _caretPosition = point; - var newCaretPixel = _buffer[_caretPosition.X, _caretPosition.Y]; - _buffer[_caretPosition.X, _caretPosition.Y] = new Pixel(newCaretPixel.Foreground, newCaretPixel.Background, isCaret: true); + Pixel newCaretPixel = _buffer[_caretPosition.X, _caretPosition.Y]; + _buffer[_caretPosition.X, _caretPosition.Y] = + new Pixel(newCaretPixel.Foreground, newCaretPixel.Background, true); } public void Foreach(Func replaceAction) @@ -97,11 +99,11 @@ public void Foreach(Func replaceAction) public void ForeachReadonly(Action action) { for (ushort j = 0; j < Height; j++) - for (ushort i = 0; i < Width; i++) - { - Pixel pixel = this[(PixelBufferCoordinate)(i, j)]; - action(new PixelBufferCoordinate(i, j), pixel); - } + for (ushort i = 0; i < Width; i++) + { + Pixel pixel = this[(PixelBufferCoordinate)(i, j)]; + action(new PixelBufferCoordinate(i, j), pixel); + } } private (ushort x, ushort y) ToXY(int i) diff --git a/src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs b/src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs index 5567d00..5f35d4c 100644 --- a/src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs +++ b/src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs @@ -6,24 +6,26 @@ namespace Consolonia.Core.Drawing.PixelBufferImplementation [DebuggerDisplay("'{Rune}'")] public readonly struct SimpleSymbol : ISymbol { - private readonly Rune _rune = new Rune('\0'); - public SimpleSymbol() { - _rune = new Rune('\0'); + Rune = new Rune('\0'); } public SimpleSymbol(Rune rune) { - _rune = rune; + Rune = rune; } - public Rune Rune => _rune; + public Rune Rune { get; } = new('\0'); public bool IsWhiteSpace() - => Rune.IsWhiteSpace(_rune); + { + return Rune.IsWhiteSpace(Rune); + } public ISymbol Blend(ref ISymbol symbolAbove) - => (symbolAbove.Rune.Value != '\0') ? symbolAbove : this; + { + return symbolAbove.Rune.Value != '\0' ? symbolAbove : this; + } } } \ No newline at end of file diff --git a/src/Consolonia.Core/Drawing/RenderTarget.cs b/src/Consolonia.Core/Drawing/RenderTarget.cs index 855b0fc..2a6e774 100644 --- a/src/Consolonia.Core/Drawing/RenderTarget.cs +++ b/src/Consolonia.Core/Drawing/RenderTarget.cs @@ -135,8 +135,8 @@ private void RenderToDevice() new PixelBackground(PixelBackgroundMode.Colored)); (Color background, Color foreground, FontWeight weight, FontStyle style, TextDecorationCollection - textDecorations, Rune rune) - pixelSpread = (pixel.Background.Color, pixel.Foreground.Color, pixel.Foreground.Weight, + textDecorations, Rune rune) + pixelSpread = (pixel.Background.Color, pixel.Foreground.Color, pixel.Foreground.Weight, pixel.Foreground.Style, pixel.Foreground.TextDecorations, pixel.Foreground.Symbol.Rune); @@ -204,7 +204,7 @@ public void WritePixel( _lastBufferPointStart = _currentBufferPoint = bufferPoint; } - var rune = pixel.Foreground.Symbol.Rune; + Rune rune = pixel.Foreground.Symbol.Rune; if (Rune.IsControl(rune)) /*|| character is '保' or '哥'*/ rune = new Rune(' '); // some terminals do not print \0 _stringBuilder.Append(rune); diff --git a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs index 8f408b4..0fc0d2b 100644 --- a/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs +++ b/src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs @@ -102,13 +102,14 @@ FontWeight.Thin or FontWeight.ExtraLight or FontWeight.Light // not enough as some runes (like '🥰') are a single rune, but are emitted as two console characters if (str.EnumerateRunes().Count() != str.Length) { - var (left, top) = Console.GetCursorPosition(); + (int left, int top) = Console.GetCursorPosition(); _headBufferPoint = new PixelBufferCoordinate((ushort)left, (ushort)top); } else { if (_headBufferPoint.X < Size.Width - str.Length) - _headBufferPoint = new PixelBufferCoordinate((ushort)(_headBufferPoint.X + str.Length), _headBufferPoint.Y); + _headBufferPoint = + new PixelBufferCoordinate((ushort)(_headBufferPoint.X + str.Length), _headBufferPoint.Y); else _headBufferPoint = (PixelBufferCoordinate)((ushort)0, (ushort)(_headBufferPoint.Y + 1)); } diff --git a/src/Tests/Consolonia.TestsCore/UnitTestConsole.cs b/src/Tests/Consolonia.TestsCore/UnitTestConsole.cs index 65bdad1..9304e66 100644 --- a/src/Tests/Consolonia.TestsCore/UnitTestConsole.cs +++ b/src/Tests/Consolonia.TestsCore/UnitTestConsole.cs @@ -59,7 +59,7 @@ void IConsole.Print(PixelBufferCoordinate bufferPoint, Color background, Color f (ushort x, ushort y) = bufferPoint; int i = 0; - foreach (var rune in str.EnumerateRunes()) + foreach (Rune rune in str.EnumerateRunes()) { PixelBuffer.Set(new PixelBufferCoordinate((ushort)(x + i), y), _ => // ReSharper disable once AccessToModifiedClosure we are sure about inline execution