Skip to content

Commit de12614

Browse files
authored
Merge pull request #5 from simmetric/dev
Refactoring and fixed minor bugs
2 parents 19b2e23 + a21383c commit de12614

5 files changed

Lines changed: 314 additions & 260 deletions

File tree

SpacedText/Constants.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
namespace SpacedTextPlugin
88
{
9+
using System.Diagnostics.PerformanceData;
10+
911
public class Constants
1012
{
1113
public enum Properties
@@ -20,17 +22,32 @@ public enum Properties
2022
Italic,
2123
Underline,
2224
Strikeout,
23-
TextAlignment,
24-
JustifyText,
25-
JustifyExceptLastLine
25+
TextAlignment
2626
}
2727

2828
public enum TextAlignmentOptions
2929
{
3030
Left,
3131
Center,
32-
Right,
33-
Justify
32+
Right
3433
}
34+
35+
public const string Space = " ";
36+
public const char SpaceChar = ' ';
37+
public const string DefaultText = "The quick brown fox jumps over the lazy dog.";
38+
public const int MaxBitmapSize = 65535;
39+
public const int DefaultAntiAliasingLevel = 2;
40+
public const int MinAntiAliasingLevel = 1;
41+
public const int MaxAntiAliasingLevel = 6;
42+
public const TextAlignmentOptions DefaultTextAlignment = TextAlignmentOptions.Left;
43+
public const int DefaultFontSize = 20;
44+
public const int MinFontSize = 1;
45+
public const int MaxFontSize = 400;
46+
public const double DefaultLetterSpacing = 0;
47+
public const double MinLetterSpacing = -0.3;
48+
public const double MaxLetterSpacing = 3;
49+
public const double DefaultLineSpacing = 0;
50+
public const double MinLineSpacing = -0.6;
51+
public const double MaxLineSpacing = 3;
3552
}
3653
}

