From 66ce10aed5cd8c4b1df5b53645b92ee81b16d8e2 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 10 Sep 2013 20:30:59 -0400 Subject: [PATCH] gui: Implement a new surface viewer Adds support for showing individual pixels and zooming in on the image itself. All ready to be used for showing the actual original pixel values, rather than the fake qimage pixels which it shows right now. --- gui/CMakeLists.txt | 1 + gui/imageviewer.cpp | 54 +++++- gui/imageviewer.h | 10 ++ gui/mainwindow.cpp | 5 +- gui/pixelwidget.cpp | 407 ++++++++++++++++++++++++++++++++++++++++++ gui/pixelwidget.h | 116 ++++++++++++ gui/ui/imageviewer.ui | 149 +++++++++++++--- 7 files changed, 714 insertions(+), 28 deletions(-) create mode 100644 gui/pixelwidget.cpp create mode 100644 gui/pixelwidget.h diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 03e3d39..ad13475 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -14,6 +14,7 @@ set(qapitrace_SRCS jumpwidget.cpp mainwindow.cpp main.cpp + pixelwidget.cpp profiledialog.cpp profiletablemodel.cpp retracer.cpp diff --git a/gui/imageviewer.cpp b/gui/imageviewer.cpp index 1a1fe5a..cd03413 100644 --- a/gui/imageviewer.cpp +++ b/gui/imageviewer.cpp @@ -1,4 +1,5 @@ #include "imageviewer.h" +#include "pixelwidget.h" #include #include @@ -34,14 +35,28 @@ ImageViewer::ImageViewer(QWidget *parent) pal.setBrush(QPalette::Base, QBrush(px)); scrollAreaWidgetContents->setPalette(pal); + + m_pixelWidget = new PixelWidget(scrollAreaWidgetContents); + verticalLayout_2->addWidget(m_pixelWidget); + + rectLabel->hide(); + pixelLabel->hide(); + + connect(m_pixelWidget, SIGNAL(zoomChanged(int)), + zoomSpinBox, SLOT(setValue(int))); + connect(zoomSpinBox, SIGNAL(valueChanged(int)), + m_pixelWidget, SLOT(setZoom(int))); + connect(m_pixelWidget, SIGNAL(mousePosition(int, int)), + this, SLOT(showPixel(int, int))); + connect(m_pixelWidget, SIGNAL(gridGeometry(const QRect &)), + this, SLOT(showGrid(const QRect &))); } void ImageViewer::setImage(const QImage &image) { m_image = image; m_temp = m_image; - QPixmap px = QPixmap::fromImage(m_temp); - imageLabel->setPixmap(px); + m_pixelWidget->setSurface(m_image); updateGeometry(); } @@ -112,8 +127,7 @@ void ImageViewer::slotUpdate() } } - QPixmap px = QPixmap::fromImage(m_temp); - imageLabel->setPixmap(px); + m_pixelWidget->setSurface(m_temp); updateGeometry(); } @@ -138,4 +152,36 @@ QSize ImageViewer::sizeHint() const qMin(optimalWindowSize.height(), maxAvailableSize.height())); } +void ImageViewer::resizeEvent(QResizeEvent *e) +{ + QWidget::resizeEvent(e); +} + +void ImageViewer::showPixel(int x, int y) +{ + xSpinBox->setValue(x); + ySpinBox->setValue(y); + QColor clr = m_pixelWidget->colorAtCurrentPosition(); + pixelLabel->setText(tr("RGBA: (%1, %2, %3, %4)") + .arg(clr.red()) + .arg(clr.green()) + .arg(clr.blue()) + .arg(clr.alpha())); + pixelLabel->show(); +} + +void ImageViewer::showGrid(const QRect &rect) +{ + if (rect.isEmpty()) { + rectLabel->hide(); + return; + } + rectLabel->setText(tr("Grid: [%1, %2, %3, %4]") + .arg(rect.x()) + .arg(rect.y()) + .arg(rect.width()) + .arg(rect.height())); + rectLabel->show(); +} + #include "imageviewer.moc" diff --git a/gui/imageviewer.h b/gui/imageviewer.h index e878118..141ca17 100644 --- a/gui/imageviewer.h +++ b/gui/imageviewer.h @@ -4,6 +4,9 @@ #include "ui_imageviewer.h" #include +class PixelWidget; +class QLabel; + class ImageViewer : public QDialog, public Ui_ImageViewer { Q_OBJECT @@ -14,12 +17,19 @@ public: QSize sizeHint() const; +protected: + void resizeEvent(QResizeEvent *event); + private slots: void slotUpdate(); + void showPixel(int, int); + void showGrid(const QRect &rect); private: QImage m_image; QImage m_temp; + PixelWidget *m_pixelWidget; + QLabel *m_pixelLabel; }; diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 23729cd..00469bc 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -370,8 +370,11 @@ void MainWindow::replayTrace(bool dumpState, bool dumpThumbnails) void MainWindow::trimEvent() { + int trimIndex = 0; + + Q_ASSERT(m_trimEvent->type() == ApiTraceEvent::Call || + m_trimEvent->type() == ApiTraceEvent::Frame); - int trimIndex; if (m_trimEvent->type() == ApiTraceEvent::Call) { ApiTraceCall *call = static_cast(m_trimEvent); trimIndex = call->index(); diff --git a/gui/pixelwidget.cpp b/gui/pixelwidget.cpp new file mode 100644 index 0000000..88a1c5c --- /dev/null +++ b/gui/pixelwidget.cpp @@ -0,0 +1,407 @@ +/** + * The source code is based on qpixeltool.cpp. + * Original license follows. + */ + +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pixelwidget.h" + +#include +#include +#include +#ifndef QT_NO_CLIPBOARD +#include +#endif +#include +#include +#include +#include +#include + +#include + +PixelWidget::PixelWidget(QWidget *parent) + : QWidget(parent) +{ + setWindowTitle(QLatin1String("PixelTool")); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + + m_gridSize = 1; + m_gridActive = 0; + m_zoom = 1; + m_displayGridSize = false; + m_displayGridSizeId = 0; + + m_mouseDown = false; + + m_initialSize = QSize(250, 250); + + setMouseTracking(true); + setAttribute(Qt::WA_NoBackground); +} + +PixelWidget::~PixelWidget() +{ +} + +void PixelWidget::setSurface(const QImage &image) +{ + m_surface = image; + updateGeometry(); + update(); +} + +void PixelWidget::timerEvent(QTimerEvent *event) +{ + if (event->timerId() == m_displayGridSizeId) { + killTimer(m_displayGridSizeId); + m_displayGridSize = false; + } +} + +void render_string(QPainter *p, int w, int h, const QString &text, int flags) +{ + p->setBrush(QColor(255, 255, 255, 191)); + p->setPen(Qt::black); + QRect bounds; + p->drawText(0, 0, w, h, Qt::TextDontPrint | flags, text, &bounds); + + if (bounds.x() == 0) bounds.adjust(0, 0, 10, 0); + else bounds.adjust(-10, 0, 0, 0); + + if (bounds.y() == 0) bounds.adjust(0, 0, 0, 10); + else bounds.adjust(0, -10, 0, 0); + + p->drawRect(bounds); + p->drawText(bounds, flags, text); +} + +void PixelWidget::paintEvent(QPaintEvent *) +{ + QPainter p(this); + + int w = width(); + int h = height(); + + p.save(); + p.scale(zoomValue(), zoomValue()); + p.drawImage(0, 0, m_surface); + p.scale(1/zoomValue(), 1/zoomValue()); + p.restore(); + + // Draw the grid on top. + if (m_gridActive) { + p.setPen(m_gridActive == 1 ? Qt::black : Qt::white); + int incr = m_gridSize * zoomValue(); + for (int x=0; xkey()) { + case Qt::Key_Plus: + increaseZoom(); + break; + case Qt::Key_Minus: + decreaseZoom(); + break; + case Qt::Key_PageUp: + setGridSize(m_gridSize + 1); + break; + case Qt::Key_PageDown: + setGridSize(m_gridSize - 1); + break; + case Qt::Key_G: + toggleGrid(); + break; + case Qt::Key_C: + if (e->modifiers() & Qt::ControlModifier) + copyToClipboard(); + break; + case Qt::Key_S: + if (e->modifiers() & Qt::ControlModifier) { + releaseKeyboard(); + saveToFile(); + } + break; + case Qt::Key_Control: + grabKeyboard(); + break; + } +} + +void PixelWidget::keyReleaseEvent(QKeyEvent *e) +{ + switch(e->key()) { + case Qt::Key_Control: + releaseKeyboard(); + break; + default: + break; + } +} + +void PixelWidget::resizeEvent(QResizeEvent *e) +{ + QWidget::resizeEvent(e); +} + +void PixelWidget::mouseMoveEvent(QMouseEvent *e) +{ + if (m_mouseDown) + m_dragCurrent = e->pos(); + + int x = e->x() / zoomValue(); + int y = e->y() / zoomValue(); + + if (x < m_surface.width() && y < m_surface.height() && x >= 0 && y >= 0) { + m_currentColor = m_surface.pixel(x, y); + } else + m_currentColor = QColor(); + + emit mousePosition(x, y); + update(); +} + +void PixelWidget::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) + m_mouseDown = true; + m_dragStart = e->pos(); +} + +void PixelWidget::mouseReleaseEvent(QMouseEvent *) +{ + m_mouseDown = false; +} + +void PixelWidget::contextMenuEvent(QContextMenuEvent *e) +{ + QMenu menu; + + QAction title(QLatin1String("Surface Pixel Tool"), &menu); + title.setEnabled(false); + + // Grid color options... + QActionGroup gridGroup(this); + QAction whiteGrid(QLatin1String("White grid"), &gridGroup); + whiteGrid.setCheckable(true); + whiteGrid.setChecked(m_gridActive == 2); + whiteGrid.setShortcut(QKeySequence(Qt::Key_G)); + QAction blackGrid(QLatin1String("Black grid"), &gridGroup); + blackGrid.setCheckable(true); + blackGrid.setChecked(m_gridActive == 1); + blackGrid.setShortcut(QKeySequence(Qt::Key_G)); + QAction noGrid(QLatin1String("No grid"), &gridGroup); + noGrid.setCheckable(true); + noGrid.setChecked(m_gridActive == 0); + noGrid.setShortcut(QKeySequence(Qt::Key_G)); + + // Grid size options + QAction incrGrid(QLatin1String("Increase grid size"), &menu); + incrGrid.setShortcut(QKeySequence(Qt::Key_PageUp)); + connect(&incrGrid, SIGNAL(triggered()), this, SLOT(increaseGridSize())); + QAction decrGrid(QLatin1String("Decrease grid size"), &menu); + decrGrid.setShortcut(QKeySequence(Qt::Key_PageDown)); + connect(&decrGrid, SIGNAL(triggered()), this, SLOT(decreaseGridSize())); + + // Zoom options + QAction incrZoom(QLatin1String("Zoom in"), &menu); + incrZoom.setShortcut(QKeySequence(Qt::Key_Plus)); + connect(&incrZoom, SIGNAL(triggered()), this, SLOT(increaseZoom())); + QAction decrZoom(QLatin1String("Zoom out"), &menu); + decrZoom.setShortcut(QKeySequence(Qt::Key_Minus)); + connect(&decrZoom, SIGNAL(triggered()), this, SLOT(decreaseZoom())); + + // Copy to clipboard / save + QAction save(QLatin1String("Save as image"), &menu); + save.setShortcut(QKeySequence(QLatin1String("Ctrl+S"))); + connect(&save, SIGNAL(triggered()), this, SLOT(saveToFile())); +#ifndef QT_NO_CLIPBOARD + QAction copy(QLatin1String("Copy to clipboard"), &menu); + copy.setShortcut(QKeySequence(QLatin1String("Ctrl+C"))); + connect(©, SIGNAL(triggered()), this, SLOT(copyToClipboard())); +#endif + + menu.addAction(&title); + menu.addSeparator(); + menu.addAction(&whiteGrid); + menu.addAction(&blackGrid); + menu.addAction(&noGrid); + menu.addSeparator(); + menu.addAction(&incrGrid); + menu.addAction(&decrGrid); + menu.addSeparator(); + menu.addAction(&incrZoom); + menu.addAction(&decrZoom); + menu.addSeparator(); + menu.addAction(&save); +#ifndef QT_NO_CLIPBOARD + menu.addAction(©); +#endif + + menu.exec(mapToGlobal(e->pos())); + + if (noGrid.isChecked()) m_gridActive = 0; + else if (blackGrid.isChecked()) m_gridActive = 1; + else m_gridActive = 2; +} + +QSize PixelWidget::sizeHint() const +{ + if (m_surface.isNull()) + return m_initialSize; + + QSize sz(m_surface.width() * zoomValue(), + m_surface.height() * zoomValue()); + return sz; +} + +void PixelWidget::startGridSizeVisibleTimer() +{ + if (m_gridActive) { + if (m_displayGridSizeId > 0) + killTimer(m_displayGridSizeId); + m_displayGridSizeId = startTimer(5000); + m_displayGridSize = true; + update(); + } +} + +void PixelWidget::setZoom(int zoom) +{ + if (zoom > 0 && zoom != m_zoom) { + QPoint pos = m_lastMousePos; + m_lastMousePos = QPoint(); + m_zoom = zoom; + m_lastMousePos = pos; + m_dragStart = m_dragCurrent = QPoint(); + + if (m_zoom == 1) + m_gridActive = 0; + else if (!m_gridActive) + m_gridActive = 1; + + zoomChanged(m_zoom); + updateGeometry(); + update(); + } +} + +void PixelWidget::toggleGrid() +{ + if (++m_gridActive > 2) + m_gridActive = 0; + update(); +} + +void PixelWidget::setGridSize(int gridSize) +{ + if (m_gridActive && gridSize > 0) { + m_gridSize = gridSize; + startGridSizeVisibleTimer(); + update(); + } +} + +void PixelWidget::copyToClipboard() +{ + QClipboard *cb = QApplication::clipboard(); + cb->setImage(m_surface); +} + +void PixelWidget::saveToFile() +{ + QString name = QFileDialog::getSaveFileName(this, QLatin1String("Save as image"), QString(), QLatin1String("*.png")); + if (!name.isEmpty()) { + if (!name.endsWith(QLatin1String(".png"))) + name.append(QLatin1String(".png")); + m_surface.save(name, "PNG"); + } +} + +QColor PixelWidget::colorAtCurrentPosition() const +{ + return m_currentColor; +} + +#include "pixelwidget.moc" diff --git a/gui/pixelwidget.h b/gui/pixelwidget.h new file mode 100644 index 0000000..0db7e01 --- /dev/null +++ b/gui/pixelwidget.h @@ -0,0 +1,116 @@ +/** + * The source code is based on qpixeltool.h. + * Original license follows. + */ + +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PIXELWIDGET_H +#define PIXELWIDGET_H + +#include +#include + +class PixelWidget : public QWidget +{ + Q_OBJECT +public: + PixelWidget(QWidget *parent = 0); + ~PixelWidget(); + + void setSurface(const QImage &image); + + QColor colorAtCurrentPosition() const; + +signals: + void zoomChanged(int); + void mousePosition(int x, int y); + void gridGeometry(const QRect &rect); + +public: + void timerEvent(QTimerEvent *event); + void paintEvent(QPaintEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + void resizeEvent(QResizeEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void contextMenuEvent(QContextMenuEvent *event); + + QSize sizeHint() const; + +public slots: + void setZoom(int zoom); + void setGridSize(int gridSize); + void toggleGrid(); + void copyToClipboard(); + void saveToFile(); + void increaseGridSize() { setGridSize(m_gridSize + 1); } + void decreaseGridSize() { setGridSize(m_gridSize - 1); } + void increaseZoom() { setZoom(m_zoom + 1); } + void decreaseZoom() { setZoom(m_zoom - 1); } + +private: + void startGridSizeVisibleTimer(); + double zoomValue() const { return m_zoom; } + + bool m_displayGridSize; + bool m_mouseDown; + + int m_gridActive; + int m_zoom; + int m_gridSize; + + int m_displayZoomId; + int m_displayGridSizeId; + + QPoint m_lastMousePos; + QPoint m_dragStart; + QPoint m_dragCurrent; + QImage m_surface; + + QSize m_initialSize; + QColor m_currentColor; +}; + +#endif // PIXELWIDGET_H diff --git a/gui/ui/imageviewer.ui b/gui/ui/imageviewer.ui index a595ba7..cd0a9d8 100644 --- a/gui/ui/imageviewer.ui +++ b/gui/ui/imageviewer.ui @@ -19,30 +19,111 @@ true + + Qt::AlignCenter + 0 0 - 811 - 615 + 805 + 539 - - - - - - - - Qt::AlignCenter - - - - + + + + + + + X: + + + xSpinBox + + + + + + + false + + + true + + + QAbstractSpinBox::NoButtons + + + false + + + 16000 + + + + + + + Y: + + + xSpinBox + + + + + + + false + + + true + + + QAbstractSpinBox::NoButtons + + + false + + + 16000 + + + + + + + TextLabel + + + + + + + TextLabel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -98,17 +179,39 @@ - - - Qt::Horizontal + + + Zoom: - - - 40 - 20 - + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + + + + + + + + + x + + + + + + 1 + + + 15 + + + 1 + + + 1 + + -- 2.43.0