EGE基础:图像操作篇

EGE专栏:EGE专栏

目录

EGE图像处理相关函数文档

http://xege.org/manual/api/img/index.htm

一、EGE的图像功能

  读取和绘制图像对于一个图形界面程序来说是一项较为重要的功能,图像可以对界面起到美化的作用。
  对于程序来说,某些图像用基础图形来绘制是十分困难的,甚至是无法实现。这些图像大多是由插画师,美术工程师使用绘图软件精心绘制而成,十分精美,或者是由相机拍摄的摄影作品、界面截屏等。这些图像通常以文件的形式进行保存,如果能够直接从文件中读取图像,那将能免去许多设计的工作(实际上这些图自己也画不出来๑乛◡乛๑)。

  EGE支持以下的图像功能:

在这里插入图片描述

图像功能
功能描述
读取图片文件可读取 PNG, JPEG, BMP, GIF, EMF, WMF, ICO格式图片。多帧图像格式只读取第一帧
截取图像区域截取图像的部分区域作为新图像。
绘制图像可将读取的图像绘制到窗口上或其它图像中,可旋转、缩放绘制。
修改图像可对图像像素内容进行修改。
保存图像可以将图像保存成图片文件,支持BMP, PNG格式。

二、图片读取绘制简单演示

  现在做个简单的演示,读取图像文件,并将图像绘制在窗口上。

1. 找一张保存在电脑上的图片

  先找一张已经保存在电脑上的图片,需要是JPG格式或PNG格式的图片。

请添加图片描述

2. 查看图片的路径(File path)

  读取图像文件首先需要知道图片文件的路径,这样程序才能知道文件存放的位置。

  如下图所示,对于存放于下方目录中的一个PNG图片。

在这里插入图片描述

查看图片文件路径

  ① 鼠标左键单击图片文件,然后单击鼠标右键,在右键菜单中选择 “属性”

在这里插入图片描述

  ② 在属性对话框中,点击 安全 选项卡,可以看到对象名称有一串文本,这个就是文件的路径(绝对路径)。

在这里插入图片描述

  可以看到,文件路径实际上就是由地址栏中的目录路径和文件名拼接而成。

在这里插入图片描述
在这里插入图片描述

  将其复制粘贴,两边加上双引号,并把路径中间的目录分隔符 \ ,改为正斜杠 /或双反斜杠 \\ 即可得到文件路径字符串:"‪E:/博客写作/博客专栏/EGE/鬼刀.png"

  在C/C++中,反斜杠字符'\'用于转义,所以表示反斜杠本身需要用两个反斜杠'\\'来表示。

  在测试图片时,文件路径最好是全英文,以免有编码不匹配问题的发生,或者代码文件的编码使用本地编码 GB2312,和电脑使用的代码页一致。
  例如,VS Code文件编码默认是 UTF-8,而Windows系统在中文环境下默认是 GB2312,第一次配置C++开发环境时,很有可能会遇到中文乱码的情况,文件路径中的中文也同理。

3. 读取图像文件并绘制到窗口上

  现在运行个简单的测试示例,目标是读取一张图片绘制到窗口上,如果能够成功,那图像操作就完成了一半。
  将下面代码第12行中 getimage()函数参数中的 "图像文件路径" 改成自己图片文件的实际路径。

在这里插入图片描述

#include <graphics.h>

int main()
{
	initgraph(640, 480, 0);

	//创建图片对象
	PIMAGE pimg = newimage();
	
	//从文件中获取图像(和原图一样大小),输入实际的图像文件路径
	//例如, getimage(pimg, "E:/博客写作/博客专栏/EGE/鬼刀.png");
	getimage(pimg, "图像文件路径");
	
	//绘制图像,(0, 0)是图片绘制区域的左上角		
	putimage(0, 0, pimg);

	//如果后面不用了的话就销毁图像,释放内存(图像是动态分配的,注意不要内存泄漏)
	delimage(pimg);
	
	getch();
	closegraph();
	
	return 0;
}

4. 查看运行结果

  运行结果如下图所示,由于使用的图片比窗口大,所以只显示了图片的一部分。
  如果显示是全黑,无法正确显示图片,那么请先检查一下填写的图片文件的路径是否正确,注意要将\修改为/
  如果用的是其它图片,可以用windows自带的照片等工具先打开对应图片看看,查看一下是否是因为图片分辨率非常大并且左上角本身是黑色而导致分辨不出是显示的图片一角还是没有显示出图片。

在这里插入图片描述

三、文件路径

1. 文件扩展名显示设置

  对于文件读取来说,文件路径是很重要的一个参数,文件名包含文件主名文件扩展名两部分,平常情况下文件资源管理器可能默认隐藏文件扩展名,这不利于获取完整的文件名。

windows 10 资源管理器显文件扩展名的设置

  打开 文件资源管理器,点击 查看选项卡,勾选上文件扩展名选项,这样就能看到完整的文件名了。

在这里插入图片描述

2. 绝对路径和相对路径

文件名 绝对路径与相对路径

  在文件属性中的对象名称中写的路径是文件的绝对路径,以盘符和目录分隔符开头:盘符:/,可以唯一确定一个存储在计算机计算机中的文件。
在这里插入图片描述
  而相对路径是文件与当前目录的相对位置,一般情况下,项目中新建的源文件将放在项目的当前目录中,在集成开发环境中调试时,程序运行后的当前目录也是在这个位置。如果上图中的文件放在当前目录中,那么文件的相对路径为"鬼刀.png",如果是在当前目录中的 resource 文件夹下,那么路径为 "resource/鬼刀.png"

  代码中的文件路径尽量用 相对路径,当可执行文件改变存放位置时,绝对路径就会变化,这时文件就无法读取到。

四、EGE 中的图像

1. 计算机如何表示图像

  目前大多数显示器的显像部分是由一个个像素点组成,这些像素点呈行列排布,组成一个二维点阵。显示器上显示的图像便是这些发出不同色光的像素点构成。因此,一个图像只需保存其包含的像素点的颜色值即可,系统读取后就可以根据这些颜色值改变对应像素点的颜色,在屏幕上显示出对应的图像。

在这里插入图片描述

  图像所保存的像素区域通常是矩形形状,其中包含了 N N N M M M列个像素点。此时图像的宽度为 M M M个像素点,高度为 N N N 个像素点,图像的分辨率通常就用 M × N M \times N M×N 表示。

在这里插入图片描述

  查看一张图片的参数信息,可以看到分辨率参数。

