[EasyX]png透明贴图函数,简单实用

目录

一、前言

二、源代码

三、解析


一、前言

众所周知,透明贴图对于计算机绘图(在屏幕上画画)来说灰常重要,不然你的程序就是这个样子的↓

不能说难受,只能说是灰常难受。主要原因吗......easyx在显示png时透明部分会变成纯黑色,哎。因此我们要采取一些技术手段,比如透明贴图,这样我们就能正常显示png了:

于是经过我两年半的研究,成功写出了一个针对png的透明贴图函数,可以正常的显示png图像,让我们一起来看一下:)

二、源代码

#include<graphics.h>
#include<conio.h>
#include<iostream>
using namespace std;
void transparentimage(int x,int y,IMAGE img){
	IMAGE img1;
	DWORD *d1;
	img1=img;
	d1=GetImageBuffer(&img1);
	float h,s,l;
	for(int i=0;i<img1.getheight()*img1.getwidth();i++){
		RGBtoHSL(BGR(d1[i]),&h,&s,&l);
		if(l<0.03){
			d1[i]=BGR(WHITE);
		}
		if(d1[i]!=BGR(WHITE)){
			d1[i]=0;
		}
	}
	putimage(x,y,&img1,SRCAND);
	putimage(x,y,&img,SRCPAINT);
//	putimage(x+100,y,&img1);
//	putimage(x+200,y,&img);
}
int main(){
	initgraph(510,510,1);
	setbkcolor(RED);
    cleardevice();
	IMAGE img;
	loadimage(&img,"enemy1.png");
	transparentimage(0,0,img);
	getch();
	return 0;
}

效果大概是这样子:

三、解析

#include<graphics.h>
#include<conio.h>
using namespace std;

这一段就是头文件,graphics.h包含easyx.h,还提供更多函数,conio.h提供getch()函数,防止你的窗口一闪而过 ,最后别忘记加上命名空间。

int main(){
	initgraph(510,510,1);
	setbkcolor(RED);
    cleardevice();
	IMAGE img;
	loadimage(&img,"enemy1.png");
	transparentimage(0,0,img);
	getch();
	return 0;
}

这段是主函数,主要作用是新建一个窗口,然后把背景设成红色(方便对比),接着导入图片,然后用透明贴图函数显示,最后加上getch(),防止窗口一闪而过。

接下来就是透明贴图函数,让我们好好研究研究。

IMAGE img1;
DWORD *d1;
img1=img;
d1=GetImageBuffer(&img1);
float h,s,l;

这段是声明变量,img1用来储存原图,相当于原图的替身,接下来要对它进行各种处理(原图最后要用),DWORD *d1看上去非常复杂,其实DWORD就代表unsigned long或unsigned int,等于4个字节,不信看头文件的源代码

minwindef.h第124行

typedef unsigned __LONG32 DWORD;

这个__LONG32在其它地方有定义:

_mingw.h第25到29行

#ifndef __LP64__    /* 32 bit target, 64 bit Mingw target */
#define __LONG32 long
#else            /* 64 bit Cygwin target */
#define __LONG32 int
#endif

float h,s,l;就不用讲了,GetImageBuffer则是用于获取绘图设备的显示缓冲区指针。

DWORD* GetImageBuffer(IMAGE* pImg = NULL);

pImg:绘图设备指针。如果为 NULL,表示默认的绘图窗口。

那这玩意是干什么用的呢?你可以把图像看成一个一维数组,里面大概是这样的: 

 GetImageBuffer就是告诉你这个一维数组第一个元素的指针

获取到的显示缓冲区指针可以直接读写。在显示缓冲区中,每个点占用 4 个字节,因此:显示缓冲区的大小 = 宽度 × 高度 × 4 (字节)。像素点在显示缓冲区中按照从左到右、从上向下的顺序依次排列。访问显示缓冲区请勿越界,否则会造成难以预料的后果。

注意:

显示缓冲区中的每个点用BGR格式储存,要注意转换

获取地址后,d1[0]就是第一行第一个像素, d1[1]就是第一行第二个像素......以此类推。

for(int i=0;i<img1.getheight()*img1.getwidth();i++){
	RGBtoHSL(BGR(d1[i]),&h,&s,&l);
	if(l<0.03){
		d1[i]=BGR(WHITE);
	}
	if(d1[i]!=BGR(WHITE)){
		d1[i]=0;
	}
}

