Skip to content

Commit afe1d71

Browse files
committed
Optimize LFP Viewer bitmap rendering for high channel count streams
1 parent 530d26d commit afe1d71

3 files changed

Lines changed: 113 additions & 49 deletions

File tree

Plugins/LfpViewer/LfpChannelDisplay.cpp

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,18 @@ void LfpChannelDisplay::pxPaint()
132132
Graphics overlayGraphics (display->lfpChannelBitmap);
133133

134134
int center = getHeight() / 2;
135+
const int channelTop = getY() - display->getChannelBitmapYOrigin();
135136

136137
//int ifrom = canvasSplit->lastScreenBufferIndex[0]; // base everything on the first channel
137138
//int ito = screenBufferIndex;
138139

139140
// max and min of channel in absolute px coords for event displays etc - actual data might be drawn outside of this range
140-
int jfrom_wholechannel = (int) (getY() + center - channelHeight / 2) + 1 + 0;
141-
int jto_wholechannel = (int) (getY() + center + channelHeight / 2) - 0;
141+
int jfrom_wholechannel = (int) (channelTop + center - channelHeight / 2) + 1 + 0;
142+
int jto_wholechannel = (int) (channelTop + center + channelHeight / 2) - 0;
142143

143144
// max and min of channel, this is the range where actual data is drawn
144-
int jfrom_wholechannel_clip = (int) (getY() + center - (channelHeight) *canvasSplit->channelOverlapFactor) + 1;
145-
int jto_wholechannel_clip = (int) (getY() + center + (channelHeight) *canvasSplit->channelOverlapFactor) - 0;
145+
int jfrom_wholechannel_clip = (int) (channelTop + center - (channelHeight) *canvasSplit->channelOverlapFactor) + 1;
146+
int jto_wholechannel_clip = (int) (channelTop + center + (channelHeight) *canvasSplit->channelOverlapFactor) - 0;
146147