在这里插入图片描述

  在计算机内存中,系统会开辟一块可以保存 M × N M \times N M×N 个像素颜色值的区域来存储图像像素点颜色数据。

  像素颜色通常用24位RGB颜色表示。ARGB颜色除了RGB通道外,另加一个Alpha通道,一共32位。

  通用的图像文件格式中,一般有 矢量图格式位图格式 两种。
  位图 格式记录了一个图像所有像素点的颜色值,并对这些数据和其它一些信息进行压缩编码。读取到文件后,只需对这些数据解码,就能还原出对应图像的像素点颜色值。
  矢量图格式则包含一些描述性的内容,描述图形如何通过一些基础图形绘制而成,而不是保存各个像素点的颜色值,实际的图像由程序按照其描述的步骤绘制而成。

2. EGE中的图像

  图像在EGE中用 IMAGE 类型表示,类中记录了图像各个像素的 ARGB颜色值 。从图像文件中读取的图像像素点颜色值不管原颜色格式是什么,读取后都会转换成 32 32 32ARGB 格式。

  目前EGE中的 IMAGE 类是隐藏的,无法直接调用其成员变量和成员函数,EGE额外提供了一些接口,可以通过其指针类型 PIMAGE 操作图像。

在这里插入图片描述

  EGE中的表示图像对象的类为 IMAGEIMAGE 类的定义并没有直接给出,所以无法直接创建和操作 IMAGE 对象,需要通过 newimage() 函数进行创建。newimage() 创建IMAGE 对象后返回 IMAGE对象的指针,IMAGE对象的指针类型用 PIMAGE 来表示。

typedef IMAGE* PIMAGE;
//创建IMAGE对象,并返回其指针
PIMAGE pimg = newimage();

  另有一个 PCIMAGE类型,定义为指向 IMAGE常量 的指针,可以防止 IMAGE 被误修改。

typedef const IMAGE *PCIMAGE;

3. 如何定义全局图像

  EGE使用的是图像指针类型 PIMAGE,指针可任意位置定义,但在 旧版本中(·EGE19.01版本及之前·) ,调用 newimage() 创建图像需要在 initgraph() 初始化环境之后,因此以下代码报错。(提示:全局变量的初始化是在main()函数调用之前进行)

#include <graphics.h>
//EGE20.08版本以上允许,旧版本会报错
PIMAGE pimg = newimage(); 

int main()
{
	...
	return 0;
}

  EGE在20.08版本之后允许 newimage()initgraph() 之前调用。

  既然是指针类型,那么只需要在初始化环境后再调用 newimage() 创建图像对象,然后对 PIMAGE变量 进行赋值即可。

#include <graphics.h>

//这里仅定义指针变量,而不使用newimage()创建图像对象
PIMAGE pimg = NULL;	

int main() 
{
	initgraph(640, 480, 0);
	//创建图像放到 initgraph()调用之后
	pimg = newimage();	
	
	...
	
	return 0;
}

五、EGE图像的使用流程

  EGE图像的使用流程如下图所示,主要包括 创建图像构建图像内容绘制图像销毁图像 四个部分。
  图像内容可以从文件中读取,也可以是从另一个图像中得到,同时可以调用EGE绘图函数来对图像进行绘图。

在这里插入图片描述

  单次绘图流程的代码如下所示:

在这里插入图片描述
  循环绘图流程的示例代码如下所示:

在这里插入图片描述

六、图像的创建和销毁

在这里插入图片描述

1. 创建图像 newimage()

  图像创建使用的是 newimage() 函数,newimage() 函数创建图像后,会返回其指针。

  newimage() 函数有两种形式:

函数形式参数含义
newimage()无参数创建一个 1 × 1 1\times 1 1×1 大小的图像或一个空的图像(版本更新后,行为不同)
newimage(width, height)width:图像宽度
height:图像高度
创建一个指定宽高的图像

函数声明
  创建了一个空的图像或1x1大小的图像

PIMAGE  newimage();

  创建一个宽高为 width x height 的图像

PIMAGE  newimage(int width, int height);

示例代码

  使用 newimage() 创建后,将返回值赋值给PIMAGE变量即可。

PIMAGE pimg1 = newimage();        //创建默认大小的图像(空图像或1x1大小)
PIMAGE pimg2 = newimage(100, 50); //创建一个宽为100,高为50的图像

2. 图像的销毁 delimage()

  图像对象使用的是 动态内存分配,对分配的图像对象不再需要时,要使用 delimage(pimg) 销毁图像,释放内存,否则会造成内存泄漏。

delimage(pimg);

七、获取图像

1. 获取图像的四种方式

  获取图像使用 getimage() 函数,getimage() 有多个重载,分别以不同的方式获取图像。

  使用 getimage() 获取图像后得到的 IMAGE 大小会随实际图像大小而改变,而不是原先创建图像时设置的大小。

  另外,有一个专门读取png图像文件的函数 getimage_pngfile(),因为getimage() 也能读取png图像,所以不常用。

int getimage_pngfile(PIMAGE pimg, LPCSTR  filename);
int getimage_pngfile(PIMAGE pimg, LPCWSTR filename);

1.1 从图形窗口获取图像(截屏)

  从图形窗口中获取图像,可以是整个窗口,也可以窗口的部分矩形区域。

在这里插入图片描述

函数声明

在这里插入图片描述

// 从屏幕获取图像
void getimage(
    PIMAGE pDstImg,   // 保存图像的 IMAGE 对象指针
    int srcX,         // 要获取图像的矩形区域左上角 x 坐标
    int srcY,         // 要获取图像的矩形区域左上角 y 坐标
    int srcWidth,     // 要获取图像的矩形区域宽度
    int srcHeight     // 要获取图像的矩形区域高度
);

示例程序
  创建图像后,就可以使用 getimage() 截取窗口的部分矩形区域来作为图像。

PIMAGE pimg = newimage();

//设置区域位置和大小信息,这里直接就是整个窗口区域
int x = 0, y = 0;
int width = getwidth(), height = getheight();

//截取窗口中左上角坐标为(x,y),宽为width, 高为height的矩形区域。
getimage(pimg, x, y, width, height);	

  这样就可以截取窗口某个区域,将图像存到pimg里了。
  截取整个窗口区域还可以这样简化代码:

PIMAGE pimg = newimage();					
getimage(pimg, 0, 0, getwidth(), getheight());

1.2 从图像文件中获取

注意事项
  从文件中读取图像是个耗时的过程,CPU从硬盘中读取文件非常慢,所以 不要在循环中重复读取同一个图像 , 这样会不断读取硬盘,非常耗时,还会引起卡顿。如果你忘记使用 delimage() 来销毁图像,还会造成内存泄漏,占用极大的内存。

  文件中的图像都是固定的,如果要一直使用,只获取一次就可以了,后面可以一直绘制,不需要再次获取。
  可以先获取图像,再在帧循环中重复绘制,帧循环退出后再使用 delimage() 销毁图像。

getimage()支持的图像文件格式备注
png / bmp / jpg / gif / emf / wmf / ico包含多帧图像的文件只能获取第一帧,得到的只是一张静态的图片,而不是一个动图。

