Qt round rectangle, why corners are different? - c++

I try to draw a round rectangle with drawRoundedRect method directly in a QPixmap (no render engine involve here exept pure Qt one ...), I double check the size of the rectangle versus the size of my pixmap :
Pixmap : QSize(50, 73)
Rectangle: QRect(0,0 48x11)
See plenty of space ...
EDIT: some code
pixmap = QPixmap(50,73); //example size that match my case
QRectF rect(0,0,48,11);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::TextAntialiasing);
painter.setWorldMatrixEnabled(false);
painter.setPen(QPen()); //no pen
painter.setBrush(QBrush(color));
painter.drawRoundedRect(rect, 2.0, 2.0);
I disabled world transformation ...
I set set transformation to unity ...
I tried several radius (1.0,2.0,3.0,4.0) ...
I change pen width, brush color ...
But it always ends with a rectamgle with 4 diferent corners ! Like that :
I directly ouptut the pixmap to a file to be sure I wasn't scraping it during the display ... same shape.
Anyone know about Qt round rectangle with small radius ? I saw somthing about it a long time ago but I don't remenber how to deal with it !

It looks like you're not using anti-aliasing (i.e. the QPainter::Antialiasing render hint). This is a Qt quirk that occurs without it. From what I've seen/heard, the Qt devs aren't terribly concerned with fixing this (most people want anti-aliasing anyway).
The work-around (besides just using anti-aliasing) is to draw the rect yourself with QPainter::drawLine() and QPainter::drawArc(). You might have to play with numbers until it looks right -- straight calculations tend to come out a pixel or two off. Also, you might find that even with this method the lower right corner is never exactly the same as the other corners.
If you're feeling mildly ambitious, you could try fixing this and submitting a patch to Qt.
Update: Arc drawing results changed in Qt 5. In my experience, it's a big improvement.

I know this is an old problem but for Qt5 users calling setRenderHint(QPainter::Qt4CompatiblePainting); on the QPainter seems to solve the problem.
Edit:
I found a solution for generating a perfect rounded rectangle together with border color and it looks the same as the rounded rectangles used by QPushButton's border for example. This is how I implemented the paintEvent to achieve this:
void MyButtonGroup::paintEvent(QPaintEvent * e)
{
int borderSize = 5;
QColor borderColor = Qt::red;
QColor backgroundColor = Qt::blue;
int borderRadius = 3;
QPen pen;
pen.setWidth(borderSize);
pen.setColor(borderColor);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(pen);
QRectF rect(rect().x() + borderSize / 2,
rect().y() + borderSize / 2,
rect().width() - borderSize,
rect().height() - borderSize);
if(borderSize % 2 == 0)
{
painter.drawRoundedRect(rect,
borderSize,
borderSize);
}
else
{
painter.drawRoundedRect(rect.translated(0.5, 0.5),
borderRadius,
borderRadius);
}
QBrush brush(backgroundColor);
pen.setBrush(brush);
painter.setBrush(brush);
if(borderSize % 2 == 0)
{
painter.drawRoundedRect(rect,
borderRadius,
borderRadius);
}
else
{
painter.drawRoundedRect(rect.translated(0.5, 0.5),
borderRadius,
borderRadius);
}
QWidget::paintEvent(e);
}
I'm posting this because I found it a bit hard to achieve this result:

Try adding half a pixel offset (e.g.: rect.translated(0.5,0.5) ):
QRectF rect(0,0,48,11);
painter.setRenderHint(QPainter::Antialiasing,false);
painter.drawRoundedRect( rect.translated(0.5,0.5), 2.0, 2.0 );
I suppose this has to do with the coordinate system placing an integer value between two pixels.
If you draw with antialiasing and use a pen of 1 pixel width then drawing at exact integer coordinates results in lines of 2 pixel width instead.
Only with this 0.5 pixel offset you'll get lines that are exactly 1 pixel wide.
QRectF rect(0,0,48,11);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setBrush(Qt::NoBrush);
painter.setPen( Qt::white );
painter.drawRoundedRect( rect.translated(0.5,0.5), 2.0,2.0 );

