2

我在 QML 中创建了一个小型绘图应用程序,我创建了一个QQuickPaintedItem. 然后在 QML 中,我使用 aMouseArea将输入提供给我的班级。从那里我只需将鼠标位置存储在一个向量中,然后将收到的点绘制到一个QImageusing上QPainter(我使用一个简单的算法使用向量中的最后三个点绘制二次贝塞尔曲线)。然后我调用QQuickPainted::update()并在我的实现中QQuickPaintedItem::paint()绘制图像。现在程序可以正常工作了,但问题是这幅画的渲染效果很差(我已经在使用了QPainter::AntiAliasing)。下面有一张图片。如您所见,曲线不是很锐利,而且我可以看到斜线上的“像素”(当我用 OneNote 尝试同样的事情时,一切都变得平滑而美观)。

如果您想对其进行测试,这是我的 github 存储库中的完整示例(代码也在下面)。对此我能做些什么吗? 注释 2019-12-05 151210.png. 与onenote相同的图像

#ifndef DRAWINGCANVAS_H
#define DRAWINGCANVAS_H

#include <QObject>
#include <QQuickPaintedItem>
#include <QPixmap>
#include <QPainter>

struct Outline{
    QPolygonF points;

    void addPoint(QPointF p){
        points.append(p);
    }
    void clear(){
        points.clear();
    }
};

// a  custom QQuickPainted used as a canvas in QML
class DrawingCanvas : public QQuickPaintedItem
{
    Q_OBJECT
    Q_PROPERTY(bool drawing READ drawing WRITE setDrawing NOTIFY drawingChanged)
    Q_PROPERTY(int penWidth READ penWidth WRITE setPenWidth NOTIFY penWidthChanged)
    Q_PROPERTY(QString penColor READ penColor WRITE setPenColor NOTIFY penColorChanged)

public:
    explicit DrawingCanvas(QQuickItem *parent = nullptr);
    bool drawing() const;
    Q_INVOKABLE void initiateBuffer();

    Q_INVOKABLE void penPressed(QPointF pos);
    Q_INVOKABLE void penMoved(QPointF pos);
    Q_INVOKABLE void penReleased();
    int penWidth() const;

    void paint(QPainter *painter) override;

    QString penColor() const;


public slots:
    void setDrawing(bool drawing);

    void setPenWidth(int penWidth);

    void setPenColor(QString penColor);

signals:
    void drawingChanged(bool drawing);
    void penWidthChanged(int penWidth);
    void penColorChanged(QString penColor);

private:
    void drawOnBuffer(QPointF pos);

    bool m_drawing;
    QPixmap m_buffer;
    int m_penWidth;
    QString m_penColor;

    QPointF m_lastPoint;
    Outline m_currentOutline;
    QRect m_updateRect;
    QVector<Outline> m_outlines;


    bool m_outlineEraser;
};

#endif // DRAWINGCANVAS_H
#include "drawingcanvas.h"

#include <QPainter>

DrawingCanvas::DrawingCanvas(QQuickItem *parent) : QQuickPaintedItem(parent)
{
    m_penWidth = 4;
}

bool DrawingCanvas::drawing() const
{
    return m_drawing;
}

void DrawingCanvas::penPressed(QPointF pos)
{
    setDrawing(true);
    m_currentOutline.addPoint(pos);
    m_lastPoint = pos;

}

void DrawingCanvas::penMoved(QPointF pos)
{
    if(drawing()){
        m_currentOutline.addPoint(pos);
        // draw the points on the buffer
        drawOnBuffer(pos);
    }
    m_lastPoint = pos;
}

void DrawingCanvas::penReleased()
{
    setDrawing(false);
    m_outlines.append(m_currentOutline);
    m_currentOutline.clear();
    m_lastPoint = QPointF();
}

// draws the actual item
void DrawingCanvas::paint(QPainter *painter)
{
    painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    QPen pen;
    pen.setWidth(penWidth());
    pen.setColor(penColor());

    painter->setPen(pen);
    painter->drawPixmap(m_updateRect, m_buffer, m_updateRect);

    m_updateRect = QRect();
}

// draws on the image
void DrawingCanvas::drawOnBuffer(QPointF pos)
{
    QPainter bufferPainter;
    if(bufferPainter.begin(&m_buffer)){
        QPen pen;
        pen.setWidth(penWidth());
        pen.setColor(penColor());

        bufferPainter.setPen(pen);
        bufferPainter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);

        int pointsLength = m_currentOutline.points.length();
        QPainterPath path;

        // this will help smoothing the curves
        if(pointsLength > 2){
            auto previousPoint = m_currentOutline.points.at(pointsLength - 3);

            auto mid1 = (m_lastPoint + previousPoint)/2;
            auto mid2 = (pos + m_lastPoint)/2;

            path.moveTo(mid1);
            path.quadTo(m_lastPoint, mid2);
            bufferPainter.drawPath(path);
        }
        // update the canvas
        int rad = (penWidth() / 2) + 2;

        auto dirtyRect = path.boundingRect().toRect().normalized()
                .adjusted(-rad, -rad, +rad, +rad);

        // change the canvas dirty region
        if(m_updateRect.isNull()){
            m_updateRect = dirtyRect;
        }
        else{
            m_updateRect = m_updateRect.united(dirtyRect);
        }
        update(dirtyRect);

        m_lastPoint = pos;
    }
}

QString DrawingCanvas::penColor() const
{
    return m_penColor;
}

int DrawingCanvas::penWidth() const
{
    return m_penWidth;
}

void DrawingCanvas::setDrawing(bool drawing)
{
    if (m_drawing == drawing)
        return;

    m_drawing = drawing;
    emit drawingChanged(m_drawing);
}

void DrawingCanvas::setPenWidth(int penWidth)
{
    if (m_penWidth == penWidth)
        return;

    m_penWidth = penWidth;
    emit penWidthChanged(m_penWidth);
}

void DrawingCanvas::setPenColor(QString penColor)
{
    if (m_penColor == penColor)
        return;

    m_penColor = penColor;
    emit penColorChanged(m_penColor);
}

// initiates the QImage buffer
void DrawingCanvas::initiateBuffer()
{
    qDebug() << this << "Initiating buffer" << width() << height();
    m_buffer = QPixmap(width(), height());
}

在 QML 中:

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.12
import QtQuick.Dialogs 1.3
import Drawing 1.0

ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Flickable {
        id: scrollView
        anchors.fill: parent
        contentHeight: drawingCanvas.height
        DrawingCanvas {
            id: drawingCanvas
            width: parent.width
            height: 2000
            penColor: "red"
            onWidthChanged: drawingCanvas.initiateBuffer()
        }
    }

    MouseArea {
        anchors.fill: parent
        anchors.rightMargin: 20
        onPressed: drawingCanvas.penPressed(
                       Qt.point(mouseX, mouseY + scrollView.contentY))
        onPositionChanged: drawingCanvas.penMoved(
                               Qt.point(mouseX, mouseY + scrollView.contentY))
        onReleased: drawingCanvas.penReleased()
    }
}
4

1 回答 1

1

您的渲染问题似乎不是由于抗锯齿 qt 选项,而更多是由于笔画的平滑。我建议您修改自定义贝塞尔平滑技术或为此 [0] 使用专用库。

其次,如果你想要“OneNote 绘图的感觉”,你应该在你的绘图方法中创建一个专用的 QPen,并使用 QPen 和 QBrush 选项 [1] “玩”。我在两张截图之间看到的主要区别是笔刷比例动态(在笔触的开头和结尾处)。

0:例如https://github.com/oysteinmyrmo/bezier

1:https ://doc.qt.io/qt-5/qpen.html

于 2019-12-30T16:39:17.167 回答