]> git.cworth.org Git - apitrace/commitdiff
Merge branch 'gui-thumbnails'
authorJosé Fonseca <jose.r.fonseca@gmail.com>
Sat, 24 Mar 2012 09:30:51 +0000 (09:30 +0000)
committerJosé Fonseca <jose.r.fonseca@gmail.com>
Sat, 24 Mar 2012 09:30:51 +0000 (09:30 +0000)
16 files changed:
common/image.hpp
common/image_pnm.cpp
gui/apicalldelegate.cpp
gui/apitrace.cpp
gui/apitrace.h
gui/apitracecall.cpp
gui/apitracecall.h
gui/apitracemodel.cpp
gui/apitracemodel.h
gui/main.cpp
gui/mainwindow.cpp
gui/mainwindow.h
gui/retracer.cpp
gui/retracer.h
gui/traceloader.cpp
gui/ui/mainwindow.ui

index 6316791bba210763dd79d10ed52afd6a20bfef9c..ea3eefb0d589c6e3f342860b8a1bd9720b3011bf 100644 (file)
@@ -108,6 +108,8 @@ bool writePixelsToBuffer(unsigned char *pixels,
 Image *
 readPNG(const char *filename);
 
+const char *
+readPNMHeader(const char *buffer, size_t size, unsigned *channels, unsigned *width, unsigned *height);
 
 } /* namespace image */
 
index c95f640fcc1ee6ff69af318f3bc52ba0d4edf1a8..f9cd05d1fbc7593dafcb6e5bf77d48beb7505a1d 100644 (file)
@@ -28,6 +28,7 @@
 #include <assert.h>
 #include <string.h>
 #include <stdint.h>
+#include <stdio.h>
 
 #include "image.hpp"
 
@@ -108,5 +109,55 @@ Image::writePNM(std::ostream &os, const char *comment) const {
     }
 }
 
+const char *
+readPNMHeader(const char *buffer, size_t bufferSize, unsigned *channels, unsigned *width, unsigned *height)
+{
+    *channels = 0;
+    *width = 0;
+    *height = 0;
+
+    const char *currentBuffer = buffer;
+    const char *nextBuffer;
+
+    // parse number of channels
+    int scannedChannels = sscanf(currentBuffer, "P%d\n", channels);
+    if (scannedChannels != 1) { // validate scanning of channels
+        // invalid channel line
+        return buffer;
+    }
+    // convert channel token to number of channels
+    *channels = (*channels == 5) ? 1 : 3;
+
+    // advance past channel line
+    nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+    bufferSize -= nextBuffer - currentBuffer;
+    currentBuffer = nextBuffer;
+
+    // skip over optional comment
+    if (*currentBuffer == '#') {
+        // advance past comment line
+        nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+        bufferSize -= nextBuffer - currentBuffer;
+        currentBuffer = nextBuffer;
+    }
+
+    // parse dimensions of image
+    int scannedDimensions = sscanf(currentBuffer, "%d %d\n", width, height);
+    if (scannedDimensions != 2) { // validate scanning of dimensions
+        // invalid dimension line
+        return buffer;
+    }
+
+    // advance past dimension line
+    nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+    bufferSize -= nextBuffer - currentBuffer;
+    currentBuffer = nextBuffer;
+
+    // skip over "255\n" at end of header
+    nextBuffer = (const char *) memchr((const void *) currentBuffer, '\n', bufferSize) + 1;
+
+    // return start of image data
+    return nextBuffer;
+}
 
 } /* namespace image */
