Blue Blazer

Achieve perfection by constant effort and creative will.
Code every day, no matter what...

使用Xrender加速异形窗体的绘制

异形窗口一般是采用图片作背景,形状不是规则的方形窗口。
不少程序为了追求漂亮的外观都会实现它,如windows media player等软件。
而图像的Alpha值是和透明度相关的,采用部分半透明的背景能够能加提升视觉效果。

在Qt中实现异形窗体一般采用setMask(),
实现透明也有setWindowOpacity(),
但这并不是最高效的方式。

而直接使用窗口管理器的接口来设置背景透明度却不能得到一个各部分透明度不同的控件(窗口)背景。

直接与Xlib打交道是一种解决方法。

其实Qt提供了一个简便的方法来供您使用,
如果你有支持复合(Composite)的窗口管理器,如(compiz或者kwin),
就可以去除不必要的控件闪烁,并获得显著的性能提升。

首先要这样初始化你的程序。

int main(int argc, char *argv[]) {
bool argbVisual=false;
Display *dpy = XOpenDisplay(0); // 打开显示接口
if (!dpy) {
qWarning("Cannot connect to the X server");
exit(1);
}

int screen = DefaultScreen(dpy);
Colormap colormap = 0;
Visual *visual = 0;
int eventBase, errorBase;

if (XRenderQueryExtension(dpy, &eventBase, &errorBase)) {
int nvi;
XVisualInfo templ;
templ.screen = screen;
templ.depth = 32;
templ.c_class = TrueColor;
XVisualInfo *xvi = XGetVisualInfo(dpy, VisualScreenMask
VisualDepthMask
VisualClassMask, &templ, &nvi);

for (int i = 0; i < style="color: rgb(255, 0, 0);"> XRenderPictFormat *format = XRenderFindVisualFormat(dpy,
xvi[i].visual);
if (format->type == PictTypeDirect && format->direct.alphaMask) {
visual = xvi[i].visual;
colormap = XCreateColormap(dpy, RootWindow(dpy, screen),
visual, AllocNone);
argbVisual=true;
break;
}
}
}
if (argbVisual == true) {
qWarning("Found ARGB visual. Starting app...");
}
else {
qWarning("Couldn't find ARGB visual... Exiting.");
}


QApplication app(dpy, argc, argv,
Qt::HANDLE(visual), Qt::HANDLE(colormap)); // 最关键的一行
return app.exec();
}

然后在你要渲染的Widget的paintEvent里面这样做:

// 也可以用png,这里用的是svg格式背景
QSvgRender *render = new QSvgRenderer(QLatin1String("my_background.svg"));

void Widget::paintEvent(QPaintEvent *e)
{
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
p.setClipRect(e->rect());

p.save();
// QPainter使用混合扩展模式中的源覆盖模式,很重要!不然会得到白色背景
p.setCompositionMode(QPainter::CompositionMode_Source);
// 使用“透明”颜色来填充背景
p.fillRect(rect(), Qt::transparent);
p.restore();

//也可以不用一个QPixmap cache,直接在Widget上画,Qt有Backing store(类似于double buffer)来避免闪烁,使用cache好处是只需要在resizeEvent()那里更新cache就好了。
cache.fill(Qt::transparent);
QPainter p(&cache);
p.setRenderHint(QPainter::Antialiasing);
renderer->render(&p);

p.end();

p.drawPixmap(0, 0, cache);
if (iconShown) {
p.drawPixmap(20, 20, icon);
}

}

现在的Widget就是一个以my_background.svg为背景,各个部分有不同透明度的控件了,
并且渲染得到了显卡的加速:)

需要更详细完善的例子,可以参考KRunner的源代码:

http://websvn.kde.org/trunk/KDE/kdebase/workspace/krunner/krunnerapp.cpp?view=markup
http://websvn.kde.org/trunk/KDE/kdebase/workspace/krunner/krunnerdialog.cpp?view=markup

0 comments: