1/* 2 * Copyright 2012 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "SkDebuggerGUI.h" 9#include "SkForceLinking.h" 10#include "SkGraphics.h" 11#include "SkImageDecoder.h" 12#include <QListWidgetItem> 13#include "PictureRenderer.h" 14#include "SkPictureRecord.h" 15#include "SkPicturePlayback.h" 16 17__SK_FORCE_IMAGE_DECODER_LINKING; 18 19#if defined(SK_BUILD_FOR_WIN32) 20 #include "BenchSysTimer_windows.h" 21#elif defined(SK_BUILD_FOR_MAC) 22 #include "BenchSysTimer_mach.h" 23#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) 24 #include "BenchSysTimer_posix.h" 25#else 26 #include "BenchSysTimer_c.h" 27#endif 28 29 30SkDebuggerGUI::SkDebuggerGUI(QWidget *parent) : 31 QMainWindow(parent) 32 , fCentralSplitter(this) 33 , fStatusBar(this) 34 , fToolBar(this) 35 , fActionOpen(this) 36 , fActionBreakpoint(this) 37 , fActionToggleIndexStyle(this) 38 , fActionProfile(this) 39 , fActionCancel(this) 40 , fActionClearBreakpoints(this) 41 , fActionClearDeletes(this) 42 , fActionClose(this) 43 , fActionCreateBreakpoint(this) 44 , fActionDelete(this) 45 , fActionDirectory(this) 46 , fActionGoToLine(this) 47 , fActionInspector(this) 48 , fActionSettings(this) 49 , fActionPlay(this) 50 , fActionPause(this) 51 , fActionRewind(this) 52 , fActionSave(this) 53 , fActionSaveAs(this) 54 , fActionShowDeletes(this) 55 , fActionStepBack(this) 56 , fActionStepForward(this) 57 , fActionZoomIn(this) 58 , fActionZoomOut(this) 59 , fMapper(this) 60 , fListWidget(&fCentralSplitter) 61 , fDirectoryWidget(&fCentralSplitter) 62 , fCanvasWidget(this, &fDebugger) 63 , fImageWidget(&fDebugger) 64 , fMenuBar(this) 65 , fMenuFile(this) 66 , fMenuNavigate(this) 67 , fMenuView(this) 68 , fBreakpointsActivated(false) 69 , fIndexStyleToggle(false) 70 , fDeletesActivated(false) 71 , fPause(false) 72 , fLoading(false) 73{ 74 setupUi(this); 75 fListWidget.setSelectionMode(QAbstractItemView::ExtendedSelection); 76 connect(&fListWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(registerListClick(QListWidgetItem *))); 77 connect(&fActionOpen, SIGNAL(triggered()), this, SLOT(openFile())); 78 connect(&fActionDirectory, SIGNAL(triggered()), this, SLOT(toggleDirectory())); 79 connect(&fDirectoryWidget, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(loadFile(QListWidgetItem *))); 80 connect(&fActionDelete, SIGNAL(triggered()), this, SLOT(actionDelete())); 81 connect(&fListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(toggleBreakpoint())); 82 connect(&fActionRewind, SIGNAL(triggered()), this, SLOT(actionRewind())); 83 connect(&fActionPlay, SIGNAL(triggered()), this, SLOT(actionPlay())); 84 connect(&fActionStepBack, SIGNAL(triggered()), this, SLOT(actionStepBack())); 85 connect(&fActionStepForward, SIGNAL(triggered()), this, SLOT(actionStepForward())); 86 connect(&fActionBreakpoint, SIGNAL(triggered()), this, SLOT(actionBreakpoints())); 87 connect(&fActionToggleIndexStyle, SIGNAL(triggered()), this, SLOT(actionToggleIndexStyle())); 88 connect(&fActionInspector, SIGNAL(triggered()), this, SLOT(actionInspector())); 89 connect(&fActionSettings, SIGNAL(triggered()), this, SLOT(actionSettings())); 90 connect(&fFilter, SIGNAL(activated(QString)), this, SLOT(toggleFilter(QString))); 91 connect(&fActionProfile, SIGNAL(triggered()), this, SLOT(actionProfile())); 92 connect(&fActionCancel, SIGNAL(triggered()), this, SLOT(actionCancel())); 93 connect(&fActionClearBreakpoints, SIGNAL(triggered()), this, SLOT(actionClearBreakpoints())); 94 connect(&fActionClearDeletes, SIGNAL(triggered()), this, SLOT(actionClearDeletes())); 95 connect(&fActionClose, SIGNAL(triggered()), this, SLOT(actionClose())); 96 connect(&fSettingsWidget, SIGNAL(visibilityFilterChanged()), this, SLOT(actionCommandFilter())); 97#if SK_SUPPORT_GPU 98 connect(&fSettingsWidget, SIGNAL(glSettingsChanged()), this, SLOT(actionGLWidget())); 99#endif 100 connect(&fSettingsWidget, SIGNAL(texFilterSettingsChanged()), this, SLOT(actionTextureFilter())); 101 connect(fSettingsWidget.getRasterCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionRasterWidget(bool))); 102 connect(fSettingsWidget.getOverdrawVizCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionOverdrawVizWidget(bool))); 103 connect(fSettingsWidget.getMegaVizCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionMegaVizWidget(bool))); 104 connect(fSettingsWidget.getPathOpsCheckBox(), SIGNAL(toggled(bool)), this, SLOT(actionPathOpsWidget(bool))); 105 connect(&fActionPause, SIGNAL(toggled(bool)), this, SLOT(pauseDrawing(bool))); 106 connect(&fActionCreateBreakpoint, SIGNAL(activated()), this, SLOT(toggleBreakpoint())); 107 connect(&fActionShowDeletes, SIGNAL(triggered()), this, SLOT(showDeletes())); 108 connect(&fCanvasWidget, SIGNAL(hitChanged(int)), this, SLOT(selectCommand(int))); 109 connect(&fCanvasWidget, SIGNAL(hitChanged(int)), &fSettingsWidget, SLOT(updateHit(int))); 110 connect(&fCanvasWidget, SIGNAL(scaleFactorChanged(float)), this, SLOT(actionScale(float))); 111 connect(&fCanvasWidget, SIGNAL(commandChanged(int)), &fSettingsWidget, SLOT(updateCommand(int))); 112 connect(&fActionSaveAs, SIGNAL(triggered()), this, SLOT(actionSaveAs())); 113 connect(&fActionSave, SIGNAL(triggered()), this, SLOT(actionSave())); 114 115 fMapper.setMapping(&fActionZoomIn, SkCanvasWidget::kIn_ZoomCommand); 116 fMapper.setMapping(&fActionZoomOut, SkCanvasWidget::kOut_ZoomCommand); 117 118 connect(&fActionZoomIn, SIGNAL(triggered()), &fMapper, SLOT(map())); 119 connect(&fActionZoomOut, SIGNAL(triggered()), &fMapper, SLOT(map())); 120 connect(&fMapper, SIGNAL(mapped(int)), &fCanvasWidget, SLOT(zoom(int))); 121 122 fInspectorWidget.setDisabled(true); 123 fMenuEdit.setDisabled(true); 124 fMenuNavigate.setDisabled(true); 125 fMenuView.setDisabled(true); 126 127 SkGraphics::Init(); 128} 129 130SkDebuggerGUI::~SkDebuggerGUI() { 131 SkGraphics::Term(); 132} 133 134void SkDebuggerGUI::actionBreakpoints() { 135 fBreakpointsActivated = !fBreakpointsActivated; 136 for (int row = 0; row < fListWidget.count(); row++) { 137 QListWidgetItem *item = fListWidget.item(row); 138 item->setHidden(item->checkState() == Qt::Unchecked && fBreakpointsActivated); 139 } 140} 141 142void SkDebuggerGUI::actionToggleIndexStyle() { 143 fIndexStyleToggle = !fIndexStyleToggle; 144 SkListWidget* list = (SkListWidget*) fListWidget.itemDelegate(); 145 list->setIndexStyle(fIndexStyleToggle ? SkListWidget::kIndex_IndexStyle : 146 SkListWidget::kOffset_IndexStyle); 147 fListWidget.update(); 148} 149 150void SkDebuggerGUI::showDeletes() { 151 fDeletesActivated = !fDeletesActivated; 152 for (int row = 0; row < fListWidget.count(); row++) { 153 QListWidgetItem *item = fListWidget.item(row); 154 item->setHidden(fDebugger.isCommandVisible(row) && fDeletesActivated); 155 } 156} 157 158// The timed picture playback uses the SkPicturePlayback's profiling stubs 159// to time individual commands. The offsets are needed to map SkPicture 160// offsets to individual commands. 161class SkTimedPicturePlayback : public SkPicturePlayback { 162public: 163 static SkTimedPicturePlayback* CreateFromStream(SkStream* stream, const SkPictInfo& info, 164 SkPicture::InstallPixelRefProc proc, 165 const SkTDArray<bool>& deletedCommands) { 166 // Mimics SkPicturePlayback::CreateFromStream 167 SkAutoTDelete<SkTimedPicturePlayback> playback(SkNEW_ARGS(SkTimedPicturePlayback, 168 (deletedCommands, info))); 169 if (!playback->parseStream(stream, proc)) { 170 return NULL; // we're invalid 171 } 172 return playback.detach(); 173 } 174 175 SkTimedPicturePlayback(const SkTDArray<bool>& deletedCommands, 176 const SkPictInfo& info) 177 : INHERITED(info) 178 , fSkipCommands(deletedCommands) 179 , fTot(0.0) 180 , fCurCommand(0) { 181 fTimes.setCount(deletedCommands.count()); 182 fTypeTimes.setCount(LAST_DRAWTYPE_ENUM+1); 183 this->resetTimes(); 184 } 185 186 void resetTimes() { 187 for (int i = 0; i < fTimes.count(); ++i) { 188 fTimes[i] = 0.0; 189 } 190 for (int i = 0; i < fTypeTimes.count(); ++i) { 191 fTypeTimes[i] = 0.0f; 192 } 193 fTot = 0.0; 194 } 195 196 int count() const { return fTimes.count(); } 197 198 double time(int index) const { return fTimes[index] / fTot; } 199 200 const SkTDArray<double>* typeTimes() const { return &fTypeTimes; } 201 202 double totTime() const { return fTot; } 203 204protected: 205 BenchSysTimer fTimer; 206 SkTDArray<bool> fSkipCommands; // has the command been deleted in the GUI? 207 SkTDArray<double> fTimes; // sum of time consumed for each command 208 SkTDArray<double> fTypeTimes; // sum of time consumed for each type of command (e.g., drawPath) 209 double fTot; // total of all times in 'fTimes' 210 int fCurType; 211 int fCurCommand; // the current command being executed/timed 212 213#ifdef SK_DEVELOPER 214 virtual bool preDraw(int opIndex, int type) SK_OVERRIDE { 215 fCurCommand = opIndex; 216 217 if (fSkipCommands[fCurCommand]) { 218 return true; 219 } 220 221 fCurType = type; 222 // The SkDebugCanvas doesn't recognize these types. This class needs to 223 // convert or else we'll wind up with a mismatch between the type counts 224 // the debugger displays and the profile times. 225 if (DRAW_POS_TEXT_TOP_BOTTOM == type) { 226 fCurType = DRAW_POS_TEXT; 227 } else if (DRAW_POS_TEXT_H_TOP_BOTTOM == type) { 228 fCurType = DRAW_POS_TEXT_H; 229 } 230 231#if defined(SK_BUILD_FOR_WIN32) 232 // CPU timer doesn't work well on Windows 233 fTimer.startWall(); 234#else 235 fTimer.startCpu(); 236#endif 237 238 return false; 239 } 240 241 virtual void postDraw(int opIndex) SK_OVERRIDE { 242#if defined(SK_BUILD_FOR_WIN32) 243 // CPU timer doesn't work well on Windows 244 double time = fTimer.endWall(); 245#else 246 double time = fTimer.endCpu(); 247#endif 248 249 SkASSERT(opIndex == fCurCommand); 250 SkASSERT(fCurType <= LAST_DRAWTYPE_ENUM); 251 252 fTimes[fCurCommand] += time; 253 fTypeTimes[fCurType] += time; 254 fTot += time; 255 } 256#endif 257 258private: 259 typedef SkPicturePlayback INHERITED; 260}; 261 262// Wrap SkPicture to allow installation of an SkTimedPicturePlayback object 263class SkTimedPicture : public SkPicture { 264public: 265 static SkTimedPicture* CreateTimedPicture(SkStream* stream, 266 SkPicture::InstallPixelRefProc proc, 267 const SkTDArray<bool>& deletedCommands) { 268 SkPictInfo info; 269 if (!InternalOnly_StreamIsSKP(stream, &info)) { 270 return NULL; 271 } 272 273 // Check to see if there is a playback to recreate. 274 if (stream->readBool()) { 275 SkTimedPicturePlayback* playback = SkTimedPicturePlayback::CreateFromStream( 276 stream, 277 info, proc, 278 deletedCommands); 279 if (NULL == playback) { 280 return NULL; 281 } 282 283 return SkNEW_ARGS(SkTimedPicture, (playback, info.fWidth, info.fHeight)); 284 } 285 286 return NULL; 287 } 288 289 void resetTimes() { ((SkTimedPicturePlayback*) fPlayback)->resetTimes(); } 290 291 int count() const { return ((SkTimedPicturePlayback*) fPlayback)->count(); } 292 293 // return the fraction of the total time this command consumed 294 double time(int index) const { return ((SkTimedPicturePlayback*) fPlayback)->time(index); } 295 296 const SkTDArray<double>* typeTimes() const { return ((SkTimedPicturePlayback*) fPlayback)->typeTimes(); } 297 298 double totTime() const { return ((SkTimedPicturePlayback*) fPlayback)->totTime(); } 299 300private: 301 // disallow default ctor b.c. we don't have a good way to setup the fPlayback ptr 302 SkTimedPicture(); 303 // Private ctor only used by CreateTimedPicture, which has created the playback. 304 SkTimedPicture(SkTimedPicturePlayback* playback, int width, int height) 305 : INHERITED(playback, width, height) {} 306 // disallow the copy ctor - enabling would require copying code from SkPicture 307 SkTimedPicture(const SkTimedPicture& src); 308 309 typedef SkPicture INHERITED; 310}; 311 312// This is a simplification of PictureBenchmark's run with the addition of 313// clearing of the times after the first pass (in resetTimes) 314void SkDebuggerGUI::run(SkTimedPicture* pict, 315 sk_tools::PictureRenderer* renderer, 316 int repeats) { 317 SkASSERT(pict); 318 if (NULL == pict) { 319 return; 320 } 321 322 SkASSERT(renderer != NULL); 323 if (NULL == renderer) { 324 return; 325 } 326 327 renderer->init(pict, NULL, NULL, NULL, false); 328 329 renderer->setup(); 330 renderer->render(); 331 renderer->resetState(true); // flush, swapBuffers and Finish 332 333 // We throw this away the first batch of times to remove first time effects (such as paging in this program) 334 pict->resetTimes(); 335 336 for (int i = 0; i < repeats; ++i) { 337 renderer->setup(); 338 renderer->render(); 339 renderer->resetState(false); // flush & swapBuffers, but don't Finish 340 } 341 renderer->resetState(true); // flush, swapBuffers and Finish 342 343 renderer->end(); 344} 345 346void SkDebuggerGUI::actionProfile() { 347 // In order to profile we pass the command offsets (that were read-in 348 // in loadPicture by the SkOffsetPicture) to an SkTimedPlaybackPicture. 349 // The SkTimedPlaybackPicture in turn passes the offsets to an 350 // SkTimedPicturePlayback object which uses them to track the performance 351 // of individual commands. 352 if (fFileName.isEmpty()) { 353 return; 354 } 355 356 SkFILEStream inputStream; 357 358 inputStream.setPath(fFileName.c_str()); 359 if (!inputStream.isValid()) { 360 return; 361 } 362 363 SkAutoTUnref<SkTimedPicture> picture(SkTimedPicture::CreateTimedPicture(&inputStream, 364 &SkImageDecoder::DecodeMemory, fSkipCommands)); 365 if (NULL == picture.get()) { 366 return; 367 } 368 369 // For now this #if allows switching between tiled and simple rendering 370 // modes. Eventually this will be accomplished via the GUI 371#if 0 372 // With the current batch of SysTimers, profiling in tiled mode 373 // gets swamped by the timing overhead: 374 // 375 // tile mode simple mode 376 // debugger 64.2ms 12.8ms 377 // bench_pictures 16.9ms 12.4ms 378 // 379 // This is b.c. in tiled mode each command is called many more times 380 // but typically does less work on each invocation (due to clipping) 381 sk_tools::TiledPictureRenderer* renderer = NULL; 382 383 renderer = SkNEW(sk_tools::TiledPictureRenderer); 384 renderer->setTileWidth(256); 385 renderer->setTileHeight(256); 386#else 387 sk_tools::SimplePictureRenderer* renderer = NULL; 388 389 renderer = SkNEW(sk_tools::SimplePictureRenderer); 390 391#if SK_SUPPORT_GPU 392 if (fSettingsWidget.isGLActive()) { 393 renderer->setDeviceType(sk_tools::PictureRenderer::kGPU_DeviceType); 394 renderer->setSampleCount(fSettingsWidget.getGLSampleCount()); 395 } 396#endif 397 398#endif 399 400 static const int kNumRepeats = 10; 401 402 run(picture.get(), renderer, kNumRepeats); 403 404 SkASSERT(picture->count() == fListWidget.count()); 405 406 // extract the individual command times from the SkTimedPlaybackPicture 407 for (int i = 0; i < picture->count(); ++i) { 408 double temp = picture->time(i); 409 410 QListWidgetItem* item = fListWidget.item(i); 411 412 item->setData(Qt::UserRole + 4, 100.0*temp); 413 } 414 415 setupOverviewText(picture->typeTimes(), picture->totTime(), kNumRepeats); 416 setupClipStackText(); 417} 418 419void SkDebuggerGUI::actionCancel() { 420 for (int row = 0; row < fListWidget.count(); row++) { 421 fListWidget.item(row)->setHidden(false); 422 } 423} 424 425void SkDebuggerGUI::actionClearBreakpoints() { 426 for (int row = 0; row < fListWidget.count(); row++) { 427 QListWidgetItem* item = fListWidget.item(row); 428 item->setCheckState(Qt::Unchecked); 429 item->setData(Qt::DecorationRole, 430 QPixmap(":/blank.png")); 431 } 432} 433 434void SkDebuggerGUI::actionClearDeletes() { 435 for (int row = 0; row < fListWidget.count(); row++) { 436 QListWidgetItem* item = fListWidget.item(row); 437 item->setData(Qt::UserRole + 2, QPixmap(":/blank.png")); 438 fDebugger.setCommandVisible(row, true); 439 fSkipCommands[row] = false; 440 } 441 if (fPause) { 442 fCanvasWidget.drawTo(fPausedRow); 443 fImageWidget.draw(); 444 } else { 445 fCanvasWidget.drawTo(fListWidget.currentRow()); 446 fImageWidget.draw(); 447 } 448} 449 450void SkDebuggerGUI::actionCommandFilter() { 451 fDebugger.highlightCurrentCommand(fSettingsWidget.getVisibilityFilter()); 452 fCanvasWidget.drawTo(fListWidget.currentRow()); 453 fImageWidget.draw(); 454} 455 456void SkDebuggerGUI::actionClose() { 457 this->close(); 458} 459 460void SkDebuggerGUI::actionDelete() { 461 462 for (int row = 0; row < fListWidget.count(); ++row) { 463 QListWidgetItem* item = fListWidget.item(row); 464 465 if (!item->isSelected()) { 466 continue; 467 } 468 469 if (fDebugger.isCommandVisible(row)) { 470 item->setData(Qt::UserRole + 2, QPixmap(":/delete.png")); 471 fDebugger.setCommandVisible(row, false); 472 fSkipCommands[row] = true; 473 } else { 474 item->setData(Qt::UserRole + 2, QPixmap(":/blank.png")); 475 fDebugger.setCommandVisible(row, true); 476 fSkipCommands[row] = false; 477 } 478 } 479 480 int currentRow = fListWidget.currentRow(); 481 482 if (fPause) { 483 fCanvasWidget.drawTo(fPausedRow); 484 fImageWidget.draw(); 485 } else { 486 fCanvasWidget.drawTo(currentRow); 487 fImageWidget.draw(); 488 } 489} 490 491#if SK_SUPPORT_GPU 492void SkDebuggerGUI::actionGLWidget() { 493 bool isToggled = fSettingsWidget.isGLActive(); 494 if (isToggled) { 495 fCanvasWidget.setGLSampleCount(fSettingsWidget.getGLSampleCount()); 496 } 497 fCanvasWidget.setWidgetVisibility(SkCanvasWidget::kGPU_WidgetType, !isToggled); 498} 499#endif 500 501void SkDebuggerGUI::actionInspector() { 502 if (fInspectorWidget.isHidden()) { 503 fInspectorWidget.setHidden(false); 504 fImageWidget.setHidden(false); 505 } else { 506 fInspectorWidget.setHidden(true); 507 fImageWidget.setHidden(true); 508 } 509} 510 511void SkDebuggerGUI::actionPlay() { 512 for (int row = fListWidget.currentRow() + 1; row < fListWidget.count(); 513 row++) { 514 QListWidgetItem *item = fListWidget.item(row); 515 if (item->checkState() == Qt::Checked) { 516 fListWidget.setCurrentItem(item); 517 return; 518 } 519 } 520 fListWidget.setCurrentRow(fListWidget.count() - 1); 521} 522 523void SkDebuggerGUI::actionRasterWidget(bool isToggled) { 524 fCanvasWidget.setWidgetVisibility(SkCanvasWidget::kRaster_8888_WidgetType, !isToggled); 525} 526 527void SkDebuggerGUI::actionOverdrawVizWidget(bool isToggled) { 528 fDebugger.setOverdrawViz(isToggled); 529 fCanvasWidget.update(); 530} 531 532void SkDebuggerGUI::actionMegaVizWidget(bool isToggled) { 533 fDebugger.setMegaViz(isToggled); 534 fCanvasWidget.update(); 535} 536 537void SkDebuggerGUI::actionPathOpsWidget(bool isToggled) { 538 fDebugger.setPathOps(isToggled); 539 fCanvasWidget.update(); 540} 541 542void SkDebuggerGUI::actionTextureFilter() { 543 SkPaint::FilterLevel level; 544 bool enabled = fSettingsWidget.getFilterOverride(&level); 545 fDebugger.setTexFilterOverride(enabled, level); 546 fCanvasWidget.update(); 547} 548 549void SkDebuggerGUI::actionRewind() { 550 fListWidget.setCurrentRow(0); 551} 552 553void SkDebuggerGUI::actionSave() { 554 fFileName = fPath.toAscii().data(); 555 fFileName.append("/"); 556 fFileName.append(fDirectoryWidget.currentItem()->text().toAscii().data()); 557 saveToFile(fFileName); 558} 559 560void SkDebuggerGUI::actionSaveAs() { 561 QString filename = QFileDialog::getSaveFileName(this, "Save File", "", 562 "Skia Picture (*skp)"); 563 if (!filename.endsWith(".skp", Qt::CaseInsensitive)) { 564 filename.append(".skp"); 565 } 566 saveToFile(SkString(filename.toAscii().data())); 567} 568 569void SkDebuggerGUI::actionScale(float scaleFactor) { 570 fSettingsWidget.setZoomText(scaleFactor); 571} 572 573void SkDebuggerGUI::actionSettings() { 574 if (fSettingsWidget.isHidden()) { 575 fSettingsWidget.setHidden(false); 576 } else { 577 fSettingsWidget.setHidden(true); 578 } 579} 580 581void SkDebuggerGUI::actionStepBack() { 582 int currentRow = fListWidget.currentRow(); 583 if (currentRow != 0) { 584 fListWidget.setCurrentRow(currentRow - 1); 585 } 586} 587 588void SkDebuggerGUI::actionStepForward() { 589 int currentRow = fListWidget.currentRow(); 590 QString curRow = QString::number(currentRow); 591 QString curCount = QString::number(fListWidget.count()); 592 if (currentRow < fListWidget.count() - 1) { 593 fListWidget.setCurrentRow(currentRow + 1); 594 } 595} 596 597void SkDebuggerGUI::drawComplete() { 598 fInspectorWidget.setMatrix(fDebugger.getCurrentMatrix()); 599 fInspectorWidget.setClip(fDebugger.getCurrentClip()); 600} 601 602void SkDebuggerGUI::saveToFile(const SkString& filename) { 603 SkFILEWStream file(filename.c_str()); 604 SkAutoTUnref<SkPicture> copy(fDebugger.copyPicture()); 605 606 copy->serialize(&file); 607} 608 609void SkDebuggerGUI::loadFile(QListWidgetItem *item) { 610 if (fDirectoryWidgetActive) { 611 fFileName = fPath.toAscii().data(); 612 // don't add a '/' to files in the local directory 613 if (fFileName.size() > 0) { 614 fFileName.append("/"); 615 } 616 fFileName.append(item->text().toAscii().data()); 617 loadPicture(fFileName); 618 } 619} 620 621void SkDebuggerGUI::openFile() { 622 QString temp = QFileDialog::getOpenFileName(this, tr("Open File"), "", 623 tr("Files (*.*)")); 624 openFile(temp); 625} 626 627void SkDebuggerGUI::openFile(const QString &filename) { 628 fDirectoryWidgetActive = false; 629 if (!filename.isEmpty()) { 630 QFileInfo pathInfo(filename); 631 loadPicture(SkString(filename.toAscii().data())); 632 setupDirectoryWidget(pathInfo.path()); 633 } 634 fDirectoryWidgetActive = true; 635} 636 637void SkDebuggerGUI::pauseDrawing(bool isPaused) { 638 fPause = isPaused; 639 fPausedRow = fListWidget.currentRow(); 640 fCanvasWidget.drawTo(fPausedRow); 641 fImageWidget.draw(); 642} 643 644void SkDebuggerGUI::registerListClick(QListWidgetItem *item) { 645 if(!fLoading) { 646 int currentRow = fListWidget.currentRow(); 647 648 if (currentRow != -1) { 649 if (!fPause) { 650 fCanvasWidget.drawTo(currentRow); 651 fImageWidget.draw(); 652 } 653 SkTDArray<SkString*> *currInfo = fDebugger.getCommandInfo( 654 currentRow); 655 656 /* TODO(chudy): Add command type before parameters. Rename v 657 * to something more informative. */ 658 if (currInfo) { 659 QString info; 660 info.append("<b>Parameters: </b><br/>"); 661 for (int i = 0; i < currInfo->count(); i++) { 662 663 info.append(QString((*currInfo)[i]->c_str())); 664 info.append("<br/>"); 665 } 666 fInspectorWidget.setText(info, SkInspectorWidget::kDetail_TabType); 667 fInspectorWidget.setDisabled(false); 668 } 669 setupClipStackText(); 670 } 671 672 } 673} 674 675void SkDebuggerGUI::selectCommand(int command) { 676 if (fPause) { 677 fListWidget.setCurrentRow(command); 678 } 679} 680 681void SkDebuggerGUI::toggleBreakpoint() { 682 QListWidgetItem* item = fListWidget.currentItem(); 683 if (item->checkState() == Qt::Unchecked) { 684 item->setCheckState(Qt::Checked); 685 item->setData(Qt::DecorationRole, 686 QPixmap(":/breakpoint_16x16.png")); 687 } else { 688 item->setCheckState(Qt::Unchecked); 689 item->setData(Qt::DecorationRole, 690 QPixmap(":/blank.png")); 691 } 692} 693 694void SkDebuggerGUI::toggleDirectory() { 695 fDirectoryWidget.setHidden(!fDirectoryWidget.isHidden()); 696} 697 698void SkDebuggerGUI::toggleFilter(QString string) { 699 for (int row = 0; row < fListWidget.count(); row++) { 700 QListWidgetItem *item = fListWidget.item(row); 701 item->setHidden(item->text() != string); 702 } 703} 704 705void SkDebuggerGUI::setupUi(QMainWindow *SkDebuggerGUI) { 706 QIcon windowIcon; 707 windowIcon.addFile(QString::fromUtf8(":/skia.png"), QSize(), 708 QIcon::Normal, QIcon::Off); 709 SkDebuggerGUI->setObjectName(QString::fromUtf8("SkDebuggerGUI")); 710 SkDebuggerGUI->resize(1200, 1000); 711 SkDebuggerGUI->setWindowIcon(windowIcon); 712 SkDebuggerGUI->setWindowTitle("Skia Debugger"); 713 714 fActionOpen.setShortcuts(QKeySequence::Open); 715 fActionOpen.setText("Open"); 716 717 QIcon breakpoint; 718 breakpoint.addFile(QString::fromUtf8(":/breakpoint.png"), 719 QSize(), QIcon::Normal, QIcon::Off); 720 fActionBreakpoint.setShortcut(QKeySequence(tr("Ctrl+B"))); 721 fActionBreakpoint.setIcon(breakpoint); 722 fActionBreakpoint.setText("Breakpoints"); 723 724 fActionToggleIndexStyle.setShortcut(QKeySequence(tr("Ctrl+T"))); 725 fActionToggleIndexStyle.setText("Toggle Index Style"); 726 727 QIcon cancel; 728 cancel.addFile(QString::fromUtf8(":/reload.png"), QSize(), 729 QIcon::Normal, QIcon::Off); 730 fActionCancel.setIcon(cancel); 731 fActionCancel.setText("Clear Filter"); 732 733 fActionClearBreakpoints.setShortcut(QKeySequence(tr("Alt+B"))); 734 fActionClearBreakpoints.setText("Clear Breakpoints"); 735 736 fActionClearDeletes.setShortcut(QKeySequence(tr("Alt+X"))); 737 fActionClearDeletes.setText("Clear Deletes"); 738 739 fActionClose.setShortcuts(QKeySequence::Quit); 740 fActionClose.setText("Exit"); 741 742 fActionCreateBreakpoint.setShortcut(QKeySequence(tr("B"))); 743 fActionCreateBreakpoint.setText("Set Breakpoint"); 744 745 fActionDelete.setShortcut(QKeySequence(tr("X"))); 746 fActionDelete.setText("Delete Command"); 747 748 fActionDirectory.setShortcut(QKeySequence(tr("Ctrl+D"))); 749 fActionDirectory.setText("Directory"); 750 751 QIcon profile; 752 profile.addFile(QString::fromUtf8(":/profile.png"), QSize(), 753 QIcon::Normal, QIcon::Off); 754 fActionProfile.setIcon(profile); 755 fActionProfile.setText("Profile"); 756 fActionProfile.setDisabled(true); 757 758 QIcon inspector; 759 inspector.addFile(QString::fromUtf8(":/inspector.png"), 760 QSize(), QIcon::Normal, QIcon::Off); 761 fActionInspector.setShortcut(QKeySequence(tr("Ctrl+I"))); 762 fActionInspector.setIcon(inspector); 763 fActionInspector.setText("Inspector"); 764 765 QIcon settings; 766 settings.addFile(QString::fromUtf8(":/inspector.png"), 767 QSize(), QIcon::Normal, QIcon::Off); 768 fActionSettings.setShortcut(QKeySequence(tr("Ctrl+G"))); 769 fActionSettings.setIcon(settings); 770 fActionSettings.setText("Settings"); 771 772 QIcon play; 773 play.addFile(QString::fromUtf8(":/play.png"), QSize(), 774 QIcon::Normal, QIcon::Off); 775 fActionPlay.setShortcut(QKeySequence(tr("Ctrl+P"))); 776 fActionPlay.setIcon(play); 777 fActionPlay.setText("Play"); 778 779 QIcon pause; 780 pause.addFile(QString::fromUtf8(":/pause.png"), QSize(), 781 QIcon::Normal, QIcon::Off); 782 fActionPause.setShortcut(QKeySequence(tr("Space"))); 783 fActionPause.setCheckable(true); 784 fActionPause.setIcon(pause); 785 fActionPause.setText("Pause"); 786 787 QIcon rewind; 788 rewind.addFile(QString::fromUtf8(":/rewind.png"), QSize(), 789 QIcon::Normal, QIcon::Off); 790 fActionRewind.setShortcut(QKeySequence(tr("Ctrl+R"))); 791 fActionRewind.setIcon(rewind); 792 fActionRewind.setText("Rewind"); 793 794 fActionSave.setShortcut(QKeySequence::Save); 795 fActionSave.setText("Save"); 796 fActionSave.setDisabled(true); 797 fActionSaveAs.setShortcut(QKeySequence::SaveAs); 798 fActionSaveAs.setText("Save As"); 799 fActionSaveAs.setDisabled(true); 800 801 fActionShowDeletes.setShortcut(QKeySequence(tr("Ctrl+X"))); 802 fActionShowDeletes.setText("Deleted Commands"); 803 804 QIcon stepBack; 805 stepBack.addFile(QString::fromUtf8(":/previous.png"), QSize(), 806 QIcon::Normal, QIcon::Off); 807 fActionStepBack.setShortcut(QKeySequence(tr("["))); 808 fActionStepBack.setIcon(stepBack); 809 fActionStepBack.setText("Step Back"); 810 811 QIcon stepForward; 812 stepForward.addFile(QString::fromUtf8(":/next.png"), 813 QSize(), QIcon::Normal, QIcon::Off); 814 fActionStepForward.setShortcut(QKeySequence(tr("]"))); 815 fActionStepForward.setIcon(stepForward); 816 fActionStepForward.setText("Step Forward"); 817 818 fActionZoomIn.setShortcut(QKeySequence(tr("Ctrl+="))); 819 fActionZoomIn.setText("Zoom In"); 820 fActionZoomOut.setShortcut(QKeySequence(tr("Ctrl+-"))); 821 fActionZoomOut.setText("Zoom Out"); 822 823 fListWidget.setItemDelegate(new SkListWidget(&fListWidget)); 824 fListWidget.setObjectName(QString::fromUtf8("listWidget")); 825 fListWidget.setMinimumWidth(250); 826 827 fFilter.addItem("--Filter By Available Commands--"); 828 829 fDirectoryWidget.setMinimumWidth(250); 830 fDirectoryWidget.setStyleSheet("QListWidget::Item {padding: 5px;}"); 831 832 fCanvasWidget.setSizePolicy(QSizePolicy::Expanding, 833 QSizePolicy::Expanding); 834 835 fImageWidget.setFixedSize(SkImageWidget::kImageWidgetWidth, 836 SkImageWidget::kImageWidgetHeight); 837 838 fInspectorWidget.setSizePolicy(QSizePolicy::Expanding, 839 QSizePolicy::Expanding); 840 fInspectorWidget.setMaximumHeight(300); 841 842 fSettingsAndImageLayout.setSpacing(6); 843 fSettingsAndImageLayout.addWidget(&fSettingsWidget); 844 fSettingsAndImageLayout.addWidget(&fImageWidget); 845 846 fSettingsWidget.setSizePolicy(QSizePolicy::Expanding, 847 QSizePolicy::Expanding); 848 fSettingsWidget.setMaximumWidth(250); 849 850 fLeftColumnSplitter.addWidget(&fListWidget); 851 fLeftColumnSplitter.addWidget(&fDirectoryWidget); 852 fLeftColumnSplitter.setOrientation(Qt::Vertical); 853 854 fCanvasSettingsAndImageLayout.setSpacing(6); 855 fCanvasSettingsAndImageLayout.addWidget(&fCanvasWidget); 856 fCanvasSettingsAndImageLayout.addLayout(&fSettingsAndImageLayout); 857 858 fMainAndRightColumnLayout.setSpacing(6); 859 fMainAndRightColumnLayout.addLayout(&fCanvasSettingsAndImageLayout); 860 fMainAndRightColumnLayout.addWidget(&fInspectorWidget); 861 fMainAndRightColumnWidget.setLayout(&fMainAndRightColumnLayout); 862 863 fCentralSplitter.addWidget(&fLeftColumnSplitter); 864 fCentralSplitter.addWidget(&fMainAndRightColumnWidget); 865 fCentralSplitter.setStretchFactor(0, 0); 866 fCentralSplitter.setStretchFactor(1, 1); 867 868 SkDebuggerGUI->setCentralWidget(&fCentralSplitter); 869 SkDebuggerGUI->setStatusBar(&fStatusBar); 870 871 fToolBar.setIconSize(QSize(32, 32)); 872 fToolBar.setToolButtonStyle(Qt::ToolButtonTextUnderIcon); 873 SkDebuggerGUI->addToolBar(Qt::TopToolBarArea, &fToolBar); 874 875 fSpacer.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); 876 877 fToolBar.addAction(&fActionRewind); 878 fToolBar.addAction(&fActionStepBack); 879 fToolBar.addAction(&fActionPause); 880 fToolBar.addAction(&fActionStepForward); 881 fToolBar.addAction(&fActionPlay); 882 fToolBar.addSeparator(); 883 fToolBar.addAction(&fActionInspector); 884 fToolBar.addAction(&fActionSettings); 885 fToolBar.addSeparator(); 886 fToolBar.addAction(&fActionProfile); 887 888 fToolBar.addSeparator(); 889 fToolBar.addWidget(&fSpacer); 890 fToolBar.addWidget(&fFilter); 891 fToolBar.addAction(&fActionCancel); 892 893 // TODO(chudy): Remove static call. 894 fDirectoryWidgetActive = false; 895 fFileName = ""; 896 setupDirectoryWidget(""); 897 fDirectoryWidgetActive = true; 898 899 // Menu Bar 900 fMenuFile.setTitle("File"); 901 fMenuFile.addAction(&fActionOpen); 902 fMenuFile.addAction(&fActionSave); 903 fMenuFile.addAction(&fActionSaveAs); 904 fMenuFile.addAction(&fActionClose); 905 906 fMenuEdit.setTitle("Edit"); 907 fMenuEdit.addAction(&fActionDelete); 908 fMenuEdit.addAction(&fActionClearDeletes); 909 fMenuEdit.addSeparator(); 910 fMenuEdit.addAction(&fActionCreateBreakpoint); 911 fMenuEdit.addAction(&fActionClearBreakpoints); 912 913 fMenuNavigate.setTitle("Navigate"); 914 fMenuNavigate.addAction(&fActionRewind); 915 fMenuNavigate.addAction(&fActionStepBack); 916 fMenuNavigate.addAction(&fActionStepForward); 917 fMenuNavigate.addAction(&fActionPlay); 918 fMenuNavigate.addAction(&fActionPause); 919 fMenuNavigate.addAction(&fActionGoToLine); 920 921 fMenuView.setTitle("View"); 922 fMenuView.addAction(&fActionBreakpoint); 923 fMenuView.addAction(&fActionShowDeletes); 924 fMenuView.addAction(&fActionToggleIndexStyle); 925 fMenuView.addAction(&fActionZoomIn); 926 fMenuView.addAction(&fActionZoomOut); 927 928 fMenuWindows.setTitle("Window"); 929 fMenuWindows.addAction(&fActionInspector); 930 fMenuWindows.addAction(&fActionSettings); 931 fMenuWindows.addAction(&fActionDirectory); 932 933 fActionGoToLine.setText("Go to Line..."); 934 fActionGoToLine.setDisabled(true); 935 fMenuBar.addAction(fMenuFile.menuAction()); 936 fMenuBar.addAction(fMenuEdit.menuAction()); 937 fMenuBar.addAction(fMenuView.menuAction()); 938 fMenuBar.addAction(fMenuNavigate.menuAction()); 939 fMenuBar.addAction(fMenuWindows.menuAction()); 940 941 fPause = false; 942 943 SkDebuggerGUI->setMenuBar(&fMenuBar); 944 QMetaObject::connectSlotsByName(SkDebuggerGUI); 945} 946 947void SkDebuggerGUI::setupDirectoryWidget(const QString& path) { 948 fPath = path; 949 QDir dir(path); 950 QRegExp r(".skp"); 951 fDirectoryWidget.clear(); 952 const QStringList files = dir.entryList(); 953 foreach (QString f, files) { 954 if (f.contains(r)) 955 fDirectoryWidget.addItem(f); 956 } 957} 958 959void SkDebuggerGUI::loadPicture(const SkString& fileName) { 960 fFileName = fileName; 961 fLoading = true; 962 SkStream* stream = SkNEW_ARGS(SkFILEStream, (fileName.c_str())); 963 964 SkPicture* picture = SkPicture::CreateFromStream(stream); 965 966 if (NULL == picture) { 967 QMessageBox::critical(this, "Error loading file", "Couldn't read file, sorry."); 968 SkSafeUnref(stream); 969 return; 970 } 971 972 fCanvasWidget.resetWidgetTransform(); 973 fDebugger.loadPicture(picture); 974 975 fSkipCommands.setCount(fDebugger.getSize()); 976 for (int i = 0; i < fSkipCommands.count(); ++i) { 977 fSkipCommands[i] = false; 978 } 979 980 SkSafeUnref(stream); 981 SkSafeUnref(picture); 982 983 // Will this automatically clear out due to nature of refcnt? 984 SkTArray<SkString>* commands = fDebugger.getDrawCommandsAsStrings(); 985 SkTDArray<size_t>* offsets = fDebugger.getDrawCommandOffsets(); 986 SkASSERT(commands->count() == offsets->count()); 987 988 fActionProfile.setDisabled(false); 989 990 /* fDebugCanvas is reinitialized every load picture. Need it to retain value 991 * of the visibility filter. 992 * TODO(chudy): This should be deprecated since fDebugger is not 993 * recreated. 994 * */ 995 fDebugger.highlightCurrentCommand(fSettingsWidget.getVisibilityFilter()); 996 997 this->setupListWidget(commands, offsets); 998 this->setupComboBox(commands); 999 this->setupOverviewText(NULL, 0.0, 1); 1000 fInspectorWidget.setDisabled(false); 1001 fSettingsWidget.setDisabled(false); 1002 fMenuEdit.setDisabled(false); 1003 fMenuNavigate.setDisabled(false); 1004 fMenuView.setDisabled(false); 1005 fActionSave.setDisabled(false); 1006 fActionSaveAs.setDisabled(false); 1007 fLoading = false; 1008 actionPlay(); 1009} 1010 1011void SkDebuggerGUI::setupListWidget(SkTArray<SkString>* commands, SkTDArray<size_t>* offsets) { 1012 SkASSERT(commands->count() == offsets->count()); 1013 fListWidget.clear(); 1014 int counter = 0; 1015 int indent = 0; 1016 for (int i = 0; i < commands->count(); i++) { 1017 QListWidgetItem *item = new QListWidgetItem(); 1018 item->setData(Qt::DisplayRole, (*commands)[i].c_str()); 1019 item->setData(Qt::UserRole + 1, counter++); 1020 1021 if (0 == strcmp("Restore", (*commands)[i].c_str()) || 1022 0 == strcmp("EndCommentGroup", (*commands)[i].c_str()) || 1023 0 == strcmp("PopCull", (*commands)[i].c_str())) { 1024 indent -= 10; 1025 } 1026 1027 item->setData(Qt::UserRole + 3, indent); 1028 1029 if (0 == strcmp("Save", (*commands)[i].c_str()) || 1030 0 == strcmp("Save Layer", (*commands)[i].c_str()) || 1031 0 == strcmp("BeginCommentGroup", (*commands)[i].c_str()) || 1032 0 == strcmp("PushCull", (*commands)[i].c_str())) { 1033 indent += 10; 1034 } 1035 1036 item->setData(Qt::UserRole + 4, -1); 1037 item->setData(Qt::UserRole + 5, (int)(*offsets)[i]); 1038 1039 fListWidget.addItem(item); 1040 } 1041} 1042 1043void SkDebuggerGUI::setupOverviewText(const SkTDArray<double>* typeTimes, 1044 double totTime, 1045 int numRuns) { 1046 SkString overview; 1047 fDebugger.getOverviewText(typeTimes, totTime, &overview, numRuns); 1048 fInspectorWidget.setText(overview.c_str(), SkInspectorWidget::kOverview_TabType); 1049} 1050 1051void SkDebuggerGUI::setupClipStackText() { 1052 SkString clipStack; 1053 fDebugger.getClipStackText(&clipStack); 1054 fInspectorWidget.setText(clipStack.c_str(), SkInspectorWidget::kClipStack_TabType); 1055} 1056 1057void SkDebuggerGUI::setupComboBox(SkTArray<SkString>* command) { 1058 fFilter.clear(); 1059 fFilter.addItem("--Filter By Available Commands--"); 1060 1061 std::map<std::string, int> map; 1062 for (int i = 0; i < command->count(); i++) { 1063 map[(*command)[i].c_str()]++; 1064 } 1065 1066 for (std::map<std::string, int>::iterator it = map.begin(); it != map.end(); 1067 ++it) { 1068 fFilter.addItem((it->first).c_str()); 1069 } 1070 1071 // NOTE(chudy): Makes first item unselectable. 1072 QStandardItemModel* model = qobject_cast<QStandardItemModel*>( 1073 fFilter.model()); 1074 QModelIndex firstIndex = model->index(0, fFilter.modelColumn(), 1075 fFilter.rootModelIndex()); 1076 QStandardItem* firstItem = model->itemFromIndex(firstIndex); 1077 firstItem->setSelectable(false); 1078} 1079