Best way do draw RoundRect is Path.
http://developer.nokia.com/community/wiki/Qt_rounded_rect_widget
void fillRoundRect(QPainter& painter, QRect r, int radius)
{
painter.setRenderHint(QPainter::Antialiasing,true);
QPainterPath rounded_rect;
rounded_rect.addRoundRect(r, radius, radius);
painter.setClipPath(rounded_rect);
painter.fillPath(rounded_rect,painter.brush());
painter.drawPath(rounded_rect);
}

try to play with render hints
1) disable antiAliasing;
2) enable SmoothPixmapTransform
but still no guarantee that it will help.

I have tried all tips from answers here but nothing works for me. But based on these code snippets I have found following solution:
As default set m_pPainter->setRenderHint(QPainter::Qt4CompatiblePainting, true) and only for rounded rectangles with width%2==0 disable it.
QRect rect = ConvertRectangle(rectangle);
int nPenWidth = m_pPainter->pen().width();
if ( nPenWidth % 2 == 0 )
m_pPainter->setRenderHint(QPainter::Qt4CompatiblePainting, false);
m_pPainter->drawRoundedRect(rect, dbRadiusX, dbRadiusY);
if ( nPenWidth % 2 == 0 )
m_pPainter->setRenderHint(QPainter::Qt4CompatiblePainting, true);

Related

QPainter rotation prevents correct QPixmap rendering

Reported to Qt as a bug: https://bugreports.qt.io/browse/QTBUG-93475
I am re-drawing a QPixmap multiple times in different locations, with differnt rotations by transforming the QPainter. In certain situations the QPixmap is not drawing correctly. The GIF below shows my initial discovery of this issue with a QPixmap containing a green cylinder, notice how the rendering behaves as expected to the left of the GIF, but there is a boundary beyond which the rendering is incorrect. The QPixmap content appears to stick in place, and the pixels at the edge appear to smear out accross the rest of the pixmap. In the GIF there is a magenta background to the QPixmap, this because the targetRect used by QPainter::drawPixmap() is also beuing used to seperately fill a rectangle underneath the pixmap, this was because I wanted to check that the target rect was being computed correctly.
Minimum reproducable example:
To keep things simple I am simply filling the QPixmap with magenta pixels, with a 1 pixel wide transparent edge so that the smearing causes the pixmaps to dissapear completely. It doesn't show the image "sticking" in place but it clearly shows the boundary as beyond it the pixmaps seem to dissapear.
I have been experimenting with this myself and I believe this to be entirely caused by the rotating of the QPainter.
The angle of rotation seems to have an effect, if all of the pixmaps are rotated to the same angle then the boundary changes from a fuzzy diagonal line (where fuzzy means the boundary for dissapearing is different for each pixmap) to a sharp 90 degree corner (where sharp means that the boundary for dissapearing is the same for all pixmaps).
The range of different angles also seems to play a part, if the randomly generated angles are in a small 10 degree range, then the boundary is just a slightly fuzzier right angle, with a bevelled corner. There seems to be a progression from sharp right angle to fuzzy diagonal line as the number of different rotations is applied.
Code
QtTestBed/pro:
QT += widgets
CONFIG += c++17
CONFIG -= app_bundle
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
MainWindow.cpp \
main.cpp
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
MainWindow.h
main.cpp:
#include "MainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
MainWindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QWidget>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QPainter>
#include <random>
class MainWindow : public QWidget {
Q_OBJECT
public:
MainWindow();
void wheelEvent(QWheelEvent* event) override;
void mouseReleaseEvent(QMouseEvent* /*event*/) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void resizeEvent(QResizeEvent* /*event*/) override;
void paintEvent(QPaintEvent* event) override;
private:
struct PixmapLocation {
QPointF location_;
qreal rotation_;
qreal radius_;
};
QPixmap pixmap_;
std::vector<PixmapLocation> drawLocations_;
qreal panX_ = 0.0;
qreal panY_ = 0.0;
qreal scale_ = 1.0;
bool dragging_ = false;
qreal dragX_ = 0.0;
qreal dragY_ = 0.0;
QPointF transformWindowToSimCoords(const QPointF& local) const;
QPointF transformSimToWindowCoords(const QPointF& sim) const;
static qreal randomNumber(qreal min, qreal max);
};
#endif // MAINWINDOW_H
MainWindow.cpp:
#include "MainWindow.h"
MainWindow::MainWindow()
: pixmap_(30, 50)
{
setAutoFillBackground(true);
constexpr int count = 10000;
constexpr qreal area = 10000.0;
constexpr qreal size = 44.0;
for (int i = 0; i < count; ++i) {
// qreal rotation = 0.0; // No rotation fixes the issue
// qreal rotation = 360.0; // No rotation fixes the issue
// qreal rotation = 180.0; // Mirroring also fixes the issue
// qreal rotation = 90.0; // The boundary is now a corner, and has a sharp edge (i.e. all images dissapear at the same point)
// qreal rotation = 0.1; // The boundary is now a corner, and has a sharp edge (i.e. all images dissapear at the same point)
// qreal rotation = randomNumber(0.0, 10.0); // The boundary is still a corner, with a bevel, with a fuzzy edge (i.e. not all images dissapear at the same point)
qreal rotation = randomNumber(0.0, 360.0); // The boundary appears to be a diagonal line with a fuzzy edge (i.e. not all images dissapear at the same point)
drawLocations_.push_back(PixmapLocation{ QPointF(randomNumber(-area, area), randomNumber(-area, area)), rotation, size });
}
// Make edges transparent (the middle will be drawn over)
pixmap_.fill(QColor::fromRgba(0x000000FF));
/*
* Fill with magenta almost up to the edge
*
* The transparent edge is required to see the effect, the misdrawn pixmaps
* appear to be a smear of the edge closest to the boundary between proper
* rendering and misrendering. If the pixmap is a solid block of colour then
* the effect is masked by the fact that the smeared edge looks the same as
* the correctly drawn pixmap.
*/
QPainter p(&pixmap_);
p.setPen(Qt::NoPen);
constexpr int inset = 1;
p.fillRect(pixmap_.rect().adjusted(inset, inset, -inset, -inset), Qt::magenta);
update();
}
void MainWindow::wheelEvent(QWheelEvent* event)
{
double d = 1.0 + (0.001 * double(event->angleDelta().y()));
scale_ *= d;
update();
}
void MainWindow::mouseReleaseEvent(QMouseEvent*)
{
dragging_ = false;
}
void MainWindow::mousePressEvent(QMouseEvent* event)
{
dragging_ = true;
dragX_ = event->pos().x();
dragY_ = event->pos().y();
}
void MainWindow::mouseMoveEvent(QMouseEvent* event)
{
if (dragging_) {
panX_ += ((event->pos().x() - dragX_) / scale_);
panY_ += ((event->pos().y() - dragY_) / scale_);
dragX_ = event->pos().x();
dragY_ = event->pos().y();
update();
}
}
void MainWindow::resizeEvent(QResizeEvent*)
{
update();
}
void MainWindow::paintEvent(QPaintEvent* event)
{
QPainter paint(this);
paint.setClipRegion(event->region());
paint.translate(width() / 2, height() / 2);
paint.scale(scale_, scale_);
paint.translate(panX_, panY_);
for (const PixmapLocation& entity : drawLocations_) {
paint.save();
QPointF centre = entity.location_;
const qreal scale = (entity.radius_ * 2) / std::max(pixmap_.width(), pixmap_.height());
QRectF targetRect(QPointF(0, 0), pixmap_.size() * scale);
targetRect.translate(centre - QPointF(targetRect.width() / 2, targetRect.height() / 2));
// Rotate our pixmap
paint.translate(centre);
paint.rotate(entity.rotation_);
paint.translate(-centre);
// paint.setClipping(false); // This doesn't fix it so it isn't clipping
paint.drawPixmap(targetRect, pixmap_, QRectF(pixmap_.rect()));
// paint.setClipping(true); // This doesn't fix it so it isn't clipping
paint.restore();
}
}
QPointF MainWindow::transformWindowToSimCoords(const QPointF& local) const
{
qreal x = local.x();
qreal y = local.y();
// Sim is centred on screen
x -= (width() / 2);
y -= (height() / 2);
// Sim is scaled
x /= scale_;
y /= scale_;
// Sim is transformed
x -= panX_;
y -= panY_;
return { x, y };
}
QPointF MainWindow::transformSimToWindowCoords(const QPointF& sim) const
{
qreal x = sim.x();
qreal y = sim.y();
// Sim is transformed
x += panX_;
y += panY_;
// Sim is scaled
x *= scale_;
y *= scale_;
// Sim is centred on screen
x += (width() / 2);
y += (height() / 2);
return { x, y };
}
qreal MainWindow::randomNumber(qreal min, qreal max)
{
static std::mt19937 entropy = std::mt19937();
std::uniform_real_distribution<qreal> distribution{ min, max };
// distribution.param(typename decltype(distribution)::param_type(min, max));
return distribution(entropy);
}
My research into the issue
Top left above shows all pixmaps drawing correctly, at random angles
Top right above shows the same instance as top left, panned so that the pixmaps are over the fuzzy boundary, with the bottom rightmost pixmaps not being drawn (or to be more accurate, are being drawn as entirely transparent pixels, due to the transparent edge being smeared over the entire image)
Bottom left shows all pixmaps being rotated by 0.1 degree, this leads to a sharp boundary, which when the square is panned to overlap it, clips the square to a rectangle.
Bottom right shows a small range of random angles, between 0.0 and 10.0, this leads to a slightly fuzzier, but still vertical edge, this looks similar to bottom left, but as well as the sharp clipped edge, there is also a slight gradient effect as some of the pixmaps closer to the edge have also not be rendered correctly.
I have tried turning clipping off in the QPainter when drawing the pixmaps, this has had no effect.
I have tried seperately saving a copy of the QPainters transform and setting it back afterwards, this had no effect.
I have tried upgrading to Qt 6.0.3 (which claimed to have solved a number of graphical bugs), issue still present.
The absolute coordinates don't matter, I can offset all of the locations by QPointF(-10000, 10000), pan over to them and the dissapearing point is in the same relative position in the window.
To see the bug in action, scroll out, then click and drag in the window to move the pixmaps to the lower right of the screen, depending on how far out you have zoomed, a number of the pixmaps will no longer be drawn.
Update
I have also discovered that making the original QPixmap larger makes the issue worse, i.e. the boundary becomes apparent at more zoomed in levels, plus further rendering abberations occur. Note that they are still being scaled down to the same size as before, there are just more source pixels.
I changed pixmap_(30, 50) to pixmap_(300, 500)
The image above shows that when panning to move the pixmaps towards the bottom right, they dissapear sooner than before (i.e. while zoomed further in and more towards the top left), the curved arrow indicates the movement of the pixmaps drawn in an arc beyond the dissapearance boundary, they seem to be moving faster than the correct pixmaps are drawn as they are moved.
EDIT: Closer inspection shows that the apparent circular motion is not real, the order in which pixmaps are appearing and dissapearing just made it look that way. With the below update, you can see that there are concentric rings where the pixmaps that have dissapeard re-appear (very briefly) in the correct place, but the re-appeaance is only for a thin window that seems to be narrower than the size of the pixmap, so the content that is drawn appears again to be stuck in place, but the part shown is clipped.
Update
To see the pixmaps "stick" at the boundary, you can adjust the contents of MainWindow::MainWindow() to
MainWindow::MainWindow()
: pixmap_(500, 500)
{
setAutoFillBackground(true);
constexpr int count = 10000;
constexpr qreal area = 10000.0;
constexpr qreal size = 44.0;
for (int i = 0; i < count; ++i) {
qreal rotation = randomNumber(0.0, 360.0);
drawLocations_.push_back(PixmapLocation{ QPointF(randomNumber(-area, area), randomNumber(-area, area)), rotation, size });
}
// Make edges transparent (the middle will be drawn over)
pixmap_.fill(QColor::fromRgba(0x000000FF));
/*
* Fill with magenta almost up to the edge
*
* The transparent edge is required to see the effect, the misdrawn pixmaps
* appear to be a smear of the edge closest to the boundary between proper
* rendering and misrendering. If the pixmap is a solid block of colour then
* the effect is masked by the fact that the smeared edge looks the same as
* the correctly drawn pixmap.
*/
QPainter p(&pixmap_);
p.setPen(Qt::NoPen);
constexpr int smallInset = 1;
const int bigInset = std::min(pixmap_.width(), pixmap_.height()) / 5;
p.fillRect(pixmap_.rect().adjusted(smallInset, smallInset, -smallInset, -smallInset), Qt::magenta);
p.fillRect(pixmap_.rect().adjusted(bigInset, bigInset, -bigInset, -bigInset), Qt::green);
update();
}
Which results in see right hand edge for squares that appear to have been clipped. When moving them around, the square seems to get stuck in place and instead of the edge at the boundary dissapearing, the edge furthest from the boundary dissapears first.
This issue is very interesting. As far as I could test, your code looks good, I feel like this is a Qt bug, and I think you need to report it to Qt: https://bugreports.qt.io/. You should post a single piece of code to illustrate the issue, your second one from your "Update" edit is good: it makes it easy to reproduce the issu. Maybe you should also post a small video to illustrate how things are getting wrong when you zoom in/out or move the area with the mouse.
I tried some alternatives to hopefully find a workaround, but I found none:
Tried to use a QImage rather than a QPixmap, same issue
Tried to load the pixmap from a frozen png/qrc file, same issue
Tried to use QTransform to play with scale/translation/rotation, same issue
Tried Linux and Windows 10: same issue observed
Note that:
If you don't rotate (comment paint.rotate(entity.rotation_);), the issue is not visible
If your pixmap is a simple mono-colored square (simply fill your pixmap with a single color using pixmap_.fill(QColor::fromRgba(0x12345600));), the issue is not visible anymore. That's the most surprising, looks like a pixel from the image is being reused as background and messes things up but if all the image pixels are the same it does not lead to any display issue.
Workaround proposed by the Qt team
"The issue can easily be worked around by enabling the SmoothPixmapTransform render hint on the painter"

