C# 使用 GDI+ 实现添加中心旋转(任意角度)的文字

PS:这篇文章是 GDI+ 总结系列的第三篇,如果对 GDI+ 的基础使用不熟悉的朋友可以先看第一篇文章《C# 使用 GDI+ 画图》


需求

需求是要实现给图片添加任意角度旋转的文字,文字的旋转中心要是在文字区域中央,就像 CSSrotate 函数一样的效果。如下:

1.gif


分析&思路

Graphics 类有个 RotateTransform 方法,可以传入任意角度的值来旋转画板。但是这个方法的旋转中心是画板的左上角,所以直接单单用这个方法不能满足我们的需求。此外,Graphics类还有个 TranslateTransform 方法可以改变坐标的原点,而且这个方法是沿着矩形的x,y轴平移的,即就算图片旋转了一定的角度后,再调用 TranslateTransform 方法,它还是沿着x,y轴平移。于是通过以下三个步骤即可实现图片中心旋转。

  1. 把画板(Graphics对象)原点平移到矩形中心位置(x, y)
  2. 在(x, y)位置绕原点旋转画板N度
  3. 画板退回(-x, -y)的距离

还是看不懂的同学看下面的图应该就明白了

2.png

明白了原理,那不容易推断出,如果要旋转的中心不是图片中心而是文字中心,那步骤还是一样的,只是把(x, y)改为文字中心的坐标就好了。

除了上面说的方法,其实还有一个方法可以实现中心旋转,那就是使用 Matrix 类。Matrix 类的 RotateAt 方法可以指定矩阵旋转的中心位置。

//
// 摘要:
//     沿 point 参数中指定的点并通过预先计算该旋转,来顺时针旋转此 System.Drawing.Drawing2D.Matrix。
//
// 参数:
//   angle:
//     旋转角度(范围)(单位:度)。
//
//   point:
//     一个 System.Drawing.PointF,表示旋转中心。
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public void RotateAt(float angle, PointF point);

Graphics 类的 Transform 属性返回的就是 Matrix 对象,该属性可以 getset。因此我们先获取原来的画板的矩阵,然后使用 RotateAt 方法旋转该矩阵,再把旋转后的矩阵赋值给画板就好了。


具体实现

  • 添加任意角度文字方法
/// <summary>
/// 图片添加任意角度文字(文字旋转是中心旋转,角度顺时针为正)
/// </summary>
/// <param name="imgPath">图片路径</param>
/// <param name="locationLeftTop">文字左上角定位(x1,y1)</param>
/// <param name="fontSize">字体大小,单位为像素</param>
/// <param name="text">文字内容</param>
/// <param name="angle">文字旋转角度</param>
/// <param name="fontName">字体名称</param>
/// <returns>添加文字后的Bitmap对象</returns>
public Bitmap AddText(string imgPath, string locationLeftTop, int fontSize, string text, int angle = 0, string fontName = "华文行楷")
{
    Image img = Image.FromFile(imgPath);

    int width = img.Width;
    int height = img.Height;
    Bitmap bmp = new Bitmap(width, height);
    Graphics graphics = Graphics.FromImage(bmp);
    // 画底图
    graphics.DrawImage(img, 0, 0, width, height);

    Font font = new Font(fontName, fontSize, GraphicsUnit.Pixel);
    SizeF sf = graphics.MeasureString(text, font); // 计算出来文字所占矩形区域

    // 左上角定位
    string[] location = locationLeftTop.Split(',');
    float x1 = float.Parse(location[0]);
    float y1 = float.Parse(location[1]);

    // 进行文字旋转的角度定位
    if (angle != 0)
    {
        #region 法一:TranslateTransform平移 + RotateTransform旋转

        /* 
            * 注意:
            * Graphics.RotateTransform的旋转是以Graphics对象的左上角为原点,旋转整个画板的。
            * 同时x,y坐标轴也会跟着旋转。即旋转后的x,y轴依然与矩形的边平行
            * 而Graphics.TranslateTransform方法,是沿着x,y轴平移的
            * 因此分三步可以实现中心旋转
            * 1.把画板(Graphics对象)平移到旋转中心
            * 2.旋转画板
            * 3.把画板平移退回相同的距离(此时的x,y轴仍然是与旋转后的矩形平行的)
            */
        //// 把画板的原点(默认是左上角)定位移到文字中心
        //graphics.TranslateTransform(x1 + sf.Width / 2, y1 + sf.Height / 2);
        //// 旋转画板
        //graphics.RotateTransform(angle);
        //// 回退画板x,y轴移动过的距离
        //graphics.TranslateTransform(-(x1 + sf.Width / 2), -(y1 + sf.Height / 2));

        #endregion

        #region 法二:矩阵旋转

        Matrix matrix = graphics.Transform;
        matrix.RotateAt(angle, new PointF(x1 + sf.Width / 2, y1 + sf.Height / 2));
        graphics.Transform = matrix;

        #endregion
    }

    // 写上自定义角度的文字
    graphics.DrawString(text, font, new SolidBrush(Color.Black), x1, y1);

    graphics.Dispose();
    img.Dispose();

    return bmp;
}

PS:这里简单解释一下为什么文字中心是 (x1 + sf.Width / 2, y1 + sf.Height / 2),因为 (x, y) 是左上角,而 sf.Widthsf.Height 是文字矩形区域宽、高。如图:

3.jpg

  • 测试调用
private static void Main(string[] args)
{
    try
    {
        Console.WriteLine("Start drawing ...");
        System.Drawing.Bitmap bmp = AddText(@"D:\test\1.png", "176.94,150.48", 66, "写点啥好呢", 30);
        bmp.Save(@"D:\test\output.png");
        bmp.Dispose();
        Console.WriteLine("Done!");
    }
    catch (System.Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
    finally
    {
        System.Console.WriteLine("\nPress any key to continue ...");
        System.Console.ReadKey();
    }
}

最终效果

  • 没有旋转时
    4.png
  • 中心旋转30度
    5.png

一个思考

讲完了大家来思考一个问题,如果我想做图片绕任意位置为中心进行旋转应该怎么做呢?相信看完了上面的代码大家应该都会了吧。


参考文章:
C#中基于GDI+(Graphics)图像处理系列之任意角度旋转图像
C#利用GDI+绘制旋转文字,矩形内可以根据布局方式排列文本


系列其他文章:
C# 使用 GDI+ 画图
C# 使用 GDI+ 给图片添加文字,并使文字自适应矩形区域

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容