随波逐流的空间

希望每天黎明有着希望的光茫,照耀着并希望着,我们每天思考一步,就要想到做第三步。

EasyX 绘图库实现一个输出半透明字体函数

0、程序运行截图

1、运行原理

通过把普通文字输出在一个 图片对象里头,然后对这个图,片对象进行背景与文字的分离,把分离的文字数据存入一个三维数组中,然后使用半透明画点函数,输出这个三维数 组里的字体,就有半透明的字体效果。

2、EasyX 绘图库输出半透明字体代码展示

// 实现一个 EasyX 绘图库的半透明文字输出函数。让你的绘图文字支持任意的透明度,达到让大家推波推润的效果。
// 日期:2023-7-21
// 作者:随波逐流
// 联系:2963787923@qq.com

#include<graphics.h>
#include<stdio.h>
#include<conio.h>
#include <math.h>
#include<stdlib.h>
#include<time.h>

// 初始字体大小。
int txtsiz =30;
// 设置半透明文本函数字体大小。
void settxtsiz(int al){ txtsiz = al; }
// 初始换透明度为百分比 50 也就是 128。
int alpha =128;
// 设置半透明度函数。
void setalpha(int al) { alpha = al; }
// 打开缓冲区。
DWORD* g_pBuf11;
// 进行前景色和背景色半透明计算。
COLORREF Alphargb1(int r1, int g1, int b1, int r2, int g2, int b2, int alp)
{	// 替换颜色。
	alp = alpha;
	// 半透明颜色混合计算算法。(村长修改的特殊算法,使用位移算法,能提高效率)。
	COLORREF rgb_alpha =RGB((r1 * alp + r2 *(256 - alp)) >> 8, (g1 * alp + g2 * (256 - alp)) >> 8, (b1 * alp + b2 * (256 - alp)) >> 8);
	// 返回颜色。
	return rgb_alpha;
}
// 显示缓冲区指针,并读取坐标上的颜色值。
COLORREF fast_getpixelcolor(int x, int y, int WIDTH) { COLORREF c = g_pBuf11[y * WIDTH + x]; return BGR(c); }
// 快速缓冲区半透明画点函数 2
int putpixealpha(DWORD* g_pBuf, int x, int y, COLORREF c)
{
	int WIDTH = getwidth(), HEIGHT = getheight(), puti = y * WIDTH + x;
	// 如果 puti 超出缓冲区范围,直接返回。
	if (puti < 0 || puti >= (WIDTH * HEIGHT)) { return 0; }
	COLORREF backdropcolor, bkcolor = getbkcolor();
	// 读取背景上的颜色点。
	backdropcolor = fast_getpixelcolor(x, y, WIDTH);
	// 声明定义背景颜色和绘图的颜色。
	int r2 = GetRValue(c), g2 = GetGValue(c), b2 = GetBValue(c), r1 = GetRValue(backdropcolor), g1 = GetGValue(backdropcolor), b1 = GetBValue(backdropcolor);

	// 如果获取的颜色等于背景色不进行透明计算。
	if (bkcolor == backdropcolor) { g_pBuf[puti] = BGR(c); }
	// 透明颜色混合并输出。
	if (backdropcolor != bkcolor) { g_pBuf[puti] = BGR(Alphargb1(r2, g2, b2, r1, g1, b1, 10)); }
	return 0;
}

// 缓冲区画点。
void putpixelqBuffer(DWORD* pibu, int x, int y, int w, int h, COLORREF color)
{
	long zb = (h * y), zb1 = (w - x), ab2 = zb - zb1;
	pibu[abs(-ab2)] = BGR(WHITE);
}

// 使用中点算法画任意斜率的开放式直线(包括起始点,不包括终止点,同时返回该直线的所有 X_Y 坐标与线长度)。
int line_s1(int x1, int y1, int x2, int y2, int len[][2])
{
	int w1 = getwidth(), h1 = getheight(), len1 = 0;
	if (x1 >= w1 || x2 >= w1 || x1 <= 0 || x2 <= 0 || y1 >= h1 || y2 >= h1 || y1 <= 0 || y2 <= 0)return 0;

	DWORD* p = GetImageBuffer();
	int x = x1, y = y1;
	int a = y1 - y2, b = x2 - x1;
	int cx = (b >= 0 ? 1 : (b = -b, -1));
	int cy = (a <= 0 ? 1 : (a = -a, -1));
	int ii = 0, iii = 0;
	int d, d1, d2;

	if (-a <= b)
	{	// 斜率绝对值 <= 1。
		d = 2 * a + b;
		d1 = 2 * a;
		d2 = 2 * (a + b);

		while (x != x2)
		{
			putpixelqBuffer(p, len[ii][0] = x, len[ii][1] = y, w1, h1, RED);
			ii++;
			if (d < 0)
				y += cy, d += d2;
			else
				d += d1;
			x += cx;
		}
		return ii;
	}
	else {	// 斜率绝对值 > 1。
		d = 2 * b + a;
		d1 = 2 * b;
		d2 = 2 * (a + b);

		while (y != y2)
		{
			putpixelqBuffer(p, len[iii][0] = x, len[iii][1] = y, w1, h1, RED);
			iii++;
			if (d < 0)
				d += d1;
			else
				x += cx, d += d2;
			y += cy;
		}
		return iii;
	}
}
// 多做线 X_Y 容器。
int line_xy[6][3000][2] = { 0 };

