Qt 2D drawing: anti-aliased rendering and coordinate system

1. Anti-aliased rendering

1.1 Logical drawing

The size (width and height) of a graphics primitive always corresponds to its mathematical model, the image below shows what it looks like ignoring the width of the brush it is rendered with.

 

1.2 Physical drawing (default)

By default, drawing is aliased and drawn using the following rules: When rendering with a brush with a width of one pixel, the pixels are rendered to the right and below the mathematically defined point, as shown in Figure 1 below. When rendering with a brush with an even number of pixels, the pixels will be rendered symmetrically around the mathematically defined point; while when rendering with a surface pen with an odd number of pixels, it will be drawn symmetrically with the even number first, and the last pixel will be rendered to the right and below the mathematically defined point, as shown in Figure 2 below.

 

So it seems that the image is not very smooth, it seems to be jagged, so in order to eliminate aliasing, anti-aliasing drawing is used.

The benefits of this article, the fee to receive Qt development learning materials package, technical video, content includes (C++ language foundation, Qt programming introduction, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓ See below

1.3 Anti-aliased drawing

Anti-aliased (Anti-aliased), also known as anti-aliasing or anti-aliasing, is a technology that smoothes the edges of an image to make it look softer and smoother. When QPainter draws, you can use QPainter :: RenderHint rendering hint to specify whether to use anti-aliasing function, and the RenderHint value is divided into the following three types.

 

If the antialiasing rendering hint is used when drawing, that is, use the QPainter::setRenderHint(RenderHint hint, bool on = true) function, and set the parameter hint to QPainter::Antialiasing. Then the pixels will be rendered symmetrically on both sides of the mathematically defined point, as shown in the figure below.

 

A sample program is:

QPainter painter(this);
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); //抗锯齿和使用平滑转换算法

2. Coordinate system

2.1 Introduction to Coordinate System

Qt's coordinate system is controlled by the QPainter class, and QPainter is drawn on the drawing device. A drawing device's default coordinate system has the origin (0, 0) at its upper left corner, with x-coordinates increasing to the right and y-coordinates increasing downward. On pixel-based devices, the default unit is a pixel, while on printers the default unit is a point (1/72 inch).

The code demonstration is still performed in the program in the previous section, and the content of paintEvent() is changed as follows:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setBrush(Qt::red);
    painter.drawRect(0, 0, 100, 100);
    painter.setBrush(Qt::yellow);
    painter.drawRect(-50, -50, 100, 100);
}

We first draw a red rectangle with a length and width of 100 pixels at the origin (0, 0), and then draw a yellow rectangle of the same size at (-50, -50). As you can see, we can only see a quarter of the yellow rectangle. Run the program, the effect is shown in the figure below.

 

2.2 Coordinate system transformation

By default, QPainter draws on the coordinate system of the specified device. When drawing, use the QPainter::translate() function to translate the coordinate system; you can use the QPainter::scale() function to scale the coordinate system; use the QPainter::rotate() function to rotate the coordinate system clockwise; you can also use QPainter::shear() to distort the coordinate system around the origin. As shown below.

 

We can use the convenience functions mentioned above for coordinate system transformation, or through the QTransform class.

(1) Translation transformation

Change the content of the paintEvent() function as follows:

void Widget::paintEvent(QPaintEvent *)
{
    //平移坐标系统
    QPainter painter(this);
    painter.setBrush(Qt::yellow);
    painter.drawRect(0, 0, 50, 50);
    //将坐标系原点向右、向下平移100像素点,即使原点坐标变为(100,100)
    painter.translate(100, 100);
    painter.setBrush(Qt::red);
    painter.drawRect(0, 0, 50, 50);
    //将坐标系原点向左、向上平移100像素点,即重新使原点坐标变为(0,0)
    painter.translate(-100, -100);
    painter.drawLine(0, 0, 20, 20);
}

Here, a square with a width and a height of 50 is drawn at the origin (0, 0) first, and then the coordinate system is translated using the translate() function, so that (100, 100) becomes the new origin, so when we draw again, although the logical coordinates in drawRect() are still (0, 0), what is actually displayed is a red square at (100, 100). You can use the translate() function again for reverse translation, so that the origin returns to the upper left corner of the window. Run the program, the effect is shown in the figure below.

 

(2) Zoom transformation

Change the content in the paintEvent() function as follows:

void Widget::paintEvent(QPaintEvent *)
{
    //缩放坐标系统
    QPainter painter(this);
    painter.setBrush(Qt::yellow);
    painter.drawRect(0, 0, 100, 100);
    //将坐标系统的横、纵坐标都放大两倍
    painter.scale(2, 2);
    painter.setBrush(Qt::red);
    painter.drawRect(50, 50, 50, 50);
}

It can be seen that when we use the scale() function to double the horizontal and vertical coordinates of the coordinate system, the logical (50, 50) point becomes the (100, 100) point on the window, and the logical length is 50, but the length drawn on the window is 100. Run the program, the effect is shown in the figure below.

 

(3) Rotation transformation

Change the paintEvent() function as follows:

void Widget::paintEvent(QPaintEvent *)
{
    //旋转坐标系统
    QPainter painter(this);
    painter.drawLine(0, 0, 100, 0);
    //以原点为中心,顺时针旋转30度
    painter.rotate(30);
    painter.drawLine(0, 0, 100, 0);
    painter.translate(100, 100);
    painter.rotate(30);
    painter.drawLine(0, 0, 100, 0);
}

