comparison layer/Colour3DPlotRenderer.cpp @ 1109:477521f95a84 spectrogram-minor-refactor

Start introducing translucent renderer
author Chris Cannam
date Mon, 18 Jul 2016 15:37:15 +0100
parents ea5ae9dd10ba
children 67dd16e33a3d
comparison
equal deleted inserted replaced
1108:edbe229860ac 1109:477521f95a84
20 #include "data/model/Dense3DModelPeakCache.h" 20 #include "data/model/Dense3DModelPeakCache.h"
21 #include "data/model/FFTModel.h" 21 #include "data/model/FFTModel.h"
22 22
23 #include "LayerGeometryProvider.h" 23 #include "LayerGeometryProvider.h"
24 #include "VerticalBinLayer.h" 24 #include "VerticalBinLayer.h"
25 #include "PaintAssistant.h"
26
27 #include "view/ViewManager.h" // for main model sample rate. Pity
25 28
26 #include <vector> 29 #include <vector>
27 30
28 //#define DEBUG_SPECTROGRAM_REPAINT 1 31 //#define DEBUG_SPECTROGRAM_REPAINT 1
29 32
60 63
61 Colour3DPlotRenderer::RenderResult 64 Colour3DPlotRenderer::RenderResult
62 Colour3DPlotRenderer::render(LayerGeometryProvider *v, 65 Colour3DPlotRenderer::render(LayerGeometryProvider *v,
63 QPainter &paint, QRect rect, bool timeConstrained) 66 QPainter &paint, QRect rect, bool timeConstrained)
64 { 67 {
68 RenderType renderType = decideRenderType(v);
69
70 if (renderType != DrawBufferPixelResolution) {
71 // Rendering should be fast in bin-resolution and direct draw
72 // cases because we are quite well zoomed-in, and the sums are
73 // easier this way. Calculating boundaries later will be
74 // fiddly for partial paints otherwise.
75 timeConstrained = false;
76 }
77
78 if (renderType == DirectTranslucent) {
79 renderDirectTranslucent(v, paint, rect);
80 return { rect, {} }; //!!! this return arg is not very useful
81 }
82
65 sv_frame_t startFrame = v->getStartFrame(); 83 sv_frame_t startFrame = v->getStartFrame();
66 84
67 int x0 = v->getXForViewX(rect.x()); 85 int x0 = v->getXForViewX(rect.x());
68 int x1 = v->getXForViewX(rect.x() + rect.width()); 86 int x1 = v->getXForViewX(rect.x() + rect.width());
69 if (x0 < 0) x0 = 0; 87 if (x0 < 0) x0 = 0;
78 << endl; 96 << endl;
79 cerr << " view start " << startFrame 97 cerr << " view start " << startFrame
80 << " x0 " << x0 98 << " x0 " << x0
81 << " x1 " << x1 99 << " x1 " << x1
82 << endl; 100 << endl;
83
84 bool bufferIsBinResolution = useBinResolutionForDrawBuffer(v);
85
86 if (bufferIsBinResolution) {
87 // Rendering should be fast in this situation because we are
88 // quite well zoomed-in, and the sums are easier this
89 // way. Calculating boundaries later will be fiddly for
90 // partial paints otherwise.
91 timeConstrained = false;
92 }
93 101
94 if (m_cache.isValid()) { // some part of the cache is valid 102 if (m_cache.isValid()) { // some part of the cache is valid
95 103
96 if (v->getXForFrame(m_cache.getStartFrame()) == 104 if (v->getXForFrame(m_cache.getStartFrame()) ==
97 v->getXForFrame(startFrame) && 105 v->getXForFrame(startFrame) &&
143 x0 = int(x1 * 0.3); 151 x0 = int(x1 * 0.3);
144 } 152 }
145 } 153 }
146 154
147 if (m_cache.isValid()) { 155 if (m_cache.isValid()) {
148 cerr << "cache somewhat valid" << endl;
149 156
150 // When rendering only a part of the cache, we need to make 157 // When rendering only a part of the cache, we need to make
151 // sure that the part we're rendering is adjacent to (or 158 // sure that the part we're rendering is adjacent to (or
152 // overlapping) a valid area of cache, if we have one. The 159 // overlapping) a valid area of cache, if we have one. The
153 // alternative is to ditch the valid area of cache and render 160 // alternative is to ditch the valid area of cache and render
166 // sub-regions of our target region in right-to-left order in 173 // sub-regions of our target region in right-to-left order in
167 // order to ensure contiguity 174 // order to ensure contiguity
168 rightToLeft = isLeftOfValidArea; 175 rightToLeft = isLeftOfValidArea;
169 } 176 }
170 177
171 // Note, we always paint the full height. Smaller heights can be 178 // Note, we always paint the full height to cache. We want to
172 // used when painting direct from cache (outside this function), 179 // ensure the cache is coherent without having to worry about
173 // but we want to ensure the cache is coherent without having to 180 // vertical matching of required and valid areas as well as
174 // worry about vertical matching of required and valid areas as 181 // horizontal.
175 // well as horizontal. That's why this function didn't take any 182
176 // y/height parameters. 183 if (renderType == DrawBufferBinResolution) {
177 184
178 if (bufferIsBinResolution && (m_params.binDisplay != BinDisplay::PeakFrequencies)) {
179 renderToCacheBinResolution(v, x0, x1 - x0); 185 renderToCacheBinResolution(v, x0, x1 - x0);
180 } else { 186
187 } else { // must be DrawBufferPixelResolution, handled DirectTranslucent earlier
188
181 renderToCachePixelResolution(v, x0, x1 - x0, rightToLeft, timeConstrained); 189 renderToCachePixelResolution(v, x0, x1 - x0, rightToLeft, timeConstrained);
182 } 190 }
183 191
184 QRect pr = rect & m_cache.getValidArea(); 192 QRect pr = rect & m_cache.getValidArea();
185 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(), 193 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(),
210 //!!! fft model scaling? 218 //!!! fft model scaling?
211 219
212 //!!! should we own the Dense3DModelPeakCache here? or should it persist 220 //!!! should we own the Dense3DModelPeakCache here? or should it persist
213 } 221 }
214 222
215 bool 223 Colour3DPlotRenderer::RenderType
216 Colour3DPlotRenderer::useBinResolutionForDrawBuffer(LayerGeometryProvider *v) const 224 Colour3DPlotRenderer::decideRenderType(LayerGeometryProvider *v) const
217 { 225 {
218 const DenseThreeDimensionalModel *model = m_sources.source; 226 const DenseThreeDimensionalModel *model = m_sources.source;
219 if (!model) return false; 227 if (!model || !v || !(v->getViewManager())) {
228 return DrawBufferPixelResolution; // or anything
229 }
230
220 int binResolution = model->getResolution(); 231 int binResolution = model->getResolution();
221 int zoomLevel = v->getZoomLevel(); 232 int zoomLevel = v->getZoomLevel();
222 return (binResolution > zoomLevel); 233 sv_samplerate_t modelRate = model->getSampleRate();
234
235 double rateRatio = v->getViewManager()->getMainModelSampleRate() / modelRate;
236 double relativeBinResolution = binResolution * rateRatio;
237
238 if (m_params.binDisplay == BinDisplay::PeakFrequencies) {
239 // no alternative works here
240 return DrawBufferPixelResolution;
241 }
242
243 if (!m_params.alwaysOpaque && !m_params.interpolate) {
244
245 // consider translucent option -- only if not smoothing & not
246 // explicitly requested opaque & sufficiently zoomed-in
247
248 if (model->getHeight() < v->getPaintHeight() &&
249 relativeBinResolution >= 2 * zoomLevel) {
250 return DirectTranslucent;
251 }
252 }
253
254 if (relativeBinResolution > zoomLevel) {
255 return DrawBufferBinResolution;
256 } else {
257 return DrawBufferPixelResolution;
258 }
259 }
260
261 void
262 Colour3DPlotRenderer::renderDirectTranslucent(LayerGeometryProvider *v,
263 QPainter &paint,
264 QRect rect)
265 {
266 //!!! QPoint illuminatePos;
267 // bool illuminate = v->shouldIlluminateLocalFeatures
268 // (m_sources.verticalBinLayer, illuminatePos);
269
270 const DenseThreeDimensionalModel *model = m_sources.source;
271
272 int x0 = rect.left();
273 int x1 = rect.right() + 1;
274
275 int h = v->getPaintHeight();
276
277 sv_frame_t modelStart = model->getStartFrame();
278 sv_frame_t modelEnd = model->getEndFrame();
279 int modelResolution = model->getResolution();
280
281 double rateRatio =
282 v->getViewManager()->getMainModelSampleRate() / model->getSampleRate();
283
284 // the s-prefix values are source, i.e. model, column and bin numbers
285 int sx0 = int((double(v->getFrameForX(x0)) / rateRatio - double(modelStart))
286 / modelResolution);
287 int sx1 = int((double(v->getFrameForX(x1)) / rateRatio - double(modelStart))
288 / modelResolution);
289
290 int sh = model->getHeight();
291
292 const int buflen = 40;
293 char labelbuf[buflen];
294
295 int minbin = 0;
296 int maxbin = sh - 1; //!!!
297
298 int psx = -1;
299
300 vector<float> preparedColumn;
301
302 int modelWidth = model->getWidth();
303
304 for (int sx = sx0; sx <= sx1; ++sx) {
305
306 if (sx < 0 || sx >= modelWidth) {
307 continue;
308 }
309
310 if (sx != psx) {
311
312 //!!! this is in common with renderDrawBuffer - pull it out
313
314 // order:
315 // get column -> scale -> record extents ->
316 // normalise -> peak pick -> apply display gain ->
317 // distribute/interpolate
318
319 ColumnOp::Column fullColumn = model->getColumn(sx);
320
321 // cerr << "x " << x << ", sx " << sx << ", col height " << fullColumn.size()
322 // << ", minbin " << minbin << ", maxbin " << maxbin << endl;
323
324 ColumnOp::Column column =
325 vector<float>(fullColumn.data() + minbin,
326 fullColumn.data() + maxbin + 1);
327
328 //!!! fft scale if (m_colourScale != ColourScaleType::Phase) {
329 // column = ColumnOp::fftScale(column, m_fftSize);
330 // }
331
332 //!!! extents recordColumnExtents(column,
333 // sx,
334 // overallMag,
335 // overallMagChanged);
336
337 // if (m_colourScale != ColourScaleType::Phase) {
338 column = ColumnOp::normalize(column, m_params.normalization);
339 // }
340
341 if (m_params.binDisplay == BinDisplay::PeakBins) {
342 column = ColumnOp::peakPick(column);
343 }
344
345 preparedColumn = column; //!!! unnecessary dup
346
347 psx = sx;
348 }
349
350 sv_frame_t fx = sx * modelResolution + modelStart;
351
352 if (fx + modelResolution <= modelStart || fx > modelEnd) continue;
353
354 int rx0 = v->getXForFrame(int(double(fx) * rateRatio));
355 int rx1 = v->getXForFrame(int(double(fx + modelResolution + 1) * rateRatio));
356
357 int rw = rx1 - rx0;
358 if (rw < 1) rw = 1;
359
360 bool showLabel = (rw > 10 &&
361 paint.fontMetrics().width("0.000000") < rw - 3 &&
362 paint.fontMetrics().height() < (h / sh));
363
364 for (int sy = minbin; sy <= maxbin; ++sy) {
365
366 int ry0 = m_sources.verticalBinLayer->getIYForBin(v, sy);
367 int ry1 = m_sources.verticalBinLayer->getIYForBin(v, sy + 1);
368 QRect r(rx0, ry1, rw, ry0 - ry1);
369
370 float value = preparedColumn[sy - minbin];
371 QColor colour = m_params.colourScale.getColour(value, 0);//!!! +rotation
372
373 if (rw == 1) {
374 paint.setPen(colour);
375 paint.setBrush(Qt::NoBrush);
376 paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1);
377 continue;
378 }
379
380 QColor pen(255, 255, 255, 80);
381 QColor brush(colour);
382
383 if (rw > 3 && r.height() > 3) {
384 brush.setAlpha(160);
385 }
386
387 paint.setPen(Qt::NoPen);
388 paint.setBrush(brush);
389
390 //!!! if (illuminate) {
391 // if (r.contains(illuminatePos)) {
392 // paint.setPen(v->getForeground());
393 // }
394 // }
395
396 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
397 // cerr << "rect " << r.x() << "," << r.y() << " "
398 // << r.width() << "x" << r.height() << endl;
399 #endif
400
401 paint.drawRect(r);
402
403 if (showLabel) {
404 double value = model->getValueAt(sx, sy);
405 snprintf(labelbuf, buflen, "%06f", value);
406 QString text(labelbuf);
407 PaintAssistant::drawVisibleText
408 (v,
409 paint,
410 rx0 + 2,
411 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
412 text,
413 PaintAssistant::OutlinedText);
414 }
415 }
416 }
417
223 } 418 }
224 419
225 void 420 void
226 Colour3DPlotRenderer::renderToCachePixelResolution(LayerGeometryProvider *v, 421 Colour3DPlotRenderer::renderToCachePixelResolution(LayerGeometryProvider *v,
227 int x0, int repaintWidth, 422 int x0, int repaintWidth,