ClassiCube/ClassicalSharp/2D/Drawing/GdiPlusDrawer2D.TextMC.cs

151 lines
4.6 KiB
C#

using System;
using System.Drawing;
using ClassicalSharp.GraphicsAPI;
namespace ClassicalSharp {
public unsafe sealed partial class GdiPlusDrawer2D {
// NOTE: This drawer is still a big work in progress and not close to done
// TODO: italic and bold
Bitmap fontBmp;
FastBitmap fontPixels;
int boxSize;
const int italicSize = 8;
public override void SetFontBitmap( Bitmap bmp ) {
fontBmp = bmp;
boxSize = fontBmp.Width / 16;
fontPixels = new FastBitmap( fontBmp, true );
CalculateTextWidths();
}
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;
}
public override void DrawBitmappedText( ref DrawTextArgs args, int x, int y ) {
if( !args.SkipPartsCheck )
GetTextParts( args.Text );
using( FastBitmap fastBmp = new FastBitmap( curBmp, true ) )
DrawTextImpl( fastBmp, ref args, x, y );
}
void DrawTextImpl( FastBitmap fastBmp, ref DrawTextArgs args, int x, int y ) {
bool italic = args.Font.Style == FontStyle.Italic;
if( args.UseShadow ) {
int shadowX = x + 2, shadowY = y + 2;
for( int i = 0; i < parts.Count; i++ ) {
TextPart part = parts[i];
part.TextColour = FastColour.Black;
DrawPart( fastBmp, args.Font, ref shadowX, shadowY, part );
}
}
for( int i = 0; i < parts.Count; i++ ) {
TextPart part = parts[i];
DrawPart( fastBmp, args.Font, ref x, y, part );
}
}
void DrawPart( FastBitmap fastBmp, Font font, ref int x, int y, TextPart part ) {
string text = part.Text;
FastColour textCol = part.TextColour;
float point = font.Size;
int xMul = font.Style == FontStyle.Italic ? 1 : 0;
foreach( char c in text ) {
int coords = ConvertToCP437( c );
int srcX = (coords & 0x0F) * boxSize;
int srcY = (coords >> 4) * boxSize;
int srcWidth = widths[coords], dstWidth = PtToPx( point, srcWidth );
int srcHeight = boxSize, dstHeight = PtToPx( point, srcHeight );
for( int yy = 0; yy < dstHeight; yy++ ) {
int fontY = srcY + yy * srcHeight / dstHeight;
int* fontRow = fontPixels.GetRowPtr( fontY );
int* dstRow = fastBmp.GetRowPtr( y + yy );
int xOffset = xMul * ((dstHeight - 1 - yy) / italicSize);
for( int xx = 0; xx < dstWidth; xx++ ) {
int fontX = srcX + xx * srcWidth / dstWidth;
int pixel = fontRow[fontX];
if( (byte)(pixel >> 24) < 127 ) continue;
int col = pixel & ~0xFFFFFF;
col |= ((pixel & 0xFF) * textCol.B / 255);
col |= (((pixel >> 8) & 0xFF) * textCol.G / 255) << 8;
col |= (((pixel >> 16) & 0xFF) * textCol.R / 255) << 16;
dstRow[x + xx + xOffset] = col;
}
}
x += PtToPx( point, srcWidth + 1 );
}
}
public override Size MeasureBitmappedSize( ref DrawTextArgs args ) {
GetTextParts( args.Text );
float point = args.Font.Size;
Size total = new Size( 0, PtToPx( point, boxSize ) );
for( int i = 0; i < parts.Count; i++ ) {
foreach( char c in parts[i].Text ) {
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 && parts.Count > 0 ) {
total.Width += 2; total.Height += 2;
}
return total;
}
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)'?';
}
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 / boxSize) * point / 72f * 96f );
}
void DisposeBitmappedText() {
fontPixels.Dispose();
fontBmp.Dispose();
}
}
}