(九)EGE图像操作

目录

  • 图像操作
    • 先把文件扩展名显示设置打开
    • 图像对象
      • 定义PIMAGE全局变量
    • 图片绘制流程演示
      • 创建图像
      • 获取图像
        • 从图形窗口获取图像(截屏)
        • 从图像文件中获取
        • 从另一个 PIMAGE 中获取
        • 从资源文件获取
        • 如何检测是否获取了图像
        • 图像数组,加载多张图片
        • 图片缩放
          • 图片缩放流程
            • 图片缩放函数
          • 从图片文件获取缩放后的图像函数编写
      • 内存泄漏
        • 来看看内存泄漏的情况(不使用 delimage() 的)
      • 图像绘制
        • putimage()函数
        • 透明绘图
          • putimage_withalpha() 绘制带透明通道的图片
          • putimage_alphablend() 两张图片半透明混合
          • putimage_transparent()
          • putimage_alphatransparent()
        • 旋转绘图
          • putimage_rotate()
          • putimage_rotatezoom()
        • 模糊滤镜
      • 图像属性
      • 图像保存
        • 保存成bmp图像文件
        • 保存成png图像文件
      • 获取图像缓存区
    • 资源文件
      • getimage()无法获取 png格式 资源文件
      • VC系列(VS2017)
        • 添加图片资源文件
          • resource.h 头文件
          • 项目名.rc
      • CodeBlocks
        • 创建rc文件
        • 编写rc文件
          • 先把图片文件放到项目中
          • 编写rc文件
      • DevC++
        • 创建rc文件
        • 编写rc文件

图像操作

EGE图像处理相关函数文档
http://xege.org/manual/api/img/index.htm

先把文件扩展名显示设置打开

打开 文件资源管理器, 勾选上文件扩展名。这样就能看到完整的文件名。
(九)EGE图像操作_第1张图片

图像对象

  EGE中的表示图像对象的类为 IMAGE, 而PIMAGE 为 指向 IMAGE 对象的指针,即 IMAGE*, 定义如下:

typedef IMAGE* PIMAGE;

  为了照顾学C语言而没学C++的新手,EGE表示图像不用 IMAGE类而用指向 IMAGE对象的指针PIMAGE来表示。很多图像处理函数也是以 PIMAGE 作为参数,而不是IMAGE

定义PIMAGE全局变量

可能有人对指针的简单操作不是很很懂
  PIMAGE就是个指针类型,可以定义为局部变量也可以定义为全局变量,但是定义全局变量的时候不能使用 newimage(),因为newimage() 需要保证在创建图形窗口后调用。
  你可以创建窗口后再调用newimage()PIMAGE变量 进行赋值。

#include 

PIMAGE pimg;

int main() 
{
	initgraph(640, 480, 0);
	
	pimg = newimage();
	
	...
	
	getch();
	closegraph();
	return 0;
}

图片绘制流程演示

  • 现在做个简单的演示,从图片中获取图像并绘制在窗口上
  • 从文件中获取图像需要将文件名写对。为了简便,请进入到源文件所在的目录,直接把图片文件放在cpp源文件旁边
  • 进入方法,是在文件标签中右键,选择打开所在的文件夹
    (九)EGE图像操作_第2张图片
    (九)EGE图像操作_第3张图片
  • 文件名必须要有扩展名,扩展名不能省,表示图片的类型。大小写没关系,但不要打错。比如一张图片文件名为 “image.jpg”, jpg就是扩展名, 类似的扩展名还有png, bmp, gif
  • 文件名可以使用绝对路径
  • 关于文件名,可以 看【文件名 绝对路径与相对路径】
#include 

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

	//创建图片对象
	PIMAGE pimg = newimage();				
	
	//从文件中获取图像(和原图一样大小)
	//图片文件名如"picture.jpg", "picture.png")
	getimage(pimg, "图片文件名(带扩展名)");
	
	//绘制图像,(0, 0)是图片绘制区域的左上角		
	putimage(0, 0, pimg);

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

如果文件名写对,那就会在窗口中显示图片。如果没显示出来,试试绝对路径

  • 点击图片文件,再右键选择 “属性”

(九)EGE图像操作_第4张图片

  • 点击安全选项卡,复制对象名称,这个就是绝对路径文件名,把中间的 \ 改为 /, 或者改为 \\ 即可

(九)EGE图像操作_第5张图片
如上图,最终的图片文件名为:"E:\\VSProject\\egeProject\\egeProject\\花火bg.jpg"
所以程序中是

getimage(pimg, "E:\\VSProject\\egeProject\\egeProject\\花火bg.jpg");

创建图像

  • 创建图像newimage()一定要在 initgraph( ) 之后调用,否则程序会崩溃。
    创建一个空的 IMAGE对象(其实是1x1大小,只有一个像素点的图像),并返回指向它的指针。
PIMAGE pimg;
pimg = newimage();	//创建了一个空 IMAGE 对象

  下面创建了一个宽高分别为100和50的图像,并返回该图像的指针。

PIMAGE pimg = newimage(100, 50);
  • 图像不需要时一定要使用 delimage(pimg) 销毁图像,释放内存,不然会造成内存泄漏
	delimage(pimg);
  • 所以图像其实是动态内存分配的,不用时需要回收内存,不能像下面这样,指向另一个图像时直接赋值,原来动态分配的对象就无法回收了。
PIMAGE pimg = newimage();
pimg = newimage();

正确的应该是这样

PIMAGE pimg = newimage();
delimage(pimg);
pimg = newimage();

获取图像

  获取图像使用 getimage() 函数,该函数有多个重载,分别从不同的地方获取图像。
使用getimage()获取图像后得到的 IMAGE 大小会随实际图像大小而改变,而不是你原先创建图像时设置的大小。

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

