[Bug] Qt-based view fails to save screenshot on Windows7

This is an issue specific to Qt-based view widget, and specific to Windows 7 (perhaps Vista as well).

Reproducer:
- launch Qt sample from OCC package
- import a shape
- File/Export/Image, specify myfile.gif (.bmp or other)
- the image will be a plain gray rectangle

On Windows XP this should work fine.
On Windows 7 DRAW also works fine (pload ALL; box a 1 1 1; vinit; vdisplay a; vfit; vdump box.gif).

Based on limited investigation, I believe the root-cause is in the way Qt communicates with Windows through the handle (QWidget::winId()), window attributes ( setAttribute(Qt::WA_PaintOnScreen); setAttribute(Qt::WA_NoSystemBackground);) and alike. Seems like on Windows 7 the screen buffer does not update underlying widget buffer and when trying to export an image, the initial widget contents (that were created during QWidget creation) are just stored. The latter is true as in CAD Exchanger this bug is reproduced as storing not a gray rectangle but a fragment of a widget that was on the screen *right before* the OpenGL view was created.

Hope this helps.
Roman

Timo Roth's picture

We have the same problem with our OCCT-based application, but we don't use Qt. Our application is written in Delphi and displays OCC on a panel. OCC is used via a .NET-wrapper.

We tested it on some of our machines.
The problem occurs on:
- Win Vista, 64 bit
- Win 7, 64 bit

It does not occur on:
- Win 7, 32 bit
- Win XP, 32 & 64 bit

It also does not occur if we use a machine via RDP.

The following thread seems to be related:
http://www.opencascade.org/org/forum/thread_17723/

Roman Lygin's picture

Hi Timo,
Thanks for these additional details.
I am experiencing problems on Win 7, 64 bit (OCC itself is 32 bit). Works fine on XP, 32bit.

As I mentioned, dump in Draw works just fine on my Win7 machine:
Draw> pload ALL
Draw> box b 1 1 1
Draw> vinit
Draw> vdisplay b
Draw> vfit
Draw> vdump box.bmp

OCC folks told me that they are working on redesigning the code related to image processing in OCC. So hopefully this will be fixed some time.

Roman
---
opencascade.blogspot.com
www.cadexchanger.com

Patrik Mueller's picture

Hi Roman,

not sure if it helps you:

I'm using QGLFramebufferObjectFormat inside a QGLWidget to create screenshots.

Could that be an alternative solution for you?

Greets,

Patrik

Patrik Mueller's picture

Sorry - meant using QGLFramebufferObject ;-)

Roman Lygin's picture

Hi Patrik,

Thanks for the hint. Certainly it is possible to grab the QGLWidget contents. However OCC/Qt viewer does not use it - it simply maps OpenGL contents using window handle returned by QWidget.
I thought into this direction - to try to link OCC viewer with QGLWidget - however the OCC folks advised me against this saying there are troubles (possible). So I did not pursue that direction. Have you managed to do that or did you mean something different ?

Thanks anyway.
Roman
---
opencascade.blogspot.com
www.cadexchanger.com

Patrik Mueller's picture

Hi Roman,

yes - I use OCC V3d_Viewer with QGL. But I'm still a newbie with QT (coming from MFC) - so perhaps there are some problems but I don't have at the moment.

It would be nice to hear from OCC about these troubles so we could do some more tests.

Greets,

Patrik

Roman Lygin's picture

I seem to have managed to overcome the issue and the recently released CAD Exchanger 2.0.2 does not have the initial problem.
However I view this more of a hack and don't fully understand how it works, so please use at your own risk ;-).

I have subclassed the QtOCC view class from QGLWidget (not QWidget) and used QGLWidget::grabFrameBuffer().
Here are the code excerpts:

class QOLib_EXPORT QOOcc_View3d : public /*QWidget*/ QGLWidget
{
...
};

/*! \class QOOcc_View3d
...
QOOcc_View3d inherits QGLWidget (not plain QWidget) to use its grabeFrameBuffer() in
order to save screenshot. This seems to be an ugly work-around as OCC and Qt still use
different contexts but for some magic reason Qt's buffer gets filled by OCC's.
*/

QOOcc_View3d::QOOcc_View3d(const Handle(AIS_InteractiveContext)& theAISContext,
QWidget *theParent,
Qt::WindowFlags theFlags) : QGLWidget (QGLFormat (
//QGL::SampleBuffers - from Qt's Hello GL example - fails
//from http://www.opencascade.org/org/forum/thread_15794:
QGL::DoubleBuffer|QGL::DirectRendering|QGL::Rgba|QGL::NoStencilBuffer
),
theParent, 0 /*shareWidget*/,theFlags) /*QWidget (theParent, theFlags)*/,
...

void QOOcc_View3d::DumpView (const Standard_CString theFileName)
{
QImage anImage = this->grabFrameBuffer();
anImage.save (theFileName);
}

We had a few fruitful email exchanges with Sergey Anikin from the OCC team and he had some good insights into the issue, and was going to follow up on this thread. He reminded me one from a while ago - http://www.opencascade.org/org/forum/thread_15794 - from which I grabbed QGLWidget flags mentioned by Francois Lauzon.

So, thanks to all who participated in this and prior threads. Hope this helps.
Roman
---
opencascade.blogspot.com
www.cadexchanger.com

JuryS's picture

Hi everybody ! I think that this problem with OpenGL module.
I found something on this page:
http://pyopengl.sourceforge.net/documentation/manual/glRenderMode.3G.xml

There is problem with call_togl_redraw in opengl library, because when I try to use redraw buffer is empty or some errors.
glRenderMode(GL_FEEDBACK) return integer value 0;
there is no GL_INVALID_ENUM or GL_INVALID_OPERATION operation, but I think that FeedbackBuffer is Empty after call_togl_redraw calling.

JuryS's picture

Also this problem on WinXP 32 bit with new version of Intel chipset like G31/G33. I test this now and when I'm insert the Nvidia card in my comp, I have no this problem. I think that newest version of Win or Linux have newest drivers and method redraw is not working.

JuryS's picture

I don't know correctly what is don't working, but with next realization of call_togl_redraw export image like *.PDF, *.WMF, *.EMF, *.SVG, *.PS and *.EPS working cool on Win7 / WinXP 32 and Linux with newest Video CARD;

void EXPORT
call_togl_redraw
(
CALL_DEF_VIEW * aview,
CALL_DEF_LAYER * anunderlayer,
CALL_DEF_LAYER * anoverlayer
)
{
call_func_redraw_all_structs_begin (aview->WsId);
call_func_redraw_all_structs_proc (aview->WsId);
call_func_redraw_all_structs_end (aview->WsId, 1);
return;
}