用GDI绘制复杂透明图形的方法
acmilan2017/03/14软件综合 IP:四川

可以使用Windows 98加入的支持Alpha通道的三个API绘制复杂图形

GdiGradientFill:向HDC进行矩形或三角形填充,三角形填充类似Direct3D或OpenGL

GdiTransparentBlt:HDC之间进行抠色传送

GdiAlphaBlend:与其它HDC之间进行Alpha混合

大部分情况下并不需要GdiTransparentBlt,而比较多使用GdiGradientFill和GdiAlphaBlend。

注意颜色格式为预乘alpha的32位色,即在提交颜色之前要进行预乘:

<code class="language-cpp">red = red * alpha / 0xff;
green = green * alpha / 0xff;
blue = blue * alpha / 0xff;
</code>

GdiGradientFill比较特殊,颜色范围为0x0000到0xff00:

<code class="language-cpp">red = red * alpha / 0xff00;
green = green * alpha / 0xff00;
blue = blue * alpha / 0xff00;
</code>

这几个函数比较慢,为了提高效率,建议只在内存位图中进行GdiGradientFill和GdiAlphaBlend,最后一次BitBlt到设备。

gradientfill_alphablend.png

Windows NT还支持以下两个函数:

MaskBlt:带掩码的传送

PlgBlt:平行四边形传送,可以旋转和挤压,允许有掩码,但有掩码的情况下不能旋转和挤压。

如果以上函数全部使用,则程序只能支持Windows 2000以上操作系统。

所有操作系统都支持的传送:

BitBlt:按位传送

StretchBlt:带伸缩的传送

PatBlt:画刷传送


GdiGradientFill和GdiAlphaBlend示例代码:

<code class="language-cpp">	case WM_PAINT:
	{
		// 开始绘制
		hdc = BeginPaint(hWnd, &ps);

		// 得到客户区大小
		RECT rc;
		GetClientRect(hWnd, &rc);
		int width = rc.right - rc.left;
		int height = rc.bottom - rc.top;

		// 创建目标缓冲位图和位图设备
		HBITMAP htargetbmp = CreateCompatibleBitmap(hdc, width, height);
		HDC htargetdc = CreateCompatibleDC(hdc);
		SelectObject(htargetdc, htargetbmp);

		// 填充底色,此处也可以使用FillRect
		TRIVERTEX vrectfill[] = {
			{ 0, 0, 0xff00, 0x0000, 0x0000, 0xff00 },
			{ width, height, 0x0000, 0xff00, 0x0000, 0xff00 },
		};
		GRADIENT_RECT grrectfill[] = { { 0, 1 } };
		GdiGradientFill(htargetdc, vrectfill, 2, grrectfill, 1, GRADIENT_FILL_RECT_H);

		// 创建包含alpha通道的位图和位图设备
		// 我们要在这上边绘制一个透明的圆形
		HBITMAP halphabmp = CreateBitmap(width, height, 1, 32, NULL);
		HDC halphadc = CreateCompatibleDC(hdc);
		SelectObject(halphadc, halphabmp);
		
		// 透明圆形的颜色和位置参数
		// 颜色范围从0x0000到0xff00
		COLOR16 circle_red = 0, circle_green = 0, circle_blue = 0xff00, circle_alpha = 0x7f00;
		int circle_x = 100, circle_y = 100, circle_rad = 50;
		
		// 使用三角形绘制圆形
		// 进行alpha预乘
		circle_red = circle_red * circle_alpha / 0xff00;
		circle_green = circle_green * circle_alpha / 0xff00;
		circle_blue = circle_blue * circle_alpha / 0xff00;
		// 构造圆形的顶点:一个圆心和360个顶点
		TRIVERTEX vtri[361] = { {
				circle_x, circle_y, circle_red, circle_green, circle_blue, circle_alpha
			} };
		for (int i = 0; i < 360; i++)
		{
			vtri[i + 1].x = circle_x + circle_rad * cos(i / 360.0f * 2 * acos(-1));
			vtri[i + 1].y = circle_y + circle_rad * sin(i / 360.0f * 2 * acos(-1));
			vtri[i + 1].Red = circle_red;
			vtri[i + 1].Green = circle_green;
			vtri[i + 1].Blue = circle_blue;
			vtri[i + 1].Alpha = circle_alpha;
		}
		// 构造圆形的三角面:圆心和两个圆上的点
		GRADIENT_TRIANGLE gttri[360] = {};
		for (int i = 0; i < 360; i++)
		{
			gttri[i].Vertex1 = 0;
			gttri[i].Vertex2 = i + 1;
			gttri[i].Vertex3 = (i + 1) % 360 + 1;
		}
		// 绘制圆形
		GdiGradientFill(halphadc, vtri, 361, gttri, 360, GRADIENT_FILL_TRIANGLE);

		// 将包含alpha通道的圆混合到目标缓冲绘图
		// 然后删除相关设备和位图
		BLENDFUNCTION bfalpha = { AC_SRC_OVER, 0, 0xff, AC_SRC_ALPHA };
		GdiAlphaBlend(htargetdc, 0, 0, width, height, halphadc, 0, 0, width, height, bfalpha);
		DeleteDC(halphadc);
		DeleteObject(halphadc);

		// 将目标缓冲绘图传送到设备
		// 然后删除相关设备和位图
		BitBlt(hdc, 0, 0, width, height, htargetdc, 0, 0, SRCCOPY);
		DeleteDC(htargetdc);
		DeleteObject(htargetbmp);
		
		// 结束绘制
		EndPaint(hWnd, &ps);
		return 0;
	}
</code>

[修改于 7年9个月前 - 2017/03/14 04:40:33]

来自:计算机科学 / 软件综合
4
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
acmilan 作者
7年9个月前 修改于 7年9个月前 IP:四川
831802

GDI对32位alpha位图仅能进行COLORONCOLOR的缩放,也就是直接丢弃像素,不能实现HALFTONE缩放,也就没办法进行超采样除锯齿。

如果觉得GDI+比较慢,很难实现特殊效果的话,可以使用Direct2D/DirectWrite/WIC。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
7年9个月前 修改于 7年9个月前 IP:四川
831805

但是呢。。。Direct2D后来又走了错误的道路。。。

Direct2D 1.0倒是很好用,1.1就不行了,非得手工初始化D3D11然后绑上去,并且还要装Platform Update。。。

一个2D API搞得这么复杂让谁用。。。

加速图像处理常见的做法是BitBlt出来,进行运算,再BitBlt进去。如果CPU运算太慢,可以开SSE或AVX优化,或者直接调用D3D9做运算,面向高端的话也可以用C++AMP调用GPU。

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
7年9个月前 修改于 7年9个月前 IP:四川
831816
引用 glcolor:
可以试试这个开源库,也是国人写的:XXXXXXXXXXXXXXXXXX/skywind3000/BasicBitmap
中文blog页面:XXXXXXXXXXXXXyw……
感觉主要的性能瓶颈是内存位图合成而不是Blt,窗口绘制也就最后一步BitBlt。
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年10个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}