从图形窗口中获取某个区域的图像,可以是部分,也可以是整个窗口。
使用下面这个函数

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

下面我们对整个窗口进行截图

  • 先创建个空图像对象,获得指向图像对象的指针。因为getimage()后会自动改变图像对象的大小,所以开始时不用指定大小。
  • 获取窗口大小
  • 使用 getimage();
PIMAGE pimg = newimage();
int x = 0, y = 0;
int width = getwidth(), height = getheight();					
getimage(pimg, x, y, width, height);	

这样就将整个窗口截图,存到pimg里了,我们可以这样简化代码:

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

  如下图,在一个宽为X, 高为Y的窗口中,获取一个图像,图像是从窗口左上角(x, y)处宽为width, 高为height的区域截取下来的。
(九)EGE图像操作_第6张图片

从图像文件中获取

注意事项

从文件中读取图像是个耗时的过程,不要在帧循环中获取图像, 因为这样会不断获取图像,是非常耗时的,而且如果你忘记使用delimage()来销毁,那就会造成内存泄漏,占用极大的内存。

  因为文件中的图像都是固定的,可以在帧循环外获取好图像后,在帧循环中绘制,帧循环退出后再使用delimage() 销毁图像

支持(png / bmp / jpg / gif / emf / wmf / ico)类型的图片,其中gif只能获取第一帧,得到的只是一张静态的图片,而不是一个动图。

// 从图片文件获取图像(png/bmp/jpg/gif/emf/wmf/ico)
void getimage(
    PIMAGE pDstImg,       // 保存图像的 IMAGE 对象指针
    LPCTSTR pImgFile,     // 图片文件名
    int zoomWidth = 0,    // 设定图像缩放至的宽度(0 表示默认宽度,不缩放)
    int zoomHeight = 0    // 设定图像缩放至的高度(0 表示默认高度,不缩放)
);

  假设有一张图片的带路径的文件名为 "C:/ege/image.png"
注意,虽然windows 的文件路径显示为 C:\ege\image.png,只有一个反斜杠 但是C\C++中的反斜杠与其他符号搭配用作转义字符,所以反斜杠本身需要用 \\ 来表示。当然,用斜杠更好,这样就不会显得很烦

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

就可以将图像用来绘图。

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


从另一个 PIMAGE 中获取

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

  我需要的图像在另一个PIMAGE中,由 anotherPimg指向, 想从这个图像获取到pimg中。这时,pimg的宽高会变成width, height

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

(九)EGE图像操作_第7张图片

从资源文件获取

  资源图片文件支持很多种格式,但经过测试并不支持读取png图片,一般就用jpg图片代替,不过这样就话图片就丢失了透明度。

// 从资源文件获取图像(bmp/jpg/gif/emf/wmf/ico)
void getimage(
    PIMAGE pDstImg,       // 保存图像的 IMAGE 对象指针
    LPCTSTR pResType,     // 资源类型
    LPCTSTR pResName,     // 资源名称
    int zoomWidth = 0,    // 设定图像缩放至的宽度(0 表示默认宽度,不缩放)
    int zoomHeight = 0    // 设定图像缩放至的高度(0 表示默认高度,不缩放)
);

调用如下:

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

关于资源文件,在文末有说明

如何检测是否获取了图像

  如果你看到所需的图像并没有被绘制出来,不能就判断为获取图像出了问题,因为有可能是自己做了一些其它的绘图操作,比如清屏,被其它绘图覆盖等等导致图像没有显示在窗口,这些都是有可能的。
  正确的判断方法是在获取图像后,利用 getwidth()getheight() 获取图像的宽高。因为一开始创建的是空图像,即大小为1x1的图像,如果没有成功获取图像,那么返回都是1。如果成功获取了图像,那么得到的就是图像的大小参数。
  不应该绘制在EGE图像窗口上,因为要绘图,很容易被清屏影响且不太适合多文本的输出,应该调出控制台窗口,显示在控制台中。
  如下所示,在获取图像后输出图像的宽高数据来检测是否正确获取图像。

#define SHOW_CONSOLE
#include 
#include 

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

	//创建图片对象
	PIMAGE pimg = newimage();				
	
	//从文件中获取图像(和原图一样大小)
	//图片文件名如"picture.jpg", "picture.png")
	getimage(pimg, "图片文件名(带扩展名)");
	
	//输出图像宽高数据
	printf("[%d, %d]\n", getwidth(pimg), getheight(pimg));
	
	//绘制图像,(0, 0)是图片绘制区域的左上角		
	putimage(0, 0, pimg);

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

图像数组,加载多张图片

  经常需要从加载很多图像用作动画,所以定义可以 PIMAGE 数组,然后从文件中加载。
  动画中很多图像是一组动作,是有编号的一组图像,假设有很多图像, 名字是 “image1.png” ~ “image20.png” , 一共二十张,序号1 ~ 20.那么可以先用 sprintf() 生成文件名字符串,再进行加载, sprintf 在头文件 stdio.h 中。
因为这里图片序号是从1到20,数组序号是0~19, 所以要根据实际调整一下下标。pimgs[i] 对应的是图片 i +1.

PIMAGE pimgs[20];
char fileName[30];

for (int i = 0; i < 20; i++) {
	sprintf(fileName, "image%d.png", i + 1);
	pimgs[i] = newimage();
	getimage(pimgs[i], fileName);
}

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

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

图片缩放

  getimage() 实际是没有缩放功能的,但是我们经常需要将图片缩放到合适的尺寸。
  EGE的图片绘制putimage() 有缩放功能,这样我们从图像文件中获取图像后,缩放绘制图像到另一个图像对象上,就能得到想要的尺寸了。

图片缩放流程

流程是

  • 先进行缩放参数正确性判断
  • 从图片文件中获取原比例图像存入临时对象
  • 缩放绘制到目标图像上
  • 销毁临时图像

