From: José Fonseca Date: Sat, 24 Mar 2012 09:30:51 +0000 (+0000) Subject: Merge branch 'gui-thumbnails' X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=97ac28e65ca20b5649552597afaeee1d67766f6a;hp=561266b131b7a44cd80fca436462529a56393299;p=apitrace Merge branch 'gui-thumbnails' --- diff --git a/common/image.hpp b/common/image.hpp index 6316791..ea3eefb 100644 --- a/common/image.hpp +++ b/common/image.hpp @@ -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 */ diff --git a/common/image_pnm.cpp b/common/image_pnm.cpp index c95f640..f9cd05d 100644 --- a/common/image_pnm.cpp +++ b/common/image_pnm.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #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 */ diff --git a/gui/apicalldelegate.cpp b/gui/apicalldelegate.cpp index 11ed3a5..c687175 100644 --- a/gui/apicalldelegate.cpp +++ b/gui/apicalldelegate.cpp @@ -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(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()); diff --git a/gui/apitrace.cpp b/gui/apitrace.cpp index 5758b07..89385b9 100644 --- a/gui/apitrace.cpp +++ b/gui/apitrace.cpp @@ -115,7 +115,7 @@ ApiTrace::FrameMarker ApiTrace::frameMarker() const return m_frameMarker; } -QList ApiTrace::frames() const +const QList & 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 &thumbnails) +{ + QList frames = m_frames; + + QList::const_iterator thumbnail = thumbnails.begin(); + + foreach (ApiTraceFrame *frame, frames) { + if (thumbnail != thumbnails.end()) { + frame->setThumbnail(*thumbnail); + + ++thumbnail; + + emit changed(frame); + } + } +} + #include "apitrace.moc" diff --git a/gui/apitrace.h b/gui/apitrace.h index 2833f60..a5c0329 100644 --- a/gui/apitrace.h +++ b/gui/apitrace.h @@ -67,7 +67,7 @@ public: ApiTraceCall *callWithIndex(int idx) const; - QList frames() const; + const QList & 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 &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, diff --git a/gui/apitracecall.cpp b/gui/apitracecall.cpp index 267dc09..dfaaeef 100644 --- a/gui/apitracecall.cpp +++ b/gui/apitracecall.cpp @@ -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; +} diff --git a/gui/apitracecall.h b/gui/apitracecall.h index 3a9faaf..945ab0c 100644 --- a/gui/apitracecall.h +++ b/gui/apitracecall.h @@ -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*); diff --git a/gui/apitracemodel.cpp b/gui/apitracemodel.cpp index 7303ae1..0863c1b 100644 --- a/gui/apitracemodel.cpp +++ b/gui/apitracemodel.cpp @@ -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(event); - const QList frames = m_trace->frames(); + const QList & 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(event)); + } else if (event->type() == ApiTraceEvent::Frame) { + frameChanged(static_cast(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 & frames = m_trace->frames(); + int row = frames.indexOf(frame); + QModelIndex index = createIndex(row, 0, frame); + emit dataChanged(index, index); +} + void ApiTraceModel::endAddingFrames() { endInsertRows(); diff --git a/gui/apitracemodel.h b/gui/apitracemodel.h index fe6b5ce..e7354aa 100644 --- a/gui/apitracemodel.h +++ b/gui/apitracemodel.h @@ -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); diff --git a/gui/main.cpp b/gui/main.cpp index d7af53d..18e07f1 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -6,12 +6,14 @@ #include #include #include +#include Q_DECLARE_METATYPE(QList); Q_DECLARE_METATYPE(QVector); Q_DECLARE_METATYPE(Qt::CaseSensitivity); Q_DECLARE_METATYPE(ApiTrace::SearchResult); Q_DECLARE_METATYPE(ApiTrace::SearchRequest); +Q_DECLARE_METATYPE(QList); static void usage(void) { @@ -28,6 +30,7 @@ int main(int argc, char **argv) qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); + qRegisterMetaType >(); QStringList args = app.arguments(); int i = 1; diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 039889c..fce9984 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -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&)), + this, SLOT(replayThumbnailsFound(const QList&))); connect(m_retracer, SIGNAL(retraceErrors(const QList&)), this, SLOT(slotRetraceErrors(const QList&))); @@ -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 &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(event); + m_ui.detailsWebView->setHtml(call->toHtml()); + } } } diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 66be2e2..d65aa0e 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -8,6 +8,8 @@ #include #include +#include +#include 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 &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 &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 diff --git a/gui/retracer.cpp b/gui/retracer.cpp index 17ac14d..7a49ae9 100644 --- a/gui/retracer.cpp +++ b/gui/retracer.cpp @@ -2,8 +2,12 @@ #include "apitracecall.h" +#include "image.hpp" + #include #include +#include +#include #include @@ -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&)), + this, SIGNAL(foundThumbnails(const QList&))); connect(retrace, SIGNAL(retraceErrors(const QList&)), this, SIGNAL(retraceErrors(const QList&))); @@ -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 = "<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 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 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) { diff --git a/gui/retracer.h b/gui/retracer.h index e5c391b..1a78841 100644 --- a/gui/retracer.h +++ b/gui/retracer.h @@ -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 &thumbnails); void retraceErrors(const QList &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 &thumbnails); void error(const QString &msg); void retraceErrors(const QList &errors); @@ -106,6 +115,7 @@ private: bool m_benchmarking; bool m_doubleBuffered; bool m_captureState; + bool m_captureThumbnails; qlonglong m_captureCall; QProcessEnvironment m_processEnvironment; diff --git a/gui/traceloader.cpp b/gui/traceloader.cpp index 7cb07f0..0e6ed47 100644 --- a/gui/traceloader.cpp +++ b/gui/traceloader.cpp @@ -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; diff --git a/gui/ui/mainwindow.ui b/gui/ui/mainwindow.ui index 5b48dc2..52cf49e 100644 --- a/gui/ui/mainwindow.ui +++ b/gui/ui/mainwindow.ui @@ -75,6 +75,7 @@ + @@ -525,6 +526,17 @@ Ctrl+L + + + false + + + Show &Thumbnails + + + Ctrl+T + + Options