Here, a horizontal straight line is first drawn, and then the coordinate system is rotated by 30 degrees, and another straight line is drawn. As you can see, the default is to rotate around the origin (0, 0). If you want to change the rotation center, you can use the translate() function. For example, the center is moved to (100, 100) point, then rotated 30 degrees, and draw a straight line. Run the program, the effect is shown in the figure below.

 

(4) Distortion transformation

Change the paintEvent() function as follows:

void Widget::paintEvent(QPaintEvent *)
{
    //扭曲坐标系统
    QPainter painter(this);
    painter.setBrush(Qt::yellow);
    painter.drawRect(0, 0, 50, 50);
    //纵向扭曲变形
    painter.shear(0, 1);
    painter.setBrush(Qt::red);
    painter.drawRect(50, 0, 50, 50);
}

shear() has two parameters, the first is to distort the horizontal direction, the second is to distort the vertical direction, and the value is the degree of distortion. For example, if the vertical distortion value is 1 in the program, then the left side of the red square moves down one unit, and the right side moves down two units. A value of 1 means that the right side moves down one unit more than the left side. You can change the value and test the effect. Run the program, the effect is shown in the figure below.

 

2.3 "Window-Viewport" conversion

When drawing with QPainter, logical coordinates are used for drawing, and then converted to physical coordinates of the drawing device. The mapping from logical coordinates to physical coordinates is handled by QPainter's worldTransform() function and QPainter's viewport() and window() functions. Among them, the viewport (viewport) represents an arbitrary rectangle specified in physical coordinates, and the window (window, which is different from the concept of widgets mentioned before) represents the same rectangle in logical coordinates. By default, logical coordinates and physical coordinates are coincident, and they are equivalent to rectangles on the drawing device.

Using the "window-viewport" transformation allows the logical coordinate system to be adapted to the application's requirements, and this mechanism can also be used to make the drawing code independent of the drawing device. Let's look at an example.

Create a Widget window application (its default width is 400 pixels, height is 300 pixels, and the upper left corner is the origin). First draw a square normally:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    p.setPen(Qt::blue);
    p.drawRect(0, 0, 100, 100);
}

This is no problem, because the current drawing device is a Widget, and its upper left corner is the origin (0, 0). Results as shown below.

 

Now we use setWindow to set the logical coordinate rectangle:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    p.setPen(Qt::blue);
    p.drawRect(0, 0, 100, 100);

    p.setWindow(-50, -50, 100, 100);
    p.setPen(Qt::red);
    p.drawRect(0, 0, 100, 100);
}

At this time, the effect is as shown in the figure below.

 

Now let’s talk about the function of p.setWindow(-50, -50, 100, 100). It linearly maps the logical coordinate rectangle (the term window window mentioned later) with our current device physical coordinate rectangle (the term viewport viewport mentioned later). The device physical coordinate rectangle mentioned here is the coordinates of our visible Widget, which is a rectangle with (0, 0) point in the upper left corner, a width of 400, and a height of 300. The schematic diagram of the linear mapping is as follows:

 

That is to say, after calling p.setWindow(-50, -50, 100, 100) and using p to draw again, the coordinate origin is no longer the upper left corner of the Widget, but its center. The previously drawn square with a width of 100 and a height of 100 will now be proportionally changed to a width of 400 and a height of 300, which is the red rectangle we see.

Let's modify the code again:

void Widget::paintEvent(QPaintEvent *)

{
    QPainter p(this);
    p.setPen(Qt::blue);
    p.drawRect(0, 0, 100, 100);

    p.setWindow(-50, -50, 100, 100);
    p.setPen(Qt::red);
    p.drawRect(0, 0, 20, 20);
}

The running effect is shown in the figure below:

 

We will make the drawn red rectangle smaller, and it can be clearly seen that it should be a square, but now it has become a rectangle. It is caused by the scale transformation mentioned above, so how can we make it display the proper shape? Let's set the viewport:

void Widget::paintEvent(QPaintEvent *)
{
    QPainter p(this);
    p.setPen(Qt::blue);
    p.drawRect(0, 0, 100, 100);

    int side = qMin(width(), height());
    p.setViewport((width() - side)/2, (height() - side) /2, side, side);
    p.setWindow(-50, -50, 100, 100);

    p.setPen(Qt::red);
    p.drawRect(0, 0, 20, 20);
}

Now use setViewport to set the viewport to a square, that is, the Widget is the largest square in the area, so that when the logical coordinates and physical coordinates are scaled, the width and height of the red rectangle will not be deformed due to the different zoom ratios, as shown in the figure below.

 

The question arises: So why modify this logical coordinate rectangle?

This is for the convenience of our drawing, because we generally only want to draw in the standard coordinate system, and do not consider the specific coordinate systems of different drawing devices (for example, the origin of some device coordinates is in the upper left corner, and some are in the center, etc.)

Extended terms: window, viewport

The drawing area in our imagination with a width of 100, a height of 100, and the origin in the center mentioned here is a rectangle under logical coordinates, that is, the so-called window set by using setWindow; while the actual drawing device, such as the Widget component here, sets a rectangle on its visualized area called a viewport (viewport in English). The default is the size of the visualized area, but it can be set through setViewport. The window corresponds to the viewport and can be transformed linearly. In this way, we can set the viewport first and then set the corresponding window to ensure that the graphics drawn by our code in the standard imaginary coordinate system can be accurately displayed on different drawing device interfaces.

The article is transferred from the blog garden (fengMisaka): Qt 2D drawing two: anti-aliasing rendering and coordinate system

The benefits of this article, the fee to receive Qt development learning materials package, technical video, content includes (C++ language foundation, Qt programming introduction, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓ See below 

Guess you like

Origin blog.csdn.net/QtCompany/article/details/131817056