Qt: рисование QPixmap с высоким разрешением DPI

Я написал приложение, которое рисует два улыбающихся лица:

введите описание изображения здесь

Первый рисуется прямо на QWidget:

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

Второй нарисован на QPixmap, который, в свою очередь, переносится на виджет:

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 и запустил приложение:

введите описание изображения здесь

Первая грань четкая и четкая, а вторая — пиксельная. Это потому, что он нарисован в пиксельной карте с низким разрешением. Поэтому я увеличил этот 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());
}

Результат:

введите описание изображения здесь

Второе лицо выглядит так, как будто оно нарисовано с правильным разрешением, но затем увеличено. Теперь я застрял. Как нарисовать QPixmap, а затем нарисовать 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();
}

person el.pescado    schedule 02.02.2017    source источник
comment
Я думаю, что тестирование без дисплея с высоким разрешением заведет вас по кругу. Я не думаю, что установка коэффициента масштабирования на 2 является допустимой симуляцией. Обычно все, что вам нужно сделать, это установить атрибут DPI get-rendere/36058882#36058882" title="изменение размера масштабирования экрана в dpi, чтобы размер шрифта приложений qt отображался"> stackoverflow.com/questions/20464814/   -  person Nicolas Holthaus    schedule 02.02.2017
comment
Это работает волшебным образом для виджета, потому что дополнительное разрешение обрабатывается внутри. Очевидно, что если вы рисуете на пиксельной карте, вам придется справиться с этим самостоятельно.   -  person dtech    schedule 03.02.2017
comment
@NicolasHolthaus: Пока я читал документы по QT, этого должно быть достаточно: Qt 5.6 поддерживает межплатформенное масштабирование с высоким разрешением для устаревших приложений, аналогично масштабированию, выполняемому изначально в macOS. Это позволяет приложениям, написанным для экранов с низким разрешением, без изменений работать на устройствах с высоким разрешением. Эта функция не является обязательной и может быть включена следующими переменными среды: (...) QT_SCALE_FACTOR [числовой].   -  person el.pescado    schedule 03.02.2017
comment
@ddriver: Это правда, и поэтому я спрашиваю, как самому справиться с рисованием на пиксельной карте?   -  person el.pescado    schedule 03.02.2017


Ответы (1)


Если вы хотите рендеринг HighDPI, вы также должны использовать аргументы QRectF и QPointF для функций QPainter. В вашей функции paintFace(...) настройте функции drawEllipse и drawText так, чтобы они использовали аргументы QRectF, а не QRect. Это может помочь.

Не рекомендуется использовать qApp->devicePixelRatio(). Есть люди со смешанными мониторами HighDPI и Non-HighDPI. Поскольку вы находитесь в функции виджета paintEvent(...), вы можете напрямую использовать функцию-член QWidget devicePixelRatioF(), а не qApp->devicePixelRatio(). Это обеспечит правильную визуализацию виджета, даже если пользователь перемещает виджет между мониторами со смешанным разрешением.

Вы также должны включить масштабирование High DPI в Qt через: 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();
}
person tiho    schedule 08.11.2017