From 4f4b6e2a2f73d7ff200ac9e400937b3cc09a1d0c Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Tue, 27 Oct 2015 10:18:04 +1100 Subject: [PATCH] Start work on using default.png as an alternative for text rendering. --- ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs | 75 +--------- .../2D/Drawing/GdiPlusDrawerFont.cs | 86 +++++++++++ .../2D/Drawing/GdiPlusDrawerMCFont.cs | 140 ++++++++++++++++++ ClassicalSharp/2D/Drawing/IDrawer2D.cs | 4 +- ClassicalSharp/ClassicalSharp.csproj | 2 + ClassicalSharp/Game/Game.cs | 2 +- Launcher2/LauncherWindow.cs | 2 +- 7 files changed, 238 insertions(+), 73 deletions(-) create mode 100644 ClassicalSharp/2D/Drawing/GdiPlusDrawerFont.cs create mode 100644 ClassicalSharp/2D/Drawing/GdiPlusDrawerMCFont.cs diff --git a/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs b/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs index 51de10296..f0a67b004 100644 --- a/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs +++ b/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.cs @@ -7,25 +7,14 @@ using ClassicalSharp.GraphicsAPI; namespace ClassicalSharp { - public sealed class GdiPlusDrawer2D : IDrawer2D { - - StringFormat format; - Bitmap measuringBmp; - Graphics measuringGraphics; + public abstract class GdiPlusDrawer2D : IDrawer2D { + Dictionary brushCache = new Dictionary( 16 ); - Graphics g; - + protected Graphics g; + protected Bitmap curBmp; public GdiPlusDrawer2D( IGraphicsApi graphics ) { this.graphics = graphics; - format = StringFormat.GenericTypographic; - format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces; - format.Trimming = StringTrimming.None; - //format.FormatFlags |= StringFormatFlags.NoWrap; - //format.FormatFlags |= StringFormatFlags.NoClip; - measuringBmp = new Bitmap( 1, 1 ); - measuringGraphics = Graphics.FromImage( measuringBmp ); - measuringGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; } public override void SetBitmap( Bitmap bmp ) { @@ -37,42 +26,7 @@ namespace ClassicalSharp { g = Graphics.FromImage( bmp ); g.TextRenderingHint = TextRenderingHint.AntiAlias; g.SmoothingMode = SmoothingMode.HighQuality; - } - - public override void DrawText( ref DrawTextArgs args, float x, float y ) { - if( !args.SkipPartsCheck ) - GetTextParts( args.Text ); - - Brush shadowBrush = GetOrCreateBrush( Color.Black ); - for( int i = 0; i < parts.Count; i++ ) { - TextPart part = parts[i]; - Brush textBrush = GetOrCreateBrush( part.TextColour ); - if( args.UseShadow ) - g.DrawString( part.Text, args.Font, shadowBrush, x + Offset, y + Offset, format ); - - g.DrawString( part.Text, args.Font, textBrush, x, y, format ); - x += g.MeasureString( part.Text, args.Font, Int32.MaxValue, format ).Width; - } - } - - public override void DrawClippedText( ref DrawTextArgs args, float x, float y, float maxWidth, float maxHeight ) { - if( !args.SkipPartsCheck ) - GetTextParts( args.Text ); - - Brush shadowBrush = GetOrCreateBrush( Color.Black ); - format.Trimming = StringTrimming.EllipsisCharacter; - for( int i = 0; i < parts.Count; i++ ) { - TextPart part = parts[i]; - Brush textBrush = GetOrCreateBrush( part.TextColour ); - RectangleF rect = new RectangleF( x + Offset, y + Offset, maxWidth, maxHeight ); - if( args.UseShadow ) - g.DrawString( part.Text, args.Font, shadowBrush, rect, format ); - - rect = new RectangleF( x, y, maxWidth, maxHeight ); - g.DrawString( part.Text, args.Font, textBrush, rect, format ); - x += g.MeasureString( part.Text, args.Font, Int32.MaxValue, format ).Width; - } - format.Trimming = StringTrimming.None; + curBmp = bmp; } public override void DrawRect( FastColour colour, int x, int y, int width, int height ) { @@ -132,30 +86,13 @@ namespace ClassicalSharp { return bmp; } - public override Size MeasureSize( ref DrawTextArgs args ) { - GetTextParts( args.Text ); - SizeF total = SizeF.Empty; - for( int i = 0; i < parts.Count; i++ ) { - SizeF size = measuringGraphics.MeasureString( parts[i].Text, args.Font, Int32.MaxValue, format ); - total.Height = Math.Max( total.Height, size.Height ); - total.Width += size.Width; - } - - if( args.UseShadow && parts.Count > 0 ) { - total.Width += Offset; total.Height += Offset; - } - return Size.Ceiling( total ); - } - public override void DisposeInstance() { - measuringBmp.Dispose(); - measuringGraphics.Dispose(); foreach( var pair in brushCache ) { pair.Value.Dispose(); } } - SolidBrush GetOrCreateBrush( Color color ) { + protected SolidBrush GetOrCreateBrush( Color color ) { int key = color.ToArgb(); SolidBrush brush; if( brushCache.TryGetValue( key, out brush ) ) { diff --git a/ClassicalSharp/2D/Drawing/GdiPlusDrawerFont.cs b/ClassicalSharp/2D/Drawing/GdiPlusDrawerFont.cs new file mode 100644 index 000000000..a06485851 --- /dev/null +++ b/ClassicalSharp/2D/Drawing/GdiPlusDrawerFont.cs @@ -0,0 +1,86 @@ +using System; +using System.Drawing; +using System.Drawing.Text; +using ClassicalSharp.GraphicsAPI; + +namespace ClassicalSharp { + + public sealed class GdiPlusDrawerFont : GdiPlusDrawer2D { + + StringFormat format; + Bitmap measuringBmp; + Graphics measuringGraphics; + + public GdiPlusDrawerFont( IGraphicsApi graphics ) : base( graphics ) { + format = StringFormat.GenericTypographic; + format.FormatFlags |= StringFormatFlags.MeasureTrailingSpaces; + format.Trimming = StringTrimming.None; + //format.FormatFlags |= StringFormatFlags.NoWrap; + //format.FormatFlags |= StringFormatFlags.NoClip; + + measuringBmp = new Bitmap( 1, 1 ); + measuringGraphics = Graphics.FromImage( measuringBmp ); + measuringGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; + } + + public override void DrawText( ref DrawTextArgs args, int x, int y ) { + if( !args.SkipPartsCheck ) + GetTextParts( args.Text ); + + Brush shadowBrush = GetOrCreateBrush( Color.Black ); + float textX = x; + for( int i = 0; i < parts.Count; i++ ) { + TextPart part = parts[i]; + Brush textBrush = GetOrCreateBrush( part.TextColour ); + if( args.UseShadow ) + g.DrawString( part.Text, args.Font, shadowBrush, textX + Offset, y + Offset, format ); + + g.DrawString( part.Text, args.Font, textBrush, textX, y, format ); + textX += g.MeasureString( part.Text, args.Font, Int32.MaxValue, format ).Width; + } + } + + public override void DrawClippedText( ref DrawTextArgs args, int x, int y, float maxWidth, float maxHeight ) { + if( !args.SkipPartsCheck ) + GetTextParts( args.Text ); + + Brush shadowBrush = GetOrCreateBrush( Color.Black ); + format.Trimming = StringTrimming.EllipsisCharacter; + float textX = x; + + for( int i = 0; i < parts.Count; i++ ) { + TextPart part = parts[i]; + Brush textBrush = GetOrCreateBrush( part.TextColour ); + RectangleF rect = new RectangleF( textX + Offset, y + Offset, maxWidth, maxHeight ); + if( args.UseShadow ) + g.DrawString( part.Text, args.Font, shadowBrush, rect, format ); + + rect = new RectangleF( textX, y, maxWidth, maxHeight ); + g.DrawString( part.Text, args.Font, textBrush, rect, format ); + textX += g.MeasureString( part.Text, args.Font, Int32.MaxValue, format ).Width; + } + format.Trimming = StringTrimming.None; + } + + public override Size MeasureSize( ref DrawTextArgs args ) { + GetTextParts( args.Text ); + SizeF total = SizeF.Empty; + for( int i = 0; i < parts.Count; i++ ) { + SizeF size = measuringGraphics.MeasureString( parts[i].Text, args.Font, Int32.MaxValue, format ); + total.Height = Math.Max( total.Height, size.Height ); + total.Width += size.Width; + } + + if( args.UseShadow && parts.Count > 0 ) { + total.Width += Offset; total.Height += Offset; + } + return Size.Ceiling( total ); + } + + public override void DisposeInstance() { + base.DisposeInstance(); + measuringGraphics.Dispose(); + measuringBmp.Dispose(); + } + } +} diff --git a/ClassicalSharp/2D/Drawing/GdiPlusDrawerMCFont.cs b/ClassicalSharp/2D/Drawing/GdiPlusDrawerMCFont.cs new file mode 100644 index 000000000..5bc889b64 --- /dev/null +++ b/ClassicalSharp/2D/Drawing/GdiPlusDrawerMCFont.cs @@ -0,0 +1,140 @@ +using System; +using System.Drawing; +using ClassicalSharp.GraphicsAPI; + +namespace ClassicalSharp { + + public sealed class GdiPlusDrawerMCFont : GdiPlusDrawer2D { + + // NOTE: This drawer is still a big work in progress and not close to done + // TODO: proper shadow colour + // TODO: italic and bold + Bitmap realBmp; + public GdiPlusDrawerMCFont( IGraphicsApi graphics ) : base( graphics ) { + realBmp = new Bitmap( "default.png" ); + CalculateTextWidths(); + } + + int[] widths = new int[256]; + unsafe void CalculateTextWidths() { + using( FastBitmap fastBmp = new FastBitmap( realBmp, true ) ) { + for( int i = 0; i < 256; i++ ) { + int tileX = (i & 0x0F) * 8; + int tileY = (i >> 4) * 8; + MakeTile( fastBmp, i, tileX, tileY ); + } + } + widths[(int)' '] = 4; + } + + unsafe void MakeTile( FastBitmap fastBmp, int i, int tileX, int tileY ) { + for( int x = 7; x >= 0; x-- ) { + for( int y = 0; y < 8; y++ ) { + uint pixel = (uint)fastBmp.GetRowPtr( tileY + y )[tileX + x]; + uint a = pixel >> 24; + if( a >= 127 ) { + widths[i] = x + 1; + Console.WriteLine( widths[i] ); + return; + } + } + } + widths[i] = 0; + } + + public override void DrawText( ref DrawTextArgs args, int x, int y ) { + if( !args.SkipPartsCheck ) + GetTextParts( args.Text ); + + if( args.UseShadow ) { + int shadowX = x + 1, shadowY = y + 1; + for( int i = 0; i < parts.Count; i++ ) { + TextPart part = parts[i]; + part.TextColour = FastColour.Black; + DrawPart( ref shadowX, shadowY, args.Font.Size, part ); + } + } + + for( int i = 0; i < parts.Count; i++ ) { + TextPart part = parts[i]; + DrawPart( ref x, y, args.Font.Size, part ); + } + } + + void DrawPart( ref int x, int y, float point, TextPart part ) { + string text = part.Text; + foreach( char c in text ) { + int coords = (int)c; + int srcX = (coords & 0x0F) * 8; + int srcY = (coords >> 4) * 8; + + for( int yy = 0; yy < 8; yy++ ) { + for( int xx = 0; xx < widths[coords]; xx++ ) { + FastColour col = realBmp.GetPixel( srcX + xx, srcY + yy ); + if( col.A < 127 ) continue; + col.R = (byte)(col.R * part.TextColour.R / 255); + col.G = (byte)(col.G * part.TextColour.G / 255); + col.B = (byte)(col.B * part.TextColour.B / 255); + + curBmp.SetPixel( x + xx, y + yy, col ); + } + } + x += widths[coords] + 1; + /*int rawWidth = widths[coords] + 1; + int rawHeight = 8; + g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; + g.DrawImage( realBmp, + new Rectangle( x, y, PtToPx( point, rawWidth ), PtToPx( point, rawHeight ) ), + new Rectangle( srcX, srcY, 8, 8 ), + GraphicsUnit.Pixel ); + + for( int yy = 0; yy < PtToPx( point, rawHeight ); yy++ ) { + for( int xx = 0; xx < PtToPx( point, rawWidth ); xx++ ) { + FastColour col = curBmp.GetPixel( x + xx, y + yy ); + if( col.A < 127 ) continue; + col.R = (byte)(col.R * part.TextColour.R / 255); + col.G = (byte)(col.G * part.TextColour.G / 255); + col.B = (byte)(col.B * part.TextColour.B / 255); + + curBmp.SetPixel( x + xx, y + yy, col ); + } + } + x += PtToPx( point, rawWidth );*/ + } + } + + public override void DrawClippedText( ref DrawTextArgs args, int x, int y, float maxWidth, float maxHeight ) { + throw new NotImplementedException(); + } + + public override Size MeasureSize( ref DrawTextArgs args ) { + GetTextParts( args.Text ); + float point = args.Font.Size; + //Size total = new Size( 0, PtToPx( point, 8 ) ); + Size total = new Size( 0, 8 ); + + for( int i = 0; i < parts.Count; i++ ) { + foreach( char c in parts[i].Text ) { + total.Width += widths[(int)c] + 1;//PtToPx( point, widths[(int)c] + 1 ); + } + } + + if( args.UseShadow && parts.Count > 0 ) { + total.Width += 1; total.Height += 1; + } + return total; + } + + int PtToPx( int point ) { + return (int)Math.Ceiling( (float)point / 72 * 96 ); // TODO: non 96 dpi? + } + + int PtToPx( float point, float value ) { + return (int)Math.Ceiling( (value / 8f) * point / 72f * 96f ); + } + + public override void DisposeInstance() { + base.DisposeInstance(); + } + } +} diff --git a/ClassicalSharp/2D/Drawing/IDrawer2D.cs b/ClassicalSharp/2D/Drawing/IDrawer2D.cs index 67fce1fe5..cc80da7f4 100644 --- a/ClassicalSharp/2D/Drawing/IDrawer2D.cs +++ b/ClassicalSharp/2D/Drawing/IDrawer2D.cs @@ -18,11 +18,11 @@ namespace ClassicalSharp { /// Draws a string using the specified arguments and fonts at the /// specified coordinates in the currently bound bitmap. - public abstract void DrawText( ref DrawTextArgs args, float x, float y ); + public abstract void DrawText( ref DrawTextArgs args, int x, int y ); /// Draws a string using the specified arguments and fonts at the /// specified coordinates in the currently bound bitmap, clipping if necessary. - public abstract void DrawClippedText( ref DrawTextArgs args, float x, float y, float maxWidth, float maxHeight ); + public abstract void DrawClippedText( ref DrawTextArgs args, int x, int y, float maxWidth, float maxHeight ); /// Draws a 2D flat rectangle of the specified dimensions at the /// specified coordinates in the currently bound bitmap. diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj index e1af5f97b..251a2fac9 100644 --- a/ClassicalSharp/ClassicalSharp.csproj +++ b/ClassicalSharp/ClassicalSharp.csproj @@ -75,6 +75,8 @@ + + diff --git a/ClassicalSharp/Game/Game.cs b/ClassicalSharp/Game/Game.cs index f02978046..62c7df727 100644 --- a/ClassicalSharp/Game/Game.cs +++ b/ClassicalSharp/Game/Game.cs @@ -99,7 +99,7 @@ namespace ClassicalSharp { InputHandler = new InputHandler( this ); Chat = new ChatLog( this ); Chat.FontSize = Options.GetInt( OptionsKey.FontSize, 6, 30, 12 ); - Drawer2D = new GdiPlusDrawer2D( Graphics ); + Drawer2D = new GdiPlusDrawerFont( Graphics ); defaultIb = Graphics.MakeDefaultIb(); MouseSensitivity = Options.GetInt( OptionsKey.Sensitivity, 1, 100, 30 ); BlockInfo = new BlockInfo(); diff --git a/Launcher2/LauncherWindow.cs b/Launcher2/LauncherWindow.cs index 81d78cfde..7bb4d1198 100644 --- a/Launcher2/LauncherWindow.cs +++ b/Launcher2/LauncherWindow.cs @@ -76,7 +76,7 @@ namespace Launcher2 { Window = new NativeWindow( 480, 480, Program.AppName, 0, GraphicsMode.Default, DisplayDevice.Default ); Window.Visible = true; - Drawer = new GdiPlusDrawer2D( null ); + Drawer = new GdiPlusDrawerFont( null ); Init(); platformDrawer.Init( Window.WindowInfo );