annotate layer/Colour3DPlotRenderer.cpp @ 1160:a429b2acb45d 3.0-integration

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