C#制作不规则窗体,如何使边缘平滑(抗锯齿)?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#制作不规则窗体,如何使边缘平滑(抗锯齿)?相关的知识,希望对你有一定的参考价值。
大家好,我想请教下,C#中,如何实现类似“搜狗拼音输入法”的皮肤更换,我要直接采用搜狗输入法的那些皮肤素材。
可是,如果单纯的把窗体弄成none,然后设置透明色,接着然后放图片,这样的出来的皮肤会有“锯齿”,请问怎么解决呢?(可是搜狗也使用这些图片的,为什么他们没有锯齿?)
如果要试用GDI,请问应该怎么做,请给出源码示例,求教,谢谢(拒绝使用第三方控件)
不想用太复杂的方法,或者有没有一个比较完善的“抗锯齿算法”,谢谢?
最近写个小程序,想让窗体自动隐藏,到csdn搜索,发现不少网友问这个问题,可是具体实现的例子不多,我经过琢磨,实现可记录停靠位置,可左上右三方停靠并隐藏。现将实现的例子拿出来供大家参考。 实现窗体自动隐藏方法很多,可以使用定时器,不断监视鼠标,当鼠标移动到窗体边缘时,显示窗体,当鼠标离开后隐藏窗体。也可以在鼠标收到WM_NCMOUSEMOVE或 WM_MOUSEMOVE(无边框窗体)时激活窗体,然后在窗体消息WM_ACTIVE中设置显示或隐藏,这种方法在窗体未失去焦点时不会隐藏。我在原本的设计中便使用这种方法,只是在设计时发现非主窗体不太合适,激活窗体时会出现两个键盘焦点,而且我所需要的焦点是虚假的,可能我的设计不当,那位朋友若能完美实现,不妨交流一下。
本代码的流程如下:
初始化窗体时设置窗体位置,并设置依靠状态窗体状态。
当接收到WM_MOUSEMOVE消息时,检查窗体是否显示,若否,显示,并打开定时器。
在WM_MOVING中检测窗体位置,并自动靠拢边界。
在定时器中检测鼠标,当鼠标离开窗体后,关闭定时器,隐藏窗体。 当然,在隐藏窗体时首先判断位置,若停靠在边缘,则隐藏,否则,不隐藏。
现在我们一步步看代码。
int alignType;//全局变量,用于记录窗体停靠状态
enum
ALIGN_NONE,//不停靠
ALIGN_TOP,//停靠上边
ALIGN_LEFT,//停靠左边
ALIGN_RIGHT//停靠右边
;
#define NEAR_SIZE 20//定义自动停靠有效距离
#define NEAR_SIDE 2//窗体隐藏后在屏幕上保留的像素,以使鼠标可以触及
/*
下面代码处理窗体消息WM_MOVING,pRect是由参数lParam传来的指针
*/
void OnMoving(HWND hWnd, LPRECT pRect)
//未靠边界由pRect测试
if (alignType == ALIGN_NONE)
if (pRect->top < NEAR_SIZE)//在上边有效距离内,自动靠拢。
alignType = ALIGN_TOP;
pRect->bottom -= pRect->top;
pRect->top = 0;
if (pRect->left < NEAR_SIZE)//在左边有效距离内
alignType = ALIGN_LEFT;
pRect->right -= pRect->left;
pRect->left = 0;
else if (pRect->right + NEAR_SIZE >
ScreenX)//在右边有效距离内,ScreenX为屏幕宽度,可由GetSystemMetrics(SM_CYSCREEN)得到。
alignType = ALIGN_RIGHT;
pRect->left += (ScreenX - pRect->right);
pRect->right = ScreenX;
else
//靠边界由鼠标测试
POINT pt;
GetCursorPos(&pt);
if (alignType == ALIGN_TOP)
if (pt.y >
NEAR_SIZE)//由于我们移动窗体时,鼠标在标题栏内,当鼠标位置超过有效距离后,我们可以考虑用户要向下拖动鼠标。我们便解除上部停靠。
alignType = ALIGN_NONE;
pRect->bottom += NEAR_SIZE;
pRect->top = NEAR_SIZE;
else
pRect->bottom -= pRect->top;
pRect->top = 0;
if (pRect->left < NEAR_SIZE)//在上部停靠时,我们也考虑左右边角。
pRect->right -= pRect->left;
pRect->left = 0;
else if (pRect->right + NEAR_SIZE > ScreenX)
pRect->left += (ScreenX - pRect->right);
pRect->right = ScreenX;
if (alignType == ALIGN_LEFT)
if (pt.x - pRect->right >
0)//鼠标可以在整个标题条来回移动,所以我们不能简单用左边界和鼠标的距离来解除停靠,这里我们在鼠标离开右边界时解除停靠。
alignType = ALIGN_NONE;
pRect->right += NEAR_SIZE;
pRect->left = NEAR_SIZE;
else
pRect->right -= pRect->left;
pRect->left = 0;
if (pRect->top < NEAR_SIZE)//考虑左上角。
pRect->bottom -= pRect->top;
pRect->top = 0;
else if (alignType == ALIGN_RIGHT)
if (pt.x < pRect->left)//当鼠标离开左边界时,解除停靠。
alignType = ALIGN_NONE;
pRect->left -= NEAR_SIZE;
pRect->right -= NEAR_SIZE;
else
pRect->left += (ScreenX - pRect->right);
pRect->right = ScreenX;
if (pRect->top < NEAR_SIZE)//考虑右上角。
pRect->bottom -= pRect->top;
pRect->top = 0;
/*
在窗体初始化是设定窗体状态,如果可以停靠,便停靠在边缘
我本想寻求其他方法来解决初始化,而不是为它专一寻求一个函数,可是,窗体初始化时不发送WM_MOVING消息,我不得不重复类似任务.
*/
void NearSide(HWND hWnd)
int change = 0;
RECT rect;
GetWindowRect(hWnd, &rect);
alignType = ALIGN_NONE;
if (rect.left < NEAR_SIZE)
alignType = ALIGN_LEFT;
if ((rect.left != 0) && rect.right != NEAR_SIDE)
rect.right -= rect.left;
rect.left = 0;
change = 1;
else if (rect.right > ScreenX - NEAR_SIZE)
alignType = ALIGN_RIGHT;
if (rect.right != ScreenX && rect.left != ScreenX - NEAR_SIDE)
rect.left += (ScreenX - rect.right);
rect.right = ScreenX;
change = 1;
//调整上下
if (rect.top < NEAR_SIZE)
alignType = ALIGN_TOP;
if (rect.top != 0 && rect.bottom != NEAR_SIDE)
rect.bottom -= rect.top;
rect.top = 0;
change = 1;
if (change)
MoveWindow(hWnd, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, TRUE);
/*
窗体的显示隐藏由该函数完成,参数hide决定显示还是隐藏.
*/
void HideSide(HWND hWnd, BOOL hide)
RECT rc;
int moves = 20;//动画滚动窗体的步数,如果你觉得不够平滑,可以增大该值.
int xStep, yStep;
int xEnd, yEnd;
int width;
int height;
register int i;
GetWindowRect(hWnd, &rc);
width = rc.right - rc.left;
height = rc.bottom - rc.top;
//下边判断窗体该如何移动,由停靠方式决定
switch (alignType)
case ALIGN_TOP:
//向上移藏
xStep = 0;
xEnd = rc.left;
if (hide)
yStep = -rc.bottom / moves;
yEnd = -height + NEAR_SIDE;
else
yStep = -rc.top / moves;
yEnd = 0;
break;
case ALIGN_LEFT:
//向左移藏
yStep = 0;
yEnd = rc.top;
if (hide)
xStep = -rc.right / moves;
xEnd = -width + NEAR_SIDE;
else
xStep = -rc.left / moves;
xEnd = 0;
break;
case ALIGN_RIGHT:
//向右移藏
yStep = 0;
yEnd = rc.top;
if (hide)
xStep = (ScreenX - rc.left) / moves;
xEnd = ScreenX - NEAR_SIDE;
else
xStep = (ScreenX - rc.right) / moves;
xEnd = ScreenX - width;
break;
default:
return;
//动画滚动窗体.
for (i = 0; i < moves; i++)
rc.left += xStep;
rc.top += yStep;
SetWindowPos(hWnd, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE |
SWP_NOSENDCHANGING);
RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
Sleep(5);
SetWindowPos(hWnd, NULL, xEnd, yEnd, 0, 0, SWP_NOSIZE);
if (!hide)//如果窗体已被显示,设置定时器.监视鼠标.
SetTimer(hWnd, WM_TIMER, 500, NULL);
/*
下面就是通过窗体回调函数将这些函数组织起来.
这里仅列出使用的消息
*/
case WM_TIMER://定时器消息
POINT pt;
RECT rc;
GetCursorPos(&pt);
GetWindowRect(hWnd, &rc);
if (!PtInRect(&rc, pt))//若鼠标不在窗体内,隐藏窗体.
KillTimer(hWnd, WM_TIMER);
HideSide(hWnd, TRUE);
break;
case WM_CREATE:
case WM_INITDIALOG: //初始化消息
SetWindowPos(…) //程序保存窗体上次靠位置,在这里恢复.
NearSide(hWnd);
break;
//这两个消息是在窗体移动开始时和结束时产生的,我们在窗体开始移动时关闭定时器,移动结束后再打开,这样避免窗体移动时隐藏,金山快译的浮动条就有这种情况出现.
case WM_ENTERSIZEMOVE:
KillTimer(hWnd, WM_TIMER);
break;
case WM_EXITSIZEMOVE:
SetTimer(hWnd, WM_TIMER, 500, NULL);
break;
case WM_MOUSEMOVE://受到窗体移动消息时,判断窗体是否显示,
RECT rc;
GetWindowRect(hWnd, &rc);
if (rc.left < 0 || rc.top < 0 || rc.right > ScreenX) //未显示
HideSide(hWnd, FALSE);
break;
case WM_MOVING://处理窗体移动时消息,实现自动停靠
OnMoving(hWnd, (LPRECT) lParam);
break;
case WM_MOVE:
//保存窗体位置
这些代码是从我的代码中摘录出来的,我已尽量检查它们的完整性,但人总有犯错的时候,如果你发现这些代码有问题,或有更好的建议,请联系我. 参考技术A 不用第三方类库?那你就只能直接操作显存了,这样是最高效的办法,就是稍微麻烦
一些。以前写图像识别的时候写过一个,当时我们用的都是C和汇编,C#的话你也可
以直接嵌入_asm ...... 操作显卡的端口寄存器,大致原理应该也就是直接
写屏,用图形方式在图形缓冲区里构建你需要构建的窗口或者控件的样式,比如你
需要窗口是带动画效果并且程不规则图形的,或者是直接以半透明不规则图片做成
的窗口,等等。
你所说的抗锯齿化是不是图形学里面的那些比如光栅化,抗锯齿化,过滤器,
Mini-Map,Filter....这些东西?这些是显卡驱动直接支持的,如果不使用第
三方库只能自己写算法,比如你所说的抗锯齿化,就要自己写算法去实现不规则窗口
的边缘或者部分区域的像素抖动(部分像素抖动的越快看起来越平滑),具体的底层算法你可以参考Opengl或者Mesa实现
源码里的算法;这是非常非常麻烦的,最省事儿的办法就是直接调用显卡驱动的API,
或者直接调用Opengl的API,不规则窗口里可以内嵌其他类型的窗口应用程序,
比如在窗口重绘的过程中指定内部子窗口的位置,这样看起来像是一个窗口,你完全
可以写一个不规则窗口,并且窗口里面运行OpenGl。 参考技术B 自定义界面推荐用WPF做
WPF的界面有XAML定义,界面与代码分离,你可以用反射加载XAML的方式渲染界面
对于图片边上的锯齿,你可以设置一个RGB区间的透明色,而不是单一的透明色
这样会有很大改善 参考技术C 汗..用得着这么复杂么?
搜狗拼音等类似的桌面应用软件的半透明以及边缘羽化都是可以直接通过图片实现的。
首先用PS等图像编辑工具创建带有alpha通道的png图片(就是看上去背景有黑白格那种)。
在渲染的时候调用带alpha通道的方法绘制即可(这是DX游戏引擎处理的方式)。
如果只是窗体应用程序,只需要用img或其他控件指定src即可。但要注意避免2张带透明通道的图片重合。因为窗体绘制引擎无法计算2张图片的透明度,故显示为粉红色。这点要注意。
还有其他问题,密我 参考技术D 窗体都是矩形的。。。
实现不规则,只是让某些部分变透明而已。。。
平滑的网页字体,性感的抗锯齿显示
Well known fix for ugly webfonts...
html { -webkit-font-smoothing: antialiased; }
以上是关于C#制作不规则窗体,如何使边缘平滑(抗锯齿)?的主要内容,如果未能解决你的问题,请参考以下文章