Antialiasing in Qt's QGraphicsScene make overlapping lines darker - qt

When using anti-aliasing rendering in Qt's QGraphicsScene, there is a behavior that makes drawings appear not as expected: overlapping lines become darker. I could not see any description of this behavior in the documentation, and I cannot find a way to disable it.
For example if I want to draw such a polygon:
Because of the number of points, it is impossible not to have overlapping lines - fine. But because anti-aliasing is activated, some borders appear 'thicker' than others.
Is there any way to avoid this and have anti-aliased lines that can overlap and yet at the same time be rendered without getting darker?
I know of course that I can redefine the paint() function and draw manually individual lines that do not overlap, but this is what I want to avoid. I am using Pyside and this would significantly slow down the application, due to the high frequency at which paint() is being called.
EDIT Fixed by defining the object shape using QPainterPath / QGraphicsPathItem instead of QPolygon / QGraphicsPolygonItem. In that case the moveTo function allows to avoid lines that overlap.

Another thing you could try is adding half a pixel to your coordinates (not dimensions). This fixed the anti-aliasing issue for me.
XCoord = int(XValue) + 0.5
YCoord = int(XValue) + 0.5
Also make sure that before that you have integer pixel values.

Related

Why are the transparent pixels not blending correctly in WebGL

Result of my code:
Basically, what the issue is, the transparent part of my image are not blending correctly with what is drawn before it. I know I can do a
if(alpha<=0){discard;}
in the fragment shader, the only issue is I plan on having a ton of fragments and don't want the if statement for each fragment on mobile devices.
Here is my code related to alpha, and depth testing:
var gl = canvas.getContext("webgl2",
{
antialias : false,
alpha : false,
premultipliedAlpha: false,
}
);
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.GREATER);
Also, these are textured gl.POINTS I am drawing. If I change the order the two images are drawn in the buffer, the problem doesn't exist. They will be dynamically rotating during the program's runtime so this is not an option.
It's not clear what your issue is without more code but it looks like a depth test issue.
Assuming I understand correctly you're drawing 2 rectangles? If you draw the red one before the blue one then depending on how you have the depth test setup the blue one will fail the depth test when the X area is drawn.
You generally solve this by sorting what you draw, making sure to draw things further away first.
For a grid of "tiles" you can generally sort by walking the grid itself in the correct direction instead of "sorting"
On the other hand, if all of your transparency is 100% draw or not draw then discard has its advantages and you can draw front to back. The reason is because in that case drawing front to back, the pixel drawn (not discarded) by the red quad will be rejected when drawing the blue quad by the depth test. The depth test is usually optimized to happen before running the fragment shader for a certain pixel. If the depth test says the pixel will not be drawn then no reason to even run the fragment shader for that pixel, time saved. Unfortunately as soon as you have any transparency that is not 100% opaque or 100% transparent then you need to sort and draw back to front. Some of these issues are covered in this article
A few notes:
you mentioned mobile devices and you mentioned WebGL2 in your code sample. There is no WebGL2 on iOS
you said you're drawing with POINTS. The spec says only POINTS of 1 pixel in size are required. It looks like you're safe up to points of size 60 but to be safe it's generally best to draw with triangles as there are other isses with points
you might also be interested in sprites with depth

Drawing a grid efficiently with EaselJS StageGL

I would like to draw a grid on a canvas using EaselJS. I am using the new WebGL stage, StageGL.
A grid is basically N times of a horizontal line and M times of a vertical line.
I see multiple options:
Draw N+M lines as all different shapes (I am talking about EaselJS "Shape" instances), cache them (as WebGL needs rasters) and add them to the stage.
Draw 1 horizontal and 1 vertical line, cache them (as WebGL needs rasters) and somehow draw the same image in the stage
Draw a single shape which consists of N+M paths, cache it and add it to the stage.
Option #1 seems naive to me. They're all the same image, why drawing them to the cache N+M times?
Option #2 would solve the problem in option #1, but I don't know how to do it.
Option #3 results in a very large image. For N=50, M=50 and gridSpacing=50px, it would result in a 2500x2500 px image. I don't know if this is ideal.
Which one is the best approach?
Are there any other approaches? I don't think I am the first person who draws a grid :)
You can pretty easily cache a shape, and use the resulting cache (canvas) as the source for a Bitmap.
var shape = new createjs.Shape();
shape.graphics.drawStuff();
// Since shapes have no bounds, you will have to know the bounds based on what you draw:
shape.cache(x, y, w, h);
var bmp = new createjs.Bitmap(shape.cacheCanvas);
You can draw as many of these Bitmaps without any additional cost, since its the same source canvas/image. EaselJS StageGL (latest NEXT, released shortly hopefully) renders this in WebGL no problem.
Check out the SpriteSheetBuilder demo and docs in GitHub to draw content to a SpriteSheet/Sprite instead of a Bitmap.
Cheers.

How to make qt qgraphicsview scale to not affect stipple pattern?

