comparison layer/Colour3DPlotRenderer.cpp @ 1094:8a815776151c spectrogram-minor-refactor

Split out cache rendering functions and some fixes to calculations
author Chris Cannam
date Thu, 07 Jul 2016 19:18:31 +0100
parents c8c747783110
children ba62684a4512
comparison
equal deleted inserted replaced
1093:cd22f74dc159 1094:8a815776151c
23 #include "LayerGeometryProvider.h" 23 #include "LayerGeometryProvider.h"
24 #include "VerticalBinLayer.h" 24 #include "VerticalBinLayer.h"
25 25
26 #include <vector> 26 #include <vector>
27 27
28 //#define DEBUG_SPECTROGRAM_REPAINT 1
29
28 using namespace std; 30 using namespace std;
29 31
30 Colour3DPlotRenderer::RenderResult 32 Colour3DPlotRenderer::RenderResult
31 Colour3DPlotRenderer::render(LayerGeometryProvider *v, QPainter &paint, QRect rect) 33 Colour3DPlotRenderer::render(LayerGeometryProvider *v, QPainter &paint, QRect rect)
32 { 34 {
53 55
54 m_cache.resize(v->getPaintSize()); 56 m_cache.resize(v->getPaintSize());
55 m_cache.setZoomLevel(v->getZoomLevel()); 57 m_cache.setZoomLevel(v->getZoomLevel());
56 58
57 cerr << "cache start " << m_cache.getStartFrame() 59 cerr << "cache start " << m_cache.getStartFrame()
58 << " view start " << startFrame
59 << " valid left " << m_cache.getValidLeft() 60 << " valid left " << m_cache.getValidLeft()
60 << " valid right " << m_cache.getValidRight() 61 << " valid right " << m_cache.getValidRight()
62 << endl;
63 cerr << " view start " << startFrame
61 << " x0 " << x0 64 << " x0 " << x0
62 << " x1 " << x1 65 << " x1 " << x1
63 << endl; 66 << endl;
64 67
68 bool bufferIsBinResolution = useBinResolutionForDrawBuffer(v);
69
70 if (bufferIsBinResolution) {
71 // Rendering should be fast in this situation because we are
72 // quite well zoomed-in, and the sums are easier this
73 // way. Calculating boundaries later will be fiddly for
74 // partial paints otherwise.
75 timeConstrained = false;
76 }
65 77
66 if (m_cache.isValid()) { // some part of the cache is valid 78 if (m_cache.isValid()) { // some part of the cache is valid
67 79
68 if (v->getXForFrame(m_cache.getStartFrame()) == 80 if (v->getXForFrame(m_cache.getStartFrame()) ==
69 v->getXForFrame(startFrame) && 81 v->getXForFrame(startFrame) &&
138 // sub-regions of our target region in right-to-left order in 150 // sub-regions of our target region in right-to-left order in
139 // order to ensure contiguity 151 // order to ensure contiguity
140 rightToLeft = isLeftOfValidArea; 152 rightToLeft = isLeftOfValidArea;
141 } 153 }
142 154
143 renderToCache(v, x0, x1 - x0, rightToLeft, timeConstrained); 155 // Note, we always paint the full height. Smaller heights can be
156 // used when painting direct from cache (outside this function),
157 // but we want to ensure the cache is coherent without having to
158 // worry about vertical matching of required and valid areas as
159 // well as horizontal. That's why this function didn't take any
160 // y/height parameters.
161
162 if (bufferIsBinResolution) {
163 renderToCacheBinResolution(v, x0, x1 - x0);
164 } else {
165 renderToCachePixelResolution(v, x0, x1 - x0, rightToLeft, timeConstrained);
166 }
144 167
145 QRect pr = rect & m_cache.getValidArea(); 168 QRect pr = rect & m_cache.getValidArea();
146 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(), 169 paint.drawImage(pr.x(), pr.y(), m_cache.getImage(),
147 pr.x(), pr.y(), pr.width(), pr.height()); 170 pr.x(), pr.y(), pr.width(), pr.height());
148 171
173 //!!! fft model scaling? 196 //!!! fft model scaling?
174 197
175 //!!! should we own the Dense3DModelPeakCache here? or should it persist 198 //!!! should we own the Dense3DModelPeakCache here? or should it persist
176 } 199 }
177 200
201 bool
202 Colour3DPlotRenderer::useBinResolutionForDrawBuffer(LayerGeometryProvider *v) const
203 {
204 DenseThreeDimensionalModel *model = m_sources.source;
205 if (!model) return false;
206 int binResolution = model->getResolution();
207 int zoomLevel = v->getZoomLevel();
208 return (binResolution > zoomLevel);
209 }
210
178 void 211 void
179 Colour3DPlotRenderer::renderToCache(LayerGeometryProvider *v, 212 Colour3DPlotRenderer::renderToCachePixelResolution(LayerGeometryProvider *v,
180 int x0, int repaintWidth, 213 int x0, int repaintWidth,
181 bool rightToLeft, bool timeConstrained) 214 bool rightToLeft,
182 { 215 bool timeConstrained)
183 // Draw to the draw buffer, and then scale-copy from there. 216 {
217 cerr << "renderToCachePixelResolution" << endl;
218
219 // Draw to the draw buffer, and then copy from there. The draw
220 // buffer is at the same resolution as the target in the cache, so
221 // no extra scaling needed.
184 222
185 DenseThreeDimensionalModel *model = m_sources.source; 223 DenseThreeDimensionalModel *model = m_sources.source;
186 if (!model || !model->isOK() || !model->isReady()) { 224 if (!model || !model->isOK() || !model->isReady()) {
187 throw std::logic_error("no source model provided, or model not ready"); 225 throw std::logic_error("no source model provided, or model not ready");
188 } 226 }
189 227
190 // The draw buffer contains a fragment at either our pixel
191 // resolution (if there is more than one time-bin per pixel) or
192 // time-bin resolution (if a time-bin spans more than one pixel).
193 // We need to ensure that it starts and ends at points where a
194 // time-bin boundary occurs at an exact pixel boundary, and with a
195 // certain amount of overlap across existing pixels so that we can
196 // scale and draw from it without smoothing errors at the edges.
197
198 // If (getFrameForX(x) / increment) * increment ==
199 // getFrameForX(x), then x is a time-bin boundary. We want two
200 // such boundaries at either side of the draw buffer -- one which
201 // we draw up to, and one which we subsequently crop at.
202
203 bool bufferIsBinResolution = false;
204 int binResolution = model->getResolution();
205 int zoomLevel = v->getZoomLevel();
206 if (binResolution > zoomLevel) bufferIsBinResolution = true;
207
208 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
209 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
210
211 int drawWidth;
212
213 if (bufferIsBinResolution) {
214 for (int x = x0; ; --x) {
215 sv_frame_t f = v->getFrameForX(x);
216 if ((f / binResolution) * binResolution == f) {
217 if (leftCropFrame == -1) leftCropFrame = f;
218 else if (x < x0 - 2) {
219 leftBoundaryFrame = f;
220 break;
221 }
222 }
223 }
224 for (int x = x0 + repaintWidth; ; ++x) {
225 sv_frame_t f = v->getFrameForX(x);
226 if ((f / binResolution) * binResolution == f) {
227 if (rightCropFrame == -1) rightCropFrame = f;
228 else if (x > x0 + repaintWidth + 2) {
229 rightBoundaryFrame = f;
230 break;
231 }
232 }
233 }
234 drawWidth = int((rightBoundaryFrame - leftBoundaryFrame) / binResolution);
235 } else {
236 drawWidth = repaintWidth;
237 }
238
239 // We always paint the full height. Smaller heights can be used
240 // when painting direct from cache (outside this function), but we
241 // want to ensure the cache is coherent without having to worry
242 // about vertical matching of required and valid areas as well as
243 // horizontal. That's why this function didn't take any y/height
244 // parameters.
245 int h = v->getPaintHeight(); 228 int h = v->getPaintHeight();
246 229
247 clearDrawBuffer(drawWidth, h); 230 clearDrawBuffer(repaintWidth, h);
248 231
249 vector<int> binforx(drawWidth); 232 vector<int> binforx(repaintWidth);
250 vector<double> binfory(h); 233 vector<double> binfory(h);
251 234
252 bool usePeaksCache = false; 235 bool usePeaksCache = false;
253 int binsPerPeak = 1; 236 int binsPerPeak = 1;
254 237 int zoomLevel = v->getZoomLevel();
255 if (bufferIsBinResolution) { 238 int binResolution = model->getResolution();
256 239
257 for (int x = 0; x < drawWidth; ++x) { 240 for (int x = 0; x < repaintWidth; ++x) {
258 binforx[x] = int(leftBoundaryFrame / binResolution) + x; 241 sv_frame_t f0 = v->getFrameForX(x0 + x);
259 } 242 double s0 = double(f0 - model->getStartFrame()) / binResolution;
260 243 binforx[x] = int(s0 + 0.0001);
261 // calculating boundaries later will be too fiddly for partial 244 }
262 // paints, and painting should be fast anyway when this is the 245
263 // case because it means we're well zoomed in 246 if (m_sources.peaks) { // peaks cache exists
264 timeConstrained = false; 247
265 248 binsPerPeak = m_sources.peaks->getColumnsPerPeak();
266 } else { 249 usePeaksCache = (binResolution * binsPerPeak) < zoomLevel;
267 for (int x = 0; x < drawWidth; ++x) { 250
268 sv_frame_t f0 = v->getFrameForX(x0 + x); 251 if (m_params.colourScale.getScale() ==
269 double s0 = double(f0 - model->getStartFrame()) / binResolution; 252 ColourScale::PhaseColourScale) {
270 binforx[x] = int(s0 + 0.0001); 253 usePeaksCache = false;
271 } 254 }
272 255 }
273 if (m_sources.peaks) { // peaks cache exists 256
274 257 cerr << "[PIX] zoomLevel = " << zoomLevel
275 binsPerPeak = m_sources.peaks->getColumnsPerPeak(); 258 << ", binResolution " << binResolution
276 usePeaksCache = (binResolution * binsPerPeak) < zoomLevel; 259 << ", binsPerPeak " << binsPerPeak
277 260 << ", peak cache " << m_sources.peaks
278 if (m_params.colourScale.getScale() == 261 << ", usePeaksCache = " << usePeaksCache
279 ColourScale::PhaseColourScale) { 262 << endl;
280 usePeaksCache = false; 263
281 }
282 }
283 }
284
285 for (int y = 0; y < h; ++y) { 264 for (int y = 0; y < h; ++y) {
286 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1); 265 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
287 } 266 }
288 267
289 int attainedWidth = renderDrawBuffer(repaintWidth, 268 int attainedWidth = renderDrawBuffer(repaintWidth,
292 binfory, 271 binfory,
293 usePeaksCache, 272 usePeaksCache,
294 rightToLeft, 273 rightToLeft,
295 timeConstrained); 274 timeConstrained);
296 275
297 //!!! now scale-copy to cache
298
299 if (attainedWidth == 0) return; 276 if (attainedWidth == 0) return;
277
278 // draw buffer is pixel resolution, no scaling factors or padding involved
300 279
301 int paintedLeft = x0; 280 int paintedLeft = x0;
302 if (rightToLeft) { 281 if (rightToLeft) {
303 paintedLeft += (repaintWidth - attainedWidth); 282 paintedLeft += (repaintWidth - attainedWidth);
304 } 283 }
305 284
306 if (bufferIsBinResolution) { 285 m_cache.drawImage(paintedLeft, attainedWidth,
307 286 m_drawBuffer,
308 int scaledLeft = v->getXForFrame(leftBoundaryFrame); 287 paintedLeft - x0, attainedWidth);
309 int scaledRight = v->getXForFrame(rightBoundaryFrame); 288 }
310 289
311 QImage scaled = m_drawBuffer.scaled 290 void
312 (scaledRight - scaledLeft, h, 291 Colour3DPlotRenderer::renderToCacheBinResolution(LayerGeometryProvider *v,
313 Qt::IgnoreAspectRatio, (m_params.interpolate ? 292 int x0, int repaintWidth)
314 Qt::SmoothTransformation : 293 {
315 Qt::FastTransformation)); 294 cerr << "renderToCacheBinResolution" << endl;
295
296 // Draw to the draw buffer, and then scale-copy from there. Draw
297 // buffer is at bin resolution, i.e. buffer x == source column
298 // number. We use toolkit smooth scaling for interpolation.
299
300 DenseThreeDimensionalModel *model = m_sources.source;
301 if (!model || !model->isOK() || !model->isReady()) {
302 throw std::logic_error("no source model provided, or model not ready");
303 }
304
305 // The draw buffer will contain a fragment at bin resolution. We
306 // need to ensure that it starts and ends at points where a
307 // time-bin boundary occurs at an exact pixel boundary, and with a
308 // certain amount of overlap across existing pixels so that we can
309 // scale and draw from it without smoothing errors at the edges.
310
311 // If (getFrameForX(x) / increment) * increment ==
312 // getFrameForX(x), then x is a time-bin boundary. We want two
313 // such boundaries at either side of the draw buffer -- one which
314 // we draw up to, and one which we subsequently crop at.
315
316 sv_frame_t leftBoundaryFrame = -1, leftCropFrame = -1;
317 sv_frame_t rightBoundaryFrame = -1, rightCropFrame = -1;
318
319 int drawBufferWidth;
320 int binResolution = model->getResolution();
321
322 for (int x = x0; ; --x) {
323 sv_frame_t f = v->getFrameForX(x);
324 if ((f / binResolution) * binResolution == f) {
325 if (leftCropFrame == -1) leftCropFrame = f;
326 else if (x < x0 - 2) {
327 leftBoundaryFrame = f;
328 break;
329 }
330 }
331 }
332 for (int x = x0 + repaintWidth; ; ++x) {
333 sv_frame_t f = v->getFrameForX(x);
334 if ((f / binResolution) * binResolution == f) {
335 if (rightCropFrame == -1) rightCropFrame = f;
336 else if (x > x0 + repaintWidth + 2) {
337 rightBoundaryFrame = f;
338 break;
339 }
340 }
341 }
342 drawBufferWidth = int
343 ((rightBoundaryFrame - leftBoundaryFrame) / binResolution);
344
345 int h = v->getPaintHeight();
346
347 clearDrawBuffer(drawBufferWidth, h);
348
349 vector<int> binforx(drawBufferWidth);
350 vector<double> binfory(h);
351
352 for (int x = 0; x < drawBufferWidth; ++x) {
353 binforx[x] = int(leftBoundaryFrame / binResolution) + x;
354 }
355
356 cerr << "[BIN] binResolution " << binResolution
357 << endl;
358
359 for (int y = 0; y < h; ++y) {
360 binfory[y] = m_sources.verticalBinLayer->getBinForY(v, h - y - 1);
361 }
362
363 int attainedWidth = renderDrawBuffer(drawBufferWidth,
364 h,
365 binforx,
366 binfory,
367 false,
368 false,
369 false);
370
371 if (attainedWidth == 0) return;
372
373 int scaledLeft = v->getXForFrame(leftBoundaryFrame);
374 int scaledRight = v->getXForFrame(rightBoundaryFrame);
375
376 QImage scaled = m_drawBuffer.scaled
377 (scaledRight - scaledLeft, h,
378 Qt::IgnoreAspectRatio, (m_params.interpolate ?
379 Qt::SmoothTransformation :
380 Qt::FastTransformation));
316 381
317 int scaledLeftCrop = v->getXForFrame(leftCropFrame); 382 int scaledLeftCrop = v->getXForFrame(leftCropFrame);
318 int scaledRightCrop = v->getXForFrame(rightCropFrame); 383 int scaledRightCrop = v->getXForFrame(rightCropFrame);
319 384
320 int targetLeft = scaledLeftCrop; 385 int targetLeft = scaledLeftCrop;
321 if (targetLeft < 0) { 386 if (targetLeft < 0) {
322 targetLeft = 0; 387 targetLeft = 0;
323 } 388 }
324 389
325 int targetWidth = scaledRightCrop - targetLeft; 390 int targetWidth = scaledRightCrop - targetLeft;
326 if (targetLeft + targetWidth > m_cache.getSize().width()) { 391 if (targetLeft + targetWidth > m_cache.getSize().width()) {
327 targetWidth = m_cache.getSize().width() - targetLeft; 392 targetWidth = m_cache.getSize().width() - targetLeft;
328 } 393 }
329 394
330 int sourceLeft = targetLeft - scaledLeft; 395 int sourceLeft = targetLeft - scaledLeft;
331 if (sourceLeft < 0) { 396 if (sourceLeft < 0) {
332 sourceLeft = 0; 397 sourceLeft = 0;
333 } 398 }
334 399
335 int sourceWidth = targetWidth; 400 int sourceWidth = targetWidth;
336 401
337 if (targetWidth > 0) { 402 cerr << "repaintWidth = " << repaintWidth
338 m_cache.drawImage(targetLeft, targetWidth, 403 << ", targetWidth = " << targetWidth << endl;
339 scaled, 404
340 sourceLeft, sourceWidth); 405 if (targetWidth > 0) {
341 } 406 m_cache.drawImage(targetLeft, targetWidth,
342 407 scaled,
343 } else { 408 sourceLeft, sourceWidth);
344
345 m_cache.drawImage(paintedLeft, attainedWidth,
346 m_drawBuffer,
347 paintedLeft - x0, attainedWidth);
348 } 409 }
349 } 410 }
350 411
351 int 412 int
352 Colour3DPlotRenderer::renderDrawBuffer(int w, int h, 413 Colour3DPlotRenderer::renderDrawBuffer(int w, int h,
389 } 450 }
390 451
391 int columnCount = 0; 452 int columnCount = 0;
392 453
393 vector<float> preparedColumn; 454 vector<float> preparedColumn;
455
456 int modelWidth = sourceModel->getWidth();
457 cerr << "modelWidth " << modelWidth << endl;
394 458
395 for (int x = start; x != finish; x += step) { 459 for (int x = start; x != finish; x += step) {
396 460
397 // x is the on-canvas pixel coord; sx (later) will be the 461 // x is the on-canvas pixel coord; sx (later) will be the
398 // source column index 462 // source column index
411 vector<float> pixelPeakColumn; 475 vector<float> pixelPeakColumn;
412 476
413 for (int sx = sx0; sx < sx1; ++sx) { 477 for (int sx = sx0; sx < sx1; ++sx) {
414 478
415 #ifdef DEBUG_SPECTROGRAM_REPAINT 479 #ifdef DEBUG_SPECTROGRAM_REPAINT
416 // cerr << "sx = " << sx << endl; 480 cerr << "sx = " << sx << endl;
417 #endif 481 #endif
418 482
419 if (sx < 0 || sx >= sourceModel->getWidth()) { 483 if (sx < 0 || sx >= modelWidth) {
420 continue; 484 continue;
421 } 485 }
422 486
423 if (sx != psx) { 487 if (sx != psx) {
424 488
427 // normalise -> peak pick -> apply display gain -> 491 // normalise -> peak pick -> apply display gain ->
428 // distribute/interpolate 492 // distribute/interpolate
429 493
430 ColumnOp::Column fullColumn = sourceModel->getColumn(sx); 494 ColumnOp::Column fullColumn = sourceModel->getColumn(sx);
431 495
432 cerr << "x " << x << ", sx " << sx << ", col height " << fullColumn.size() 496 // cerr << "x " << x << ", sx " << sx << ", col height " << fullColumn.size()
433 << ", minbin " << minbin << ", maxbin " << maxbin << endl; 497 // << ", minbin " << minbin << ", maxbin " << maxbin << endl;
434 498
435 ColumnOp::Column column = 499 ColumnOp::Column column =
436 vector<float>(fullColumn.data() + minbin, 500 vector<float>(fullColumn.data() + minbin,
437 fullColumn.data() + maxbin + 1); 501 fullColumn.data() + maxbin + 1);
438 502