Created
May 8, 2022 20:24
-
-
Save marcussacana/2e6bfa87ebb1a6b11eb41708125aba7d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Drawing; | |
using System.Drawing.Imaging; | |
using System.IO; | |
using System.Runtime.CompilerServices; | |
namespace TextDraw | |
{ | |
public static class GDI | |
{ | |
static string RevText(string Text) { | |
return Text.Replace(" .", ".").Replace(" ", " "); | |
} | |
public static void DrawText(string ImagePath, string Text, int X, int Y, int Width, int Height, long ForeColor) { | |
var Tmp = Path.GetTempFileName(); | |
File.Delete(Tmp); | |
using (Bitmap bmp = (Bitmap)Image.FromFile(ImagePath)) | |
{ | |
DrawText(bmp, RevText(Text), X, Y, Width, Height, ForeColor, false); | |
bmp.Save(Tmp, ImageFormat.Png); | |
} | |
File.Delete(ImagePath); | |
File.Move(Tmp, ImagePath); | |
} | |
public static void DrawText(Bitmap Image, string Text, int X, int Y, int Width, int Height, long ForeColor, bool NoExpand) { | |
using (Graphics g = Graphics.FromImage(Image)) | |
{ | |
var Area = new RectangleF(X, Y, Width, Height); | |
if (!NoExpand) | |
Area = Image.ExpandRectangleInSolidArea(Area, out _); | |
g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; | |
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; | |
var Format = new StringFormat() { | |
Alignment = StringAlignment.Center, | |
LineAlignment = StringAlignment.Center | |
}; | |
for (float Size = 64; Size > 0; Size -= 0.5f) | |
{ | |
var Font = new Font("Consolas", Size, FontStyle.Regular, GraphicsUnit.Pixel, 0, false); | |
var TSize = g.MeasureString(Text, Font, (int)Area.Width, Format); | |
if (TSize.Height > Area.Height) | |
continue; | |
if (Size > 11) | |
{ | |
bool Overflow = false; | |
foreach (var Word in Text.Split(' ')) | |
{ | |
if (string.IsNullOrWhiteSpace(Word)) | |
continue; | |
if (g.MeasureString(Word, Font).Width > (int)Area.Width - (Size * 0.7f)) | |
{ | |
Overflow = true; | |
break; | |
} | |
} | |
if (Overflow) | |
continue; | |
} | |
var ACenter = Area.GetCenter(); | |
var TArea = new RectangleF((ACenter.X - (TSize.Width / 2)), ACenter.Y - (TSize.Height / 2), TSize.Width, TSize.Height); | |
if (!NoExpand && Image.RectangleIsUnderSolidArea(TArea)) | |
{ | |
var NewArea = Image.ExpandRectangleInSolidArea(TArea, out bool Expanded).ToRectangle(); | |
if (Expanded) | |
{ | |
DrawText(Image, Text, NewArea.X, NewArea.Y, NewArea.Width, NewArea.Height, ForeColor, true); | |
return; | |
} | |
} | |
#if DEBUG | |
g.DrawRectangle(new Pen(Brushes.Red, 3f), TArea.ToRectangle()); | |
#endif | |
g.DrawString(Text, Font, new SolidBrush(Color.FromArgb(unchecked((int)ForeColor))), Area, Format); | |
g.Flush(); | |
break; | |
} | |
} | |
} | |
} | |
static class Extensions { | |
internal static RectangleF ExpandRectangleInSolidArea(this Bitmap Image, RectangleF Area, out bool Expanded) { | |
var AreaEx = Area; | |
//All Borders | |
RectangleF NewArea; | |
do | |
{ | |
NewArea = AreaEx; | |
AreaEx = NewArea.Expand(1); | |
} while (Image.RectangleIsUnderSolidArea(AreaEx)); | |
//Top only | |
AreaEx = NewArea; | |
do | |
{ | |
NewArea = AreaEx; | |
AreaEx = NewArea.ExpandTop(1); | |
} while (Image.RectangleIsUnderSolidArea(AreaEx)); | |
//Bottom Only | |
AreaEx = NewArea; | |
do | |
{ | |
NewArea = AreaEx; | |
AreaEx = NewArea.ExpandBottom(1); | |
} while (Image.RectangleIsUnderSolidArea(AreaEx)); | |
//Left Only | |
AreaEx = NewArea; | |
do | |
{ | |
NewArea = AreaEx; | |
AreaEx = NewArea.ExpandLeft(1); | |
} while (Image.RectangleIsUnderSolidArea(AreaEx)); | |
//Right Only | |
AreaEx = NewArea; | |
do | |
{ | |
NewArea = AreaEx; | |
AreaEx = NewArea.ExpandRight(1); | |
} while (Image.RectangleIsUnderSolidArea(AreaEx)); | |
Expanded = NewArea.Width * NewArea.Height > Area.Width * Area.Height; | |
return NewArea; | |
} | |
internal static Rectangle ToRectangle(this RectangleF Rect) => new Rectangle((int)Rect.X, (int)Rect.Y, (int)Rect.Width, (int)Rect.Height); | |
internal static bool RectangleIsUnderSolidArea(this Bitmap Image, RectangleF Area) | |
{ | |
if (Image.IsValidRectangle(Area)) | |
return false; | |
var Center = Area.GetCenter(); | |
Color BGColor = Image.GetPixel((int)Center.X, (int)Center.Y); | |
bool IsWhite = BGColor.IsWhite(); | |
for (float X = Area.Left; X <= Area.Right; X++) | |
{ | |
var PixelTop = Image.GetPixel((int)X, (int)Area.Top); | |
var PixelBottom = Image.GetPixel((int)X, (int)Area.Bottom); | |
if (IsWhite && !PixelTop.IsWhite()) | |
return false; | |
if (!IsWhite && !PixelTop.IsSimilarTo(BGColor)) | |
return false; | |
if (IsWhite && !PixelBottom.IsWhite()) | |
return false; | |
if (!IsWhite && !PixelBottom.IsSimilarTo(BGColor)) | |
return false; | |
} | |
for (float Y = Area.Top; Y <= Area.Bottom; Y++) | |
{ | |
var PixelLeft = Image.GetPixel((int)Area.Left, (int)Y); | |
var PixelRight = Image.GetPixel((int)Area.Right, (int)Y); | |
if (IsWhite && !PixelLeft.IsWhite()) | |
return false; | |
if (!IsWhite && !PixelLeft.IsSimilarTo(BGColor)) | |
return false; | |
if (IsWhite && !PixelRight.IsWhite()) | |
return false; | |
if (!IsWhite && !PixelRight.IsSimilarTo(BGColor)) | |
return false; | |
} | |
return true; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal static bool IsWhite(this Color Pixel) | |
{ | |
if (Pixel == Color.White) | |
return true; | |
if (Pixel.R >= 240 && Pixel.G >= 240 && Pixel.B >= 240) | |
return true; | |
return false; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal static bool IsSimilarTo(this Color Pixel, Color Reference) { | |
if (Pixel == Reference) | |
return true; | |
if (Math.Abs(Pixel.R - Reference.R) < 10 && | |
Math.Abs(Pixel.G - Reference.G) < 10 && | |
Math.Abs(Pixel.B - Reference.B) < 10) | |
return true; | |
return false; | |
} | |
internal static bool IsValidRectangle(this Bitmap Image, RectangleF Rect) => Image.Size.IsValidRectangle(Rect); | |
internal static bool IsValidRectangle(this Size Size, RectangleF Rect) { | |
if (Rect.X <= 0 || Rect.Y <= 0) | |
return false; | |
if (Rect.X + Rect.Width > Size.Width) | |
return false; | |
if (Rect.Y + Rect.Height > Size.Height) | |
return false; | |
return true; | |
} | |
internal static PointF GetCenter(this RectangleF Rectangle) => new PointF(Rectangle.X + (Rectangle.Width/2), Rectangle.Y + (Rectangle.Height/2)); | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal static RectangleF Expand(this RectangleF Original, int Pixels) { | |
return new RectangleF(Original.X - Pixels, Original.Y - Pixels, Original.Width + (Pixels*2), Original.Height + (Pixels * 2)); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal static RectangleF ExpandTop(this RectangleF Original, int Pixels) { | |
return new RectangleF(Original.X, Original.Y - Pixels, Original.Width, Original.Height + Pixels); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal static RectangleF ExpandBottom(this RectangleF Original, int Pixels) { | |
return new RectangleF(Original.X, Original.Y, Original.Width, Original.Height + Pixels); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal static RectangleF ExpandLeft(this RectangleF Original, int Pixels) { | |
return new RectangleF(Original.X - Pixels, Original.Y, Original.Width + Pixels, Original.Height); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
internal static RectangleF ExpandRight(this RectangleF Original, int Pixels) { | |
return new RectangleF(Original.X, Original.Y, Original.Width + Pixels, Original.Height); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment