Qt:绘制高 DPI QPixmaps

12

我编写了一个绘制两个笑脸的应用程序:

enter image description here

第一个是直接绘制在QWidget上的:

void DirectFace::paintEvent(QPaintEvent *ev)
{
    QPainter painter(this);
    paintFace(painter);
}

第二个是画在 QPixmap 上,然后将其 blit 到小部件上:

void BufferedFace::paintEvent(QPaintEvent *ev)
{
    QPixmap buffer(width(), height());
    buffer.fill(Qt::transparent);
    QPainter painter(&buffer);
    paintFace(painter);

    QPainter p(this);
    p.drawPixmap(ev->rect(), buffer, ev->rect());
}

目前为止都很好。我想看看我的应用程序在高分辨率屏幕上的效果(我没有这样的屏幕),所以我设置了QT_SCALE_FACTOR=2,然后运行了我的应用程序:

enter image description here

第一张图片是清晰的,而第二张图片是有像素的。这是因为它被绘制到低分辨率的 pixmap 上。因此,我已经放大了那个 QPixmap 并设置了正确的 devicePixelRatio

void BufferedFace::paintEvent(QPaintEvent *ev)
{
    qreal pixelRatio = qApp->devicePixelRatio();
    QPixmap buffer(width() * pixelRatio, height() * pixelRatio);
    buffer.setDevicePixelRatio(pixelRatio);
    buffer.fill(Qt::transparent);
    QPainter painter(&buffer);
    paintFace(painter);

    QPainter p(this);
    p.drawPixmap(ev->rect(), buffer, ev->rect());
}

结果:

enter image description here

第二张图片看起来像是以正确的分辨率绘制,然后进行了放大。现在我卡住了。如何在 QPixmap 上绘制并将其绘制出来,以便在 Retina/HiDPI 屏幕上正常工作?

整个应用程序:

#include <QtWidgets>

class SmilingFace : public QWidget
{
    public:
    SmilingFace(QWidget *parent) : QWidget(parent) {};
    void paintFace(QPainter &painter);
};

class DirectFace : public SmilingFace
{
    public:
    DirectFace(QWidget *parent) : SmilingFace(parent) {}
    void paintEvent(QPaintEvent *ev) override;
};

class BufferedFace : public SmilingFace
{
    public:
    BufferedFace(QWidget *parent) : SmilingFace(parent) {}
    void paintEvent(QPaintEvent *ev) override;
};


void SmilingFace::paintFace(QPainter &painter)
{
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(Qt::lightGray));
    painter.drawEllipse(1, 1, width()-2, height()-2);

    painter.setPen(Qt::white);
    painter.setFont(QFont("", 32));
    painter.drawText(rect(), Qt::AlignHCenter, ";)");
}

void DirectFace::paintEvent(QPaintEvent *ev)
{
    QPainter painter(this);
    paintFace(painter);
}

void BufferedFace::paintEvent(QPaintEvent *ev)
{
    QPixmap buffer(width(), height());
    buffer.fill(Qt::transparent);
    QPainter painter(&buffer);
    paintFace(painter);

    QPainter p(this);
    p.drawPixmap(ev->rect(), buffer, ev->rect());
}

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    w.setWindowTitle("HiDPI");

    DirectFace d(&w);
    d.resize(48, 48);
    d.move(16, 16);

    BufferedFace i(&w);
    i.resize(48, 48);
    i.move(16 + 48 + 16, 16);

    w.show();

    return a.exec();
}

我认为在没有高DPI显示器的情况下测试这个程序会让你陷入循环。我不认为将比例因子设置为2是有效的模拟。通常,你只需要设置DPI属性即可 https://dev59.com/wGIj5IYBdhLWcg3wPy_H#36058882 - Nicolas Holthaus
它对于小部件的工作是“神奇”的,因为额外的分辨率是在内部处理的。显然,如果你在 pixmap 上绘画,你就必须自己处理。 - dtech
根据我阅读的QT文档,这应该足够了:“Qt 5.6支持跨平台高DPI缩放,用于传统应用程序,类似于macOS本地完成的缩放。这使得为低DPI屏幕编写的应用程序可以在高DPI设备上不变地运行。此功能是可选的,并且可以通过以下环境变量启用:(...) QT_SCALE_FACTOR [numeric]”。 - el.pescado - нет войне
@ddriver:没错,那么我该如何处理自己在 pixmap 上的绘制呢? - el.pescado - нет войне
1个回答

5
如果您想要高清晰度渲染,您还应该在QPainter函数中使用QRectF和QPointF参数。在paintFace(...)函数中,将drawEllipse和drawText函数调整为使用QRectF参数而不是QRect。这可能会有所帮助。
不建议使用qApp->devicePixelRatio()。有些人会使用混合的高分辨率和非高分辨率监视器。因为您在小部件paintEvent(...)函数中,所以可以直接使用QWidget成员函数devicePixelRatioF(),而不是qApp->devicePixelRatio()。这将处理小部件的正确渲染,即使用户在混合分辨率的监视器之间移动小部件也是如此。
您还应该通过以下方式启用Qt中的高DPI缩放:QCoreApplication :: setAttribute(Qt :: AA_EnableHighDpiScaling);
下面是完整的解决方案,在高清晰度和非高清晰度屏幕上完美呈现笑脸,即使在小部件在不同的显示器间移动也是如此。已在Qt 5.9.2中测试。
#include <QtWidgets>

class SmilingFace : public QWidget
{
public:
    SmilingFace(QWidget *parent) : QWidget(parent) {};
    void paintFace(QPainter &painter);
};

class DirectFace : public SmilingFace
{
public:
    DirectFace(QWidget *parent) : SmilingFace(parent) {}
    void paintEvent(QPaintEvent *ev) override;
};

class BufferedFace : public SmilingFace
{
public:
    BufferedFace(QWidget *parent) : SmilingFace(parent) {}
    void paintEvent(QPaintEvent *ev) override;
};


void SmilingFace::paintFace(QPainter &painter)
{
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::NoPen);
    painter.setBrush(QBrush(Qt::lightGray));
    painter.drawEllipse(QRectF(1, 1, width() - 2, height() - 2));

    painter.setPen(Qt::white);
    painter.setFont(QFont("", 32));
    painter.drawText(QRectF(0, 0, width(), height()), Qt::AlignHCenter, ";)");
}

void DirectFace::paintEvent(QPaintEvent *ev)
{
    QPainter painter(this);
    paintFace(painter);
}

void BufferedFace::paintEvent(QPaintEvent *ev)
{
    qreal dpr = devicePixelRatioF();
    QPixmap buffer(width() * dpr, height() * dpr);
    buffer.setDevicePixelRatio(dpr);
    buffer.fill(Qt::transparent);
    QPainter painter(&buffer);
    paintFace(painter);

    QPainter p(this);
    p.drawPixmap(ev->rect(), buffer, buffer.rect());
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QApplication a(argc, argv);

    QWidget w;
    w.setWindowTitle("HiDPI");

    DirectFace d(&w);
    d.resize(48, 48);
    d.move(16, 16);

    BufferedFace i(&w);
    i.resize(48, 48);
    i.move(16 + 48 + 16, 16);

    w.show();

    return a.exec();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接