index 11ed3a5ca03d5c59c96b8c7bc05c67e2e5583bf8..c68717580ddc6e5e5d94f3b95eb9fd27b8d6cf45 100644 (file)
@@ -27,12 +27,23 @@ void ApiCallDelegate::paint(QPainter *painter,
     Q_ASSERT(index.column() == 0);
 
     if (event) {
-        QPoint offset;
+        QPoint offset = QPoint(0, 0);
         QStaticText text = event->staticText();
         //text.setTextWidth(option.rect.width());
         //QStyledItemDelegate::paint(painter, option, index);
         QStyle *style = QApplication::style();
         style->drawControl(QStyle::CE_ItemViewItem, &option, painter, 0);
+
+        // draw thumbnail of frame
+        if(event->type() == ApiTraceEvent::Frame) {
+            ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+            const QImage & thumbnail = frame->thumbnail();
+            if (!thumbnail.isNull()) {
+                painter->drawImage(option.rect.topLeft() + offset, thumbnail);
+                offset += QPoint(option.rect.height() + 16, 0);
+            }
+        }
+
         if (event->hasState()) {
             QPixmap px = m_stateEmblem.pixmap(option.rect.height(),
                                               option.rect.height());
index 5758b0750a3acb0ca743af3908bafe6a21d59796..89385b9c58cfe200f4ba80e4cd4eec15bad2574b 100644 (file)
@@ -115,7 +115,7 @@ ApiTrace::FrameMarker ApiTrace::frameMarker() const
     return m_frameMarker;
 }
 
-QList<ApiTraceFrame*> ApiTrace::frames() const
+const QList<ApiTraceFrame*> & ApiTrace::frames() const
 {
     return m_frames;
 }
@@ -271,9 +271,11 @@ void ApiTrace::loadFrame(ApiTraceFrame *frame)
 
 void ApiTrace::finishedParsing()
 {
-    ApiTraceFrame *firstFrame = m_frames[0];
-    if (firstFrame && !firstFrame->isLoaded()) {
-        loadFrame(firstFrame);
+    if (!m_frames.isEmpty()) {
+        ApiTraceFrame *firstFrame = m_frames[0];
+        if (firstFrame && !firstFrame->isLoaded()) {
+            loadFrame(firstFrame);
+        }
     }
 }
 
@@ -450,7 +452,7 @@ int ApiTrace::callInFrame(int callIdx) const
 {
     unsigned numCalls = 0;
 
-    for (int frameIdx = 0; frameIdx <= m_frames.size(); ++frameIdx) {
+    for (int frameIdx = 0; frameIdx < m_frames.size(); ++frameIdx) {
         const ApiTraceFrame *frame = m_frames[frameIdx];
         unsigned numCallsInFrame =  frame->isLoaded()
                 ? frame->numChildren()
@@ -496,4 +498,21 @@ bool ApiTrace::isFrameLoading(ApiTraceFrame *frame) const
     return m_loadingFrames.contains(frame);
 }
 
+void ApiTrace::bindThumbnailsToFrames(const QList<QImage> &thumbnails)
+{
+    QList<ApiTraceFrame *> frames = m_frames;
+
+    QList<QImage>::const_iterator thumbnail = thumbnails.begin();
+
+    foreach (ApiTraceFrame *frame, frames) {
+        if (thumbnail != thumbnails.end()) {
+            frame->setThumbnail(*thumbnail);
+
+            ++thumbnail;
+
+            emit changed(frame);
+        }
+    }
+}
+
 #include "apitrace.moc"
index 2833f60cddf90d5063cf7b40d8f951d5b1e0181c..a5c0329242b497ca0376f64aed4a80d66a746010 100644 (file)
@@ -67,7 +67,7 @@ public:
 
     ApiTraceCall *callWithIndex(int idx) const;
 
-    QList<ApiTraceFrame*> frames() const;
+    const QList<ApiTraceFrame*> & frames() const;
     ApiTraceFrame *frameAt(int idx) const;
     int numFrames() const;
     int numCallsInFrame(int idx) const;
@@ -100,6 +100,7 @@ public slots:
     void findCallIndex(int index);
     void setCallError(const ApiTraceError &error);
 
+    void bindThumbnailsToFrames(const QList<QImage> &thumbnails);
 
 signals:
     void loadTrace(const QString &name);
@@ -109,7 +110,7 @@ signals:
     void finishedLoadingTrace();
     void invalidated();
     void framesInvalidated();
-    void changed(ApiTraceCall *call);
+    void changed(ApiTraceEvent *event);
     void startedSaving();
     void saved();
     void findResult(const ApiTrace::SearchRequest &request,
index 267dc0980edeaaa7ea02c26de9e981d25e96b6fa..dfaaeef0e478a6a7b7a9fba76b02ec510a67bcba 100644 (file)
@@ -1197,3 +1197,13 @@ unsigned ApiTraceFrame::lastCallIndex() const
         return m_lastCallIndex;
     }
 }
+
+void ApiTraceFrame::setThumbnail(const QImage & thumbnail)
+{
+    m_thumbnail = thumbnail;
+}
+
+const QImage & ApiTraceFrame::thumbnail() const
+{
+    return m_thumbnail;
+}
index 3a9faafd495e851c281609d5c7c94e9431020c7d..945ab0cb8eedbff9cf1c29389c1a06bc839ea2e9 100644 (file)
@@ -335,6 +335,10 @@ public:
 
     void setLastCallIndex(unsigned index);
     unsigned lastCallIndex() const;
+
+    void setThumbnail(const QImage & thumbnail);
+    const QImage & thumbnail() const;
+
 private:
     ApiTrace *m_parentTrace;
     quint64 m_binaryDataSize;
@@ -342,6 +346,7 @@ private:
     bool m_loaded;
     unsigned m_callsToLoad;
     unsigned m_lastCallIndex;
+    QImage m_thumbnail;
 };
 Q_DECLARE_METATYPE(ApiTraceFrame*);
 
index 7303ae1c31e836bb2e507fe9b5fd34af657fa574..0863c1b5bc0eb19ad1b649b9d83442483a2fc5ad 100644 (file)
@@ -248,8 +248,8 @@ void ApiTraceModel::setApiTrace(ApiTrace *trace)
             this, SLOT(beginAddingFrames(int, int)));
     connect(m_trace, SIGNAL(endAddingFrames()),
             this, SLOT(endAddingFrames()));
-    connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
-            this, SLOT(callChanged(ApiTraceCall*)));
+    connect(m_trace, SIGNAL(changed(ApiTraceEvent*)),
+            this, SLOT(changed(ApiTraceEvent*)));
     connect(m_trace, SIGNAL(beginLoadingFrame(ApiTraceFrame*,int)),
             this, SLOT(beginLoadingFrame(ApiTraceFrame*,int)));
     connect(m_trace, SIGNAL(endLoadingFrame(ApiTraceFrame*)),
@@ -294,7 +294,7 @@ void ApiTraceModel::stateSetOnEvent(ApiTraceEvent *event)
         emit dataChanged(index, index);
     } else if (event->type() == ApiTraceEvent::Frame) {
         ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
-        const QList<ApiTraceFrame*> frames = m_trace->frames();
+        const QList<ApiTraceFrame*> frames = m_trace->frames();
         int row = frames.indexOf(frame);
         QModelIndex index = createIndex(row, 0, frame);
         emit dataChanged(index, index);
@@ -318,6 +318,15 @@ QModelIndex ApiTraceModel::indexForCall(ApiTraceCall *call) const
     return createIndex(row, 0, call);
 }
 
+void ApiTraceModel::changed(ApiTraceEvent *event)
+{
+    if (event->type() == ApiTraceEvent::Call) {
+        callChanged(static_cast<ApiTraceCall*>(event));
+    } else if (event->type() == ApiTraceEvent::Frame) {
+        frameChanged(static_cast<ApiTraceFrame*>(event));
+    }
+}
+
 void ApiTraceModel::callChanged(ApiTraceCall *call)
 {
     ApiTrace *trace = call->parentFrame()->parentTrace();
@@ -339,6 +348,14 @@ void ApiTraceModel::callChanged(ApiTraceCall *call)
     emit dataChanged(index, index);
 }
 
+void ApiTraceModel::frameChanged(ApiTraceFrame *frame)
+{
+    const QList<ApiTraceFrame*> & frames = m_trace->frames();
+    int row = frames.indexOf(frame);
+    QModelIndex index = createIndex(row, 0, frame);
+    emit dataChanged(index, index);
+}
+
 void ApiTraceModel::endAddingFrames()
 {
     endInsertRows();
index fe6b5cea69deb5f0702c8af239066b1db5b806bb..e7354aa6350191ad55aee5a8abe9d675079dfeeb 100644 (file)
@@ -54,7 +54,9 @@ private slots:
     void invalidateFrames();
     void beginAddingFrames(int oldCount, int numAdded);
     void endAddingFrames();
+    void changed(ApiTraceEvent *event);
     void callChanged(ApiTraceCall *call);
+    void frameChanged(ApiTraceFrame *frame);
     void beginLoadingFrame(ApiTraceFrame *frame, int numAdded);
     void endLoadingFrame(ApiTraceFrame *frame);
 
index d7af53dde789c219e76dae1428254f3b58337dfd..18e07f1ffcb87275aaa777184a22fb839c31f2fd 100644 (file)
@@ -6,12 +6,14 @@
 #include <QApplication>
 #include <QMetaType>
 #include <QVariant>
+#include <QImage>
 
 Q_DECLARE_METATYPE(QList<ApiTraceFrame*>);
 Q_DECLARE_METATYPE(QVector<ApiTraceCall*>);
 Q_DECLARE_METATYPE(Qt::CaseSensitivity);
 Q_DECLARE_METATYPE(ApiTrace::SearchResult);
 Q_DECLARE_METATYPE(ApiTrace::SearchRequest);
+Q_DECLARE_METATYPE(QList<QImage>);
 
 static void usage(void)
 {
@@ -28,6 +30,7 @@ int main(int argc, char **argv)
     qRegisterMetaType<Qt::CaseSensitivity>();
     qRegisterMetaType<ApiTrace::SearchResult>();
     qRegisterMetaType<ApiTrace::SearchRequest>();
+    qRegisterMetaType<QList<QImage> >();
     QStringList args = app.arguments();
 
     int i = 1;
index 039889ca1aa5bd0a9987cb35d2cc38eb808654cc..fce9984a33c89fc8b34b8ecca08d597ab3af3c42 100644 (file)
@@ -176,7 +176,7 @@ void MainWindow::replayStart()
             dlgUi.doubleBufferingCB->isChecked());
         m_retracer->setBenchmarking(
             !dlgUi.errorCheckCB->isChecked());
-        replayTrace(false);
+        replayTrace(false, true);
     }
 }
 
