使用C语言与EasyX实现图片文字雪屏特效

沈知远
沈知远
发布时间:2026-10-15| 4人浏览过
原创

本教程演示如何使用 C 语言结合 EasyX 图形库,制作一款能够识别图片文字并在屏幕上生成雪花覆盖效果的程序。通过解析目标图片的像素分布,驱动雪花粒子沿特定轨迹运动,最终实现类似电影中的"雪屏"视觉冲击效果。

程序原理与目标

该程序的核心逻辑是将一张图片转化为二维坐标点阵数据。程序首先读取图片,将非深色区域标记为可绘制路径;随后初始化一组随机位置的雪花对象,这些雪花会根据预设的方向移动,当它们落在标记路径上时,会留下不可擦除的痕迹,直到文字完全被“雪”覆盖或雪花循环移动形成动态视觉效果。

准备条件与编译环境

  1. 开发工具:推荐使用 Visual Studio 2019 或更高版本。
  2. 图形库:需安装 EasyX_20210224 或兼容版本。
  3. 输入素材:需要在项目根目录下放置一张尺寸为 640x480 的图片,文件名默认为 img.jpg。背景建议为浅色,文字或主体为深色,以便算法准确识别。

核心操作:修改与运行

第1步:下载并整合源码

使用以下提供的完整 C 语言源码,保存为 main.cpp 或 .c 文件。代码中包含了完整的雪花结构体定义、链表管理以及绘图逻辑。

/*	*************************
 *	程序名称:	表白程序 (雪屏特效)
 *	编译环境:	VC2019,EasyX_20210224
 *	********* 说明 **********
 *	需要自己提供一张大小为 640×480 的图片(img.jpg)在项目根目录下
 *	大小可以在程序内更改,图片名字也可以。
 *	*************************/
#include <graphics>
 
#define MAX_STAR 500			// 雪花数量上限
#define SCREEN_WIDTH 640		// 屏幕宽度
#define SCREEN_HEIGHT 480		// 屏幕高度
#define MAX_STEP 3				// 雪花每次移动最高步长
#define MAX_RADIUS 3			// 雪花最大半径
#define IMGNAME _T("img.jpg")	// 图片名字
 
using namespace std;
 
// 图片转数组(这个有很大优化空间的,需要识别彩色照片可以看这)
int imgList[SCREEN_HEIGHT][SCREEN_WIDTH] = { 0 };
 
// 雪花状态
enum STATUS
{
	STOP = 0,	// 不动
	UP,			// 向上
	DOWN,		// 向下
	LEFT,		// 向左
	RIGHT,		// 向右
	RANDOM,		// 随机
	ALL_STATUS	// 记录状态总数
};
struct STAR
{
	int x;				// 雪花的 x 坐标
	int y;				// 雪花的 y 坐标
	enum STATUS stat;	// 雪花状态
	unsigned radius;	// 雪花的半径
	int step;			// 雪花每次移动的步长
	int color;			// 雪花的颜色
};
struct SqList
{
	struct STAR* elems; // 顺序表的基地址
	int length;			// 顺序表的长度
	int size;			// 顺序表的空间
};
 
// 顺序表的接口
bool initList(SqList& L);
bool listAppend(SqList& L, struct STAR e);
bool listDelete(SqList& L, int i);
void destroyList(SqList& L);
 
bool initList(SqList& L)
{
	L.elems = new struct STAR[MAX_STAR];
	if (!L.elems) return false;
	L.length = 0;
	L.size = MAX_STAR;
	return true;
}
bool listAppend(SqList& L, struct STAR e)
{
	if (L.length == L.size) return false;	// 存储空间已满
	L.elems[L.length] = e;
	L.length++;								// 表长加 1
	return true;
}
bool listDelete(SqList& L, int i)
{
	if (i < 0 || i >= L.length) return false;
	if (i == L.length - 1)
	{										// 删除最后一个元素
		L.length--;
		return true;
	}
	for (int j = i; j < L.length - 1; j++)
	{
		L.elems[j] = L.elems[j + 1];		// 删除位置的后续元素一次往前移
	}
	L.length--;
	return true;
}
void destroyList(SqList& L)
{
	if (L.elems) delete[]L.elems;			// 释放存储空间
	L.length = 0;
	L.size = 0;
}
 
