Render Image into subclassed QDeclarativeItem using OpenGL - qt

I am trying to perform several image manipulations using OpenGL ES 2.0 and display the output into a subclassed QDeclarativeItem which will then be used within my QML GUI.
I read through the answers found here: http://developer.qt.nokia.com/forums/viewthread/4109
and succeeded in drawing a red-to-blue colored rectangle within my QML GUI by overriding the paint() methode of the QDeclarativeItem:
void GLDeclarativeItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
painter->beginNativePainting();
glBegin(GL_QUADS);
glColor3ub(0,0,255);
glVertex2d(0, 0);
glVertex2d(0, height());
glColor3ub(255,0,0);
glVertex2d(width(), height());
glVertex2d(width(), 0);
glEnd();
painter->endNativePainting();
}
However, what I am trying to achieve is to draw the image which will be handled within my custom QGLWidget as the content of the above described QDeclarativeItem (instead of the red-to-blue colored content).
Within my custom QGLWidget I am using:
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
for drawing, which worked fine. However, I can not find the conversion between the drawing within my GLWidget and my GLDeclarativeItem. All the things I have tried so far just gave me a rectangle without any content at all.
Thanks for your help!

I did a similar thing, but solved differently: I used an overlay.
I simply put the OpenGL rendering in its own widget which is placed on top of the QDeclarativeView.
This works quite well, although you cannot draw ontop of the OpenGL rendering. If you should really need to, stack over another QML view with a translucent background.
Good luck.

Related

QGLWidget image disappears after resizing window