SpacedText/PInvoked.cs

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,60 @@
1-
using System;
2-
3-
namespace SpacedTextPlugin
1+
namespace SpacedTextPlugin
42
{
3+
using System;
54
using System.Drawing;
65
using System.Drawing.Drawing2D;
76

87
public class PInvoked
98
{
10-
public static void TextOut(Graphics G, string text, int x, int y, Font font, float letterSpacing)
9+
public static void TextOut(Graphics g, string text, int x, int y, Font font, double letterSpacing)
1110
{
12-
G.PixelOffsetMode = PixelOffsetMode.HighQuality;
13-
IntPtr Hdc = default(IntPtr);
14-
IntPtr FontPtr = default(IntPtr);
11+
g.PixelOffsetMode = PixelOffsetMode.Half;
12+
IntPtr hdc = default(IntPtr);
13+
IntPtr fontPtr = default(IntPtr);
1514
try
1615
{
1716
//Grab the Graphic object's handle
18-
Hdc = G.GetHdc();
17+
hdc = g.GetHdc();
1918
//Set the current GDI font
20-
FontPtr = Interop.SelectObject(Hdc, font.ToHfont());
19+
fontPtr = Interop.SelectObject(hdc, font.ToHfont());
2120
//Set the drawing surface background color
22-
Interop.SetBkColor(Hdc, ColorTranslator.ToWin32(Color.Black));
21+
Interop.SetBkColor(hdc, ColorTranslator.ToWin32(Color.Black));
2322
//Set the text color
24-
Interop.SetTextColor(Hdc, ColorTranslator.ToWin32(Color.White));
23+
Interop.SetTextColor(hdc, ColorTranslator.ToWin32(Color.White));
2524
//Set the kerning
26-
Interop.SetTextCharacterExtra(Hdc, (int) Math.Round(letterSpacing*font.Size));
27-
Interop.TextOut(Hdc, x, y, text, text.Length);
25+
Interop.SetTextCharacterExtra(hdc, (int) Math.Round(letterSpacing*font.Size));
26+
Interop.TextOut(hdc, x, y, text, text.Length);
2827
}
2928
finally
3029
{
3130
//Release the font
32-
Interop.DeleteObject(FontPtr);
31+
Interop.DeleteObject(fontPtr);
3332
//Release the handle on the graphics object
34-
G.ReleaseHdc();
33+
g.ReleaseHdc();
3534
}
3635
}
3736

38-
public static Size MeasureString(Graphics G, string text, Font font, float letterSpacing)
37+
public static Size MeasureString(Graphics g, string text, Font font, double letterSpacing)
3938
{
40-
IntPtr Hdc = default(IntPtr);
41-
IntPtr FontPtr = default(IntPtr);
39+
IntPtr hdc = default(IntPtr);
40+
IntPtr fontPtr = default(IntPtr);
4241
Size size = new Size();
4342
try
4443
{
4544
//Grab the Graphic object's handle
46-
Hdc = G.GetHdc();
45+
hdc = g.GetHdc();
4746
//Set the current GDI font
48-
FontPtr = Interop.SelectObject(Hdc, font.ToHfont());
47+
fontPtr = Interop.SelectObject(hdc, font.ToHfont());
4948
//Set the kerning
50-
Interop.SetTextCharacterExtra(Hdc, (int) Math.Round(letterSpacing*font.Size));
51-
Interop.GetTextExtentPoint(Hdc, text, text.Length, ref size);
49+
Interop.SetTextCharacterExtra(hdc, (int) Math.Round(letterSpacing*font.Size));
50+
Interop.GetTextExtentPoint(hdc, text, text.Length, ref size);
5251
}
5352
finally
5453
{
5554
//Release the font
56-
Interop.DeleteObject(FontPtr);
55+
Interop.DeleteObject(fontPtr);
5756
//Release the handle on the graphics object
58-
G.ReleaseHdc();
57+
g.ReleaseHdc();
5958
}
6059
return size;
6160
}

SpacedText/SpacedText.cs

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
namespace SpacedTextPlugin
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Drawing;
6+
using System.Drawing.Drawing2D;
7+
using System.Drawing.Imaging;
8+
using System.Drawing.Text;
9+
using System.Linq;
10+
using PaintDotNet;
11+
using C = Constants;
12+
13+
internal class SpacedText : IDisposable
14+
{
15+
//configuration options
16+
public string Text { get; set; }
17+
public string FontFamily { get; set; }
18+
public int FontSize { get; set; }
19+
public double LetterSpacing { get; set; }
20+
public double LineSpacing { get; set; }
21+
public int AntiAliasLevel { get; set; }
22+
public FontStyle FontStyle { get; set; }
23+
public Constants.TextAlignmentOptions TextAlign { get; set; }
24+
25+
//flow control
26+
public bool IsCancelRequested { get; set; }
27+
28+
//public result
29+
public Rectangle Bounds { get; private set; }
30+
public Surface BufferSurface { get; private set; }
31+
32+
//private
33+
private readonly ImageAttributes imgAttr;
34+
35+
public SpacedText()
36+
{
37+
AntiAliasLevel = Constants.DefaultAntiAliasingLevel;
38+
39+
ColorMap[] colorMap = {
40+
new ColorMap
41+
{
42+
OldColor = Color.Black,
43+
NewColor = Color.Transparent
44+
}
45+
};
46+
imgAttr = new ImageAttributes();
47+
imgAttr.SetRemapTable(colorMap);
48+
}
49+
50+
public void RenderText(Rectangle bounds)
51+
{
52+
Bounds = bounds;
53+
Font font = new Font(FontFamily, FontSize * AntiAliasLevel, FontStyle, GraphicsUnit.Pixel);
54+
55+
//render text on larger bitmap so it can be anti-aliased while scaling down
56+
Bitmap bm = new Bitmap(Bounds.Size.Width * AntiAliasLevel, Bounds.Size.Height * AntiAliasLevel);
57+
Graphics gr = Graphics.FromImage(bm);
58+
gr.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
59+
gr.SmoothingMode = SmoothingMode.HighQuality;
60+
gr.CompositingMode = CompositingMode.SourceOver;
61+
gr.Clear(Color.Black);
62+
63+
//letterspacing may be changed during execution
64+
double letterSpacing = LetterSpacing;
65+
66+
if (!IsCancelRequested)
67+
{
68+
//split in lines
69+
List<string> lines = LineWrap(gr, font, letterSpacing, bm);
70+
71+
if (!IsCancelRequested)
72+
{
73+
//draw lines
74+
DrawLines(lines, gr, font, letterSpacing, bm);
75+
}
76+
}
77+
78+
//scale bitmap down onto result-size bitmap and apply anti-aliasing
79+
Bitmap resultBm = new Bitmap(Bounds.Width, Bounds.Height);
80+
Graphics resultGr = Graphics.FromImage(resultBm);
81+
resultGr.InterpolationMode = InterpolationMode.HighQualityBicubic;
82+
resultGr.DrawImage(bm, 0f, 0f, Bounds.Width, Bounds.Height);
83+
BufferSurface = Surface.CopyFromBitmap(resultBm);
84+
85+
#if DEBUG
86+
bm.Save("C:\\dev\\buffer.png", ImageFormat.Png);
87+
resultBm.Save("C:\\dev\\result.png", ImageFormat.Png);
88+
#endif
89+
90+
//cleanup
91+
gr.Dispose();
92+
bm.Dispose();
93+
resultGr.Dispose();
94+
resultBm.Dispose();
95+
}
96+
97+
private void DrawLines(List<string> lines, Graphics gr, Font font, double letterSpacing, Bitmap bm)
98+
{
99+
int lineStart = 0;
100+
int lineNum = 0;
101+
foreach (string line in lines)
102+
{
103+
if (IsCancelRequested || lineStart > Bounds.Bottom * AntiAliasLevel)
104+
{
105+
break;
106+
}
107+
108+
lineNum++;
109+
110+
if (!line.Equals(string.Empty))
111+
{
112+
int left = FontSize / 2;
113+
114+
//measure text
115+
Size textBounds = PInvoked.MeasureString(gr, line, font, letterSpacing);
116+
if (TextAlign != Constants.TextAlignmentOptions.Left)
117+
{
118+
if (TextAlign == Constants.TextAlignmentOptions.Center)
119+
{
120+
left = bm.Width / 2 - textBounds.Width / 2;
121+
}
122+
else if (TextAlign == Constants.TextAlignmentOptions.Right)
123+
{
124+
left = bm.Width - (textBounds.Width + FontSize);
125+
}
126+
}
127+
128+
if (textBounds.Width > 0 && textBounds.Height > 0 &&
129+
textBounds.Width * AntiAliasLevel < Constants.MaxBitmapSize && textBounds.Height * AntiAliasLevel < Constants.MaxBitmapSize)
130+
{
131+
//create new bitmap for line
132+
Bitmap lineBm = new Bitmap(textBounds.Width * AntiAliasLevel, textBounds.Height * AntiAliasLevel);
133+
Graphics lineGr = Graphics.FromImage(lineBm);
134+
//draw text
135+
PInvoked.TextOut(lineGr, line, 0, 0, font, letterSpacing);
136+
#if DEBUG
137+
lineBm.Save($"C:\\dev\\line{lineNum}.png", ImageFormat.Png);
138+
#endif
139+
//draw lineBm to bm leaving out black
140+
gr.DrawImage(lineBm, new Rectangle(new Point(left, lineStart), lineBm.Size), 0, 0, lineBm.Width,
141+
lineBm.Height, GraphicsUnit.Pixel, imgAttr);
142+
lineGr.Dispose();
143+
lineBm.Dispose();
144+
}
145+
}
146+
147+
lineStart += font.Height + (int)Math.Round(font.Height * LineSpacing);
148+
}
149+
}
150+
151+
private List<string> LineWrap(Graphics gr, Font font, double letterSpacing, Bitmap bm)
152+
{
153+
string[] words = Text.Replace(Environment.NewLine, " " + Environment.NewLine + C.Space)
154+
.Split(new[] {C.SpaceChar}, StringSplitOptions.RemoveEmptyEntries);
155+
List<string> lines = new List<string>();
156+
157+
string currentLine = words.Any() ? words.First() + C.Space : string.Empty;
158+
159+
foreach (string word in words.Skip(1))
160+
{
161+
//if manual line break: end current line and start new line
162+
if (word.Equals(Environment.NewLine))
163+
{
164+
lines.Add(currentLine.Trim());
165+
currentLine = string.Empty;
166+
continue;
167+
}
168+
169+
//measure currentline + word
170+
//else add word to currentline
171+
if (PInvoked.MeasureString(gr, currentLine + word, font, letterSpacing).Width > bm.Width - FontSize)
172+
{
173+
//if outside bounds, then add line
174+
lines.Add(currentLine.Trim());
175+
currentLine = word + C.Space;
176+
}
177+
else
178+
{
179+
currentLine += word + C.Space;
180+
}
181+
}
182+
//add currentline
183+
if (!string.IsNullOrEmpty(currentLine))
184+
{
185+
lines.Add(currentLine.Trim());
186+
}
187+
return lines;
188+
}
189+
190+
public void Dispose()
191+
{
192+
BufferSurface.Dispose();
193+
}
194+
}
195+
}

SpacedText/SpacedText.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
<Compile Include="Constants.cs" />
6969
<Compile Include="Interop.cs" />
7070
<Compile Include="PInvoked.cs" />
71+
<Compile Include="SpacedText.cs" />
7172
<Compile Include="SpacedTextEffectsPlugin.cs" />
7273
<Compile Include="Properties\AssemblyInfo.cs" />
7374
</ItemGroup>

0 commit comments

Comments
 (0)