Why does QPainter::drawPoint draw a horizontal line segment?

I'm trying to draw a 3-pixel large point with QPainter. But the following code instead draws a horizontal line with width of 3 pixels.
#include <QPainter>
#include <QImage>
int main()
{
const int w=1000, h=1000;
QImage img(w, h, QImage::Format_RGBX8888);
{
QPainter p(&img);
p.fillRect(0,0,w,h,Qt::black);
p.scale(w,h);
p.setPen(QPen(Qt::red, 3./w, Qt::SolidLine, Qt::RoundCap));
p.drawPoint(QPointF(0.1,0.1));
}
img.save("test.png");
}
Here's the top left corner of the resulting image:
I am expecting to get a point which is red circle, or at least a square — but instead I get this line segment. If I comment out p.scale(w,h) and draw the point with width 3 (instead of 3./w) at position (100,100), then I get a small mostly symmetric 3-pixel in height and width point.
What's going on? Why do I get a line segment instead of point as expected? And how to fix it, without resorting to drawing an ellipse or to avoiding QPainter::scale?
I'm using Qt 5.10.0 on Linux x86 with g++ 5.5.0. The same happens on Qt 5.5.1.
It appears that QPaintEngineEx::drawPoints renders points as line segments of length 1/63.. See the following code from qtbase/src/gui/painting/qpaintengineex.cpp in the Qt sources:
void QPaintEngineEx::drawPoints(const QPointF *points, int pointCount)
{
QPen pen = state()->pen;
if (pen.capStyle() == Qt::FlatCap)
pen.setCapStyle(Qt::SquareCap);
if (pen.brush().isOpaque()) {
while (pointCount > 0) {
int count = qMin(pointCount, 16);
qreal pts[64];
int oset = -1;
for (int i=0; i<count; ++i) {
pts[++oset] = points[i].x();
pts[++oset] = points[i].y();
pts[++oset] = points[i].x() + 1/63.;
pts[++oset] = points[i].y();
}
QVectorPath path(pts, count * 2, qpaintengineex_line_types_16, QVectorPath::LinesHint);
stroke(path, pen);
pointCount -= 16;
points += 16;
}
} else {
for (int i=0; i<pointCount; ++i) {
qreal pts[] = { points[i].x(), points[i].y(), points[i].x() + qreal(1/63.), points[i].y() };
QVectorPath path(pts, 2, 0);
stroke(path, pen);
}
}
}
Notice the pts[++oset] = points[i].x() + 1/63.; line in the opaque brush branch. This is the second vertex of the path — shifted with respect to the desired position of the point.
This explains why the line extends to the right of the position requested and why it depends on the scale. So, it seems the code in the OP isn't wrong for an ideal QPainter implementation, but just has come across a Qt bug (be it in the implementation of the method or in its documentation).
So the conclusion: one has to work around this problem by either using different scale, or drawing ellipses, or drawing line segments with much smaller lengths than what QPainter::drawPoints does.
I've reported this as QTBUG-70409.
Though I have not been able to pin point why exactly the problem occurs, I have got relatively close to the solution. The problem lies with scaling. I did a lots of trial and error with different scaling and width of point with the below code.
const int w=500, h=500;
const int scale = 100;
float xPos = 250;
float yPos = 250;
float widthF = 5;
QImage img(w, h, QImage::Format_RGBX8888);
{
QPainter p(&img);
p.setRenderHints(QPainter::Antialiasing);
p.fillRect(0,0,w,h,Qt::black);
p.scale(scale, scale);
p.setPen(QPen(Qt::red, widthF/(scale), Qt::SolidLine, Qt::RoundCap));
p.drawPoint(QPointF(xPos/scale, yPos/scale));
}
img.save("test.png");
The above code produces the image
My observations are
1) Due to high scaling, the point (which is just 3 pixel wide) is not able to scale proportionally at lower width (width of point), if you set width as something like 30 the round shape is visible.
2) If you want to keep the width of point low then you have to decrease the scaling.
Sadly I can not explain why at high scaling it is not expanding proportionally.

