[英]QPainting QPixmap using clipping to gain performance
我正在嘗試在 Qt 中創建一個支持縮放的簡單圖像查看器。
要顯示圖像文件,我將其加載到 QImage 中並創建 QPixmap。
class NN: public QWidget{
Q_OBJECT
Q_DISABLE_COPY(NN)
public:
NN(QWidget* parent = nullptr) : QWidget(parent){
}
const QPixmap& pixmap() const
{
return m_pixmap;
}
void setPixmap(const QPixmap& px)
{
m_pixmap = px;
update();
}
protected:
void paintEvent(QPaintEvent*)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, false);
style()->drawItemPixmap(&painter, rect(), Qt::AlignCenter, m_pixmap.scaled(rect().size()));
}
private:
QPixmap m_pixmap;
};
(此小部件是 ScrollArea 的一部分)
這工作正常,但是當我嘗試加載大圖像並放大時,性能開始下降(滯后)。
我想將剪輯應用於drawItemPixmap()
方法,但我不太確定它如何以及是否有助於提高性能。
我的問題是剪輯的想法是否可行,如果可行,如何。 如果沒有,也許還有另一種獲得性能的方法?
當m_pixmap
和/或rect()
非常大時,大部分減速可能來自這里:
m_pixmap.scaled(rect().size())
在這里,您要求 Qt 創建一個與rect()
大小相同的新QPixmap
對象,這可能是一項非常昂貴的操作; 並將該QPixmap
對象傳遞到drawItemPixmap()
的調用中,該調用將僅繪制像素圖的一小部分,之后QPixmap
對象將被丟棄,並且整個過程將在您下次要重新繪制時再次完成目的。
不用說,這可能非常低效。
更有效的方法是調用QPainter::drawPixmap(const QRect & target, const Pixmap & pixmap, const QRect & source) ,如下所示:
painter.drawPixmap(rect(), m_pixmap, srcRect);
...並且drawPixmap()
將通過重新縮放m_pixmap
內的srcRect
的內容來繪制大小為rect()
(即只是小部件的大小rect()
的縮放像素圖; 比重新縮放整個m_pixmap
圖像更有效。
當然,您需要為srcRect
計算正確的 left/top/width/height 值,但這應該是簡單的一點代數。 (基本上只是根據小部件的當前縮放/平移狀態找出像素圖的哪個部分當前應該可見)
正如我在這個答案中指出的那樣,最好使用 QGraphicsView 進行圖像縮放,因此我會將代碼轉換為 C++:
#include <QtWidgets>
class ImageViewer: public QGraphicsView{
public:
ImageViewer(QWidget *parent=nullptr):QGraphicsView(parent){
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
// setAlignment(Qt::AlignLeft | Qt::AlignTop);
setAlignment(Qt::AlignCenter);
setBackgroundRole(QPalette::Dark);
QGraphicsScene *scene = new QGraphicsScene(this);
setScene(scene);
pixmapItem = new QGraphicsPixmapItem;
scene->addItem(pixmapItem);
}
bool setPixmap(const QPixmap & pixmap){
if(pixmap.isNull())
return false;
pixmapItem->setPixmap(pixmap);
return true;
}
void zoom(qreal f){
scale(f, f);
}
void zoomIn(){
zoom(factor);
}
void zoomOut(){
zoom(1.0 / factor);
}
void resetZoom(){
resetTransform();
}
void fitToWindow(){
fitInView(sceneRect(), Qt::KeepAspectRatio);
}
private:
qreal factor = 2.0;
QGraphicsPixmapItem * pixmapItem;
};
class MainWindow: public QMainWindow{
Q_OBJECT
public:
MainWindow(QWidget *parent=nullptr):QMainWindow(parent),
view(new ImageViewer)
{
setCentralWidget(view);
createActions();
createMenus();
resize(640, 480);
}
private Q_SLOTS:
void open(){
QStringList l;
for(const QByteArray & ba: QImageReader::supportedImageFormats()){
l << ("*." + QString::fromUtf8(ba));
}
QString filter = QString("Image Files(%1)").arg(l.join(" "));
QString fileName = QFileDialog::getOpenFileName(
this,
tr("Open Image"),
QDir::currentPath(),
filter
);
if(!fileMenu->isEmpty()){
bool loaded = view->setPixmap(QPixmap(fileName));
fitToWindowAct->setEnabled(loaded);
updateActions();
}
}
void fitToWindow(){
if(fitToWindowAct->isChecked())
view->fitToWindow();
else
view->resetZoom();
updateActions();
}
void about(){
QMessageBox::about(this, "ImageViewer", "ImageViewer");
}
private:
void createActions(){
openAct = new QAction("&Open...", this);
openAct->setShortcut(QKeySequence("Ctrl+O"));
connect(openAct, &QAction::triggered, this, &MainWindow::open);
exitAct = new QAction("E&xit", this);
exitAct->setShortcut(QKeySequence("Ctrl+Q"));
connect(exitAct, &QAction::triggered, this, &MainWindow::close);
zoomInAct = new QAction(tr("Zoom &In (25%)"), this);
zoomInAct->setShortcut(QKeySequence("Ctrl++"));
zoomInAct->setEnabled(false);
connect(zoomInAct, &QAction::triggered, view, &ImageViewer::zoomIn);
zoomOutAct = new QAction(tr("Zoom &Out (25%)"), this);
zoomOutAct->setShortcut(QKeySequence("Ctrl+-"));
zoomOutAct->setEnabled(false);
connect(zoomOutAct, &QAction::triggered, view, &ImageViewer::zoomOut);
normalSizeAct = new QAction(tr("&Normal Size"), this);
normalSizeAct->setShortcut(QKeySequence("Ctrl+S"));
normalSizeAct->setEnabled(false);
connect(normalSizeAct, &QAction::triggered, view, &ImageViewer::resetZoom);
fitToWindowAct = new QAction(tr("&Fit to Window"), this);
fitToWindowAct->setShortcut(QKeySequence("Ctrl+F"));
fitToWindowAct->setEnabled(false);
fitToWindowAct->setCheckable(true);
connect(fitToWindowAct, &QAction::triggered, this, &MainWindow::fitToWindow);
aboutAct = new QAction(tr("&About"), this);
connect(aboutAct, &QAction::triggered, this, &MainWindow::about);
aboutQtAct = new QAction(tr("About &Qt"), this);
connect(aboutQtAct, &QAction::triggered, qApp, &QApplication::aboutQt);
}
void createMenus(){
fileMenu = new QMenu(tr("&File"), this);
fileMenu->addAction(openAct);
fileMenu->addSeparator();
fileMenu->addAction(exitAct);
viewMenu = new QMenu(tr("&View"), this);
viewMenu->addAction(zoomInAct);
viewMenu->addAction(zoomOutAct);
viewMenu->addAction(normalSizeAct);
viewMenu->addSeparator();
viewMenu->addAction(fitToWindowAct);
helpMenu = new QMenu(tr("&Help"), this);
helpMenu->addAction(aboutAct);
helpMenu->addAction(aboutQtAct);
menuBar()->addMenu(fileMenu);
menuBar()->addMenu(viewMenu);
menuBar()->addMenu(helpMenu);
}
void updateActions(){
zoomInAct->setEnabled(not fitToWindowAct->isChecked());
zoomOutAct->setEnabled(not fitToWindowAct->isChecked());
normalSizeAct->setEnabled(not fitToWindowAct->isChecked());
}
ImageViewer *view;
QAction *openAct;
QAction *exitAct;
QAction *zoomInAct;
QAction *zoomOutAct;
QAction *normalSizeAct;
QAction *fitToWindowAct;
QAction *aboutAct;
QAction *aboutQtAct;
QMenu *fileMenu;
QMenu *viewMenu;
QMenu *helpMenu;
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.