示例:

  • 例如,我们有一张图片 girl.jpg, 不知道它的尺寸,但是我们要的是320 x 320大小的,那么我们可以这样
  • 创建好我们要的图像,这个图像大小是必须设置的
//宽和高参数必须有,设置为我们所要的图像的大小
PIMAGE pimg = newimage(320, 320);
  • 再创建个临时图像,从图片文件上获取图像
PIMAGE temp = newimage();
getimage(temp, "girl.jpg");
  • 获取图像的宽和高,然后缩放绘制到我们要的图像上
int tempWidth = getwidth(temp), tempHeight = getheight(temp);
//缩放绘制到pimg上
putimage(pimg, 0, 0, 320, 320, temp, 0, 0, tempWidth, tempHeight);
  • temp图像只是做个中转,我们并不需要, 别忘了销毁
delimage(temp);
temp = NULL;
  • 然后pimg 上就是我们所要尺寸的图像了,可以 putimage(0, 0, pimg) 试试
  • 完整代码如下(所要的合适尺寸的图片保存在了pimg中
	PIMAGE pimg = newimage(320, 320);

	//创建临时图像
	PIMAGE temp = newimage();
	getimage(temp, "girl.jpg");
	int tempWidth = getwidth(temp), tempHeight = getheight(temp);
	//
	putimage(pimg, 0, 0, 320, 320, temp, 0, 0, tempWidth, tempHeight);

	delimage(temp);
	temp = NULL;

	//绘制出来看看
	putimage(00, pimg);
  • 最后我们就得到了我们想要的宽和高为 320x320 的图像,保存在了 pimg 中,接下来就可以使用了。
//将图片绘制在窗口上
putimage(0, 0, pimg);

//如果pimg不需要了记得销毁
delimage(pimg);
pimg = NULL;
图片缩放函数
  • 我们可以将图片缩放写成一个函数
    下面的函数将从图片文件中加载缩放成固定尺寸的图像,图像的大小为 width 和 height
//从图片文件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);
}

使用如下

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

//绘制图片查看效果
putimage(0, 0, pimg);
delimage(pimg);



从图片文件获取缩放后的图像函数编写

为方便使用,写了缩放加载图片的函数,可以用于缩放读取图片。
缺点是会丢失原有PIMAGE的设置参数,比如你设置了图片的背景颜色,填充颜色等(因为是用新的PIMAGE代替了原来的。。。)
如果你不想丢失设置参数的话,可以先拷贝一个副本,然后用 resize() 调整尺寸,再复制回来。

  • 获取固定大小的图像(用到下面的一个zoomImage() 函数)
int getimage_zoom(PIMAGE& pDstImg, LPCSTR  pImgFile, int zoomWidth, int zoomHeight)
{
	int retVal = getimage(pDstImg, pImgFile);
	zoomImage(pDstImg, zoomWidth, zoomHeight);
	return retVal;	
}

等比例缩放

  • 用到下面的一个zoomImage() 函数
//从图片文件中获取比例缩放后的图像
int getimage_zoom(PIMAGE& pDstImg, LPCSTR  pImgFile, float scale)
{
	int retVal = getimage(pDstImg, pImgFile);
	zoomImage(pDstImg, scale);
	return retVal;
}

获取缩放后的图像副本

  • 返回一个PIMAGE,是原来图像的一个缩放拷贝。不用时记得调用delimage()销毁图像,防止内存泄漏

PIMAGE getZoomImageCopy(PIMAGE pimg, int zoomWidth, int zoomHeight)
{
	int width = getwidth(pimg), height = getheight(pimg);
	if (zoomWidth <= 0 || zoomHeight <= 0) {
		zoomWidth  = width;
		zoomHeight = height;
	}
	PIMAGE tempPimg = newimage(zoomWidth, zoomHeight);
	putimage(tempPimg, 0, 0, zoomWidth, zoomHeight, pimg, 0, 0, width, height);
	return tempPimg;
}

PIMAGE getZoomImageCopy(PIMAGE pimg, float scale)
{
	int width = getwidth(pimg), height = getheight(pimg);
	if (0 < scale && scale != 1.0f) {
		width = int(width * scale + 0.5f);
		height = int(height * scale + 0.5f);
	}
	return getZoomImageCopy(pimg, width, height);
}

直接缩放原图像

  • 源图像会改变大小
void zoomImage(PIMAGE& pimg, int zoomWidth, int zoomHeight)
{
	//和原图像大小一样,不用缩放,直接返回
	if (zoomWidth == getwidth(pimg) && zoomHeight == getheight(pimg))
		return;

	PIMAGE copyImg = getZoomImageCopy(pimg, zoomWidth, zoomHeight);
	delimage(pimg);

	pimg = copyImg;
}

void zoomImage(PIMAGE& pimg, float scale)
{
	if (scale == 1.0f || scale < 0)
		return;

	//计算比例缩放后的尺寸,并近似取整
	int width  = int(getwidth(pimg) * scale + 0.5f);
	int height = int(getheight(pimg) * scale + 0.5f);

	zoomImage(pimg, width, height);
}

内存泄漏

来看看内存泄漏的情况(不使用 delimage() 的)

先调出 任务管理器
鼠标右击左下角的桌面图标,调出任务管理器
或者快捷键(Ctrl + Alt + Del), 然后点击 “任务管理器”
(九)EGE图像操作_第8张图片
打开后注意现在的内存使用情况
(九)EGE图像操作_第9张图片
当然,如果你有安全管家悬浮球,也可以看这个
(九)EGE图像操作_第10张图片
接下来是内存泄漏情况, 下面仅仅是加载一张图片,在循环里进行,没有 delimage(),。
运行下面的程序