Scaling items and rendering

I am making a small game in C++11 with Qt. However, I am having some issues with scaling.
The background of my map is an image. Each pixel of that image represents a tile, on which a protagonist can walk and enemies/healthpacks can be.
To set the size of a tile, I calculat the maximum amount like so (where imageRows & imageCols is amount of pixels on x- and y-axis of the background image):
QRect rec = QApplication::desktop()->screenGeometry();
int maxRows = rec.height() / imageRows;
int maxCols = rec.width() / imageCols;
if(maxRows < maxCols){
pixSize = maxRows;
} else{
pixSize = maxCols;
}
Now that I have the size of a tile, I add the background-image to the scene (in GameScene ctor, extends from QGraphicsScene):
auto background = new QGraphicsPixmapItem();
background->setPixmap(QPixmap(":/images/map.png").scaledToWidth(imageCols * pixSize));
this->addItem(background);
Then for adding enemies (they extend from a QGraphicsPixMapItem):
Enemy *enemy = new Enemy();
enemy->setPixmap(QPixmap(":/images/enemy.png").scaledToWidth(pixSize));
scene->addItem(enemy);
This all works fine, except that on large maps images get scaled once (to a height of lets say 2 pixels), and when zooming in on that item it does not get more clear, but stays a big pixel. Here is an example: the left one is on a small map where pixSize is pretty big, the second one has a pixSize of pretty small.
So how should I solve this? In general having a pixSize based on the screen resolution is not really useful, since the QGrapicsScene is resized to fit the QGraphicsView it is in, so in the end the view still determines how big the pixels show on the screen.
MyGraphicsView w;
w.setScene(gameScene);
w.fitInView(gameScene->sceneRect(), Qt::KeepAspectRatio);
I think you might want to look at the chip example from Qt (link to Qt5 but also works for Qt4).
The thing that might help you is in the chip.cpp file:
in the paint method:
const qreal lod = option->levelOfDetailFromTransform(painter->worldTransform());
where painter is simply a QPainter and option is of type QStyleOptionGraphicsItem. This quantity gives you back a measure of the current zoom level of your QGraphicsView and thus as in the example you can adjust what is being drawn at which level, e.g.
if (lod < 0.2) {
if (lod < 0.125) {
painter->fillRect(QRectF(0, 0, 110, 70), fillColor);
return;
}
QBrush b = painter->brush();
painter->setBrush(fillColor);
painter->drawRect(13, 13, 97, 57);
painter->setBrush(b);
return;
}
[...]
if (lod >= 2) {
QFont font("Times", 10);
font.setStyleStrategy(QFont::ForceOutline);
painter->setFont(font);
painter->save();
painter->scale(0.1, 0.1);
painter->drawText(170, 180, QString("Model: VSC-2000 (Very Small Chip) at %1x%2").arg(x).arg(y));
painter->drawText(170, 200, QString("Serial number: DLWR-WEER-123L-ZZ33-SDSJ"));
painter->drawText(170, 220, QString("Manufacturer: Chip Manufacturer"));
painter->restore();
}
Does this help?

