EGE图像处理相关函数文档
http://xege.org/manual/api/img/index.htm
打开 文件资源管理器, 勾选上文件扩展名。这样就能看到完整的文件名。
EGE中的表示图像对象的类为 IMAGE, 而PIMAGE 为 指向 IMAGE 对象的指针,即 IMAGE*, 定义如下:
typedef IMAGE* PIMAGE;
为了照顾学C语言而没学C++的新手,EGE表示图像不用 IMAGE类而用指向 IMAGE对象的指针PIMAGE来表示。很多图像处理函数也是以 PIMAGE 作为参数,而不是IMAGE 。
可能有人对指针的简单操作不是很很懂
PIMAGE就是个指针类型,可以定义为局部变量,也可以定义为全局变量,但是定义全局变量的时候不能使用 newimage(),因为newimage() 需要保证在创建图形窗口后调用。
你可以创建窗口后再调用newimage() 对 PIMAGE变量 进行赋值。
#include
PIMAGE pimg;
int main()
{
initgraph(640, 480, 0);
pimg = newimage();
...
getch();
closegraph();
return 0;
}
#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;
}
如果文件名写对,那就会在窗口中显示图片。如果没显示出来,试试绝对路径
如上图,最终的图片文件名为:"E:\\VSProject\\egeProject\\egeProject\\花火bg.jpg"
所以程序中是
getimage(pimg, "E:\\VSProject\\egeProject\\egeProject\\花火bg.jpg");
(其实是1x1大小,只有一个像素点的图像)
,并返回指向它的指针。PIMAGE pimg;
pimg = newimage(); //创建了一个空 IMAGE 对象
下面创建了一个宽高分别为100和50的图像,并返回该图像的指针。
PIMAGE pimg = newimage(100, 50);
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 // 要获取图像的区域高度
);
下面我们对整个窗口进行截图
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的区域截取下来的。
注意事项
从文件中读取图像是个耗时的过程,不要在帧循环中获取图像, 因为这样会不断获取图像,是非常耗时的,而且如果你忘记使用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().
// 从另一个 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);
资源图片文件支持很多种格式,但经过测试并不支持读取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() 有缩放功能,这样我们从图像文件中获取图像后,缩放绘制图像到另一个图像对象上,就能得到想要的尺寸了。
流程是
- 先进行缩放参数正确性判断
- 从图片文件中获取原比例图像存入临时对象
- 缩放绘制到目标图像上
- 销毁临时图像
示例:
//宽和高参数必须有,设置为我们所要的图像的大小
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);
delimage(temp);
temp = NULL;
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(0, 0, pimg);
//将图片绘制在窗口上
putimage(0, 0, pimg);
//如果pimg不需要了记得销毁
delimage(pimg);
pimg = NULL;
//从图片文件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() 调整尺寸,再复制回来。
int getimage_zoom(PIMAGE& pDstImg, LPCSTR pImgFile, int zoomWidth, int zoomHeight)
{
int retVal = getimage(pDstImg, pImgFile);
zoomImage(pDstImg, zoomWidth, zoomHeight);
return retVal;
}
等比例缩放
//从图片文件中获取比例缩放后的图像
int getimage_zoom(PIMAGE& pDstImg, LPCSTR pImgFile, float scale)
{
int retVal = getimage(pDstImg, pImgFile);
zoomImage(pDstImg, scale);
return retVal;
}
获取缩放后的图像副本
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);
}
先调出 任务管理器
鼠标右击左下角的桌面图标,调出任务管理器
或者快捷键(Ctrl + Alt + Del), 然后点击 “任务管理器”
打开后注意现在的内存使用情况
当然,如果你有安全管家悬浮球,也可以看这个
接下来是内存泄漏情况, 下面仅仅是加载一张图片,在循环里进行,没有 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 的内存。
运行结束后,内存才统一释放,恢复正常。
所以如果你在帧循环中不断地加载图像,而不使用 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
下面是正确的写法,把加载放到帧循环外
#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
图像的绘制分别有以下几个函数:
函数声明请查看EGE官网文档及 ege.h头文件
图像可以绘制到窗口上,也可以绘制到另一个图像上, 这两种的区别是,绘制到图像上的函数参数比绘制到窗口上的函数多一个PIMAGE参数,这个PIMAGE就是你要保存绘制结果的图像
假设pimg所保存的IMAGE图像是我们要绘制的
下面来看一下putimage()的函数声明,putimage()有很多重载函数,即函数的参数不同,分别用于不同的情况
void EGEAPI putimage(
int dstX,
int dstY, //目标位置
const PIMAGE pSrcImg, //源图像
DWORD dwRop = SRCCOPY
);
将图像绘制在窗口上, 坐标是 (dstX, dstY), 也就是前两个参数, 第三个参数就是要绘制的图像了
常常看到 参数有src 和 dst
src 是 source 的缩写, 来源的意思,代表着源图像
dst 是 destination 的缩写,目的地 的意思, 代表着目标图像
那么上面dstX, dstY就是目标位置的 x, y 坐标了。
最后一个参数是三元光栅操作码,不用管它,不写就行,因为它有默认参数SRCCOPY (默认参数就是如果不写,那么就传入默认的值)
。
用法:将图像绘制到窗口 (x, y) 位置
putimage(x, y, pimg);
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);
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);
下面是更详细的使用介绍
PIMAGE pimg = newimage();
getimage(pimg, "girl.jpg");
图像就已经获取好了。
int x = 0, y = 0;
putimage(x, y, pimg);
int x = 100, y = 150;
putimage(x, y, pimg);
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);
那现在我们截取图像中间的某个部分,左上角为(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);
上面我们从图像里截取了部分,然后绘制。绘制的图像和我们截取的部分图像大小是一样的。我们可以在后面添加两个截取的图像区域参数,实现缩放效果。
现在我们要把图像的左上角部分,画到布满整个窗口,图像和窗口大小都为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);
所以总结putimage的参数;
x, y 代表绘制的窗口区域左上角坐标
width, height 代表绘制的窗口区域宽和高
pimg 保存了图像
srcX, srcY 为图像的要绘制部分的左上角坐标
areaWidth, areaHeight 为图像要绘制部分的宽和高
绘制到窗口的函数:
原图绘制
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, x, y, pimg)
截取图像部分绘制
putimage(anotherPimg,x, y, width, height, pimg, srcX, srcY);
截取图像部分并进行缩放绘制
putimage(anotherPimg,x, y, width, height, pimg, srcX, srcY, areaWidth, areaHeight);
这个函数是用来绘制 带透明通道 的图片的。有些图片是带有alpha通道的,颜色使用的是ARGB,也就是说,它的颜色除了RGB三种颜色外,还多个了A(alpha),用来说明该像素的透明度。(当然jpg图片是不带alpha通道的)
比如下面这张图,是一张带透明通道是PNG图片,天空那部分其实是透明的
下面是将带透明通道的图片分别用putimage()(上)
和putimage_withalpha()(下)
的结果。
用 putimage_withalpha()
绘制,因为天空那部分是完全透明的(透明度alpha为0)
,所以背景是什么颜色,显示出来的就是什么颜色。
而用 putimage() 绘制, 忽略透明度,就照着原本的颜色画(完全透明的部分原本的颜色一般都是黑色的)
函数声明如下:
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);
先来看看效果:
下面有三张图片,前两张是原图,最后一张是半透明混合后的图。可以看到,图片似乎变成了透明的,能看到后面的部分。
下面是函数声明,下面的每一个参数旁都有注释
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
接下来进行混合, 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);
下面是效果图(分别是不透明,半透明,全透明)
可以看到,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);
用于指定某种颜色变为全透明
先来看看效果图
一共有三幅图
左边是原图
中间 指定红色为透明色,绘制时红色就不见了,看到的是背景
右边 指定绿色为透明色 , 绘制时绿色就变为透明,看到的是背景
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。
下面进行要进行半透明绘图,只要源图像的人物,忽略掉它的黑色背景。
通过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);
可以看到源图像的黑色背景变成透明的了,但是为什么有残留黑色色块?
因为 源图像上并不是所有的黑色都是EGB(0, 0, 0) , 这些颜色绘制时就不会变成透明。我们可以用PS进行处理,把外面的部分涂成纯黑色。
再重新运行试试,
emmm…涂得不是很彻底,差不多差不多,不用在意这些细节。。。
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的像素点变透明,其它的地方进行透明度混合。
我们常常需要把某个图片旋转一定角度后再绘制,这里有两个函数。
先来看看函数的参数:
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,不平滑处理。平滑处理相对来说会进行更多的计算。
- nXOriginDest, nYOriginDest
旋转中心在目标图像上的坐标。- centerx, centery
旋转中心相对于纹理图像左上角的偏移,以纹理图像的尺寸=1.0f为基准,如上图,旋转中心在纹理坐标系中的坐标约为 (-1.5f, -1.2f)
如下图,旋转30°后的位置为
所以,如果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 的情况
(分别是围绕外部点, 左上角, 自身中心)
旋转的同时还允许缩放。
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 //区域高度
);
参数说明:
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 图像可以保存成图像文件。
int saveimage(PIMAGE pimg, LPCSTR filename);
int saveimage(PIMAGE pimg, LPCWSTR filename);
使用示例:(pimg保存了图像, 产生的bmp图片文件保存在当前文件夹中)
saveimage(pimg, "img.bmp");
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);
color_t* getbuffer(PIMAGE pimg);
如果参数 pimg 为NULL,那么获取的是EGE窗口缓存的首地址
注意,因为是一维数组,所以表示是 buffer[i * nCol + j] , 而不是 buffer[i][j] , 其中nCol 代表列数, 可以用getwidth(pimg) 获取
和我们平常的数组表格画法是一样的。
如图,图像中交叉的位置是第二行,第六列(从0开始算起), 也就是 buffer[ 2 * width + 6 ],
但是在图像的坐标系中位置坐标是 (6, 2) ,这个需要注意,别弄反了。
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] =
}
}
下面来进行个利用 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;
}
资源文件是什么? 看下图,VS的项目中有个资源文件 文件夹,加到资源文件中的文件,编译时会放入exe执行文件中。这样制作成程序时,就不用担心因放在外面的图片音乐等文件丢失而导致运行出错的情况,因为文件已经嵌入exe执行文件中。
当前,既然是嵌入exe执行文件中的,那就不能用读取文件的方式读取。
下面是各编译器以读取资源文件的方式获取图片。
打开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) 配对
所以如下便可获取:
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;
}
在项目,点击工具栏文件(File) ->新建(new)->空文件(empty file)
选择是(添加到项目中)
可以看到我图片的放在当前目录下的,文件名就是 “花火bg.jpg”,也可以自己新建个文件夹,把图片放进去,这样方便管理,不要随便扔桌面上。
资源名称 资源类型 "带路径文件名"
资源类型和资源名称自己命名(资源类型最好按照文件格式命名)
,这里命名
上面用到带路径文件名,如果不知道是什么,请去看 (二十一)文件名 绝对路径与相对路径,推荐用相对路径
编写如下:
IMAGE_JPG_BG JPG "花火bg.jpg"
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),查看有没有错误, 没有错误就可以点击运行了,就能看到显示的图片了。
接下来和 CodeBlocks一样,去上面看