I draw few rectangles inside the QGraphicsView ; I use custom stipple pattern for these by creating a QBrush with my QPixmap. This gets displayed with the default zoom level as expected.
When I call view->scale(), the rectangles show up bigger or smaller as I expected. However Qt has scaled the individual bits of the stipple pattern which is not expected; I expected it to draw the larger or smaller rectangle again with the brush.
Eg.
If I had used a stipple pattern with one pixel dot and pixel space, after zooming in, I want to see a larger rectangle but I want the same stipple pattern with same pixel gaps. Is this achievable somehow? Thanks.
I ran into the same problem while developing an EDA tool companion in Qt.
After some trying, what I did (and seems to work for me) is to create a custom graphics item. On the paint method, I do:
QBrush newBrush = brush_with_pattern;
newBrush.setTransform(QTransform(painter->worldTransform().inverted()));
painter->setBrush(newBrush);
That is to apply the inverse transformation of the item to the brush (so it does not scale).
I think that the setDashOffset is only for the border of the shapes (not the fill).
You may use QPen::setDashOffset:
http://harmattan-dev.nokia.com/docs/library/html/qt4/qpen.html#setDashOffset
You'll need to set the offset based on the scenes zoom/scale level. You can grab a pointer to the scene in your item by calling scene(), don't forget to check for NULL though since it will be NULL when not added to the scene (although you shouldn't in theory get a paint() when not in a scene).
The other option is to use:
http://doc.qt.digia.com/qt/qpainter.html#scale
To undo the views scaling on your painter.
In case anyone is still looking on this, a related question here regarding scaling of standard fill patterns instead of pixmap fill patterns may help. Basically, it may not be possible to modify scaling of standard fill patterns (a few workaround ideas are listed), but, working with alpha values instead gives the desired effect if you are looking for varying colors, especially gray levels - and is much less convoluted.

Qt/PyQt - frequently drawing pixmap to widget, partly not drawing correctly

I'm working on a Qt based application (actually in PyQt but I don't think that's relevant here), part of which involves plotting a potentially continuous stream of data onto a graph in real time.
I've implemented this by creating a class derived from QWidget which buffers incoming data, and plots the graph every 30ms (by default). In __init__(), a QPixmap is created, and on every tick of a QTimer, (1) the graph is shifted to the left by the number of pixels that the new data will take up, (2) a rectangle painted in the space, (3) the points plotted, and (4) update() called on the widget, as follows (cut down):
# Amount of pixels to scroll
scroll=penw*len(points)
# The first point is not plotted now, so don't shift the graph for it
if (self.firstPoint()):
scroll-=1
p=QtGui.QPainter(pm)
# Brush setup would be here...
pm.scroll(0-scroll, 0, scroll, 0, pm.width()-scroll, pm.height())
p.drawRect(pm.width()-scroll, 0, scroll, pm.height())
# pen setup etc happens here...
offset=scroll
for point in points:
yValNew = self.graphHeight - (self.scalePoint(point))
# Skip first point
if (not(self.firstPoint())):
p.drawLine(pm.width()-offset-penw, self.yVal, pm.width()-offset, yValNew)
self.yVal = yValNew
offset-=penw
self.update()
Finally, the paintEvent simply draws the pixmap onto the widget:
p = QtGui.QPainter(self)
p.drawPixmap(0, 0, self.graphPixmap)
As far as I can see, this should work correctly, however, when data is received very fast (i.e. the entire graph is being plotted on each tick), and the widget is larger than a certain size (approx 700px), everything to the left of the 700px area lags considerably. This is perhaps best demonstrated in this video: http://dl.dropbox.com/u/1362366/keep/Graph_bug.swf.html (the video is a bit laggy due to the low frame rate, but the effect is visible)
Any ideas what could be causing this or things I could try?
Thanks.
I'm not 100% sure if this is the problem or not, but I thought I might make at least some contribution.
self.update() is an asynchronous call, which will cause a paint event at some point later when the main event loop is reached again. So it makes me wonder if your drawing is having problems because of the sync issue between when you are modifying your pixmap vs when its actually getting used in the paintEvent. Almost seems like what you would need for this exact code to work is a lock in your paintEvent, but thats pretty naughty sounding.
For a quick test, you might try forcing the event loop to flush right after your call to update:
self.update()
QtGui.QApplication.processEvents()
Not sure that will fix it, but its worth a try.
This actually might be a proper situation to be using repaint() and causing a direct paint event, since you are doing an "animation" using a controlled framerate: self.repaint()
I noticed a similar question to yours, by someone trying to graph a heart monitor in real time: http://qt-project.org/forums/viewthread/10677
Maybe you could try restructuring your code similar to that. Instead of splitting the painting into two stages, he is using a QLabel as the display widget, setting the pixmap into the QLabel, and painting the entire graph immediately instead of relying on calls to the widget.update()

Coordinate scaling causes too large font sizes in QPainter::drawText

I'm working on simple 2D visualization module for MD simulation code. What I'm trying to do is drawing positions of simulated molecules using:
myPainter.drawEllipse(myQPoint,myRx,myRy)
And that part works pretty good on my visualization widget. The thing that happened to be a problem is writing text which should represent each molecule's ID (integer).
myPainter.drawText(myPosPoint,QString::number(mySoftMolecule2D->getID()));
It draws text but it is too large. This is probably because I need to use cooridantes scaling for myPainter to draw molecules easily.
myPainter.scale(myWidgetWidth_ / simSizeX_ , myWidgetHeight_ / simSizeY_);
// myWidgetWidth_ is much bigger simSizeX_
// myWidgetHeight_ is much bigger simSizeY_
I tried putting such lines before I perform scaling cooridnates in myPainter:
QFont myFont;
myFont.setPointSizeF(1.0); // values less than 1.0 doesn't work
myFont.setFamily("Courier");
myPainter.setFont(myFont);
but the molecules' label are still much too big.
Thanks in advance for any help.
Since you want to size a font based on a fixed pixel size, try using QFont::setPixelSize(int pixelSize) instead of setPointSize().
Remember the positions you want to draw the text at, then draw in two stages. The first is the molecules, the second the text. Before drawing the molecules, save the state of the painter, and restore it before drawing the text. This should prevent scaling of the text while allowing the molecules to be scaled.

Resources