函数声明
在这里插入图片描述

  第二个参数 pImgFile的类型LPCTSTR 其实是 char * 类型,需要填写图片文件的路径,这样才能找到文件存放的位置并读取。文件路径可以是绝对路径,也可以是相对路径。

// 从图片文件获取图像(png/bmp/jpg/gif/emf/wmf/ico)
void getimage(
    PIMAGE pDstImg,		// 保存图像的 IMAGE 对象指针
    LPCTSTR pImgFile	// 图片文件路径
);

示例代码

	PIMAGE pimg = newimage();
	getimage(pimg, "C:/ege/image.png");

  从图像文件中获取图像的 getimage(), 它的第三和第四个参数虽然写着是缩放宽高,但目前实际上是未使用的参数,没有缩放的效果。想要缩放,可以借助 putimage()

不要在循环中从同一个文件中重复读取图像

  如下所示:draw函数用于绘图,都是同一个图片,什么都没变,为什么读这么多次呢?

//draw会被循环调用
void draw()
{
	//都是同样的内容,读一次就好了呀
	PIMAGE pimg = newimage();
	getimage(pimg, "filename.jpg");
	putimage(0, 0, pimg);
	delimage(pimg);
}

int main()
{
	initgraph(640, 480, 0);
	
	...
	
	
	for (;is_run();delay_fps(60) {
		cleardevice();
		draw();
	}
	
	closegraph();
	return 0;
}

  读一次就可以了,真是够了啊。


PIMAGE pimg;

void draw()
{
	putimage(0, 0, pimg);
}

int main()
{
	initgraph(640, 480, 0);
	
	...
	
	pimg = newimage();
	getimage(pimg, "filename.jpg");
	
	for (;is_run();delay_fps(60) {
		cleardevice();
		draw();
	}
	
	delimage(pimg);
	
	closegraph();
	return 0;
}

1.3 从另一个 PIMAGE 中获取

  可以截取一个图像的部分区域作为图像内容。
在这里插入图片描述

  截取图像区域如下所示:
在这里插入图片描述

函数声明

在这里插入图片描述

// 从另一个 IMAGE 对象中获取图像
void getimage(
    PIMAGE pDstImg,       // 保存图像的 IMAGE 对象指针
    const IMAGE *pSrcImg, // 源图像 IMAGE 对象
    int srcX,             // 要获取图像的区域左上角 x 坐标
    int srcY,             // 要获取图像的区域左上角 y 坐标
    int srcWidth,         // 要获取图像的区域宽度
    int srcHeight         // 要获取图像的区域高度
);

示例代码

  源图像的图像指针为 anotherPimg, 从中截取部分区域保存到pimg中。

PIMAGE pimg = newimage();
getimage(pimg, anotherPimg, x, y, width, height);

1.4 从资源文件获取

  资源文件就是编译器将图片集成进可执行文件中,图片不再作为外部文件的形式存在,程序运行时可以从可执行文件中提取图片,增加了程序的独立性。

函数声明

// 从资源文件获取图像(bmp/jpg/gif/emf/wmf/ico)
void getimage(
    PIMAGE pDstImg,       // 保存图像的 IMAGE 对象指针
    LPCTSTR pResType,     // 资源类型
    LPCTSTR pResName      // 资源名称
);

调用如下:

getimage(pimg, "资源类型", "资源名称");

  关于资源文件的获取,在文章末尾说明,篇幅太长,就不放在这里了。

2. 如何检测是否成功读取到图像

  检测是否成功读取到图像,并不能单纯依靠查看绘制结果,原因如:

  • 绘图函数调用时参数填写错误。
  • 图像本身原因,如分辨率过大,部分区域是纯色的
  • 绘图流程出现问题,图形可能被覆盖、清除,或者没有对内容进行刷新等。
判断方法说明
利用 getimage() 函数返回值返回值为0说明读取成功,返回值为非0值说明读取失败。
利用getwidth(), getheight()查看图像宽高属性如果图像宽高出现0或1这种不正确的数值,说明读取图片文件出现了问题。

2.1 利用getimage返回值判断

  这种适合用于从文件中读取图像的操作,当从文件中读取图像失败时(因找不到文件,文件格式错误等原因而导致读取失败)getimage() 将返回 非0值

  下面读取一张图片,根据 getimage()返回值 判断读取操作是否成功,读取操作结果输出到控制台中

//创建图片对象
PIMAGE pimg = newimage();				
int errorCode = getimage(pimg, "图片文件路径");

if (errorCode == 0) {
	//图片读取成功
} else {
	//图片读取失败
}

2.2 利用getwidth, getheight判断

  newimage() 创建图像后,图像大小为 0 × 0 0 \times 0 0×0 1 × 1 1 \times 1 1×1,如果读取成功,图像大小将会被修改成读取的图片的宽高。此时读取图像宽高信息即可。

//创建图片对象
PIMAGE pimg = newimage();
getimage(pimg, "图片文件路径");
int width = getwidth(pimg), height = getheight(pimg);

//输出图像宽高数据
printf("[%d, %d]\n", width, height);

3. 图像数组,加载多张图像

  从文件中加载图像,其实只需要文件路径即可。我们可以将序列帧按一定格式顺序命名,然后生成对应的文件路径字符串,即可。
  假设下图中的图片文件保存在当前目录的 img 文件夹下。
在这里插入图片描述

  生成字符串可以用标准库中的 sprinf() 函数, 它可以根据参数生成对应的字符串并存入字符数组中。

  多张图像可以用 PIMAGE数组 保存,也可以使用 C++ 标准模板库中的 vector, array等,创建后使用 newimage() 给PIMAGE 赋值即可。

//定义长度为20的数组
PIMAGE images[20] = {NULL};

//数组也可以使用动态分配的方式,记得对images调用free释放内存
const int imageNum = 20;
PIMAGE* images = (PIMAGE*)malloc(imageNum * sizeof(PIMAGE));

  如上图中的,图片文件名格式为:image + 数字+.jpg 。图片保存在当前目录的img 文件夹下,文件路径对应格式字符串 为 "img/image%d.jpg",用 sprintf() 生成即可。

PIMAGE pimgs[20];
//字符数组,用于保存生成的文件名
char fileName[64];

for (int i = 0; i < 20; i++) {
	//根据格式生成文件名,保存到fileName[]中
	sprintf(fileName, "img/image%d.jpg", i + 1);
	//创建图像,并根据文件名加载
	pimgs[i] = newimage();
	getimage(pimgs[i], fileName);
}

  当图像不使用时,用 delimage() 来销毁图像。

for (int i = 0; i < 20; i++) {
	delimage(pimgs[i]);
	pimgs[i] = NULL;
}

4. 图像缩放

  getimage() 实际是没有缩放功能的,那该如何处理图像缩放的问题。

4.1 图片缩放的考虑

  如果图像在绘图时是大小固定不变的,如做窗口背景:
  ① 首先考虑利用软件工具对图片文件进行缩放,调整至合适的分辨率后保存,这时读取到程序中,直接就是合适的大小(可以使用PS等图片工具)
  ② 然后考虑先把图像读取到程序中,再利用EGE的函数改变图像大小 (使用putimage()生成另一个合适大小的图像)
  ③ 不改变原始图像的大小,在绘制图像时,控制绘制出的图像大小( putimage() 函数可以缩放绘制)
在这里插入图片描述
  如果图像在绘图时大小是会不断变化的:这种就不需要修改图片文件分辨率,选用一个较为高清的图片,在绘制图像时使用 putimage() 缩放绘制即可。

4.2 使用EGE接口改变图像大小

  getimage() 读取图片文件后,得到图像是原图大小,并且getimage()不能本身是不能设置图像大小的。如果想要缩放原图像,可以使用一个临时图像做中转,读取图片后再缩放绘制到目标图像中,完成后销毁临时图像,这样缩放后的图像就保存在了目标图像中。
在这里插入图片描述

示例代码
  假设需要的图像大小是 320 × 320 320 \times 320 320×320

PIMAGE temp = newimage();			//创建临时图像
getimage(temp, "girl.jpg");			//读取图片

//创建指定大小的图像
PIMAGE pimg = newimage(320, 320);

//把图像绘制成需要的大小
putimage(pimg, 0, 0, 320, 320, temp, 0, 0, getwidth(temp), getheight(temp));

delimage(temp);		//临时的temp图像不需要了,销毁掉

  最后我们就得到了我们想要的宽和高为 320x320 的图像,保存在了 pimg 中,接下来就可以使用了。

//将图片绘制在窗口上
putimage(0, 0, pimg);

4.3 从文件中加载图像并缩放至指定大小

  我们可以将图片缩放写成一个函数,将图片缩放成想要的大小。

//从图片文件中加载图像,并将图像缩放成 width x height 大小
void getZoomImage(PIMAGE pimg, const char* fileName, int width, int height)
{
	if (pimg == NULL)
		return;
		
	PIMAGE temp = newimage();
	getimage(temp, fileName);
	
	if ((getwidth(pimg) != width) || (getheight(pimg) != height))
		resize(pimg, width, height);
		
	putimage(pimg, 0, 0, width, height, temp, 0, 0, getwidth(temp), getheight(temp));

	delimage(temp);
}

使用如下:

PIMAGE pimg = newimage();
getZoomImage(pimg, "girl.jpg", 320, 320);

4.4 缩放原图像

  下面这个函数是对原图像进行缩放,缩放后图像指针指向新的图像。

void zoomImage(PIMAGE& pimg, int zoomWidth, int zoomHeight)
{
	//pimg为空,或目标图像大小和原图像一样,则不用缩放,直接返回
	if ((pimg == NULL) || (zoomWidth == getwidth(pimg) && zoomHeight == getheight(pimg)))
		return;

	PIMAGE zoomImage = newimage(zoomWidth, zoomHeight);
	putimage(zoomImage, 0, 0, zoomWidth, zoomHeight, pimg, 0, 0, getwidth(pimg), getheight(pimg));
	delimage(pimg);

	pimg = zoomImage;
}

5. 内存泄漏

  先看看内存泄漏的情况(不调用 delimage() 销毁图像的)
  先调出 任务管理器鼠标右击左下角的桌面图标,调出任务管理器或者快捷键(Ctrl + Alt + Del), 然后点击 “任务管理器”

在这里插入图片描述
  打开后注意现在的内存使用情况。
在这里插入图片描述
  当然,如果你有安全管家悬浮球,也可以看这个
在这里插入图片描述
  接下来是查看内存泄漏情况。下面仅仅是加载一张图片,在循环里进行,没有 delimage(),。
  运行下面的程序:

#include <graphics.h>

int main()
{
	initgraph(512, 512, 0);
	setbkcolor(WHITE);
	setcolor(BLACK);
	setfont(22, 0, "楷体");
	xyprintf(40, 40, "按任意键继续,每按一次,注意一次内存情况");

	for (int i = 0; i < 10; i++) {
		
		//从屏幕截取图像,不释放内存
		for (int j = 0; j < 200; j++) {
			PIMAGE pimg = newimage();
			getimage(pimg, 0, 0, 512, 512);
		}
		xyprintf(40, 80, "第%2d 次,共10次, 现在内存泄漏约%4dMB", i + 1, 200 * (i+1));
		getch();
	}
	
	closegraph();

	return 0;
}

  可以看到,按一次按键内存升高一点, 最后占用了约 2GB 的内存。
  运行结束后,内存才统一释放,恢复正常。

在这里插入图片描述
  所以如果你在帧循环中不断地加载图像,而不使用 delimage(), 将会看到内存占用不断升高。
  大概是下面这种写法
  运行一下,看看不断升高的内存占用。

#include <graphics.h>

int main()
{
	initgraph(512, 512, 0);
	setbkcolor(WHITE);
	setcolor(BLACK);
	setfont(22, 0, "楷体");

	for (; is_run(); delay_fps(60)) {
		PIMAGE pimg = newimage();
		getimage(pimg, 0, 0, 512, 512);
	}
		
	closegraph();

	return 0;
}

  如果你仅仅是加载那一张图片,内存占用仅仅10MB
在这里插入图片描述
  下面是正确的写法,把加载图像放到帧循环外,同一个图像不必重复加载。

#include <graphics.h>

int main()
{
	initgraph(512, 512, 0);
	setbkcolor(WHITE);
	setcolor(BLACK);
	setfont(22, 0, "楷体");

	PIMAGE pimg = newimage();
	getimage(pimg, 0, 0, 512, 512);

	for (; is_run(); delay_fps(60)) {

		//绘图
	}
	//不用时释放内存
	delimage(pimg);
	
	closegraph();

	return 0;
}

八、 图像绘制

EGE 图像处理函数文档

  图像的绘制分别有以下几个函数:

功能额外函数
普通绘制putimage()
普通绘制支持带透明度图像putimage_withalpha()
功能额外函数
透明绘制指定图像整体透明度putimage_alphablend()
透明绘制指定某个颜色的像素透明putimage_transparent()
透明绘制指定某个颜色的像素透明,并指定图像整体透明度putimage_alphatransparent()
功能额外函数
旋转绘制putimage_rotate()
旋转绘制可缩放putimage_rotatezoom()

  函数声明具体请查看EGE官网文档及 ege.h头文件

1. 图像普通绘制

1.1 putimage()

图像处理相关函数:putimage

   putimage() 函数可以将图像绘制到另一个图像上。同时,窗口中绘图区域也是一个图像,因此 putimage() 也可以绘制在窗口上。
  由于在窗口上绘制是个常用操作,所以EGE额外增加了一组用于绘制在窗口上的函数,可以从函数参数中看出来,这一组函数省略了目标图像参数,意为绘制到窗口上。
  这里就讲解直接绘制在窗口上的那一组函数,另一组指定目标图像的函数,添加一个 PIMAGE 参数即可

  putimage() 有三种形式,分别是全图绘制部分绘制部分缩放绘制

在这里插入图片描述

1.1.1 全图绘制

  将原图以相同大小绘制到窗口的某个位置。
在这里插入图片描述

函数声明

void EGEAPI putimage(
	int dstX,				//绘制区域左上角x坐标
	int dstY,				//绘制区域左上角y坐标
	const PIMAGE pSrcImg	//源图像
);

示例代码

putimage(x, y, pimg);

1.1.2 部分绘制

  截图源图像的部分区域,以相同大小绘制到窗口上。截取区域和绘制区域大小相同,所以省略了截取区域的宽高参数。

在这里插入图片描述

函数声明

void EGEAPI putimage(
	int dstX,		//绘制区域左上角x坐标
	int dstY,		//绘制区域左上角y坐标
	int dstWidth,	//绘制区域宽度
	int dstHeight,	//绘制区域高度
	const PIMAGE pSrcImg,	//源图像
	int srcX,		//截取区域左上角x坐标
	int srcY,		//截取区域左上角y坐标
);

示例代码

putimage(x, y, width, height, pimg, srcX, srcY);

1.1.3 部分缩放绘制

  截取图像部分区域,缩放绘制到窗口的目标区域上。因为图像会被缩放拉伸,如果两个区域宽高比例不一样,那么图像会变形,所以最好两个区域的宽高比例相等。
在这里插入图片描述

函数声明

void EGEAPI putimage(
	int dstX,				//绘制区域左上角x坐标
	int dstY,				//绘制区域左上角y坐标
	int dstWidth,			//绘制区域宽度
	int dstHeight,			//绘制区域高度
	const PIMAGE pSrcImg,	//源图像
	int srcX,				//截取区域左上角x坐标
	int srcY,				//截取区域左上角y坐标
	int srcWidth,			//截取区域宽度
	int srcHeight			//截取区域高度
);

1.2 putimage_withalpha() 支持带透明度图像

  putimage_withalpha() 支持带透明通道的图片,这样就可以使用带透明通道的图片素材。

  putimage_withalpha() 在EGE20.08及之前版本只支持部分绘制,不支持缩放绘制。

  带透明度的图像可以将像素颜色和背景混合,图像的一部分有半透明或全透明的效果。JPG格式图片通常不带有透明,带透明度图像可以使用PNG格式。

在这里插入图片描述

  如果想绘制到窗口上,目标图像的PIMAGE参数传入NULL即可。
在这里插入图片描述

函数声明

int putimage_withalpha(
	PIMAGE imgdest,		// 目标图像
	PIMAGE imgsrc,		// 源图像
    int dstX,    		// 目标图像左上角x坐标 
    int dstY,    		// 目标图像左上角y坐标
	int srcX = 0,		// 源图像左上角x坐标
    int srcY = 0,		// 源图像左上角y坐标
    int srcWidth = 0,   // 原图像混合区域宽度
    int srcHeight = 0   // 源图像混合区域高度
);

示例代码

//图像全图绘制到窗口上,绘制区域左上角坐标为(20, 30)
putimage_withalpha(NULL, pimg, 20, 30);

//图像全图绘制到目标图像上,绘制区域左上角坐标为(20, 30)
putimage_withalpha(targetPimg, pimg, 20, 30);

//截取图像的左上角部分绘制到窗口上,绘制区域左上角坐标为(20, 30)
putimage_withalpha(NULL, pimg, 20, 30, 0, 0, getwidth(pimg) / 2, getheight(pimg) / 2);

2. 图像透明绘制

2.1 putimage_alphablend() 图像半透明混合

  如下图所示,前两张是两张完全不透明的图片,经过半透明混合后,得到第三张图。可以看到,图片似乎变成了透明的,能看到后面的部分。
在这里插入图片描述

函数声明
  混合两张图像,指定源图像的透明度。
  透明度参数alpha为0时,源图像全透明;alpha为255时,源图像完全不透明。
在这里插入图片描述

int putimage_alphablend(
    PIMAGE imgdest,		// 目标图像
    PIMAGE imgsrc,		// 源图像
    int dstX,			// 目标图像左上角x坐标 
    int dstY,			// 目标图像左上角y坐标
    unsigned char alpha,// alpha
    int srcX = 0,		// 源图像左上角x坐标
    int srcY = 0,		// 源图像左上角y坐标
    int srcWidth = 0,	// 原图像混合区域宽度
    int srcHeight = 0	// 源图像混合区域高度
);

2.3 putimage_transparent() 指定图像透明像素

  用于指定源图像中某种颜色的像素变为全透明。
  下图中左边是原图,中间为在绘制时 指定源图像中的红色像素为透明色,绘制时红色就消失,看到的是背景色;右图为在绘制时 指定源图像中的绿色为透明色 , 绘制时绿色就变为透明,看到的是背景色

在这里插入图片描述
函数声明
  目标图像参数传入NULL即绘制在窗口上。

int putimage_transparent(
    PIMAGE imgdest,			// 目标图像
    PIMAGE imgsrc,			// 源图像
    int dstX,				// 目标图像左上角x坐标 
    int dstY,				// 目标图像左上角y坐标
    color_t crTransparent,  // 源图像中要变成透明的像素颜色
    int srcX = 0,			// 源图像左上角x坐标
    int srcY = 0,			// 源图像左上角y坐标
	int srcWidth = 0,		// 原图像混合区域宽度
    int srcHeight = 0		// 源图像混合区域高度
);

  这个和 putimage_alphablend() 函数 只是有一个参数不同,透明度alpha 变成了一个颜色参数。

示例代码

PIMAGE pimg = newimage();
...
int x = 20, y = 30;
color_t transColor = EGERGB(0, 0, 0);
putimage_transparent(NULL, pimg, x, y, transColor);

2.4 putimage_alphatransparent()

  这个就是上面两个的合体,把源图像的一些颜色为 crTransparent的像素点变透明,其它的地方进行透明度混合。
函数声明

int putimage_alphatransparent(
    PIMAGE imgdest,
    PIMAGE imgsrc,
    int dstX
    int dstY,
    color_t crTransparent,
    unsigned char alpha,
    int srcX = 0,
    int srcY = 0, 
    int srcWidth = 0,
    int srcHeight = 0
);

3. 旋转绘图

3.1 putimage_rotate()

  将旋转一定角度后源图像绘制在目标图像上。

在这里插入图片描述
  这里需要注意的是旋转中心是以源图像的纹理坐标系来描述的。

在这里插入图片描述

函数声明

int EGEAPI putimage_rotate(
	PIMAGE imgdest,			//目标图像
	PIMAGE imgsrc,			//源图像
	int nXOriginDest,		//目标绘制位置x坐标
	int nYOriginDest,		//目标绘制位置y坐标
	float centerx,			//旋转中心的纹理x坐标
	float centery,			//旋转中心的纹理y坐标
	float radian,			//旋转的弧度,弧度 = 角度 * π / 180  
	int btransparent = 0,	//是否使用图像的透明通道
	int alpha = -1,			//图像整体透明度,范围为0~255, alpha=-1 意味着忽略透明度
	int smooth = 0			//是否进行平滑处理
);

下面分别是旋转中心纹理坐标为 ( − 0.5 , − 0.5 ) (-0.5, -0.5) (0.5,0.5), ( 0.0 , 0.0 ) (0.0, 0.0) (0.0,0.0) ( 0.5 , 0.5 ) (0.5, 0.5) (0.5,0.5) 的情况

绕图像外部点旋转绕图像左上角旋转绕图像中心旋转
旋转中心纹理坐标 ( − 0.5 , − 0.5 ) (-0.5, -0.5) (0.5,0.5) ( 0.0 , 0.0 ) (0.0, 0.0) (0.0,0.0) ( 0.5 , 0.5 ) (0.5, 0.5) (0.5,0.5)
效果图在这里插入图片描述在这里插入图片描述在这里插入图片描述

3.2 putimage_rotatezoom()

  旋转的同时还允许缩放。 参数类似 putimage_rotate(),只多了个zoom参数, 表示缩放比例,1.0 表示不缩放。

int EGEAPI putimage_rotatezoom(
	PIMAGE imgdest,
	PIMAGE imgtexture,
	int nXOriginDest,
	int nYOriginDest,
	float centerx,
	float centery,
	float radian,
	float zoom,
	int btransparent = 0,           // transparent (1) or not (0)
	int alpha = -1,                  // in range[0, 256], alpha== -1 means no alpha
	int smooth = 0
);

4. 模糊滤镜

  对一图片区域进行模糊滤镜操作。

函数声明
  对目标图像的某个区域进行模糊。

int imagefilter_blurring (
    PIMAGE imgdest,				//目标图像
    int intensity,				//模糊度
    int alpha,					//亮度
    int nXOriginDest = 0,		//区域左上角坐标x
    int nYOriginDest = 0,		//区域左上角坐标y
    int nWidthDest = 0,			//区域宽度
    int nHeightDest = 0			//区域高度
);

参数说明:

  • intensity, 模糊度,值越大越模糊。范围为0 ~ 0xFF(0~255),值为0时和原图一样。 当值在 0x0 - 0x7F之间时,为四向模糊;当值在 0x80 - 0xFF之间时,为八向模糊,运算量会大一倍。
  • alpha
    图像亮度,越大越亮,。取值为0x100(即256)表示亮度不变,取值为0x0表示图像变成纯黑。

下面是效果,右边为模糊后的图。

在这里插入图片描述

六、图像其它操作

1. 图像属性

1.1 重设图像尺寸

  重新分配内存,改变图像大小(原图像内容丢失)

void resize(PIMAGE pimg, int width, int height);

  注意,我们不能用这个来对图像直接进行缩放,因为使用后图像内容会丢失,主要用于对图形的内存重新进行动态分配。

示例代码

resize(pimg, 400, 300);

1.2 获取图像宽度和高度

  假设有一张图片

PIMAGE pimg = newimage(160, 90);

  那么可以获取图片的宽高(单位像素)

int width = getwidth(pimg)
int height = getheight(pimg)

  其中,图像指针pimg默认值为NULL, 表示获取的是窗口。

   getmaxx(), 同 getwidth( ), getmaxy(), 同 getheight( )


2. 获取和设置图像像素

2.1 图像像素点: getpixel() 和putpixel()

  获取图像中某个像素点的颜色可使用 getpixel() 函数。
  设置图像中某个像素点的颜色可使用 putpixel() 函数。

函数声明:

color_t getpixel(int x, int y, PIMAGE pimg = NULL);
void    putpixel(int x, int y, color_t color, PIMAGE pimg = NULL);

  注意,这个获取的是 ARGB 颜色。==

getpixel()使用示例:

color_t ARGBcolor = getpixel(x, y, pimg);

putpixel()使用示例:

color_t ARGBcolor = WHITE;
putpixel(x, y, pimg);

2.2 那如何获取窗口像素点颜色呢

  获取窗口像素点颜色 同样是使用 getpixel() ,可以看到 getpixel() 第三个参数是PIMAGE类型,默认参数为NULL,参数为NULL 时指的就是 窗口
  同样地,设置窗口像素点颜色 也用putpixel()PIMAGE参数传入NULL或使用默认参数即可。

getpixel() 第三个参数省略或者传入NULL时,即为获取窗口像素点颜色。

获取窗口像素点的ARGB颜色:

color_t ARGBcolor = getpixel(x, y);

获取窗口像素点的RGB颜色,即将透明度设置为0xFF。

color_t RGBcolor = EGEACOLOR(0xFF, getpixel(x, y));

2.2 更快的像素操作:getpixel_f() 和 putpixel_f()

函数声明:

color_t getpixel_f(int x, int y, PCIMAGE pimg = NULL);
void    putpixel_f(int x, int y, color_t color, PIMAGE pimg = NULL);	//设置图像像素

   getpixel_f() 函数和 getpixel() 函数相比 少了边界判断操作, 也就是说 不判断像素点坐标是否在图像范围内。这样带来的是更快的速度,但牺牲了安全性,可能发生访问越界。可以在确保像素点坐标(x, y)在有效范围内时使用。

  比如遍历图像所有像素点的情况下,就可以使用。遍历时已经确保了坐标的有效性,此时内部无需做边界判断。

示例:

void forEachPixel(PIMAGE pimg)
{
	int width  = getwidth(pimg);
	int height = getheight(pimg);
	
	//遍历图像像素点
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			//获取图像像素点颜色
			color_t color = getpixel_f(x, y, pimg);
		}
	}
}

  实际上更快的是利用getbuffer()获取图像的内部缓冲区首地址,直接获取数组值,这样可节省函数调用的开销。

