我正在为图像绘制字符串。图像的大小是动态的,或者换句话说,图像与显示字符串所需的一样大。为了实现这一点,我在使用Graphics.DrawString()渲染文本之前使用Graphics.MeasureString()测量大小。
这一切都很好,直到轮换发挥作用。到目前为止,我正在将字符串绘制到位图并旋转我得到的整个位图。
问题是我的调色板非常有限,没有混合颜色。所以我必须避免任何类型的抗锯齿,这只能通过旋转文本位图使用InterpolationMode.NearestNeighbor。虽然这确保没有渲染出不需要的颜色,但结果确实非常难看(从用户的角度来看)。
我的想法:应该可以通过使用Graphics.RotateTransform()旋转它来将文本绘制到位图并避免剪裁,不是吗?
因为我必须首先定义要绘制的图像的大小,并且因为这个大小通过旋转而增加,所以我不知道如何完成这个。
非常感谢任何帮助!
答案 0 :(得分:4)
这段代码会给你一个想法:
public void DrawText(bool debug, Graphics g, string text, Font font, Brush brush, StringFormat format, float x, float y, float width, float height, float rotation)
{
float centerX = width / 2;
float centerY = height / 2;
if (debug)
{
g.FillEllipse(Brushes.Green, centerX - 5f, centerY - 5f, 10f, 10f);
}
GraphicsState gs = g.Save();
Matrix mat = new Matrix();
mat.RotateAt(rotation, new PointF(centerX, centerY), MatrixOrder.Append);
g.Transform = mat;
SizeF szf = g.MeasureString(text, font);
g.DrawString(text, font, brush, (width / 2) - (szf.Width / 2), (height / 2) - (szf.Height / 2), format);
g.Restore(gs);
}
这是一种使用GraphicsPath测量旋转文本边界的方法。逻辑很简单,GraphicsPath将文本转换为点列表,然后计算边界矩形。
public RectangleF GetRotatedTextBounds(string text, Font font, StringFormat format, float rotation, float dpiY)
{
GraphicsPath gp = new GraphicsPath();
float emSize = dpiY * font.Size / 72;
gp.AddString(text, font.FontFamily, (int)font.Style, emSize, new PointF(0, 0), format);
Matrix mat = new Matrix();
mat.Rotate(rotation, MatrixOrder.Append);
gp.Transform(mat);
return gp.GetBounds();
}
测试代码:
float fontSize = 25f;
float rotation = 30f;
RectangleF txBounds = GetRotatedTextBounds("TEST TEXT", new Font("Verdana", fontSize, System.Drawing.FontStyle.Bold), StringFormat.GenericDefault, rotation, 96f);
float inflateValue = 10 * (fontSize / 100f);
txBounds.Inflate(inflateValue, inflateValue);
Bitmap bmp = new System.Drawing.Bitmap((int)txBounds.Width, (int)txBounds.Height);
using (Graphics gr = Graphics.FromImage(bmp))
{
gr.Clear(Color.White);
DrawText(true, gr, "TEST TEXT", new Font("Verdana", fontSize, System.Drawing.FontStyle.Bold), Brushes.Red, new StringFormat(System.Drawing.StringFormatFlags.DisplayFormatControl), 0, 0, txBounds.Width, txBounds.Height, rotation);
}
答案 1 :(得分:1)
我的解决方案作为可执行的MVC操作:
public class ImageController : Controller
{
public ActionResult Test()
{
var text = DateTime.Now.ToString();
var font = new Font("Arial", 20, FontStyle.Regular);
var angle = 233;
SizeF textSize = GetEvenTextImageSize(text, font);
SizeF imageSize;
if (angle == 0)
imageSize = textSize;
else
imageSize = GetRotatedTextImageSize(textSize, angle);
using (var canvas = new Bitmap((int)imageSize.Width, (int)imageSize.Height))
{
using(var graphics = Graphics.FromImage(canvas))
{
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
SizeF textContainerSize = graphics.VisibleClipBounds.Size;
graphics.TranslateTransform(textContainerSize.Width / 2, textContainerSize.Height / 2);
graphics.RotateTransform(angle);
graphics.DrawString(text, font, Brushes.Black, -(textSize.Width / 2), -(textSize.Height / 2));
}
var stream = new MemoryStream();
canvas.Save(stream, ImageFormat.Png);
stream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(stream, "image/png");
}
}
private static SizeF GetEvenTextImageSize(string text, Font font)
{
using (var image = new Bitmap(1, 1, PixelFormat.Format32bppArgb))
{
using (Graphics graphics = Graphics.FromImage(image))
{
return graphics.MeasureString(text, font);
}
}
}
private static SizeF GetRotatedTextImageSize(SizeF fontSize, int angle)
{
// Source: http://www.codeproject.com/KB/graphics/rotateimage.aspx
double theta = angle * Math.PI / 180.0;
while (theta < 0.0)
theta += 2 * Math.PI;
double adjacentTop, oppositeTop;
double adjacentBottom, oppositeBottom;
if ((theta >= 0.0 && theta < Math.PI / 2.0) || (theta >= Math.PI && theta < (Math.PI + (Math.PI / 2.0))))
{
adjacentTop = Math.Abs(Math.Cos(theta)) * fontSize.Width;
oppositeTop = Math.Abs(Math.Sin(theta)) * fontSize.Width;
adjacentBottom = Math.Abs(Math.Cos(theta)) * fontSize.Height;
oppositeBottom = Math.Abs(Math.Sin(theta)) * fontSize.Height;
}
else
{
adjacentTop = Math.Abs(Math.Sin(theta)) * fontSize.Height;
oppositeTop = Math.Abs(Math.Cos(theta)) * fontSize.Height;
adjacentBottom = Math.Abs(Math.Sin(theta)) * fontSize.Width;
oppositeBottom = Math.Abs(Math.Cos(theta)) * fontSize.Width;
}
int nWidth = (int)Math.Ceiling(adjacentTop + oppositeBottom);
int nHeight = (int)Math.Ceiling(adjacentBottom + oppositeTop);
return new SizeF(nWidth, nHeight);
}
}