Drawing points of handwritten stroke using DrawEllipse (GDI+)

I'm working on an application that draws handwritten strokes. Strokes are internally stored as vectors of points and they can be transformed into std::vector<Gdiplus::Point>. Points are so close to each other, that simple drawing of each point should result into an image of continual stroke.
I'm using Graphics.DrawEllipse (GDI+) method to draw these points. Here's the code:
// prepare bitmap:
Bitmap *bitmap = new Gdiplus::Bitmap(w, h, PixelFormat32bppRGB);
Graphics graphics(bitmap);
// draw the white background:
SolidBrush myBrush(Color::White);
graphics.FillRectangle(&myBrush, 0, 0, w, h);
Pen blackPen(Color::Black);
blackPen.SetWidth(1.4f);
// draw stroke:
std::vector<Gdiplus::Point> stroke = getStroke();
for (UINT i = 0; i < stroke.size(); ++i)
{
// draw point:
graphics.DrawEllipse(&blackPen, stroke[i].X, stroke[i].Y, 2, 2);
}
At the end I just save this bitmap as a PNG image and sometimes the following problem occurs:
When I saw this "hole" in my stroke, I decided to draw my points again, but this time, by using ellipse with width and height set to 1 by using redPen with width set to 0.1f. So right after the code above I added the following code:
Pen redPen(Color::Red);
redPen.SetWidth(0.1f);
for (UINT i = 0; i < stroke.size(); ++i)
{
// draw point:
graphics.DrawEllipse(&redPen, stroke[i].X, stroke[i].Y, 1, 1);
}
And the new stoke I've got looked like this:
When I use Graphics.DrawRectangle instead of DrawEllipse while drawing this new red stroke, it never happens that this stroke (drawn by drawing rectangles) would have different width or holes in it:
I can't think of any possible reason, why drawing circles would result into this weird behaviour. How come that stroke is always continual and never deformed in any way when I use Graphics.DrawRectangle?
Could anyone explain, what's going on here? Am I missing something?
By the way I'm using Windows XP (e.g. in case it's a known bug). Any help will be appreciated.
I've made the wrong assumption that if I use Graphics.DrawEllipse to draw a circle with radius equal to 2px with pen of width about 2px, it will result in a filled circle with diameter about 4-5 px being drawn.
But I've found out that I actually can't rely on the width of the pen while drawing a circle this way. This method is meant only for drawing of border of this shape, thus for drawing filled ellipse it's much better to use Graphics.FillEllipse.
Another quite important fact to consider is that both of mentioned functions take as parameters coordinates that specify "upper-left corner of the rectangle that specifies the boundaries of the ellipse", so I should subtract half of the radius from both coordinates to make sure the original coordinates specify the middle of this circle.
Here's the new code:
// draw the white background:
SolidBrush whiteBrush(Color::White);
graphics.FillRectangle(&whiteBrush, 0, 0, w, h);
// draw stroke:
Pen blackBrush(Color::Black);
std::vector<Gdiplus::Point> stroke = getStroke();
for (UINT i = 0; i < stroke.size(); ++i)
graphics.FillEllipse(&blackBrush, stroke[i].X - 2, stroke[i].Y - 2, 4, 4);
// draw original points:
Pen redBrush(Color::Red);
std::vector<Gdiplus::Point> origStroke = getOriginalStroke();
for (UINT i = 0; i < origStroke.size(); ++i)
graphics.FillRectangle(&redBrush, origStroke[i].X, origStroke[i].Y, 1, 1);
which yields following result:
So in case someone will face the same problem as I did, the solution is:

