EasyX 绘图库实现一个输出半透明字体函数
2023-7-21 ~ 2023-11-5
(1)
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.参考资料
- 我的另一篇文章:EasyX 绘图库实现一个缓冲区半透明绘图