#include 

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 的内存。
运行结束后,内存才统一释放,恢复正常。

(九)EGE图像操作_第11张图片
所以如果你在帧循环中不断地加载图像,而不使用 delimage(), 将会看到内存占用不断升高。
大概是下面这种写法
运行一下,看看不断升高的内存占用。

#include 

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
(九)EGE图像操作_第12张图片
下面是正确的写法,把加载放到帧循环外

#include 

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图像处理函数文档
http://xege.org/manual/api/img/index.htm

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

  • putimage() 绘制图像
  • putimage_alphablend() 以半透明方式绘制图像
  • putimage_transparent() 以透明方式绘制图像
  • putimage_alphatransparent() 以透明、半透明方式绘制图像
  • putimage_withalpha 绘制带透明通道图像
  • putimage_rotate() 绘制旋转一定角度后的图像
  • putimage_rotatezoom 绘制旋转缩放后的图像

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



putimage()函数

  图像可以绘制到窗口上,也可以绘制到另一个图像上, 这两种的区别是,绘制到图像上的函数参数比绘制到窗口上的函数多一个PIMAGE参数,这个PIMAGE就是你要保存绘制结果的图像

假设pimg所保存的IMAGE图像是我们要绘制的
下面来看一下putimage()的函数声明,putimage()有很多重载函数,即函数的参数不同,分别用于不同的情况

  1. 先来看看这个, 这个是最常用的,也是最简单的,一共四个参数。
void EGEAPI putimage(
	int dstX,
	int dstY,		//目标位置
	const PIMAGE pSrcImg,	//源图像
	DWORD dwRop = SRCCOPY
); 

将图像绘制在窗口上, 坐标是 (dstX, dstY), 也就是前两个参数, 第三个参数就是要绘制的图像了
常常看到 参数有srcdst
srcsource 的缩写, 来源的意思,代表着源图像
dstdestination 的缩写,目的地 的意思, 代表着目标图像
那么上面dstX, dstY就是目标位置的 x, y 坐标了。
最后一个参数是三元光栅操作码不用管它,不写就行,因为它有默认参数SRCCOPY (默认参数就是如果不写,那么就传入默认的值)
用法:将图像绘制到窗口 (x, y) 位置

putimage(x, y, pimg);
  1. 这个就比较长了,不常用
void EGEAPI putimage(
	int dstX,
	int dstY,
	int dstWidth,
	int dstHeight,
	const PIMAGE pSrcImg,
	int srcX,
	int srcY,
	DWORD dwRop = SRCCOPY
);    // 绘制图像到屏幕(指定宽高)

参数:
前面的参数变成了四个
dstX, dstY, dstWidth, dstHeight, 表示绘制目标区域的位置(x, y), 以及宽高width 和 height
pSrcImg 就是源图像
srcX, srcY 是源图像中截取的部分开始的位置,这参数使得绘制的时候可以 只截取源图像的一部分来绘制,而不用绘制一整张图像。截取部分的大小和 dstWidth, dstHeight 是一样的。
比如,源图像大小为 300 x 300, 我想要绘制它的右下角部分,那么srcX, srcY 就是它的中心位置,(150, 150), 宽高也是150。

putimage(x, y, 150, 150, pimg, 150, 150);

那么右上角呢?截取区域的位置坐标(x, y)那就是在(150, 0)了

putimage(x, y, 150, 150, pimg, 150, 0);
  1. 这个也是常用的,因为它有缩放的功能
void EGEAPI putimage(
	int dstX,
	int dstY,
	int dstWidth,
	int dstHeight,
	const PIMAGE pSrcImg,	//源图像
	int srcX,
	int srcY,
	int srcWidth,
	int srcHeight,
	DWORD dwRop = SRCCOPY
);   // 绘制图像到屏幕(指定源宽高和目标宽高进行拉伸)

可以看到,后面又多了两个参数 srcWidth, srcHeight ,代表截取区域的宽高。
作用就是,从源图像 (srcX, srcY) 的位置,截取宽高为 srcWidth, srcHeight 的区域,然后缩放成 dstWidth, dstHeight 大小,绘制到窗口 (dstX, dstY) 的位置
比如图像只有100 x 100大小,而窗口有 640 x 480,想把图像布满整个窗口
那么可以确定
目标位置是(0, 0), 宽是640,高是480,
图像截取区域是全部,那就是开始位置是(0, 0), 宽高都是 100,所以调用如下

putimage(0, 0, 640, 480, pimg, 0, 0, 100, 100);

因为 窗口的宽高比640 : 480 = 4:3 和图像的宽高比 100:100 = 1:1 不一样,所以会看到图片变形。想要图片不变形,最好设置成宽高比一样,那么怎么办呢,改图片截取区域。要截取 4:3大小,最大的话,就是截取 (100 :75) 了如果是截取中心的话,那么应该从位置应该下移 (100 - 75) / 2 =12.5, 那就 (0, 12), 所以不变形的调用如下:

putimage(0, 0, 640, 480, pimg, 0, 12, 100, 75);

下面是更详细的使用介绍

  • 绘制图像到窗口上
      在和我们的源文件main.cpp 同一个目录下,有个文件名为 “girl.jpg” 的图像文件, 它的大小为 640 x 640
    (九)EGE图像操作_第13张图片现在我们从这个文件中获取图像。
	PIMAGE pimg = newimage();
	getimage(pimg, "girl.jpg");

  图像就已经获取好了。

  • 在窗口上绘制,绘制区域的左上角坐标为 (x, y), 宽 width, 高 heigtpimg 图像大小相同
    (九)EGE图像操作_第14张图片
  • 现在这个窗口大小为 640 * 640,图像大小也为 640 * 640,则绘制点在(0, 0)处刚好合适
	int x = 0, y = 0;
	putimage(x, y, pimg);