2.3 获取图像缓冲区:getbuffer()

  调用 **getbuffer()**可以得到图像内部缓冲区的首地址,我们可以通过直接读写缓冲区数据来达到高效进行图像处理的目的。

函数声明

color_t*	getbuffer(PIMAGE pimg);
color_t*	getbuffer(PCIMAGE pimg);

  如果参数 pimg 为NULL,那么获取的是EGE窗口缓存的首地址,传入其它图像则获取该图像的帧缓冲首地址。

  getbuffer()有两个重载,当参数是NULL时,将不确定调用的是哪个函数,所以需要强制转换一下:

getbuffer((PIMAGE)NULL);
getbuffer((PCIMAGE)NULL);

  getbuffer()返回的是缓冲区的首地址,是一维指针, 缓冲区大小可以通过 getwidth(), getheight() 获取得到的原图像大小来确定,缓冲区总像素个数为 w i d t h ⋅ h e i g h t \mathrm{width \cdot height} widthheight

int width  = getwidth(pimg);
int height = getheight(pimg);

  以下为遍历缓冲区所有像素:

color_t* buffer = getbuffer(pimg);
int len= getwidth(pimg) * getheight(pimg);

for (int i = 0; i < len; i++)
	buffer[i] = color;

  注意,因为是一维数组,所以对于图像上 坐标为 ( x , y ) (x, y) (x,y) 的像素,表示是 buffer[y * width + x] , 而不是 buffer[y][x]

  如下图所示:
在这里插入图片描述

  所以图像从上到下,从左到右遍历每个像素应该为:

color_t* buffer = getbuffer((PIMAGE)pimg);
int width = getwidth(pimg), height = getheight(pimg);

for (int y = 0; y < height; i++) {
	for (int x = 0; x < width; j++) {
		//这里面进行图像处理,将坐标为(x, y)的元素赋值一个颜色
		buffer[x + y * width] = color;
	}
}

  下面做个利用 getbuffer() 返回的帧缓冲首地址 将 RGB彩色图像转灰度图 的示例。

//#define SHOW_CONSOLE

#include <graphics.h>

//从图片文件fileName 中加载图像,图像缩放到成 width * height
void getZoomImage(PIMAGE pimg, const char* fileName, int width, int height)
{
	PIMAGE temp = newimage();
	getimage(temp, fileName);

	if (getwidth(pimg) != width || getheight(pimg) != height)
		resize(pimg, width, height);

	putimage(pimg, 0, 0, width, height, temp, 0, 0, getwidth(temp), getheight(temp));

	delimage(temp);
}

int main()
{
	//设置为普通窗口, 设置窗口位置, 手动渲染模式
	setinitmode(INIT_RENDERMANUAL, 100, 100);
	initgraph(640, 320);						//初始化窗口
	setcaption("EGE彩色图转灰度图");				//设置窗口标题

	//设置窗口背景为白色
	setbkcolor(WHITE);

	//获取图片
	PIMAGE pimg = newimage();
	getZoomImage(pimg, "girl.jpg", 320, 320);

	//绘制原图,作参照
	putimage(0, 0, pimg);

	//获取图像缓存区首地址
	color_t* buffer = getbuffer(pimg);
	//计算图像大小
	int width = getwidth(pimg), height = getheight(pimg);

	//彩色图转灰度图
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			color_t color = buffer[x + y*width];

			//获取颜色原来透明度,因为源图像可能是带有透明度的,这样可以保留下来
			unsigned int  alpha = EGEGET_A(color);
			//将原来RGB颜色转成灰色
			color_t gray = RGBtoGRAY(color);
			//将alpha值和计算出的灰度值合成对应的灰色			
			buffer[x + y * width] = EGEAGRAY(alpha, gray);
		}
	}

	//把修改后的图片绘制出来
	putimage(320, 0, pimg);

	//如果原图带有透明度,可以用下面这个绘制图像
	//putimage_withalpha(NULL, 0, 0, imgdest);

	getch();

	//销毁图像
	delimage(pimg);

	//绘图结束,关闭窗口
	closegraph();

	return 0;
}