QImage resizing

I'm coding small graphics editor and need some help.
I'm painting QImage like this:
void Editor::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
// zoom is an int, representing zoomFactor from 1 to 12.
painter.drawImage(
QRect(0, 0, image.width() * zoom , image.height() * zoom),
image);
if (zoom >= 3 && showGrid) {
painter.setPen(palette().foreground().color());
painter.setPen(Qt::DotLine);
// this is how I draw grid
for (int i = 0; i <= image.width(); ++i)
painter.drawLine(zoom * i, 0,
zoom * i, zoom * image.height());
for (int j = 0; j <= image.height(); ++j)
painter.drawLine(0, zoom * j,
zoom * image.width(), zoom * j);
}
// (...)
}
It works fine with images like this (16 x 16)
Troubles begin when I open images like this (25 X 28)
As you can see pixels are drawn with different width and height!
What am I doing wrong? Please, help :)
UPD: Problem solved unexpectedly. I noticed, that Editor was QGLWidget, so I tried to change it to QWidget and everything worked just fine. Stupid me -_-
Btw, may be there's more convenient ways to zoom image?(like crop pixels, that are not needed to be painted)
The code for handling highly zoomed images has been "optimized" some time ago in Qt and now it is unfortunately buggy. I didn't check the code but my wild guess is that texture "speed" or "offset" used for drawing was before computed in floating point and it's now computed using fixed point.
I don't remember exactly with which version this was introduced, but it was quite early after 4.0. We have one of our applications that needs to allow to place a cross with sub-pixel precision and scaling over the point when zoom factor is high you can notice the picture is "wobbling".
I'm the first that would not claim a bug in somone else's code unless 100% sure, but this is one of those cases in which I am indeed 100% sure.
The only way out is to draw the zoomed image manually, either reimplementing texture mapping code or (if you only need int > 1 zoom factors) by drawing one pixel at a time with drawRect... it should be fast enough on a PC.
Note that the bug may be a common bug of video drivers instead of a bug in Qt... I've seen that the problem on our software is present on different platforms (Windows/Linux/OsX) and indeed IIRC only when using QWidget (and not when using QGLWidget).