(九)EGE图像操作_第15张图片
可以改变一下x 和 y, 再试试

	int x = 100, y = 150;
	putimage(x, y, pimg);

值可以直接写在函数参数中,定义变量是为了说明各个参数的意义
(九)EGE图像操作_第16张图片

  • 绘制图像到窗口上(指定宽高,从图像上截取部分区域,绘制到窗口上)
    假设窗口大小为640 x640, 原图像大小为640 x 640, 现在要绘制图像左上角的部分。那么我们所要从图像上截取的区域,左上角坐标为(0, 0), 宽为320, 高为320,.

x, y为绘制区域的左上角坐标
Origninx, Originy 为我们从图像上截取区域的左上角坐标
width, height, 为我们从图像上截取的区域大小, 也是绘制在窗口上的区域大小

	int x = 0, y = 0;	//窗口绘制区域的左上角坐标
	int srcX = 0, srcY = 0; //图像截取区域的左上角坐标
	int width = 320, height = 320;
	putimage(x, y, width, width, pimg, srcX, srcY);

值可以直接写在函数参数中,定义变量是为了说明各个参数的意义
(九)EGE图像操作_第17张图片

那现在我们截取图像中间的某个部分,左上角为(100, 150), 绘制在窗口的右上角(那么绘制区域左上角的坐标为(320,0)

	int x = 320, y = 0;	//窗口绘制区域的左上角坐标
	int srcX = 100, srcY = 150; //图像截取区域的左上角坐标
	int width = 320, height = 320;
	putimage(x, y, width, width, pimg, srcX, srcY);

值可以直接写在函数参数中,定义变量是为了说明各个参数的意义
(九)EGE图像操作_第18张图片

  • 绘制图像到窗口上(缩放

  上面我们从图像里截取了部分,然后绘制。绘制的图像和我们截取的部分图像大小是一样的。我们可以在后面添加两个截取的图像区域参数,实现缩放效果。
  现在我们要把图像的左上角部分,画到布满整个窗口,图像和窗口大小都为640x640,所以我们得到
绘画区域: 左上角坐标(0, 0), 宽 width=640, 高height = 640;
图像截取区域:左上角坐标(0, 0),宽 areaWidth = 320, areaHeight = 320;
所以代码如下:

	int x = 0, y = 0;
	int width = 640, height = 640;
	int srcX = 0, srcY = 0;
	int areaWidth = 320, areaHeight = 320;

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

值可以直接写在函数参数中,定义变量是为了说明各个参数的意义
(九)EGE图像操作_第19张图片

所以总结putimage的参数;
x, y 代表绘制的窗口区域左上角坐标
width, height 代表绘制的窗口区域宽和高
pimg 保存了图像
srcX, srcY 为图像的要绘制部分的左上角坐标
areaWidth, areaHeight 为图像要绘制部分的宽和高

绘制到窗口的函数:

  • putimage(窗口区域参数, pimg, 图像区域参数);

原图绘制

putimage(x, y, pimg)

截取图像部分绘制

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

截取图像部分并进行缩放绘制

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

绘制到另一个图像上的参数:
anotherPimg代表另一个图像, 我们要把pimg上的图像绘制到anotherPimg里面
  注意,绘制之前 anotherPimg 需要使用 newimage(width, height) 先创建一个图像, 并且width和height参数不能为空,因为putimage不会帮anotherPimg调整大小,anotherPimg小了就只绘制部分

PIMAGE anotherPimg = newimage(320, 320);

相比绘制到窗口上,绘制到另一个图像上就在前面多了PIMAGE参数

  • putimage(anotherPimg, 窗口区域参数, pimg, 图像区域参数)

原图绘制

putimage(anotherPimg, x, y, pimg)

截取图像部分绘制

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

截取图像部分并进行缩放绘制

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

透明绘图

putimage_withalpha() 绘制带透明通道的图片

  这个函数是用来绘制 带透明通道 的图片的。有些图片是带有alpha通道的,颜色使用的是ARGB,也就是说,它的颜色除了RGB三种颜色外,还多个了A(alpha),用来说明该像素的透明度。(当然jpg图片是不带alpha通道的)
比如下面这张图,是一张带透明通道是PNG图片,天空那部分其实是透明的
(九)EGE图像操作_第20张图片
下面是将带透明通道的图片分别用putimage()(上)putimage_withalpha()(下)的结果。
putimage_withalpha()
绘制,因为天空那部分是完全透明的(透明度alpha为0),所以背景是什么颜色,显示出来的就是什么颜色。
而用 putimage() 绘制, 忽略透明度,就照着原本的颜色画(完全透明的部分原本的颜色一般都是黑色的)

(九)EGE图像操作_第21张图片

  函数声明如下:

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   // 源图像混合区域高度
);

  参数请参考 putimage().
上面可以看到,后四个参数是有默认值的。表示不写后四个参数则默认原图绘制, 如果传入参数,则截取一部分绘制。
前面多了个目标图像的参数 imgdest, 表示绘制到哪个图像上,如果绘制到窗口,则传入参数 NULL

如,原图绘制到窗口 (x,y) 位置

若 pimg 为源图像
putimage_withalpha(NULL, pimg, x, y);
putimage_alphablend() 两张图片半透明混合

先来看看效果:
下面有三张图片,前两张是原图,最后一张是半透明混合后的图。可以看到,图片似乎变成了透明的,能看到后面的部分。
(九)EGE图像操作_第22张图片(九)EGE图像操作_第23张图片(九)EGE图像操作_第24张图片
下面是函数声明,下面的每一个参数旁都有注释

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	// 源图像混合区域高度
);

  函数的参数和 putimage() 很像, 操作的是两张图片, 一张是 imgsrc , 另一张是imgdest
  上面两个图像名后两个后缀,src是source的简写, 来源的意思, dest是destination的缩写,目标的意思
  混合之后的图像保存在 imgdest 中, 带有 dest 的参数是与保存最终结果的图像相关的。
  两个图像混合后,混合后的图像保存在目标图像imgdest 中, 源图像imgsrc 是不会改变的。
  由注释就可以看出来所要的参数, 这里不多说明。
  不同的是多了个 alpha 值,范围是0~255, 这个代表透明度,相当于源图像覆盖在目标图像上,alpha值越大,源图像越不透明,相当于目标图像完全被源图像挡住,当alpha 值为0时,源图像就完全透明,得到的还是目标图像原来的样子。
  两张图片,坐标是源图像 imgsrc, 右边是目标图像 imgdest
