comparison layer/Colour3DPlotRenderer.cpp @ 1324:13d9b422f7fe zoom

Merge from default branch
author Chris Cannam
date Mon, 17 Sep 2018 13:51:31 +0100
parents a34a2a25907c
children bc2cb82050a0
comparison
equal deleted inserted replaced
1183:57d192e26331 1324:13d9b422f7fe
15 15
16 #include "Colour3DPlotRenderer.h" 16 #include "Colour3DPlotRenderer.h"
17 #include "RenderTimer.h" 17 #include "RenderTimer.h"
18 18
19 #include "base/Profiler.h" 19 #include "base/Profiler.h"
20 #include "base/HitCount.h"
20 21
21 #include "data/model/DenseThreeDimensionalModel.h" 22 #include "data/model/DenseThreeDimensionalModel.h"
22 #include "data/model/Dense3DModelPeakCache.h" 23 #include "data/model/Dense3DModelPeakCache.h"
23 #include "data/model/FFTModel.h" 24 #include "data/model/FFTModel.h"
24 25
92 Colour3DPlotRenderer::render(const LayerGeometryProvider *v, 93 Colour3DPlotRenderer::render(const LayerGeometryProvider *v,
93 QPainter &paint, QRect rect, bool timeConstrained) 94 QPainter &paint, QRect rect, bool timeConstrained)
94 { 95 {
95 RenderType renderType = decideRenderType(v); 96 RenderType renderType = decideRenderType(v);
96 97
97 if (renderType != DrawBufferPixelResolution) { 98 if (timeConstrained) {
98 // Rendering should be fast in bin-resolution and direct draw 99 if (renderType != DrawBufferPixelResolution) {
99 // cases because we are quite well zoomed-in, and the sums are 100 // Rendering should be fast in bin-resolution and direct
100 // easier this way. Calculating boundaries later will be 101 // draw cases because we are quite well zoomed-in, and the
101 // fiddly for partial paints otherwise. 102 // sums are easier this way. Calculating boundaries later
102 timeConstrained = false; 103 // will be fiddly for partial paints otherwise.
103 } 104 timeConstrained = false;
104 105
106 } else if (m_secondsPerXPixelValid) {
107 double predicted = m_secondsPerXPixel * rect.width();
108 #ifdef DEBUG_COLOUR_PLOT_REPAINT
109 SVDEBUG << "Predicted time for width " << rect.width() << " = "
110 << predicted << " (" << m_secondsPerXPixel << " x "
111 << rect.width() << ")" << endl;
112 #endif
113 if (predicted < 0.2) {
114 #ifdef DEBUG_COLOUR_PLOT_REPAINT
115 SVDEBUG << "Predicted time looks fast enough: no partial renders"
116 << endl;
117 #endif
118 timeConstrained = false;
119 }
120 }
121 }
122
105 int x0 = v->getXForViewX(rect.x()); 123 int x0 = v->getXForViewX(rect.x());
106 int x1 = v->getXForViewX(rect.x() + rect.width()); 124 int x1 = v->getXForViewX(rect.x() + rect.width());
107 if (x0 < 0) x0 = 0; 125 if (x0 < 0) x0 = 0;
108 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth(); 126 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth();
109 127
119 MagnitudeRange range = renderDirectTranslucent(v, paint, rect); 137 MagnitudeRange range = renderDirectTranslucent(v, paint, rect);
120 return { rect, range }; 138 return { rect, range };
121 } 139 }
122 140
123 #ifdef DEBUG_COLOUR_PLOT_REPAINT 141 #ifdef DEBUG_COLOUR_PLOT_REPAINT
124 cerr << "cache start " << m_cache.getStartFrame() 142 SVDEBUG << "cache start " << m_cache.getStartFrame()
125 << " valid left " << m_cache.getValidLeft() 143 << " valid left " << m_cache.getValidLeft()
126 << " valid right " << m_cache.getValidRight() 144 << " valid right " << m_cache.getValidRight()
127 << endl; 145 << endl;
128 cerr << " view start " << startFrame 146 SVDEBUG << " view start " << startFrame
129 << " x0 " << x0 147 << " x0 " << x0
130 << " x1 " << x1 148 << " x1 " << x1
131 << endl; 149 << endl;
132 #endif 150 #endif
151
152 static HitCount count("Colour3DPlotRenderer: image cache");
133 153
134 if (m_cache.isValid()) { // some part of the cache is valid 154 if (m_cache.isValid()) { // some part of the cache is valid
135 155
136 if (v->getXForFrame(m_cache.getStartFrame()) == 156 if (v->getXForFrame(m_cache.getStartFrame()) ==
137 v->getXForFrame(startFrame) && 157 v->getXForFrame(startFrame) &&
138 m_cache.getValidLeft() <= x0 && 158 m_cache.getValidLeft() <= x0 &&
139 m_cache.getValidRight() >= x1) { 159 m_cache.getValidRight() >= x1) {
140 160
141 #ifdef DEBUG_COLOUR_PLOT_REPAINT 161 #ifdef DEBUG_COLOUR_PLOT_REPAINT
142 cerr << "cache hit" << endl; 162 SVDEBUG << "cache hit" << endl;
143 #endif 163 #endif
164 count.hit();
144 165
145 // cache is valid for the complete requested area 166 // cache is valid for the complete requested area
146 paint.drawImage(rect, m_cache.getImage(), rect); 167 paint.drawImage(rect, m_cache.getImage(), rect);
147 168
148 MagnitudeRange range = m_magCache.getRange(x0, x1 - x0); 169 MagnitudeRange range = m_magCache.getRange(x0, x1 - x0);
149 170
150 return { rect, range }; 171 return { rect, range };
151 172
152 } else { 173 } else {
153 #ifdef DEBUG_COLOUR_PLOT_REPAINT 174 #ifdef DEBUG_COLOUR_PLOT_REPAINT
154 cerr << "cache partial hit" << endl; 175 SVDEBUG << "cache partial hit" << endl;
155 #endif 176 #endif
177 count.partial();
156 178
157 // cache doesn't begin at the right frame or doesn't 179 // cache doesn't begin at the right frame or doesn't
158 // contain the complete view, but might be scrollable or 180 // contain the complete view, but might be scrollable or
159 // partially usable 181 // partially usable
160 m_cache.scrollTo(v, startFrame); 182 m_cache.scrollTo(v, startFrame);
173 } 195 }
174 } 196 }
175 } 197 }
176 } else { 198 } else {
177 // cache is completely invalid 199 // cache is completely invalid
200 count.miss();
178 m_cache.setStartFrame(startFrame); 201 m_cache.setStartFrame(startFrame);
179 m_magCache.setStartFrame(startFrame); 202 m_magCache.setStartFrame(startFrame);
180 } 203 }
181 204
182 bool rightToLeft = false; 205 bool rightToLeft = false;
183 206
184 int reqx0 = x0; 207 int reqx0 = x0;
185 int reqx1 = x1; 208 int reqx1 = x1;
186 209
187 if (!m_cache.isValid() && timeConstrained) { 210 if (!m_cache.isValid() && timeConstrained) {
188 // When rendering the whole area, in a context where we might
189 // not be able to complete the work, start from somewhere near
190 // the middle so that the region of interest appears first
191
192 //!!! (perhaps we should avoid doing this if past repaints
193 //!!! have been fast enough to do the whole in one shot)
194 if (x0 == 0 && x1 == v->getPaintWidth()) { 211 if (x0 == 0 && x1 == v->getPaintWidth()) {
195 x0 = int(x1 * 0.3); 212
213 // When rendering the whole area, in a context where we
214 // might not be able to complete the work, start from
215 // somewhere near the middle so that the region of
216 // interest appears first.
217 //
218 // This is very useful if we actually are slow to render,
219 // but if we're not sure how fast we'll be, we should
220 // prefer not to because it can be distracting to render
221 // fast from the middle and then jump back to fill in the
222 // start. That is:
223 //
224 // - if our seconds-per-x-pixel count is invalid, then we
225 // don't do this: we've probably only just been created
226 // and don't know how fast we'll be yet (this happens
227 // often while zooming rapidly in and out). The exception
228 // to the exception is if we're displaying peak
229 // frequencies; this we can assume to be slow. (Note that
230 // if the seconds-per-x-pixel is valid and we know we're
231 // fast, then we've already set timeConstrained false
232 // above so this doesn't apply)
233 //
234 // - if we're using a peak cache, we don't do this;
235 // drawing from peak cache is often (even if not always)
236 // fast.
237
238 bool drawFromTheMiddle = true;
239
240 if (!m_secondsPerXPixelValid &&
241 (m_params.binDisplay != BinDisplay::PeakFrequencies)) {
242 drawFromTheMiddle = false;
243 } else {
244 int peakCacheIndex = -1, binsPerPeak = -1;
245 getPreferredPeakCache(v, peakCacheIndex, binsPerPeak);
246 if (peakCacheIndex >= 0) { // have a peak cache
247 drawFromTheMiddle = false;
248 }
249 }
250
251 if (drawFromTheMiddle) {
252 double offset = 0.5 * (double(rand()) / double(RAND_MAX));
253 x0 = int(x1 * offset);
254 }
196 } 255 }
197 } 256 }
198 257
199 if (m_cache.isValid()) { 258 if (m_cache.isValid()) {
200 259
236 QRect pr = rect & m_cache.getValidArea(); 295 QRect pr = rect & m_cache.getValidArea();
237 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(), 296 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(),
238 pr.x(), pr.y(), pr.width(), pr.height()); 297 pr.x(), pr.y(), pr.width(), pr.height());
239 298
240 if (!timeConstrained && (pr != rect)) { 299 if (!timeConstrained && (pr != rect)) {
241 cerr << "WARNING: failed to render entire requested rect " 300 SVCERR << "WARNING: failed to render entire requested rect "
242 << "even when not time-constrained" << endl; 301 << "even when not time-constrained" << endl;
243 } 302 }
244 303
245 MagnitudeRange range = m_magCache.getRange(reqx0, reqx1 - reqx0); 304 MagnitudeRange range = m_magCache.getRange(reqx0, reqx1 - reqx0);
246 305
285 } 344 }
286 } 345 }
287 346
288 ColumnOp::Column 347 ColumnOp::Column
289 Colour3DPlotRenderer::getColumn(int sx, int minbin, int nbins, 348 Colour3DPlotRenderer::getColumn(int sx, int minbin, int nbins,
290 bool usePeakCache) const 349 int peakCacheIndex) const
291 { 350 {
292 Profiler profiler("Colour3DPlotRenderer::getColumn"); 351 Profiler profiler("Colour3DPlotRenderer::getColumn");
293 352
294 // order: 353 // order:
295 // get column -> scale -> normalise -> record extents -> 354 // get column -> scale -> normalise -> record extents ->
307 366
308 column = vector<float>(fullColumn.data() + minbin, 367 column = vector<float>(fullColumn.data() + minbin,
309 fullColumn.data() + minbin + nbins); 368 fullColumn.data() + minbin + nbins);
310 369
311 } else { 370 } else {
312 371
313 ColumnOp::Column fullColumn = 372 ColumnOp::Column fullColumn =
314 (usePeakCache ? m_sources.peakCache : m_sources.source)-> 373 (peakCacheIndex >= 0 ?
315 getColumn(sx); 374 m_sources.peakCaches[peakCacheIndex] :
375 m_sources.source)
376 ->getColumn(sx);
316 377
317 column = vector<float>(fullColumn.data() + minbin, 378 column = vector<float>(fullColumn.data() + minbin,
318 fullColumn.data() + minbin + nbins); 379 fullColumn.data() + minbin + nbins);
319 380
320 column = ColumnOp::applyGain(column, m_params.scaleFactor); 381 column = ColumnOp::applyGain(column, m_params.scaleFactor);
387 // order: 448 // order:
388 // get column -> scale -> normalise -> record extents -> 449 // get column -> scale -> normalise -> record extents ->
389 // peak pick -> distribute/interpolate -> apply display gain 450 // peak pick -> distribute/interpolate -> apply display gain
390 451
391 // this does the first three: 452 // this does the first three:
392 preparedColumn = getColumn(sx, minbin, nbins, false); 453 preparedColumn = getColumn(sx, minbin, nbins, -1);
393 454
394 magRange.sample(preparedColumn); 455 magRange.sample(preparedColumn);
395 456
396 if (m_params.binDisplay == BinDisplay::PeakBins) { 457 if (m_params.binDisplay == BinDisplay::PeakBins) {
397 preparedColumn = ColumnOp::peakPick(preparedColumn); 458 preparedColumn = ColumnOp::peakPick(preparedColumn);
401 // applied by the colour scale object when mapping it 462 // applied by the colour scale object when mapping it
402 463
403 psx = sx; 464 psx = sx;
404 } 465 }
405 466
406 sv_frame_t fx = sx * modelResolution + modelStart; 467 sv_frame_t fx = sx * modelResolution + modelStart;
407 468
408 if (fx + modelResolution <= modelStart || fx > modelEnd) continue; 469 if (fx + modelResolution <= modelStart || fx > modelEnd) continue;
409 470
410 int rx0 = v->getXForFrame(int(double(fx) * rateRatio)); 471 int rx0 = v->getXForFrame(int(double(fx) * rateRatio));
411 int rx1 = v->getXForFrame(int(double(fx + modelResolution + 1) * rateRatio)); 472 int rx1 = v->getXForFrame(int(double(fx + modelResolution + 1) * rateRatio));
412 473
413 int rw = rx1 - rx0; 474 int rw = rx1 - rx0;
414 if (rw < 1) rw = 1; 475 if (rw < 1) rw = 1;
415 476
416 bool showLabel = (rw > 10 && 477 bool showLabel = (rw > 10 &&
417 paint.fontMetrics().width("0.000000") < rw - 3 && 478 paint.fontMetrics().width("0.000000") < rw - 3 &&
418 paint.fontMetrics().height() < (h / sh)); 479 paint.fontMetrics().height() < (h / sh));
419 480
420 for (int sy = minbin; sy < minbin + nbins; ++sy) { 481 for (int sy = minbin; sy < minbin + nbins; ++sy) {
421 482
422 int ry0 = m_sources.verticalBinLayer->getIYForBin(v, sy); 483 int ry0 = m_sources.verticalBinLayer->getIYForBin(v, sy);
423 int ry1 = m_sources.verticalBinLayer->getIYForBin(v, sy + 1); 484 int ry1 = m_sources.verticalBinLayer->getIYForBin(v, sy + 1);
424 485
425 if (m_params.invertVertical) { 486 if (m_params.invertVertical) {
438 paint.setBrush(Qt::NoBrush); 499 paint.setBrush(Qt::NoBrush);
439 paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1); 500 paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1);
440 continue; 501 continue;
441 } 502 }
442 503
443 QColor pen(255, 255, 255, 80); 504 QColor pen(255, 255, 255, 80);
444 QColor brush(colour); 505 QColor brush(colour);
445 506
446 if (rw > 3 && r.height() > 3) { 507 if (rw > 3 && r.height() > 3) {
447 brush.setAlpha(160); 508 brush.setAlpha(160);
448 } 509 }
449 510
450 paint.setPen(Qt::NoPen); 511 paint.setPen(Qt::NoPen);
451 paint.setBrush(brush); 512 paint.setBrush(brush);
452 513
453 if (illuminate) { 514 if (illuminate) {
454 if (r.contains(illuminatePos)) { 515 if (r.contains(illuminatePos)) {
455 paint.setPen(v->getForeground()); 516 paint.setPen(v->getForeground());
456 } 517 }
457 } 518 }
458 519
459 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT 520 #ifdef DEBUG_COLOUR_PLOT_REPAINT
460 // cerr << "rect " << r.x() << "," << r.y() << " " 521 // SVDEBUG << "rect " << r.x() << "," << r.y() << " "
461 // << r.width() << "x" << r.height() << endl; 522 // << r.width() << "x" << r.height() << endl;
462 #endif 523 #endif
463 524
464 paint.drawRect(r); 525 paint.drawRect(r);
465 526
466 if (showLabel) { 527 if (showLabel) {
467 double value = model->getValueAt(sx, sy); 528 double value = model->getValueAt(sx, sy);
468 snprintf(labelbuf, buflen, "%06f", value); 529 snprintf(labelbuf, buflen, "%06f", value);
469 QString text(labelbuf); 530 QString text(labelbuf);
470 PaintAssistant::drawVisibleText 531 PaintAssistant::drawVisibleText
471 (v, 532 (v,
472 paint, 533 paint,
473 rx0 + 2, 534 rx0 + 2,
474 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(), 535 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
475 text, 536 text,
476 PaintAssistant::OutlinedText); 537 PaintAssistant::OutlinedText);
477 } 538 }
478 } 539 }
479 } 540 }
480 541
481 return magRange; 542 return magRange;
543 }
544
545 void
546 Colour3DPlotRenderer::getPreferredPeakCache(const LayerGeometryProvider *v,
547 int &peakCacheIndex,
548 int &binsPerPeak) const
549 {
550 peakCacheIndex = -1;
551 binsPerPeak = -1;
552
553 const DenseThreeDimensionalModel *model = m_sources.source;
554 if (!model) return;
555 if (m_params.binDisplay == BinDisplay::PeakFrequencies) return;
556 if (m_params.colourScale.getScale() == ColourScaleType::Phase) return;
557
558 int zoomLevel = v->getZoomLevel();
559 int binResolution = model->getResolution();
560
561 for (int ix = 0; in_range_for(m_sources.peakCaches, ix); ++ix) {
562 int bpp = m_sources.peakCaches[ix]->getColumnsPerPeak();
563 int equivZoom = binResolution * bpp;
564 if (zoomLevel >= equivZoom) {
565 // this peak cache would work, though it might not be best
566 if (bpp > binsPerPeak) {
567 // ok, it's better than the best one we've found so far
568 peakCacheIndex = ix;
569 binsPerPeak = bpp;
570 }
571 }
572 }
573
574 #ifdef DEBUG_COLOUR_PLOT_REPAINT
575 SVDEBUG << "getPreferredPeakCache: zoomLevel = " << zoomLevel
576 << ", binResolution " << binResolution
577 << ", binsPerPeak " << binsPerPeak
578 << ", peakCacheIndex " << peakCacheIndex
579 << ", peakCaches " << m_sources.peakCaches.size()
580 << endl;
581 #endif
482 } 582 }
483 583
484 void 584 void
485 Colour3DPlotRenderer::renderToCachePixelResolution(const LayerGeometryProvider *v, 585 Colour3DPlotRenderer::renderToCachePixelResolution(const LayerGeometryProvider *v,
486 int x0, int repaintWidth, 586 int x0, int repaintWidth,
487 bool rightToLeft, 587 bool rightToLeft,
488 bool timeConstrained) 588 bool timeConstrained)
489 { 589 {
490 Profiler profiler("Colour3DPlotRenderer::renderToCachePixelResolution"); 590 Profiler profiler("Colour3DPlotRenderer::renderToCachePixelResolution");
491 #ifdef DEBUG_COLOUR_PLOT_REPAINT 591 #ifdef DEBUG_COLOUR_PLOT_REPAINT
492 cerr << "renderToCachePixelResolution" << endl; 592 SVDEBUG << "renderToCachePixelResolution" << endl;
493 #endif 593 #endif
494 594
495 // Draw to the draw buffer, and then copy from there. The draw 595 // Draw to the draw buffer, and then copy from there. The draw
496 // buffer is at the same resolution as the target in the cache, so 596 // buffer is at the same resolution as the target in the cache, so
497 // no extra scaling needed. 597 // no extra scaling needed.
498 598
499 const DenseThreeDimensionalModel *model = m_sources.source; 599 const DenseThreeDimensionalModel *model = m_sources.source;
500 if (!model || !model->isOK() || !model->isReady()) { 600 if (!model || !model->isOK() || !model->isReady()) {
501 throw std::logic_error("no source model provided, or model not ready"); 601 throw std::logic_error("no source model provided, or model not ready");
502 } 602 }
503 603
504 int h = v->getPaintHeight(); 604 int h = v->getPaintHeight();
505 605
506 clearDrawBuffer(repaintWidth, h); 606 clearDrawBuffer(repaintWidth, h);
507 607
508 vector<int> binforx(repaintWidth); 608 vector<int> binforx(repaintWidth);
509 vector<double> binfory(h); 609 vector<double> binfory(h);
510 610
511 bool usePeakCache = false;
512 int binsPerPeak = 1;
513 int zoomLevel = v->getZoomLevel();
514 int binResolution = model->getResolution(); 611 int binResolution = model->getResolution();
515 612
516 for (int x = 0; x < repaintWidth; ++x) { 613 for (int x = 0; x < repaintWidth; ++x) {
517 sv_frame_t f0 = v->getFrameForX(x0 + x); 614 sv_frame_t f0 = v->getFrameForX(x0 + x);
518 double s0 = double(f0 - model->getStartFrame()) / binResolution; 615 double s0 = double(f0 - model->getStartFrame()) / binResolution;
519 binforx[x] = int(s0 + 0.0001); 616 binforx[x] = int(s0 + 0.0001);
520 } 617 }
521 618
522 if (m_sources.peakCache) { 619 int peakCacheIndex = -1;
523 binsPerPeak = m_sources.peakCache->getColumnsPerPeak(); 620 int binsPerPeak = -1;
524 usePeakCache = (zoomLevel >= binResolution * binsPerPeak); 621
525 if (m_params.colourScale.getScale() == 622 getPreferredPeakCache(v, peakCacheIndex, binsPerPeak);
526 ColourScaleType::Phase) {
527 usePeakCache = false;
528 }
529 }
530
531 SVDEBUG << "[PIX] zoomLevel = " << zoomLevel
532 << ", binResolution " << binResolution
533 << ", binsPerPeak " << binsPerPeak
534 << ", peak cache " << m_sources.peakCache
535 << ", usePeakCache = " << usePeakCache
536 << endl;
537 623
538 for (int y = 0; y < h; ++y) { 624 for (int y = 0; y < h; ++y) {
539 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1); 625 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
540 } 626 }
541 627
553 } else { 639 } else {
554 attainedWidth = renderDrawBuffer(repaintWidth, 640 attainedWidth = renderDrawBuffer(repaintWidth,
555 h, 641 h,
556 binforx, 642 binforx,
557 binfory, 643 binfory,
558 usePeakCache, 644 peakCacheIndex,
559 rightToLeft, 645 rightToLeft,
560 timeConstrained); 646 timeConstrained);
561 } 647 }
562 648
563 if (attainedWidth == 0) return; 649 if (attainedWidth == 0) return;
642 Colour3DPlotRenderer::renderToCacheBinResolution(const LayerGeometryProvider *v, 728 Colour3DPlotRenderer::renderToCacheBinResolution(const LayerGeometryProvider *v,
643 int x0, int repaintWidth) 729 int x0, int repaintWidth)
644 { 730 {
645 Profiler profiler("Colour3DPlotRenderer::renderToCacheBinResolution"); 731 Profiler profiler("Colour3DPlotRenderer::renderToCacheBinResolution");
646 #ifdef DEBUG_COLOUR_PLOT_REPAINT 732 #ifdef DEBUG_COLOUR_PLOT_REPAINT
647 cerr << "renderToCacheBinResolution" << endl; 733 SVDEBUG << "renderToCacheBinResolution" << endl;
648 #endif 734 #endif
649 735
650 // Draw to the draw buffer, and then scale-copy from there. Draw 736 // Draw to the draw buffer, and then scale-copy from there. Draw
651 // buffer is at bin resolution, i.e. buffer x == source column 737 // buffer is at bin resolution, i.e. buffer x == source column
652 // number. We use toolkit smooth scaling for interpolation. 738 // number. We use toolkit smooth scaling for interpolation.
653 739
654 const DenseThreeDimensionalModel *model = m_sources.source; 740 const DenseThreeDimensionalModel *model = m_sources.source;
655 if (!model || !model->isOK() || !model->isReady()) { 741 if (!model || !model->isOK() || !model->isReady()) {
656 throw std::logic_error("no source model provided, or model not ready"); 742 throw std::logic_error("no source model provided, or model not ready");
657 } 743 }
658 744
659 // The draw buffer will contain a fragment at bin resolution. We 745 // The draw buffer will contain a fragment at bin resolution. We
660 // need to ensure that it starts and ends at points where a 746 // need to ensure that it starts and ends at points where a
661 // time-bin boundary occurs at an exact pixel boundary, and with a 747 // time-bin boundary occurs at an exact pixel boundary, and with a
708 794
709 for (int x = 0; x < drawBufferWidth; ++x) { 795 for (int x = 0; x < drawBufferWidth; ++x) {
710 binforx[x] = int(leftBoundaryFrame / binResolution) + x; 796 binforx[x] = int(leftBoundaryFrame / binResolution) + x;
711 } 797 }
712 798
799 #ifdef DEBUG_COLOUR_PLOT_REPAINT
713 SVDEBUG << "[BIN] binResolution " << binResolution << endl; 800 SVDEBUG << "[BIN] binResolution " << binResolution << endl;
801 #endif
714 802
715 for (int y = 0; y < h; ++y) { 803 for (int y = 0; y < h; ++y) {
716 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1); 804 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
717 } 805 }
718 806
719 int attainedWidth = renderDrawBuffer(drawBufferWidth, 807 int attainedWidth = renderDrawBuffer(drawBufferWidth,
720 h, 808 h,
721 binforx, 809 binforx,
722 binfory, 810 binfory,
723 false, 811 -1,
724 false, 812 false,
725 false); 813 false);
726 814
727 if (attainedWidth == 0) return; 815 if (attainedWidth == 0) return;
728 816
729 int scaledLeft = v->getXForFrame(leftBoundaryFrame); 817 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
730 int scaledRight = v->getXForFrame(rightBoundaryFrame); 818 int scaledRight = v->getXForFrame(rightBoundaryFrame);
731 819
732 #ifdef DEBUG_COLOUR_PLOT_REPAINT 820 #ifdef DEBUG_COLOUR_PLOT_REPAINT
733 cerr << "scaling draw buffer from width " << m_drawBuffer.width() 821 SVDEBUG << "scaling draw buffer from width " << m_drawBuffer.width()
734 << " to " << (scaledRight - scaledLeft) << " (nb drawBufferWidth = " 822 << " to " << (scaledRight - scaledLeft) << " (nb drawBufferWidth = "
735 << drawBufferWidth << ")" << endl; 823 << drawBufferWidth << ")" << endl;
736 #endif 824 #endif
737 825
738 QImage scaled = scaleDrawBufferImage 826 QImage scaled = scaleDrawBufferImage
739 (m_drawBuffer, scaledRight - scaledLeft, h); 827 (m_drawBuffer, scaledRight - scaledLeft, h);
740 828
755 if (sourceLeft < 0) { 843 if (sourceLeft < 0) {
756 sourceLeft = 0; 844 sourceLeft = 0;
757 } 845 }
758 846
759 #ifdef DEBUG_COLOUR_PLOT_REPAINT 847 #ifdef DEBUG_COLOUR_PLOT_REPAINT
760 cerr << "repaintWidth = " << repaintWidth 848 SVDEBUG << "repaintWidth = " << repaintWidth
761 << ", targetWidth = " << targetWidth << endl; 849 << ", targetWidth = " << targetWidth << endl;
762 #endif 850 #endif
763 851
764 if (targetWidth > 0) { 852 if (targetWidth > 0) {
765 // we are copying from an image that has already been scaled, 853 // we are copying from an image that has already been scaled,
766 // hence using the same width in both geometries 854 // hence using the same width in both geometries
781 869
782 int 870 int
783 Colour3DPlotRenderer::renderDrawBuffer(int w, int h, 871 Colour3DPlotRenderer::renderDrawBuffer(int w, int h,
784 const vector<int> &binforx, 872 const vector<int> &binforx,
785 const vector<double> &binfory, 873 const vector<double> &binfory,
786 bool usePeakCache, 874 int peakCacheIndex,
787 bool rightToLeft, 875 bool rightToLeft,
788 bool timeConstrained) 876 bool timeConstrained)
789 { 877 {
790 // Callers must have checked that the appropriate subset of 878 // Callers must have checked that the appropriate subset of
791 // Sources data members are set for the supplied flags (e.g. that 879 // Sources data members are set for the supplied flags (e.g. that
792 // peakCache model exists if usePeakCache) 880 // peakCache corresponding to peakCacheIndex exists)
793 881
794 RenderTimer timer(timeConstrained ? 882 RenderTimer timer(timeConstrained ?
795 RenderTimer::FastRender : 883 RenderTimer::FastRender :
796 RenderTimer::NoTimeout); 884 RenderTimer::NoTimeout);
797 885
798 Profiler profiler("Colour3DPlotRenderer::renderDrawBuffer"); 886 Profiler profiler("Colour3DPlotRenderer::renderDrawBuffer");
799 887
800 int divisor = 1; 888 int divisor = 1;
801 const DenseThreeDimensionalModel *sourceModel = m_sources.source; 889 const DenseThreeDimensionalModel *sourceModel = m_sources.source;
802 if (usePeakCache) { 890 if (peakCacheIndex >= 0) {
803 divisor = m_sources.peakCache->getColumnsPerPeak(); 891 divisor = m_sources.peakCaches[peakCacheIndex]->getColumnsPerPeak();
804 sourceModel = m_sources.peakCache; 892 sourceModel = m_sources.peakCaches[peakCacheIndex];
805 } 893 }
806 894
895 #ifdef DEBUG_COLOUR_PLOT_REPAINT
807 SVDEBUG << "renderDrawBuffer: w = " << w << ", h = " << h 896 SVDEBUG << "renderDrawBuffer: w = " << w << ", h = " << h
808 << ", usePeakCache = " << usePeakCache << " (divisor = " 897 << ", peakCacheIndex = " << peakCacheIndex << " (divisor = "
809 << divisor << "), rightToLeft = " << rightToLeft 898 << divisor << "), rightToLeft = " << rightToLeft
810 << ", timeConstrained = " << timeConstrained << endl; 899 << ", timeConstrained = " << timeConstrained << endl;
811 SVDEBUG << "renderDrawBuffer: normalization = " << int(m_params.normalization) 900 SVDEBUG << "renderDrawBuffer: normalization = " << int(m_params.normalization)
812 << ", binDisplay = " << int(m_params.binDisplay) 901 << ", binDisplay = " << int(m_params.binDisplay)
813 << ", binScale = " << int(m_params.binScale) 902 << ", binScale = " << int(m_params.binScale)
814 << ", alwaysOpaque = " << m_params.alwaysOpaque 903 << ", alwaysOpaque = " << m_params.alwaysOpaque
815 << ", interpolate = " << m_params.interpolate << endl; 904 << ", interpolate = " << m_params.interpolate << endl;
905 #endif
816 906
817 int sh = sourceModel->getHeight(); 907 int sh = sourceModel->getHeight();
818 908
819 int minbin = int(binfory[0] + 0.0001); 909 int minbin = int(binfory[0] + 0.0001);
820 if (minbin >= sh) minbin = sh - 1; 910 if (minbin >= sh) minbin = sh - 1;
822 912
823 int nbins = int(binfory[h-1] + 0.0001) - minbin + 1; 913 int nbins = int(binfory[h-1] + 0.0001) - minbin + 1;
824 if (minbin + nbins > sh) nbins = sh - minbin; 914 if (minbin + nbins > sh) nbins = sh - minbin;
825 915
826 #ifdef DEBUG_COLOUR_PLOT_REPAINT 916 #ifdef DEBUG_COLOUR_PLOT_REPAINT
827 cerr << "minbin = " << minbin << ", nbins = " << nbins << ", last binfory = " 917 SVDEBUG << "minbin = " << minbin << ", nbins = " << nbins << ", last binfory = "
828 << binfory[h-1] << " (rounds to " << int(binfory[h-1]) << ") (model height " << sh << ")" << endl; 918 << binfory[h-1] << " (rounds to " << int(binfory[h-1]) << ") (model height " << sh << ")" << endl;
829 #endif 919 #endif
830 920
831 int psx = -1; 921 int psx = -1;
832 922
838 start = w-1; 928 start = w-1;
839 finish = -1; 929 finish = -1;
840 step = -1; 930 step = -1;
841 } 931 }
842 932
843 int columnCount = 0; 933 int xPixelCount = 0;
844 934
845 vector<float> preparedColumn; 935 vector<float> preparedColumn;
846 936
847 int modelWidth = sourceModel->getWidth(); 937 int modelWidth = sourceModel->getWidth();
848 938
849 #ifdef DEBUG_COLOUR_PLOT_REPAINT 939 #ifdef DEBUG_COLOUR_PLOT_REPAINT
850 cerr << "modelWidth " << modelWidth << ", divisor " << divisor << endl; 940 SVDEBUG << "modelWidth " << modelWidth << ", divisor " << divisor << endl;
851 #endif 941 #endif
852 942
853 for (int x = start; x != finish; x += step) { 943 for (int x = start; x != finish; x += step) {
854 944
855 // x is the on-canvas pixel coord; sx (later) will be the 945 // x is the on-canvas pixel coord; sx (later) will be the
856 // source column index 946 // source column index
857 947
858 ++columnCount; 948 ++xPixelCount;
859 949
860 if (binforx[x] < 0) continue; 950 if (binforx[x] < 0) continue;
861 951
862 int sx0 = binforx[x] / divisor; 952 int sx0 = binforx[x] / divisor;
863 int sx1 = sx0; 953 int sx1 = sx0;
865 if (sx0 < 0) sx0 = sx1 - 1; 955 if (sx0 < 0) sx0 = sx1 - 1;
866 if (sx0 < 0) continue; 956 if (sx0 < 0) continue;
867 if (sx1 <= sx0) sx1 = sx0 + 1; 957 if (sx1 <= sx0) sx1 = sx0 + 1;
868 958
869 #ifdef DEBUG_COLOUR_PLOT_REPAINT 959 #ifdef DEBUG_COLOUR_PLOT_REPAINT
870 // cerr << "x = " << x << ", binforx[x] = " << binforx[x] << ", sx range " << sx0 << " -> " << sx1 << endl; 960 // SVDEBUG << "x = " << x << ", binforx[x] = " << binforx[x] << ", sx range " << sx0 << " -> " << sx1 << endl;
871 #endif 961 #endif
872 962
873 vector<float> pixelPeakColumn; 963 vector<float> pixelPeakColumn;
874 MagnitudeRange magRange; 964 MagnitudeRange magRange;
875 965
885 // get column -> scale -> normalise -> record extents -> 975 // get column -> scale -> normalise -> record extents ->
886 // peak pick -> distribute/interpolate -> apply display gain 976 // peak pick -> distribute/interpolate -> apply display gain
887 977
888 // this does the first three: 978 // this does the first three:
889 ColumnOp::Column column = getColumn(sx, minbin, nbins, 979 ColumnOp::Column column = getColumn(sx, minbin, nbins,
890 usePeakCache); 980 peakCacheIndex);
891 981
892 magRange.sample(column); 982 magRange.sample(column);
893 983
894 if (m_params.binDisplay == BinDisplay::PeakBins) { 984 if (m_params.binDisplay == BinDisplay::PeakBins) {
895 column = ColumnOp::peakPick(column); 985 column = ColumnOp::peakPick(column);
934 } 1024 }
935 1025
936 m_magRanges.push_back(magRange); 1026 m_magRanges.push_back(magRange);
937 } 1027 }
938 1028
939 double fractionComplete = double(columnCount) / double(w); 1029 double fractionComplete = double(xPixelCount) / double(w);
940 if (timer.outOfTime(fractionComplete)) { 1030 if (timer.outOfTime(fractionComplete)) {
941 cerr << "out of time" << endl; 1031 #ifdef DEBUG_COLOUR_PLOT_REPAINT
942 return columnCount; 1032 SVDEBUG << "out of time" << endl;
943 } 1033 #endif
944 } 1034 updateTimings(timer, xPixelCount);
945 1035 return xPixelCount;
946 return columnCount; 1036 }
1037 }
1038
1039 updateTimings(timer, xPixelCount);
1040 return xPixelCount;
947 } 1041 }
948 1042
949 int 1043 int
950 Colour3DPlotRenderer::renderDrawBufferPeakFrequencies(const LayerGeometryProvider *v, 1044 Colour3DPlotRenderer::renderDrawBufferPeakFrequencies(const LayerGeometryProvider *v,
951 int w, int h, 1045 int w, int h,
957 // Callers must have checked that the appropriate subset of 1051 // Callers must have checked that the appropriate subset of
958 // Sources data members are set for the supplied flags (e.g. that 1052 // Sources data members are set for the supplied flags (e.g. that
959 // fft model exists) 1053 // fft model exists)
960 1054
961 RenderTimer timer(timeConstrained ? 1055 RenderTimer timer(timeConstrained ?
962 RenderTimer::FastRender : 1056 RenderTimer::SlowRender :
963 RenderTimer::NoTimeout); 1057 RenderTimer::NoTimeout);
964 1058
965 const FFTModel *fft = m_sources.fft; 1059 const FFTModel *fft = m_sources.fft;
966 1060
967 int sh = fft->getHeight(); 1061 int sh = fft->getHeight();
985 start = w-1; 1079 start = w-1;
986 finish = -1; 1080 finish = -1;
987 step = -1; 1081 step = -1;
988 } 1082 }
989 1083
990 int columnCount = 0; 1084 int xPixelCount = 0;
991 1085
992 vector<float> preparedColumn; 1086 vector<float> preparedColumn;
993 1087
994 int modelWidth = fft->getWidth(); 1088 int modelWidth = fft->getWidth();
995 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1089 #ifdef DEBUG_COLOUR_PLOT_REPAINT
996 cerr << "modelWidth " << modelWidth << endl; 1090 SVDEBUG << "modelWidth " << modelWidth << endl;
997 #endif 1091 #endif
998 1092
999 double minFreq = 1093 double minFreq =
1000 (double(minbin) * fft->getSampleRate()) / fft->getFFTSize(); 1094 (double(minbin) * fft->getSampleRate()) / fft->getFFTSize();
1001 double maxFreq = 1095 double maxFreq =
1002 (double(minbin + nbins - 1) * fft->getSampleRate()) / fft->getFFTSize(); 1096 (double(minbin + nbins - 1) * fft->getSampleRate()) / fft->getFFTSize();
1003 1097
1004 bool logarithmic = (m_params.binScale == BinScale::Log); 1098 bool logarithmic = (m_params.binScale == BinScale::Log);
1099
1100 #ifdef DEBUG_COLOUR_PLOT_REPAINT
1101 SVDEBUG << "start = " << start << ", finish = " << finish
1102 << ", step = " << step << endl;
1103 #endif
1005 1104
1006 for (int x = start; x != finish; x += step) { 1105 for (int x = start; x != finish; x += step) {
1007 1106
1008 // x is the on-canvas pixel coord; sx (later) will be the 1107 // x is the on-canvas pixel coord; sx (later) will be the
1009 // source column index 1108 // source column index
1010 1109
1011 ++columnCount; 1110 ++xPixelCount;
1012 1111
1013 if (binforx[x] < 0) continue; 1112 if (binforx[x] < 0) continue;
1014 1113
1015 int sx0 = binforx[x]; 1114 int sx0 = binforx[x];
1016 int sx1 = sx0; 1115 int sx1 = sx0;
1027 if (sx < 0 || sx >= modelWidth) { 1126 if (sx < 0 || sx >= modelWidth) {
1028 continue; 1127 continue;
1029 } 1128 }
1030 1129
1031 if (sx != psx) { 1130 if (sx != psx) {
1032 preparedColumn = getColumn(sx, minbin, nbins, false); 1131 preparedColumn = getColumn(sx, minbin, nbins, -1);
1033 magRange.sample(preparedColumn); 1132 magRange.sample(preparedColumn);
1034 psx = sx; 1133 psx = sx;
1035 } 1134 }
1036 1135
1037 if (sx == sx0) { 1136 if (sx == sx0) {
1045 } 1144 }
1046 } 1145 }
1047 } 1146 }
1048 1147
1049 if (!pixelPeakColumn.empty()) { 1148 if (!pixelPeakColumn.empty()) {
1149
1150 #ifdef DEBUG_COLOUR_PLOT_REPAINT
1151 // SVDEBUG << "found " << peakfreqs.size() << " peak freqs at column "
1152 // << sx0 << endl;
1153 #endif
1050 1154
1051 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin(); 1155 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
1052 pi != peakfreqs.end(); ++pi) { 1156 pi != peakfreqs.end(); ++pi) {
1053 1157
1054 int bin = pi->first; 1158 int bin = pi->first;
1063 (freq, minFreq, maxFreq, logarithmic); 1167 (freq, minFreq, maxFreq, logarithmic);
1064 1168
1065 int iy = int(y + 0.5); 1169 int iy = int(y + 0.5);
1066 if (iy < 0 || iy >= h) continue; 1170 if (iy < 0 || iy >= h) continue;
1067 1171
1068 m_drawBuffer.setPixel 1172 auto pixel = m_params.colourScale.getPixel(value);
1069 (x, 1173
1070 iy, 1174 #ifdef DEBUG_COLOUR_PLOT_REPAINT
1071 m_params.colourScale.getPixel(value)); 1175 // SVDEBUG << "frequency " << freq << " for bin " << bin
1176 // << " -> y = " << y << ", iy = " << iy << ", value = "
1177 // << value << ", pixel " << pixel << "\n";
1178 #endif
1179
1180 m_drawBuffer.setPixel(x, iy, pixel);
1072 } 1181 }
1073 1182
1074 m_magRanges.push_back(magRange); 1183 m_magRanges.push_back(magRange);
1075 } 1184
1076 1185 } else {
1077 double fractionComplete = double(columnCount) / double(w); 1186 #ifdef DEBUG_COLOUR_PLOT_REPAINT
1187 SVDEBUG << "pixel peak column for range " << sx0 << " to " << sx1
1188 << " is empty" << endl;
1189 #endif
1190 }
1191
1192 double fractionComplete = double(xPixelCount) / double(w);
1078 if (timer.outOfTime(fractionComplete)) { 1193 if (timer.outOfTime(fractionComplete)) {
1079 return columnCount; 1194 #ifdef DEBUG_COLOUR_PLOT_REPAINT
1080 } 1195 SVDEBUG << "out of time" << endl;
1081 } 1196 #endif
1082 1197 updateTimings(timer, xPixelCount);
1083 return columnCount; 1198 return xPixelCount;
1199 }
1200 }
1201
1202 updateTimings(timer, xPixelCount);
1203 return xPixelCount;
1204 }
1205
1206 void
1207 Colour3DPlotRenderer::updateTimings(const RenderTimer &timer, int xPixelCount)
1208 {
1209 double secondsPerXPixel = timer.secondsPerItem(xPixelCount);
1210
1211 // valid if we have enough data points, or if the overall time is
1212 // massively slow anyway (as we definitely need to warn about that)
1213 bool valid = (xPixelCount > 20 || secondsPerXPixel > 0.01);
1214
1215 if (valid) {
1216 m_secondsPerXPixel = secondsPerXPixel;
1217 m_secondsPerXPixelValid = true;
1218
1219 #ifdef DEBUG_COLOUR_PLOT_REPAINT
1220 SVDEBUG << "across " << xPixelCount << " x-pixels, seconds per x-pixel = "
1221 << m_secondsPerXPixel << endl;
1222 #endif
1223 }
1084 } 1224 }
1085 1225
1086 void 1226 void
1087 Colour3DPlotRenderer::recreateDrawBuffer(int w, int h) 1227 Colour3DPlotRenderer::recreateDrawBuffer(int w, int h)
1088 { 1228 {