147148
if (jfrom_wholechannel < 0)
148149
{
@@ -196,27 +197,31 @@ void LfpChannelDisplay::pxPaint()
196197
LfpBitmapPlotterInfo plotterInfo; // hold and pass plotting info for each plotting method class
197198

198199
const bool showRangeMarkers = isSelected || display->getSingleChannelState();
200+
const bool showMidline = channelHeight > 4;
199201

200202
for (int ii = ifrom; ii <= endIndex; ii++)
201203
{
202204
int i = (ifrom_local + ii - ifrom) % getWidth();
203205
int index = ii % canvasSplit->screenBufferWidth;
204206

205207
//draw zero line
206-
int m = getY() + center;
207-
208-
if (m > 0 && m < display->lfpChannelBitmap.getHeight())
208+
if (showMidline)
209209
{
210-
bdLfpChannelBitmap.setPixelColour (i, m, Colour (50, 50, 50));
210+
int m = channelTop + center;
211+
212+
if (m > 0 && m < display->lfpChannelBitmap.getHeight())
213+
{
214+
bdLfpChannelBitmap.setPixelColour (i, m, Colour (50, 50, 50));
215+
}
211216
}
212217

213218
//draw range markers
214219
if (showRangeMarkers)
215220
{
216-
int start = getY() + center - channelHeight / 2;
221+
int start = channelTop + center - channelHeight / 2;
217222
int jump = jmax (1, channelHeight / 4);
218223

219-
for (m = start; m <= start + jump * 4; m += jump)
224+
for (int m = start; m <= start + jump * 4; m += jump)
220225
{
221226
if (m > 0 && m < display->lfpChannelBitmap.getHeight())
222227
{
@@ -228,7 +233,8 @@ void LfpChannelDisplay::pxPaint()
228233

229234
// draw event markers
230235
const int rawEventState = canvasSplit->getEventState (index); // get event state
231-
drawEventOverlay (rawEventState, i, jfrom_wholechannel, jto_wholechannel, overlayGraphics); // draw event markers
236+
if (rawEventState != 0)
237+
drawEventOverlay (rawEventState, i, jfrom_wholechannel, jto_wholechannel, overlayGraphics); // draw event markers
232238

233239
// get y-coordinates for plotting
234240
double a_raw = canvasSplit->getYCoordMax (chan, index);
@@ -317,7 +323,7 @@ void LfpChannelDisplay::pxPaint()
317323
int samplerange = to - from;
318324

319325
plotterInfo.channelID = chan;
320-
plotterInfo.y = getY();
326+
plotterInfo.y = channelTop;
321327
plotterInfo.from = from;
322328
plotterInfo.to = to;
323329
plotterInfo.samp = i;
@@ -408,14 +414,15 @@ void LfpChannelDisplay::pxPaintHistory (int playhead, int rightEdge, int maxScre
408414
Graphics overlayGraphics (display->lfpChannelBitmap);
409415

410416
int center = getHeight() / 2;
417+
const int channelTop = getY() - display->getChannelBitmapYOrigin();
411418

412419
// max and min of channel in absolute px coords for event displays etc - actual data might be drawn outside of this range
413-
int jfrom_wholechannel = (int) (getY() + center - channelHeight / 2) + 1 + 0;
414-
int jto_wholechannel = (int) (getY() + center + channelHeight / 2) - 0;
420+
int jfrom_wholechannel = (int) (channelTop + center - channelHeight / 2) + 1 + 0;
421+
int jto_wholechannel = (int) (channelTop + center + channelHeight / 2) - 0;
415422

416423
// max and min of channel, this is the range where actual data is drawn
417-
int jfrom_wholechannel_clip = (int) (getY() + center - (channelHeight) *canvasSplit->channelOverlapFactor) + 1;
418-
int jto_wholechannel_clip = (int) (getY() + center + (channelHeight) *canvasSplit->channelOverlapFactor) - 0;
424+
int jfrom_wholechannel_clip = (int) (channelTop + center - (channelHeight) *canvasSplit->channelOverlapFactor) + 1;
425+
int jto_wholechannel_clip = (int) (channelTop + center + (channelHeight) *canvasSplit->channelOverlapFactor) - 0;
419426

420427
if (jfrom_wholechannel < 0)
421428
{
@@ -453,6 +460,7 @@ void LfpChannelDisplay::pxPaintHistory (int playhead, int rightEdge, int maxScre
453460
LfpBitmapPlotterInfo plotterInfo; // hold and pass plotting info for each plotting method class
454461

455462
const bool showRangeMarkers = isSelected || display->getSingleChannelState();
463+
const bool showMidline = channelHeight > 4;
456464

457465
for (int ii = 0; ii < rightEdge; ii++)
458466
{
@@ -489,22 +497,25 @@ void LfpChannelDisplay::pxPaintHistory (int playhead, int rightEdge, int maxScre
489497
// std::cout << "First screenBufferIndex: " << index << std::endl;
490498

491499
//draw zero line
492-
int m = getY() + center;
493-
494-
if (m > 0 && m < display->lfpChannelBitmap.getHeight())
500+
if (showMidline)
495501
{
496-
//if (bdLfpChannelBitmap.getPixelColour(i, m).isTransparent()) { // make sure we're not drawing over an existing plot from another channel
497-
bdLfpChannelBitmap.setPixelColour (i, m, Colour (50, 50, 50));
498-
// }
502+
int m = channelTop + center;
503+
504+
if (m > 0 && m < display->lfpChannelBitmap.getHeight())
505+
{
506+
//if (bdLfpChannelBitmap.getPixelColour(i, m).isTransparent()) { // make sure we're not drawing over an existing plot from another channel
507+
bdLfpChannelBitmap.setPixelColour (i, m, Colour (50, 50, 50));
508+
// }
509+
}
499510
}
500511

501512
//draw range markers
502513
if (showRangeMarkers)
503514
{
504-
int start = getY() + center - channelHeight / 2;
515+
int start = channelTop + center - channelHeight / 2;
505516
int jump = jmax (1, channelHeight / 4);
506517

507-
for (m = start; m <= start + jump * 4; m += jump)
518+
for (int m = start; m <= start + jump * 4; m += jump)
508519
{
509520
if (m > 0 && m < display->lfpChannelBitmap.getHeight())
510521
{
@@ -515,7 +526,8 @@ void LfpChannelDisplay::pxPaintHistory (int playhead, int rightEdge, int maxScre
515526

516527
// draw event markers
517528
const int rawEventState = canvasSplit->getEventState (index); // get event state
518-
drawEventOverlay (rawEventState, i, jfrom_wholechannel, jto_wholechannel, overlayGraphics); // draw event markers
529+
if (rawEventState != 0)
530+
drawEventOverlay (rawEventState, i, jfrom_wholechannel, jto_wholechannel, overlayGraphics); // draw event markers
519531

520532
// get y max-min for plotting
521533
double a_raw = canvasSplit->getYCoordMax (chan, index);
@@ -604,7 +616,7 @@ void LfpChannelDisplay::pxPaintHistory (int playhead, int rightEdge, int maxScre
604616
int samplerange = to - from;
605617

606618
plotterInfo.channelID = chan;
607-
plotterInfo.y = getY();
619+
plotterInfo.y = channelTop;
608620
plotterInfo.from = from;
609621
plotterInfo.to = to;
610622
plotterInfo.samp = i;

Plugins/LfpViewer/LfpDisplay.cpp

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,59 @@ int LfpDisplay::getTotalHeight()
350350
return totalHeight;
351351
}
352352

353+
int LfpDisplay::getChannelBitmapYOrigin() const
354+
{
355+
return channelBitmapYOrigin;
356+
}
357+
358+
Rectangle<int> LfpDisplay::getChannelBitmapWindowBounds()
359+
{
360+
const int bitmapWidth = jmax (1, getWidth() - canvasSplit->leftmargin);
361+
const int componentHeight = jmax (1, getHeight());
362+
363+
if (viewport == nullptr || viewport->getViewHeight() <= 0)
364+
return Rectangle<int> (0, 0, bitmapWidth, jmin (componentHeight, 10));
365+
366+
const int channelHeightForMargin = drawableChannels.size() > 0 ? drawableChannels[0]->getHeight() : 30;
367+
const int margin = jmax (64, channelHeightForMargin * 2);
368+
const int viewTop = jlimit (0, componentHeight, viewport->getViewPositionY());
369+
const int viewBottom = jlimit (0, componentHeight, viewTop + jmax (1, viewport->getViewHeight()));
370+
371+
if (! lfpChannelBitmap.isNull()
372+
&& lfpChannelBitmap.getWidth() == bitmapWidth
373+
&& channelBitmapYOrigin >= 0
374+
&& channelBitmapYOrigin <= viewTop
375+
&& channelBitmapYOrigin + lfpChannelBitmap.getHeight() >= viewBottom
376+
&& channelBitmapYOrigin + lfpChannelBitmap.getHeight() <= componentHeight)
377+
{
378+
return Rectangle<int> (0, channelBitmapYOrigin, bitmapWidth, lfpChannelBitmap.getHeight());
379+
}
380+
381+
const int bitmapTop = jmax (0, viewTop - margin);
382+
const int bitmapBottom = jmin (componentHeight, viewBottom + margin);
383+
384+
return Rectangle<int> (0, bitmapTop, bitmapWidth, jmax (1, bitmapBottom - bitmapTop));
385+
}
386+
387+
void LfpDisplay::ensureChannelBitmapForVisibleArea()
388+
{
389+
const auto bitmapBounds = getChannelBitmapWindowBounds();
390+
391+
if (lfpChannelBitmap.isNull()
392+
|| lfpChannelBitmap.getWidth() != bitmapBounds.getWidth()
393+
|| lfpChannelBitmap.getHeight() != bitmapBounds.getHeight()
394+
|| channelBitmapYOrigin != bitmapBounds.getY())
395+
{
396+
lfpChannelBitmap = Image (Image::ARGB,
397+
bitmapBounds.getWidth(),
398+
bitmapBounds.getHeight(),
399+
true,
400+
SoftwareImageType());
401+
channelBitmapYOrigin = bitmapBounds.getY();
402+
canvasSplit->fullredraw = true;
403+
}
404+
}
405+
353406
void LfpDisplay::restoreViewPosition()
354407
{
355408
if (! getSingleChannelState())
@@ -362,11 +415,6 @@ void LfpDisplay::resized()
362415

363416
//LOGD(" !! LFP DISPLAY RESIZED TO: ", getWidth(), " pixels.");
364417

365-
if (getWidth() > 0 && getHeight() > 0)
366-
lfpChannelBitmap = Image (Image::ARGB, getWidth() - canvasSplit->leftmargin, getHeight(), true, SoftwareImageType());
367-
else
368-
lfpChannelBitmap = Image (Image::ARGB, 10, 10, true, SoftwareImageType());
369-
370418
if (getWidth() == 0)
371419
{
372420
//LOGD(" ::: Not visible, returning.");
@@ -405,6 +453,7 @@ void LfpDisplay::resized()
405453
}
406454

407455
canvasSplit->fullredraw = true;
456+
ensureChannelBitmapForVisibleArea();
408457

409458
//LOGD(" RESIZED IN: ", MS_FROM_START, " milliseconds");
410459
start = Time::getHighResolutionTicks();
@@ -422,9 +471,10 @@ void LfpDisplay::resized()
422471

423472
void LfpDisplay::paint (Graphics& g)
424473
{
425-
// g.drawImageAt(lfpChannelBitmap, canvasSplit->leftmargin, 0);
426-
auto viewArea = viewport->getViewArea();
427-
g.drawImage (lfpChannelBitmap, canvasSplit->leftmargin, viewArea.getY(), getWidth() - canvasSplit->leftmargin, viewArea.getHeight(), 0, viewArea.getY(), lfpChannelBitmap.getWidth(), viewArea.getHeight());
474+
if (lfpChannelBitmap.isNull())
475+
return;
476+
477+
g.drawImageAt (lfpChannelBitmap, canvasSplit->leftmargin, channelBitmapYOrigin);
428478
}
429479

430480
void LfpDisplay::sync()
@@ -441,17 +491,13 @@ void LfpDisplay::refresh()
441491
if (numChans == 0)
442492
return;
443493

444-
// Ensure the lfpChannelBitmap has been initialized
445-
if (lfpChannelBitmap.isNull() || lfpChannelBitmap.getWidth() < getWidth() - canvasSplit->leftmargin)
446-
{
447-
resized();
448-
}
494+
ensureChannelBitmapForVisibleArea();
449495

450496
int totalXPixels = lfpChannelBitmap.getWidth();
451497
int totalYPixels = lfpChannelBitmap.getHeight();
452498

453-
int topBorder = viewport->getViewPositionY();
454-
int bottomBorder = viewport->getViewHeight() + topBorder;
499+
int topBorder = channelBitmapYOrigin;
500+
int bottomBorder = topBorder + totalYPixels;
455501

456502
//std::cout << "refresh display " << std::endl;
457503

@@ -581,14 +627,6 @@ void LfpDisplay::refresh()
581627
// fillfrom_local << " : " << fillto_local << " :: " << totalPixelsToFill << " ::: " << totalXPixels << std::endl;
582628
// }
583629

584-
for (int i = 0; i < numChans; i++)
585-
{
586-
channels[i]->ifrom = channelFillFrom; // canvasSplit->lastScreenBufferIndex[0];
587-
channels[i]->ito = channelFillTo; // canvasSplit->screenBufferIndex[0];
588-
channels[i]->ifrom_local = fillfrom_local;
589-
channels[i]->ito_local = fillto_local;
590-
}
591-
592630
if (fillfrom_local < fillto_local)
593631
{
594632
int x1 = fillfrom_local;
@@ -620,6 +658,10 @@ void LfpDisplay::refresh()
620658

621659
if ((topBorder <= componentBottom && bottomBorder >= componentTop)) // only draw things that are visible
622660
{
661+
drawableChannels[i]->ifrom = channelFillFrom; // canvasSplit->lastScreenBufferIndex[0];
662+
drawableChannels[i]->ito = channelFillTo; // canvasSplit->screenBufferIndex[0];
663+
drawableChannels[i]->ifrom_local = fillfrom_local;
664+
drawableChannels[i]->ito_local = fillto_local;
623665
drawableChannels[i]->pxPaint(); // draws to lfpChannelBitmap
624666
}
625667
}

Plugins/LfpViewer/LfpDisplay.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ class LfpDisplay : public Component,
241241
/** Returns a const pointer to the internally managed plotter method class */
242242
LfpBitmapPlotter* const getPlotterPtr() const;
243243

244+
/** Top edge, in component coordinates, represented by lfpChannelBitmap. */
245+
int getChannelBitmapYOrigin() const;
246+
244247
/** Current background colour (based on the selected colour scheme)*/
245248
Colour backgroundColour;
246249

@@ -297,6 +300,12 @@ class LfpDisplay : public Component,
297300
/** Used to throttle refresh speed when scrolling backwards */
298301
void timerCallback() override;
299302

303+
/** Keeps lfpChannelBitmap sized to the visible viewport plus a small vertical margin. */
304+
void ensureChannelBitmapForVisibleArea();
305+
306+
/** Returns the component-space bounds represented by lfpChannelBitmap. */
307+
Rectangle<int> getChannelBitmapWindowBounds();
308+
300309
int singleChan;
301310

302311
int pausePoint;
@@ -320,6 +329,7 @@ class LfpDisplay : public Component,
320329
int scrollY;
321330

322331
int totalHeight;
332+
int channelBitmapYOrigin = 0;
323333

324334
String colourGrouping;
325335

0 commit comments

Comments
 (0)