(九)EGE图像操作_第25张图片 (九)EGE图像操作_第26张图片
  接下来进行混合, alpha值分别是0xFF, 0x40, 0x00(即255, 64, 0)

	//获取图片
	PIMAGE imgsrc = newimage();
	getimage(imgsrc, "girl.jpg");
	
	PIMAGE imgdest = newimage();
	getimage(imgdest, "girl2.jpg");

	//透明混合
	int alpha = 0x40;
	putimage_alphablend(imgdest, imgsrc, 0, 0, alpha, 0, 0, 640, 640);
	//绘制到窗口上
	putimage(0, 0, imgdest);

	//释放内存
	delimage(imgsrc);
	delimage(imgdest);

下面是效果图(分别是不透明,半透明,全透明)
(九)EGE图像操作_第27张图片(九)EGE图像操作_第28张图片(九)EGE图像操作_第29张图片
可以看到,alpha值越小, 源图像越透明, 为0时完全看不见源图像。
试试改变不同的值,看看效果

//获取图片
	PIMAGE imgsrc = newimage();
	getimage(imgsrc, "girl.jpg");

	PIMAGE imgdest = newimage();
	getimage(imgdest, "girl2.jpg");

	//透明混合
	int x = 100, y = 100;
	int areaWidth = 320, areaHeight = 320;
	int Originx = 0, Originy = 0;
	int alpha = 0x80;
	putimage_alphablend(imgdest, imgsrc, x, y, alpha, Originx, Originy, areaWidth, areaHeight);
	//绘制到窗口上
	putimage(0, 0, imgdest);

	//释放内存
	delimage(imgsrc);
	delimage(imgdest);

(九)EGE图像操作_第30张图片

putimage_transparent()

用于指定某种颜色变为全透明
先来看看效果图
一共有三幅图
左边是原图
中间 指定红色为透明色,绘制时红色就不见了,看到的是背景
右边 指定绿色为透明色 , 绘制时绿色就变为透明,看到的是背景

(九)EGE图像操作_第31张图片
下面是函数的声明,每一个参数旁都有注释

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 变成了一个颜色参数。

下面是一个使用示例,以原图绘制, 绘制到窗口(x, y)位置上

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

  这个函数也是相当于把源图像覆盖在目标图像上,如果源图像上有和 crTransparent 同样颜色的像素,那么这个像素就会变成透明,从而看到底下的目标图像。
  左边是源图像,大小为400 x 400, 右边是目标图像, 大小为640 x 640。
(九)EGE图像操作_第32张图片(九)EGE图像操作_第33张图片
下面进行要进行半透明绘图,只要源图像的人物,忽略掉它的黑色背景。
通过windows画图工具的吸管(取色器),可以得到黑色背景是纯黑, 颜色值是EGERGB(0, 0, 0) ,所以混合如下:

//获取图片
	PIMAGE imgsrc = newimage();
	getimage(imgsrc, "boy.jpg");

	PIMAGE imgdest = newimage();
	getimage(imgdest, "girl2.jpg");

	//透明混合
	int x = 100, y = 100;
	color_t transparentColor = EGERGB(0, 0, 0);
	putimage_transparent(imgdest, imgsrc, x, y, transparentColor);
	//绘制到窗口上
	putimage(0, 0, imgdest);

	//释放内存
	delimage(imgsrc);
	delimage(imgdest);

(九)EGE图像操作_第34张图片
  可以看到源图像的黑色背景变成透明的了,但是为什么有残留黑色色块?
因为 源图像上并不是所有的黑色都是EGB(0, 0, 0) , 这些颜色绘制时就不会变成透明。我们可以用PS进行处理,把外面的部分涂成纯黑色。
  再重新运行试试,

(九)EGE图像操作_第35张图片
emmm…涂得不是很彻底,差不多差不多,不用在意这些细节。。。

putimage_alphatransparent()
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
);

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

旋转绘图

我们常常需要把某个图片旋转一定角度后再绘制,这里有两个函数。

  • putimage_rotate()
    将图像图片旋转后绘制
  • putimage_rotatezoom
    putimage_rotate() 的增强版,多了缩放的功能
putimage_rotate()

先来看看函数的参数:

int EGEAPI putimage_rotate(
	PIMAGE imgdest,			//目标图像
	PIMAGE imgtexture,		//纹理图像
	int nXOriginDest,
	int nYOriginDest,
	float centerx,
	float centery,
	float radian,			//旋转的弧度,不是角度, 弧度 = 角度 * π / 180  
	int btransparent = 0,	// transparent (1) or not (0)
	int alpha = -1,			// 范围为0~255, alpha=-1 意味着忽略透明度
	int smooth = 0			// 是否进行平滑处理
);

