mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-24 10:02:08 -05:00
247 lines
8.3 KiB
C#
247 lines
8.3 KiB
C#
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
|
|
using System;
|
|
using System.Drawing;
|
|
using ClassicalSharp.GraphicsAPI;
|
|
#if ANDROID
|
|
using Android.Graphics;
|
|
#endif
|
|
|
|
namespace ClassicalSharp {
|
|
|
|
/// <summary> Class responsible for performing drawing operations on bitmaps
|
|
/// and for converting bitmaps into graphics api textures. </summary>
|
|
/// <remarks> Uses GDI+ on Windows, uses Cairo on Mono. </remarks>
|
|
unsafe partial class IDrawer2D {
|
|
|
|
public Bitmap FontBitmap;
|
|
|
|
/// <summary> Sets the bitmap that contains the bitmapped font characters as an atlas. </summary>
|
|
public void SetFontBitmap( Bitmap bmp ) {
|
|
FontBitmap = bmp;
|
|
boxSize = FontBitmap.Width / 16;
|
|
fontPixels = new FastBitmap( FontBitmap, true, true );
|
|
CalculateTextWidths();
|
|
}
|
|
|
|
protected FastBitmap fontPixels;
|
|
protected int boxSize;
|
|
protected const int italicSize = 8;
|
|
protected int[] widths = new int[256];
|
|
void CalculateTextWidths() {
|
|
for( int i = 0; i < 256; i++ )
|
|
MakeTile( i, (i & 0x0F) * boxSize, (i >> 4) * boxSize );
|
|
widths[(int)' '] = boxSize / 4;
|
|
}
|
|
|
|
void MakeTile( int i, int tileX, int tileY ) {
|
|
// find first column (from right) where there is a solid pixel
|
|
for( int x = boxSize - 1; x >= 0; x-- )
|
|
for( int y = 0; y < boxSize; y++ )
|
|
{
|
|
int pixel = fontPixels.GetRowPtr( tileY + y )[tileX + x];
|
|
byte a = (byte)(pixel >> 24);
|
|
if( a >= 127 ) { // found a solid pixel
|
|
widths[i] = x + 1;
|
|
return;
|
|
}
|
|
}
|
|
widths[i] = 0;
|
|
}
|
|
|
|
protected void DrawBitmapTextImpl( FastBitmap dst, ref DrawTextArgs args, int x, int y ) {
|
|
bool underline = args.Font.Style == FontStyle.Underline;
|
|
if( args.UseShadow ) {
|
|
int offset = ShadowOffset( args.Font.Size );
|
|
DrawPart( dst, ref args, x + offset, y + offset, true );
|
|
if( underline ) DrawUnderline( dst, x + offset, 0, ref args, true );
|
|
}
|
|
|
|
DrawPart( dst, ref args, x, y, false );
|
|
if( underline ) DrawUnderline( dst, x, -2, ref args, false );
|
|
}
|
|
|
|
void DrawPart( FastBitmap dst, ref DrawTextArgs args, int x, int y, bool shadowCol ) {
|
|
FastColour col = Colours['f'];
|
|
if( shadowCol )
|
|
col = BlackTextShadows ? FastColour.Black : FastColour.Scale( col, 0.25f );
|
|
FastColour lastCol = col;
|
|
|
|
int xMul = args.Font.Style == FontStyle.Italic ? 1 : 0;
|
|
int runCount = 0, lastY = -1;
|
|
string text = args.Text;
|
|
int point = Utils.Floor( args.Font.Size );
|
|
byte* coordsPtr = stackalloc byte[256];
|
|
|
|
for( int i = 0; i < text.Length; i++ ) {
|
|
char c = text[i];
|
|
bool code = c == '&' && i < text.Length - 1;
|
|
if( code && ValidColour( text[i + 1] ) ) {
|
|
col = Colours[text[i + 1]];
|
|
if( shadowCol )
|
|
col = BlackTextShadows ? FastColour.Black : FastColour.Scale( col, 0.25f );
|
|
i++; continue; // Skip over the colour code.
|
|
}
|
|
int coords = ConvertToCP437( c );
|
|
|
|
// First character in the string, begin run counting
|
|
if( lastY == -1 ) {
|
|
lastY = coords >> 4; lastCol = col;
|
|
coordsPtr[0] = (byte)coords; runCount = 1;
|
|
continue;
|
|
}
|
|
if( lastY == (coords >> 4) && col == lastCol ) {
|
|
coordsPtr[runCount] = (byte)coords; runCount++;
|
|
continue;
|
|
}
|
|
|
|
DrawRun( dst, x, y, xMul, runCount, coordsPtr, point, lastCol );
|
|
lastY = coords >> 4; lastCol = col;
|
|
for( int j = 0; j < runCount; j++ )
|
|
x += PtToPx( point, widths[coordsPtr[j]] + 1 );
|
|
coordsPtr[0] = (byte)coords; runCount = 1;
|
|
}
|
|
DrawRun( dst, x, y, xMul, runCount, coordsPtr, point, lastCol );
|
|
}
|
|
|
|
void DrawRun( FastBitmap dst, int x, int y, int xMul,
|
|
int runCount, byte* coords, int point, FastColour col ) {
|
|
if( runCount == 0 ) return;
|
|
int srcY = (coords[0] >> 4) * boxSize;
|
|
int textHeight = AdjTextSize( point ), cellHeight = CellSize( textHeight );
|
|
int padding = (cellHeight - textHeight) / 2;
|
|
int startX = x;
|
|
|
|
ushort* dstWidths = stackalloc ushort[runCount];
|
|
for( int i = 0; i < runCount; i++ )
|
|
dstWidths[i] = (ushort)PtToPx( point, widths[coords[i]] );
|
|
|
|
for( int yy = 0; yy < textHeight; yy++ ) {
|
|
int fontY = srcY + yy * boxSize / textHeight;
|
|
int* fontRow = fontPixels.GetRowPtr( fontY );
|
|
int dstY = y + (yy + padding);
|
|
if( dstY >= dst.Height ) return;
|
|
|
|
int* dstRow = dst.GetRowPtr( dstY );
|
|
int xOffset = xMul * ((textHeight - 1 - yy) / italicSize);
|
|
for( int i = 0; i < runCount; i++ ) {
|
|
int srcX = (coords[i] & 0x0F) * boxSize;
|
|
int srcWidth = widths[coords[i]], dstWidth = dstWidths[i];
|
|
|
|
for( int xx = 0; xx < dstWidth; xx++ ) {
|
|
int fontX = srcX + xx * srcWidth / dstWidth;
|
|
int src = fontRow[fontX];
|
|
if( (byte)(src >> 24) == 0 ) continue;
|
|
int dstX = x + xx + xOffset;
|
|
if( dstX >= dst.Width ) break;
|
|
|
|
int pixel = src & ~0xFFFFFF;
|
|
pixel |= ((src & 0xFF) * col.B / 255);
|
|
pixel |= (((src >> 8) & 0xFF) * col.G / 255) << 8;
|
|
pixel |= (((src >> 16) & 0xFF) * col.R / 255) << 16;
|
|
dstRow[dstX] = pixel;
|
|
}
|
|
x += PtToPx( point, srcWidth + 1 );
|
|
}
|
|
x = startX;
|
|
}
|
|
}
|
|
|
|
void DrawUnderline( FastBitmap dst, int x, int yOffset, ref DrawTextArgs args, bool shadowCol ) {
|
|
int point = Utils.Floor( args.Font.Size );
|
|
int padding = CellSize( point ) - AdjTextSize( point );
|
|
int height = AdjTextSize( point ) + Utils.CeilDiv( padding, 2 );
|
|
int offset = ShadowOffset( args.Font.Size );
|
|
|
|
int col = FastColour.White.ToArgb();
|
|
string text = args.Text;
|
|
if( args.UseShadow ) height += offset;
|
|
int startX = x;
|
|
|
|
for( int yy = height - offset; yy < height; yy++ ) {
|
|
int* dstRow = dst.GetRowPtr( yy + yOffset );
|
|
|
|
for( int i = 0; i < text.Length; i++ ) {
|
|
char c = text[i];
|
|
bool code = c == '&' && i < text.Length - 1;
|
|
if( code && ValidColour( text[i + 1] ) ) {
|
|
col = Colours[text[i + 1]].ToArgb();
|
|
i++; continue; // Skip over the colour code.
|
|
}
|
|
if( shadowCol ) col = FastColour.Black.ToArgb();
|
|
|
|
int coords = ConvertToCP437( c );
|
|
int width = PtToPx( point, widths[coords] + 1 );
|
|
for( int xx = 0; xx < width; xx++ )
|
|
dstRow[x + xx] = col;
|
|
x += width;
|
|
}
|
|
x = startX;
|
|
col = FastColour.White.ToArgb();
|
|
}
|
|
}
|
|
|
|
protected Size MeasureBitmappedSizeImpl( ref DrawTextArgs args ) {
|
|
if( String.IsNullOrEmpty( args.Text ) ) return Size.Empty;
|
|
int textHeight = AdjTextSize( Utils.Floor( args.Font.Size ) );
|
|
Size total = new Size( 0, CellSize( textHeight ) );
|
|
int point = Utils.Floor( args.Font.Size );
|
|
|
|
for( int i = 0; i < args.Text.Length; i++ ) {
|
|
char c = args.Text[i];
|
|
bool code = c == '&' && i < args.Text.Length - 1;
|
|
if( code && ValidColour( args.Text[i + 1] ) ) {
|
|
i++; continue; // Skip over the colour code.
|
|
}
|
|
|
|
int coords = ConvertToCP437( c );
|
|
total.Width += PtToPx( point, widths[coords] + 1 );
|
|
}
|
|
|
|
if( args.Font.Style == FontStyle.Italic )
|
|
total.Width += Utils.CeilDiv( total.Height, italicSize );
|
|
if( args.UseShadow ) {
|
|
int offset = ShadowOffset( args.Font.Size );
|
|
total.Width += offset; total.Height += offset;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
protected static int ConvertToCP437( char c ) {
|
|
if( c >= ' ' && c <= '~')
|
|
return (int)c;
|
|
|
|
int cIndex = Utils.ControlCharReplacements.IndexOf( c );
|
|
if( cIndex >= 0 ) return cIndex;
|
|
int eIndex = Utils.ExtendedCharReplacements.IndexOf( c );
|
|
if( eIndex >= 0 ) return 127 + eIndex;
|
|
return (int)'?';
|
|
}
|
|
|
|
protected int ShadowOffset( float fontSize ) {
|
|
if( fontSize < 13 ) return 1;
|
|
if( fontSize < 32 ) return 2;
|
|
return 3;
|
|
}
|
|
|
|
protected int PtToPx( int point, int value ) {
|
|
return Utils.CeilDiv( value * point, boxSize );
|
|
}
|
|
|
|
/// <summary> Rounds the given font size up to the nearest whole
|
|
/// multiple of the size of a character in default.png. </summary>
|
|
protected int AdjTextSize( int point ) {
|
|
return point; //Utils.CeilDiv( point, boxSize ) * boxSize;
|
|
}
|
|
|
|
/// <summary> Returns the height of the bounding box that
|
|
/// contains both the given font size in addition to padding. </summary>
|
|
protected static int CellSize( int point ) {
|
|
return Utils.CeilDiv( point * 3, 2 );
|
|
}
|
|
|
|
protected void DisposeBitmappedText() {
|
|
fontPixels.Dispose();
|
|
FontBitmap.Dispose();
|
|
}
|
|
}
|
|
}
|