@@ -186,6 +186,7 @@ void MainWindow::replayStop()
     m_ui.actionStop->setEnabled(false);
     m_ui.actionReplay->setEnabled(true);
     m_ui.actionLookupState->setEnabled(true);
+    m_ui.actionShowThumbnails->setEnabled(true);
 }
 
 void MainWindow::newTraceFile(const QString &fileName)
@@ -198,11 +199,13 @@ void MainWindow::newTraceFile(const QString &fileName)
     if (fileName.isEmpty()) {
         m_ui.actionReplay->setEnabled(false);
         m_ui.actionLookupState->setEnabled(false);
+        m_ui.actionShowThumbnails->setEnabled(false);
         setWindowTitle(tr("QApiTrace"));
     } else {
         QFileInfo info(fileName);
         m_ui.actionReplay->setEnabled(true);
         m_ui.actionLookupState->setEnabled(true);
+        m_ui.actionShowThumbnails->setEnabled(true);
         setWindowTitle(
             tr("QApiTrace - %1").arg(info.fileName()));
     }
@@ -213,6 +216,7 @@ void MainWindow::replayFinished(const QString &output)
     m_ui.actionStop->setEnabled(false);
     m_ui.actionReplay->setEnabled(true);
     m_ui.actionLookupState->setEnabled(true);