解析一下参数

  • imgdest:就是目标图像,绘制的结果保存在这个图像中, 为NULL就代表绘制在窗口上。
  • imgtexture纹理图像, 可以看做源图像
  • nXOriginDest:目标图像上的旋转中心x坐标
  • nYOriginDest:目标图像上的旋转中心y坐标
  • centerx:旋转中心相对于纹理图像左上角的x偏移,。注意,偏移量是float型, 1.0f便代表整个纹理图像的宽, 2.0f便代表两倍宽度。可以为负值。
  • centery:旋转中心相对于纹理图像左上角的y偏移。注意,偏移量是float型, 1.0f便代表整个纹理图像的宽, 2.0f便代表两倍宽度。可以为负值。
  • radian :旋转的弧度值,弧度 = 角度 * π / 180, 顺时针为正,逆时针为负。
  • btransparent 设置为 1 时可以绘制带透明通道的纹理图像,设置为0时忽略图像的透明通道。
  • alpha :透明度,范围0~255, 0代表纹理完全透明,255代表不透明 ,为-1代表忽略透明度。默认值为-1.
  • smooth:旋转后的图像会失真,锯齿感严重,设置为1则对图像进行平滑处理,让图像平滑些。默认值为0,不平滑处理。平滑处理相对来说会进行更多的计算。

主要是关于旋转中心的问题, 下面来说说有关的四个参数
(九)EGE图像操作_第36张图片

  • nXOriginDest, nYOriginDest
    旋转中心在目标图像上的坐标。
  • centerx, centery
    旋转中心相对于纹理图像左上角的偏移,以纹理图像的尺寸=1.0f为基准,如上图,旋转中心在纹理坐标系中的坐标约为 (-1.5f, -1.2f)

如下图,旋转30°后的位置为

(九)EGE图像操作_第37张图片
  所以,如果centerx = 0.0f, centery = 0.0f, 那么旋转中心与图像左上角重合,图像绕着左上角的位置旋转;
如果想要图像绕着自身中心旋转, 则 设置centerx = 0.5f, centery = 0.5f即可。
  下面来做个图像旋转示例,修改参数加深理解:

  这里用到上面图片缩放内容的一个 getimage_zoom() 函数

#include 

//从图片文件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()
{
	initgraph(640, 640, INIT_RENDERMANUAL);			//初始化窗口
	setcaption("EGE图像旋转");		//设置窗口标题

	//绘图部分
	setbkcolor(WHITE);	
			
	PIMAGE imgsrc = newimage(), imgdest = newimage();
	getZoomImage(imgsrc, "girl.jpg", 150, 150);
	getZoomImage(imgdest, "girl2.jpg", 640, 640);

	int x = 320, y = 320;						//旋转中心坐标
	float centerx = -0.5f, centery = -0.5f;		//旋转中心相对图像左上角偏移
	float speed = 0.03f;						//旋转速度
	int alpha = 255;							//透明度
	int smooth = 0;								//是否进行图像平滑处理

	for (float radian = 0.0f; is_run(); delay_fps(60), radian += speed) {
		//cleardevice();
		putimage(0, 0, imgdest);
		
		putimage_rotate(NULL, imgsrc, x, y, centerx, centery, radian, 0, alpha, smooth);
	}
	//销毁图像
	delimage(imgsrc);
	delimage(imgdest);

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

	return 0;
}

下面分别是center 为 -0.5f, 0.0f 和 0.5f 的情况

(分别是围绕外部点, 左上角, 自身中心)

putimage_rotatezoom()

旋转的同时还允许缩放。

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
);

只比 putimage_rotate() 多了个zoom参数, 是缩放比例, float型,1.0f 表示不缩放

模糊滤镜

对一图片区域进行模糊滤镜操作。效果如下

可以看到,模糊了一点。
使用的是 imagefilter_blurring() 函数
函数声明如下:

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表示图像变成纯黑。

下面是效果,右边为模糊后的图。
(九)EGE图像操作_第38张图片

图像属性

  • 重设图像尺寸
    使用 resize()可以改变图像的尺寸(原保存的图像丢失)
void resize(PIMAGE pimg, int width, int height);

注意,我们不能用这个来对存有图像的pimg直接进行缩放,因为使用后图像会丢失,和开始就使用 pimg = newimage(newWidth, newHeight) 得到的图像一样,都是一张纯黑色的图
  例如,如果原来newimage(width, height)后的图像大小不符合,现在需要400x300的

resize(pimg, 400, 300);
  • 获取图像宽度和高度
    假设有一张图片
PIMAGE pimg = newimage(160, 90);

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

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

  其中,pimg默认值为NULL, 使用getwidth(), getheight()不带参数将获取的是窗口的宽高。
   getmaxx(), 同 getwidth( )
   getmaxy(), 同 getheight( )

图像保存

IMAGE 图像可以保存成图像文件。

保存成bmp图像文件

  • 将pimg的图像保存成图片, filename是带图片类型扩展名的文件名
    文件扩展名写成 bmp, 虽然你写成jpg, png都可以,因为图像软件都能直接打开,图像软件能自动识别文件格式,但是图像文件实际是bmp格式
int  saveimage(PIMAGE pimg, LPCSTR  filename);
int  saveimage(PIMAGE pimg, LPCWSTR filename);

使用示例:(pimg保存了图像, 产生的bmp图片文件保存在当前文件夹中)

saveimage(pimg, "img.bmp"); 

保存成png图像文件

  • 保存成PNG图片, 后边有个bAlpha 参数, true是带alpha通道保存,false是不带alpha通道保存。
int  savepng(PIMAGE pimg, LPCSTR  filename, int bAlpha = 0);
int  savepng(PIMAGE pimg, LPCWSTR filename, int bAlpha = 0);

使用示例:(pimg保存了图像, 产生的png图片文件保存在当前文件夹中)

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

获取图像缓存区

  • 通过调用 getbuffer(), 可以得到图像的缓存区的首地址,然后便可以进行图像处理。
    函数声明:
color_t*	getbuffer(PIMAGE pimg);