// 开放式多段线绘图函数,通过指定 5 点坐标,返回 5 点坐标上的所有 X_Y 坐标与多段线长度。
int pl(int l, int p[], int line_xy1[][2])
{
	for (int g = 0; g < 6; g++)
		for (int i = 0; i < 3000; i++)
		{
			for (int j = 0; j < 2; j++)
			{
				line_xy[g][i][j] = 0;
			}
		}
	// tr 是奇数偶数开关
	int tr = 0;
	// 是五段线分别每一段直线的长度容器。
	int s[10][1] = { 0 };
	// 累加
	int iii = 0;
	// 累加

	// 特殊法配合奇偶开关绘制多段线
	for (int i = 0; i < l * 2; i++)
	{	// 奇偶开关
		if ((tr % 2) == 0)
		{
			// 核心绘制坐标点的线段并依次返回坐标与线长度
			s[iii][1] = line_s1(p[i], p[i + 1], p[i + 2], p[i + 3], line_xy[iii]);
			iii++;
			tr++;
		}
		else if ((tr % 2) != 0)
		{
			tr++;
		}
	}
	// 绘制末段线。
	s[4][1] = line_s1(p[l * 2 - 2], p[l * 2 - 1], p[0], p[1], line_xy[4]);

	// 把三维数组里的五段线 X_Y 坐标存入二级数组。
	int ss = 0;
	for (int g = 0; g < iii + 1; g++)
		for (int i = 0; i < s[g][1]; i++)
		{
			for (int j = 0; j < 2; j++)
			{
				line_xy1[ss][j] = line_xy[g][i][j];
			}
			ss++;
		}
	// 把五段线长度统一成完整的多线线长度。
	int ssg = 0;
	for (int i = 0; i < iii + 1; i++)
	{
		ssg = ssg + s[i][1];
	}
	// 返回多段线长度。
	return ssg;
}
struct linexy
{
	// 存放一条直线的绘制过程的 x_y 坐标二次使用。
	int line_xy2[9][3000][2] = { 0 };
}lixy;

/* 设置字体大小的函数 */
int settextzise(int zise)
{
	LOGFONT f;
	gettextstyle(&f);									// 获取当前字体设置。
	f.lfHeight = zise;									// 设置字体高度为 48。
	_tcscpy_s(f.lfFaceName, _T("黑体"));
	f.lfQuality = ANTIALIASED_QUALITY;					// 设置输出效果为抗锯齿。
	settextstyle(&f);
	return zise;
}
int tex[2000][2000][1] = { 0 };
// 半透明字体输出函数 X_Y 字体坐标,str 输入的字符患,rgb 字体颜色,size 字体大小。
int outtext_alpha(int x, int y, LPCTSTR str, COLORREF rgb, int size)
{	// 初始化字体大小
	settextzise(size);
	// 把文字设成不透明会更好的获取颜色。
	setbkmode(OPAQUE);
	// 获取背景颜色。
	COLORREF bkcolor = getbkcolor();
	// 获取文字颜色。
	COLORREF textcolor = gettextcolor();
	// 从图片里排除背景颜色只获取文字颜色
	COLORREF bk_textcolor;
	// 定义输出文字图片对你大小是文字的大小
	IMAGE img(textwidth(str), textheight(str));

	// 把文字写入图片对像中
	SetWorkingImage(&img);
	// 设置字体大小。
	settextzise(size);
	// 把文字绘制在图片里
	outtextxy(0, 0, str);

	SetWorkingImage();
	// 将 img 对象显示在绘图窗口中

	// 用图片缓冲区打开文字
	DWORD* pMem = GetImageBuffer(&img);
	// 获取图片文字的高宽
	int WIDTH = img.getwidth();
	int HEIGHT = img.getheight();

	// 读取图片中的文字,排除背景,保留文字。
	for (int i = 0; i < img.getwidth(); i++)
	{
		for (int j = 0; j < img.getheight(); j++)
		{
			int xy = j * WIDTH + i;
			if (xy < 0 || xy >= (WIDTH * HEIGHT)) { return 0; }

			int r = GetRValue(pMem[xy]);
			int g = GetGValue(pMem[xy]);
			int b = GetBValue(pMem[xy]);

			bk_textcolor = RGB(r, g, b);
			if (bk_textcolor == bkcolor) { tex[i][j][1] = 2; }
			else if (bk_textcolor == textcolor) { tex[i][j][1] = 1; }
		}
	}

	// 从三维数组里绘图半透明的文字
	for (int i = x; i < (x + img.getwidth()); i++)
	{
		for (int j = y; j < (y + img.getheight()); j++)
		{
			if (tex[i - x][j - y][1] == 2) { 0; }
			else if (tex[i - x][j - y][1] == 1) { putpixealpha(g_pBuf11, i, j, rgb); }
		}
	}
	return 0;
}

