Skip to content

Instantly share code, notes, and snippets.

@marcussacana
Created May 8, 2022 20:24
Show Gist options
  • Save marcussacana/2e6bfa87ebb1a6b11eb41708125aba7d to your computer and use it in GitHub Desktop.
Save marcussacana/2e6bfa87ebb1a6b11eb41708125aba7d to your computer and use it in GitHub Desktop.
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