+    m_ui.actionShowThumbnails->setEnabled(true);
 
     m_progressBar->hide();
     if (output.length() < 80) {
@@ -234,6 +238,7 @@ void MainWindow::replayError(const QString &message)
     m_ui.actionStop->setEnabled(false);
     m_ui.actionReplay->setEnabled(true);
     m_ui.actionLookupState->setEnabled(true);
+    m_ui.actionShowThumbnails->setEnabled(true);
     m_stateEvent = 0;
     m_nonDefaultsLookupEvent = 0;
 
@@ -268,7 +273,7 @@ void MainWindow::finishedLoadingTrace()
     }
 }
 
-void MainWindow::replayTrace(bool dumpState)
+void MainWindow::replayTrace(bool dumpState, bool dumpThumbnails)
 {
     if (m_trace->fileName().isEmpty()) {
         return;
@@ -277,6 +282,7 @@ void MainWindow::replayTrace(bool dumpState)
     m_retracer->setFileName(m_trace->fileName());
     m_retracer->setAPI(m_api);
     m_retracer->setCaptureState(dumpState);
+    m_retracer->setCaptureThumbnails(dumpThumbnails);
     if (m_retracer->captureState() && m_selectedEvent) {
         int index = 0;
         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
@@ -300,9 +306,17 @@ void MainWindow::replayTrace(bool dumpState)
 
     m_ui.actionStop->setEnabled(true);
     m_progressBar->show();
-    if (dumpState) {
-        statusBar()->showMessage(
-            tr("Looking up the state..."));
+    if (dumpState || dumpThumbnails) {
+        if (dumpState && dumpThumbnails) {
+            statusBar()->showMessage(
+                tr("Looking up the state and capturing thumbnails..."));
+        } else if (dumpState) {
+            statusBar()->showMessage(
+                tr("Looking up the state..."));
+        } else if (dumpThumbnails) {
+            statusBar()->showMessage(
+                tr("Capturing thumbnails..."));
+        }
     } else {
         statusBar()->showMessage(
             tr("Replaying the trace file..."));
@@ -326,7 +340,12 @@ void MainWindow::lookupState()
         return;
     }
     m_stateEvent = m_selectedEvent;
-    replayTrace(true);
+    replayTrace(true, false);
+}
+
+void MainWindow::showThumbnails()
+{
+    replayTrace(false, true);
 }
 
 MainWindow::~MainWindow()
@@ -724,8 +743,8 @@ void MainWindow::initConnections()
             this, SLOT(slotStartedSaving()));
     connect(m_trace, SIGNAL(saved()),
             this, SLOT(slotSaved()));
-    connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
-            this, SLOT(slotTraceChanged(ApiTraceCall*)));
+    connect(m_trace, SIGNAL(changed(ApiTraceEvent*)),
+            this, SLOT(slotTraceChanged(ApiTraceEvent*)));
     connect(m_trace, SIGNAL(findResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)),
             this, SLOT(slotSearchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)));
     connect(m_trace, SIGNAL(foundFrameStart(ApiTraceFrame*)),
@@ -741,6 +760,8 @@ void MainWindow::initConnections()
             this, SLOT(replayError(const QString&)));
     connect(m_retracer, SIGNAL(foundState(ApiTraceState*)),
             this, SLOT(replayStateFound(ApiTraceState*)));
