// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT using System; using System.Collections.Generic; using System.Drawing; using ClassicalSharp.GraphicsAPI; #if ANDROID using Android.Graphics; #endif namespace ClassicalSharp { /// Class responsible for performing drawing operations on bitmaps /// and for converting bitmaps into graphics api textures. /// Uses GDI+ on Windows, uses Cairo on Mono. public abstract partial class IDrawer2D : IDisposable { protected IGraphicsApi graphics; public const float Offset = 1.3f; /// Whether chat text should be drawn and measuring using the currently bitmapped font, /// false uses the font supplied as the DrawTextArgs argument supplied to the function. public bool UseBitmappedChat = false; /// Sets the underlying bitmap that drawing operations will be performed on. public abstract void SetBitmap( Bitmap bmp ); /// Draws a 2D flat rectangle of the specified dimensions at the /// specified coordinates in the currently bound bitmap. public abstract void DrawRect( FastColour colour, int x, int y, int width, int height ); /// Draws the outline of a 2D flat rectangle of the specified dimensions /// at the specified coordinates in the currently bound bitmap. public abstract void DrawRectBounds( FastColour colour, float lineWidth, int x, int y, int width, int height ); /// Clears the entire bound bitmap to the specified colour. public abstract void Clear( FastColour colour ); /// Clears the entire given area to the specified colour. public abstract void Clear( FastColour colour, int x, int y, int width, int height ); /// Disposes of any resources used by this class that are associated with the underlying bitmap. public abstract void Dispose(); /// Returns a new bitmap that has 32-bpp pixel format. public abstract Bitmap ConvertTo32Bpp( Bitmap src ); /// Returns a new bitmap that has 32-bpp pixel format. public void ConvertTo32Bpp( ref Bitmap src ) { Bitmap newBmp = ConvertTo32Bpp( src ); src.Dispose(); src = newBmp; } /// Draws a string using the specified arguments and font at the /// specified coordinates in the currently bound bitmap. 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, int x, int y, float maxWidth, float maxHeight ); /// Draws a string using the specified arguments and the current bitmapped font at the /// specified coordinates in the currently bound bitmap. public abstract void DrawBitmappedText( ref DrawTextArgs args, int x, int y ); /// Draws a string using the specified arguments, using the specified font or /// the current bitmapped font depending on 'UseBitmappedChat', at the /// specified coordinates in the currently bound bitmap. public void DrawChatText( ref DrawTextArgs args, int windowX, int windowY ) { if( !UseBitmappedChat ) DrawText( ref args, windowX, windowY ); else DrawBitmappedText( ref args, windowX, windowY ); } /// Returns the size of a bitmap needed to contain the specified text with the given arguments. public abstract Size MeasureSize( ref DrawTextArgs args ); /// Returns the size of a bitmap needed to contain the specified text with the given arguments, /// when drawn with the current bitmapped font. public abstract Size MeasureBitmappedSize( ref DrawTextArgs args ); /// Returns the size of a bitmap needed to contain the specified text with the given arguments, /// when drawn with the specified font or the current bitmapped font depending on 'UseBitmappedChat'. public Size MeasureChatSize( ref DrawTextArgs args ) { return !UseBitmappedChat ? MeasureSize( ref args ) : MeasureBitmappedSize( ref args ); } /// Draws the specified string from the arguments into a new bitmap, /// then creates a 2D texture with origin at the specified window coordinates. public Texture MakeTextTexture( ref DrawTextArgs args, int windowX, int windowY ) { return MakeTextureImpl( ref args, windowX, windowY, false ); } /// Draws the specified string from the arguments into a new bitmap, /// using the current bitmap font, then creates a 2D texture with origin at the /// specified window coordinates. public Texture MakeBitmappedTextTexture( ref DrawTextArgs args, int windowX, int windowY ) { return MakeTextureImpl( ref args, windowX, windowY, true ); } /// Draws the specified string from the arguments into a new bitmap, /// using the specified font or the current bitmapped font depending on 'UseBitmappedChat', /// then creates a 2D texture with origin at the specified window coordinates. public Texture MakeChatTextTexture( ref DrawTextArgs args, int windowX, int windowY ) { return MakeTextureImpl( ref args, windowX, windowY, UseBitmappedChat ); } Texture MakeTextureImpl( ref DrawTextArgs args, int windowX, int windowY, bool bitmapped ) { Size size = bitmapped ? MeasureBitmappedSize( ref args ) : MeasureSize( ref args ); if( size == Size.Empty ) return new Texture( -1, windowX, windowY, 0, 0, 1, 1 ); using( Bitmap bmp = CreatePow2Bitmap( size ) ) { SetBitmap( bmp ); args.SkipPartsCheck = true; if( !bitmapped ) DrawText( ref args, 0, 0 ); else DrawBitmappedText( ref args, 0, 0 ); Dispose(); return Make2DTexture( bmp, size, windowX, windowY ); } } /// Disposes of all native resources used by this class. /// You will no longer be able to perform measuring or drawing calls after this. public abstract void DisposeInstance(); /// Creates a 2D texture with origin at the specified window coordinates. public Texture Make2DTexture( Bitmap bmp, Size used, int windowX, int windowY ) { int texId = graphics.CreateTexture( bmp ); return new Texture( texId, windowX, windowY, used.Width, used.Height, (float)used.Width / bmp.Width, (float)used.Height / bmp.Height ); } /// Creates a power-of-2 sized bitmap larger or equal to to the given size. public static Bitmap CreatePow2Bitmap( Size size ) { return Platform.CreateBmp( Utils.NextPowerOf2( size.Width ), Utils.NextPowerOf2( size.Height ) ); } public FastColour[] Colours = new FastColour[256]; public IDrawer2D() { InitColours(); } public void InitColours() { for( int i = 0; i < Colours.Length; i++ ) Colours[i] = default(FastColour); for( int i = 0; i <= 9; i++ ) Colours['0' + i] = FastColour.GetHexEncodedCol( i ); for( int i = 10; i <= 15; i++) { Colours['a' + i - 10] = FastColour.GetHexEncodedCol( i ); Colours['A' + i - 10] = FastColour.GetHexEncodedCol( i ); } } protected List parts = new List( 64 ); protected struct TextPart { public string Text; public FastColour TextColour; public TextPart( string text, FastColour col ) { Text = text; TextColour = col; } } protected void GetTextParts( string value ) { parts.Clear(); if( String.IsNullOrEmpty( value ) ) { } else if( value.IndexOf( '&' ) == -1 ) { parts.Add( new TextPart( value, FastColour.White ) ); } else { SplitText( value ); } } /// Splits the input string by recognised colour codes. (e.g &f) protected void SplitText( string value ) { char code = 'f'; for( int i = 0; i < value.Length; i++ ) { int nextAnd = value.IndexOf( '&', i ); int partLength = nextAnd == -1 ? value.Length - i : nextAnd - i; if( partLength > 0 ) { string part = value.Substring( i, partLength ); FastColour col = Colours[code]; parts.Add( new TextPart( part, col ) ); } i += partLength + 1; if( nextAnd >= 0 && nextAnd + 1 < value.Length ) { if( !ValidColour( value[nextAnd + 1] ) ) { i--; // include character that isn't a valid colour code. } else { code = value[nextAnd + 1]; } } } } /// Returns whenever the given character is a valid colour code. public bool ValidColour( char c ) { return (int)c < 256 && Colours[c].A > 0; } /// Returns the last valid colour code in the given input, /// or \0 if no valid colour code was found. public char LastColour( string input, int start ) { if( start >= input.Length ) start = input.Length - 1; for( int i = start; i >= 0; i--) { if( input[i] != '&' ) continue; if( i < input.Length - 1 && ValidColour( input[i + 1] ) ) return input[i + 1]; } return '\0'; } public static bool IsWhiteColour( char c ) { return c == '\0' || c == 'f' || c == 'F'; } public void ReducePadding( ref Texture tex, int point ) { ReducePadding( ref tex, point, 4 ); } public void ReducePadding( ref int height, int point ) { ReducePadding( ref height, point, 4 ); } public void ReducePadding( ref Texture tex, int point, int scale ) { if( !UseBitmappedChat ) return; point = AdjTextSize( point ); int padding = (tex.Height - point) / scale; float vAdj = (float)padding / Utils.NextPowerOf2( tex.Height ); tex.V1 += vAdj; tex.V2 -= vAdj; tex.Height -= padding * 2; } public void ReducePadding( ref int height, int point, int scale ) { if( !UseBitmappedChat ) return; point = AdjTextSize( point ); int padding = (height - point) / scale; height -= padding * 2; } } }