freetype字体描边

最近需要用到描边字体。freetype官网给出了一份描边sample, 不过这份代码是C++的。还好csdn有兄弟用c对这个重写了一份。使用这份代码前需要了解freetype里边的一些主要概念。下面记录主要记录下这个过程。
下面的代码实现了输出字体为tga文件,内部contain的数据是rgba,生成字体的颜色可以通过代码来设置。

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <math.h>
#include <stdlib.h>
#include<malloc.h>
#include <freetype2/ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_STROKER_H
#include FT_IMAGE_H


#define uint8 unsigned char
#define uint16 unsigned short



/* origin is the upper left corner */

struct Pixel32
{

	uint8 b;
	uint8 g;
	uint8 r;
	uint8 a;
};

struct Span {
	int x;
	int y;
	int width;
	int coverage;
};

typedef struct Lspan {
	struct Span node;
	struct Lspan *next;
}Node, *PNode;

struct Rect {
	float xmin;
	float xmax;
	float ymin;
	float ymax;
};

struct vec2 {
	float x;
	float y;
};

unsigned int w_width = 0;
unsigned int w_height = 0;


#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
//获取外接矩形
void Include(const struct vec2 *r, struct Rect *rect)
{
	rect->xmin = MIN(rect->xmin, r->x);
	rect->ymin = MIN(rect->ymin, r->y);
	rect->xmax = MAX(rect->xmax, r->x);
	rect->ymax = MAX(rect->ymax, r->y);
}


void RasterCallback(const int y,
		const int count,
		const FT_Span * const spans,
		void * const user)
{
	PNode sptr = (PNode)user;
	while (sptr->next != NULL)
		sptr = sptr->next;
	int i;
	for (i = 0; i < count; ++i) {
		PNode new = calloc(sizeof(Node), 1);
		if (!new) {
			printf("failed to alloc new node\n");
			break;
		}
		new->next = NULL;
		new->node.x = spans[i].x;
		new->node.y = y;
		new->node.width = spans[i].len;
		new->node.coverage = 255;
		sptr->next = new;
		sptr = sptr->next;
	}
}

void RenderSpans(FT_Library *library,
		FT_Outline *const outline,
		Node *spans)
{
	FT_Raster_Params params;
	memset(&params, 0, sizeof(params));
	params.flags = FT_RASTER_FLAG_AA | FT_RASTER_FLAG_DIRECT;
	params.gray_spans = RasterCallback;
	params.user = spans;
	FT_Outline_Render(*library, outline, &params);
}

#pragma pack(push)
#pragma pack(1)
struct TGAHeader
{
  uint8   idLength,           // Length of optional identification sequence.
          paletteType,        // Is a palette present? (1=yes)
          imageType;          // Image data type (0=none, 1=indexed, 2=rgb,
                              // 3=grey, +8=rle packed).
  uint16  firstPaletteEntry,  // First palette index, if present.
          numPaletteEntries;  // Number of palette entries, if present.
  uint8   paletteBits;        // Number of bits per palette entry.
  uint16  x,                  // Horiz. pixel coord. of lower left of image.
          y,                  // Vert. pixel coord. of lower left of image.
          width,              // Image width in pixels.
          height;             // Image height in pixels.
  uint8   depth;              // Image color depth (bits per pixel).
  uint8    descriptor;         // Image attribute flags.

};
#pragma pack(pop)

int WriteTGA(const char *filename,
	const struct Pixel32 *pxl,
	uint16 width,
	uint16 height)
{
	int ret = 0;
	FILE *fp = fopen(filename, "w+");
	if (fp) {
		struct TGAHeader header;
		memset(&header, 0, sizeof(struct TGAHeader));
		header.imageType  = 2;
		header.width = width;
		header.height = height;
		header.depth = 32;
		header.descriptor = 0x28;
		printf("tga header is %d\n", sizeof(struct TGAHeader));
		fwrite(&header, sizeof(struct TGAHeader), 1, fp);
		/* printf("sizeof TGAHeader: %ld\n", sizeof(struct TGAHeader)); */
		fwrite(pxl, 1, sizeof(struct Pixel32) * width * height, fp);
	} else {
		printf("open file error\n");
		ret = -1;
	}

	if (fp)
		fclose(fp);
	return ret;
}

