Skip to content

Commit

Permalink
#1343 Resolves issue where user-initiated FFT size change causes wate…
Browse files Browse the repository at this point in the history
…rfall panel pixel array to be updated/resized while a memory image source repaint has been requested and the parameters become incongruent, causing an array index out of bounds error. Pushes the pixel array update operation onto the swing thread to ensure only a single thread is modifying the pixel map for rendering. (#1344)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
  • Loading branch information
DSheirer and Dennis Sheirer committed Nov 24, 2022
1 parent a24c373 commit 1b062bc
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 86 deletions.
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

142 changes: 57 additions & 85 deletions src/main/java/io/github/dsheirer/spectrum/WaterfallPanel.java
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 Dennis Sheirer
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.spectrum;

import io.github.dsheirer.settings.ColorSetting;
import io.github.dsheirer.settings.ColorSetting.ColorSettingName;
import io.github.dsheirer.settings.Setting;
import io.github.dsheirer.settings.SettingChangeListener;
import io.github.dsheirer.settings.SettingsManager;
import org.apache.commons.math3.util.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.JPanel;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
Expand All @@ -38,6 +34,11 @@
import java.awt.image.MemoryImageSource;
import java.text.DecimalFormat;
import java.util.Arrays;
import org.apache.commons.math3.util.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.JPanel;

public class WaterfallPanel extends JPanel implements DFTResultsListener,
Pausable,
Expand Down Expand Up @@ -81,13 +82,9 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
public WaterfallPanel(SettingsManager settingsManager)
{
super();

mSettingsManager = settingsManager;

mSettingsManager.addListener(this);

mColorSpectrumCursor = getColor(ColorSettingName.SPECTRUM_CURSOR);

reset();
}

Expand Down Expand Up @@ -172,7 +169,6 @@ public boolean isDisabled()
* 6 64x Zoom
*
* @param zoom level, 0 - 6.
* @param offset into the DFT bins for display
*/
public void setZoom(int zoom)
{
Expand Down Expand Up @@ -215,12 +211,9 @@ private Color getColor(ColorSettingName name)
@Override
public void settingChanged(Setting setting)
{
if(setting instanceof ColorSetting)
if(setting instanceof ColorSetting colorSetting)
{
ColorSetting colorSetting = (ColorSetting)setting;

if(((ColorSetting)setting).getColorSettingName() ==
ColorSettingName.SPECTRUM_CURSOR)
if(colorSetting.getColorSettingName() == ColorSettingName.SPECTRUM_CURSOR)
{
mColorSpectrumCursor = colorSetting.getColor();
}
Expand All @@ -240,7 +233,6 @@ public void settingDeleted(Setting setting)
public void setCursorLocation(Point point)
{
mCursorLocation = point;

repaint();
}

Expand All @@ -263,7 +255,6 @@ public void setCursorFrequency(long frequency)
public void setCursorVisible(boolean visible)
{
mCursorVisible = visible;

repaint();
}

Expand All @@ -281,7 +272,6 @@ private double getPixelOffset(int multiplier)
if(mZoom != 0)
{
double binPixelWidth = getBinPixelWidth(multiplier);

offset = -binPixelWidth * (double)(mDFTZoomWindowOffset);
}

Expand All @@ -303,18 +293,9 @@ public void paintComponent(Graphics g)
int multiplier = getZoomMultiplier();

double binPixelWidth = getBinPixelWidth(multiplier);

int offset = (int)(getPixelOffset(multiplier) - binPixelWidth);

g.drawImage(mWaterfallImage,
offset,
0,
(getWidth() * multiplier) + (int)binPixelWidth,
mImageHeight,
this);

g.drawImage(mWaterfallImage, offset, 0, (getWidth() * multiplier) + (int)binPixelWidth, mImageHeight, this);
Graphics2D graphics = (Graphics2D)g;

graphics.setColor(mColorSpectrumCursor);

if(mCursorVisible)
Expand All @@ -334,7 +315,6 @@ else if(mPaused)
}

paintZoomIndicator(graphics);

graphics.dispose();
}

Expand All @@ -348,50 +328,35 @@ private void paintZoomIndicator(Graphics2D graphics)
if(mZoom != 0)
{
int width = getWidth() / 4;

int x = (getWidth() / 2) - (width / 2);

//Draw the outer window
graphics.drawRect(x, getHeight() - 12, width, 10);

int zoomWidth = width / getZoomMultiplier();

int windowOffset = 0;

if(mDFTZoomWindowOffset != 0)
{
windowOffset = (int)(((double)mDFTZoomWindowOffset /
(double)mDFTSize) * width);
windowOffset = (int)(((double)mDFTZoomWindowOffset / (double)mDFTSize) * width);
}

//Draw the zoom window
graphics.fillRect(x + windowOffset, getHeight() - 12, zoomWidth, 10);

//Draw the zoom text
graphics.drawString("Zoom: " + getZoomMultiplier() + "x",
x + width + 3, getHeight() - 2);
graphics.drawString("Zoom: " + getZoomMultiplier() + "x", x + width + 3, getHeight() - 2);
}
}

/**
* Implements the DFT results listener interface method. This is the
* primary method for receiving new frequency bin results.
* Implements the DFT results listener interface method. This is the primary method for receiving new frequency bin results.
*/
@Override
public void receive(float[] update)
{
mDisabled = false;

//If our FFT size changes, reset our pixel map and image source
if(mDFTSize != update.length)
{
mDFTSize = update.length;

reset();
}

//Move the pixels down a row to make room for the new results
System.arraycopy(mPixels, 0, mPixels, mDFTSize, mPixels.length - mDFTSize);
byte[] newPixels = new byte[update.length];

/**
* Find the average value and scale the display to it
Expand All @@ -404,7 +369,6 @@ public void receive(float[] update)
}

float average = (float)(sum / (double)update.length - 1);

float scale = 256.0f / average;

for(int x = 0; x < update.length - 1; x++)
Expand All @@ -413,34 +377,40 @@ public void receive(float[] update)

if(value < 0)
{
mPixels[x] = 0;
newPixels[x] = 0;
}
else if(value > 255)
{
mPixels[x] = (byte)255;
newPixels[x] = (byte)255;
}
else
{
mPixels[x] = (byte)value;
newPixels[x] = (byte)value;
}
}

//Task the swing event thread to update the display
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
//Task the swing event thread to add the new pixels to the pixel array and update the display
EventQueue.invokeLater(() -> {
if(mMemoryImageSource != null)
{
if(mMemoryImageSource != null)
//If our FFT size changes, reset our pixel map and image source
if(mDFTSize != newPixels.length)
{
mDFTSize = newPixels.length;
reset();
}

//Move the pixels down a row and add in the new pixels row
System.arraycopy(mPixels, 0, mPixels, mDFTSize, mPixels.length - mDFTSize);
System.arraycopy(newPixels, 0, mPixels, 0, newPixels.length);

if(mPaused)
{
if(mPaused)
{
mMemoryImageSource.newPixels(mPausedPixels, mColorModel, 0, mDFTSize);
}
else
{
mMemoryImageSource.newPixels(mPixels, mColorModel, 0, mDFTSize);
}
mMemoryImageSource.newPixels(mPausedPixels, mColorModel, 0, mDFTSize);
}
else
{
mMemoryImageSource.newPixels(mPixels, mColorModel, 0, mDFTSize);
}
}
});
Expand All @@ -451,15 +421,17 @@ public void clearWaterfall()
Arrays.fill(mPixels, (byte)0);
mDisabled = true;

EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
EventQueue.invokeLater(() -> {
if(mMemoryImageSource != null)
{
if(mMemoryImageSource != null)
try
{
mMemoryImageSource.newPixels(mPixels, mColorModel, 0, mDFTSize);
}
catch(Exception e)
{
mLog.error("Temporary error updating cleared waterfall panel - " + e.getLocalizedMessage());
}
}
});
}
Expand Down

0 comments on commit 1b062bc

Please sign in to comment.