最近跟着导师做项目,要求使用C语言编程,达到的目标是将字符串转为jpg格式的图片,在网上翻帖子的时候看到libgd这个库正好可以满足我的使用需求,先将其使用学习过程记录如下。
参考内容博客地址:http://elkpi.com/topics/libgd-notes.html
libgd官网网址:https://libgd.github.io/
今天终于将中文字符串转图片输出图片buffer内容的模块完成了,开心一下,但是在这个过程中也暴露了许多问题,先记录如下。
先上代码:
/*
* 动态水印管理接口
* 作者:beastsam
* 日期:2018.9.18
* 功能:根据用户名,终端机器特征码,文件标题,当前时间等信息将字符串生成图片
* 参数:
* pcUserName[in] 用户名
* aucDevcode[in] 终端机器特征码
* pcTitle[in] 文件标题
* pcCurtime[in] 当前时间
* pucPic[out] 图片形式水印缓冲区
* uisize[out] 记录图片形式水印缓冲区大小
* 返回值:
* 如成功执行返回0
* 若执行失败返回错误代码
* 备注:
* 引用第三方库libgd
* 官网:https://libgd.github.io
*/
#include <stdio.h>
#include <string.h>
#include "gd.h"
#include "water_mark_manage.h"
int water_mark_manage(char *pcUserName, char *aucDevcode, char *pcTitle,
char *pcCurtime, char *pucPic, int *uisize)
{
/* 输入参数长度检查 */
if(strlen(pcUserName)>MAX_USERNAME || strlen(aucDevcode)>MAX_DEVCODE ||strlen(strlen)>MAX_TITLE ||strlen(pcCurtime)>MAX_TIME ){
return ERROR_PARAM;
}
gdImagePtr pstImg = NULL; //声明图像结构体指针,结构体具体内容见gd.h文件
int black = 0; //声明颜色索引
int white = 0;
double udFontHig = 50.0; //字体高度
char *pcErr = NULL; //gdImageStringFT()函数错误内容,若函数正常执行,函数返回NULL
int uiFontLen = 0; //存储图片生成高度
int uiFontwid = 0; //存储图片生成宽度
char pcMsg[MAX_BUFFER]; //参数集和字符串
char pcJpegName[MAX_BUFFER]; //生成图片名称
char *pcTmp = NULL; //gdimg存储缓存
//linux中文字体为文泉驿微米黑,系统路径为/usr/share/fonts/wqy-microhei.ttc
char *pcFont = "/usr/share/fonts/wqy-zenhei/wqy-zenhei.ttc";
//计算单个字体宽度,按照左上,右上,右下,左下顺序,利用(X,Y)坐标表示,因此brect共有8个值
int brect[8];
FILE *pfJpeg = NULL; //声明输出文件格式 正式代码删除
/* 拼接输入参数,将参数整合到pcMsg中 */
strncpy(pcMsg, pcUserName, strlen(pcUserName));
strcat(pcMsg,aucDevcode);
strcat(pcMsg,pcTitle);
strcat(pcMsg,pcCurtime);
/* 获取brect用以测量图片大小 */
pcErr = gdImageStringFT(NULL, &brect[0], 0, pcFont,
udFontHig, 0.0, 0, 0, pcMsg);
if(pcErr){
return ERROR_GET_FONT_SIZE;
}
/* 创建对字符串来讲一个足够大尺寸的图片,并且有一些留白 */
uiFontLen = brect[2]-brect[6]+6;
uiFontwid = brect[3]-brect[7]+6;
//创建uiFontLen*uiFontwid的图像,如果需要使用真彩色,换成gdImageCreatTrueColor接口
pstImg = gdImageCreate(uiFontLen,uiFontwid);
/* 指定背景色,因为是图中首个被定义的颜色所以为背景色.颜色为红绿蓝三原色组成 */
white = gdImageColorResolve(pstImg, 255, 255, 255);
black = gdImageColorResolve(pstImg, 0, 0, 0);
/* 设置打印字符串左下角基准点 */
uiFontLen = 3-brect[6];
uiFontwid = 3-brect[7];
/* 将字符串打印至gd image */
pcErr = gdImageStringFT(pstImg, &brect[0], black, pcFont,
udFontHig, 0.0, uiFontLen, uiFontwid, pcMsg);
if(pcErr){
return ERROR_RENDER_IMAGE;
}
/* 测试用代码段,生成图片名称,正式代码可删 91-96 */
strncpy(pcJpegName, pcMsg, strlen(pcMsg));
strcat(pcJpegName,".jpg");
//打开一个文件作为写入,"wb"意思是写二进制,对MSDOS很重要,对Unix来说影响较小
pfJpeg = fopen(pcJpegName, "wb");
//将图片输出到硬盘存储以jpg格式,第三个参数为按照默认图片质量(0-95)
gdImageJpeg(pstImg, pfJpeg, -1);
/* 将生成的图片buffer传入参数pucPic中 */
int uiTempSize;
pcTmp = (char*)gdImageJpegPtr(pstImg,&uiTempSize,-1);
memcpy(pucPic, pcTmp, uiTempSize);
*uisize = uiTempSize;
fclose(pfJpeg);
free(pcTmp);
gdImageDestroy(pstImg);
return 0;
}
下面来具体罗列一下我遇到的新手常见问题。
1.strlen和sizeof
由于该函数开始需要检测一下参数长度是否超出限制,因此我需要比较参数字符串长度。在这里作为新手的我就遇到了使用sizeof编译报错的经历。
分析:
未初始化的情况下,用strlen是不可行的,因为strlen的唯一标准是找‘\0’,记住这个就能明确strlen会执行处什么值。
初始化与否sizeof()的结果不不变,但是反应的并非真实字符串长度而是所占空间大小,所以memset初始化的时候用sizeof较好。
char* 类型应特别注意,sizeof()计算出来的是指针大小,32位系统4字节,64位占8字节,与char*的字符串毫无关系,只有char[N]字符数组使用sizeof ()计算大小,结果是数组元素个数,而非指针大小,但是如果将其用于参数传递的话,子函数中获取的将不再是字符串数组类型,而是指针,这个要特别注意。
系统函数返回值是char *类型的往往会在末尾加上'\0'。
总归,初始化后strlen计算真实字符串大小不会出错,真实大小的判断方法是找'\0'。sizeof()结果与字符串真实大小无关,与初始化与否无关,计算的是变量所占空间。
2.隐式声明与内建函数
编译过程中我就遇到了这种类型的报错,原因为忘记include相关头文件了。
3.strncpy
错误:char *a; char *b = "hello"; strncpy(a,b,strlen(b));
正确:char a[x]; char *b = "hello"; strncpy(a,b,strlen(b));
再说明一下strncpy要比strcpy用着安全一点,因为strcpy只是复制字符串,但不限制复制的数量,很容易造成缓冲溢出。strncpy要安全一些。
4.引用方式调用函数
在这里我一直由于没有搞懂char*作为参数的真正含义,所以无法将函数内的数值传递到main函数中。之后百度了一下c语言函数,看到了引用方式调用函数,瞬间豁然开朗。我之前的调用方式一直是对形参的操作,形参的数值并未传递到实参。