我目前正在尝试以非常高的分辨率绘制波形(由于缩放)。因此,波形在JScrollPane中绘制。我希望能够用它绘制大约50.000-60.000像素的宽度。
不幸的是,它在大约34000像素宽度处正确地停止绘画。问题是它不会涂漆。第一个屏幕尺寸,但其余部分正确绘制。由于我在图形方面的经验很少,我认为你可以帮助我决定如何尽可能地解决这个问题。
我考虑通过重新绘制第一个屏幕尺寸(例如重绘(矩形))或者将图片分成3个或更多帧来处理它。如果我选择第二个选项,我不知道是否应该将它全部涂在一起,或者只在它在视口上可见时才绘制它。或许还有另一种更好的解决方案我无法弄清楚?
我希望你能帮我解决这个问题并节省大量时间来尝试一切。提前谢谢。
所以这是请求的可执行文件。您可以在大约34.000像素宽度处看到不正确的绘图。可以在System.out中读取当前像素。绘图不适用于mp3,因为不支持。我建议尝试使用.wav。
主类:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
public class Main {
private JFrame mainFrame;
private JPanel upperPanel;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main window = new Main();
window.mainFrame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Initialize Application
*/
public Main() {
initializeMainFrame();
initializePanels();
}
private void initializePanels() {
upperPanel = new MainPanel();
upperPanel.setPreferredSize(new Dimension(1000, 500));
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.NORTH;
c.weightx = 1.0;
c.weighty = 1.0;
c.fill = GridBagConstraints.BOTH;
c.gridy = 0;
c.gridwidth = GridBagConstraints.REMAINDER;
mainFrame.add(upperPanel, c);
}
private void initializeMainFrame() {
mainFrame = new JFrame("Waveform Example");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GraphicsEnvironment ge = GraphicsEnvironment
.getLocalGraphicsEnvironment();
Rectangle gebounds = ge.getMaximumWindowBounds();
mainFrame.setSize(gebounds.getSize());
mainFrame.setLocationRelativeTo(null);
mainFrame.setVisible(true);
mainFrame.setLayout(new GridBagLayout());
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
JMenuItem importAudio = new JMenuItem("Import Audio");
menuBar.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.anchor = GridBagConstraints.WEST;
c.weightx = 0.3;
c.weighty = 0.3;
c.gridx = 0;
c.gridy = 0;
mainFrame.setJMenuBar(menuBar);
menuBar.add(fileMenu, c);
c.gridx = 1;
c.gridy = 0;
fileMenu.add(importAudio);
importAudio.addActionListener(new importAudioActionListener());
}
private class importAudioActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent event) {
File f = getFile();
if (f != null) {
AudioInfo audioInfo = createAudioInfo(f);
mainFrame.remove(upperPanel);
upperPanel = new MainPanel(audioInfo);
upperPanel.setPreferredSize(new Dimension(1000, 500));
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.NORTH;
c.gridy = 0;
c.weightx = 1.0;
c.weighty = 1.0;
c.fill = GridBagConstraints.BOTH;
c.gridwidth = GridBagConstraints.REMAINDER;
mainFrame.add(upperPanel, c);
mainFrame.pack();
}
}
private AudioInfo createAudioInfo(File f) {
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(f);
} catch (UnsupportedAudioFileException e1) {
System.out.println("Invalid Audio Format");
} catch (IOException e1) {
System.out.println("Invalid Input File");
}
AudioInfo retInfo = new AudioInfo(audioInputStream,
(int) f.length());
return retInfo;
}
private File getFile() {
// New file chooser only shows and accepts MP3 files.
JFileChooser fc = new JFileChooser();
fc.setAcceptAllFileFilterUsed(false);
fc.showOpenDialog(null);
File f = null;
try {
f = fc.getSelectedFile();
} catch (Exception fnfe) {
f = null;
System.out.println("File not found!");
}
return f;
}
}
}
包含JPanel的面板:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class MainPanel extends JPanel implements MouseWheelListener,
ComponentListener {
private boolean finishedZoom = true;
private int mouseX;
private static final long serialVersionUID = 1L;
private AudioInfo audioInfo;
private int scale = 1;
private Dimension panelSize;
private int mouseXScaled;
private int mouseYScaled;
private JScrollPane scrollPane;
private int sizeNormalizer = 150;
private JPanel thisPanel = this;
private JPanel content;
public MainPanel() {
}
public MainPanel(AudioInfo audioInfo) {
this.audioInfo = audioInfo;
this.setLayout(new BorderLayout());
panelSize = new Dimension(1000, 500);
content = getContent();
scrollPane = new JScrollPane(content);
scrollPane.setPreferredSize(panelSize);
scrollPane
.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
this.add(scrollPane, BorderLayout.CENTER);
this.setPreferredSize(panelSize);
content.addMouseWheelListener(this);
content.addComponentListener(this);
}
private JPanel getContent() {
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 1.0;
c.weighty = 1.0;
JPanel retContent = new JPanel(false);
retContent.setPreferredSize(panelSize);
retContent.setLayout(new GridBagLayout());
WaveformPanel waveformPanel = new WaveformPanel(audioInfo);
waveformPanel.setPreferredSize(panelSize);
retContent.setBackground(Color.green);
c.gridwidth = GridBagConstraints.REMAINDER; // end row
retContent.add(waveformPanel, c);
return retContent;
}
public void mouseWheelMoved(MouseWheelEvent e) {
boolean changed = false;
double notches = e.getWheelRotation();
if (e.isControlDown() && finishedZoom) {
int newScale = (int) (scale + notches * (-1) * 2);
int newWidth = (int) ((thisPanel.getPreferredSize().getWidth()) * newScale);
if (newWidth > content.getPreferredSize().getWidth()) {
System.out.println("new width original: " + newWidth);
content.setVisible(false);
content.setPreferredSize(new Dimension(
newWidth,
(int) ((thisPanel.getPreferredSize().getHeight() - sizeNormalizer) / 3 * 2)));
content.setVisible(true);
mouseXScaled = e.getX() / scale * newScale;
mouseYScaled = e.getY() / scale * newScale;
scale = newScale;
changed = true;
} else if (newWidth < content.getPreferredSize().getWidth()
&& newWidth > thisPanel.getWidth()) {
content.setVisible(false);
content.setPreferredSize(new Dimension(
newWidth,
(int) ((thisPanel.getPreferredSize().getHeight() - sizeNormalizer) / 3 * 2)));
content.setVisible(true);
mouseXScaled = e.getX() / scale * newScale;
mouseYScaled = e.getY() / scale * newScale;
scale = newScale;
changed = true;
} else if (newWidth <= thisPanel.getWidth()) {
newWidth = (int) (thisPanel.getPreferredSize().getWidth());
newScale = 1;
content.setVisible(false);
content.setPreferredSize(new Dimension(
newWidth,
(int) ((thisPanel.getPreferredSize().getHeight() - sizeNormalizer) / 3 * 2)));
content.setVisible(true);
mouseXScaled = e.getX() / scale * newScale;
mouseYScaled = e.getY() / scale * newScale;
scale = newScale;
}
if (changed) {
finishedZoom = false;
}
mouseX = e.getX();
} else if (!e.isControlDown()) {
int scrollBarValue = scrollPane.getHorizontalScrollBar().getValue();
Rectangle viewRect = scrollPane.getViewport().getViewRect();
scrollPane
.getHorizontalScrollBar()
.setValue(
(int) ((int) scrollBarValue + ((viewRect.width - 100) * notches)));
}
}
public int getHorizontalScroll() {
return scrollPane.getHorizontalScrollBar().getValue();
}
@Override
public void componentHidden(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentMoved(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
@Override
public void componentResized(ComponentEvent arg0) {
if (mouseXScaled != 0 && mouseYScaled != 0) {
int scrollBarVal = scrollPane.getHorizontalScrollBar().getValue();
int newX = (int) (scrollBarVal + mouseXScaled - mouseX);
scrollPane.getHorizontalScrollBar().setValue(newX);
finishedZoom = true;
}
}
@Override
public void componentShown(ComponentEvent arg0) {
// TODO Auto-generated method stub
}
}
AudioInfo类:
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
public class AudioInfo {
private static final int NUM_BITS_PER_BYTE = 8;
private AudioInputStream encodedInputSream;
private int[][] encodedSamplesContainer;
private byte[] encodedBuffer;
// cached values
private int sampleMax = 0;
private int sampleMin = 0;
private double biggestSample;
public AudioInfo(AudioInputStream encodedInputStream, int fileSize) {
encodedBuffer = new byte[fileSize];
this.encodedInputSream = encodedInputStream;
encodedBuffer = createSampleArrayCollection(encodedInputStream,
encodedBuffer);
encodedSamplesContainer = getSampleArray(encodedBuffer);
if (sampleMax > sampleMin) {
biggestSample = sampleMax;
} else {
biggestSample = Math.abs(((double) sampleMin));
}
}
protected int getNumberOfChannels() {
return 2;
}
/**
* Reads the audio input stream into a tmp array and then inserts the tmp
* array into a buffer array. Resets the mark of the audio input stream
* after finish buffering. Then cuts the array from fileSize*10 to the final
* size.
*/
private byte[] createSampleArrayCollection(AudioInputStream inputStream,
byte[] inBuffer) {
byte[] buffer = new byte[inBuffer.length];
int sumReadBytes = 0;
try {
// inputStream.mark(Integer.MAX_VALUE);
//inputStream.reset();
boolean end = false;
while (!end) {
int available = inputStream.available();
if (available <= 0) {
end = true;
}
if (!end) {
byte[] tmp = new byte[available];
int readBytes = inputStream.read(tmp);
tmp = cutArray(tmp, readBytes);
insertArray(buffer, tmp, sumReadBytes);
sumReadBytes += readBytes;
}
}
//inputStream.reset();
} catch (IOException e) {
e.printStackTrace();
}
buffer = cutArray(buffer, sumReadBytes);
return buffer;
}
/**
*
* @param cutThis
* array that has to be cut
* @param cutPoint
* index at which the array will be cut off
* @return the buffer array cut off at the point of cutpoint
*/
private byte[] cutArray(byte[] cutThis, int cutPoint) {
byte[] tmp = new byte[cutPoint];
for (int i = 0; i < tmp.length; i++) {
tmp[i] = cutThis[i];
}
return tmp;
}
/**
*
* @param insertIntoThis
* the array you want to insert in the other
* @param tmp
* the array that is going to be inserted
*/
private byte[] insertArray(byte[] insertIntoThis, byte[] tmp,
int nextEmptyField) {
for (int i = 0; i < tmp.length; i++) {
insertIntoThis[nextEmptyField] = tmp[i];
nextEmptyField++;
}
return insertIntoThis;
}
/**
*
* @param eightBitByteArray
* Array of an eight bit byte array.
* @return int audio information array for every channel.
*/
private int[][] getSampleArray(byte[] eightBitByteArray) {
int[][] toReturn = new int[getNumberOfChannels()][eightBitByteArray.length
/ (2 * getNumberOfChannels()) + 1];
int index = 0;
// loop through the byte[]
for (int t = 0; t + 4 < eightBitByteArray.length;) {
// for each iteration, loop through the channels
for (int a = 0; a < getNumberOfChannels(); a++) {
// do the byte to sample conversion
// see AmplitudeEditor for more info
int low = (int) eightBitByteArray[t];
t++;
int high = (int) eightBitByteArray[t];
t++;
int sample = (high << 8) + (low & 0x00ff);
if (sample < sampleMin) {
sampleMin = sample;
} else if (sample > sampleMax) {
sampleMax = sample;
}
// set the value.
toReturn[a][index] = sample;
}
index++;
}
return toReturn;
}
/**
*
* @param panelHeight
* @return calculated yScaleFactor
*/
public double getYScaleFactor(int panelHeight) {
return (panelHeight / (biggestSample * 2 * 1.5));
}
/**
*
* @param channel
* number of the channel you want the audio information of
* @return int array of the audio information of the given channel.
*/
protected int[] getAudio(int channel) {
return encodedSamplesContainer[channel];
}
/**
*
* @param xScale
* @return calculates the increment for given xScale
*/
protected int getIncrement(double xScale) {
try {
int increment = (int) (encodedSamplesContainer[0].length / (encodedSamplesContainer[0].length * xScale));
return increment;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
}
波形面板类:
import javax.swing.*;
import java.awt.*;
public class WaveformPanel extends JPanel {
private static final long serialVersionUID = 1L;
private static final Color BACKGROUND_COLOR = Color.black;
private static final Color REFERENCE_LINE_COLOR = Color.blue;
private static final Color WAVEFORM_COLOR = Color.blue;
private AudioInfo helper;
private int[] samples;
public WaveformPanel(AudioInfo helper) {
super();
this.helper = helper;
setBackground(BACKGROUND_COLOR);
samples = helper.getAudio(0);
}
/**
* Paints the component of the melted channel audio data.
*/
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int lineHeight = getHeight() / 2;
g.setColor(REFERENCE_LINE_COLOR);
g.drawLine(0, lineHeight, (int) getWidth(), lineHeight);
drawWaveform(g, samples);
}
protected double getXScaleFactor(int panelWidth) {
double width = (double) panelWidth;
return (width / ((double) samples.length));
}
private double getIncrement(double xScale) {
try {
double increment = (samples.length / (samples.length * xScale));
return increment;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
/**
* @param g
* graphic of this panel
* @param samples
* audio samples of a channel
*
* Draws a waveform with given input on a graphic.
*/
protected void drawWaveform(Graphics g, int[] samples) {
int buffer = 30;
if (samples == null) {
return;
}
double oldX = 0;
double xIndex = 0;
double increment = getIncrement(getXScaleFactor(getWidth() - buffer * 2));
g.setColor(WAVEFORM_COLOR);
System.out.println("width: " + this.getWidth());
double t = 0;
int drawLength = samples.length;
for (; t < drawLength; t += increment) {
double scaleFactor = helper.getYScaleFactor(getHeight());
double scaledSample = samples[(int) t] * scaleFactor;
double y = ((getHeight() / 2) - (scaledSample));
double yMirror = ((getHeight() / 2) + scaledSample);
g.drawLine((int) (oldX + buffer), (int) yMirror,
(int) (xIndex + buffer), (int) y);
xIndex++;
oldX = xIndex;
}
}
}
答案 0 :(得分:1)
作为替代方案,请参阅此MCTaRE,它成功呈现两倍宽度的图像。将其滚动到半宽(或任何宽度)以查看没有伪影的图像。
请注意,我在该示例中调用setPreferredSize
来保存一些代码行,但请参阅Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?(是)。
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class BigImageWaveform {
public static void main(String[] args) {
final BufferedImage bi = new BufferedImage(
2*34000, 500, BufferedImage.TYPE_INT_RGB);
draw(bi);
Runnable r = new Runnable() {
@Override
public void run() {
JScrollPane jsp = new JScrollPane(
new JLabel(new ImageIcon(bi)),
JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
Dimension d = jsp.getPreferredSize();
jsp.setPreferredSize(new Dimension(1000, (int)d.getHeight()));
JOptionPane.showMessageDialog(null, jsp);
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
public static void draw(BufferedImage bi) {
Graphics2D g = bi.createGraphics();
int w = bi.getWidth();
int h = bi.getHeight();
GradientPaint gp = new GradientPaint(
0f,0f,Color.RED,
101f,0f,Color.GREEN,true);
g.setPaint(gp);
g.fillRect(0,0,w,h);
gp = new GradientPaint(
0f,0f,new Color(0,0,255,128),
97f,103f,new Color(220,0,220,164), true);
g.setPaint(gp);
g.fillRect(0,0,w,h);
gp = new GradientPaint(
0f,0f,new Color(0,0,0,0),
(float)w,0f,new Color(0,0,0,128), true);
g.setPaint(gp);
g.fillRect(0,0,w,h);
g.dispose();
}
}
答案 1 :(得分:0)
在对两个更强大的Windows系统进行测试之后,我发现它的问题是Linux问题或性能问题,因为我的笔记本电脑大约有两年的时间并且非常便宜。
如果有人可以在Linux系统上测试它会很棒。否则我将把这个问题标记为已回答。