【问题标题】:How to draw a string at an exact pixel position如何在精确的像素位置绘制字符串
【发布时间】:2016-01-13 21:52:57
【问题描述】:

我尝试在 C# 中将字符串(单个字符)绘制到位图中的确切位置:

Bitmap bmp = new Bitmap(64, 64);
Graphics g = Graphics.FromImage(bmp);
g.DrawString("W", font1, new SolidBrush(myColor), new Point(32,32);

在单个字母周围渲染了太多空白空间,以至于我无法猜测“需要”的位置来绘制字符以使其在最后的正确位置。

现在我有了字符的像素精确尺寸(查看单独渲染的位图中的位)。但是,如果我无法在准确的位置(例如中心或右上角或 ....)绘制角色,则此信息是无用的。

还有其他方法可以用 C# 在位图上绘制文本吗?或者是否有任何转换方法可以转换 DrawString 需要的真实像素位置?

【问题讨论】:

  • 您是否正在寻找 Graphics.MeasureString/MEasureCharRanges 来计算边界矩形?
  • 在画之前可以measure the string吗?
  • 好吧,只需将绘图原点偏移字形宽度/高度的一半
  • 检查完所有像素后,您可以从“额外”位图中复制像素。您还可以设计自己的“字体”并自己栅格化字符。
  • @Konrad - Alex 和 Corak 提出的观点是,一旦您知道边界(“外部”)矩形和非空白像素(“内部矩形)之间的偏移量,您就可以 补偿该偏移量。但是,Corak 给出了错误的公式。给定空白宽度LM/TM/RM/BM(左/上/右/下边距):定位左上角pos.X -= LM; pos.Y -= TM。要更正 居中 位置(假设您已经将 pos 调整为居中):pos.X -= (LM-RM)/2; pos.Y -= (TM-BM)/2;

标签: c# .net graphics bitmap drawstring


【解决方案1】:

无需查看像素或开始使用您自己的字体..

您可以使用GraphicsPath 代替DrawStringTextRenderer,因为它会通过GraphicsPath.GetBounds() 让您知道它的净边界矩形

当你知道了之后,你可以计算出如何使用TranslateTransform移动Graphics对象:

private void button1_Click(object sender, EventArgs e)
{
    string text = "Y";                  // whatever
    Bitmap bmp = new Bitmap(64, 64);    // whatever
    bmp.SetResolution(96, 96);          // whatever
    float fontSize = 32f;               // whatever

    using ( Graphics g = Graphics.FromImage(bmp))
    using ( GraphicsPath GP = new GraphicsPath())
    using ( FontFamily fontF = new FontFamily("Arial"))
    {
        testPattern(g, bmp.Size);      // optional

        GP.AddString(text, fontF, 0, fontSize, Point.Empty,
                     StringFormat.GenericTypographic);
        // this is the net bounds without any whitespace:
        Rectangle br = Rectangle.Round(GP.GetBounds());

        g.DrawRectangle(Pens.Red,br); // just for testing

        // now we center:
        g.TranslateTransform( (bmp.Width - br.Width )  / 2 - br.X,
                              (bmp.Height - br.Height )/ 2 - br.Y);
        // and fill
        g.FillPath(Brushes.Black, GP);
        g.ResetTransform();
    }

    // whatever you want to do..
    pictureBox1.Image = bmp;
    bmp.Save("D:\\__test.png", ImageFormat.Png);

}

一个小测试例程,让我们更好地看到居中:

void testPattern(Graphics g, Size sz)
{
    List<Brush> brushes = new List<Brush>() 
    {   Brushes.SlateBlue, Brushes.Yellow, 
        Brushes.DarkGoldenrod, Brushes.Lavender };
    int bw2 = sz.Width / 2;
    int bh2 = sz.Height / 2;
    for (int i = bw2; i > 0; i--)
        g.FillRectangle(brushes[i%4],bw2 - i, bh2 - i, i + i, i + i );

}

GetBounds 方法返回一个RectangleF;在我的例子中是{X=0.09375, Y=6.0625, Width=21, Height=22.90625}。请注意,由于四舍五入,事情总是可以减一..

您可能希望也可能不希望将 Graphics 设置更改为特殊的 Smoothingmodes 等。

还应该注意的是,这将通过边界矩形进行自动,即机械居中。这可能与'optical or visual centering' 完全不同,后者很难编码,并且在某种程度上是个人品味的问题。但排版既是一门艺术,也是一门职业。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多