VincentWei / MiniGUI

A modern and mature cross-platform window system for embedded systems and smart IoT devices.
http://www.minigui.com
GNU General Public License v3.0
676 stars 157 forks source link

关于开启窗口双缓存导致摄像头视频预览失效的问题 #92

Closed htk719809837 closed 1 year ago

htk719809837 commented 1 year ago

使用的rel-5.0的代码
你好,由于我们部分界面需要调用摄像头预览,所以需要在显示视频的地方设置一个透明像素,但是如果开启窗口的双缓存,我们设置的透明区域就失效,实际显示的是上一个未刷新的页面;如果不开启双缓存,显示视频的地方则正常。但是因为双缓存对于刷新很重要,所以希望官方尽快给一个解决方案,支持开启双缓存的窗口可以挖出一个透明区域。使用的显示引擎是最新的fbcon(打开新更新的fb引擎的双缓存),这个问题在3.0里面也存在

部分代码如下: 创建窗口时,设置窗口的双缓存属性。 stDlg.dwStyle = WS_VISIBLE; stDlg.dwExStyle = WS_EX_USEPARENTRDR | WS_EX_AUTOSECONDARYDC;

在paint里面 设置透明色的刷子,刷上我们要显示视频的区域 SetBrushColor(hdc, RGBA2Pixel(HDC_SCREEN,0x00, 0x00, 0x00,0x00)); FillBox(hdc, x, y,w, h);

VincentWei commented 1 year ago

如果设备屏幕是通过 Alpha 分量来设置像素是否透明的,则该问题应该是由于 fbcon 的像素格式设置不正确而导致的。

假定 fbcon 引擎的像素格式是 32 位色,如果内核的 fbcon 驱动程序没有提供正确的 Alpha 分量信息,则我们在程序中设置的 Alpha 分量就会被忽略而始终当做 0xFF 处理,这样通过 RGBA2Pixel 设置的透明分量就不会起效。在早先的版本中,Alpha 分量被忽略,这恰好让这段代码可以获得预期的效果。

所以,首先要确保 fbcon 引擎从内核中获取了正确的 alpha 分量信息。如果 alpha 分量信息是正确的,则要看创建窗口双缓冲区时创建的缓冲区是否具有和屏幕一样的像素格式。

如果设备屏幕通过 Alpha 通道来支持透明,则可以使用 gvfbpc_xvfb 图形引擎来模拟这种情形。在 MiniGUI.cfg 文件中,设置 pc_xvfb.defaultmode 的值为 800x600-32bpp.rgba32 即可开启对 Alpha 分量的支持。如果此时使用上面的代码把屏幕特定区域设置为透明色,会看到灰白相间的网格背景。

也就是说,如果使用 gvfbpc_xvfb 图形引擎可以达到正常的透明效果,那问题大概率出现在内核 fb 驱动程序返回给 fbcon 引擎的像素格式信息上。我见过很多驱动程序返回的信息不正确。

htk719809837 commented 1 year ago

目前我们设备不可以跑除了fb和commlcd之外的引擎,commlcd也存在这个问题, 但是只要窗口不开双缓存,视频预览就没有问题的。 目前我们fb驱动程序返回的数据确实不对,我们在fbvideo.c里是这样写的: if (ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) { //GAL_SetError("NEWGAL>FBCON: Couldn't get console pixel format\n"); //FB_VideoQuit(this); //return(-1); vinfo.bits_per_pixel = 32; vinfo.red.msb_right = 0; vinfo.red.length = 8; vinfo.red.offset = 16; vinfo.green.msb_right = 0; vinfo.green.length = 8; vinfo.green.offset = 8; vinfo.blue.msb_right = 0; vinfo.blue.length = 8; vinfo.blue.offset = 0; vinfo.transp.msb_right = 0; vinfo.transp.length = 8; vinfo.transp.offset = 24; //end
}

上面是否有问题呢? 或者说不用其他图形引擎有没有办法找到问题呢,毕竟窗口创建不开双缓存是不会出现问题的

htk719809837 commented 1 year ago

或者说fb的透明值如果底层驱动没有的话,有没有办法加进去呢

VincentWei commented 1 year ago

毕竟窗口创建不开双缓存是不会出现问题的