在这里插入图片描述

3. 图像保存

  EGE可以将图像保存成 BMP 格式文件或 PNG 格式文件。

保存的图片格式函数
BMPsaveimage()
PNGsavepng()

3.1 保存成BMP格式图片

  saveimage() 可将PIMAGE中的图像保存成BMP格式的图像文件中。

函数声明:

int  saveimage(PIMAGE pimg, LPCSTR  filename);
int  saveimage(PIMAGE pimg, LPCWSTR filename);

  filename是文件名,如 "image.bmp"。如果文件名不带路径,则默认保存在当前目录中,会覆盖掉同名文件。

  文件扩展名应是 bmp, 因为保存的图片是BMP格式。虽然你写成jpg, png都可以用图像软件打开,但这只是因为大多图像软件都有自动识别文件格式的功能。应该按照图片实际格式来命名。

使用示例:

//创建图像并获取
PIMAGE pimg = newimage();
getimage(...)

...

//保存图像
saveimage(pimg, "img.bmp"); 

3.2 保存成png格式图片

  savepng() 可将PIMAGE中的图像保存到PNG图像文件中。
  filename是文件名,如 "image.bmp"。如果文件名不带路径,则默认保存在当前目录中,会覆盖掉同名文件。

函数声明:

int  savepng(PIMAGE pimg, LPCSTR  filename, int bAlpha = 0);
int  savepng(PIMAGE pimg, LPCWSTR filename, int bAlpha = 0);