void gen_tga(FT_Face *face, FT_Library *library, unsigned int ch, int wch) {
	FT_UInt gindex;
	FT_GlyphSlot slot;
	FT_Stroker stroker;
	FT_Error error;
	PNode sp = NULL;
	PNode olsp = NULL;
	PNode sph = NULL;
	PNode olsph = NULL;
	static int han_count = 0;

	w_width = wch ? w_width *2 : w_width;

	/* use 50pt at 100dpi */
	error = FT_Set_Char_Size(*face, w_width<<6, w_height<<6, 72, 72);  /* set character size */
	/* error handling omitted */
	if (error) {
		printf("FT_Set_Char_Size error\n");
		goto exit;
	}
	/*get slot*/
	slot = (*face)->glyph;
	/*get charactor index*/
	gindex = FT_Get_Char_Index(*face, ch);
	/*load image to slot*/
	error = FT_Load_Glyph(*face, gindex, FT_LOAD_NO_BITMAP);
	if (error) {
		printf("FT_Load_Glyph error\n");
		goto exit;
	}

	printf("outline contours are %d, npoints are %d\n", slot->outline.n_contours,
	       slot->outline.n_points);
	/*外形数据*/
	if (slot->format == FT_GLYPH_FORMAT_OUTLINE) {
		// Render the basic glyph to a span list.
		/*创建链表用来保存跨度span数据,span
		 * 是y固定,x坐标和宽度确定的一条扫描线*/
		PNode sp = (PNode)malloc(sizeof(Node));
		if (!sp) {
			printf("failed to malloc Node\n");
			goto exit;
		}
		memset(&(sp->node), 0, sizeof(struct Span));
		sp->next = NULL;
		/*获取实际外形span*/
		RenderSpans(library, &(slot->outline), sp);

		// Next we need the spans for the outline. 下面的代码用来获取
		// 加宽外层轮廓的span组,
		// 加宽轮廓的数据实际图形比上面的外形图形宽出来的部分是轮廓,两份
		// 数据叠加在一起就获得描边图形
		PNode olsp = (PNode)malloc(sizeof(Node));
		if (!olsp) {
			printf("failed to malloc olsp\n");
			goto exit;
		}
		memset(&(olsp->node), 0, sizeof(struct Span));
		olsp->next = NULL;

		error = FT_Stroker_New((*library), &stroker);
		FT_Stroker_Set(stroker,
				3 * 64,
				FT_STROKER_LINECAP_ROUND,
				FT_STROKER_LINEJOIN_ROUND,
				0);

		FT_Glyph glyph;
		if (FT_Get_Glyph(slot, &glyph) == 0) {
			error = FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1);
			/* printf("error = %d\n", error); */
			// Again, this needs to be an outline to work.
			if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
				// Render the outline spans to the span list
				FT_Outline *o = &(((FT_OutlineGlyph)glyph)->outline);
				printf("stroke contours are %d, npoints are %d\n", o->n_contours, o->n_points);
				RenderSpans(library, o, olsp);
			}
			// Clean up afterwards.
			FT_Stroker_Done(stroker);
			FT_Done_Glyph(glyph);

			// Now we need to put it all together.


			do {
				// Figure out what the bounding rect is for both the span lists.
				struct Rect rect;
				memset(&rect, 0, sizeof(struct Rect));
				PNode ptr = NULL;

				sph = sp->next;
				olsph = olsp->next;
				if (!sph || !olsph)
					break;

				rect.xmin = sph->node.x;
				rect.xmax = sph->node.y;
				rect.ymin = sph->node.x;
				rect.ymax = sph->node.y;
				//获取实际图形的外接矩形
				for(ptr = sph; ptr != NULL; ptr = ptr->next) {
					struct vec2 vc1, vc2;
					vc1.x = ptr->node.x;
					vc1.y = ptr->node.y;
					vc2.x = ptr->node.x + ptr->node.width - 1;
					vc2.y = ptr->node.y;

					Include(&vc1, &rect);
					Include(&vc2, &rect);
				}
				//获取加宽轮廓图形的外接矩形
				for (ptr = olsph; ptr != NULL; ptr = ptr->next) {
					struct vec2 vc1, vc2;
					vc1.x = ptr->node.x;
					vc1.y = ptr->node.y;
					vc2.x = ptr->node.x + ptr->node.width - 1;
					vc2.y = ptr->node.y;

					Include(&vc1, &rect);
					Include(&vc2, &rect);
				}
				//计算实际图形大小
				int imgWidth = MAX(w_width, rect.xmax - rect.xmin + 1);
				int imgHeight = MAX(w_height, rect.ymax - rect.ymin + 1);
				int imgSize = imgWidth * imgHeight;
				printf("imgHeight=%d\n", imgHeight);
				printf("imgWidth=%d\n", imgWidth);
				printf("imgSize=%d\n", imgSize);

				// Allocate data for our image and clear it out to transparent.
				/* struct Pixel32 pxl[imgSize]; */
				//下面用来保存rgba数据
				struct Pixel32 *pxl;
				pxl = malloc(sizeof(struct Pixel32) * imgSize);
				if (!pxl) {
					fprintf(stderr, "fail malloc img\n");
					exit(-1);
				}

				memset(pxl, 0, sizeof(struct Pixel32) * imgSize);

				// Loop over the outline spans and just draw them into the image.
				// 用红色画加宽轮廓的图形,作为背景
				int w;
				for (ptr = olsph; ptr != NULL; ptr = ptr->next)
					for (w = 0; w < ptr->node.width; ++w) {
						unsigned int m = (unsigned int)((imgHeight - 1 - (ptr->node.y - rect.ymin)) * imgWidth +
								ptr->node.x - rect.xmin + w);
						pxl[m].r = 255;
						pxl[m].g = 0;
						pxl[m].b = 0;
						pxl[m].a = 255;
					}

				//用白色画未加宽轮廓的原始图形覆盖背景,没有覆盖的订房即为描边
				for (ptr = sph; ptr != NULL; ptr = ptr->next)
					for (w = 0; w < ptr->node.width; ++w) {
						struct Pixel32 src;
						int m = (int)((imgHeight - 1 - (ptr->node.y - rect.ymin)) * imgWidth +
								ptr->node.x - rect.xmin + w);
						//src.r = 255;
						//src.g = 255;
						//src.b = 255;
						//src.a = 255;//ptr->node.coverage;
						pxl[m].r = 255;//(int)(pxl[m].r + ((src.r - pxl[m].r) * src.a) / 255.0f);
						pxl[m].g = 255;//(int)(pxl[m].g + ((src.g - pxl[m].g) * src.a) / 255.0f);
						pxl[m].b = 255;//(int)(pxl[m].b + ((src.b - pxl[m].b) * src.a) / 255.0f);
						pxl[m].a = 255;//MIN(255, pxl[m].a + src.a);
					}


				// Dump the every charactor image to disk.
				char fn[50];
				memset(fn, 0, 30);
				if (wch) {
					sprintf(fn, "outhan%d.tga", han_count);
					han_count ++;
				}
				else
					sprintf(fn, "out%c.tga", ch);
				printf("outfile: %s\n", fn);
				WriteTGA(fn, pxl, imgWidth, imgHeight);

				free(pxl);
				pxl = NULL;
			} while(0);
		}
	}

exit:
	while(sp) {
		PNode pre = sp;
		sp = sp->next;
		free(pre);
		pre = NULL;
	}

	while(olsp) {
		PNode pre = olsp;
		olsp = olsp->next;
		free(pre);
		pre = NULL;
	}
}


int main(int argc, char** argv)
{
	FT_Library library;
	FT_Face face;
	FT_Error error;

	char *filename;
	char *text;
	int n, num_chars;

	if (argc != 4) {
		fprintf(stderr, "usage: %s font width sample-text \n", argv[0]);
		exit(1);
	}

	filename = argv[1];   /* first argument     */
	w_width = atoi(argv[2]);
	text = argv[3];   /* second argument    */
	num_chars = strlen(text);

	w_height = w_width * 2;

	error = FT_Init_FreeType(&library);     /* initialize library */
	if (error) {
		printf("FT_Init_FreeType failed\n");
		return -1;
	}

	error = FT_New_Face(library, filename, 0, &face); /* create face object */
	if (error) {
		printf("FT_New_Face failed\n");
		FT_Done_FreeType(library);
		return -1;
	}

	for (n = 0; n < num_chars; n++)
	{
		unsigned int ch_in = 0;
		if (0x0e == ((uint8_t)text[n]) >> 4) {
			ch_in = (uint16_t)(text[n] & 0x0f) << 12 |
			     (uint16_t)(text[n + 1] & 0x3f) << 6 |
			     (uint16_t)(text[n + 2] & 0x3f);
			n += 2;
			gen_tga(&face, &library, ch_in, 1);
		} else {
			ch_in = text[n];
			gen_tga(&face, &library, ch_in, 0);
		}
	}

	FT_Done_Face(face);
	FT_Done_FreeType(library);

	return 0;
}

参考
参考1
freetype使用总结

FreeType是一个开源的字体渲染库,它提供了一套功能强大的API,用于加载、解析和渲染字体文件。使用FreeType库,您可以在应用程序中实现高质量的字体渲染。 以下是使用FreeType库加载和渲染字体的基本步骤: 1. 下载和安装FreeType库:您可以从FreeType官方网站(https://www.freetype.org)下载库文件,并按照文档进行安装。 2. 引入头文件和链接库:在您的项目中引入FreeType的头文件和链接库。具体的引入方式会根据您使用的编程语言和开发环境而有所不同。 3. 初始化FreeType库:在使用FreeType之前,需要调用FT_Init_FreeType函数来初始化FreeType库。 4. 加载字体文件:使用FT_New_Face函数加载字体文件。您需要提供字体文件的路径,并指定要加载的字体索引(如果字体文件包含多个字体)。 5. 设置字体大小:使用FT_Set_Pixel_Sizes函数或FT_Set_Char_Size函数设置字体的大小。 6. 渲染字符:使用FT_Load_Char函数加载要渲染的字符,并使用FT_Render_Glyph函数将字符渲染为位图。 7. 获取位图数据:通过FT_GlyphSlot结构体中的bitmap成员获取位图数据。 8. 绘制位图:将位图数据绘制到屏幕或纹理上,以实现字体渲染效果。 9. 释放资源:在使用完FreeType库后,需要调用相应的清理函数来释放资源,例如FT_Done_Face和FT_Done_FreeType。 请注意,以上仅为使用FreeType库的基本步骤,具体的实现方式会根据您的需求和编程语言而有所不同。您可以参考FreeType库的文档和示例代码,以及相关编程语言的FreeType库绑定或封装库的使用指南,来更详细地了解和应用FreeType库。 希望这些信息对您有所帮助!如果您有任何进一步的问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值