annotate layer/Colour3DPlotRenderer.cpp @ 1135:628cd329c241 spectrogram-minor-refactor

Use a count of bins rather than min and max bins (because the name maxbin tells us nothing about whether the range is inclusive or not)
author Chris Cannam
date Wed, 03 Aug 2016 14:20:27 +0100
parents b4b155cfd8b4
children 9ff838a64461
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@1131 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@1133 333 int minbin = m_sources.verticalBinLayer->getIBinForY(v, h);
Chris@1135 334 if (minbin >= sh) minbin = sh - 1;
Chris@1135 335 if (minbin < 0) minbin = 0;
Chris@1135 336
Chris@1135 337 int nbins = m_sources.verticalBinLayer->getIBinForY(v, 0) - minbin + 1;
Chris@1135 338 if (minbin + nbins > sh) nbins = sh - minbin;
Chris@1133 339
Chris@1109 340 int psx = -1;
Chris@1109 341
Chris@1109 342 vector<float> preparedColumn;
Chris@1109 343
Chris@1109 344 int modelWidth = model->getWidth();
Chris@1109 345
Chris@1109 346 for (int sx = sx0; sx <= sx1; ++sx) {
Chris@1109 347
Chris@1109 348 if (sx < 0 || sx >= modelWidth) {
Chris@1109 349 continue;
Chris@1109 350 }
Chris@1109 351
Chris@1109 352 if (sx != psx) {
Chris@1109 353
Chris@1109 354 //!!! this is in common with renderDrawBuffer - pull it out
Chris@1109 355
Chris@1109 356 // order:
Chris@1109 357 // get column -> scale -> record extents ->
Chris@1124 358 // normalise -> peak pick -> apply display gain
Chris@1124 359
Chris@1109 360 ColumnOp::Column fullColumn = model->getColumn(sx);
Chris@1109 361
Chris@1109 362 ColumnOp::Column column =
Chris@1109 363 vector<float>(fullColumn.data() + minbin,
Chris@1135 364 fullColumn.data() + minbin + nbins);
Chris@1109 365
Chris@1125 366 column = ColumnOp::applyGain(column, m_params.scaleFactor);
Chris@1121 367
Chris@1109 368 // if (m_colourScale != ColourScaleType::Phase) {
Chris@1115 369 preparedColumn = ColumnOp::normalize(column, m_params.normalization);
Chris@1109 370 // }
Chris@1131 371
Chris@1131 372 magRange.sample(preparedColumn);
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@1135 398 for (int sy = minbin; sy < minbin + nbins; ++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 divisor = 1;
Chris@1100 713 const DenseThreeDimensionalModel *sourceModel = m_sources.source;
Chris@1083 714 if (usePeaksCache) {
Chris@1083 715 divisor = m_sources.peaks->getColumnsPerPeak();
Chris@1083 716 sourceModel = m_sources.peaks;
Chris@1083 717 }
Chris@1083 718
Chris@1135 719 int sh = sourceModel->getHeight();
Chris@1135 720
Chris@1135 721 int minbin = int(binfory[0] + 0.0001);
Chris@1135 722 if (minbin >= sh) minbin = sh - 1;
Chris@1135 723 if (minbin < 0) minbin = 0;
Chris@1135 724
Chris@1135 725 int nbins = int(binfory[h-1]) - minbin + 1;
Chris@1135 726 if (minbin + nbins > sh) nbins = sh - minbin;
Chris@1135 727
Chris@1083 728 int psx = -1;
Chris@1083 729
Chris@1083 730 int start = 0;
Chris@1083 731 int finish = w;
Chris@1083 732 int step = 1;
Chris@1083 733
Chris@1083 734 if (rightToLeft) {
Chris@1083 735 start = w-1;
Chris@1083 736 finish = -1;
Chris@1083 737 step = -1;
Chris@1083 738 }
Chris@1083 739
Chris@1083 740 int columnCount = 0;
Chris@1083 741
Chris@1083 742 vector<float> preparedColumn;
Chris@1094 743
Chris@1094 744 int modelWidth = sourceModel->getWidth();
Chris@1121 745
Chris@1121 746 cerr << "modelWidth " << modelWidth << ", divisor " << divisor << endl;
Chris@1121 747
Chris@1083 748 for (int x = start; x != finish; x += step) {
Chris@1083 749
Chris@1083 750 // x is the on-canvas pixel coord; sx (later) will be the
Chris@1083 751 // source column index
Chris@1083 752
Chris@1083 753 ++columnCount;
Chris@1120 754
Chris@1123 755 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1121 756 cerr << "x = " << x << ", binforx[x] = " << binforx[x] << endl;
Chris@1120 757 #endif
Chris@1083 758
Chris@1083 759 if (binforx[x] < 0) continue;
Chris@1083 760
Chris@1083 761 int sx0 = binforx[x] / divisor;
Chris@1083 762 int sx1 = sx0;
Chris@1083 763 if (x+1 < w) sx1 = binforx[x+1] / divisor;
Chris@1083 764 if (sx0 < 0) sx0 = sx1 - 1;
Chris@1083 765 if (sx0 < 0) continue;
Chris@1083 766 if (sx1 <= sx0) sx1 = sx0 + 1;
Chris@1083 767
Chris@1083 768 vector<float> pixelPeakColumn;
Chris@1121 769 MagnitudeRange magRange;
Chris@1083 770
Chris@1083 771 for (int sx = sx0; sx < sx1; ++sx) {
Chris@1083 772
Chris@1123 773 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1094 774 cerr << "sx = " << sx << endl;
Chris@1083 775 #endif
Chris@1083 776
Chris@1094 777 if (sx < 0 || sx >= modelWidth) {
Chris@1083 778 continue;
Chris@1083 779 }
Chris@1083 780
Chris@1083 781 if (sx != psx) {
Chris@1083 782
Chris@1083 783 // order:
Chris@1083 784 // get column -> scale -> record extents ->
Chris@1124 785 // normalise -> peak pick -> distribute/interpolate ->
Chris@1124 786 // apply display gain
Chris@1083 787
Chris@1083 788 ColumnOp::Column fullColumn = sourceModel->getColumn(sx);
Chris@1090 789
Chris@1135 790 cerr << "x " << x << ", sx " << sx << ", col height " << fullColumn.size()
Chris@1135 791 << ", minbin " << minbin << ", nbins " << nbins << endl;
Chris@1090 792
Chris@1083 793 ColumnOp::Column column =
Chris@1083 794 vector<float>(fullColumn.data() + minbin,
Chris@1135 795 fullColumn.data() + minbin + nbins);
Chris@1083 796
Chris@1125 797 column = ColumnOp::applyGain(column, m_params.scaleFactor);
Chris@1125 798
Chris@1105 799 // if (m_colourScale != ColourScaleType::Phase) {
Chris@1083 800 column = ColumnOp::normalize(column, m_params.normalization);
Chris@1083 801 // }
Chris@1083 802
Chris@1131 803 magRange.sample(column);
Chris@1131 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@1135 876 const FFTModel *fft = m_sources.fft;
Chris@1135 877
Chris@1135 878 int sh = fft->getHeight();
Chris@1135 879
Chris@1097 880 int minbin = int(binfory[0] + 0.0001);
Chris@1135 881 if (minbin >= sh) minbin = sh - 1;
Chris@1097 882 if (minbin < 0) minbin = 0;
Chris@1097 883
Chris@1135 884 int nbins = int(binfory[h-1]) - minbin + 1;
Chris@1135 885 if (minbin + nbins > sh) nbins = sh - minbin;
Chris@1097 886
Chris@1097 887 FFTModel::PeakSet peakfreqs;
Chris@1097 888
Chris@1097 889 int psx = -1;
Chris@1097 890
Chris@1097 891 int start = 0;
Chris@1097 892 int finish = w;
Chris@1097 893 int step = 1;
Chris@1097 894
Chris@1097 895 if (rightToLeft) {
Chris@1097 896 start = w-1;
Chris@1097 897 finish = -1;
Chris@1097 898 step = -1;
Chris@1097 899 }
Chris@1097 900
Chris@1097 901 int columnCount = 0;
Chris@1097 902
Chris@1097 903 vector<float> preparedColumn;
Chris@1097 904
Chris@1097 905 int modelWidth = fft->getWidth();
Chris@1097 906 cerr << "modelWidth " << modelWidth << endl;
Chris@1097 907
Chris@1135 908 double minFreq =
Chris@1135 909 (double(minbin) * fft->getSampleRate()) / fft->getFFTSize();
Chris@1135 910 double maxFreq =
Chris@1135 911 (double(minbin + nbins - 1) * fft->getSampleRate()) / fft->getFFTSize();
Chris@1097 912
Chris@1103 913 bool logarithmic = (m_params.binScale == BinScale::Log);
Chris@1097 914
Chris@1097 915 for (int x = start; x != finish; x += step) {
Chris@1097 916
Chris@1097 917 // x is the on-canvas pixel coord; sx (later) will be the
Chris@1097 918 // source column index
Chris@1097 919
Chris@1097 920 ++columnCount;
Chris@1097 921
Chris@1097 922 if (binforx[x] < 0) continue;
Chris@1097 923
Chris@1097 924 int sx0 = binforx[x];
Chris@1097 925 int sx1 = sx0;
Chris@1097 926 if (x+1 < w) sx1 = binforx[x+1];
Chris@1097 927 if (sx0 < 0) sx0 = sx1 - 1;
Chris@1097 928 if (sx0 < 0) continue;
Chris@1097 929 if (sx1 <= sx0) sx1 = sx0 + 1;
Chris@1097 930
Chris@1097 931 vector<float> pixelPeakColumn;
Chris@1121 932 MagnitudeRange magRange;
Chris@1097 933
Chris@1097 934 for (int sx = sx0; sx < sx1; ++sx) {
Chris@1097 935
Chris@1097 936 if (sx < 0 || sx >= modelWidth) {
Chris@1097 937 continue;
Chris@1097 938 }
Chris@1097 939
Chris@1097 940 if (sx != psx) {
Chris@1097 941
Chris@1097 942 ColumnOp::Column fullColumn = fft->getColumn(sx);
Chris@1097 943
Chris@1097 944 ColumnOp::Column column =
Chris@1097 945 vector<float>(fullColumn.data() + minbin,
Chris@1135 946 fullColumn.data() + minbin + nbins + 1);
Chris@1097 947
Chris@1125 948 column = ColumnOp::applyGain(column, m_params.scaleFactor);
Chris@1121 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@1131 955 magRange.sample(preparedColumn);
Chris@1131 956
Chris@1097 957 psx = sx;
Chris@1097 958 }
Chris@1097 959
Chris@1097 960 if (sx == sx0) {
Chris@1097 961 pixelPeakColumn = preparedColumn;
Chris@1097 962 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
Chris@1135 963 minbin, minbin + nbins - 1);
Chris@1097 964 } else {
Chris@1097 965 for (int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
Chris@1097 966 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
Chris@1097 967 preparedColumn[i]);
Chris@1097 968 }
Chris@1097 969 }
Chris@1097 970 }
Chris@1097 971
Chris@1097 972 if (!pixelPeakColumn.empty()) {
Chris@1121 973
Chris@1097 974 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
Chris@1097 975 pi != peakfreqs.end(); ++pi) {
Chris@1097 976
Chris@1097 977 int bin = pi->first;
Chris@1097 978 double freq = pi->second;
Chris@1097 979
Chris@1097 980 if (bin < minbin) continue;
Chris@1135 981 if (bin >= minbin + nbins) break;
Chris@1097 982
Chris@1097 983 double value = pixelPeakColumn[bin - minbin];
Chris@1097 984
Chris@1097 985 double y = v->getYForFrequency
Chris@1097 986 (freq, minFreq, maxFreq, logarithmic);
Chris@1097 987
Chris@1097 988 int iy = int(y + 0.5);
Chris@1097 989 if (iy < 0 || iy >= h) continue;
Chris@1097 990
Chris@1097 991 m_drawBuffer.setPixel
Chris@1097 992 (x,
Chris@1097 993 iy,
Chris@1097 994 m_params.colourScale.getPixel(value));
Chris@1097 995 }
Chris@1121 996
Chris@1121 997 m_magRanges.push_back(magRange);
Chris@1097 998 }
Chris@1097 999
Chris@1097 1000 double fractionComplete = double(columnCount) / double(w);
Chris@1097 1001 if (timer.outOfTime(fractionComplete)) {
Chris@1097 1002 return columnCount;
Chris@1097 1003 }
Chris@1097 1004 }
Chris@1097 1005
Chris@1097 1006 return columnCount;
Chris@1097 1007 }
Chris@1097 1008
Chris@1079 1009 void
Chris@1095 1010 Colour3DPlotRenderer::recreateDrawBuffer(int w, int h)
Chris@1079 1011 {
Chris@1095 1012 m_drawBuffer = QImage(w, h, QImage::Format_Indexed8);
Chris@1079 1013
Chris@1095 1014 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@1095 1015 m_drawBuffer.setColor
Chris@1095 1016 ((unsigned char)pixel,
Chris@1112 1017 m_params.colourScale.getColourForPixel
Chris@1112 1018 (pixel, m_params.colourRotation).rgb());
Chris@1079 1019 }
Chris@1079 1020
Chris@1079 1021 m_drawBuffer.fill(0);
Chris@1121 1022 m_magRanges.clear();
Chris@1079 1023 }
Chris@1079 1024
Chris@1095 1025 void
Chris@1095 1026 Colour3DPlotRenderer::clearDrawBuffer(int w, int h)
Chris@1095 1027 {
Chris@1095 1028 if (m_drawBuffer.width() < w || m_drawBuffer.height() != h) {
Chris@1095 1029 recreateDrawBuffer(w, h);
Chris@1095 1030 } else {
Chris@1095 1031 m_drawBuffer.fill(0);
Chris@1121 1032 m_magRanges.clear();
Chris@1095 1033 }
Chris@1095 1034 }
Chris@1079 1035
Chris@1095 1036