参数说明:

  • pimg:PIMAGE 图像
  • filename: 保存的文件名。
  • bAlpha ,是否保存Alpha透明通道, true是带alpha通道保存,false是不带alpha通道保存,默认为不保存。

使用示例:(pimg为PIMAGE图像, 保存有图像数据)

savepng(pimg, "img.png", true); 

七、资源文件

  资源文件在编译时会被压缩打包放入执行文件中,exe文件运行时无需从外部读取其它文件。这样制作成程序时,就不用担心因放在外面的图片音乐等文件丢失而导致运行出错的情况,因为文件已经嵌入exe执行文件中,不再依赖外部文件。

  因为资源文件会被编译嵌入exe执行文件中,不再是文件的形式,所以就不能像普通文件那样通过路径来读取

  如下图所示,VS项目中有个资源文件 过滤器,这里就保存着资源文件和其它一些相关文件。
在这里插入图片描述

1. 不同编译器读取资源文件方法

  不同的编译器有其管理资源文件的方法,文件需要按其要求的方式进行配置,才会打包到执行文件中。

  下面是各编译器以读取资源文件的方式获取图片。

2. VC系列(VS2017)

2.1 添加图片资源文件

2.1.1 步骤
  • 右键点击资源文件文件夹, 选择 添加->资源
    在这里插入图片描述
  • 选择Bitmap, 点击 “导入”
    在这里插入图片描述
  • 这时进入到文件选择界面,设置文件类型为所有文件
    在这里插入图片描述
  • 然后就能选择自己想要添加的图片了,现在以 jpg图片为例。
    选中后会要求填写资源类型,JPG图片就写入JPG, 然后点击确定
    在这里插入图片描述
  • 然后就能看到项目中多出了resource.h头文件项目名.rc资源文件, 还有我们添加的JPG图片(并且会自动打开我们添加的图片,不用编辑,关闭就好)
    在这里插入图片描述
  • 点击资源视图,可以看到我们添加的资源文件。看到有个 “JPG” 文件夹, 这就是我们之前 填写的资源类型
    可以看到下面有个IDR_JPG1, 这就是我们添加的资源文件的名字
    在这里插入图片描述
  • 可以点击资源文件名,然后点击 属性,修改成容易记的资源名
    在这里插入图片描述
  • 在ID这一栏修改,比如,我修改成JPG_BG
    在这里插入图片描述
