QPainter 旋转阻止正确的 QPixmap 渲染
QPainter rotation prevents correct QPixmap rendering
作为错误报告给 Qt: https://bugreports.qt.io/browse/QTBUG-93475
我正在不同的位置重新绘制 QPixmap
多次,通过变换 QPainter 进行不同的旋转。在某些情况下 QPixmap
绘制不正确。下面的 GIF 显示了我最初发现此问题的 QPixmap
包含一个绿色圆柱体,请注意 GIF 左侧的渲染行为如何与预期的一样,但存在渲染不正确的边界。 QPixmap
内容似乎固定在原地,边缘的像素似乎在像素图的其余部分模糊不清。在 GIF 中 QPixmap
有一个洋红色背景,这是因为 QPainter::drawPixmap()
使用的 targetRect
也被用来单独填充像素图下方的矩形,这是因为我想检查目标矩形是否被正确计算。
最小可重现示例:
为了简单起见,我只是简单地用品红色像素填充 QPixmap
,具有 1 像素宽的透明边缘,这样涂抹会导致像素图完全消失。它没有显示图像“固定”在适当的位置,但它清楚地显示了边界,因为边界之外的像素图似乎消失了。
我自己一直在试验,我相信这完全是由于 QPainter
.
的旋转造成的
旋转的角度似乎有影响,如果所有的像素图都旋转到相同的角度,那么边界就会从一条模糊的对角线(模糊意味着消失的边界对于每个像素图不同)变为一个尖锐的 90 度角(其中尖锐意味着消失的边界对于所有像素图都是相同的)。
不同角度的范围似乎也有影响,如果随机生成的角度在 10 度的小范围内,那么边界只是一个稍微模糊的直角,有一个斜角。随着不同旋转次数的应用,似乎有从锐角到模糊对角线的进展。
代码
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);
}
我对这个问题的研究
左上角显示了所有以随机角度正确绘制的像素图
右上角显示了与左上角相同的实例,平移后像素图在模糊边界上,最右下角的像素图没有被绘制(或者更准确地说,被绘制为完全透明的像素,由于透明边缘被涂抹在整个图像上)
左下角显示所有像素图都旋转了 0.1 度,这导致了一个锐利的边界,当平移正方形使其重叠时,将正方形裁剪为矩形。
右下角显示了一个小范围的随机角度,介于 0.0 和 10.0 之间,这导致边缘稍微模糊,但仍然是垂直边缘,这看起来与左下角相似,但还有锐利的剪裁边缘,有还有轻微的渐变效果,因为靠近边缘的一些像素图也没有正确渲染。
我曾尝试在绘制像素图时在 QPainter
中关闭裁剪,但没有效果。
我曾尝试单独保存 QPainter
s 转换的副本,然后再将其设置回去,但没有任何效果。
我已经尝试升级到 Qt 6.0.3(声称已经解决了一些图形错误),问题仍然存在。
绝对坐标无关紧要,我可以将所有位置偏移 QPointF(-10000, 10000)
,平移到它们,消失点在 window 中的相同相对位置。
要查看正在运行的错误,请向外滚动,然后在 window 中单击并拖动以将像素图移动到屏幕的右下方,具体取决于您缩放的距离,一些将不再绘制像素图。
更新
我还发现,将原始 QPixmap
变大会使问题变得更糟,即边界在放大的级别上变得明显,并且会出现进一步的渲染偏差。请注意,它们仍被缩小到与以前相同的大小,只是源像素更多。
我把pixmap_(30, 50)
改成了pixmap_(300, 500)
上图显示当平移以将像素图向右下方移动时,它们比以前消失得更快(即,当进一步放大并向左上方进一步放大时),弯曲的箭头表示绘制的像素图的移动在消失边界之外的弧中,它们的移动速度似乎比在移动时绘制的正确像素图快。
编辑:仔细检查表明明显的圆周运动不是真实的,像素图出现和消失的顺序只是让它看起来那样。通过下面的更新,您可以看到有同心环,其中已经消失的像素图重新出现(非常短暂地)在正确的位置,但重新出现只是针对一个看起来很薄的 window比像素图的尺寸窄,所以绘制的内容再次出现卡在原地,但显示的部分被剪裁了。
更新
要看到像素图“粘”在边界处,您可以将 MainWindow::MainWindow()
的内容调整为
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();
}
这导致 看到似乎已被剪裁的正方形的右边缘。当四处移动它们时,正方形似乎卡在原地,而不是边界处的边缘消失,离边界最远的边缘首先消失。
这个问题很有意思。据我测试,你的代码看起来不错,我觉得这是一个 Qt 错误,我认为你需要向 Qt 报告它:https://bugreports.qt.io/。您应该 post 一段代码来说明问题,“更新”编辑中的第二段代码很好:它可以轻松重现问题。也许您还应该 post 一个小视频来说明当您缩放 in/out 或使用鼠标移动区域时出现的问题。
我尝试了一些替代方法希望找到解决方法,但我发现 none:
- 尝试使用
QImage
而不是 QPixmap
,同样的问题
- 试图从冻结的 png/qrc 文件加载像素图,同样的问题
- 尝试使用
QTransform
与 scale/translation/rotation 一起玩,同样的问题
- 已尝试 Linux 和 Windows 10:观察到相同的问题
注意:
- 如果不旋转(评论
paint.rotate(entity.rotation_);
),问题不可见
- 如果您的像素图是一个简单的单色正方形(使用
pixmap_.fill(QColor::fromRgba(0x12345600));
简单地用单一颜色填充您的像素图),则问题不再可见。这是最令人惊讶的,看起来图像中的一个像素被重新用作背景并弄乱了一切,但如果所有图像像素都相同,则不会导致任何显示问题。
Qt 团队提出的解决方法
"The issue can easily be worked around by enabling the SmoothPixmapTransform render hint on the painter"
作为错误报告给 Qt: https://bugreports.qt.io/browse/QTBUG-93475
我正在不同的位置重新绘制 QPixmap
多次,通过变换 QPainter 进行不同的旋转。在某些情况下 QPixmap
绘制不正确。下面的 GIF 显示了我最初发现此问题的 QPixmap
包含一个绿色圆柱体,请注意 GIF 左侧的渲染行为如何与预期的一样,但存在渲染不正确的边界。 QPixmap
内容似乎固定在原地,边缘的像素似乎在像素图的其余部分模糊不清。在 GIF 中 QPixmap
有一个洋红色背景,这是因为 QPainter::drawPixmap()
使用的 targetRect
也被用来单独填充像素图下方的矩形,这是因为我想检查目标矩形是否被正确计算。
最小可重现示例:
为了简单起见,我只是简单地用品红色像素填充 QPixmap
,具有 1 像素宽的透明边缘,这样涂抹会导致像素图完全消失。它没有显示图像“固定”在适当的位置,但它清楚地显示了边界,因为边界之外的像素图似乎消失了。
我自己一直在试验,我相信这完全是由于 QPainter
.
旋转的角度似乎有影响,如果所有的像素图都旋转到相同的角度,那么边界就会从一条模糊的对角线(模糊意味着消失的边界对于每个像素图不同)变为一个尖锐的 90 度角(其中尖锐意味着消失的边界对于所有像素图都是相同的)。
不同角度的范围似乎也有影响,如果随机生成的角度在 10 度的小范围内,那么边界只是一个稍微模糊的直角,有一个斜角。随着不同旋转次数的应用,似乎有从锐角到模糊对角线的进展。
代码
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);
}
我对这个问题的研究
左上角显示了所有以随机角度正确绘制的像素图
右上角显示了与左上角相同的实例,平移后像素图在模糊边界上,最右下角的像素图没有被绘制(或者更准确地说,被绘制为完全透明的像素,由于透明边缘被涂抹在整个图像上)
左下角显示所有像素图都旋转了 0.1 度,这导致了一个锐利的边界,当平移正方形使其重叠时,将正方形裁剪为矩形。
右下角显示了一个小范围的随机角度,介于 0.0 和 10.0 之间,这导致边缘稍微模糊,但仍然是垂直边缘,这看起来与左下角相似,但还有锐利的剪裁边缘,有还有轻微的渐变效果,因为靠近边缘的一些像素图也没有正确渲染。
我曾尝试在绘制像素图时在 QPainter
中关闭裁剪,但没有效果。
我曾尝试单独保存 QPainter
s 转换的副本,然后再将其设置回去,但没有任何效果。
我已经尝试升级到 Qt 6.0.3(声称已经解决了一些图形错误),问题仍然存在。
绝对坐标无关紧要,我可以将所有位置偏移 QPointF(-10000, 10000)
,平移到它们,消失点在 window 中的相同相对位置。
要查看正在运行的错误,请向外滚动,然后在 window 中单击并拖动以将像素图移动到屏幕的右下方,具体取决于您缩放的距离,一些将不再绘制像素图。
更新
我还发现,将原始 QPixmap
变大会使问题变得更糟,即边界在放大的级别上变得明显,并且会出现进一步的渲染偏差。请注意,它们仍被缩小到与以前相同的大小,只是源像素更多。
我把pixmap_(30, 50)
改成了pixmap_(300, 500)
上图显示当平移以将像素图向右下方移动时,它们比以前消失得更快(即,当进一步放大并向左上方进一步放大时),弯曲的箭头表示绘制的像素图的移动在消失边界之外的弧中,它们的移动速度似乎比在移动时绘制的正确像素图快。
编辑:仔细检查表明明显的圆周运动不是真实的,像素图出现和消失的顺序只是让它看起来那样。通过下面的更新,您可以看到有同心环,其中已经消失的像素图重新出现(非常短暂地)在正确的位置,但重新出现只是针对一个看起来很薄的 window比像素图的尺寸窄,所以绘制的内容再次出现卡在原地,但显示的部分被剪裁了。
更新
要看到像素图“粘”在边界处,您可以将 MainWindow::MainWindow()
的内容调整为
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();
}
这导致
这个问题很有意思。据我测试,你的代码看起来不错,我觉得这是一个 Qt 错误,我认为你需要向 Qt 报告它:https://bugreports.qt.io/。您应该 post 一段代码来说明问题,“更新”编辑中的第二段代码很好:它可以轻松重现问题。也许您还应该 post 一个小视频来说明当您缩放 in/out 或使用鼠标移动区域时出现的问题。
我尝试了一些替代方法希望找到解决方法,但我发现 none:
- 尝试使用
QImage
而不是QPixmap
,同样的问题 - 试图从冻结的 png/qrc 文件加载像素图,同样的问题
- 尝试使用
QTransform
与 scale/translation/rotation 一起玩,同样的问题 - 已尝试 Linux 和 Windows 10:观察到相同的问题
注意:
- 如果不旋转(评论
paint.rotate(entity.rotation_);
),问题不可见 - 如果您的像素图是一个简单的单色正方形(使用
pixmap_.fill(QColor::fromRgba(0x12345600));
简单地用单一颜色填充您的像素图),则问题不再可见。这是最令人惊讶的,看起来图像中的一个像素被重新用作背景并弄乱了一切,但如果所有图像像素都相同,则不会导致任何显示问题。
Qt 团队提出的解决方法
"The issue can easily be worked around by enabling the SmoothPixmapTransform render hint on the painter"