/************************************
* 功能:移动雪花,并在指定区域留下雪痕
* 输入参数:
* L - 雪花对象
* i - 雪花在全局数组中的下标
* 返回值:无
************************************/
void MoveStar(SqList& L, int i)
{
	// 留下雪痕
	if (L.elems[i].stat == DOWN)
	{
		if (imgList[L.elems[i].y][L.elems[i].x] == 1)
		{
			L.elems[i].y += L.elems[i].step;
			L.elems[i].x -= 2;
		}
	}
	if (L.elems[i].stat == UP)
	{
		if (imgList[L.elems[i].y][L.elems[i].x] == 1)
		{
			L.elems[i].y -= L.elems[i].step;
			L.elems[i].x -= 2;
		}
	}
	if (L.elems[i].stat == LEFT)
	{
		if (imgList[L.elems[i].y][L.elems[i].x] == 1) L.elems[i].x -= L.elems[i].step;
	}
	if (L.elems[i].stat == RIGHT)
	{
		if (imgList[L.elems[i].y][L.elems[i].x] == 1) L.elems[i].x += L.elems[i].step;
	}
 
	if (L.elems[i].stat == STOP) return;
	// 擦除原来的雪花
	setfillcolor(BLACK);
	solidcircle(L.elems[i].x, L.elems[i].y, L.elems[i].radius);
	if (L.elems[i].stat == DOWN)
	{
		L.elems[i].y += L.elems[i].step;
		L.elems[i].x -= 2;
		if (L.elems[i].x < 0) L.elems[i].x = SCREEN_WIDTH;
		if (L.elems[i].y > SCREEN_HEIGHT) L.elems[i].y = 0;
		//if(L.elems[i].y>SCREEN_HEIGHT) listDelete(L, i);	// 这段代码可以让飘出屏幕外的雪花消亡
	}
	else if (L.elems[i].stat == UP)
	{
		L.elems[i].y -= L.elems[i].step;
		L.elems[i].x -= 2;
		if (L.elems[i].x < 0) L.elems[i].x = SCREEN_WIDTH;
		if (L.elems[i].y < 0) L.elems[i].y = SCREEN_HEIGHT;
		//if(L.elems[i].y<0) listDelete(L, i);
	}
	else if (L.elems[i].stat == LEFT)
	{
		L.elems[i].x -= L.elems[i].step;
		if (L.elems[i].x > 0) L.elems[i].x = SCREEN_WIDTH;
		//if(L.elems[i].x<0) listDelete(L, i);
	}
	else if (L.elems[i].stat == RIGHT)
	{
		L.elems[i].x += L.elems[i].step;
		if (L.elems[i].x > SCREEN_HEIGHT) L.elems[i].x = 0;
		//if(L.elems[i].x>SCREEN_WIDTH) listDelete(L, i);
	}
	setfillcolor(L.elems[i].color);
	solidcircle(L.elems[i].x, L.elems[i].y, L.elems[i].radius);
}
 
 
/************************************
* 功能:初始化雪花
* 输入参数:
* i - 雪花在全局数组中的下标
* 返回值:无
************************************/
void initStar(struct STAR& _star)
{
	int rgb = 0;
	//rand() 得到随机数范围 0 - 32767 RAND_MAX
	_star.x = rand() % SCREEN_WIDTH;		// x 范围 0 - SCREEN_WIDTH
	_star.y = rand() % SCREEN_HEIGHT;		// y 范围 0 - SCREEN_HEIGHT
	//_star.stat = STATUS(rand() % 6);		// 雪花状态:随机
	_star.stat = DOWN;						// 雪花状态:向下
	_star.radius = 1 + rand() % MAX_RADIUS; // 半径控制 1 - MAX_RADIUS
	_star.step = rand() % MAX_STEP + 1;		// 步长 1 - MAX_STEP
	rgb = 255 * _star.step / MAX_STEP;		// RGB:0 - 255
	_star.color = RGB(rgb, rgb, rgb);
}
 
 
int main()
{
	bool quit = false;
	struct STAR star;
	SqList starList;
 
	// 初始化屏幕
	initgraph(SCREEN_WIDTH, SCREEN_HEIGHT);
 
	// 初始化图片
	IMAGE img;
	loadimage(&img, IMGNAME);
	SetWorkingImage(&img);	// 设置 img为绘制设备
	COLORREF color;			// 记录像素颜色
	BYTE r, b, g;			// 记录像素RGB
 
	for (int y = 0; y < SCREEN_HEIGHT; y++)
	{
		for (int x = 0; x < SCREEN_WIDTH; x++)
		{
			color = getpixel(x, y);		// 获取像素颜色
			r = GetRValue(color);
			b = GetBValue(color);
			g = GetGValue(color);
			if (r < 200 && b < 200 && g < 200)
			{							// 判断需留下“雪痕”的数组位置
				imgList[y][x] = 1;
			}
		}
	}
 
	SetWorkingImage();	// 设置回默认绘制设备
	cleardevice();
 
	// 初始化保存雪花状态的顺序表
	initList(starList);
 
	for (int i = 0; i < MAX_STAR; i++)
	{
		initStar(star);
		listAppend(starList, star);
	}
 
	for (int i = 0; i < starList.length; i++)
	{
		setfillcolor(starList.elems[i].color);
		solidcircle(starList.elems[i].x, starList.elems[i].y,
			starList.elems[i].radius);
	}
 
	while (quit == false)
	{
		for (int i = 0; i < starList.length; i++)
		{
			MoveStar(starList, i);
		}
 
		if (starList.length == 0)
		{	// 若设置雪花离开屏幕后消亡,则会触发此退出
			quit = true;
		}
		Sleep(50);
	}
	system("pause");
	closegraph();
	return 0;
}

