Mercurial > hg > svgui
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 { |