I am working on some software to display semitransparent images in a row.
It worked fine using glut functions for resizing etc. but then i decided to move to Qt for a GUI.
all glut functions are stripped and i use a QGLWidget to render my OpenGL stuff.
When i open the program, everything works as wanted but as soon as i resize the mainwindow (and thus the QGLWidget is being resized) the contents of my openGL widget disappear or appear cluttered.
Strangely though the widget is completely grey (my glclearcolor) when there is no cluttered render of my scene content.
This is how it should look like (after starting the program):
http://abload.de/img/okw0qh0.png
And this is how it looks like after resizing the mainwindow:
http://abload.de/img/errort1peu.png
(sometimes it is just grey)
my resizeGL function looks like this and gets called properly:
void prosta3dwidget::resizeGL(int width, int height)
{
if (height == 0) return; // To prevent divide by 0
GLfloat aspect = (GLfloat)width / (GLfloat)height; //get aspect ratio
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, aspect, 0.5f, 100.0f);
glViewport(0, 0, width, height);
update(); //redraw content
glMatrixMode(GL_MODELVIEW);
}
Okay, noticing that this must have something to do with the depth buffer (gluPerspective manual notes that depth buffer precision is affected by zNear and zFar values: https://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml )
i disabled the GL_DEPTH_TEST (uncommented glEnable(GL_DEPTH_TEST)) and everything seems to work as desired. yet i dont fully understand why.
problem solved, new mistery achieved.

How to efficiently change a very small part of a very large QPixmap?

Drawing on a widget in Qt is simple. First you create a QPixmap, assign a QPainter to it, draw with the QPainter, and at the end set the pixmap to the widget.
For example,
QPixmap pixmap(ui->label->width(), ui->label->height());
pixmap.fill(background_color);
QPainter painter(&pixmap)
painter.setpen(foreground_color);
//painter.drawLine(....) etc.
ui->label->setPixmap(pixmap);
So far so good.
However, what if the pixmap is very large, and I have to change a very small portion of it very often? In my case I profiled it to be my bottleneck. Instead of creating a new pixmap and copying the old onto it, we can load the existing pixmap and only make the necessary changes to it. However, it is also still very slow.
Pixmap pixmap = *(ui->label->pixmap()); // takes almost zero time
QPainter painter(&pixmap); // takes up 40% of the time
//draw stuff with painter // takes almost zero time
ui->label->setPixmap(pixmap); // takes up 60% of the time
Even if I re-use the painter so that I don't have to create it again every time, I still have to call setPixMap(pixmap), otherwise the image is not refreshed.
Is there a way around it?
Must I embed an OpenGL window or something similar, or is there a way with just using native Qt classes? Note, that the change in the image is very small and takes up an insignificant amount of time, it's the redrawing of the whole pixmap which is time-consuming. As I don't know beforehand where I'll be making the changes, splitting the pixmap up onto multiple small labels would be very complicated.
If you're using a large QLabel, then it would be better using a QGraphicsScene and QGraphicsView.
The scene allows you to add items, and the view is a widget which is like a window looking into the scene.
You could start by adding a QGraphicsPixmapItem, but I think you'd be better off deriving from QGraphicsItem, storing a QPixmap and rendering the image in its paint function.
Here's an example of the derived class header
class MyImage : public QGraphicsItem
{
public:
MyImage(QGraphicsItem* parent);
QRectF boundingRect();
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
protected:
void QGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
private:
QPixmap m_pixmap;
}
In the paint function, use the QPainter to draw m_pixmap;

how to draw text in QQuickItem

I've been searching the internet how to draw or render text on QQuickItem but to no avail. I opt not to use QQuickPaintedItem which uses QPainter paint() function. Aside from that there is also a known issue of QQuickPaintedItem on iOS retina display devices where the display is blurred and edges were not sharp.
Please advise any possible work-around on this.
Since QtDeclarative has been deprecated already, I opt not to use
QQuickPaintedItem which uses QPainter paint() function
That statement doesn't make a lot of sense. QtDeclarative is QtQuick1, QQuickPaintedItem is part of the QtQuick2 module and has nothing to do with QtDeclarative. Furthermore, even though it uses QPainter it is still hardware accelerated using OpenGL.
Overloading a custom QQuickItem to draw text in it manually, without assistance from QPainter or any other similar class will be a very complex task.
A QQuickItem is basically the class behind QML's Item element. QML also has a Text element. QML has been designed for rapid UI development, it is entirely pointless to draw the text manually. You don't need any C++ for this, only QML:
Item {
Text {
text: "something"
}
}
Take a look at the Text element and its properties, you can specify font, color and whatnot. You can directly use the element as a source for graphics effects too.
You can use QPainter to draw on a QImage like a canvas. Then you can turn this into a QSGTexture that can be assigned to a QSGSimpleTextureNode.
Here is an excerpt of code I recently wrote to do this:
QColor color("steelblue");
QRect rect(0,0,aw, ah);
for(auto ch: m_charList){
QImage canvas(rect.width(), rect.height(), QImage::Format_RGBA8888);
canvas.fill(QColor("transparent"));
QPainter painter(&canvas);
QFont font = painter.font();
//font.setPixelSize(48);
font.setPixelSize(rect.width());
font.setBold(true);
painter.setFont(font);
painter.setPen(color);
QRect bounding = QRect(0, 0, rect.width(), rect.height());
painter.drawText(0, 0, rect.width(), rect.height(), Qt::AlignCenter, ch, &bounding);
QSGTexture *texture = this->window()->createTextureFromImage(canvas);
m_textureList[ch] = texture;
}
The repo with the full working code in context is here.
There are MANY reasons to want to render like this. You can do rotations in 3D space to QSGNodes for one.

How do you create cool "mirror" effect for items displayed in a graphical GUI?

I have a QGraphicsScene and I add a couple of QGraphicsItem(s) to the scene. All the items being added to the scene are QGraphicsPixmapItem.
I want the output displayed scene to have a "mirror" visual effect for each item that is added to the scene. I would like the "mirror" visual effect to look a little something to the iTunes mirror affect when you display albums:
(NOTE: Image seen above is from "CoverFlow company website". CoverFlow are the people that I think implemented the iTunes album display "mirror" visual effect.)
Notice how each item in this scene has a mirror below it.
How do you create this "mirror" visual effect (seen in the screenshot) for each item?
I suppose this is how I would do it. I guess this is good for you as well according to the comment.
I would simply create the reflected mirrored image from the original using the QPainter and merge the two (using the QPainter again). This resulting image is the one shown using the QGraphicsPixmapItem. In the code below I created the reflected mirrored version of the image. All you need to do then is to tune the parameters and merge.
#include <QApplication>
#include <QLabel>
#include <QPixmap>
#include <QPainter>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// Just to show the original image.
QLabel label;
QImage original(<place_an_image_path_here>);
label.setPixmap(QPixmap::fromImage(original));
label.show();
// Create the gradient that will be placed over the image.
QPoint start(0, 0);
QPoint end(0, original.height());
QLinearGradient gradient(start, end);
gradient.setColorAt(0.0, Qt::white);
gradient.setColorAt(0.5, Qt::black);
// Create the mask to be used on the mirrored image.
QImage mask(original.size(), original.format());
QPainter painter(&mask);
// You may want to add additional opacity according
// to the sample image shown.
painter.setOpacity(0.8);
painter.fillRect(original.rect(), gradient);
painter.end();
// Create the mirrored reflection.
QImage reflection = original.mirrored();
reflection.setAlphaChannel(mask);
// Just to show the result.
QLabel labelReflection;
labelReflection.setPixmap(QPixmap::fromImage(reflection));
labelReflection.show();
return a.exec();
}
You can load the resulting image (the result of the merge of the two) in a QGraphicsPixmapItem and then you can go on applying all the transformations you need.
EDIT: I forgot that you may also want to set an additional opacity, as the image provided seems to do. I didn't try, but it might be possible to do the same using QPixmaps. This should improve performance or even allow, depending on the platform and on the paint engine you're using, accelerated painting.
EDIT2: As requested this is the output of my test code:
(I hope this image is not under some copyright or similar, I tried to check but nothing was written)