第2步:调整图片识别阈值

源码默认识别 RGB 值均小于 200 的像素作为文字区域。如果你的图片素材对比度较低,可以修改 main 函数中的判断条件:

if (r < 200 && b < 200 && g < 200)

根据实际图片颜色调整该数值。

第3步:替换为纯文字模式(可选)

如果不想使用外部图片,可以直接在代码中编写固定文字。找到源码中的图片加载部分:

// 初始化图片
    IMAGE img;
    loadimage(&img, IMGNAME);
    SetWorkingImage(&img);    // 设置 img为绘制设备
    COLORREF color;            // 记录像素颜色
    BYTE r, b, g;            // 记录像素RGB

将其替换为以下绘图代码,即可在屏幕上直接输出“我想和你 有一个家”并作为识别路径:

// 初始化图片
    IMAGE img(SCREEN_WIDTH, SCREEN_HEIGHT);
    //loadimage(&img, IMGNAME);
    SetWorkingImage(&img);    // 设置 img为绘制设备
    setbkcolor(WHITE);
    cleardevice();
    settextcolor(BLACK);
    settextstyle(120, 55, _T("黑体"));
    outtextxy(100, 50, _T("我想和你"));
    outtextxy(100, 250, _T("有一个家"));
    COLORREF color;            // 记录像素颜色
    BYTE r, b, g;            // 记录像素RGB

注意:使用此模式时,后续的像素遍历逻辑依然有效,但文字位置需确保在遍历范围内。

程序效果与注意事项

程序运行时,屏幕会出现大量随机生成的彩色圆点(模拟雪花)。当雪花移动到黑色文字区域时,会停止移动并变为黑色(留下痕迹),从而逐渐勾勒出文字形状。Sleep(50) 控制了帧率,可根据需要调整速度。

常见问题排查

  • 编译错误:确保头文件引用正确,且已链接 EasyX 库。如果是 .c 文件,可能需要重命名为 .cpp 以支持 using namespace std; 和引用传递语法。
  • 雪花乱飞未留痕迹:检查图片是否为深色文字/浅色背景。如果识别不出文字,通常是因为像素 RGB 值未达到设定的阈值,请尝试减小 200 这个数值。
  • 图片不显示:确保 img.jpg 放在项目的源代码同级目录下,或者修改 IMGNAME 宏定义中的路径。

以上就是基于 C 语言和 EasyX 实现图片文字雪屏特效的详细内容,更多关于 C 语言表白神器及图形库应用的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。