+    connect(m_retracer, SIGNAL(foundThumbnails(const QList<QImage>&)),
+            this, SLOT(replayThumbnailsFound(const QList<QImage>&)));
     connect(m_retracer, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
             this, SLOT(slotRetraceErrors(const QList<ApiTraceError>&)));
 
@@ -778,6 +799,8 @@ void MainWindow::initConnections()
             this, SLOT(replayStop()));
     connect(m_ui.actionLookupState, SIGNAL(triggered()),
             this, SLOT(lookupState()));
+    connect(m_ui.actionShowThumbnails, SIGNAL(triggered()),
+            this, SLOT(showThumbnails()));
     connect(m_ui.actionOptions, SIGNAL(triggered()),
             this, SLOT(showSettings()));
 
@@ -838,6 +861,11 @@ void MainWindow::replayStateFound(ApiTraceState *state)
     m_nonDefaultsLookupEvent = 0;
 }
 
+void MainWindow::replayThumbnailsFound(const QList<QImage> &thumbnails)
+{
+    m_trace->bindThumbnailsToFrames(thumbnails);
+}
+
 void MainWindow::slotGoTo()
 {
     m_searchWidget->hide();
@@ -1013,11 +1041,14 @@ ApiTraceFrame * MainWindow::selectedFrame() const
     return NULL;
 }
 
