libgd使用笔记

libgd是一个开源的图像处理的库程序,全称是GD Graphics Library,我们很常用的WEB就有使用到这个库。你可以看看,在使用PHP的时候,是否有安装php5-gd,这个就是php5的gd模块。此次开发的时候,要使用到一个功能,就是讲文字转成图片,对于这个,可能大多数有做过验证码之类的小模块的话,应该都会是使用gd库来实现的。本来最初的话,我是打算使用的ImageMagick来实现的,代码都敲得差不多了,最后拷到板子上的时候,发现ImageMagick的执行程序会出现段错误。你妹啊。所以就只要找个替代品,还好只是小模块。就找上了gd库了。不过功能弱了不少。 其实gd库,已经有给出了很详细的使用说明了。哦!这边说明一下,官网最新的版本是2.1.0版本的,我发现没有这个说明文档,不过有examples。我最开始是用我们工程中的版本,是比较早期的版本2.0.35,差了一个小版本号。发现代码结构都变化了不少呢!在2.0.35版本有个index.html文件。里面就有各个接口的参数说明及部分实例。英文的,虽然英语差了些,但都看得懂吧。

#include <gd.h>
#include <gdfontl.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    /* Declare the image */
    gdImagePtr im;
    /* Declare output files */
    FILE *pngout;
    char *s = "Hello.123";
    /* Declare color indexes */
    int black;
    int white;

    /* 创建100x100的图像,如果需要使用真彩色,
     * 换成 gdImageCreateTrueColor 接口 
     */
    im = gdImageCreate(100, 100);
    
    /* 黑色作为背景,我这里使用了RGBA模式,也就是有透明的图像,
     * 使用宏 gdAlphaTransparent,背景就为透明了。还有就是默认创建的第一层图像即为背景层
     */
    black = gdImageColorAllocateAlpha(im, 0, 0, 0, gdAlphaTransparent);  
    /* 创建白色的前景层,这里就不适用Alpha通道了。 */
    white = gdImageColorAllocate(im, 255, 255, 255);  

    /* 要写Hello.123到图像的中间,居中对齐,所以就是下面这样的计算方式了,
     * gd提供了以下几种的字体大小,分别是 
     * 大小         获取函数             头文件
     * Tiny        gdFontGetTiny       gdfontt.h
     * Small       gdFontGetSmall      gdfonts.h 
     * MediumBold  gdFontGetMediumBold gdfontmb.h 
     * Large       gdFontGetLarge      gdfontl.h 
     * Giant       gdFontGetGiant      gdfontg.h 
     * 这里使用的是large的字体,字体大小为 8x16, 
     * 下面的函数,将字体画到图像中 
     */
    gdImageString(im, gdFontGetLarge(),
        im->sx / 2 - (strlen(s) * gdFontGetLarge()->w / 2),
        im->sy / 2 - gdFontGetLarge()->h / 2,
        s, white);

    /* 打开文件 */
    pngout = fopen("test.png", "wb");

    /* 不同的图像格式,对应不同的输出函数 
     * PNG -- gdImagePng 
     * Gif -- gdImageGif 
     * Tiff -- gdImageTiff 等,
     */
    gdImagePng(im, pngout);

    /* Close the files. */
    fclose(pngout);

    /* Destroy the image in memory. */
    gdImageDestroy(im);
    
    return 0;
}

上面的代码输出的图像如下(怎么是空的。。因为背景透明,字体是白色,然后浏览器默认也是白色背景。。。): test 接口都很简单,还支持划线,使用的函数是gdImageLine函数来实现的。还有就是我使用的比较旧的版本不支持BMP的格式,就从2.1.0将BMP的相关代码拷贝到2.0.35上,添加Makefile.am编译也是可以用的。还有就是测试过,那个透明的话,png只能是使用普通的图像,也就是使用gdImageCreate创建的句柄,如果使用gdImageCreateTrueColor创建的真彩色图像,则就没有透明了。还有就是BMP普通图像是8bit的,真彩色的才是24bit的图像。 关于字体的话,我大概看了下,应该是只支持ASCII字符,想要支持更多的话,可能只能自己去实现了。字体大小的话,也比较死板一些,不知道能不能支持导入字体文件,这样的话,可用性就更高了。ImageMagick就支持转换的时候配置字体,很是强大,不过依赖也相对较多。但是总体来说gd还是很不错的。虽小但很强悍!接口还很简单,很容易上手! 如果是在ubuntu下进行开发的话,需要安装开发头文件

sudo apt-get install libgd-dev

头文件都在/usr/include下了。版本是最新的2.1.0! 继续更新,上文说了,支持的只有ASCII字符,其实也支持字体的,尤其是支持中文的字体,这点很不错。我也是晚上看到接口有gdImageStringFT,FT即freetype,这个有用过linux的应该都知道吧。linux的字体渲染就是基于这个开源项目的。包括GNU/linux,iOS,Android,ChromeOS,Ghostscript。对于接口的使用,也给出了示例代码。其中,如果你把字体改成linux下常用的中文字体的话,比如文泉驿微米黑,那么就可以讲中文输出到图像中了。使用freetype的话,必须编译libgd的适合有加–with-freetype,默认的话,是开启的。

#include "gd.h"
#include <string.h>

