簡體   English   中英

QPainting QPixmap 使用裁剪來獲得性能

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM