annotate layer/Colour3DPlotRenderer.cpp @ 1548:bd6af89982d7

Permit getScaleProvidingLayerForUnit to return a dormant layer if there is no visible alternative. This is necessary to avoid the scale disappearing in Tony when the spectrogram is toggled off.
author Chris Cannam
date Thu, 17 Oct 2019 14:44:22 +0100
parents bfd8b22fd67c
children e95cefd4aa81
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@1214 20 #include "base/HitCount.h"
Chris@1117 21
Chris@1075 22 #include "data/model/DenseThreeDimensionalModel.h"
Chris@1075 23 #include "data/model/Dense3DModelPeakCache.h"
Chris@1075 24 #include "data/model/FFTModel.h"
Chris@1075 25
Chris@1077 26 #include "LayerGeometryProvider.h"
Chris@1082 27 #include "VerticalBinLayer.h"
Chris@1109 28 #include "PaintAssistant.h"
Chris@1139 29 #include "ImageRegionFinder.h"
Chris@1109 30
Chris@1109 31 #include "view/ViewManager.h" // for main model sample rate. Pity
Chris@1075 32
Chris@1079 33 #include <vector>
Chris@1079 34
Chris@1325 35 #include <utility>
Chris@1325 36 using namespace std::rel_ops;
Chris@1325 37
Chris@1362 38 //#define DEBUG_COLOUR_PLOT_REPAINT 1
Chris@1502 39 //#define DEBUG_COLOUR_PLOT_CACHE_SELECTION 1
Chris@1094 40
Chris@1079 41 using namespace std;
Chris@1079 42
Chris@1073 43 Colour3DPlotRenderer::RenderResult
Chris@1113 44 Colour3DPlotRenderer::render(const LayerGeometryProvider *v, QPainter &paint, QRect rect)
Chris@1076 45 {
Chris@1090 46 return render(v, paint, rect, false);
Chris@1076 47 }
Chris@1076 48
Chris@1076 49 Colour3DPlotRenderer::RenderResult
Chris@1113 50 Colour3DPlotRenderer::renderTimeConstrained(const LayerGeometryProvider *v,
Chris@1090 51 QPainter &paint, QRect rect)
Chris@1076 52 {
Chris@1090 53 return render(v, paint, rect, true);
Chris@1076 54 }
Chris@1076 55
Chris@1096 56 QRect
Chris@1121 57 Colour3DPlotRenderer::getLargestUncachedRect(const LayerGeometryProvider *v)
Chris@1096 58 {
Chris@1121 59 RenderType renderType = decideRenderType(v);
Chris@1121 60
Chris@1121 61 if (renderType == DirectTranslucent) {
Chris@1121 62 return QRect(); // never cached
Chris@1121 63 }
Chris@1121 64
Chris@1096 65 int h = m_cache.getSize().height();
Chris@1096 66
Chris@1096 67 QRect areaLeft(0, 0, m_cache.getValidLeft(), h);
Chris@1096 68 QRect areaRight(m_cache.getValidRight(), 0,
Chris@1096 69 m_cache.getSize().width() - m_cache.getValidRight(), h);
Chris@1096 70
Chris@1096 71 if (areaRight.width() > areaLeft.width()) {
Chris@1096 72 return areaRight;
Chris@1096 73 } else {
Chris@1096 74 return areaLeft;
Chris@1096 75 }
Chris@1096 76 }
Chris@1096 77
Chris@1122 78 bool
Chris@1122 79 Colour3DPlotRenderer::geometryChanged(const LayerGeometryProvider *v)
Chris@1122 80 {
Chris@1122 81 RenderType renderType = decideRenderType(v);
Chris@1122 82
Chris@1122 83 if (renderType == DirectTranslucent) {
Chris@1122 84 return true; // never cached
Chris@1122 85 }
Chris@1122 86
Chris@1122 87 if (m_cache.getSize() == v->getPaintSize() &&
Chris@1122 88 m_cache.getZoomLevel() == v->getZoomLevel() &&
Chris@1122 89 m_cache.getStartFrame() == v->getStartFrame()) {
Chris@1122 90 return false;
Chris@1122 91 } else {
Chris@1122 92 return true;
Chris@1122 93 }
Chris@1122 94 }
Chris@1122 95
Chris@1076 96 Colour3DPlotRenderer::RenderResult
Chris@1113 97 Colour3DPlotRenderer::render(const LayerGeometryProvider *v,
Chris@1090 98 QPainter &paint, QRect rect, bool timeConstrained)
Chris@1073 99 {
Chris@1109 100 RenderType renderType = decideRenderType(v);
Chris@1109 101
Chris@1221 102 if (timeConstrained) {
Chris@1221 103 if (renderType != DrawBufferPixelResolution) {
Chris@1221 104 // Rendering should be fast in bin-resolution and direct
Chris@1221 105 // draw cases because we are quite well zoomed-in, and the
Chris@1221 106 // sums are easier this way. Calculating boundaries later
Chris@1221 107 // will be fiddly for partial paints otherwise.
Chris@1221 108 timeConstrained = false;
Chris@1221 109
Chris@1221 110 } else if (m_secondsPerXPixelValid) {
Chris@1221 111 double predicted = m_secondsPerXPixel * rect.width();
Chris@1236 112 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1499 113 SVDEBUG << "render " << m_sources.source
Chris@1499 114 << ": Predicted time for width " << rect.width() << " = "
Chris@1236 115 << predicted << " (" << m_secondsPerXPixel << " x "
Chris@1236 116 << rect.width() << ")" << endl;
Chris@1221 117 #endif
Chris@1450 118 if (predicted < 0.175) {
Chris@1221 119 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1499 120 SVDEBUG << "render " << m_sources.source
Chris@1499 121 << ": Predicted time looks fast enough: no partial renders"
Chris@1221 122 << endl;
Chris@1221 123 #endif
Chris@1221 124 timeConstrained = false;
Chris@1221 125 }
Chris@1221 126 }
Chris@1109 127 }
Chris@1221 128
Chris@1079 129 int x0 = v->getXForViewX(rect.x());
Chris@1079 130 int x1 = v->getXForViewX(rect.x() + rect.width());
Chris@1079 131 if (x0 < 0) x0 = 0;
Chris@1079 132 if (x1 > v->getPaintWidth()) x1 = v->getPaintWidth();
Chris@1079 133
Chris@1120 134 sv_frame_t startFrame = v->getStartFrame();
Chris@1451 135
Chris@1534 136 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1534 137 SVDEBUG << "render " << m_sources.source
Chris@1534 138 << ": cache size is " << m_cache.getSize().width()
Chris@1534 139 << "x" << m_cache.getSize().height()
Chris@1534 140 << " at zoom level " << m_cache.getZoomLevel() << endl;
Chris@1534 141 #endif
Chris@1534 142
Chris@1534 143 bool justCreated = m_cache.getSize().isEmpty();
Chris@1534 144
Chris@1451 145 bool justInvalidated =
Chris@1451 146 (m_cache.getSize() != v->getPaintSize() ||
Chris@1451 147 m_cache.getZoomLevel() != v->getZoomLevel());
Chris@1534 148
Chris@1534 149 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1534 150 SVDEBUG << "render " << m_sources.source
Chris@1534 151 << ": justCreated = " << justCreated
Chris@1534 152 << ", justInvalidated = " << justInvalidated
Chris@1534 153 << endl;
Chris@1534 154 #endif
Chris@1120 155
Chris@1079 156 m_cache.resize(v->getPaintSize());
Chris@1079 157 m_cache.setZoomLevel(v->getZoomLevel());
Chris@1079 158
Chris@1119 159 m_magCache.resize(v->getPaintSize().width());
Chris@1119 160 m_magCache.setZoomLevel(v->getZoomLevel());
Chris@1119 161
Chris@1120 162 if (renderType == DirectTranslucent) {
Chris@1121 163 MagnitudeRange range = renderDirectTranslucent(v, paint, rect);
Chris@1121 164 return { rect, range };
Chris@1120 165 }
Chris@1120 166
Chris@1123 167 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1499 168 SVDEBUG << "render " << m_sources.source
Chris@1499 169 << ": cache start " << m_cache.getStartFrame()
Chris@1499 170 << " valid left " << m_cache.getValidLeft()
Chris@1499 171 << " valid right " << m_cache.getValidRight()
Chris@1499 172 << endl;
Chris@1500 173 SVDEBUG << "render " << m_sources.source
Chris@1500 174 << ": view start " << startFrame
Chris@1499 175 << " x0 " << x0
Chris@1499 176 << " x1 " << x1
Chris@1499 177 << endl;
Chris@1123 178 #endif
Chris@1214 179
Chris@1214 180 static HitCount count("Colour3DPlotRenderer: image cache");
Chris@1451 181
Chris@1079 182 if (m_cache.isValid()) { // some part of the cache is valid
Chris@1079 183
Chris@1079 184 if (v->getXForFrame(m_cache.getStartFrame()) ==
Chris@1079 185 v->getXForFrame(startFrame) &&
Chris@1079 186 m_cache.getValidLeft() <= x0 &&
Chris@1079 187 m_cache.getValidRight() >= x1) {
Chris@1090 188
Chris@1123 189 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 190 SVDEBUG << "render " << m_sources.source
Chris@1500 191 << ": cache hit" << endl;
Chris@1123 192 #endif
Chris@1214 193 count.hit();
Chris@1090 194
Chris@1079 195 // cache is valid for the complete requested area
Chris@1079 196 paint.drawImage(rect, m_cache.getImage(), rect);
Chris@1119 197
Chris@1122 198 MagnitudeRange range = m_magCache.getRange(x0, x1 - x0);
Chris@1119 199
Chris@1119 200 return { rect, range };
Chris@1079 201
Chris@1079 202 } else {
Chris@1123 203 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 204 SVDEBUG << "render " << m_sources.source
Chris@1500 205 << ": cache partial hit" << endl;
Chris@1123 206 #endif
Chris@1214 207 count.partial();
Chris@1090 208
Chris@1079 209 // cache doesn't begin at the right frame or doesn't
Chris@1079 210 // contain the complete view, but might be scrollable or
Chris@1079 211 // partially usable
Chris@1090 212 m_cache.scrollTo(v, startFrame);
Chris@1119 213 m_magCache.scrollTo(v, startFrame);
Chris@1079 214
Chris@1079 215 // if we are not time-constrained, then we want to paint
Chris@1081 216 // the whole area in one go; we don't return a partial
Chris@1081 217 // paint. To avoid providing the more complex logic to
Chris@1081 218 // handle painting discontiguous areas, if the only valid
Chris@1079 219 // part of cache is in the middle, just make the whole
Chris@1079 220 // thing invalid and start again.
Chris@1079 221 if (!timeConstrained) {
Chris@1079 222 if (m_cache.getValidLeft() > x0 &&
Chris@1079 223 m_cache.getValidRight() < x1) {
Chris@1079 224 m_cache.invalidate();
Chris@1079 225 }
Chris@1079 226 }
Chris@1079 227 }
Chris@1090 228 } else {
Chris@1118 229 // cache is completely invalid
Chris@1214 230 count.miss();
Chris@1090 231 m_cache.setStartFrame(startFrame);
Chris@1119 232 m_magCache.setStartFrame(startFrame);
Chris@1075 233 }
Chris@1075 234
Chris@1079 235 bool rightToLeft = false;
Chris@1079 236
Chris@1122 237 int reqx0 = x0;
Chris@1122 238 int reqx1 = x1;
Chris@1122 239
Chris@1079 240 if (!m_cache.isValid() && timeConstrained) {
Chris@1079 241 if (x0 == 0 && x1 == v->getPaintWidth()) {
Chris@1237 242
Chris@1237 243 // When rendering the whole area, in a context where we
Chris@1237 244 // might not be able to complete the work, start from
Chris@1237 245 // somewhere near the middle so that the region of
Chris@1237 246 // interest appears first.
Chris@1237 247 //
Chris@1237 248 // This is very useful if we actually are slow to render,
Chris@1237 249 // but if we're not sure how fast we'll be, we should
Chris@1237 250 // prefer not to because it can be distracting to render
Chris@1237 251 // fast from the middle and then jump back to fill in the
Chris@1237 252 // start. That is:
Chris@1237 253 //
Chris@1237 254 // - if our seconds-per-x-pixel count is invalid, then we
Chris@1237 255 // don't do this: we've probably only just been created
Chris@1237 256 // and don't know how fast we'll be yet (this happens
Chris@1237 257 // often while zooming rapidly in and out). The exception
Chris@1237 258 // to the exception is if we're displaying peak
Chris@1237 259 // frequencies; this we can assume to be slow. (Note that
Chris@1237 260 // if the seconds-per-x-pixel is valid and we know we're
Chris@1237 261 // fast, then we've already set timeConstrained false
Chris@1237 262 // above so this doesn't apply)
Chris@1237 263 //
Chris@1237 264 // - if we're using a peak cache, we don't do this;
Chris@1237 265 // drawing from peak cache is often (even if not always)
Chris@1237 266 // fast.
Chris@1237 267
Chris@1237 268 bool drawFromTheMiddle = true;
Chris@1237 269
Chris@1237 270 if (!m_secondsPerXPixelValid &&
Chris@1237 271 (m_params.binDisplay != BinDisplay::PeakFrequencies)) {
Chris@1237 272 drawFromTheMiddle = false;
Chris@1237 273 } else {
Chris@1237 274 int peakCacheIndex = -1, binsPerPeak = -1;
Chris@1237 275 getPreferredPeakCache(v, peakCacheIndex, binsPerPeak);
Chris@1237 276 if (peakCacheIndex >= 0) { // have a peak cache
Chris@1237 277 drawFromTheMiddle = false;
Chris@1237 278 }
Chris@1237 279 }
Chris@1237 280
Chris@1237 281 if (drawFromTheMiddle) {
Chris@1222 282 double offset = 0.5 * (double(rand()) / double(RAND_MAX));
Chris@1222 283 x0 = int(x1 * offset);
Chris@1213 284 }
Chris@1079 285 }
Chris@1079 286 }
Chris@1079 287
Chris@1079 288 if (m_cache.isValid()) {
Chris@1090 289
Chris@1079 290 // When rendering only a part of the cache, we need to make
Chris@1079 291 // sure that the part we're rendering is adjacent to (or
Chris@1079 292 // overlapping) a valid area of cache, if we have one. The
Chris@1079 293 // alternative is to ditch the valid area of cache and render
Chris@1079 294 // only the requested area, but that's risky because this can
Chris@1079 295 // happen when just waving the pointer over a small part of
Chris@1079 296 // the view -- if we lose the partly-built cache every time
Chris@1079 297 // the user does that, we'll never finish building it.
Chris@1079 298 int left = x0;
Chris@1079 299 int width = x1 - x0;
Chris@1079 300 bool isLeftOfValidArea = false;
Chris@1079 301 m_cache.adjustToTouchValidArea(left, width, isLeftOfValidArea);
Chris@1079 302 x0 = left;
Chris@1079 303 x1 = x0 + width;
Chris@1079 304
Chris@1079 305 // That call also told us whether we should be painting
Chris@1079 306 // sub-regions of our target region in right-to-left order in
Chris@1079 307 // order to ensure contiguity
Chris@1079 308 rightToLeft = isLeftOfValidArea;
Chris@1079 309 }
Chris@1075 310
Chris@1109 311 // Note, we always paint the full height to cache. We want to
Chris@1109 312 // ensure the cache is coherent without having to worry about
Chris@1109 313 // vertical matching of required and valid areas as well as
Chris@1109 314 // horizontal.
Chris@1094 315
Chris@1109 316 if (renderType == DrawBufferBinResolution) {
Chris@1109 317
Chris@1094 318 renderToCacheBinResolution(v, x0, x1 - x0);
Chris@1109 319
Chris@1109 320 } else { // must be DrawBufferPixelResolution, handled DirectTranslucent earlier
Chris@1109 321
Chris@1534 322 if (timeConstrained && !justCreated && justInvalidated) {
Chris@1500 323 SVDEBUG << "render " << m_sources.source
Chris@1500 324 << ": invalidated cache in time-constrained context, that's all we're doing for now - wait for next update to start filling" << endl;
Chris@1451 325 } else {
Chris@1451 326 renderToCachePixelResolution(v, x0, x1 - x0, rightToLeft, timeConstrained);
Chris@1451 327 }
Chris@1094 328 }
Chris@1079 329
Chris@1079 330 QRect pr = rect & m_cache.getValidArea();
Chris@1079 331 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(),
Chris@1079 332 pr.x(), pr.y(), pr.width(), pr.height());
Chris@1079 333
Chris@1079 334 if (!timeConstrained && (pr != rect)) {
Chris@1494 335 QRect cva = m_cache.getValidArea();
Chris@1265 336 SVCERR << "WARNING: failed to render entire requested rect "
Chris@1451 337 << "even when not time-constrained: wanted "
Chris@1450 338 << rect.x() << "," << rect.y() << " "
Chris@1451 339 << rect.width() << "x" << rect.height() << ", got "
Chris@1450 340 << pr.x() << "," << pr.y() << " "
Chris@1450 341 << pr.width() << "x" << pr.height()
Chris@1451 342 << ", after request of width " << (x1 - x0)
Chris@1494 343 << endl
Chris@1494 344 << "(cache valid area is "
Chris@1494 345 << cva.x() << "," << cva.y() << " "
Chris@1494 346 << cva.width() << "x" << cva.height() << ")"
Chris@1450 347 << endl;
Chris@1079 348 }
Chris@1120 349
Chris@1122 350 MagnitudeRange range = m_magCache.getRange(reqx0, reqx1 - reqx0);
Chris@1412 351
Chris@1412 352 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1499 353 SVDEBUG << "render " << m_sources.source
Chris@1499 354 << ": returning rect rendered as " << pr.x() << "," << pr.y()
Chris@1412 355 << " " << pr.width() << "x" << pr.height() << endl;
Chris@1499 356 SVDEBUG << "render " << m_sources.source
Chris@1499 357 << ": mag range from cache in x-range " << reqx0
Chris@1412 358 << " to " << reqx1 << " is " << range.getMin() << " -> "
Chris@1412 359 << range.getMax() << endl;
Chris@1412 360 #endif
Chris@1120 361
Chris@1120 362 return { pr, range };
Chris@1073 363 }
Chris@1073 364
Chris@1109 365 Colour3DPlotRenderer::RenderType
Chris@1113 366 Colour3DPlotRenderer::decideRenderType(const LayerGeometryProvider *v) const
Chris@1094 367 {
Chris@1469 368 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
Chris@1109 369 if (!model || !v || !(v->getViewManager())) {
Chris@1109 370 return DrawBufferPixelResolution; // or anything
Chris@1109 371 }
Chris@1109 372
Chris@1094 373 int binResolution = model->getResolution();
Chris@1325 374 ZoomLevel zoomLevel = v->getZoomLevel();
Chris@1109 375 sv_samplerate_t modelRate = model->getSampleRate();
Chris@1109 376
Chris@1109 377 double rateRatio = v->getViewManager()->getMainModelSampleRate() / modelRate;
Chris@1109 378 double relativeBinResolution = binResolution * rateRatio;
Chris@1109 379
Chris@1109 380 if (m_params.binDisplay == BinDisplay::PeakFrequencies) {
Chris@1109 381 // no alternative works here
Chris@1109 382 return DrawBufferPixelResolution;
Chris@1109 383 }
Chris@1109 384
Chris@1109 385 if (!m_params.alwaysOpaque && !m_params.interpolate) {
Chris@1109 386
Chris@1109 387 // consider translucent option -- only if not smoothing & not
Chris@1109 388 // explicitly requested opaque & sufficiently zoomed-in
Chris@1109 389
Chris@1117 390 if (model->getHeight() * 3 < v->getPaintHeight() &&
Chris@1325 391 zoomLevel < ZoomLevel(ZoomLevel::FramesPerPixel,
Chris@1325 392 int(round(relativeBinResolution / 3)))) {
Chris@1109 393 return DirectTranslucent;
Chris@1109 394 }
Chris@1109 395 }
Chris@1109 396
Chris@1325 397 if (ZoomLevel(ZoomLevel::FramesPerPixel,
Chris@1325 398 int(round(relativeBinResolution))) > zoomLevel) {
Chris@1109 399 return DrawBufferBinResolution;
Chris@1109 400 } else {
Chris@1109 401 return DrawBufferPixelResolution;
Chris@1109 402 }
Chris@1109 403 }
Chris@1109 404
Chris@1138 405 ColumnOp::Column
Chris@1161 406 Colour3DPlotRenderer::getColumn(int sx, int minbin, int nbins,
Chris@1502 407 shared_ptr<DenseThreeDimensionalModel> source) const
Chris@1138 408 {
Chris@1138 409 // order:
Chris@1138 410 // get column -> scale -> normalise -> record extents ->
Chris@1138 411 // peak pick -> distribute/interpolate -> apply display gain
Chris@1138 412
Chris@1138 413 // we do the first bit here:
Chris@1138 414 // get column -> scale -> normalise
Chris@1138 415
Chris@1138 416 ColumnOp::Column column;
Chris@1364 417
Chris@1364 418 if (m_params.showDerivative && sx > 0) {
Chris@1364 419
Chris@1502 420 auto prev = getColumnRaw(sx - 1, minbin, nbins, source);
Chris@1502 421 column = getColumnRaw(sx, minbin, nbins, source);
Chris@1364 422
Chris@1364 423 for (int i = 0; i < nbins; ++i) {
Chris@1364 424 column[i] -= prev[i];
Chris@1364 425 }
Chris@1364 426
Chris@1364 427 } else {
Chris@1502 428 column = getColumnRaw(sx, minbin, nbins, source);
Chris@1364 429 }
Chris@1364 430
Chris@1364 431 if (m_params.colourScale.getScale() == ColourScaleType::Phase &&
Chris@1469 432 !m_sources.fft.isNone()) {
Chris@1364 433 return column;
Chris@1364 434 } else {
Chris@1364 435 column = ColumnOp::applyGain(column, m_params.scaleFactor);
Chris@1364 436 column = ColumnOp::normalize(column, m_params.normalization);
Chris@1364 437 return column;
Chris@1364 438 }
Chris@1364 439 }
Chris@1364 440
Chris@1364 441 ColumnOp::Column
Chris@1364 442 Colour3DPlotRenderer::getColumnRaw(int sx, int minbin, int nbins,
Chris@1502 443 shared_ptr<DenseThreeDimensionalModel> source) const
Chris@1364 444 {
Chris@1364 445 Profiler profiler("Colour3DPlotRenderer::getColumn");
Chris@1364 446
Chris@1364 447 ColumnOp::Column column;
Chris@1469 448 ColumnOp::Column fullColumn;
Chris@1364 449
Chris@1469 450 if (m_params.colourScale.getScale() == ColourScaleType::Phase) {
Chris@1469 451 auto fftModel = ModelById::getAs<FFTModel>(m_sources.fft);
Chris@1469 452 if (fftModel) {
Chris@1469 453 fullColumn = fftModel->getPhases(sx);
Chris@1469 454 }
Chris@1138 455 }
Chris@1138 456
Chris@1469 457 if (fullColumn.empty()) {
Chris@1502 458 fullColumn = source->getColumn(sx);
Chris@1469 459 }
Chris@1502 460
Chris@1469 461 column = vector<float>(fullColumn.data() + minbin,
Chris@1469 462 fullColumn.data() + minbin + nbins);
Chris@1138 463 return column;
Chris@1138 464 }
Chris@1138 465
Chris@1121 466 MagnitudeRange
Chris@1113 467 Colour3DPlotRenderer::renderDirectTranslucent(const LayerGeometryProvider *v,
Chris@1109 468 QPainter &paint,
Chris@1109 469 QRect rect)
Chris@1109 470 {
Chris@1117 471 Profiler profiler("Colour3DPlotRenderer::renderDirectTranslucent");
Chris@1117 472
Chris@1121 473 MagnitudeRange magRange;
Chris@1121 474
Chris@1115 475 QPoint illuminatePos;
Chris@1115 476 bool illuminate = v->shouldIlluminateLocalFeatures
Chris@1115 477 (m_sources.verticalBinLayer, illuminatePos);
Chris@1109 478
Chris@1469 479 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
Chris@1469 480 if (!model) return magRange;
Chris@1109 481
Chris@1109 482 int x0 = rect.left();
Chris@1109 483 int x1 = rect.right() + 1;
Chris@1109 484
Chris@1109 485 int h = v->getPaintHeight();
Chris@1109 486
Chris@1109 487 sv_frame_t modelStart = model->getStartFrame();
Chris@1109 488 sv_frame_t modelEnd = model->getEndFrame();
Chris@1109 489 int modelResolution = model->getResolution();
Chris@1109 490
Chris@1109 491 double rateRatio =
Chris@1109 492 v->getViewManager()->getMainModelSampleRate() / model->getSampleRate();
Chris@1109 493
Chris@1109 494 // the s-prefix values are source, i.e. model, column and bin numbers
Chris@1109 495 int sx0 = int((double(v->getFrameForX(x0)) / rateRatio - double(modelStart))
Chris@1109 496 / modelResolution);
Chris@1109 497 int sx1 = int((double(v->getFrameForX(x1)) / rateRatio - double(modelStart))
Chris@1109 498 / modelResolution);
Chris@1109 499
Chris@1109 500 int sh = model->getHeight();
Chris@1109 501
Chris@1109 502 const int buflen = 40;
Chris@1109 503 char labelbuf[buflen];
Chris@1109 504
Chris@1133 505 int minbin = m_sources.verticalBinLayer->getIBinForY(v, h);
Chris@1135 506 if (minbin >= sh) minbin = sh - 1;
Chris@1135 507 if (minbin < 0) minbin = 0;
Chris@1135 508
Chris@1135 509 int nbins = m_sources.verticalBinLayer->getIBinForY(v, 0) - minbin + 1;
Chris@1135 510 if (minbin + nbins > sh) nbins = sh - minbin;
Chris@1133 511
Chris@1109 512 int psx = -1;
Chris@1109 513
Chris@1109 514 vector<float> preparedColumn;
Chris@1109 515
Chris@1109 516 int modelWidth = model->getWidth();
Chris@1109 517
Chris@1109 518 for (int sx = sx0; sx <= sx1; ++sx) {
Chris@1109 519
Chris@1109 520 if (sx < 0 || sx >= modelWidth) {
Chris@1109 521 continue;
Chris@1109 522 }
Chris@1109 523
Chris@1109 524 if (sx != psx) {
Chris@1109 525
Chris@1138 526 // order:
Chris@1138 527 // get column -> scale -> normalise -> record extents ->
Chris@1138 528 // peak pick -> distribute/interpolate -> apply display gain
Chris@1109 529
Chris@1138 530 // this does the first three:
Chris@1502 531 preparedColumn = getColumn(sx, minbin, nbins, model);
Chris@1131 532
Chris@1131 533 magRange.sample(preparedColumn);
Chris@1109 534
Chris@1109 535 if (m_params.binDisplay == BinDisplay::PeakBins) {
Chris@1115 536 preparedColumn = ColumnOp::peakPick(preparedColumn);
Chris@1109 537 }
Chris@1109 538
Chris@1124 539 // Display gain belongs to the colour scale and is
Chris@1124 540 // applied by the colour scale object when mapping it
Chris@1124 541
Chris@1109 542 psx = sx;
Chris@1109 543 }
Chris@1109 544
Chris@1266 545 sv_frame_t fx = sx * modelResolution + modelStart;
Chris@1109 546
Chris@1266 547 if (fx + modelResolution <= modelStart || fx > modelEnd) continue;
Chris@1109 548
Chris@1109 549 int rx0 = v->getXForFrame(int(double(fx) * rateRatio));
Chris@1266 550 int rx1 = v->getXForFrame(int(double(fx + modelResolution + 1) * rateRatio));
Chris@1109 551
Chris@1266 552 int rw = rx1 - rx0;
Chris@1266 553 if (rw < 1) rw = 1;
Chris@1109 554
Chris@1471 555 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1471 556 // replacement (horizontalAdvance) was only added in Qt 5.11
Chris@1471 557 // which is too new for us
Chris@1471 558 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1471 559
Chris@1266 560 bool showLabel = (rw > 10 &&
Chris@1266 561 paint.fontMetrics().width("0.000000") < rw - 3 &&
Chris@1266 562 paint.fontMetrics().height() < (h / sh));
Chris@1109 563
Chris@1266 564 for (int sy = minbin; sy < minbin + nbins; ++sy) {
Chris@1109 565
Chris@1109 566 int ry0 = m_sources.verticalBinLayer->getIYForBin(v, sy);
Chris@1109 567 int ry1 = m_sources.verticalBinLayer->getIYForBin(v, sy + 1);
Chris@1116 568
Chris@1116 569 if (m_params.invertVertical) {
Chris@1116 570 ry0 = h - ry0 - 1;
Chris@1116 571 ry1 = h - ry1 - 1;
Chris@1116 572 }
Chris@1116 573
Chris@1109 574 QRect r(rx0, ry1, rw, ry0 - ry1);
Chris@1109 575
Chris@1109 576 float value = preparedColumn[sy - minbin];
Chris@1112 577 QColor colour = m_params.colourScale.getColour(value,
Chris@1112 578 m_params.colourRotation);
Chris@1109 579
Chris@1109 580 if (rw == 1) {
Chris@1109 581 paint.setPen(colour);
Chris@1109 582 paint.setBrush(Qt::NoBrush);
Chris@1109 583 paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1);
Chris@1109 584 continue;
Chris@1109 585 }
Chris@1109 586
Chris@1266 587 QColor pen(255, 255, 255, 80);
Chris@1266 588 QColor brush(colour);
Chris@1109 589
Chris@1109 590 if (rw > 3 && r.height() > 3) {
Chris@1109 591 brush.setAlpha(160);
Chris@1109 592 }
Chris@1109 593
Chris@1266 594 paint.setPen(Qt::NoPen);
Chris@1266 595 paint.setBrush(brush);
Chris@1109 596
Chris@1266 597 if (illuminate) {
Chris@1266 598 if (r.contains(illuminatePos)) {
Chris@1266 599 paint.setPen(v->getForeground());
Chris@1266 600 }
Chris@1266 601 }
Chris@1109 602
Chris@1214 603 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1214 604 // SVDEBUG << "rect " << r.x() << "," << r.y() << " "
Chris@1109 605 // << r.width() << "x" << r.height() << endl;
Chris@1109 606 #endif
Chris@1109 607
Chris@1266 608 paint.drawRect(r);
Chris@1109 609
Chris@1266 610 if (showLabel) {
Chris@1109 611 double value = model->getValueAt(sx, sy);
Chris@1109 612 snprintf(labelbuf, buflen, "%06f", value);
Chris@1109 613 QString text(labelbuf);
Chris@1109 614 PaintAssistant::drawVisibleText
Chris@1109 615 (v,
Chris@1109 616 paint,
Chris@1109 617 rx0 + 2,
Chris@1109 618 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
Chris@1109 619 text,
Chris@1109 620 PaintAssistant::OutlinedText);
Chris@1266 621 }
Chris@1266 622 }
Chris@1109 623 }
Chris@1121 624
Chris@1121 625 return magRange;
Chris@1094 626 }
Chris@1094 627
Chris@1080 628 void
Chris@1213 629 Colour3DPlotRenderer::getPreferredPeakCache(const LayerGeometryProvider *v,
Chris@1213 630 int &peakCacheIndex,
Chris@1213 631 int &binsPerPeak) const
Chris@1213 632 {
Chris@1213 633 peakCacheIndex = -1;
Chris@1213 634 binsPerPeak = -1;
Chris@1213 635
Chris@1469 636 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
Chris@1213 637 if (!model) return;
Chris@1217 638 if (m_params.binDisplay == BinDisplay::PeakFrequencies) return;
Chris@1217 639 if (m_params.colourScale.getScale() == ColourScaleType::Phase) return;
Chris@1213 640
Chris@1325 641 ZoomLevel zoomLevel = v->getZoomLevel();
Chris@1213 642 int binResolution = model->getResolution();
Chris@1213 643
Chris@1213 644 for (int ix = 0; in_range_for(m_sources.peakCaches, ix); ++ix) {
Chris@1473 645 auto peakCache = ModelById::getAs<Dense3DModelPeakCache>
Chris@1473 646 (m_sources.peakCaches[ix]);
Chris@1473 647 if (!peakCache) continue;
Chris@1473 648 int bpp = peakCache->getColumnsPerPeak();
Chris@1325 649 ZoomLevel equivZoom(ZoomLevel::FramesPerPixel, binResolution * bpp);
Chris@1502 650 #ifdef DEBUG_COLOUR_PLOT_CACHE_SELECTION
Chris@1500 651 SVDEBUG << "render " << m_sources.source
Chris@1500 652 << ": getPreferredPeakCache: zoomLevel = " << zoomLevel
Chris@1450 653 << ", cache " << ix << " has bpp = " << bpp
Chris@1450 654 << " for equivZoom = " << equivZoom << endl;
Chris@1450 655 #endif
Chris@1213 656 if (zoomLevel >= equivZoom) {
Chris@1213 657 // this peak cache would work, though it might not be best
Chris@1213 658 if (bpp > binsPerPeak) {
Chris@1213 659 // ok, it's better than the best one we've found so far
Chris@1213 660 peakCacheIndex = ix;
Chris@1213 661 binsPerPeak = bpp;
Chris@1213 662 }
Chris@1213 663 }
Chris@1213 664 }
Chris@1213 665
Chris@1502 666 #ifdef DEBUG_COLOUR_PLOT_CACHE_SELECTION
Chris@1499 667 SVDEBUG << "render " << m_sources.source
Chris@1499 668 << ": getPreferredPeakCache: zoomLevel = " << zoomLevel
Chris@1213 669 << ", binResolution " << binResolution
Chris@1213 670 << ", peakCaches " << m_sources.peakCaches.size()
Chris@1450 671 << ": preferring peakCacheIndex " << peakCacheIndex
Chris@1450 672 << " for binsPerPeak " << binsPerPeak
Chris@1213 673 << endl;
Chris@1214 674 #endif
Chris@1213 675 }
Chris@1213 676
Chris@1213 677 void
Chris@1113 678 Colour3DPlotRenderer::renderToCachePixelResolution(const LayerGeometryProvider *v,
Chris@1094 679 int x0, int repaintWidth,
Chris@1094 680 bool rightToLeft,
Chris@1094 681 bool timeConstrained)
Chris@1079 682 {
Chris@1117 683 Profiler profiler("Colour3DPlotRenderer::renderToCachePixelResolution");
Chris@1143 684 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1499 685 SVDEBUG << "render " << m_sources.source
Chris@1500 686 << ": [PIXEL] renderToCachePixelResolution" << endl;
Chris@1143 687 #endif
Chris@1094 688
Chris@1094 689 // Draw to the draw buffer, and then copy from there. The draw
Chris@1094 690 // buffer is at the same resolution as the target in the cache, so
Chris@1094 691 // no extra scaling needed.
Chris@1079 692
Chris@1469 693 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
Chris@1469 694 if (!model) return;
Chris@1079 695
Chris@1079 696 int h = v->getPaintHeight();
Chris@1079 697
Chris@1094 698 clearDrawBuffer(repaintWidth, h);
Chris@1079 699
Chris@1094 700 vector<int> binforx(repaintWidth);
Chris@1079 701 vector<double> binfory(h);
Chris@1079 702
Chris@1094 703 int binResolution = model->getResolution();
Chris@1079 704
Chris@1094 705 for (int x = 0; x < repaintWidth; ++x) {
Chris@1094 706 sv_frame_t f0 = v->getFrameForX(x0 + x);
Chris@1094 707 double s0 = double(f0 - model->getStartFrame()) / binResolution;
Chris@1094 708 binforx[x] = int(s0 + 0.0001);
Chris@1094 709 }
Chris@1080 710
Chris@1212 711 int peakCacheIndex = -1;
Chris@1212 712 int binsPerPeak = -1;
Chris@1212 713
Chris@1217 714 getPreferredPeakCache(v, peakCacheIndex, binsPerPeak);
Chris@1094 715
Chris@1080 716 for (int y = 0; y < h; ++y) {
Chris@1090 717 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
Chris@1080 718 }
Chris@1079 719
Chris@1097 720 int attainedWidth;
Chris@1097 721
Chris@1103 722 if (m_params.binDisplay == BinDisplay::PeakFrequencies) {
Chris@1097 723 attainedWidth = renderDrawBufferPeakFrequencies(v,
Chris@1097 724 repaintWidth,
Chris@1097 725 h,
Chris@1097 726 binforx,
Chris@1097 727 binfory,
Chris@1097 728 rightToLeft,
Chris@1097 729 timeConstrained);
Chris@1097 730
Chris@1097 731 } else {
Chris@1097 732 attainedWidth = renderDrawBuffer(repaintWidth,
Chris@1080 733 h,
Chris@1080 734 binforx,
Chris@1080 735 binfory,
Chris@1212 736 peakCacheIndex,
Chris@1080 737 rightToLeft,
Chris@1080 738 timeConstrained);
Chris@1097 739 }
Chris@1083 740
Chris@1094 741 if (attainedWidth == 0) return;
Chris@1084 742
Chris@1094 743 // draw buffer is pixel resolution, no scaling factors or padding involved
Chris@1084 744
Chris@1084 745 int paintedLeft = x0;
Chris@1084 746 if (rightToLeft) {
Chris@1084 747 paintedLeft += (repaintWidth - attainedWidth);
Chris@1084 748 }
Chris@1084 749
Chris@1094 750 m_cache.drawImage(paintedLeft, attainedWidth,
Chris@1094 751 m_drawBuffer,
Chris@1094 752 paintedLeft - x0, attainedWidth);
Chris@1121 753
Chris@1121 754 for (int i = 0; in_range_for(m_magRanges, i); ++i) {
Chris@1121 755 m_magCache.sampleColumn(i, m_magRanges.at(i));
Chris@1121 756 }
Chris@1094 757 }
Chris@1084 758
Chris@1167 759 QImage
Chris@1167 760 Colour3DPlotRenderer::scaleDrawBufferImage(QImage image,
Chris@1167 761 int targetWidth,
Chris@1167 762 int targetHeight) const
Chris@1167 763 {
Chris@1167 764 int sourceWidth = image.width();
Chris@1167 765 int sourceHeight = image.height();
Chris@1167 766
Chris@1167 767 // We can only do this if we're making the image larger --
Chris@1167 768 // otherwise peaks may be lost. So this should be called only when
Chris@1167 769 // rendering in DrawBufferBinResolution mode. Whenever the bin
Chris@1167 770 // size is smaller than the pixel size, in either x or y axis, we
Chris@1167 771 // should be using DrawBufferPixelResolution mode instead
Chris@1167 772
Chris@1167 773 if (targetWidth < sourceWidth || targetHeight < sourceHeight) {
Chris@1167 774 throw std::logic_error("Colour3DPlotRenderer::scaleDrawBufferImage: Can only use this function when making the image larger; should be rendering DrawBufferPixelResolution instead");
Chris@1167 775 }
Chris@1167 776
Chris@1167 777 if (sourceWidth <= 0 || sourceHeight <= 0) {
Chris@1167 778 throw std::logic_error("Colour3DPlotRenderer::scaleDrawBufferImage: Source image is empty");
Chris@1167 779 }
Chris@1167 780
Chris@1167 781 if (targetWidth <= 0 || targetHeight <= 0) {
Chris@1167 782 throw std::logic_error("Colour3DPlotRenderer::scaleDrawBufferImage: Target image is empty");
Chris@1167 783 }
Chris@1167 784
Chris@1167 785 // This function exists because of some unpredictable behaviour
Chris@1167 786 // from Qt when scaling images with FastTransformation mode. We
Chris@1167 787 // continue to use Qt's scaler for SmoothTransformation but let's
Chris@1167 788 // bring the non-interpolated version "in-house" so we know what
Chris@1167 789 // it's really doing.
Chris@1167 790
Chris@1167 791 if (m_params.interpolate) {
Chris@1167 792 return image.scaled(targetWidth, targetHeight,
Chris@1167 793 Qt::IgnoreAspectRatio,
Chris@1167 794 Qt::SmoothTransformation);
Chris@1167 795 }
Chris@1167 796
Chris@1167 797 // Same format as the target cache
Chris@1531 798 QImage target(targetWidth, targetHeight,
Chris@1531 799 QImage::Format_ARGB32_Premultiplied);
Chris@1167 800
Chris@1167 801 for (int y = 0; y < targetHeight; ++y) {
Chris@1167 802
Chris@1167 803 QRgb *targetLine = reinterpret_cast<QRgb *>(target.scanLine(y));
Chris@1167 804
Chris@1167 805 int sy = int((uint64_t(y) * sourceHeight) / targetHeight);
Chris@1167 806 if (sy == sourceHeight) --sy;
Chris@1167 807
Chris@1531 808 // The source image is 8-bit indexed
Chris@1531 809 const uchar *sourceLine = image.constScanLine(sy);
Chris@1532 810
Chris@1532 811 int psx = -1;
Chris@1532 812 QRgb colour = {};
Chris@1531 813
Chris@1167 814 for (int x = 0; x < targetWidth; ++x) {
Chris@1167 815
Chris@1167 816 int sx = int((uint64_t(x) * sourceWidth) / targetWidth);
Chris@1167 817 if (sx == sourceWidth) --sx;
Chris@1531 818
Chris@1532 819 if (sx > psx) {
Chris@1532 820 colour = image.color(sourceLine[sx]);
Chris@1532 821 }
Chris@1532 822
Chris@1532 823 targetLine[x] = colour;
Chris@1532 824 psx = sx;
Chris@1167 825 }
Chris@1167 826 }
Chris@1167 827
Chris@1167 828 return target;
Chris@1167 829 }
Chris@1167 830
Chris@1094 831 void
Chris@1113 832 Colour3DPlotRenderer::renderToCacheBinResolution(const LayerGeometryProvider *v,
Chris@1094 833 int x0, int repaintWidth)
Chris@1094 834 {
Chris@1117 835 Profiler profiler("Colour3DPlotRenderer::renderToCacheBinResolution");
Chris@1143 836 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1499 837 SVDEBUG << "render " << m_sources.source
Chris@1500 838 << ": [BIN] renderToCacheBinResolution" << endl;
Chris@1143 839 #endif
Chris@1094 840
Chris@1094 841 // Draw to the draw buffer, and then scale-copy from there. Draw
Chris@1094 842 // buffer is at bin resolution, i.e. buffer x == source column
Chris@1094 843 // number. We use toolkit smooth scaling for interpolation.
Chris@1084 844
Chris@1469 845 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_sources.source);
Chris@1469 846 if (!model) return;
Chris@1094 847
Chris@1094 848 // The draw buffer will contain a fragment at bin resolution. We
Chris@1094 849 // need to ensure that it starts and ends at points where a
Chris@1094 850 // time-bin boundary occurs at an exact pixel boundary, and with a
Chris@1094 851 // certain amount of overlap across existing pixels so that we can
Chris@1094 852 // scale and draw from it without smoothing errors at the edges.
Chris@1094 853
Chris@1094 854 // If (getFrameForX(x) / increment) * increment ==
Chris@1094 855 // getFrameForX(x), then x is a time-bin boundary. We want two
Chris@1094 856 // such boundaries at either side of the draw buffer -- one which
Chris@1094 857 // we draw up to, and one which we subsequently crop at.
Chris@1094 858
Chris@1094 859 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
Chris@1094 860 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
Chris@1094 861
Chris@1094 862 int drawBufferWidth;
Chris@1094 863 int binResolution = model->getResolution();
Chris@1094 864
Chris@1330 865 // These loops should eventually terminate provided that
Chris@1330 866 // getFrameForX always returns a multiple of the zoom level,
Chris@1330 867 // i.e. there is some x for which getFrameForX(x) == 0 and
Chris@1330 868 // subsequent return values are equally spaced
Chris@1329 869
Chris@1330 870 for (int x = x0; ; --x) {
Chris@1094 871 sv_frame_t f = v->getFrameForX(x);
Chris@1330 872 if ((f / binResolution) * binResolution == f) {
Chris@1094 873 if (leftCropFrame == -1) leftCropFrame = f;
Chris@1094 874 else if (x < x0 - 2) {
Chris@1094 875 leftBoundaryFrame = f;
Chris@1094 876 break;
Chris@1094 877 }
Chris@1094 878 }
Chris@1094 879 }
Chris@1329 880
Chris@1330 881 for (int x = x0 + repaintWidth; ; ++x) {
Chris@1094 882 sv_frame_t f = v->getFrameForX(x);
Chris@1330 883 if ((f / binResolution) * binResolution == f) {
Chris@1494 884 if (v->getXForFrame(f) < x0 + repaintWidth) {
Chris@1494 885 continue;
Chris@1494 886 }
Chris@1094 887 if (rightCropFrame == -1) rightCropFrame = f;
Chris@1094 888 else if (x > x0 + repaintWidth + 2) {
Chris@1094 889 rightBoundaryFrame = f;
Chris@1094 890 break;
Chris@1094 891 }
Chris@1094 892 }
Chris@1094 893 }
Chris@1329 894
Chris@1094 895 drawBufferWidth = int
Chris@1094 896 ((rightBoundaryFrame - leftBoundaryFrame) / binResolution);
Chris@1094 897
Chris@1094 898 int h = v->getPaintHeight();
Chris@1094 899
Chris@1095 900 // For our purposes here, the draw buffer needs to be exactly our
Chris@1095 901 // target size (so we recreate always rather than just clear it)
Chris@1095 902
Chris@1095 903 recreateDrawBuffer(drawBufferWidth, h);
Chris@1094 904
Chris@1094 905 vector<int> binforx(drawBufferWidth);
Chris@1094 906 vector<double> binfory(h);
Chris@1094 907
Chris@1094 908 for (int x = 0; x < drawBufferWidth; ++x) {
Chris@1094 909 binforx[x] = int(leftBoundaryFrame / binResolution) + x;
Chris@1094 910 }
Chris@1094 911
Chris@1214 912 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 913 SVDEBUG << "render " << m_sources.source
Chris@1500 914 << ": binResolution " << binResolution << endl;
Chris@1214 915 #endif
Chris@1094 916
Chris@1094 917 for (int y = 0; y < h; ++y) {
Chris@1094 918 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
Chris@1094 919 }
Chris@1094 920
Chris@1094 921 int attainedWidth = renderDrawBuffer(drawBufferWidth,
Chris@1094 922 h,
Chris@1094 923 binforx,
Chris@1094 924 binfory,
Chris@1212 925 -1,
Chris@1094 926 false,
Chris@1094 927 false);
Chris@1094 928
Chris@1094 929 if (attainedWidth == 0) return;
Chris@1094 930
Chris@1094 931 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
Chris@1094 932 int scaledRight = v->getXForFrame(rightBoundaryFrame);
Chris@1095 933
Chris@1143 934 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 935 SVDEBUG << "render " << m_sources.source
Chris@1500 936 << ": scaling draw buffer from width " << m_drawBuffer.width()
Chris@1494 937 << " to " << (scaledRight - scaledLeft)
Chris@1494 938 << " (nb drawBufferWidth = "
Chris@1494 939 << drawBufferWidth << ", attainedWidth = "
Chris@1494 940 << attainedWidth << ")" << endl;
Chris@1143 941 #endif
Chris@1167 942
Chris@1167 943 QImage scaled = scaleDrawBufferImage
Chris@1167 944 (m_drawBuffer, scaledRight - scaledLeft, h);
Chris@1084 945
Chris@1094 946 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
Chris@1094 947 int scaledRightCrop = v->getXForFrame(rightCropFrame);
Chris@1094 948
Chris@1094 949 int targetLeft = scaledLeftCrop;
Chris@1094 950 if (targetLeft < 0) {
Chris@1094 951 targetLeft = 0;
Chris@1094 952 }
Chris@1094 953
Chris@1094 954 int targetWidth = scaledRightCrop - targetLeft;
Chris@1094 955 if (targetLeft + targetWidth > m_cache.getSize().width()) {
Chris@1094 956 targetWidth = m_cache.getSize().width() - targetLeft;
Chris@1094 957 }
Chris@1094 958
Chris@1094 959 int sourceLeft = targetLeft - scaledLeft;
Chris@1094 960 if (sourceLeft < 0) {
Chris@1094 961 sourceLeft = 0;
Chris@1094 962 }
Chris@1494 963
Chris@1494 964 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 965 SVDEBUG << "render " << m_sources.source
Chris@1500 966 << ": leftBoundaryFrame = " << leftBoundaryFrame
Chris@1494 967 << ", leftCropFrame = " << leftCropFrame
Chris@1494 968 << ", scaledLeft = " << scaledLeft
Chris@1494 969 << ", scaledLeftCrop = " << scaledLeftCrop
Chris@1494 970 << endl;
Chris@1500 971 SVDEBUG << "render " << m_sources.source
Chris@1500 972 << ": rightBoundaryFrame = " << rightBoundaryFrame
Chris@1494 973 << ", rightCropFrame = " << rightCropFrame
Chris@1494 974 << ", scaledRight = " << scaledRight
Chris@1494 975 << ", scaledRightCrop = " << scaledRightCrop
Chris@1494 976 << endl;
Chris@1494 977 #endif
Chris@1094 978
Chris@1143 979 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 980 SVDEBUG << "render " << m_sources.source
Chris@1500 981 << ": x0 = " << x0
Chris@1494 982 << ", repaintWidth = " << repaintWidth
Chris@1494 983 << ", targetLeft = " << targetLeft
Chris@1214 984 << ", targetWidth = " << targetWidth << endl;
Chris@1143 985 #endif
Chris@1094 986
Chris@1094 987 if (targetWidth > 0) {
Chris@1136 988 // we are copying from an image that has already been scaled,
Chris@1136 989 // hence using the same width in both geometries
Chris@1094 990 m_cache.drawImage(targetLeft, targetWidth,
Chris@1094 991 scaled,
Chris@1136 992 sourceLeft, targetWidth);
Chris@1084 993 }
Chris@1121 994
Chris@1121 995 for (int i = 0; i < targetWidth; ++i) {
Chris@1136 996 // but the mag range vector has not been scaled
Chris@1136 997 int sourceIx = int((double(i + sourceLeft) / scaled.width())
Chris@1136 998 * int(m_magRanges.size()));
Chris@1121 999 if (in_range_for(m_magRanges, sourceIx)) {
Chris@1121 1000 m_magCache.sampleColumn(i, m_magRanges.at(sourceIx));
Chris@1121 1001 }
Chris@1121 1002 }
Chris@1079 1003 }
Chris@1083 1004
Chris@1083 1005 int
Chris@1083 1006 Colour3DPlotRenderer::renderDrawBuffer(int w, int h,
Chris@1083 1007 const vector<int> &binforx,
Chris@1083 1008 const vector<double> &binfory,
Chris@1212 1009 int peakCacheIndex,
Chris@1083 1010 bool rightToLeft,
Chris@1083 1011 bool timeConstrained)
Chris@1083 1012 {
Chris@1083 1013 // Callers must have checked that the appropriate subset of
Chris@1083 1014 // Sources data members are set for the supplied flags (e.g. that
Chris@1212 1015 // peakCache corresponding to peakCacheIndex exists)
Chris@1083 1016
Chris@1083 1017 RenderTimer timer(timeConstrained ?
Chris@1083 1018 RenderTimer::FastRender :
Chris@1083 1019 RenderTimer::NoTimeout);
Chris@1083 1020
Chris@1164 1021 Profiler profiler("Colour3DPlotRenderer::renderDrawBuffer");
Chris@1164 1022
Chris@1083 1023 int divisor = 1;
Chris@1473 1024
Chris@1473 1025 std::shared_ptr<DenseThreeDimensionalModel> sourceModel;
Chris@1473 1026
Chris@1473 1027 if (peakCacheIndex >= 0) {
Chris@1473 1028 auto peakCache = ModelById::getAs<Dense3DModelPeakCache>
Chris@1473 1029 (m_sources.peakCaches[peakCacheIndex]);
Chris@1473 1030 if (peakCache) {
Chris@1473 1031 divisor = peakCache->getColumnsPerPeak();
Chris@1473 1032 sourceModel = peakCache;
Chris@1473 1033 }
Chris@1473 1034 }
Chris@1473 1035
Chris@1473 1036 if (!sourceModel) {
Chris@1473 1037 sourceModel = ModelById::getAs<DenseThreeDimensionalModel>
Chris@1473 1038 (m_sources.source);
Chris@1473 1039 }
Chris@1469 1040
Chris@1473 1041 if (!sourceModel) return 0;
Chris@1083 1042
Chris@1214 1043 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1044 SVDEBUG << "render " << m_sources.source
Chris@1500 1045 << ": renderDrawBuffer: w = " << w << ", h = " << h
Chris@1212 1046 << ", peakCacheIndex = " << peakCacheIndex << " (divisor = "
cannam@1171 1047 << divisor << "), rightToLeft = " << rightToLeft
cannam@1171 1048 << ", timeConstrained = " << timeConstrained << endl;
Chris@1500 1049 SVDEBUG << "render " << m_sources.source
Chris@1500 1050 << ": renderDrawBuffer: normalization = " << int(m_params.normalization)
cannam@1171 1051 << ", binDisplay = " << int(m_params.binDisplay)
cannam@1171 1052 << ", binScale = " << int(m_params.binScale)
cannam@1171 1053 << ", alwaysOpaque = " << m_params.alwaysOpaque
cannam@1171 1054 << ", interpolate = " << m_params.interpolate << endl;
Chris@1214 1055 #endif
cannam@1171 1056
Chris@1135 1057 int sh = sourceModel->getHeight();
Chris@1135 1058
Chris@1135 1059 int minbin = int(binfory[0] + 0.0001);
Chris@1135 1060 if (minbin >= sh) minbin = sh - 1;
Chris@1135 1061 if (minbin < 0) minbin = 0;
Chris@1135 1062
Chris@1170 1063 int nbins = int(binfory[h-1] + 0.0001) - minbin + 1;
Chris@1135 1064 if (minbin + nbins > sh) nbins = sh - minbin;
Chris@1162 1065
Chris@1162 1066 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1067 SVDEBUG << "render " << m_sources.source
Chris@1500 1068 << ": minbin = " << minbin << ", nbins = " << nbins
Chris@1500 1069 << ", last binfory = " << binfory[h-1]
Chris@1500 1070 << " (rounds to " << int(binfory[h-1])
Chris@1500 1071 << ") (model height " << sh << ")" << endl;
Chris@1162 1072 #endif
Chris@1135 1073
Chris@1083 1074 int psx = -1;
Chris@1083 1075
Chris@1083 1076 int start = 0;
Chris@1083 1077 int finish = w;
Chris@1083 1078 int step = 1;
Chris@1083 1079
Chris@1083 1080 if (rightToLeft) {
Chris@1083 1081 start = w-1;
Chris@1083 1082 finish = -1;
Chris@1083 1083 step = -1;
Chris@1083 1084 }
Chris@1083 1085
Chris@1221 1086 int xPixelCount = 0;
Chris@1083 1087
Chris@1083 1088 vector<float> preparedColumn;
Chris@1094 1089
Chris@1094 1090 int modelWidth = sourceModel->getWidth();
Chris@1121 1091
Chris@1143 1092 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1093 SVDEBUG << "render " << m_sources.source
Chris@1500 1094 << ": modelWidth " << modelWidth << ", divisor " << divisor << endl;
Chris@1500 1095 SVDEBUG << "render " << m_sources.source
Chris@1500 1096 << ": start = " << start << ", finish = " << finish << ", step = " << step << endl;
Chris@1143 1097 #endif
Chris@1143 1098
Chris@1083 1099 for (int x = start; x != finish; x += step) {
Chris@1083 1100
Chris@1083 1101 // x is the on-canvas pixel coord; sx (later) will be the
Chris@1083 1102 // source column index
Chris@1083 1103
Chris@1221 1104 ++xPixelCount;
Chris@1083 1105
Chris@1083 1106 if (binforx[x] < 0) continue;
Chris@1083 1107
Chris@1083 1108 int sx0 = binforx[x] / divisor;
Chris@1083 1109 int sx1 = sx0;
Chris@1083 1110 if (x+1 < w) sx1 = binforx[x+1] / divisor;
Chris@1083 1111 if (sx0 < 0) sx0 = sx1 - 1;
Chris@1083 1112 if (sx0 < 0) continue;
Chris@1083 1113 if (sx1 <= sx0) sx1 = sx0 + 1;
Chris@1083 1114
Chris@1161 1115 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1214 1116 // SVDEBUG << "x = " << x << ", binforx[x] = " << binforx[x] << ", sx range " << sx0 << " -> " << sx1 << endl;
Chris@1161 1117 #endif
Chris@1161 1118
Chris@1083 1119 vector<float> pixelPeakColumn;
Chris@1121 1120 MagnitudeRange magRange;
Chris@1083 1121
Chris@1083 1122 for (int sx = sx0; sx < sx1; ++sx) {
Chris@1083 1123
Chris@1094 1124 if (sx < 0 || sx >= modelWidth) {
Chris@1083 1125 continue;
Chris@1083 1126 }
Chris@1083 1127
Chris@1083 1128 if (sx != psx) {
Chris@1138 1129
Chris@1138 1130 // order:
Chris@1138 1131 // get column -> scale -> normalise -> record extents ->
Chris@1138 1132 // peak pick -> distribute/interpolate -> apply display gain
Chris@1083 1133
Chris@1138 1134 // this does the first three:
Chris@1161 1135 ColumnOp::Column column = getColumn(sx, minbin, nbins,
Chris@1502 1136 sourceModel);
Chris@1083 1137
Chris@1131 1138 magRange.sample(column);
Chris@1162 1139
Chris@1103 1140 if (m_params.binDisplay == BinDisplay::PeakBins) {
Chris@1083 1141 column = ColumnOp::peakPick(column);
Chris@1083 1142 }
Chris@1083 1143
Chris@1083 1144 preparedColumn =
Chris@1124 1145 ColumnOp::distribute(column,
Chris@1083 1146 h,
Chris@1083 1147 binfory,
Chris@1083 1148 minbin,
Chris@1083 1149 m_params.interpolate);
Chris@1124 1150
Chris@1124 1151 // Display gain belongs to the colour scale and is
Chris@1124 1152 // applied by the colour scale object when mapping it
Chris@1083 1153
Chris@1083 1154 psx = sx;
Chris@1083 1155 }
Chris@1083 1156
Chris@1083 1157 if (sx == sx0) {
Chris@1083 1158 pixelPeakColumn = preparedColumn;
Chris@1083 1159 } else {
Chris@1083 1160 for (int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
Chris@1083 1161 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
Chris@1083 1162 preparedColumn[i]);
Chris@1083 1163 }
Chris@1083 1164 }
Chris@1083 1165 }
Chris@1083 1166
Chris@1083 1167 if (!pixelPeakColumn.empty()) {
Chris@1121 1168
Chris@1083 1169 for (int y = 0; y < h; ++y) {
Chris@1116 1170 int py;
Chris@1116 1171 if (m_params.invertVertical) {
Chris@1116 1172 py = y;
Chris@1116 1173 } else {
Chris@1116 1174 py = h - y - 1;
Chris@1116 1175 }
Chris@1083 1176 m_drawBuffer.setPixel
Chris@1083 1177 (x,
Chris@1116 1178 py,
Chris@1083 1179 m_params.colourScale.getPixel(pixelPeakColumn[y]));
Chris@1083 1180 }
Chris@1121 1181
Chris@1121 1182 m_magRanges.push_back(magRange);
Chris@1083 1183 }
Chris@1083 1184
Chris@1221 1185 double fractionComplete = double(xPixelCount) / double(w);
Chris@1083 1186 if (timer.outOfTime(fractionComplete)) {
Chris@1214 1187 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1188 SVDEBUG << "render " << m_sources.source
Chris@1500 1189 << ": out of time with xPixelCount = " << xPixelCount << endl;
Chris@1214 1190 #endif
Chris@1221 1191 updateTimings(timer, xPixelCount);
Chris@1221 1192 return xPixelCount;
Chris@1083 1193 }
Chris@1083 1194 }
Chris@1083 1195
Chris@1221 1196 updateTimings(timer, xPixelCount);
Chris@1451 1197
Chris@1451 1198 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1199 SVDEBUG << "render " << m_sources.source
Chris@1500 1200 << ": completed with xPixelCount = " << xPixelCount << endl;
Chris@1451 1201 #endif
Chris@1221 1202 return xPixelCount;
Chris@1083 1203 }
Chris@1083 1204
Chris@1097 1205 int
Chris@1113 1206 Colour3DPlotRenderer::renderDrawBufferPeakFrequencies(const LayerGeometryProvider *v,
Chris@1097 1207 int w, int h,
Chris@1097 1208 const vector<int> &binforx,
Chris@1097 1209 const vector<double> &binfory,
Chris@1097 1210 bool rightToLeft,
Chris@1097 1211 bool timeConstrained)
Chris@1097 1212 {
Chris@1500 1213 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1214 SVDEBUG << "render " << m_sources.source
Chris@1500 1215 << ": [PEAK] renderDrawBufferPeakFrequencies" << endl;
Chris@1500 1216 #endif
Chris@1500 1217
Chris@1097 1218 // Callers must have checked that the appropriate subset of
Chris@1097 1219 // Sources data members are set for the supplied flags (e.g. that
Chris@1097 1220 // fft model exists)
Chris@1097 1221
Chris@1097 1222 RenderTimer timer(timeConstrained ?
Chris@1221 1223 RenderTimer::SlowRender :
Chris@1097 1224 RenderTimer::NoTimeout);
Chris@1097 1225
Chris@1469 1226 auto fft = ModelById::getAs<FFTModel>(m_sources.fft);
Chris@1469 1227 if (!fft) return 0;
Chris@1135 1228
Chris@1135 1229 int sh = fft->getHeight();
Chris@1135 1230
Chris@1097 1231 int minbin = int(binfory[0] + 0.0001);
Chris@1135 1232 if (minbin >= sh) minbin = sh - 1;
Chris@1097 1233 if (minbin < 0) minbin = 0;
Chris@1097 1234
Chris@1135 1235 int nbins = int(binfory[h-1]) - minbin + 1;
Chris@1135 1236 if (minbin + nbins > sh) nbins = sh - minbin;
Chris@1097 1237
Chris@1097 1238 FFTModel::PeakSet peakfreqs;
Chris@1097 1239
Chris@1097 1240 int psx = -1;
Chris@1097 1241
Chris@1097 1242 int start = 0;
Chris@1097 1243 int finish = w;
Chris@1097 1244 int step = 1;
Chris@1097 1245
Chris@1097 1246 if (rightToLeft) {
Chris@1097 1247 start = w-1;
Chris@1097 1248 finish = -1;
Chris@1097 1249 step = -1;
Chris@1097 1250 }
Chris@1097 1251
Chris@1221 1252 int xPixelCount = 0;
Chris@1097 1253
Chris@1097 1254 vector<float> preparedColumn;
Chris@1097 1255
Chris@1097 1256 int modelWidth = fft->getWidth();
Chris@1143 1257 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1258 SVDEBUG << "render " << m_sources.source
Chris@1500 1259 << ": modelWidth " << modelWidth << endl;
Chris@1143 1260 #endif
Chris@1143 1261
Chris@1135 1262 double minFreq =
Chris@1135 1263 (double(minbin) * fft->getSampleRate()) / fft->getFFTSize();
Chris@1135 1264 double maxFreq =
Chris@1135 1265 (double(minbin + nbins - 1) * fft->getSampleRate()) / fft->getFFTSize();
Chris@1097 1266
Chris@1103 1267 bool logarithmic = (m_params.binScale == BinScale::Log);
Chris@1217 1268
Chris@1218 1269 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1270 SVDEBUG << "render " << m_sources.source
Chris@1500 1271 << ": start = " << start << ", finish = " << finish
Chris@1218 1272 << ", step = " << step << endl;
Chris@1218 1273 #endif
Chris@1218 1274
Chris@1097 1275 for (int x = start; x != finish; x += step) {
Chris@1097 1276
Chris@1097 1277 // x is the on-canvas pixel coord; sx (later) will be the
Chris@1097 1278 // source column index
Chris@1097 1279
Chris@1221 1280 ++xPixelCount;
Chris@1097 1281
Chris@1097 1282 if (binforx[x] < 0) continue;
Chris@1097 1283
Chris@1097 1284 int sx0 = binforx[x];
Chris@1097 1285 int sx1 = sx0;
Chris@1097 1286 if (x+1 < w) sx1 = binforx[x+1];
Chris@1097 1287 if (sx0 < 0) sx0 = sx1 - 1;
Chris@1097 1288 if (sx0 < 0) continue;
Chris@1097 1289 if (sx1 <= sx0) sx1 = sx0 + 1;
Chris@1097 1290
Chris@1097 1291 vector<float> pixelPeakColumn;
Chris@1121 1292 MagnitudeRange magRange;
Chris@1097 1293
Chris@1097 1294 for (int sx = sx0; sx < sx1; ++sx) {
Chris@1097 1295
Chris@1097 1296 if (sx < 0 || sx >= modelWidth) {
Chris@1097 1297 continue;
Chris@1097 1298 }
Chris@1097 1299
Chris@1097 1300 if (sx != psx) {
Chris@1502 1301 preparedColumn = getColumn(sx, minbin, nbins, fft);
Chris@1131 1302 magRange.sample(preparedColumn);
Chris@1097 1303 psx = sx;
Chris@1097 1304 }
Chris@1097 1305
Chris@1097 1306 if (sx == sx0) {
Chris@1097 1307 pixelPeakColumn = preparedColumn;
Chris@1097 1308 peakfreqs = fft->getPeakFrequencies(FFTModel::AllPeaks, sx,
Chris@1135 1309 minbin, minbin + nbins - 1);
Chris@1097 1310 } else {
Chris@1097 1311 for (int i = 0; in_range_for(pixelPeakColumn, i); ++i) {
Chris@1097 1312 pixelPeakColumn[i] = std::max(pixelPeakColumn[i],
Chris@1097 1313 preparedColumn[i]);
Chris@1097 1314 }
Chris@1097 1315 }
Chris@1097 1316 }
Chris@1097 1317
Chris@1097 1318 if (!pixelPeakColumn.empty()) {
Chris@1164 1319
Chris@1218 1320 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1221 1321 // SVDEBUG << "found " << peakfreqs.size() << " peak freqs at column "
Chris@1221 1322 // << sx0 << endl;
Chris@1218 1323 #endif
Chris@1218 1324
Chris@1097 1325 for (FFTModel::PeakSet::const_iterator pi = peakfreqs.begin();
Chris@1097 1326 pi != peakfreqs.end(); ++pi) {
Chris@1097 1327
Chris@1097 1328 int bin = pi->first;
Chris@1097 1329 double freq = pi->second;
Chris@1097 1330
Chris@1097 1331 if (bin < minbin) continue;
Chris@1135 1332 if (bin >= minbin + nbins) break;
Chris@1097 1333
Chris@1097 1334 double value = pixelPeakColumn[bin - minbin];
Chris@1097 1335
Chris@1097 1336 double y = v->getYForFrequency
Chris@1097 1337 (freq, minFreq, maxFreq, logarithmic);
Chris@1097 1338
Chris@1097 1339 int iy = int(y + 0.5);
Chris@1097 1340 if (iy < 0 || iy >= h) continue;
Chris@1097 1341
Chris@1219 1342 auto pixel = m_params.colourScale.getPixel(value);
Chris@1219 1343
Chris@1219 1344 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1219 1345 // SVDEBUG << "frequency " << freq << " for bin " << bin
Chris@1219 1346 // << " -> y = " << y << ", iy = " << iy << ", value = "
Chris@1219 1347 // << value << ", pixel " << pixel << "\n";
Chris@1219 1348 #endif
Chris@1219 1349
Chris@1219 1350 m_drawBuffer.setPixel(x, iy, pixel);
Chris@1097 1351 }
Chris@1121 1352
Chris@1121 1353 m_magRanges.push_back(magRange);
Chris@1218 1354
Chris@1218 1355 } else {
Chris@1218 1356 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1357 SVDEBUG << "render " << m_sources.source
Chris@1500 1358 << ": pixel peak column for range " << sx0 << " to " << sx1
Chris@1218 1359 << " is empty" << endl;
Chris@1218 1360 #endif
Chris@1097 1361 }
Chris@1097 1362
Chris@1221 1363 double fractionComplete = double(xPixelCount) / double(w);
Chris@1097 1364 if (timer.outOfTime(fractionComplete)) {
Chris@1218 1365 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1366 SVDEBUG << "render " << m_sources.source
Chris@1500 1367 << ": out of time" << endl;
Chris@1218 1368 #endif
Chris@1221 1369 updateTimings(timer, xPixelCount);
Chris@1221 1370 return xPixelCount;
Chris@1097 1371 }
Chris@1097 1372 }
Chris@1097 1373
Chris@1221 1374 updateTimings(timer, xPixelCount);
Chris@1221 1375 return xPixelCount;
Chris@1221 1376 }
Chris@1221 1377
Chris@1221 1378 void
Chris@1221 1379 Colour3DPlotRenderer::updateTimings(const RenderTimer &timer, int xPixelCount)
Chris@1221 1380 {
Chris@1237 1381 double secondsPerXPixel = timer.secondsPerItem(xPixelCount);
Chris@1221 1382
Chris@1237 1383 // valid if we have enough data points, or if the overall time is
Chris@1237 1384 // massively slow anyway (as we definitely need to warn about that)
Chris@1237 1385 bool valid = (xPixelCount > 20 || secondsPerXPixel > 0.01);
Chris@1237 1386
Chris@1237 1387 if (valid) {
Chris@1237 1388 m_secondsPerXPixel = secondsPerXPixel;
Chris@1237 1389 m_secondsPerXPixelValid = true;
Chris@1237 1390
Chris@1221 1391 #ifdef DEBUG_COLOUR_PLOT_REPAINT
Chris@1500 1392 SVDEBUG << "render " << m_sources.source
Chris@1500 1393 << ": across " << xPixelCount
Chris@1500 1394 << " x-pixels, seconds per x-pixel = "
Chris@1450 1395 << m_secondsPerXPixel << " (total = "
Chris@1499 1396 << (xPixelCount * m_secondsPerXPixel) << ")" << endl;
Chris@1221 1397 #endif
Chris@1237 1398 }
Chris@1097 1399 }
Chris@1097 1400
Chris@1079 1401 void
Chris@1095 1402 Colour3DPlotRenderer::recreateDrawBuffer(int w, int h)
Chris@1079 1403 {
Chris@1095 1404 m_drawBuffer = QImage(w, h, QImage::Format_Indexed8);
Chris@1079 1405
Chris@1095 1406 for (int pixel = 0; pixel < 256; ++pixel) {
Chris@1095 1407 m_drawBuffer.setColor
Chris@1095 1408 ((unsigned char)pixel,
Chris@1112 1409 m_params.colourScale.getColourForPixel
Chris@1112 1410 (pixel, m_params.colourRotation).rgb());
Chris@1079 1411 }
Chris@1079 1412
Chris@1079 1413 m_drawBuffer.fill(0);
Chris@1121 1414 m_magRanges.clear();
Chris@1079 1415 }
Chris@1079 1416
Chris@1095 1417 void
Chris@1095 1418 Colour3DPlotRenderer::clearDrawBuffer(int w, int h)
Chris@1095 1419 {
Chris@1095 1420 if (m_drawBuffer.width() < w || m_drawBuffer.height() != h) {
Chris@1095 1421 recreateDrawBuffer(w, h);
Chris@1095 1422 } else {
Chris@1095 1423 m_drawBuffer.fill(0);
Chris@1121 1424 m_magRanges.clear();
Chris@1095 1425 }
Chris@1095 1426 }
Chris@1079 1427
Chris@1139 1428 QRect
Chris@1139 1429 Colour3DPlotRenderer::findSimilarRegionExtents(QPoint p) const
Chris@1139 1430 {
Chris@1139 1431 QImage image = m_cache.getImage();
Chris@1139 1432 ImageRegionFinder finder;
Chris@1139 1433 QRect rect = finder.findRegionExtents(&image, p);
Chris@1139 1434 return rect;
Chris@1139 1435 }