int main(int argc, char **argv)
{
    gdImagePtr im;
    int black;
    int white;
    int brect[8];
    int x, y;
    char *err;
    FILE *fpPng = NULL;

    char *s = "Hello.中文"; /* String to draw. */
    double sz = 100.;
//     char *f = "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf";  /* User supplied font */
    char *f = "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc";
    /* obtain brect so that we can size the image */
    err = gdImageStringFT(NULL,&brect[0],0,f,sz,0.,0,0,s);
    if (err) { 
        fprintf(stderr, "%s", err); 
        return 1;
    }

    /* create an image big enough for the string plus a little whitespace */
    x = brect[2]-brect[6] + 6;
    y = brect[3]-brect[7] + 6;
    im = gdImageCreate(x,y);

    /* Background color (first allocated) */
    white = gdImageColorResolve(im, 255, 255, 255);
    black = gdImageColorResolve(im, 0, 0, 0);

    /* render the string, offset origin to center string*/
    /* note that we use top-left coordinate for adjustment
    * since gd origin is in top-left with y increasing downwards. */
    x = 3 - brect[6];
    y = 3 - brect[7];
    err = gdImageStringFT(im,&brect[0],black,f,sz,0.0,x,y,s);
    if (err) {
        fprintf(stderr, "%s", err); 
        return 1;
    }
    fpPng = fopen("ft.png", "wb");
    
    /* Write img to fpPng */
    gdImagePng(im, fpPng);
    fclose(fpPng);
    /* Destroy it */
    gdImageDestroy(im);
    return 0;
}

其中,有个参数brect,是个数组brect[8],这个是来计算单个字体的宽度的。gd对于字体的大小的定义,就是4个角,左下,右下,右上,左上。所以对应的xy值,也就有8个了。关于在数组中的关系如下:

0 lower left corner, X position
1 lower left corner, Y position
2 lower right corner, X position
3 lower right corner, Y position
4 upper right corner, X position
5 upper right corner, Y position
6 upper left corner, X position
7 upper left corner, Y position

逆时针给绕了一圈。原文是这样的:

The string may be arbitrarily scaled (ptsize) and rotated (angle in radians). The direction of rotation is counter-clockwise, with 0 radians (0 degrees) at 3 o’clock and PI/2 radians (90 degrees) at 12 o’clock. The user-supplied int brect[8] array is filled on return from gdImageStringFT with the 8 elements representing the 4 corner coordinates of the bounding rectangle (the smallest rectangle that completely surrounds the rendered string and does not intersect any pixel of the rendered string).

说的是3点钟方向是0度,而12点钟方向是90度。但是我把数值打印出来,却是下面这么一幅图: gd-ft-sample 其中,

brect[0] = 0
brect[1] = 3
brect[2] = 59
brect[3] = 3
brect[4] = 59
brect[5] = -12
brect[6] = 0
brect[7] = -12

参数上,也就是上文中的代码,角度为0,x,y也都是为0。但是怎么看,都像是顺时针。。。但是代码中,在最后画出字的时候,还做了调整。也就是

/* render the string, offset origin to center string*/
    /* note that we use top-left coordinate for adjustment
    * since gd origin is in top-left with y increasing downwards. */
    x = 3 - brect[6];
    y = 3 - brect[7];

xy都向下做了调整,还有就是参数的不一样,出现的结果,自然也是不同的,参数不准确的话,可是会出现

Segmentation fault (core dumped)

通过上图,很容易就可以知道,第一次计算的时候,字体的宽度,可以是brect[2] – brect[0] ,不过示例代码中竟然用奇葩的brect[2] – brect[6],虽然是一样的,都是横向间距而已。高度就是 brect[1] – brect[7] 了,加了些间距。这样才会好看一些!

不过要是用到中文的话,就涉及到编码了,在ubuntu下,默认使用UTF-8编码的,加上中文,是可以打印出来的,但是如果改成GB18030的话,就出现乱码了。所以处理中文的话,比较蛋疼。

UTF-8
UTF-8编码下,中文正常
GB18030编码,中文乱码
GB18030编码,中文乱码

还有就是字体的话,可以设置pt大小,参数中,有给出来了。有了支持中文,我就得继续去改改我那个小模块了。给字体加上去先!

转载请注明: 转载自elkPi.com

本文链接地址: libgd使用笔记

9 Comments

  1. Hermit
    2016年3月11日

    楼主,GD说文字矩形的四个点,是逆时针转的,没有错的。说3点位0度,90度为1.57,这是angle的角度。关于矩形四个点,左下角为原点,y轴是向下增长的,所以上边两个点y为负数,这是没有问题的。

    回复
    1. 米鹿π
      2016年3月11日

      哦!受教了!

      回复
      1. Hermit
        2016年3月12日

        这篇东东很赞,帮了我大忙,多谢!

        回复
        1. 米鹿π
          2016年3月13日

          客气!我也只是很粗浅的用一下

          回复
      2. Hermit
        2016年3月12日

        我现在有个新问题,我想在绘制文字前,确定字体(font)是否包含特定的字形(glyph),貌似gd做不到了?估计得直接调用FreeType的接口了

        回复
        1. 米鹿π
          2016年3月13日

          嗯!这个我当初并没研究那么深入,我也觉得是应该和字体相关,gd只是做读取字体内的数据,并转换成图片,不过如果是键入的参数在字体内找不到对应的字形,应该会返回错误才是,毕竟gd库也有调用freetype的API吧。

          回复
          1. Hermit
            2016年3月13日

            研究了下FreeType的接口,FT_Get_Char_Index可以支持到这个需求。准备先用FreeType做字符查询,确定字形存在,然后再用GD绘图。

            回复
  2. jh_xu
    2016年7月5日

    楼主你好,我想写个CGM图形文件转位图的程序,不知道这个能不能用上,恳请指点一二,谢谢

    回复
    1. 米鹿π
      2016年7月6日

      这个我也没这么用过,你可以阅读下libgd的代码,看看API是否能够满足需求?

      回复

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Scroll to top