-void MainWindow::slotTraceChanged(ApiTraceCall *call)
+void MainWindow::slotTraceChanged(ApiTraceEvent *event)
 {
-    Q_ASSERT(call);
-    if (call == m_selectedEvent) {
-        m_ui.detailsWebView->setHtml(call->toHtml());
+    Q_ASSERT(event);
+    if (event == m_selectedEvent) {
+        if (event->type() == ApiTraceEvent::Call) {
+            ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
+            m_ui.detailsWebView->setHtml(call->toHtml());
+        }
     }
 }
 
index 66be2e20d88692802fe31c5952947077db237747..d65aa0e84749ea6df648d2b9f2a79ff628e0565a 100644 (file)
@@ -8,6 +8,8 @@
 
 #include <QMainWindow>
 #include <QProcess>
+#include <QList>
+#include <QImage>
 
 class ApiTrace;
 class ApiTraceCall;
@@ -48,11 +50,13 @@ private slots:
     void replayStop();
     void replayFinished(const QString &output);
     void replayStateFound(ApiTraceState *state);
+    void replayThumbnailsFound(const QList<QImage> &thumbnails);
     void replayError(const QString &msg);
     void startedLoadingTrace();
     void loadProgess(int percent);
     void finishedLoadingTrace();
     void lookupState();
+    void showThumbnails();
     void showSettings();
     void openHelp(const QUrl &url);
     void showSurfacesMenu(const QPoint &pos);
@@ -72,7 +76,7 @@ private slots:
     void slotSaved();
     void slotGoFrameStart();
     void slotGoFrameEnd();
-    void slotTraceChanged(ApiTraceCall *call);
+    void slotTraceChanged(ApiTraceEvent *event);
     void slotRetraceErrors(const QList<ApiTraceError> &errors);
     void slotErrorSelected(QTreeWidgetItem *current);
     void slotSearchResult(const ApiTrace::SearchRequest &request,
@@ -86,7 +90,7 @@ private:
     void initObjects();
     void initConnections();
     void newTraceFile(const QString &fileName);
-    void replayTrace(bool dumpState);
+    void replayTrace(bool dumpState, bool dumpThumbnails);
     void fillStateForFrame();
 
     /* there's a difference between selected frame/call and
index 17ac14d7d8d13a97da4f2e1a61455770e9b1104d..7a49ae9b61753daa733b1f9752553d1cd9805d54 100644 (file)
@@ -2,8 +2,12 @@
 
 #include "apitracecall.h"
 
+#include "image.hpp"
+
 #include <QDebug>
 #include <QVariant>
+#include <QList>
+#include <QImage>
 
 #include <qjson/parser.h>
 
@@ -83,6 +87,16 @@ void Retracer::setCaptureState(bool enable)
     m_captureState = enable;
 }
 
+bool Retracer::captureThumbnails() const
+{
+    return m_captureThumbnails;
+}
+
+void Retracer::setCaptureThumbnails(bool enable)
+{
+    m_captureThumbnails = enable;
+}
+
 
 void Retracer::run()
 {
@@ -94,6 +108,7 @@ void Retracer::run()
     retrace->setBenchmarking(m_benchmarking);
     retrace->setDoubleBuffered(m_doubleBuffered);
     retrace->setCaptureState(m_captureState);
+    retrace->setCaptureThumbnails(m_captureThumbnails);
     retrace->setCaptureAtCallNumber(m_captureCall);
 
     connect(retrace, SIGNAL(finished(const QString&)),
@@ -106,6 +121,8 @@ void Retracer::run()
             this, SIGNAL(error(const QString&)));
     connect(retrace, SIGNAL(foundState(ApiTraceState*)),
             this, SIGNAL(foundState(ApiTraceState*)));
+    connect(retrace, SIGNAL(foundThumbnails(const QList<QImage>&)),
+            this, SIGNAL(foundThumbnails(const QList<QImage>&)));
     connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
             this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
 
@@ -142,9 +159,15 @@ void RetraceProcess::start()
         arguments << QLatin1String("-sb");
     }
 
-    if (m_captureState) {
-        arguments << QLatin1String("-D");
-        arguments << QString::number(m_captureCall);
+    if (m_captureState || m_captureThumbnails) {
+        if (m_captureState) {
+            arguments << QLatin1String("-D");
+            arguments << QString::number(m_captureCall);
+        }
+        if (m_captureThumbnails) {
+            arguments << QLatin1String("-s"); // emit snapshots
+            arguments << QLatin1String("-"); // emit to stdout
+        }
     } else {
         if (m_benchmarking) {
             arguments << QLatin1String("-b");
@@ -159,32 +182,80 @@ void RetraceProcess::start()
 
 void RetraceProcess::replayFinished(int exitCode, QProcess::ExitStatus exitStatus)
 {
-    QByteArray output = m_process->readAllStandardOutput();
+    QByteArray output;
     QString msg;
-    QString errStr = m_process->readAllStandardError();
-
-#if 0
-    qDebug()<<"Process finished = ";
-    qDebug()<<"\terr = "<<errStr;
-    qDebug()<<"\tout = "<<output;
-#endif
 
     if (exitStatus != QProcess::NormalExit) {
         msg = QLatin1String("Process crashed");
     } else if (exitCode != 0) {
         msg = QLatin1String("Process exited with non zero exit code");
     } else {
-        if (m_captureState) {
-            bool ok = false;
-            QVariantMap parsedJson = m_jsonParser->parse(output, &ok).toMap();
-            ApiTraceState *state = new ApiTraceState(parsedJson);
-            emit foundState(state);
-            msg = tr("State fetched.");
+        if (m_captureState || m_captureThumbnails) {
+            if (m_captureState) {
+                bool ok = false;
+                output = m_process->readAllStandardOutput();
+                QVariantMap parsedJson = m_jsonParser->parse(output, &ok).toMap();
+                ApiTraceState *state = new ApiTraceState(parsedJson);
+                emit foundState(state);
+                msg = tr("State fetched.");
+            }
+            if (m_captureThumbnails) {
+                m_process->setReadChannel(QProcess::StandardOutput);
+
+                QList<QImage> thumbnails;
+
+                while (!m_process->atEnd()) {
+                    unsigned channels = 0;
+                    unsigned width = 0;
+                    unsigned height = 0;
+
+                    char header[512];
+                    qint64 headerSize = 0;
+                    int headerLines = 3; // assume no optional comment line
+
+                    for (int headerLine = 0; headerLine < headerLines; ++headerLine) {
+                        qint64 headerRead = m_process->readLine(&header[headerSize], sizeof(header) - headerSize);
+
+                        // if header actually contains optional comment line, ...
+                        if (headerLine == 1 && header[headerSize] == '#') {
+                            ++headerLines;
+                        }
+
+                        headerSize += headerRead;
+                    }
+
+                    const char *headerEnd = image::readPNMHeader(header, headerSize, &channels, &width, &height);
+
+                    // if invalid PNM header was encountered, ...
+                    if (header == headerEnd) {
+                        qDebug() << "error: invalid snapshot stream encountered\n";
+                        break;
+                    }
+
+                    //qDebug() << "channels: " << channels << ", width: " << width << ", height: " << height << "\n";
+
+                    QImage snapshot = QImage(width, height, channels == 1 ? QImage::Format_Mono : QImage::Format_RGB888);
+
+                    int rowBytes = channels * width;
+                    for (int y = 0; y < height; ++y) {
+                        unsigned char *scanLine = snapshot.scanLine(y);
+                        m_process->read((char *) scanLine, rowBytes);
+                    }
+
+                    QImage thumbnail = snapshot.scaled(16, 16, Qt::KeepAspectRatio, Qt::FastTransformation);
+                    thumbnails.append(thumbnail);
+                }
+
+                emit foundThumbnails(thumbnails);
+                msg = tr("Thumbnails fetched.");
+            }
         } else {
+            output = m_process->readAllStandardOutput();
             msg = QString::fromUtf8(output);
         }
     }
 
+    QString errStr = m_process->readAllStandardError();
     QStringList errorLines = errStr.split('\n');
     QList<ApiTraceError> errors;
     QRegExp regexp("(^\\d+): +(\\b\\w+\\b): (.+$)");
@@ -295,6 +366,16 @@ void RetraceProcess::setCaptureState(bool enable)
     m_captureState = enable;
 }
 
+bool RetraceProcess::captureThumbnails() const
+{
+    return m_captureThumbnails;
+}
+
+void RetraceProcess::setCaptureThumbnails(bool enable)
+{
+    m_captureThumbnails = enable;
+}
+
 void RetraceProcess::terminate()
 {
     if (m_process) {
index e5c391bcb23e91a7b5169601910589dac3d54770..1a78841bf6a4f731e504016452a94eedda15aee9 100644 (file)
@@ -40,6 +40,9 @@ public:
     bool captureState() const;
     void setCaptureState(bool enable);
 
+    bool captureThumbnails() const;
+    void setCaptureThumbnails(bool enable);
+
 public slots:
     void start();
     void terminate();
@@ -48,6 +51,7 @@ signals:
     void finished(const QString &output);
     void error(const QString &msg);
     void foundState(ApiTraceState *state);
+    void foundThumbnails(const QList<QImage> &thumbnails);
     void retraceErrors(const QList<ApiTraceError> &errors);
 
 private slots:
@@ -60,6 +64,7 @@ private:
     bool m_benchmarking;
     bool m_doubleBuffered;
     bool m_captureState;
+    bool m_captureThumbnails;
     qlonglong m_captureCall;
 
     QProcess *m_process;
@@ -89,9 +94,13 @@ public:
     bool captureState() const;
     void setCaptureState(bool enable);
 
+    bool captureThumbnails() const;
+    void setCaptureThumbnails(bool enable);
+
 signals:
     void finished(const QString &output);
     void foundState(ApiTraceState *state);
+    void foundThumbnails(const QList<QImage> &thumbnails);
     void error(const QString &msg);
     void retraceErrors(const QList<ApiTraceError> &errors);
 
@@ -106,6 +115,7 @@ private:
     bool m_benchmarking;
     bool m_doubleBuffered;
     bool m_captureState;
+    bool m_captureThumbnails;
     qlonglong m_captureCall;
 
     QProcessEnvironment m_processEnvironment;
index 7cb07f00aa1331e814c6d7025deffe76498a36fc..0e6ed47351260a8d35e1998cc9728423cff7102f 100644 (file)
@@ -104,7 +104,7 @@ int TraceLoader::numberOfFrames() const
 
 int TraceLoader::numberOfCallsInFrame(int frameIdx) const
 {
-    if (frameIdx > m_frameBookmarks.size()) {
+    if (frameIdx >= m_frameBookmarks.size()) {
         return 0;
     }
     FrameBookmarks::const_iterator itr =
@@ -386,7 +386,7 @@ int TraceLoader::callInFrame(int callIdx) const
 {
     unsigned numCalls = 0;
 
-    for (int frameIdx = 0; frameIdx <= m_frameBookmarks.size(); ++frameIdx) {
+    for (int frameIdx = 0; frameIdx < m_frameBookmarks.size(); ++frameIdx) {
         const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
         unsigned firstCall = numCalls;
         unsigned endCall = numCalls + frameBookmark.numberOfCalls;
index 5b48dc28d54c872845d0f57bb3ddaf611b71f25f..52cf49ed85040f7fe2bb4c6eb550667589c24914 100644 (file)
@@ -75,6 +75,7 @@
     <addaction name="actionReplay"/>
     <addaction name="actionStop"/>
     <addaction name="actionLookupState"/>
+    <addaction name="actionShowThumbnails"/>
     <addaction name="separator"/>
     <addaction name="actionOptions"/>
    </widget>
     <string>Ctrl+L</string>
    </property>
   </action>
+  <action name="actionShowThumbnails">
+   <property name="enabled">
+    <bool>false</bool>
+   </property>
+   <property name="text">
+    <string>Show &amp;Thumbnails</string>
+   </property>
+   <property name="shortcut">
+    <string>Ctrl+T</string>
+   </property>
+  </action>
   <action name="actionOptions">
    <property name="text">
     <string>Options</string>