之所以开双缓冲区就会出问题,是因为双缓冲区多了一个 BitBlt 调用,如果像素格式不正确,就会在绘制的时候丢掉 Alpha 信息,而不开双缓冲区的时候,直接操作的是屏幕缓冲区,就不会暴露这个问题。

或者说fb的透明值如果底层驱动没有的话,有没有办法加进去呢

如果不能改驱动,就只能改 fbvideo.c 文件,按照正确的像素格式设置 RGBAmask 值,跳过从 fb 驱动中取信息的代码。

htk719809837 commented 1 year ago
if (ioctl(console_fd, FBIOGET_VSCREENINFO, &vinfo) < 0) {
    //GAL_SetError("NEWGAL>FBCON: Couldn't get console pixel format\n");
    //FB_VideoQuit(this);
    //return(-1);
    vinfo.bits_per_pixel = 32;
    vinfo.red.msb_right = 0;
    vinfo.red.length = 8;
    vinfo.red.offset = 16;
    vinfo.green.msb_right = 0;
    vinfo.green.length = 8;
    vinfo.green.offset = 8;
    vinfo.blue.msb_right = 0;
    vinfo.blue.length = 8;
    vinfo.blue.offset = 0;
    vinfo.transp.msb_right = 0;
    vinfo.transp.length = 8;
    vinfo.transp.offset = 24;
  //end   
}
vformat->BitsPerPixel = vinfo.bits_per_pixel;
if (vformat->BitsPerPixel < 8) {
    vformat->MSBLeft = !(vinfo.red.msb_right);
    return 0;
}
for (i=0; i<vinfo.red.length; ++i) {
    vformat->Rmask <<= 1;
    vformat->Rmask |= (0x00000001<<vinfo.red.offset);
}
for (i=0; i<vinfo.green.length; ++i) {
    vformat->Gmask <<= 1;
    vformat->Gmask |= (0x00000001<<vinfo.green.offset);
}
for (i=0; i<vinfo.blue.length; ++i) {
    vformat->Bmask <<= 1;
    vformat->Bmask |= (0x00000001<<vinfo.blue.offset);
}
for (i=0; i<vinfo.transp.length; ++i) {
    vformat->Amask <<= 1;
    vformat->Amask |= (0x00000001<<vinfo.transp.offset);
}
saved_vinfo = vinfo;

这一块的设置好像没问题呀,可以指导一下具体怎么设置吗

VincentWei commented 1 year ago

这样,您换个思路,把这个问题绕过去吧:

  1. 创建一个主窗口位于需要透明的区域上。
  2. 通过设置 WS_EX_WINTYPE_TOOLTIP 扩展风格,使之保持在最顶。
  3. 通过设置 WS_EX_TOOLWINDOW 扩展风格,使之不会获得输入焦点。
  4. 不要对这个主窗口使用双缓冲区,只要设置其背景色为像素值 0 即可,也不需要处理 MSG_PAINT 消息。

这样就可以通过这个始终存在的窗口创建一个透明的区域,而在其他主窗口中无需做任何额外的处理。

VincentWei commented 1 year ago

我用 gvfb 测试了一下,知道问题在哪儿了。之前的方案,做如下改动应该即可:

SetMemDCAlpha(hdc, 0, 0);  // 移除默认设置的 SRCPIXELAPLPHA 标志。
SetBrushColor(hdc, RGBA2Pixel(HDC_SCREEN,0x00, 0x00, 0x00,0x00));
FillBox(hdc, x, y,w, h);

创建辅助缓冲区时,如果像素格式带有 Alpha 分量,就会自动设置上 SRCPIXELALPHA 标志,这个标志会让辅助缓冲区中的 Alpha 分量在执行 BitBlt 时生效,其效果相当于和屏幕上已有的像素做了一次 Alpha 混合运算。为避免这个情况的发生,调用 SetMemDCAlpha(hdc, 0, 0); 移除默认设置的 SRCPIXELAPLPHA 标志即可。

不过要说明的是,上边给出的通过一个特别的主窗口创建透明区的方案,仍然是优选方案。

htk719809837 commented 1 year ago

可以使用,非常感谢。优选方案考虑到我们部分场景还需要再最顶层窗口叠加控件,暂时不去使用,谢谢!!!!