// 主函数
int main()
{	// 随机种子。
	srand((unsigned)time(0));
	// 创建绘图窗口
	initgraph(800, 800);
	// 全局缓冲区
	g_pBuf11 = GetImageBuffer();
	// 缓冲区获取。
	DWORD* g_pBuf = GetImageBuffer();
	int siz = 85;
	COLORREF rgb[9] = { 0 };
	TCHAR str[20][100] =
	{
		_T("EasyX"), _T("绘图库"), _T("半透明"), _T("字体展示"), _T("outtext_alpha"), _T("EasyX 绘图库"),
		_T("半透明字体"), _T("!@#$%^^&*()_+{}:"), _T("字体展示"), _T("半透明"), _T("(=^ ^=)"),
		_T("绘图库"), _T("EasyX"), _T("EasyX"), _T("绘图库"), _T("半透明"), _T("字体展示"), _T("EASYX 半透明字体"),
	};
	// 容纳透明方格背景图片变量。
	IMAGE IM(800, 800);
	// 使用奇偶开关来绘制透明背景。
	for (int i = 0; i <= 100; i++)
	{
		for (int j = 0; j <= 100; j++)
		{
			// 奇偶开关,偶数绘制灰格了。
			if (((i + j) % 2) == 0)
			{
				// 绘制灰格。
				setfillcolor(RGB(204, 204, 204));
				fillrectangle(i * 8, j * 8, i * 8 + 8, j * 8 + 8);
			}
			// 奇偶开关,奇数绘制白格了。
			else if (((i + j) % 2) != 0)
			{
				// 绘制白格。
				setfillcolor(WHITE);
				fillrectangle(i * 8, j * 8, i * 8 + 8, j * 8 + 8);
			}
		}
	}
	getimage(&IM, 0, 0, 800, 800);
	// 设置透明度为百分比 50 也就是 128。
	setalpha(128);
	BeginBatchDraw();

	while (true)
	{

		for (int i = 0; i < 9; i++)
		{
			// 设置半透文字的的随机运动轨迹坐标。
			int pts[10] = { 0 };
			for (int i = 0; i < 10; i++) { pts[i] = rand() % 800 + 1; }
			int s1 = pl(5, pts, lixy.line_xy2[i]);
			// 设置随机颜色
			rgb[i] = RGB(rand() % 255 + 1, rand() % 255 + 1, rand() % 255 + 1);

		}
		// 绘制半透明的文字输出。
		for (int i = 0; i < 2000; i++)
		{
			putimage(0, 0, &IM);
			outtextxy(0, 25 * 3 * 2 + 45 + 75 + 40 * 2, _T("原神启动!!!冲~巴巴啦呀~"));
			outtextxy(0, 25 * 3 * 2 + 45 + 75 + 40 * 2 + 50, _T("do u like van 游戏"));

			for (int j = 0; j < 9; j++)
			{
				outtext_alpha(lixy.line_xy2[j][i][0], lixy.line_xy2[j][i][1], str[j], rgb[j], 100);
			}
			outtext_alpha(2, 2, _T("EasyX 绘图库半透明字体展示"), RED, 60);
			outtext_alpha(0, 0, _T("EasyX 绘图库半透明字体展示"), WHITE, 60);
			outtext_alpha(2, 20 * 3, _T("矢奈八织圣言斩离切裁刀"), BLUE, 70);
			outtext_alpha(0, 25 * 3 + 60, _T("圣骑莲-矢奈八织-圣言蔪裁切璃刀-"), 0xa20537, 50);
			outtext_alpha(0, 25 * 3 * 2 + 45, _T("厨俱焰恋矢量颖惯枪"), 0xb167ae, 80);
			outtext_alpha(0, 25 * 3 * 2 + 45 + 75, _T("堂莎辐烮格日用,圣印灵花折叠菜之罩"), 0xa20537, 40);
			outtext_alpha(0, 25 * 3 * 2 + 45 + 75 + 40, _T("圣一品,繁璃恋锦之抨击暗夜胶伞"), 0xa20537, 50);
			FlushBatchDraw();
		}
	}
	EndBatchDraw();
	getmessage(EX_CHAR);
	closegraph();
	return 0;
}

3.参考资料

添加评论