Proper fix of UnsafeString on windows platforms - should fix input text and block info string.

This commit is contained in:
UnknownShadow200 2015-09-02 19:44:24 +10:00
parent f33428a531
commit c163ebcb59
8 changed files with 195 additions and 144 deletions

View File

@ -28,7 +28,7 @@ namespace ClassicalSharp {
int rows; int rows;
int startX, startY; int startX, startY;
readonly Font font; readonly Font font;
UnsafeString buffer = new UnsafeString( 96 ); StringBuffer buffer = new StringBuffer( 96 );
public override void Render( double delta ) { public override void Render( double delta ) {
graphicsApi.Texturing = true; graphicsApi.Texturing = true;
@ -130,9 +130,7 @@ namespace ClassicalSharp {
Block block = blocksTable[selectedIndex].BlockId; Block block = blocksTable[selectedIndex].BlockId;
UpdateBlockInfoString( block ); UpdateBlockInfoString( block );
string value = buffer.value; string value = buffer.UpdateCachedString();
if( Utils2D.needWinXpFix )
value = value.TrimEnd( Utils2D.trimChars );
Size size = Utils2D.MeasureSize( value, font, true ); Size size = Utils2D.MeasureSize( value, font, true );
int x = startX + ( blockSize * blocksPerRow ) / 2 - size.Width / 2; int x = startX + ( blockSize * blocksPerRow ) / 2 - size.Width / 2;

View File

@ -6,10 +6,10 @@ namespace ClassicalSharp {
public class FpsScreen : Screen { public class FpsScreen : Screen {
readonly Font font; readonly Font font;
UnsafeString text; StringBuffer text;
public FpsScreen( Game window ) : base( window ) { public FpsScreen( Game window ) : base( window ) {
font = new Font( "Arial", 13 ); font = new Font( "Arial", 13 );
text = new UnsafeString( 96 ); text = new StringBuffer( 96 );
} }
TextWidget fpsTextWidget; TextWidget fpsTextWidget;
@ -37,10 +37,8 @@ namespace ClassicalSharp {
.Append( ref ptr2, "), chunks/s: " ).AppendNum( ref ptr2, game.ChunkUpdates ) .Append( ref ptr2, "), chunks/s: " ).AppendNum( ref ptr2, game.ChunkUpdates )
.Append( ref ptr2, ", vertices: " ).AppendNum( ref ptr2, game.Vertices ); .Append( ref ptr2, ", vertices: " ).AppendNum( ref ptr2, game.Vertices );
} }
string textString = text.value;
if( Utils2D.needWinXpFix )
textString = textString.TrimEnd( Utils2D.trimChars );
string textString = text.UpdateCachedString();
fpsTextWidget.SetText( textString ); fpsTextWidget.SetText( textString );
maxDelta = 0; maxDelta = 0;
accumulator = 0; accumulator = 0;

View File

@ -12,8 +12,6 @@ namespace ClassicalSharp {
static Bitmap measuringBmp; static Bitmap measuringBmp;
static Graphics measuringGraphics; static Graphics measuringGraphics;
static Dictionary<int, SolidBrush> brushCache = new Dictionary<int, SolidBrush>( 16 ); static Dictionary<int, SolidBrush> brushCache = new Dictionary<int, SolidBrush>( 16 );
internal static bool needWinXpFix;
internal static char[] trimChars = { '\0' };
static Utils2D() { static Utils2D() {
format = StringFormat.GenericTypographic; format = StringFormat.GenericTypographic;
@ -24,8 +22,6 @@ namespace ClassicalSharp {
measuringBmp = new Bitmap( 1, 1 ); measuringBmp = new Bitmap( 1, 1 );
measuringGraphics = Graphics.FromImage( measuringBmp ); measuringGraphics = Graphics.FromImage( measuringBmp );
measuringGraphics.TextRenderingHint = TextRenderingHint.AntiAlias; measuringGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
OperatingSystem os = Environment.OSVersion;
needWinXpFix = os.Platform == PlatformID.Win32NT && os.Version.Major < 6;
} }
static SolidBrush GetOrCreateBrush( Color color ) { static SolidBrush GetOrCreateBrush( Color color ) {

View File

@ -19,7 +19,7 @@ namespace ClassicalSharp {
typingLogPos = game.ChatInputLog.Count; // Index of newest entry + 1. typingLogPos = game.ChatInputLog.Count; // Index of newest entry + 1.
this.font = font; this.font = font;
this.boldFont = boldFont; this.boldFont = boldFont;
chatInputText = new UnsafeString( 64 ); chatInputText = new StringBuffer( 64 );
} }
Texture chatInputTexture, chatCaretTexture; Texture chatInputTexture, chatCaretTexture;
@ -27,7 +27,7 @@ namespace ClassicalSharp {
int caretPos = -1; int caretPos = -1;
int typingLogPos = 0; int typingLogPos = 0;
public int ChatInputYOffset; public int ChatInputYOffset;
internal UnsafeString chatInputText; internal StringBuffer chatInputText;
readonly Font font, boldFont; readonly Font font, boldFont;
public override void Render( double delta ) { public override void Render( double delta ) {
@ -40,9 +40,7 @@ namespace ClassicalSharp {
X = 10; X = 10;
DrawTextArgs caretArgs = new DrawTextArgs( graphicsApi, "_", Color.White, false ); DrawTextArgs caretArgs = new DrawTextArgs( graphicsApi, "_", Color.White, false );
chatCaretTexture = Utils2D.MakeTextTexture( boldFont, 0, 0, ref caretArgs ); chatCaretTexture = Utils2D.MakeTextTexture( boldFont, 0, 0, ref caretArgs );
string value = chatInputText.value; string value = chatInputText.UpdateCachedString();
if( Utils2D.needWinXpFix )
value = value.TrimEnd( Utils2D.trimChars );
if( chatInputText.Empty ) { if( chatInputText.Empty ) {
caretPos = -1; caretPos = -1;
@ -53,9 +51,12 @@ namespace ClassicalSharp {
chatCaretTexture.X1 = 10 + size.Width; chatCaretTexture.X1 = 10 + size.Width;
size.Width += chatCaretTexture.Width; size.Width += chatCaretTexture.Width;
} else { } else {
Size trimmedSize = Utils2D.MeasureSize( value.Substring( 0, caretPos ), font, false ); string subString = chatInputText.GetSubstring( caretPos );
Size trimmedSize = Utils2D.MeasureSize( subString, font, false );
chatInputText.RestoreLength();
chatCaretTexture.X1 = 10 + trimmedSize.Width; chatCaretTexture.X1 = 10 + trimmedSize.Width;
Size charSize = Utils2D.MeasureSize( value.Substring( caretPos, 1 ), font, false ); Size charSize = Utils2D.MeasureSize( new String( value[caretPos], 1 ), font, false );
chatCaretTexture.Width = charSize.Width; chatCaretTexture.Width = charSize.Width;
} }
size.Height = Math.Max( size.Height, chatCaretTexture.Height ); size.Height = Math.Max( size.Height, chatCaretTexture.Height );

View File

@ -164,7 +164,7 @@
<Compile Include="Utils\TerrainAtlas1D.cs" /> <Compile Include="Utils\TerrainAtlas1D.cs" />
<Compile Include="Utils\TerrainAtlas2D.cs" /> <Compile Include="Utils\TerrainAtlas2D.cs" />
<Compile Include="Utils\TextureRectangle.cs" /> <Compile Include="Utils\TextureRectangle.cs" />
<Compile Include="Utils\UnsafeString.cs" /> <Compile Include="Utils\StringBuffer.cs" />
<Compile Include="Utils\Utils.cs" /> <Compile Include="Utils\Utils.cs" />
<Compile Include="Utils\Vector3I.cs" /> <Compile Include="Utils\Vector3I.cs" />
</ItemGroup> </ItemGroup>

View File

@ -0,0 +1,180 @@
using System;
using System.Reflection;
namespace ClassicalSharp {
// Class used to minimise memory allocations of strings.
// Really, only useful for FpsScreen and TextInputWidget as they allocate lots of very small strings.
// Seriously, you should *not* use this anywhere else, as Empty and Length are O(N).
internal sealed unsafe class StringBuffer {
internal string value;
internal int capacity;
static readonly FieldInfo arrayField, lengthField;
static bool supportsLengthSetting;
static StringBuffer() {
if( OpenTK.Configuration.RunningOnMono )
return;
arrayField = typeof( String ).GetField( "m_arrayLength", BindingFlags.NonPublic | BindingFlags.Instance );
lengthField = typeof( String ).GetField( "m_stringLength", BindingFlags.NonPublic | BindingFlags.Instance );
// Make sure we are running on a framework that has both methods -
// otherwise we play it safe and just use TrimEnd().
if( arrayField != null && lengthField != null ) {
supportsLengthSetting = true;
} else {
arrayField = null;
lengthField = null;
}
}
public StringBuffer( int capacity ) {
this.capacity = capacity;
value = new String( '\0', capacity );
}
public StringBuffer Append( int index, char c ) {
fixed( char* ptr = value ) {
ptr[index] = c;
return this;
}
}
public StringBuffer Append( int index, string s ) {
fixed( char* ptr = value ) {
char* offsetPtr = ptr + index;
return Append( ref offsetPtr, s );
}
}
public StringBuffer Append( ref char* ptr, string s ) {
for( int i = 0; i < s.Length; i++ ) {
*ptr++ = s[i];
}
return this;
}
public StringBuffer Append( ref char* ptr, char c) {
*ptr++ = c;
return this;
}
static char[] numBuffer = new char[20];
public StringBuffer AppendNum( ref char* ptr, long num ) {
int index = 0;
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
num /= 10;
while( num > 0 ) {
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
num /= 10;
}
for( int i = index - 1; i >= 0; i-- ) {
*ptr++ = numBuffer[i];
}
return this;
}
public StringBuffer Clear() {
fixed( char* ptr = value ) {
return Clear( ptr );
}
}
public StringBuffer Clear( char* ptr ) {
for( int i = 0; i < capacity; i++ ) {
*ptr++ = '\0';
}
return this;
}
public void DeleteAt( int index ) {
fixed( char* ptr = value ) {
char* offsetPtr = ptr + index;
for( int i = index; i < capacity - 1; i++ ) {
*offsetPtr = *( offsetPtr + 1 );
offsetPtr++;
}
*offsetPtr = '\0';
}
}
public void InsertAt( int index, char c ) {
fixed( char* ptr = value ) {
char* offsetPtr = ptr + capacity - 1;
for( int i = capacity - 1; i > index; i-- ) {
*offsetPtr = *( offsetPtr - 1 );
offsetPtr--;
}
*offsetPtr = c;
}
}
public bool Empty {
get {
fixed( char* ptr = value ) {
for( int i = 0; i < capacity; i++ ) {
if( ptr[i] != '\0' )
return false;
}
}
return true;
}
}
public int Length {
get {
int len = capacity;
fixed( char* ptr = value ) {
for( int i = capacity - 1; i >= 0; i-- ) {
if( ptr[i] != '\0' )
break;
len--;
}
}
return len;
}
}
/// <summary> Hack that modifies the underlying string's length to avoid memory allocations. </summary>
/// <returns> The underlying string - ***do not*** store this because it is mutable!
/// You should only use this string for temporary measuring and drawing. </returns>
public string UpdateCachedString() {
return supportsLengthSetting ? value : value.TrimEnd( trimEnd );
}
static char[] trimEnd = { '\0' };
public override string ToString() {
return GetCopy( Length );
}
public string GetSubstring( int length ) {
return supportsLengthSetting ? SetLength( length ) : GetCopy( length );
}
public void RestoreLength() {
if( supportsLengthSetting )
SetLength( Length );
}
string SetLength( int len ) {
arrayField.SetValue( value, len + 1 );
lengthField.SetValue( value, len );
return value;
}
string GetCopy( int len ) {
string copiedString = new String( '\0', len );
fixed( char* src = value, dst = copiedString ) {
for( int i = 0; i < len; i++ ) {
dst[i] = src[i];
}
}
return copiedString;
}
}
}

View File

@ -1,122 +0,0 @@
using System;
namespace ClassicalSharp {
// Class used to minimise memory allocations of strings.
// Really, only useful for FpsScreen and TextInputWidget as they allocate lots of very small strings.
// Seriously, you should *not* use this anywhere else, as Empty and Length are O(N).
internal sealed unsafe class UnsafeString {
internal string value;
internal int capacity;
public UnsafeString( int capacity ) {
this.capacity = capacity;
value = new String( '\0', capacity );
}
public UnsafeString Append( int index, char c ) {
fixed( char* ptr = value ) {
ptr[index] = c;
return this;
}
}
public UnsafeString Append( int index, string s ) {
fixed( char* ptr = value ) {
char* offsetPtr = ptr + index;
return Append( ref offsetPtr, s );
}
}
public UnsafeString Append( ref char* ptr, string s ) {
for( int i = 0; i < s.Length; i++ ) {
*ptr++ = s[i];
}
return this;
}
public UnsafeString Append( ref char* ptr, char c) {
*ptr++ = c;
return this;
}
static char[] numBuffer = new char[20];
public UnsafeString AppendNum( ref char* ptr, long num ) {
int index = 0;
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
num /= 10;
while( num > 0 ) {
numBuffer[index++] = (char)( '0' + ( num % 10 ) );
num /= 10;
}
for( int i = index - 1; i >= 0; i-- ) {
*ptr++ = numBuffer[i];
}
return this;
}
public UnsafeString Clear() {
fixed( char* ptr = value ) {
return Clear( ptr );
}
}
public UnsafeString Clear( char* ptr ) {
for( int i = 0; i < capacity; i++ ) {
*ptr++ = '\0';
}
return this;
}
public void DeleteAt( int index ) {
fixed( char* ptr = value ) {
char* offsetPtr = ptr + index;
for( int i = index; i < capacity - 1; i++ ) {
*offsetPtr = *( offsetPtr + 1 );
offsetPtr++;
}
*offsetPtr = '\0';
}
}
public void InsertAt( int index, char c ) {
fixed( char* ptr = value ) {
char* offsetPtr = ptr + capacity - 1;
for( int i = capacity - 1; i > index; i-- ) {
*offsetPtr = *( offsetPtr - 1 );
offsetPtr--;
}
*offsetPtr = c;
}
}
public bool Empty {
get {
for( int i = 0; i < capacity; i++ ) {
if( value[i] != '\0' )
return false;
}
return true;
}
}
public int Length {
get {
int len = capacity;
for( int i = capacity - 1; i >= 0; i-- ) {
if( value[i] != '\0' )
break;
len--;
}
return len;
}
}
public override string ToString() {
return value.TrimEnd( '\0' );
}
}
}

View File

@ -113,7 +113,7 @@ namespace SharpDX.Direct3D9 {
return lockedRect; return lockedRect;
} }
public void SetData( int level, LockFlags flags, IntPtr data,int bytes ) { public void SetData( int level, LockFlags flags, IntPtr data, int bytes ) {
LockedRectangle rect = LockRectangle( level, flags ); LockedRectangle rect = LockRectangle( level, flags );
MemUtils.memcpy( data, rect.DataPointer, bytes ); MemUtils.memcpy( data, rect.DataPointer, bytes );
UnlockRectangle( level ); UnlockRectangle( level );