From 12f82d926e3c95d18eeba09697922026b58d5428 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Tue, 27 Jan 2015 10:10:43 +1100 Subject: [PATCH] Optimise pixel operations with FastBitmap. --- Blocks/BlockInfo.Optimised.cs | 7 ++-- FastBitmap.cs | 71 +++++++++++------------------------ TextureAtlas2D.cs | 15 ++++---- 3 files changed, 33 insertions(+), 60 deletions(-) diff --git a/Blocks/BlockInfo.Optimised.cs b/Blocks/BlockInfo.Optimised.cs index 1fde09df8..b75217f8a 100644 --- a/Blocks/BlockInfo.Optimised.cs +++ b/Blocks/BlockInfo.Optimised.cs @@ -164,11 +164,12 @@ namespace ClassicalSharp { } } - static void MovePortion( int srcX, int srcY, int destX, int destY, FastBitmap src, int size ) { + unsafe static void MovePortion( int srcX, int srcY, int dstX, int dstY, FastBitmap src, int size ) { for( int y = 0; y < size; y++ ) { + int* srcRow = src.GetRowPtr( srcY + y ); + int* dstRow = src.GetRowPtr( dstY + y ); for( int x = 0; x < size; x++ ) { - int col = src.GetPixel( srcX + x, srcY + y ); - src.SetPixel( destX + x, destY + y, col ); + dstRow[dstX + x] = srcRow[srcX + x]; } } } diff --git a/FastBitmap.cs b/FastBitmap.cs index 5e606a742..7944bc996 100644 --- a/FastBitmap.cs +++ b/FastBitmap.cs @@ -6,33 +6,8 @@ namespace ClassicalSharp { /// Represents a wrapper around a bitmap. Provides /// a fast implementation for getting and setting pixels in that bitmap. - public class FastBitmap : IDisposable { - - unsafe abstract class PixelAccessor { - public byte* scan0; - public int stride; - - public abstract int GetPixel( int x, int y ); - - public abstract void SetPixel( int x, int y, int col ); - } - - unsafe class _32bppARGBAccessor : PixelAccessor { - - public override int GetPixel( int x, int y ) { - // row + ( x << 2 ) points to the B part of the pixel at (x, bottomY/row) - // casting to Int* then returning the value is a cheap way of - // doing the bitwise arithmetic that would otherwise be used. - // TODO: Does this work with big-endian systems? I don't know. - int* row = (int*)( scan0 + ( y * stride ) ); - return row[x]; // b g r a - } - - public override void SetPixel( int x, int y, int col ) { - int* row = (int*)( scan0 + ( y * stride ) ); - row[x] = col; - } - } + /// Only supports 32 bit RGBA pixel format. + public unsafe class FastBitmap : IDisposable { /// Constructs a new FastBitmap wrapped around the specified Bitmap. /// Bitmap which is wrapped. @@ -47,7 +22,8 @@ namespace ClassicalSharp { public Bitmap Bitmap; BitmapData data; - PixelAccessor accessor; + byte* scan0; + int stride; public bool IsLocked { get { return data != null; } @@ -58,7 +34,7 @@ namespace ClassicalSharp { get { return data.Scan0; } } - /// Gets the stride width/scan width of the bitmap. + /// Gets the stride width/scan width of the bitmap. /// (i.e. the actual size of each scanline, including padding) public int Stride { get { return data.Stride; } @@ -74,26 +50,23 @@ namespace ClassicalSharp { get { return data.Height; } } - /// Locks the wrapped bitmap into system memory, + /// Locks the wrapped bitmap into system memory, /// so that fast get/set pixel operations can be performed. - /// Only 24 and 32 bit bitmap formats are supported. - public unsafe void LockBits() { + public void LockBits() { if( Bitmap == null ) throw new InvalidOperationException( "Bmp is null." ); if( data != null ) { Bitmap.UnlockBits( data ); } PixelFormat format = Bitmap.PixelFormat; - if( format == PixelFormat.Format32bppArgb ) { - accessor = new _32bppARGBAccessor(); - } else { + if( format != PixelFormat.Format32bppArgb ) { throw new NotSupportedException( "Unsupported bitmap pixel format:" + format ); } Rectangle rec = new Rectangle( 0, 0, Bitmap.Width, Bitmap.Height ); data = Bitmap.LockBits( rec, ImageLockMode.ReadWrite, format ); - accessor.scan0 = (byte*)data.Scan0; - accessor.stride = data.Stride; + scan0 = (byte*)data.Scan0; + stride = data.Stride; } public void Dispose() { @@ -104,26 +77,24 @@ namespace ClassicalSharp { if( data != null ) { Bitmap.UnlockBits( data ); data = null; - accessor = null; + scan0 = (byte*)IntPtr.Zero; + stride = 0; } } - /// Gets the colour of the pixel at the specified coordinates. - /// X coordinate of the pixel. - /// Y coordinate of the pixel. - /// Does *not* perform bounds checking for performance reasons. - /// The colour at that pixel, in ARGB form. (A is highest 24 bits) public int GetPixel( int x, int y ) { - return accessor.GetPixel( x, y ); + // TODO: Does this work with big-endian systems? + int* row = (int*)( scan0 + ( y * stride ) ); + return row[x]; // b g r a + } + + public int* GetRowPtr( int y ) { + return (int*)( scan0 + ( y * stride ) ); } - /// Sets the colour of the pixel at the specified coordinates. - /// X coordinate of the pixel. - /// Y coordinate of the pixel. - /// The new colour of the pixel, in ARGB form. (A is highest 8 bits) - /// Does *not* perform bounds checking for performance reasons. public void SetPixel( int x, int y, int col ) { - accessor.SetPixel( x, y, col ); + int* row = (int*)( scan0 + ( y * stride ) ); + row[x] = col; } } } \ No newline at end of file diff --git a/TextureAtlas2D.cs b/TextureAtlas2D.cs index f03aa83b0..be9c64139 100644 --- a/TextureAtlas2D.cs +++ b/TextureAtlas2D.cs @@ -146,13 +146,14 @@ namespace ClassicalSharp { y = id / ElementsPerRow; } - void CopyPortion( int x, int y, int destX, int destY, FastBitmap atlas, FastBitmap dest ) { - int atlasX = x * horElementSize; - int atlasY = y * verElementSize; - for( int pixelY = 0; pixelY < verElementSize; pixelY++ ) { - for( int pixelX = 0; pixelX < horElementSize; pixelX++ ) { - int col = atlas.GetPixel( atlasX + pixelX, atlasY + pixelY ); - dest.SetPixel( destX + pixelX, destY + pixelY, col ); + unsafe void CopyPortion( int tileX, int tileY, int dstX, int dstY, FastBitmap atlas, FastBitmap dst ) { + int atlasX = tileX * horElementSize; + int atlasY = tileY * verElementSize; + for( int y = 0; y < verElementSize; y++ ) { + int* srcRow = atlas.GetRowPtr( atlasY + y ); + int* dstRow = dst.GetRowPtr( dstY + y ); + for( int x = 0; x < horElementSize; x++ ) { + dstRow[dstX + x] = srcRow[atlasX + x]; } } }