2.1.2 resource.h 头文件内容

打开resource.h 头文件,就会看到如下内容
就是一些宏定义, 代表了我们添加的资源的编号, 我们就是根据这个编号读取资源的。
下面有两个是相同的编号,是因为第二个是我刚才修改的名字,第一个现在已经没用了。
在这里插入图片描述

2.1.3 项目名.rc 文件内容

.rc文件 右键,选择 查看代码F7
在这里插入图片描述

  • 如果弹出这种框,选
    在这里插入图片描述
  • 往下拉看到下面这部分
    绿色的就是 资源名称, 蓝色的是 资源类型, 紫色的是 带相对路径的文件名
    在这里插入图片描述

2.2 资源文件的读取

  现在资源文件已经添加完成。那么在程序中如何读取资源文件呢?
  先在文件中 包含 resource.h 头文件(注意了,那个头文件不一定是叫resource.h,因为生成时可能会因重名而重命名)

#include "resource.h"

然后就是查看资源文件的宏定义, 我之前命名是叫 JPG_BG
在这里插入图片描述
然后看看 getimage() 的参数要求,资源类型资源名称都是字符串参数

int getimage(
	PIMAGE pimg,			// 保存图像的 IMAGE 对象指针
    const char* pResType,	// 资源类型
    const char* pResName,	// 资源名称
);

int getimage(
	PIMAGE pimg,			// 保存图像的 IMAGE 对象指针
	const WCHAR* pResType,	// 资源类型
	const WCHAR* pResName,	// 资源名称
); 

  资源类型前面说了,就是.rc 文件中写的 JPG, 所以填 “JPG”
  而资源名称需要字符串,我们只是有一个数字编号, 宏定义是JPG_BG,需要通过宏定义MAKEINTRESOURCE() 来获取相应的字符串。
  因为上面有两个重载,有一个是用于char, 一个是用于宽字符WCHAR,当用于宽字符时,字符串需要加L

即,按照上面

"JPG"MAKEINTRESOURCEA(JPG_BG) 配对
L"JPG"MAKEINTRESOURCEW(JPG_BG) 配对

或者使用TEXT宏来帮助确定

TEXT("JPG")MAKEINTRESOURCE(JPG_BG) 配对

所以如下便可获取:

PIMAGE pimg = newimage();

getimage(pimg, TEXT("JPG"), MAKEINTRESOURCE(JPG_BG)); 
或者
getimage(pimg, "JPG", MAKEINTRESOURCEA(JPG_BG)); 
getimage(pimg, L"JPG", MAKEINTRESOURCEW(JPG_BG)); 

下面是例程:

#include <graphics.h>

#include "resource.h"

int main()
{
	initgraph(640, 480,0);
	setbkcolor(WHITE);

	//从资源文件获取
	PIMAGE pimg = newimage();
	getimage(pimg, "JPG", MAKEINTRESOURCEA(JPG_BG));

	//在窗口上绘制
	putimage(0, 0, pimg);

	getch();
	closegraph();

	return 0;
}

运行结果:
在这里插入图片描述

3. CodeBlocks

3.1 创建rc文件

在项目,点击工具栏文件(File) ->新建(new)->空文件(empty file)
选择(添加到项目中)
在这里插入图片描述
在这里插入图片描述

  • 文件命名,扩展名需要是 rc, 这里命名为 resource.rc
    在这里插入图片描述
    在这里插入图片描述

3.2 编写rc文件

3.2.1 先把图片文件放到项目中

可以看到我图片的放在当前目录下的,文件名就是 “花火bg.jpg”,也可以自己新建个文件夹,把图片放进去,这样方便管理,不要随便扔桌面上
在这里插入图片描述

3.2.2 编写rc文件
  • rc文件中的资源格式:
资源名称		资源类型		"带路径文件名"

资源类型和资源名称自己命名(资源类型最好按照文件格式命名),这里命名

  • 资源类型: JPG
  • 资源名称: IMAGE_JPG_BG

  上面用到带路径文件名,如果不知道是什么,请去看 (二十一)文件名 绝对路径与相对路径,推荐用相对路径
  编写如下:

IMAGE_JPG_BG            JPG     "花火bg.jpg"

在这里插入图片描述
然后程序中就可以用getimage()获取了

getimage(pimg, "资源类型", "资源名称");

按如上则填写为

getimage(pimg, "JPG", "IMAGE_JPG_BG");

源文件中完整程序如下:

#include <graphics.h>

int main()
{
	initgraph(600, 400, 0);
	setbkcolor(WHITE);

	//从资源文件读取图像
    PIMAGE pimg = newimage();
    getimage(pimg, "JPG", "IMAGE_JPG_BG");

	//绘制
    putimage(0, 0, pimg);

	getch();

	//释放图像
	delimage(pimg);

	closegraph();

	return 0;
}

然后点击构建(Build),查看有没有错误, 没有错误就可以点击运行了,就能看到显示的图片了。

  • 如果编译出现错误,出现以下看不懂的汉字,说明 乱码 了, 因为图片名有汉字,汉字在不同的编码格式下编码是不同的,要么图片文件名不用汉字用英文,要么设置编码格式
    在这里插入图片描述
    CodeBlocks 设置编码
    点击工具栏 设置(settings)->编辑器(editor…)
    在这里插入图片描述
  • 点击 左边 通用设置(Generak), 点击 编码设置(Encoding settings)
    编码中选择 windows-936 , 即 GBK 编码, 在中文系统下,使用的还有GB2312编码。
    点击 确定(OK)
    在这里插入图片描述
    然后rc资源文件中的文件名删去,重新输入一遍文件名,点击 重新构建(Rebuild), 就可以看到编译没有问题了。运行即可
    如果你的资源类型是ICON , 文件是.ico文件,那么编译后自动使用这个ico文件作为程序的图标

4. DevC++

4.1 创建rc文件

  • 新建文件, 添加到项目中
    在这里插入图片描述
    在这里插入图片描述
  • 保存文件,扩展名为 rc, 命名随意,这里命名为 resource.rc
    在这里插入图片描述
    在这里插入图片描述

4.2 编写RC文件

  接下来是编写RC文件(即资源文件),内容和 CodeBlocks是一样的,包括测试代码也相同,因为都是用的GCC编译器,所以可以翻到上面CodeBlocks部分查看RC文件的编写


EGE专栏:EGE专栏

评论 44
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

依稀_yixy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值