如果参数 pimg 为NULL,那么获取的是EGE窗口缓存的首地址

  • 返回的是缓存区的首地址,是一维指针, 缓存区大小为:
    行数 = 图像 的, 获取方法 int row = getheight(pimg)
    列数 = 图像 的, 获取方法 int col = getwidth(pimg)

注意,因为是一维数组,所以表示是 buffer[i * nCol + j] , 而不是 buffer[i][j] , 其中nCol 代表列数, 可以用getwidth(pimg) 获取
和我们平常的数组表格画法是一样的。
如图,图像中交叉的位置是第二行第六列(从0开始算起), 也就是 buffer[ 2 * width + 6 ],
但是在图像的坐标系中位置坐标是 (6, 2) ,这个需要注意,别弄反了。

(九)EGE图像操作_第39张图片

  • 所以图像从上到下,从左到右遍历每个像素应该为:
color_t* buffer = getbuffer(pimg);
int width = getwidth(pimg), height = getheight(pimg);

for (int i = 0; i < height; i++) {
	for (int j = 0; j < width; j++) {
	//这里面进行图像处理
	buffer[i * width + j] = 
	}
}
  • 上面的 buffer[i * width + j] 对应的像素在图像的坐标位置是 (j, i).

下面来进行个利用 getbuffer() 返回的缓存区将RGB彩色图像转灰度图的示例。

//#define SHOW_CONSOLE

#include 

//从图片文件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;
}

(九)EGE图像操作_第40张图片

资源文件

  资源文件是什么? 看下图,VS的项目中有个资源文件 文件夹,加到资源文件中的文件,编译时会放入exe执行文件中。这样制作成程序时,就不用担心因放在外面的图片音乐等文件丢失而导致运行出错的情况,因为文件已经嵌入exe执行文件中。
(九)EGE图像操作_第41张图片
  当前,既然是嵌入exe执行文件中的,那就不能用读取文件的方式读取。

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

getimage()无法获取 png格式 资源文件

VC系列(VS2017)

添加图片资源文件

  • 右键点击资源文件文件夹, 选择 添加->资源
    (九)EGE图像操作_第42张图片
  • 选择Bitmap, 点击 “导入”
    (九)EGE图像操作_第43张图片
  • 这时进入到文件选择界面,设置文件类型为所有文件
    (九)EGE图像操作_第44张图片
  • 然后就能选择自己想要添加的图片了,现在以 jpg图片为例,因为 png 资源文件 getimage() 读取不了
    选中后会要求填写资源类型,JPG图片就写入JPG, 然后点击确定
    (九)EGE图像操作_第45张图片
  • 然后就能看到项目中多出了resource.h头文件项目名.rc资源文件, 还有我们添加的JPG图片
    并且会自动打开我们添加的图片,不用编辑,关闭就好
    (九)EGE图像操作_第46张图片
  • 点击资源视图,可以看到我们添加的资源文件。看到有个 “JPG” 文件夹, 这就是我们之前 填写的资源类型
    可以看到下面有个IDR_JPG1, 这就是我们添加的资源文件的名字
    (九)EGE图像操作_第47张图片
  • 可以点击资源文件名,然后点击 属性,修改成容易记的资源名
    (九)EGE图像操作_第48张图片
  • 在ID这一栏修改,比如,我修改成JPG_BG
    (九)EGE图像操作_第49张图片
resource.h 头文件

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

项目名.rc

.rc文件 右键,选择 查看代码F7
(九)EGE图像操作_第51张图片

  • 如果弹出这种框,选
    (九)EGE图像操作_第52张图片
  • 往下拉看到下面这部分
    绿色的就是 资源名称, 蓝色的是 资源类型, 紫色的是 带相对路径的文件名
    (九)EGE图像操作_第53张图片
    现在资源文件已经添加完成。那么现在就是获取了
    先在文件中 包含 resource.h 头文件(注意了,那个头文件不一定是叫resource.h)
#include "resource.h"

然后就是查看资源文件的宏定义, 我之前命名是叫 JPG_BG
(九)EGE图像操作_第54张图片
然后看看 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) 配对

所以如下便可获取:

PIMAGE pimg = newimage();
getimage(pimg, "JPG", MAKEINTRESOURCEA(JPG_BG)); 

或者宽字符型
getimage(pimg, L"JPG", MAKEINTRESOURCEW(JPG_BG)); 

下面是例程:

#include 

#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;
}

运行结果:
(九)EGE图像操作_第55张图片

CodeBlocks

创建rc文件

在项目,点击工具栏文件(File) ->新建(new)->空文件(empty file)
选择(添加到项目中)
(九)EGE图像操作_第56张图片
(九)EGE图像操作_第57张图片

  • 文件命名,扩展名需要是 rc, 这里命名为 resource.rc
    (九)EGE图像操作_第58张图片
    (九)EGE图像操作_第59张图片

编写rc文件

先把图片文件放到项目中

可以看到我图片的放在当前目录下的,文件名就是 “花火bg.jpg”,也可以自己新建个文件夹,把图片放进去,这样方便管理,不要随便扔桌面上
(九)EGE图像操作_第60张图片

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

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

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

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

IMAGE_JPG_BG            JPG     "花火bg.jpg"

(九)EGE图像操作_第61张图片
然后程序中就可以用getimage()获取了

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

按如上则填写为

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

源文件中完整程序如下:

#include 

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

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

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

	getch();

	closegraph();

	return 0;
}

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

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

DevC++

创建rc文件

  • 新建文件, 添加到项目中
    (九)EGE图像操作_第65张图片
    (九)EGE图像操作_第66张图片
  • 保存文件,扩展名为 rc, 命名随意,这里命名为 resource.rc
    (九)EGE图像操作_第67张图片
    (九)EGE图像操作_第68张图片

编写rc文件

接下来和 CodeBlocks一样,去上面看

你可能感兴趣的:(EGE)