Optimized Line drawing in QT

I am new to QT. i am working on the Graphics.
i am using QWidget for drawing graphics(For drawing graphics in QWidget paint event). i need to draw background and foreground graphics. Background is fixed graphics. foregrounds i am drawing lines.
Each 100 millisecond i need to draw 20points. This drawing time is 8 sec. Total i need to draw 1600 points (total points represents the contentious line).
i am using QTimer to invoke this drawing in each 100ms. first few drawing drawn very fast. in the middle of the drawing it's become slow.
the problem is i need to draw all the foreground and background in each 100ms.
Please help me to fix the problem. if any one have sample code please provide. Thanks in advance.
Is there any way to draw only partial area ie. only particular modified region of the graphics?
QPainter-drawing can be very slow without hardware support. Using QGraphicsView won't help if all lines are visible, since it internally uses QPainter anyway.
If you just have to draw 20 new points (or lines) per update and per update background gets cleared so you have to render everything again, there are few things you could try:
1) Disable background autofill. See: QWidget::autoFillBackground
Add something like this to your widget init:
setAutoFillBackground(false);
setAttribute(Qt::WA_OpaquePaintEvent, true);
setAttribute(Qt::WA_NoSystemBackground, true);
Now on the first update render background and first lines. For next updates just skip rendering background and render only new lines.
2) Use double buffering. For example, create QImage of the size of your widget.
.h
private:
QImage m_targetImage;
.cpp
// constructor
m_targetImage = QImage(width(), height(), QImage::Format_ARGB32);
// paint event
// draw to image
QPainter p;
p.begin(&m_targetImage);
static bool firstUpdate = true;
if (firstUpdate)
{
// draw background)
p.drawImage(...);
firstUpdate = false;
}
// draw latest lines
p.drawLines(....);
p.end();
// draw image in widget paint
QPainter painter;
painter.begin(this);
painter.drawImage(0, 0, m_targetImage);
painter.end();
3) Use QGLWidget if possible. Inherit your widget from QGLWidget instead of QWidget. This method doesn't work on all platforms and speed increase might not be enough. Also using OpenGL brings all kind of new problems.

Resources