19 #include "base/Profiler.h" 20 #include "base/HitCount.h" 22 #include "data/model/DenseThreeDimensionalModel.h" 23 #include "data/model/Dense3DModelPeakCache.h" 24 #include "data/model/FFTModel.h" 46 return render(v, paint, rect,
false);
51 QPainter &paint, QRect rect)
53 return render(v, paint, rect,
true);
61 if (renderType == DirectTranslucent) {
65 int h = m_cache.getSize().height();
67 QRect areaLeft(0, 0, m_cache.getValidLeft(), h);
68 QRect areaRight(m_cache.getValidRight(), 0,
69 m_cache.getSize().width() - m_cache.getValidRight(), h);
71 if (areaRight.width() > areaLeft.width()) {
83 if (renderType == DirectTranslucent) {
98 QPainter &paint, QRect rect,
bool timeConstrained)
102 if (timeConstrained) {
103 if (renderType != DrawBufferPixelResolution) {
108 timeConstrained =
false;
110 }
else if (m_secondsPerXPixelValid) {
111 double predicted = m_secondsPerXPixel * rect.width();
112 #ifdef DEBUG_COLOUR_PLOT_REPAINT 113 SVDEBUG <<
"render " << m_sources.source
114 <<
": Predicted time for width " << rect.width() <<
" = " 115 << predicted <<
" (" << m_secondsPerXPixel <<
" x " 116 << rect.width() <<
")" << endl;
118 if (predicted < 0.175) {
119 #ifdef DEBUG_COLOUR_PLOT_REPAINT 120 SVDEBUG <<
"render " << m_sources.source
121 <<
": Predicted time looks fast enough: no partial renders" 124 timeConstrained =
false;
136 #ifdef DEBUG_COLOUR_PLOT_REPAINT 137 SVDEBUG <<
"render " << m_sources.source
138 <<
": cache size is " << m_cache.getSize().width()
139 <<
"x" << m_cache.getSize().height()
140 <<
" at zoom level " << m_cache.getZoomLevel() << endl;
143 bool justCreated = m_cache.getSize().isEmpty();
145 bool justInvalidated =
149 #ifdef DEBUG_COLOUR_PLOT_REPAINT 150 SVDEBUG <<
"render " << m_sources.source
151 <<
": justCreated = " << justCreated
152 <<
", justInvalidated = " << justInvalidated
162 if (renderType == DirectTranslucent) {
163 MagnitudeRange range = renderDirectTranslucent(v, paint, rect);
164 return { rect, range };
167 #ifdef DEBUG_COLOUR_PLOT_REPAINT 168 SVDEBUG <<
"render " << m_sources.source
169 <<
": cache start " << m_cache.getStartFrame()
170 <<
" valid left " << m_cache.getValidLeft()
171 <<
" valid right " << m_cache.getValidRight()
173 SVDEBUG <<
"render " << m_sources.source
174 <<
": view start " << startFrame
180 static HitCount count(
"Colour3DPlotRenderer: image cache");
182 if (m_cache.isValid()) {
186 m_cache.getValidLeft() <= x0 &&
187 m_cache.getValidRight() >= x1) {
189 #ifdef DEBUG_COLOUR_PLOT_REPAINT 190 SVDEBUG <<
"render " << m_sources.source
191 <<
": cache hit" << endl;
196 paint.drawImage(rect, m_cache.getImage(), rect);
198 MagnitudeRange range = m_magCache.getRange(x0, x1 - x0);
200 return { rect, range };
203 #ifdef DEBUG_COLOUR_PLOT_REPAINT 204 SVDEBUG <<
"render " << m_sources.source
205 <<
": cache partial hit" << endl;
212 m_cache.scrollTo(v, startFrame);
213 m_magCache.scrollTo(v, startFrame);
221 if (!timeConstrained) {
222 if (m_cache.getValidLeft() > x0 &&
223 m_cache.getValidRight() < x1) {
224 m_cache.invalidate();
231 m_cache.setStartFrame(startFrame);
232 m_magCache.setStartFrame(startFrame);
235 bool rightToLeft =
false;
240 if (!m_cache.isValid() && timeConstrained) {
268 bool drawFromTheMiddle =
true;
270 if (!m_secondsPerXPixelValid &&
272 drawFromTheMiddle =
false;
274 int peakCacheIndex = -1, binsPerPeak = -1;
275 getPreferredPeakCache(v, peakCacheIndex, binsPerPeak);
276 if (peakCacheIndex >= 0) {
277 drawFromTheMiddle =
false;
281 if (drawFromTheMiddle) {
282 double offset = 0.5 * (double(rand()) / double(RAND_MAX));
283 x0 = int(x1 * offset);
288 if (m_cache.isValid()) {
300 bool isLeftOfValidArea =
false;
301 m_cache.adjustToTouchValidArea(left, width, isLeftOfValidArea);
308 rightToLeft = isLeftOfValidArea;
316 if (renderType == DrawBufferBinResolution) {
318 renderToCacheBinResolution(v, x0, x1 - x0);
322 if (timeConstrained && !justCreated && justInvalidated) {
323 SVDEBUG <<
"render " << m_sources.source
324 <<
": invalidated cache in time-constrained context, that's all we're doing for now - wait for next update to start filling" << endl;
326 renderToCachePixelResolution(v, x0, x1 - x0, rightToLeft, timeConstrained);
330 QRect pr = rect & m_cache.getValidArea();
331 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(),
332 pr.x(), pr.y(), pr.width(), pr.height());
334 if (!timeConstrained && (pr != rect)) {
335 QRect cva = m_cache.getValidArea();
336 SVCERR <<
"WARNING: failed to render entire requested rect " 337 <<
"even when not time-constrained: wanted " 338 << rect.x() <<
"," << rect.y() <<
" " 339 << rect.width() <<
"x" << rect.height() <<
", got " 340 << pr.x() <<
"," << pr.y() <<
" " 341 << pr.width() <<
"x" << pr.height()
342 <<
", after request of width " << (x1 - x0)
344 <<
"(cache valid area is " 345 << cva.x() <<
"," << cva.y() <<
" " 346 << cva.width() <<
"x" << cva.height() <<
")" 350 MagnitudeRange range = m_magCache.getRange(reqx0, reqx1 - reqx0);
352 #ifdef DEBUG_COLOUR_PLOT_REPAINT 353 SVDEBUG <<
"render " << m_sources.source
354 <<
": returning rect rendered as " << pr.x() <<
"," << pr.y()
355 <<
" " << pr.width() <<
"x" << pr.height() << endl;
356 SVDEBUG <<
"render " << m_sources.source
357 <<
": mag range from cache in x-range " << reqx0
358 <<
" to " << reqx1 <<
" is " << range.getMin() <<
" -> " 359 << range.getMax() << endl;
362 return { pr, range };
368 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
370 return DrawBufferPixelResolution;
373 int binResolution = model->getResolution();
375 sv_samplerate_t modelRate = model->getSampleRate();
378 double relativeBinResolution = binResolution * rateRatio;
382 return DrawBufferPixelResolution;
385 if (!m_params.alwaysOpaque && !m_params.interpolate) {
391 zoomLevel < ZoomLevel(ZoomLevel::FramesPerPixel,
392 int(round(relativeBinResolution / 3)))) {
393 return DirectTranslucent;
397 if (ZoomLevel(ZoomLevel::FramesPerPixel,
398 int(round(relativeBinResolution))) > zoomLevel) {
399 return DrawBufferBinResolution;
401 return DrawBufferPixelResolution;
407 shared_ptr<DenseThreeDimensionalModel> source)
const 416 ColumnOp::Column column;
418 if (m_params.showDerivative && sx > 0) {
420 auto prev = getColumnRaw(sx - 1, minbin, nbins, source);
421 column = getColumnRaw(sx, minbin, nbins, source);
423 for (
int i = 0; i < nbins; ++i) {
424 column[i] -= prev[i];
428 column = getColumnRaw(sx, minbin, nbins, source);
432 !m_sources.fft.isNone()) {
435 column = ColumnOp::applyGain(column, m_params.scaleFactor);
436 column = ColumnOp::normalize(column, m_params.normalization);
443 shared_ptr<DenseThreeDimensionalModel> source)
const 445 Profiler profiler(
"Colour3DPlotRenderer::getColumn");
447 ColumnOp::Column column;
448 ColumnOp::Column fullColumn;
451 auto fftModel = ModelById::getAs<FFTModel>(m_sources.fft);
453 fullColumn = fftModel->getPhases(sx);
457 if (fullColumn.empty()) {
458 fullColumn = source->getColumn(sx);
461 column = ColumnOp::Column(fullColumn.data() + minbin,
462 fullColumn.data() + minbin + nbins);
471 Profiler profiler(
"Colour3DPlotRenderer::renderDirectTranslucent");
473 MagnitudeRange magRange;
475 QPoint illuminatePos;
477 (m_sources.verticalBinLayer, illuminatePos);
479 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
480 if (!model)
return magRange;
482 int x0 = rect.left();
483 int x1 = x0 + rect.width();
487 sv_frame_t modelStart = model->getStartFrame();
488 sv_frame_t modelEnd = model->getEndFrame();
489 int modelResolution = model->getResolution();
495 int sx0 = int((
double(v->
getFrameForX(x0)) / rateRatio -
double(modelStart))
497 int sx1 = int((
double(v->
getFrameForX(x1)) / rateRatio -
double(modelStart))
500 int sh = model->getHeight();
502 const int buflen = 40;
503 char labelbuf[buflen];
505 int minbin = m_sources.verticalBinLayer->getIBinForY(v, h);
506 if (minbin >= sh) minbin = sh - 1;
507 if (minbin < 0) minbin = 0;
509 int nbins = m_sources.verticalBinLayer->getIBinForY(v, 0) - minbin + 1;
510 if (minbin + nbins > sh) nbins = sh - minbin;
514 ColumnOp::Column preparedColumn;
516 int modelWidth = model->getWidth();
518 for (
int sx = sx0; sx <= sx1; ++sx) {
520 if (sx < 0 || sx >= modelWidth) {
531 preparedColumn = getColumn(sx, minbin, nbins, model);
533 magRange.sample(preparedColumn);
536 preparedColumn = ColumnOp::peakPick(preparedColumn);
545 sv_frame_t fx = sx * modelResolution + modelStart;
547 if (fx + modelResolution <= modelStart || fx > modelEnd)
continue;
550 int rx1 = v->
getXForFrame(
int(
double(fx + modelResolution + 1) * rateRatio));
558 #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 560 bool showLabel = (rw > 10 &&
561 paint.fontMetrics().width(
"0.000000") < rw - 3 &&
562 paint.fontMetrics().height() < (h / sh));
564 for (
int sy = minbin; sy < minbin + nbins; ++sy) {
566 int ry0 = m_sources.verticalBinLayer->getIYForBin(v, sy);
567 int ry1 = m_sources.verticalBinLayer->getIYForBin(v, sy + 1);
569 if (m_params.invertVertical) {
574 QRect r(rx0, ry1, rw, ry0 - ry1);
576 float value = preparedColumn[sy - minbin];
577 QColor colour = m_params.colourScale.getColour(value,
578 m_params.colourRotation);
581 paint.setPen(colour);
582 paint.setBrush(Qt::NoBrush);
583 paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1);
587 QColor pen(255, 255, 255, 80);
588 QColor brush(colour);
590 if (rw > 3 && r.height() > 3) {
594 paint.setPen(Qt::NoPen);
595 paint.setBrush(brush);
598 if (r.contains(illuminatePos)) {
603 #ifdef DEBUG_COLOUR_PLOT_REPAINT 611 double value = model->getValueAt(sx, sy);
612 snprintf(labelbuf, buflen,
"%06f", value);
613 QString text(labelbuf);
618 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
631 int &binsPerPeak)
const 636 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
642 int binResolution = model->getResolution();
644 for (
int ix = 0; in_range_for(m_sources.peakCaches, ix); ++ix) {
645 auto peakCache = ModelById::getAs<Dense3DModelPeakCache>
646 (m_sources.peakCaches[ix]);
647 if (!peakCache)
continue;
648 int bpp = peakCache->getColumnsPerPeak();
649 ZoomLevel equivZoom(ZoomLevel::FramesPerPixel, binResolution * bpp);
650 #ifdef DEBUG_COLOUR_PLOT_CACHE_SELECTION 651 SVDEBUG <<
"render " << m_sources.source
652 <<
": getPreferredPeakCache: zoomLevel = " << zoomLevel
653 <<
", cache " << ix <<
" has bpp = " << bpp
654 <<
" for equivZoom = " << equivZoom << endl;
656 if (zoomLevel >= equivZoom) {
658 if (bpp > binsPerPeak) {
666 #ifdef DEBUG_COLOUR_PLOT_CACHE_SELECTION 667 SVDEBUG <<
"render " << m_sources.source
668 <<
": getPreferredPeakCache: zoomLevel = " << zoomLevel
669 <<
", binResolution " << binResolution
670 <<
", peakCaches " << m_sources.peakCaches.size()
671 <<
": preferring peakCacheIndex " << peakCacheIndex
672 <<
" for binsPerPeak " << binsPerPeak
679 int x0,
int repaintWidth,
681 bool timeConstrained)
683 Profiler profiler(
"Colour3DPlotRenderer::renderToCachePixelResolution");
684 #ifdef DEBUG_COLOUR_PLOT_REPAINT 685 SVDEBUG <<
"render " << m_sources.source
686 <<
": [PIXEL] renderToCachePixelResolution" << endl;
693 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
698 clearDrawBuffer(repaintWidth, h);
700 vector<int> binforx(repaintWidth);
701 vector<double> binfory(h);
703 int binResolution = model->getResolution();
705 for (
int x = 0; x < repaintWidth; ++x) {
707 double s0 = double(f0 - model->getStartFrame()) / binResolution;
708 binforx[x] = int(s0 + 0.0001);
711 int peakCacheIndex = -1;
712 int binsPerPeak = -1;
714 getPreferredPeakCache(v, peakCacheIndex, binsPerPeak);
716 for (
int y = 0; y < h; ++y) {
717 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
723 attainedWidth = renderDrawBufferPeakFrequencies(v,
732 attainedWidth = renderDrawBuffer(repaintWidth,
741 if (attainedWidth == 0)
return;
745 int paintedLeft = x0;
747 paintedLeft += (repaintWidth - attainedWidth);
750 m_cache.drawImage(paintedLeft, attainedWidth,
752 paintedLeft - x0, attainedWidth);
754 for (
int i = 0; in_range_for(m_magRanges, i); ++i) {
755 m_magCache.sampleColumn(i, m_magRanges.at(i));
762 int targetHeight)
const 764 int sourceWidth = image.width();
765 int sourceHeight = image.height();
773 if (targetWidth < sourceWidth || targetHeight < sourceHeight) {
774 throw std::logic_error(
"Colour3DPlotRenderer::scaleDrawBufferImage: Can only use this function when making the image larger; should be rendering DrawBufferPixelResolution instead");
777 if (sourceWidth <= 0 || sourceHeight <= 0) {
778 throw std::logic_error(
"Colour3DPlotRenderer::scaleDrawBufferImage: Source image is empty");
781 if (targetWidth <= 0 || targetHeight <= 0) {
782 throw std::logic_error(
"Colour3DPlotRenderer::scaleDrawBufferImage: Target image is empty");
791 if (m_params.interpolate) {
792 return image.scaled(targetWidth, targetHeight,
793 Qt::IgnoreAspectRatio,
794 Qt::SmoothTransformation);
798 QImage target(targetWidth, targetHeight,
799 QImage::Format_ARGB32_Premultiplied);
801 for (
int y = 0; y < targetHeight; ++y) {
803 QRgb *targetLine =
reinterpret_cast<QRgb *
>(target.scanLine(y));
805 int sy = int((uint64_t(y) * sourceHeight) / targetHeight);
806 if (sy == sourceHeight) --sy;
809 const uchar *sourceLine = image.constScanLine(sy);
814 for (
int x = 0; x < targetWidth; ++x) {
816 int sx = int((uint64_t(x) * sourceWidth) / targetWidth);
817 if (sx == sourceWidth) --sx;
820 colour = image.color(sourceLine[sx]);
823 targetLine[x] = colour;
833 int x0,
int repaintWidth)
835 Profiler profiler(
"Colour3DPlotRenderer::renderToCacheBinResolution");
836 #ifdef DEBUG_COLOUR_PLOT_REPAINT 837 SVDEBUG <<
"render " << m_sources.source
838 <<
": [BIN] renderToCacheBinResolution" << endl;
845 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
859 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
860 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
863 int binResolution = model->getResolution();
870 for (
int x = x0; ; --x) {
872 if ((f / binResolution) * binResolution == f) {
873 if (leftCropFrame == -1) leftCropFrame = f;
874 else if (x < x0 - 2) {
875 leftBoundaryFrame = f;
881 for (
int x = x0 + repaintWidth; ; ++x) {
883 if ((f / binResolution) * binResolution == f) {
887 if (rightCropFrame == -1) rightCropFrame = f;
888 else if (x > x0 + repaintWidth + 2) {
889 rightBoundaryFrame = f;
895 drawBufferWidth = int
896 ((rightBoundaryFrame - leftBoundaryFrame) / binResolution);
903 recreateDrawBuffer(drawBufferWidth, h);
905 vector<int> binforx(drawBufferWidth);
906 vector<double> binfory(h);
908 for (
int x = 0; x < drawBufferWidth; ++x) {
909 binforx[x] = int(leftBoundaryFrame / binResolution) + x;
912 #ifdef DEBUG_COLOUR_PLOT_REPAINT 913 SVDEBUG <<
"render " << m_sources.source
914 <<
": binResolution " << binResolution << endl;
917 for (
int y = 0; y < h; ++y) {
918 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
921 int attainedWidth = renderDrawBuffer(drawBufferWidth,
929 if (attainedWidth == 0)
return;
934 #ifdef DEBUG_COLOUR_PLOT_REPAINT 935 SVDEBUG <<
"render " << m_sources.source
936 <<
": scaling draw buffer from width " << m_drawBuffer.width()
937 <<
" to " << (scaledRight - scaledLeft)
938 <<
" (nb drawBufferWidth = " 939 << drawBufferWidth <<
", attainedWidth = " 940 << attainedWidth <<
")" << endl;
943 QImage scaled = scaleDrawBufferImage
944 (m_drawBuffer, scaledRight - scaledLeft, h);
949 int targetLeft = scaledLeftCrop;
950 if (targetLeft < 0) {
954 int targetWidth = scaledRightCrop - targetLeft;
955 if (targetLeft + targetWidth > m_cache.getSize().width()) {
956 targetWidth = m_cache.getSize().width() - targetLeft;
959 int sourceLeft = targetLeft - scaledLeft;
960 if (sourceLeft < 0) {
964 #ifdef DEBUG_COLOUR_PLOT_REPAINT 965 SVDEBUG <<
"render " << m_sources.source
966 <<
": leftBoundaryFrame = " << leftBoundaryFrame
967 <<
", leftCropFrame = " << leftCropFrame
968 <<
", scaledLeft = " << scaledLeft
969 <<
", scaledLeftCrop = " << scaledLeftCrop
971 SVDEBUG <<
"render " << m_sources.source
972 <<
": rightBoundaryFrame = " << rightBoundaryFrame
973 <<
", rightCropFrame = " << rightCropFrame
974 <<
", scaledRight = " << scaledRight
975 <<
", scaledRightCrop = " << scaledRightCrop
979 #ifdef DEBUG_COLOUR_PLOT_REPAINT 980 SVDEBUG <<
"render " << m_sources.source
982 <<
", repaintWidth = " << repaintWidth
983 <<
", targetLeft = " << targetLeft
984 <<
", targetWidth = " << targetWidth << endl;
987 if (targetWidth > 0) {
990 m_cache.drawImage(targetLeft, targetWidth,
992 sourceLeft, targetWidth);
995 for (
int i = 0; i < targetWidth; ++i) {
997 int sourceIx = int((
double(i + sourceLeft) / scaled.width())
998 *
int(m_magRanges.size()));
999 if (in_range_for(m_magRanges, sourceIx)) {
1000 m_magCache.sampleColumn(i, m_magRanges.at(sourceIx));
1007 const vector<int> &binforx,
1008 const vector<double> &binfory,
1011 bool timeConstrained)
1021 Profiler profiler(
"Colour3DPlotRenderer::renderDrawBuffer");
1025 std::shared_ptr<DenseThreeDimensionalModel> sourceModel;
1027 if (peakCacheIndex >= 0) {
1028 auto peakCache = ModelById::getAs<Dense3DModelPeakCache>
1029 (m_sources.peakCaches[peakCacheIndex]);
1031 divisor = peakCache->getColumnsPerPeak();
1032 sourceModel = peakCache;
1037 sourceModel = ModelById::getAs<DenseThreeDimensionalModel>
1041 if (!sourceModel)
return 0;
1043 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1044 SVDEBUG <<
"render " << m_sources.source
1045 <<
": renderDrawBuffer: w = " << w <<
", h = " << h
1046 <<
", peakCacheIndex = " << peakCacheIndex <<
" (divisor = " 1047 << divisor <<
"), rightToLeft = " << rightToLeft
1048 <<
", timeConstrained = " << timeConstrained << endl;
1049 SVDEBUG <<
"render " << m_sources.source
1050 <<
": renderDrawBuffer: normalization = " << int(m_params.normalization)
1051 <<
", binDisplay = " << int(m_params.binDisplay)
1052 <<
", binScale = " << int(m_params.binScale)
1053 <<
", alwaysOpaque = " << m_params.alwaysOpaque
1054 <<
", interpolate = " << m_params.interpolate << endl;
1057 int sh = sourceModel->getHeight();
1059 int minbin = int(binfory[0] + 0.0001);
1060 if (minbin >= sh) minbin = sh - 1;
1061 if (minbin < 0) minbin = 0;
1063 int nbins = int(binfory[h-1] + 0.0001) - minbin + 1;
1064 if (minbin + nbins > sh) nbins = sh - minbin;
1066 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1067 SVDEBUG <<
"render " << m_sources.source
1068 <<
": minbin = " << minbin <<
", nbins = " << nbins
1069 <<
", last binfory = " << binfory[h-1]
1070 <<
" (rounds to " << int(binfory[h-1])
1071 <<
") (model height " << sh <<
")" << endl;
1086 int xPixelCount = 0;
1088 ColumnOp::Column preparedColumn;
1090 int modelWidth = sourceModel->getWidth();
1092 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1093 SVDEBUG <<
"render " << m_sources.source
1094 <<
": modelWidth " << modelWidth <<
", divisor " << divisor << endl;
1095 SVDEBUG <<
"render " << m_sources.source
1096 <<
": start = " << start <<
", finish = " << finish <<
", step = " << step << endl;
1099 for (
int x = start; x != finish; x += step) {
1106 if (binforx[x] < 0)
continue;
1108 int sx0 = binforx[x] / divisor;
1110 if (x+1 < w) sx1 = binforx[x+1] / divisor;
1111 if (sx0 < 0) sx0 = sx1 - 1;
1112 if (sx0 < 0)
continue;
1113 if (sx1 <= sx0) sx1 = sx0 + 1;
1115 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1119 ColumnOp::Column pixelPeakColumn;
1120 MagnitudeRange magRange;
1122 for (
int sx = sx0; sx < sx1; ++sx) {
1124 if (sx < 0 || sx >= modelWidth) {
1135 ColumnOp::Column column = getColumn(sx, minbin, nbins,
1138 magRange.sample(column);
1141 column = ColumnOp::peakPick(column);
1145 ColumnOp::distribute(column,
1149 m_params.interpolate);
1158 pixelPeakColumn = preparedColumn;
1160 for (
int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
1161 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
1167 if (!pixelPeakColumn.empty()) {
1169 for (
int y = 0; y < h; ++y) {
1171 if (m_params.invertVertical) {
1176 m_drawBuffer.setPixel
1179 m_params.colourScale.getPixel(pixelPeakColumn[y]));
1182 m_magRanges.push_back(magRange);
1185 double fractionComplete = double(xPixelCount) / double(w);
1186 if (timer.
outOfTime(fractionComplete)) {
1187 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1188 SVDEBUG <<
"render " << m_sources.source
1189 <<
": out of time with xPixelCount = " << xPixelCount << endl;
1191 updateTimings(timer, xPixelCount);
1196 updateTimings(timer, xPixelCount);
1198 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1199 SVDEBUG <<
"render " << m_sources.source
1200 <<
": completed with xPixelCount = " << xPixelCount << endl;
1208 const vector<int> &binforx,
1209 const vector<double> &binfory,
1211 bool timeConstrained)
1213 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1214 SVDEBUG <<
"render " << m_sources.source
1215 <<
": [PEAK] renderDrawBufferPeakFrequencies" << endl;
1226 auto fft = ModelById::getAs<FFTModel>(m_sources.fft);
1229 int sh = fft->getHeight();
1231 int minbin = int(binfory[0] + 0.0001);
1232 if (minbin >= sh) minbin = sh - 1;
1233 if (minbin < 0) minbin = 0;
1235 int nbins = int(binfory[h-1]) - minbin + 1;
1236 if (minbin + nbins > sh) nbins = sh - minbin;
1238 FFTModel::PeakSet peakfreqs;
1252 int xPixelCount = 0;
1254 ColumnOp::Column preparedColumn;
1256 int modelWidth = fft->getWidth();
1257 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1258 SVDEBUG <<
"render " << m_sources.source
1259 <<
": modelWidth " << modelWidth << endl;
1263 (double(minbin) * fft->getSampleRate()) / fft->getFFTSize();
1265 (double(minbin + nbins - 1) * fft->getSampleRate()) / fft->getFFTSize();
1269 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1270 SVDEBUG <<
"render " << m_sources.source
1271 <<
": start = " << start <<
", finish = " << finish
1272 <<
", step = " << step << endl;
1275 for (
int x = start; x != finish; x += step) {
1282 if (binforx[x] < 0)
continue;
1284 int sx0 = binforx[x];
1286 if (x+1 < w) sx1 = binforx[x+1];
1287 if (sx0 < 0) sx0 = sx1 - 1;
1288 if (sx0 < 0)
continue;
1289 if (sx1 <= sx0) sx1 = sx0 + 1;
1291 ColumnOp::Column pixelPeakColumn;
1292 MagnitudeRange magRange;
1294 for (
int sx = sx0; sx < sx1; ++sx) {
1296 if (sx < 0 || sx >= modelWidth) {
1301 preparedColumn = getColumn(sx, minbin, nbins, fft);
1302 magRange.sample(preparedColumn);
1307 pixelPeakColumn = preparedColumn;
1308 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
1309 minbin, minbin + nbins - 1);
1311 for (
int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
1312 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
1318 if (!pixelPeakColumn.empty()) {
1320 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1325 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
1326 pi != peakfreqs.end(); ++pi) {
1328 int bin = pi->first;
1329 double freq = pi->second;
1331 if (bin < minbin)
continue;
1332 if (bin >= minbin + nbins)
break;
1334 double value = pixelPeakColumn[bin - minbin];
1337 (freq, minFreq, maxFreq, logarithmic);
1339 int iy = int(y + 0.5);
1340 if (iy < 0 || iy >= h)
continue;
1342 auto pixel = m_params.colourScale.getPixel(value);
1344 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1350 m_drawBuffer.setPixel(x, iy, pixel);
1353 m_magRanges.push_back(magRange);
1356 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1357 SVDEBUG <<
"render " << m_sources.source
1358 <<
": pixel peak column for range " << sx0 <<
" to " << sx1
1359 <<
" is empty" << endl;
1363 double fractionComplete = double(xPixelCount) / double(w);
1364 if (timer.
outOfTime(fractionComplete)) {
1365 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1366 SVDEBUG <<
"render " << m_sources.source
1367 <<
": out of time" << endl;
1369 updateTimings(timer, xPixelCount);
1374 updateTimings(timer, xPixelCount);
1385 bool valid = (xPixelCount > 20 || secondsPerXPixel > 0.01);
1388 m_secondsPerXPixel = secondsPerXPixel;
1389 m_secondsPerXPixelValid =
true;
1391 #ifdef DEBUG_COLOUR_PLOT_REPAINT 1392 SVDEBUG <<
"render " << m_sources.source
1393 <<
": across " << xPixelCount
1394 <<
" x-pixels, seconds per x-pixel = " 1395 << m_secondsPerXPixel <<
" (total = " 1396 << (xPixelCount * m_secondsPerXPixel) <<
")" << endl;
1404 m_drawBuffer = QImage(w, h, QImage::Format_Indexed8);
1406 for (
int pixel = 0; pixel < 256; ++pixel) {
1407 m_drawBuffer.setColor
1408 ((
unsigned char)pixel,
1409 m_params.colourScale.getColourForPixel
1410 (pixel, m_params.colourRotation).rgb());
1413 m_drawBuffer.fill(0);
1414 m_magRanges.clear();
1420 if (m_drawBuffer.width() < w || m_drawBuffer.height() != h) {
1421 recreateDrawBuffer(w, h);
1423 m_drawBuffer.fill(0);
1424 m_magRanges.clear();
1431 QImage image = m_cache.getImage();
virtual int getXForViewX(int viewx) const =0
Return the closest pixel x-coordinate corresponding to a given view x-coordinate. ...
int renderDrawBuffer(int w, int h, const std::vector< int > &binforx, const std::vector< double > &binfory, int peakCacheIndex, bool rightToLeft, bool timeConstrained)
virtual QColor getForeground() const =0
void clearDrawBuffer(int w, int h)
void renderToCachePixelResolution(const LayerGeometryProvider *v, int x0, int repaintWidth, bool rightToLeft, bool timeConstrained)
virtual QSize getPaintSize() const
An operation that the user might accept being slower.
bool outOfTime(double fractionComplete)
Return true if we have run out of time and should suspend rendering and handle user events instead...
virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) const =0
virtual ZoomLevel getZoomLevel() const =0
Return the zoom level, i.e.
RenderResult renderTimeConstrained(const LayerGeometryProvider *v, QPainter &paint, QRect rect)
Render the requested area using the given painter, obtaining geometry (e.g.
void recreateDrawBuffer(int w, int h)
sv_samplerate_t getMainModelSampleRate() const
The sample rate of the current main model.
virtual sv_frame_t getFrameForX(int x) const =0
Return the closest frame to the given pixel x-coordinate.
An operation that should always complete, i.e.
bool geometryChanged(const LayerGeometryProvider *v)
Return true if the provider's geometry differs from the cache, or if we are not using a cache...
QImage scaleDrawBufferImage(QImage source, int targetWidth, int targetHeight) const
ColumnOp::Column getColumnRaw(int sx, int minbin, int nbins, std::shared_ptr< DenseThreeDimensionalModel > source) const
Interface for classes that provide geometry information (such as size, start frame, and a large number of other properties) about the disposition of a layer.
virtual double getYForFrequency(double frequency, double minFreq, double maxFreq, bool logarithmic) const =0
Return the (maybe fractional) pixel y-coordinate corresponding to a given frequency, if the frequency range is as specified.
virtual sv_frame_t getStartFrame() const =0
Retrieve the first visible sample frame on the widget.
void updateTimings(const RenderTimer &timer, int xPixelCount)
RenderResult render(const LayerGeometryProvider *v, QPainter &paint, QRect rect)
Render the requested area using the given painter, obtaining geometry (e.g.
virtual int getPaintHeight() const
ColumnOp::Column getColumn(int sx, int minbin, int nbins, std::shared_ptr< DenseThreeDimensionalModel > source) const
virtual ViewManager * getViewManager() const =0
QRect findRegionExtents(QImage *image, QPoint origin) const
MagnitudeRange renderDirectTranslucent(const LayerGeometryProvider *v, QPainter &paint, QRect rect)
int renderDrawBufferPeakFrequencies(const LayerGeometryProvider *v, int w, int h, const std::vector< int > &binforx, const std::vector< double > &binfory, bool rightToLeft, bool timeConstrained)
QRect getLargestUncachedRect(const LayerGeometryProvider *v)
Return the area of the largest rectangle within the entire area of the cache that is unavailable in t...
double secondsPerItem(int itemsRendered) const
RenderType decideRenderType(const LayerGeometryProvider *) const
static void drawVisibleText(const LayerGeometryProvider *, QPainter &p, int x, int y, QString text, TextStyle style)
void renderToCacheBinResolution(const LayerGeometryProvider *v, int x0, int repaintWidth)
virtual int getXForFrame(sv_frame_t frame) const =0
Return the pixel x-coordinate corresponding to a given sample frame (which may be negative)...
virtual int getPaintWidth() const
void getPreferredPeakCache(const LayerGeometryProvider *, int &peakCacheIndex, int &binsPerPeak) const
A normal rendering operation with normal responsiveness demands.
QRect findSimilarRegionExtents(QPoint point) const
Return the enclosing rectangle for the region of similar colour to the given point within the cache...