首先,这个循环第一行就是遍历每一个像素, 一个图像所有的像素的个数就是这个图像长乘宽,图像的长用img1.getwidth()获得,宽用img1.getheight()获得,因此所有的像素的个数就是img1.getwidth()*img1.getheight(),最后因为显示缓冲区从0开始,i的遍历范围就是0到img1.getwidth()*img1.getheight()-1。

下面一行的RGBtoHSL(BGR(d1[i]),&h,&s,&l);是将这个像素的颜色转换成HSL颜色,并将结果保存在h,s,l这3个变量里,那么BGR是干嘛的呢?上文说过,显示缓冲区中的每个点用BGR格式储存,与我们平时用的RGB是相反的,而BGR这个函数的作用正是交换B和R,因此要先转成RGB,再转成HSL。如果你了解过HSL,就会知道它是按色相、饱和度、亮度储存的,只要一个像素的亮度足够低,我们就可以认定它是黑色,下面的判断就是处理这件事的。其实也可以用RGB,只要判断它为0就可以了。但是在实际操作过程中会发现有些像素虽然不是黑色,但十分接近,肉眼看不出来,而且RGB的数值都是在个位数,如果不处理最后会有黑色的色块(我这个函数是针对png的,背景色比较统一,jpg更严重)

下面这两个判断是如果这个像素是黑色(或接近),就把它变成白色,也就是原本透明的部分,如果它不是白色,也就是它原本不是透明的,是主体,就把它变成黑色。一番操作下来,img(原图)是这样的:

img1是这样的:

那么 我们得到这两张图有什么用呢?这跟透明贴图也没关系啊?别急,我们只要将两张图放在一起,在加亿点东东:

putimage(x,y,&img1,SRCAND);
putimage(x,y,&img,SRCPAINT);

下面就是见证时刻的奇迹,啊呸,见证奇迹的时刻:

可以看到,我们的飞机摆脱了阴影的束缚,自由的在红色的屏幕上翱翔......那这是为什么呢? 以及那亿点东东又是什么意思?

那亿点东东就是三元光栅操作码,也就是位操作模式,如果你仔细观察,就会发现putimage的函数原型中有这样一个东西:

void putimage(int dstX,int dstY,IMAGE *pSrcImg,DWORD dwRop = SRCCOPY);

 没错,dwRop就是它,它的默认值SRCCOPY就是直接拷贝,目标图像 = 源图像。实际上,除了直接拷贝,还有很多种赋值方法,如SRCAND是目标图像 = 目标图像 AND 源图像,源图像是原来的图片,目标图像是背景,SRCAND就是让图片的每个像素和背景的每个像素做与运算,并将结果显示在背景上,SRCPAINT同理,只不过是或运算。这里有一些常用的:

含义
DSTINVERT目标图像 = NOT 目标图像
MERGECOPY目标图像 = 源图像 AND 当前填充颜色
MERGEPAINT目标图像 = 目标图像 OR (NOT 源图像)
NOTSRCCOPY目标图像 = NOT 源图像
NOTSRCERASE目标图像 = NOT (目标图像 OR 源图像)
PATCOPY目标图像 = 当前填充颜色
PATINVERT目标图像 = 目标图像 XOR 当前填充颜色
PATPAINT目标图像 = 目标图像 OR ((NOT 源图像) OR 当前填充颜色)
SRCAND目标图像 = 目标图像 AND 源图像
SRCCOPY目标图像 = 源图像
SRCERASE目标图像 = (NOT 目标图像) AND 源图像
SRCINVERT目标图像 = 目标图像 XOR 源图像
SRCPAINT目标图像 = 目标图像 OR 源图像

注:

  1. AND / OR / NOT / XOR 为布尔运算,分别是与(&)、或(|)、非(~)以及异或。
  2. "当前填充颜色"是指通过 setfillcolor 设置的用于当前填充的颜色。

全部的三元光栅操作码请参考这里但我不建议大家贸然阅读

by the way,第一个函数执行完,图像是这样的:

这是因为白色在二进制中全为1,与任何一个数做与运算结果还是那个数,而黑色在二进制中全为0,与任何一个数做与运算结果还是0。这样,我们就成功地把背景给去掉了。

执行完第二个函数,因为黑色在二进制中全为0,与任何一个数做或运算还是那个数。因此,原图中黑色的部分被忽略,原图中彩色的部分无视背景黑色的部分,到这里,我们就成功实现了透明贴图。(*^▽^*)

更多透明贴图及讲解见详解透明贴图和三元光栅操作

原创博文:DEV-C++ 安装EasyX

Linux编译运行c/c++代码(仅需控制台)-CSDN博客

  • 38
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值