annotate layer/Colour3DPlotRenderer.cpp @ 1127:9fb8dfd7ce4c spectrogram-minor-refactor

Fix threshold in spectrogram -- it wasn't working in the last release. There is a new protocol for this. Formerly the threshold parameter had a range from -50dB to 0 with the default at -50, and -50 treated internally as "no threshold". However, there was a hardcoded, hidden internal threshold for spectrogram colour mapping at -80dB with anything below this being rounded to zero. Now the threshold parameter has range -81 to -1 with the default at -80, -81 is treated internally as "no threshold", and there is no hidden internal threshold. So the default behaviour is the same as before, an effective -80dB threshold, but it is now possible to change this in both directions. Sessions reloaded from prior versions may look slightly different because, if the session says there should be no threshold, there will now actually be no threshold instead of having the hidden internal one. Still need to do something in the UI to make it apparent that the -81dB setting removes the threshold entirely. This is at least no worse than the previous, also obscured, magic -50dB setting.
author Chris Cannam
date Mon, 01 Aug 2016 16:21:01 +0100
parents 50324fca1328
children dc4b8fd3fcb7
rev   line source
Chris@1071 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1071 2
Chris@1071 3 /*
Chris@1071 4 Sonic Visualiser
Chris@1071 5 An audio file viewer and annotation editor.
Chris@1071 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1071 7 This file copyright 2006-2016 Chris Cannam and QMUL.
Chris@1071 8
Chris@1071 9 This program is free software; you can redistribute it and/or
Chris@1071 10 modify it under the terms of the GNU General Public License as
Chris@1071 11 published by the Free Software Foundation; either version 2 of the
Chris@1071 12 License, or (at your option) any later version. See the file
Chris@1071 13 COPYING included with this distribution for more information.
Chris@1071 14 */
Chris@1071 15
Chris@1071 16 #include "Colour3DPlotRenderer.h"
Chris@1074 17 #include "RenderTimer.h"
Chris@1071 18
Chris@1117 19 #include "base/Profiler.h"
Chris@1117 20
Chris@1075 21 #include "data/model/DenseThreeDimensionalModel.h"
Chris@1075 22 #include "data/model/Dense3DModelPeakCache.h"
Chris@1075 23 #include "data/model/FFTModel.h"
Chris@1075 24
Chris@1077 25 #include "LayerGeometryProvider.h"
Chris@1082 26 #include "VerticalBinLayer.h"
Chris@1109 27 #include "PaintAssistant.h"
Chris@1109 28
Chris@1109 29 #include "view/ViewManager.h" // for main model sample rate. Pity
Chris@1075 30
Chris@1079 31 #include <vector>
Chris@1079 32
Chris@1123 33 #define DEBUG_COLOUR_PLOT_REPAINT 1
Chris@1094 34
Chris@1079 35 using namespace std;
Chris@1079 36
Chris@1073 37 Colour3DPlotRenderer::RenderResult
Chris@1113 38 Colour3DPlotRenderer::render(const LayerGeometryProvider *v, QPainter &paint, QRect rect)
Chris@1076 39 {
Chris@1090 40 return render(v, paint, rect, false);
Chris@1076 41 }
Chris@1076 42
Chris@1076 43 Colour3DPlotRenderer::RenderResult
Chris@1113 44 Colour3DPlotRenderer::renderTimeConstrained(const LayerGeometryProvider *v,
Chris@1090 45 QPainter &paint, QRect rect)
Chris@1076 46 {
Chris@1090 47 return render(v, paint, rect, true);
Chris@1076 48 }
Chris@1076 49
Chris@1096 50 QRect
Chris@1121 51 Colour3DPlotRenderer::getLargestUncachedRect(const LayerGeometryProvider *v)
Chris@1096 52 {
Chris@1121 53 RenderType renderType = decideRenderType(v);
Chris@1121 54
Chris@1121 55 if (renderType == DirectTranslucent) {
Chris@1121 56 return QRect(); // never cached
Chris@1121 57 }
Chris@1121 58
Chris@1096 59 int h = m_cache.getSize().height();
Chris@1096 60
Chris@1096 61 QRect areaLeft(0, 0, m_cache.getValidLeft(), h);
Chris@1096 62 QRect areaRight(m_cache.getValidRight(), 0,
Chris@1096 63 m_cache.getSize().width() - m_cache.getValidRight(), h);
Chris@1096 64
Chris@1096 65 if (areaRight.width() > areaLeft.width()) {
Chris@1096 66 return areaRight;
Chris@1096 67 } else {
Chris@1096 68 return areaLeft;
Chris@1096 69 }
Chris@1096 70 }
Chris@1096 71
Chris@1122 72 bool
Chris@1122 73 Colour3DPlotRenderer::geometryChanged(const LayerGeometryProvider *v)
Chris@1122 74 {
Chris@1122 75 RenderType renderType = decideRenderType(v);
Chris@1122 76
Chris@1122 77 if (renderType == DirectTranslucent) {
Chris@1122 78 return true; // never cached
Chris@1122 79 }
Chris@1122 80
Chris@1122 81 if (m_cache.getSize() == v->getPaintSize() &&
Chris@1122 82 m_cache.getZoomLevel() == v->getZoomLevel() &&
Chris@1122 83 m_cache.getStartFrame() == v->getStartFrame()) {
Chris@1122 84 return false;
Chris@1122 85 } else {
Chris@1122 86 return true;
Chris@1122 87 }
Chris@1122 88 }
Chris@1122 89
Chris@1076 90 Colour3DPlotRenderer::RenderResult
Chris@1113 91 Colour3DPlotRenderer::render(const LayerGeometryProvider *v,
Chris@1090 92 QPainter &paint, QRect rect, bool timeConstrained)
Chris@1073 93 {
Chris@1109 94 RenderType renderType = decideRenderType(v);
Chris@1109 95
Chris@1109 96 if (renderType != DrawBufferPixelResolution) {
Chris@1109 97 // Rendering should be fast in bin-resolution and direct draw
Chris@1109 98 // cases because we are quite well zoomed-in, and the sums are
Chris@1109 99 // easier this way. Calculating boundaries later will be
Chris@1109 100 // fiddly for partial paints otherwise.
Chris@1109 101 timeConstrained = false;
Chris@1109 102 }
Chris@1109 103
Chris@1079 104 int x0 = v->getXForViewX(rect.x());
Chris@1079 105 int x1 = v->getXForViewX(rect.x() + rect.width());
Chris@1079 106 if (x0 < 0) x0 = 0;
Chris@1079 107 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth();
Chris@1079 108
Chris@1120 109 sv_frame_t startFrame = v->getStartFrame();
Chris@1120 110
Chris@1079 111 m_cache.resize(v->getPaintSize());
Chris@1079 112 m_cache.setZoomLevel(v->getZoomLevel());
Chris@1079 113
Chris@1119 114 m_magCache.resize(v->getPaintSize().width());
Chris@1119 115 m_magCache.setZoomLevel(v->getZoomLevel());
Chris@1119 116
Chris@1120 117 if (renderType == DirectTranslucent) {
Chris@1121 118 MagnitudeRange range = renderDirectTranslucent(v, paint, rect);
Chris@1121 119 return { rect, range };
Chris@1120 120 }
Chris@1120 121
Chris@1123 122 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1090 123 cerr << "cache start " << m_cache.getStartFrame()
Chris@1090 124 << " valid left " << m_cache.getValidLeft()
Chris@1090 125 << " valid right " << m_cache.getValidRight()
Chris@1094 126 << endl;
Chris@1094 127 cerr << " view start " << startFrame
Chris@1090 128 << " x0 " << x0
Chris@1090 129 << " x1 " << x1
Chris@1090 130 << endl;
Chris@1123 131 #endif
Chris@1090 132
Chris@1079 133 if (m_cache.isValid()) { // some part of the cache is valid
Chris@1079 134
Chris@1079 135 if (v->getXForFrame(m_cache.getStartFrame()) ==
Chris@1079 136 v->getXForFrame(startFrame) &&
Chris@1079 137 m_cache.getValidLeft() <= x0 &&
Chris@1079 138 m_cache.getValidRight() >= x1) {
Chris@1090 139
Chris@1123 140 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1090 141 cerr << "cache hit" << endl;
Chris@1123 142 #endif
Chris@1090 143
Chris@1079 144 // cache is valid for the complete requested area
Chris@1079 145 paint.drawImage(rect, m_cache.getImage(), rect);
Chris@1119 146
Chris@1122 147 MagnitudeRange range = m_magCache.getRange(x0, x1 - x0);
Chris@1119 148
Chris@1119 149 return { rect, range };
Chris@1079 150
Chris@1079 151 } else {
Chris@1123 152 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1090 153 cerr << "cache partial hit" << endl;
Chris@1123 154 #endif
Chris@1090 155
Chris@1079 156 // cache doesn't begin at the right frame or doesn't
Chris@1079 157 // contain the complete view, but might be scrollable or
Chris@1079 158 // partially usable
Chris@1090 159 m_cache.scrollTo(v, startFrame);
Chris@1119 160 m_magCache.scrollTo(v, startFrame);
Chris@1079 161
Chris@1079 162 // if we are not time-constrained, then we want to paint
Chris@1081 163 // the whole area in one go; we don't return a partial
Chris@1081 164 // paint. To avoid providing the more complex logic to
Chris@1081 165 // handle painting discontiguous areas, if the only valid
Chris@1079 166 // part of cache is in the middle, just make the whole
Chris@1079 167 // thing invalid and start again.
Chris@1079 168 if (!timeConstrained) {
Chris@1079 169 if (m_cache.getValidLeft() > x0 &&
Chris@1079 170 m_cache.getValidRight() < x1) {
Chris@1079 171 m_cache.invalidate();
Chris@1079 172 }
Chris@1079 173 }
Chris@1079 174 }
Chris@1090 175 } else {
Chris@1118 176 // cache is completely invalid
Chris@1090 177 m_cache.setStartFrame(startFrame);
Chris@1119 178 m_magCache.setStartFrame(startFrame);
Chris@1075 179 }
Chris@1075 180
Chris@1079 181 bool rightToLeft = false;
Chris@1079 182
Chris@1122 183 int reqx0 = x0;
Chris@1122 184 int reqx1 = x1;
Chris@1122 185
Chris@1079 186 if (!m_cache.isValid() && timeConstrained) {
Chris@1081 187 // When rendering the whole area, in a context where we might
Chris@1081 188 // not be able to complete the work, start from somewhere near
Chris@1081 189 // the middle so that the region of interest appears first
Chris@1079 190
Chris@1079 191 //!!! (perhaps we should avoid doing this if past repaints
Chris@1079 192 //!!! have been fast enough to do the whole in one shot)
Chris@1079 193 if (x0 == 0 && x1 == v->getPaintWidth()) {
Chris@1079 194 x0 = int(x1 * 0.3);
Chris@1079 195 }
Chris@1079 196 }
Chris@1079 197
Chris@1079 198 if (m_cache.isValid()) {
Chris@1090 199
Chris@1079 200 // When rendering only a part of the cache, we need to make
Chris@1079 201 // sure that the part we're rendering is adjacent to (or
Chris@1079 202 // overlapping) a valid area of cache, if we have one. The
Chris@1079 203 // alternative is to ditch the valid area of cache and render
Chris@1079 204 // only the requested area, but that's risky because this can
Chris@1079 205 // happen when just waving the pointer over a small part of
Chris@1079 206 // the view -- if we lose the partly-built cache every time
Chris@1079 207 // the user does that, we'll never finish building it.
Chris@1079 208 int left = x0;
Chris@1079 209 int width = x1 - x0;
Chris@1079 210 bool isLeftOfValidArea = false;
Chris@1079 211 m_cache.adjustToTouchValidArea(left, width, isLeftOfValidArea);
Chris@1079 212 x0 = left;
Chris@1079 213 x1 = x0 + width;
Chris@1079 214
Chris@1079 215 // That call also told us whether we should be painting
Chris@1079 216 // sub-regions of our target region in right-to-left order in
Chris@1079 217 // order to ensure contiguity
Chris@1079 218 rightToLeft = isLeftOfValidArea;
Chris@1079 219 }
Chris@1075 220
Chris@1109 221 // Note, we always paint the full height to cache. We want to
Chris@1109 222 // ensure the cache is coherent without having to worry about
Chris@1109 223 // vertical matching of required and valid areas as well as
Chris@1109 224 // horizontal.
Chris@1094 225
Chris@1109 226 if (renderType == DrawBufferBinResolution) {
Chris@1109 227
Chris@1094 228 renderToCacheBinResolution(v, x0, x1 - x0);
Chris@1109 229
Chris@1109 230 } else { // must be DrawBufferPixelResolution, handled DirectTranslucent earlier
Chris@1109 231
Chris@1094 232 renderToCachePixelResolution(v, x0, x1 - x0, rightToLeft, timeConstrained);
Chris@1094 233 }
Chris@1079 234
Chris@1079 235 QRect pr = rect & m_cache.getValidArea();
Chris@1079 236 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(),
Chris@1079 237 pr.x(), pr.y(), pr.width(), pr.height());
Chris@1079 238
Chris@1079 239 if (!timeConstrained && (pr != rect)) {
Chris@1079 240 //!!! on a first cut, there is a risk that this will happen
Chris@1079 241 //!!! when we are at start/end of model -- trap, report, and
Chris@1079 242 //!!! then fix
Chris@1079 243 throw std::logic_error("internal error: failed to render entire requested rect even when not time-constrained");
Chris@1079 244 }
Chris@1120 245
Chris@1122 246 MagnitudeRange range = m_magCache.getRange(reqx0, reqx1 - reqx0);
Chris@1120 247
Chris@1120 248 return { pr, range };
Chris@1079 249
Chris@1079 250 //!!! todo, here or in caller: illuminateLocalFeatures
Chris@1079 251
Chris@1123 252 //!!! todo: handle vertical range other than full range of column
Chris@1110 253
Chris@1079 254 //!!! fft model scaling?
Chris@1073 255 }
Chris@1073 256
Chris@1109 257 Colour3DPlotRenderer::RenderType
Chris@1113 258 Colour3DPlotRenderer::decideRenderType(const LayerGeometryProvider *v) const
Chris@1094 259 {
Chris@1100 260 const DenseThreeDimensionalModel *model = m_sources.source;
Chris@1109 261 if (!model || !v || !(v->getViewManager())) {
Chris@1109 262 return DrawBufferPixelResolution; // or anything
Chris@1109 263 }
Chris@1109 264
Chris@1094 265 int binResolution = model->getResolution();
Chris@1094 266 int zoomLevel = v->getZoomLevel();
Chris@1109 267 sv_samplerate_t modelRate = model->getSampleRate();
Chris@1109 268
Chris@1109 269 double rateRatio = v->getViewManager()->getMainModelSampleRate() / modelRate;
Chris@1109 270 double relativeBinResolution = binResolution * rateRatio;
Chris@1109 271
Chris@1109 272 if (m_params.binDisplay == BinDisplay::PeakFrequencies) {
Chris@1109 273 // no alternative works here
Chris@1109 274 return DrawBufferPixelResolution;
Chris@1109 275 }
Chris@1109 276
Chris@1109 277 if (!m_params.alwaysOpaque && !m_params.interpolate) {
Chris@1109 278
Chris@1109 279 // consider translucent option -- only if not smoothing & not
Chris@1109 280 // explicitly requested opaque & sufficiently zoomed-in
Chris@1109 281
Chris@1117 282 if (model->getHeight() * 3 < v->getPaintHeight() &&
Chris@1117 283 relativeBinResolution >= 3 * zoomLevel) {
Chris@1109 284 return DirectTranslucent;
Chris@1109 285 }
Chris@1109 286 }
Chris@1109 287
Chris@1109 288 if (relativeBinResolution > zoomLevel) {
Chris@1109 289 return DrawBufferBinResolution;
Chris@1109 290 } else {
Chris@1109 291 return DrawBufferPixelResolution;
Chris@1109 292 }
Chris@1109 293 }
Chris@1109 294
Chris@1121 295 MagnitudeRange
Chris@1113 296 Colour3DPlotRenderer::renderDirectTranslucent(const LayerGeometryProvider *v,
Chris@1109 297 QPainter &paint,
Chris@1109 298 QRect rect)
Chris@1109 299 {
Chris@1117 300 Profiler profiler("Colour3DPlotRenderer::renderDirectTranslucent");
Chris@1117 301
Chris@1121 302 MagnitudeRange magRange;
Chris@1121 303
Chris@1115 304 QPoint illuminatePos;
Chris@1115 305 bool illuminate = v->shouldIlluminateLocalFeatures
Chris@1115 306 (m_sources.verticalBinLayer, illuminatePos);
Chris@1109 307
Chris@1109 308 const DenseThreeDimensionalModel *model = m_sources.source;
Chris@1109 309
Chris@1109 310 int x0 = rect.left();
Chris@1109 311 int x1 = rect.right() + 1;
Chris@1109 312
Chris@1109 313 int h = v->getPaintHeight();
Chris@1109 314
Chris@1109 315 sv_frame_t modelStart = model->getStartFrame();
Chris@1109 316 sv_frame_t modelEnd = model->getEndFrame();
Chris@1109 317 int modelResolution = model->getResolution();
Chris@1109 318
Chris@1109 319 double rateRatio =
Chris@1109 320 v->getViewManager()->getMainModelSampleRate() / model->getSampleRate();
Chris@1109 321
Chris@1109 322 // the s-prefix values are source, i.e. model, column and bin numbers
Chris@1109 323 int sx0 = int((double(v->getFrameForX(x0)) / rateRatio - double(modelStart))
Chris@1109 324 / modelResolution);
Chris@1109 325 int sx1 = int((double(v->getFrameForX(x1)) / rateRatio - double(modelStart))
Chris@1109 326 / modelResolution);
Chris@1109 327
Chris@1109 328 int sh = model->getHeight();
Chris@1109 329
Chris@1109 330 const int buflen = 40;
Chris@1109 331 char labelbuf[buflen];
Chris@1109 332
Chris@1115 333 int minbin = 0; //!!!
Chris@1109 334 int maxbin = sh - 1; //!!!
Chris@1109 335
Chris@1109 336 int psx = -1;
Chris@1109 337
Chris@1109 338 vector<float> preparedColumn;
Chris@1109 339
Chris@1109 340 int modelWidth = model->getWidth();
Chris@1109 341
Chris@1109 342 for (int sx = sx0; sx <= sx1; ++sx) {
Chris@1109 343
Chris@1109 344 if (sx < 0 || sx >= modelWidth) {
Chris@1109 345 continue;
Chris@1109 346 }
Chris@1109 347
Chris@1109 348 if (sx != psx) {
Chris@1109 349
Chris@1109 350 //!!! this is in common with renderDrawBuffer - pull it out
Chris@1109 351
Chris@1109 352 // order:
Chris@1109 353 // get column -> scale -> record extents ->
Chris@1124 354 // normalise -> peak pick -> apply display gain
Chris@1124 355
Chris@1109 356 ColumnOp::Column fullColumn = model->getColumn(sx);
Chris@1109 357
Chris@1109 358 ColumnOp::Column column =
Chris@1109 359 vector<float>(fullColumn.data() + minbin,
Chris@1109 360 fullColumn.data() + maxbin + 1);
Chris@1109 361
Chris@1125 362 column = ColumnOp::applyGain(column, m_params.scaleFactor);
Chris@1125 363
Chris@1121 364 magRange.sample(column);
Chris@1121 365
Chris@1109 366 //!!! fft scale if (m_colourScale != ColourScaleType::Phase) {
Chris@1109 367 // column = ColumnOp::fftScale(column, m_fftSize);
Chris@1109 368 // }
Chris@1109 369
Chris@1109 370 // if (m_colourScale != ColourScaleType::Phase) {
Chris@1115 371 preparedColumn = ColumnOp::normalize(column, m_params.normalization);
Chris@1109 372 // }
Chris@1109 373
Chris@1109 374 if (m_params.binDisplay == BinDisplay::PeakBins) {
Chris@1115 375 preparedColumn = ColumnOp::peakPick(preparedColumn);
Chris@1109 376 }
Chris@1109 377
Chris@1124 378 // Display gain belongs to the colour scale and is
Chris@1124 379 // applied by the colour scale object when mapping it
Chris@1124 380
Chris@1109 381 psx = sx;
Chris@1109 382 }
Chris@1109 383
Chris@1109 384 sv_frame_t fx = sx * modelResolution + modelStart;
Chris@1109 385
Chris@1109 386 if (fx + modelResolution <= modelStart || fx > modelEnd) continue;
Chris@1109 387
Chris@1109 388 int rx0 = v->getXForFrame(int(double(fx) * rateRatio));
Chris@1109 389 int rx1 = v->getXForFrame(int(double(fx + modelResolution + 1) * rateRatio));
Chris@1109 390
Chris@1109 391 int rw = rx1 - rx0;
Chris@1109 392 if (rw < 1) rw = 1;
Chris@1109 393
Chris@1109 394 bool showLabel = (rw > 10 &&
Chris@1109 395 paint.fontMetrics().width("0.000000") < rw - 3 &&
Chris@1109 396 paint.fontMetrics().height() < (h / sh));
Chris@1109 397
Chris@1109 398 for (int sy = minbin; sy <= maxbin; ++sy) {
Chris@1109 399
Chris@1109 400 int ry0 = m_sources.verticalBinLayer->getIYForBin(v, sy);
Chris@1109 401 int ry1 = m_sources.verticalBinLayer->getIYForBin(v, sy + 1);
Chris@1116 402
Chris@1116 403 if (m_params.invertVertical) {
Chris@1116 404 ry0 = h - ry0 - 1;
Chris@1116 405 ry1 = h - ry1 - 1;
Chris@1116 406 }
Chris@1116 407
Chris@1109 408 QRect r(rx0, ry1, rw, ry0 - ry1);
Chris@1109 409
Chris@1109 410 float value = preparedColumn[sy - minbin];
Chris@1112 411 QColor colour = m_params.colourScale.getColour(value,
Chris@1112 412 m_params.colourRotation);
Chris@1109 413
Chris@1109 414 if (rw == 1) {
Chris@1109 415 paint.setPen(colour);
Chris@1109 416 paint.setBrush(Qt::NoBrush);
Chris@1109 417 paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1);
Chris@1109 418 continue;
Chris@1109 419 }
Chris@1109 420
Chris@1109 421 QColor pen(255, 255, 255, 80);
Chris@1109 422 QColor brush(colour);
Chris@1109 423
Chris@1109 424 if (rw > 3 && r.height() > 3) {
Chris@1109 425 brush.setAlpha(160);
Chris@1109 426 }
Chris@1109 427
Chris@1109 428 paint.setPen(Qt::NoPen);
Chris@1109 429 paint.setBrush(brush);
Chris@1109 430
Chris@1115 431 if (illuminate) {
Chris@1115 432 if (r.contains(illuminatePos)) {
Chris@1115 433 paint.setPen(v->getForeground());
Chris@1115 434 }
Chris@1115 435 }
Chris@1109 436
Chris@1109 437 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@1109 438 // cerr << "rect " << r.x() << "," << r.y() << " "
Chris@1109 439 // << r.width() << "x" << r.height() << endl;
Chris@1109 440 #endif
Chris@1109 441
Chris@1109 442 paint.drawRect(r);
Chris@1109 443
Chris@1109 444 if (showLabel) {
Chris@1109 445 double value = model->getValueAt(sx, sy);
Chris@1109 446 snprintf(labelbuf, buflen, "%06f", value);
Chris@1109 447 QString text(labelbuf);
Chris@1109 448 PaintAssistant::drawVisibleText
Chris@1109 449 (v,
Chris@1109 450 paint,
Chris@1109 451 rx0 + 2,
Chris@1109 452 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
Chris@1109 453 text,
Chris@1109 454 PaintAssistant::OutlinedText);
Chris@1109 455 }
Chris@1109 456 }
Chris@1109 457 }
Chris@1121 458
Chris@1121 459 return magRange;
Chris@1094 460 }
Chris@1094 461
Chris@1080 462 void
Chris@1113 463 Colour3DPlotRenderer::renderToCachePixelResolution(const LayerGeometryProvider *v,
Chris@1094 464 int x0, int repaintWidth,
Chris@1094 465 bool rightToLeft,
Chris@1094 466 bool timeConstrained)
Chris@1079 467 {
Chris@1117 468 Profiler profiler("Colour3DPlotRenderer::renderToCachePixelResolution");
Chris@1094 469 cerr << "renderToCachePixelResolution" << endl;
Chris@1094 470
Chris@1094 471 // Draw to the draw buffer, and then copy from there. The draw
Chris@1094 472 // buffer is at the same resolution as the target in the cache, so
Chris@1094 473 // no extra scaling needed.
Chris@1079 474
Chris@1100 475 const DenseThreeDimensionalModel *model = m_sources.source;
Chris@1079 476 if (!model || !model->isOK() || !model->isReady()) {
Chris@1079 477 throw std::logic_error("no source model provided, or model not ready");
Chris@1079 478 }
Chris@1079 479
Chris@1079 480 int h = v->getPaintHeight();
Chris@1079 481
Chris@1094 482 clearDrawBuffer(repaintWidth, h);
Chris@1079 483
Chris@1094 484 vector<int> binforx(repaintWidth);
Chris@1079 485 vector<double> binfory(h);
Chris@1079 486
Chris@1079 487 bool usePeaksCache = false;
Chris@1079 488 int binsPerPeak = 1;
Chris@1094 489 int zoomLevel = v->getZoomLevel();
Chris@1094 490 int binResolution = model->getResolution();
Chris@1079 491
Chris@1094 492 for (int x = 0; x < repaintWidth; ++x) {
Chris@1094 493 sv_frame_t f0 = v->getFrameForX(x0 + x);
Chris@1094 494 double s0 = double(f0 - model->getStartFrame()) / binResolution;
Chris@1094 495 binforx[x] = int(s0 + 0.0001);
Chris@1094 496 }
Chris@1080 497
Chris@1094 498 if (m_sources.peaks) { // peaks cache exists
Chris@1080 499
Chris@1094 500 binsPerPeak = m_sources.peaks->getColumnsPerPeak();
Chris@1094 501 usePeaksCache = (binResolution * binsPerPeak) < zoomLevel;
Chris@1094 502
Chris@1094 503 if (m_params.colourScale.getScale() ==
Chris@1105 504 ColourScaleType::Phase) {
Chris@1094 505 usePeaksCache = false;
Chris@1079 506 }
Chris@1079 507 }
Chris@1082 508
Chris@1094 509 cerr << "[PIX] zoomLevel = " << zoomLevel
Chris@1094 510 << ", binResolution " << binResolution
Chris@1094 511 << ", binsPerPeak " << binsPerPeak
Chris@1094 512 << ", peak cache " << m_sources.peaks
Chris@1094 513 << ", usePeaksCache = " << usePeaksCache
Chris@1094 514 << endl;
Chris@1094 515
Chris@1080 516 for (int y = 0; y < h; ++y) {
Chris@1090 517 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
Chris@1080 518 }
Chris@1079 519
Chris@1097 520 int attainedWidth;
Chris@1097 521
Chris@1103 522 if (m_params.binDisplay == BinDisplay::PeakFrequencies) {
Chris@1097 523 attainedWidth = renderDrawBufferPeakFrequencies(v,
Chris@1097 524 repaintWidth,
Chris@1097 525 h,
Chris@1097 526 binforx,
Chris@1097 527 binfory,
Chris@1097 528 rightToLeft,
Chris@1097 529 timeConstrained);
Chris@1097 530
Chris@1097 531 } else {
Chris@1097 532 attainedWidth = renderDrawBuffer(repaintWidth,
Chris@1080 533 h,
Chris@1080 534 binforx,
Chris@1080 535 binfory,
Chris@1080 536 usePeaksCache,
Chris@1080 537 rightToLeft,
Chris@1080 538 timeConstrained);
Chris@1097 539 }
Chris@1083 540
Chris@1094 541 if (attainedWidth == 0) return;
Chris@1084 542
Chris@1094 543 // draw buffer is pixel resolution, no scaling factors or padding involved
Chris@1084 544
Chris@1084 545 int paintedLeft = x0;
Chris@1084 546 if (rightToLeft) {
Chris@1084 547 paintedLeft += (repaintWidth - attainedWidth);
Chris@1084 548 }
Chris@1084 549
Chris@1094 550 m_cache.drawImage(paintedLeft, attainedWidth,
Chris@1094 551 m_drawBuffer,
Chris@1094 552 paintedLeft - x0, attainedWidth);
Chris@1121 553
Chris@1121 554 for (int i = 0; in_range_for(m_magRanges, i); ++i) {
Chris@1121 555 m_magCache.sampleColumn(i, m_magRanges.at(i));
Chris@1121 556 }
Chris@1094 557 }
Chris@1084 558
Chris@1094 559 void
Chris@1113 560 Colour3DPlotRenderer::renderToCacheBinResolution(const LayerGeometryProvider *v,
Chris@1094 561 int x0, int repaintWidth)
Chris@1094 562 {
Chris@1117 563 Profiler profiler("Colour3DPlotRenderer::renderToCacheBinResolution");
Chris@1094 564 cerr << "renderToCacheBinResolution" << endl;
Chris@1094 565
Chris@1094 566 // Draw to the draw buffer, and then scale-copy from there. Draw
Chris@1094 567 // buffer is at bin resolution, i.e. buffer x == source column
Chris@1094 568 // number. We use toolkit smooth scaling for interpolation.
Chris@1084 569
Chris@1100 570 const DenseThreeDimensionalModel *model = m_sources.source;
Chris@1094 571 if (!model || !model->isOK() || !model->isReady()) {
Chris@1094 572 throw std::logic_error("no source model provided, or model not ready");
Chris@1094 573 }
Chris@1094 574
Chris@1094 575 // The draw buffer will contain a fragment at bin resolution. We
Chris@1094 576 // need to ensure that it starts and ends at points where a
Chris@1094 577 // time-bin boundary occurs at an exact pixel boundary, and with a
Chris@1094 578 // certain amount of overlap across existing pixels so that we can
Chris@1094 579 // scale and draw from it without smoothing errors at the edges.
Chris@1094 580
Chris@1094 581 // If (getFrameForX(x) / increment) * increment ==
Chris@1094 582 // getFrameForX(x), then x is a time-bin boundary. We want two
Chris@1094 583 // such boundaries at either side of the draw buffer -- one which
Chris@1094 584 // we draw up to, and one which we subsequently crop at.
Chris@1094 585
Chris@1094 586 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
Chris@1094 587 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
Chris@1094 588
Chris@1094 589 int drawBufferWidth;
Chris@1094 590 int binResolution = model->getResolution();
Chris@1094 591
Chris@1094 592 for (int x = x0; ; --x) {
Chris@1094 593 sv_frame_t f = v->getFrameForX(x);
Chris@1094 594 if ((f / binResolution) * binResolution == f) {
Chris@1094 595 if (leftCropFrame == -1) leftCropFrame = f;
Chris@1094 596 else if (x < x0 - 2) {
Chris@1094 597 leftBoundaryFrame = f;
Chris@1094 598 break;
Chris@1094 599 }
Chris@1094 600 }
Chris@1094 601 }
Chris@1094 602 for (int x = x0 + repaintWidth; ; ++x) {
Chris@1094 603 sv_frame_t f = v->getFrameForX(x);
Chris@1094 604 if ((f / binResolution) * binResolution == f) {
Chris@1094 605 if (rightCropFrame == -1) rightCropFrame = f;
Chris@1094 606 else if (x > x0 + repaintWidth + 2) {
Chris@1094 607 rightBoundaryFrame = f;
Chris@1094 608 break;
Chris@1094 609 }
Chris@1094 610 }
Chris@1094 611 }
Chris@1094 612 drawBufferWidth = int
Chris@1094 613 ((rightBoundaryFrame - leftBoundaryFrame) / binResolution);
Chris@1094 614
Chris@1094 615 int h = v->getPaintHeight();
Chris@1094 616
Chris@1095 617 // For our purposes here, the draw buffer needs to be exactly our
Chris@1095 618 // target size (so we recreate always rather than just clear it)
Chris@1095 619
Chris@1095 620 recreateDrawBuffer(drawBufferWidth, h);
Chris@1094 621
Chris@1094 622 vector<int> binforx(drawBufferWidth);
Chris@1094 623 vector<double> binfory(h);
Chris@1094 624
Chris@1094 625 for (int x = 0; x < drawBufferWidth; ++x) {
Chris@1094 626 binforx[x] = int(leftBoundaryFrame / binResolution) + x;
Chris@1094 627 }
Chris@1094 628
Chris@1094 629 cerr << "[BIN] binResolution " << binResolution
Chris@1094 630 << endl;
Chris@1094 631
Chris@1094 632 for (int y = 0; y < h; ++y) {
Chris@1094 633 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
Chris@1094 634 }
Chris@1094 635
Chris@1094 636 int attainedWidth = renderDrawBuffer(drawBufferWidth,
Chris@1094 637 h,
Chris@1094 638 binforx,
Chris@1094 639 binfory,
Chris@1094 640 false,
Chris@1094 641 false,
Chris@1094 642 false);
Chris@1094 643
Chris@1094 644 if (attainedWidth == 0) return;
Chris@1094 645
Chris@1094 646 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
Chris@1094 647 int scaledRight = v->getXForFrame(rightBoundaryFrame);
Chris@1095 648
Chris@1095 649 cerr << "scaling draw buffer from width " << m_drawBuffer.width()
Chris@1095 650 << " to " << (scaledRight - scaledLeft) << " (nb drawBufferWidth = "
Chris@1095 651 << drawBufferWidth << ")" << endl;
Chris@1094 652
Chris@1094 653 QImage scaled = m_drawBuffer.scaled
Chris@1094 654 (scaledRight - scaledLeft, h,
Chris@1094 655 Qt::IgnoreAspectRatio, (m_params.interpolate ?
Chris@1094 656 Qt::SmoothTransformation :
Chris@1094 657 Qt::FastTransformation));
Chris@1084 658
Chris@1094 659 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
Chris@1094 660 int scaledRightCrop = v->getXForFrame(rightCropFrame);
Chris@1094 661
Chris@1094 662 int targetLeft = scaledLeftCrop;
Chris@1094 663 if (targetLeft < 0) {
Chris@1094 664 targetLeft = 0;
Chris@1094 665 }
Chris@1094 666
Chris@1094 667 int targetWidth = scaledRightCrop - targetLeft;
Chris@1094 668 if (targetLeft + targetWidth > m_cache.getSize().width()) {
Chris@1094 669 targetWidth = m_cache.getSize().width() - targetLeft;
Chris@1094 670 }
Chris@1094 671
Chris@1094 672 int sourceLeft = targetLeft - scaledLeft;
Chris@1094 673 if (sourceLeft < 0) {
Chris@1094 674 sourceLeft = 0;
Chris@1094 675 }
Chris@1094 676
Chris@1094 677 int sourceWidth = targetWidth;
Chris@1094 678
Chris@1094 679 cerr << "repaintWidth = " << repaintWidth
Chris@1094 680 << ", targetWidth = " << targetWidth << endl;
Chris@1094 681
Chris@1094 682 if (targetWidth > 0) {
Chris@1094 683 m_cache.drawImage(targetLeft, targetWidth,
Chris@1094 684 scaled,
Chris@1094 685 sourceLeft, sourceWidth);
Chris@1084 686 }
Chris@1121 687
Chris@1121 688 for (int i = 0; i < targetWidth; ++i) {
Chris@1121 689 int sourceIx = int((double(i) / targetWidth) * sourceWidth);
Chris@1121 690 if (in_range_for(m_magRanges, sourceIx)) {
Chris@1121 691 m_magCache.sampleColumn(i, m_magRanges.at(sourceIx));
Chris@1121 692 }
Chris@1121 693 }
Chris@1079 694 }
Chris@1083 695
Chris@1083 696 int
Chris@1083 697 Colour3DPlotRenderer::renderDrawBuffer(int w, int h,
Chris@1083 698 const vector<int> &binforx,
Chris@1083 699 const vector<double> &binfory,
Chris@1083 700 bool usePeaksCache,
Chris@1083 701 bool rightToLeft,
Chris@1083 702 bool timeConstrained)
Chris@1083 703 {
Chris@1083 704 // Callers must have checked that the appropriate subset of
Chris@1083 705 // Sources data members are set for the supplied flags (e.g. that
Chris@1083 706 // peaks model exists if usePeaksCache)
Chris@1083 707
Chris@1083 708 RenderTimer timer(timeConstrained ?
Chris@1083 709 RenderTimer::FastRender :
Chris@1083 710 RenderTimer::NoTimeout);
Chris@1083 711
Chris@1083 712 int minbin = int(binfory[0] + 0.0001);
Chris@1083 713 int maxbin = int(binfory[h-1]);
Chris@1083 714 if (minbin < 0) minbin = 0;
Chris@1083 715 if (maxbin < 0) maxbin = minbin+1;
Chris@1083 716
Chris@1083 717 int divisor = 1;
Chris@1100 718 const DenseThreeDimensionalModel *sourceModel = m_sources.source;
Chris@1083 719 if (usePeaksCache) {
Chris@1083 720 divisor = m_sources.peaks->getColumnsPerPeak();
Chris@1083 721 sourceModel = m_sources.peaks;
Chris@1083 722 }
Chris@1083 723
Chris@1083 724 int psx = -1;
Chris@1083 725
Chris@1083 726 int start = 0;
Chris@1083 727 int finish = w;
Chris@1083 728 int step = 1;
Chris@1083 729
Chris@1083 730 if (rightToLeft) {
Chris@1083 731 start = w-1;
Chris@1083 732 finish = -1;
Chris@1083 733 step = -1;
Chris@1083 734 }
Chris@1083 735
Chris@1083 736 int columnCount = 0;
Chris@1083 737
Chris@1083 738 vector<float> preparedColumn;
Chris@1094 739
Chris@1094 740 int modelWidth = sourceModel->getWidth();
Chris@1121 741
Chris@1121 742 cerr << "modelWidth " << modelWidth << ", divisor " << divisor << endl;
Chris@1121 743
Chris@1083 744 for (int x = start; x != finish; x += step) {
Chris@1083 745
Chris@1083 746 // x is the on-canvas pixel coord; sx (later) will be the
Chris@1083 747 // source column index
Chris@1083 748
Chris@1083 749 ++columnCount;
Chris@1120 750
Chris@1123 751 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1121 752 cerr << "x = " << x << ", binforx[x] = " << binforx[x] << endl;
Chris@1120 753 #endif
Chris@1083 754
Chris@1083 755 if (binforx[x] < 0) continue;
Chris@1083 756
Chris@1083 757 int sx0 = binforx[x] / divisor;
Chris@1083 758 int sx1 = sx0;
Chris@1083 759 if (x+1 < w) sx1 = binforx[x+1] / divisor;
Chris@1083 760 if (sx0 < 0) sx0 = sx1 - 1;
Chris@1083 761 if (sx0 < 0) continue;
Chris@1083 762 if (sx1 <= sx0) sx1 = sx0 + 1;
Chris@1083 763
Chris@1083 764 vector<float> pixelPeakColumn;
Chris@1121 765 MagnitudeRange magRange;
Chris@1083 766
Chris@1083 767 for (int sx = sx0; sx < sx1; ++sx) {
Chris@1083 768
Chris@1123 769 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1094 770 cerr << "sx = " << sx << endl;
Chris@1083 771 #endif
Chris@1083 772
Chris@1094 773 if (sx < 0 || sx >= modelWidth) {
Chris@1083 774 continue;
Chris@1083 775 }
Chris@1083 776
Chris@1083 777 if (sx != psx) {
Chris@1083 778
Chris@1083 779 // order:
Chris@1083 780 // get column -> scale -> record extents ->
Chris@1124 781 // normalise -> peak pick -> distribute/interpolate ->
Chris@1124 782 // apply display gain
Chris@1083 783
Chris@1083 784 ColumnOp::Column fullColumn = sourceModel->getColumn(sx);
Chris@1090 785
Chris@1094 786 // cerr << "x " << x << ", sx " << sx << ", col height " << fullColumn.size()
Chris@1094 787 // << ", minbin " << minbin << ", maxbin " << maxbin << endl;
Chris@1090 788
Chris@1083 789 ColumnOp::Column column =
Chris@1083 790 vector<float>(fullColumn.data() + minbin,
Chris@1083 791 fullColumn.data() + maxbin + 1);
Chris@1083 792
Chris@1125 793 column = ColumnOp::applyGain(column, m_params.scaleFactor);
Chris@1125 794
Chris@1105 795 //!!! fft scale if (m_colourScale != ColourScaleType::Phase) {
Chris@1083 796 // column = ColumnOp::fftScale(column, m_fftSize);
Chris@1083 797 // }
Chris@1083 798
Chris@1121 799 magRange.sample(column);
Chris@1083 800
Chris@1105 801 // if (m_colourScale != ColourScaleType::Phase) {
Chris@1083 802 column = ColumnOp::normalize(column, m_params.normalization);
Chris@1083 803 // }
Chris@1083 804
Chris@1103 805 if (m_params.binDisplay == BinDisplay::PeakBins) {
Chris@1083 806 column = ColumnOp::peakPick(column);
Chris@1083 807 }
Chris@1083 808
Chris@1083 809 preparedColumn =
Chris@1124 810 ColumnOp::distribute(column,
Chris@1083 811 h,
Chris@1083 812 binfory,
Chris@1083 813 minbin,
Chris@1083 814 m_params.interpolate);
Chris@1124 815
Chris@1124 816 // Display gain belongs to the colour scale and is
Chris@1124 817 // applied by the colour scale object when mapping it
Chris@1083 818
Chris@1083 819 psx = sx;
Chris@1083 820 }
Chris@1083 821
Chris@1083 822 if (sx == sx0) {
Chris@1083 823 pixelPeakColumn = preparedColumn;
Chris@1083 824 } else {
Chris@1083 825 for (int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
Chris@1083 826 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
Chris@1083 827 preparedColumn[i]);
Chris@1083 828 }
Chris@1083 829 }
Chris@1083 830 }
Chris@1083 831
Chris@1083 832 if (!pixelPeakColumn.empty()) {
Chris@1121 833
Chris@1083 834 for (int y = 0; y < h; ++y) {
Chris@1116 835 int py;
Chris@1116 836 if (m_params.invertVertical) {
Chris@1116 837 py = y;
Chris@1116 838 } else {
Chris@1116 839 py = h - y - 1;
Chris@1116 840 }
Chris@1083 841 m_drawBuffer.setPixel
Chris@1083 842 (x,
Chris@1116 843 py,
Chris@1083 844 m_params.colourScale.getPixel(pixelPeakColumn[y]));
Chris@1083 845 }
Chris@1121 846
Chris@1121 847 m_magRanges.push_back(magRange);
Chris@1083 848 }
Chris@1083 849
Chris@1083 850 double fractionComplete = double(columnCount) / double(w);
Chris@1083 851 if (timer.outOfTime(fractionComplete)) {
Chris@1121 852 cerr << "out of time" << endl;
Chris@1083 853 return columnCount;
Chris@1083 854 }
Chris@1083 855 }
Chris@1083 856
Chris@1083 857 return columnCount;
Chris@1083 858 }
Chris@1083 859
Chris@1097 860 int
Chris@1113 861 Colour3DPlotRenderer::renderDrawBufferPeakFrequencies(const LayerGeometryProvider *v,
Chris@1097 862 int w, int h,
Chris@1097 863 const vector<int> &binforx,
Chris@1097 864 const vector<double> &binfory,
Chris@1097 865 bool rightToLeft,
Chris@1097 866 bool timeConstrained)
Chris@1097 867 {
Chris@1097 868 // Callers must have checked that the appropriate subset of
Chris@1097 869 // Sources data members are set for the supplied flags (e.g. that
Chris@1097 870 // fft model exists)
Chris@1097 871
Chris@1097 872 RenderTimer timer(timeConstrained ?
Chris@1097 873 RenderTimer::FastRender :
Chris@1097 874 RenderTimer::NoTimeout);
Chris@1097 875
Chris@1097 876 int minbin = int(binfory[0] + 0.0001);
Chris@1097 877 int maxbin = int(binfory[h-1]);
Chris@1097 878 if (minbin < 0) minbin = 0;
Chris@1097 879 if (maxbin < 0) maxbin = minbin+1;
Chris@1097 880
Chris@1100 881 const FFTModel *fft = m_sources.fft;
Chris@1097 882
Chris@1097 883 FFTModel::PeakSet peakfreqs;
Chris@1097 884
Chris@1097 885 int psx = -1;
Chris@1097 886
Chris@1097 887 int start = 0;
Chris@1097 888 int finish = w;
Chris@1097 889 int step = 1;
Chris@1097 890
Chris@1097 891 if (rightToLeft) {
Chris@1097 892 start = w-1;
Chris@1097 893 finish = -1;
Chris@1097 894 step = -1;
Chris@1097 895 }
Chris@1097 896
Chris@1097 897 int columnCount = 0;
Chris@1097 898
Chris@1097 899 vector<float> preparedColumn;
Chris@1097 900
Chris@1097 901 int modelWidth = fft->getWidth();
Chris@1097 902 cerr << "modelWidth " << modelWidth << endl;
Chris@1097 903
Chris@1097 904 double minFreq = (double(minbin) * fft->getSampleRate()) / fft->getFFTSize();
Chris@1097 905 double maxFreq = (double(maxbin) * fft->getSampleRate()) / fft->getFFTSize();
Chris@1097 906
Chris@1103 907 bool logarithmic = (m_params.binScale == BinScale::Log);
Chris@1097 908
Chris@1097 909 for (int x = start; x != finish; x += step) {
Chris@1097 910
Chris@1097 911 // x is the on-canvas pixel coord; sx (later) will be the
Chris@1097 912 // source column index
Chris@1097 913
Chris@1097 914 ++columnCount;
Chris@1097 915
Chris@1097 916 if (binforx[x] < 0) continue;
Chris@1097 917
Chris@1097 918 int sx0 = binforx[x];
Chris@1097 919 int sx1 = sx0;
Chris@1097 920 if (x+1 < w) sx1 = binforx[x+1];
Chris@1097 921 if (sx0 < 0) sx0 = sx1 - 1;
Chris@1097 922 if (sx0 < 0) continue;
Chris@1097 923 if (sx1 <= sx0) sx1 = sx0 + 1;
Chris@1097 924
Chris@1097 925 vector<float> pixelPeakColumn;
Chris@1121 926 MagnitudeRange magRange;
Chris@1097 927
Chris@1097 928 for (int sx = sx0; sx < sx1; ++sx) {
Chris@1097 929
Chris@1097 930 if (sx < 0 || sx >= modelWidth) {
Chris@1097 931 continue;
Chris@1097 932 }
Chris@1097 933
Chris@1097 934 if (sx != psx) {
Chris@1097 935
Chris@1097 936 ColumnOp::Column fullColumn = fft->getColumn(sx);
Chris@1097 937
Chris@1097 938 ColumnOp::Column column =
Chris@1097 939 vector<float>(fullColumn.data() + minbin,
Chris@1097 940 fullColumn.data() + maxbin + 1);
Chris@1097 941
Chris@1125 942 column = ColumnOp::applyGain(column, m_params.scaleFactor);
Chris@1125 943
Chris@1121 944 magRange.sample(column);
Chris@1121 945
Chris@1105 946 //!!! fft scale if (m_colourScale != ColourScaleType::Phase) {
Chris@1097 947 // column = ColumnOp::fftScale(column, getFFTSize());
Chris@1097 948 // }
Chris@1097 949
Chris@1105 950 //!!! if (m_colourScale != ColourScaleType::Phase) {
Chris@1124 951 preparedColumn = ColumnOp::normalize
Chris@1124 952 (column, m_params.normalization);
Chris@1097 953 //!!! }
Chris@1097 954
Chris@1097 955 psx = sx;
Chris@1097 956 }
Chris@1097 957
Chris@1097 958 if (sx == sx0) {
Chris@1097 959 pixelPeakColumn = preparedColumn;
Chris@1097 960 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
Chris@1097 961 minbin, maxbin - 1);
Chris@1097 962 } else {
Chris@1097 963 for (int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
Chris@1097 964 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
Chris@1097 965 preparedColumn[i]);
Chris@1097 966 }
Chris@1097 967 }
Chris@1097 968 }
Chris@1097 969
Chris@1097 970 if (!pixelPeakColumn.empty()) {
Chris@1121 971
Chris@1097 972 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
Chris@1097 973 pi != peakfreqs.end(); ++pi) {
Chris@1097 974
Chris@1097 975 int bin = pi->first;
Chris@1097 976 double freq = pi->second;
Chris@1097 977
Chris@1097 978 if (bin < minbin) continue;
Chris@1097 979 if (bin > maxbin) break;
Chris@1097 980
Chris@1097 981 double value = pixelPeakColumn[bin - minbin];
Chris@1097 982
Chris@1097 983 double y = v->getYForFrequency
Chris@1097 984 (freq, minFreq, maxFreq, logarithmic);
Chris@1097 985
Chris@1097 986 int iy = int(y + 0.5);
Chris@1097 987 if (iy < 0 || iy >= h) continue;
Chris@1097 988
Chris@1097 989 m_drawBuffer.setPixel
Chris@1097 990 (x,
Chris@1097 991 iy,
Chris@1097 992 m_params.colourScale.getPixel(value));
Chris@1097 993 }
Chris@1121 994
Chris@1121 995 m_magRanges.push_back(magRange);
Chris@1097 996 }
Chris@1097 997
Chris@1097 998 double fractionComplete = double(columnCount) / double(w);
Chris@1097 999 if (timer.outOfTime(fractionComplete)) {
Chris@1097 1000 return columnCount;
Chris@1097 1001 }
Chris@1097 1002 }
Chris@1097 1003
Chris@1097 1004 return columnCount;
Chris@1097 1005 }
Chris@1097 1006
Chris@1079 1007 void
Chris@1095 1008 Colour3DPlotRenderer::recreateDrawBuffer(int w, int h)
Chris@1079 1009 {
Chris@1095 1010 m_drawBuffer = QImage(w, h, QImage::Format_Indexed8);
Chris@1079 1011
Chris@1095 1012 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@1095 1013 m_drawBuffer.setColor
Chris@1095 1014 ((unsigned char)pixel,
Chris@1112 1015 m_params.colourScale.getColourForPixel
Chris@1112 1016 (pixel, m_params.colourRotation).rgb());
Chris@1079 1017 }
Chris@1079 1018
Chris@1079 1019 m_drawBuffer.fill(0);
Chris@1121 1020 m_magRanges.clear();
Chris@1079 1021 }
Chris@1079 1022
Chris@1095 1023 void
Chris@1095 1024 Colour3DPlotRenderer::clearDrawBuffer(int w, int h)
Chris@1095 1025 {
Chris@1095 1026 if (m_drawBuffer.width() < w || m_drawBuffer.height() != h) {
Chris@1095 1027 recreateDrawBuffer(w, h);
Chris@1095 1028 } else {
Chris@1095 1029 m_drawBuffer.fill(0);
Chris@1121 1030 m_magRanges.clear();
Chris@1095 1031 }
Chris@1095 1032 }
Chris@1079 1033
Chris@1095 1034