Why is my image rotating off center? - java

I'm using Java/Slick 2D to play with graphics and using the mouse to rotate an image. Something strange happens though: the image doesn't necessarily face the mouse. At 45 degrees from the normal line it does, but the further you get, the further your are off. See the images below (the white circle being the mouse, the text being the angle):
Here is the rotation code I used:
int mX = Mouse.getX();
int mY = HEIGHT - Mouse.getY();
int pX = sprite.x;
int pY = sprite.y;
int tempY, tempX;
double mAng, pAng = sprite.angle;
double angRotate=0;
if(mX!=pX){
mAng = Math.toDegrees(Math.atan2(mY - pY, mX - pX));
if(mAng==0 && mX<=pX)
mAng=180;
}
else{
if(mY>pY)
mAng=90;
else
mAng=270;
}
sprite.angle = mAng;
sprite.image.setRotation((float) mAng);
Any ideas what's going on? I'm assuming it has something to do with the fact that the image coordinates come from the top left, but I don't know how to counter it. FYI: screen 640x460, image 128x128 and centered in window.
EDIT: Unfortunately, nothing there really worked. Here is a picture with some more information:
EDIT2: Found the answer! had to change: int px/py = sprite.x/y to
int pX = sprite.x+sprite.image.getWidth()/2;
int pY = sprite.y+sprite.image.getHeight()/2;

It looks like your getting the value of your mouse from the left and setting that distance to your rotation... Here's something that might help:
http://www.instructables.com/id/Using-Java-to-Rotate-an-Object-to-Face-the-Mouse/?ALLSTEPS

This is some example code I wrote of a similar question which might help.
Now it doesn't use slick, it uses Swing and Graphics2D but it might help you gain some ideas.
public class TestRotatePane extends JPanel {
private BufferedImage img;
private Point mousePoint;
public TestRotatePane() {
try {
img = ImageIO.read(getClass().getResource("/MT02.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(), img.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
double rotation = 0f;
int width = getWidth() - 1;
int height = getHeight() - 1;
if (mousePoint != null) {
int x = width / 2;
int y = height / 2;
int deltaX = mousePoint.x - x;
int deltaY = mousePoint.y - y;
rotation = -Math.atan2(deltaX, deltaY);
rotation = Math.toDegrees(rotation) + 180;
}
int x = (width - img.getWidth()) / 2;
int y = (height - img.getHeight()) / 2;
g2d.rotate(Math.toRadians(rotation), width / 2, height / 2);
g2d.drawImage(img, x, y, this);
x = width / 2;
y = height / 2;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.RED);
g2d.drawLine(x, y, x, y - height / 4);
g2d.dispose();
}
}
You will, obviously, need to supply your own image ;)

Related

Rotate an image by 90 180 and 270 degrees when width and height can be any size

I am having trouble writing a function that takes a BufferedImage and a Cardinal Direction and rotates the image accordingly. I have looked on many threads on stack and tried implementing myself while going over the docs for some time now and the best I'm getting is that the image seems to be rotating, however the last pixel is correct and the rest are set white or transparent not sure.
Here is where I have got so far:
private BufferedImage rotateImg(BufferedImage image, String direction){
int width = image.getWidth();
int height = image.getHeight();
BufferedImage rotated = null;
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
switch(direction){
case "SOUTH":
rotated = new BufferedImage(width, height, image.getType());
rotated.setRGB((width - 1) - x, (height - 1) - y, image.getRGB(x,y));
break;
case "WEST":
//ROTATE LEFT
rotated = new BufferedImage(height, width, image.getType());
rotated.setRGB(y, (width - 1) - x, image.getRGB(x,y));
break;
case "EAST":
//ROTATE RIGHT
rotated = new BufferedImage(height, width, image.getType());
rotated.setRGB((height - 1) - y, x, image.getRGB(x,y));
break;
default:
return image;
}
}
}
return rotated;
}
Below there are four images but as they are so small its really hard to see them. A bit of browser zoom will show them.
When you get close the cyan pixel is staying where it should for the rotation. Its just im loosing the rest of the image.
I don't know if there's a fixed requirement to rotate this image by individual pixels or not, but I'm far to simple minded to even be bothered trying.
Instead, I'd (personally) drop straight into the Graphics API itself, for example...
public static BufferedImage rotateBy(BufferedImage source, double degrees) {
// The size of the original image
int w = source.getWidth();
int h = source.getHeight();
// The angel of the rotation in radians
double rads = Math.toRadians(degrees);
// Some nice math which demonstrates I have no idea what I'm talking about
// Okay, this calculates the amount of space the image will need in
// order not be clipped when it's rotated
double sin = Math.abs(Math.sin(rads));
double cos = Math.abs(Math.cos(rads));
int newWidth = (int) Math.floor(w * cos + h * sin);
int newHeight = (int) Math.floor(h * cos + w * sin);
// A new image, into which the original can be painted
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
// The transformation which will be used to actually rotate the image
// The translation, actually makes sure that the image is positioned onto
// the viewable area of the image
AffineTransform at = new AffineTransform();
at.translate((newWidth - w) / 2, (newHeight - h) / 2);
// And we rotate about the center of the image...
int x = w / 2;
int y = h / 2;
at.rotate(rads, x, y);
g2d.setTransform(at);
// And we paint the original image onto the new image
g2d.drawImage(source, 0, 0, null);
g2d.dispose();
return rotated;
}
This method will create a new image large enough to fit the rotated version of the source image.
You could then drop it into a helper class, add some helper methods and have a basic worker (and re-usable) solution, for example...
public class ImageUtilities {
public enum Direction {
NORTH, SOUTH, EAST, WEST
}
public static BufferedImage rotateBy(BufferedImage source, Direction direction) {
switch (direction) {
case NORTH:
return source;
case SOUTH:
return rotateBy(source, 180);
case EAST:
return rotateBy(source, 90);
case WEST:
return rotateBy(source, -90);
}
return null;
}
public static BufferedImage rotateBy(BufferedImage source, double degrees) {
// The size of the original image
int w = source.getWidth();
int h = source.getHeight();
// The angel of the rotation in radians
double rads = Math.toRadians(degrees);
// Some nice math which demonstrates I have no idea what I'm talking about
// Okay, this calculates the amount of space the image will need in
// order not be clipped when it's rotated
double sin = Math.abs(Math.sin(rads));
double cos = Math.abs(Math.cos(rads));
int newWidth = (int) Math.floor(w * cos + h * sin);
int newHeight = (int) Math.floor(h * cos + w * sin);
// A new image, into which the original can be painted
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
// The transformation which will be used to actually rotate the image
// The translation, actually makes sure that the image is positioned onto
// the viewable area of the image
AffineTransform at = new AffineTransform();
at.translate((newWidth - w) / 2, (newHeight - h) / 2);
// And we rotate about the center of the image...
int x = w / 2;
int y = h / 2;
at.rotate(rads, x, y);
g2d.setTransform(at);
// And we paint the original image onto the new image
g2d.drawImage(source, 0, 0, null);
g2d.dispose();
return rotated;
}
}
(although I might use RIGHT, LEFT, UPSIDE or something, but that's me :P)
Runnable example...
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage masterImage;
private BufferedImage northImage;
private BufferedImage southImage;
private BufferedImage eastImage;
private BufferedImage westImage;
public TestPane() throws IOException {
masterImage = ImageIO.read(new File("/absolute/path/to/your/image.png"));
northImage = ImageUtilities.rotateBy(masterImage, ImageUtilities.Direction.NORTH);
southImage = ImageUtilities.rotateBy(masterImage, ImageUtilities.Direction.SOUTH);
eastImage = ImageUtilities.rotateBy(masterImage, ImageUtilities.Direction.EAST);
westImage = ImageUtilities.rotateBy(masterImage, ImageUtilities.Direction.WEST);
setLayout(new GridLayout(3, 3));
add(new JLabel(""));
add(new JLabel(new ImageIcon(northImage)));
add(new JLabel(""));
add(new JLabel(new ImageIcon(westImage)));
add(new JLabel(new ImageIcon(masterImage)));
add(new JLabel(new ImageIcon(eastImage)));
add(new JLabel(""));
add(new JLabel(new ImageIcon(southImage)));
add(new JLabel(""));
}
}
public class ImageUtilities {
public enum Direction {
NORTH, SOUTH, EAST, WEST
}
public static BufferedImage rotateBy(BufferedImage source, Direction direction) {
switch (direction) {
case NORTH:
return source;
case SOUTH:
return rotateBy(source, 180);
case EAST:
return rotateBy(source, 90);
case WEST:
return rotateBy(source, -90);
}
return null;
}
public static BufferedImage rotateBy(BufferedImage source, double degrees) {
// The size of the original image
int w = source.getWidth();
int h = source.getHeight();
// The angel of the rotation in radians
double rads = Math.toRadians(degrees);
// Some nice math which demonstrates I have no idea what I'm talking about
// Okay, this calculates the amount of space the image will need in
// order not be clipped when it's rotated
double sin = Math.abs(Math.sin(rads));
double cos = Math.abs(Math.cos(rads));
int newWidth = (int) Math.floor(w * cos + h * sin);
int newHeight = (int) Math.floor(h * cos + w * sin);
// A new image, into which the original can be painted
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
// The transformation which will be used to actually rotate the image
// The translation, actually makes sure that the image is positioned onto
// the viewable area of the image
AffineTransform at = new AffineTransform();
at.translate((newWidth - w) / 2, (newHeight - h) / 2);
// And we rotate about the center of the image...
int x = w / 2;
int y = h / 2;
at.rotate(rads, x, y);
g2d.setTransform(at);
// And we paint the original image onto the new image
g2d.drawImage(source, 0, 0, null);
g2d.dispose();
return rotated;
}
}
}
But the image is rotating in the wrong direction!
Okay, so change the angle of rotation to meet your needs!
As commented, the actual code is creating a new image for each pixel, that is, inside the inner loop. Only the image created in the last iteration is returned, containing just the last pixel.
The following code creates one single image - I tried to maintain the original code as much as possible:
private static BufferedImage rotateImg(BufferedImage image, String direction){
int width = image.getWidth();
int height = image.getHeight();
BufferedImage rotated = null;
switch(direction){
case "SOUTH":
rotated = new BufferedImage(width, height, image.getType());
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
rotated.setRGB((width - 1) - x, (height - 1) - y, image.getRGB(x,y));
}
}
break;
case "WEST":
//ROTATE LEFT
rotated = new BufferedImage(height, width, image.getType());
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
rotated.setRGB(y, (width - 1) - x, image.getRGB(x,y));
}
}
break;
case "EAST":
//ROTATE RIGHT
rotated = new BufferedImage(height, width, image.getType());
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
rotated.setRGB((height - 1) - y, x, image.getRGB(x,y));
}
}
break;
default:
return image;
}
return rotated;
}
To avoid having the loop code repeated (easier to maintain) we can use two functions - calX and calcY:
private static BufferedImage rotateImg(BufferedImage image, String direction){
int width = image.getWidth();
int height = image.getHeight();
BufferedImage rotated = null;
IntBinaryOperator calcX;
IntBinaryOperator calcY;
switch(direction){
case "SOUTH":
rotated = new BufferedImage(width, height, image.getType());
calcX = (x, y) -> (width - 1) - x;
calcY = (x, y) -> (height - 1) - y;
break;
case "WEST":
//ROTATE LEFT
rotated = new BufferedImage(height, width, image.getType());
calcX = (x, y) -> y;
calcY = (x, y) -> (width - 1) - x;
break;
case "EAST":
//ROTATE RIGHT
rotated = new BufferedImage(height, width, image.getType());
calcX = (x, y) -> (height - 1) - y;
calcY = (x, y) -> x;
break;
default:
return image;
}
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
rotated.setRGB(calcX.applyAsInt(x, y), calcY.applyAsInt(x, y), image.getRGB(x,y));
}
}
return rotated;
}

How to rotate shape from its center as well as from center of the screen

I have created Star shape using drawPolygon() but I want to rotate it from the center point of the star as well as from center of the screen.
Here is my code to rotate star from its center:
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform origX = g2d.getTransform();
AffineTransform newX = (AffineTransform) origX.clone();
newX.rotate(Math.toRadians(angle), x, y + 62); // Rotate about center of the star
g2d.setTransform(newX);
g2d.drawPolygon(starX, starY, 5);
g2d.dispose();
g.dispose();
}
If i replace:
newX.rotate(Math.toRadians(angle), x, y + 62);
to
newX.rotate(Math.toRadians(angle), this.getWidth() / 2, this.getHeight() / 2);
I can able to rotate it from center of the screen.
However, I want to achieve both the effects simultaneously.
i.e.: Like the earth which rotate around the sun as well as its own axis.
I have tried creating another AffineTransform object but it overwrite the previous one when I set it using g2d.setTransform(newObj);
Any suggestions will be extremely helpful.
Thank you.
Here is my complete code if you want to see
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
public class Star extends Applet implements Runnable{
private int[] starX;
private int[] starY;
private Thread mainThread;
private int x;
private int y;
private int angleFromCenterShape;
#Override
public void init() {
this.setSize(800, 480);
x = 250;
y = 150;
angleFromCenterShape = 0;
starX = new int[5];
starY = new int[5];
mainThread = new Thread(this);
mainThread.start();
this.setBackground(Color.BLACK);
}
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform origX = g2d.getTransform();
AffineTransform newX = (AffineTransform) origX.clone();
newX.rotate(Math.toRadians(angleFromCenterShape), x, y + 65); //Rotate about center of the star
g2d.setTransform(newX);
g2d.setColor(Color.red);
g2d.fillPolygon(starX, starY, 5);
g2d.dispose();
g.dispose();
}
#Override
public void run() {
while(true){
angleFromCenterScreen = (angleFromCenterScreen + 1) % 360; // angle loop from 0 to 36o
initStar();
try{
Thread.sleep(30);
}catch(Exception e){}
repaint();
}
}
private void initStar(){
starX[0] = x;
starX[1] = x - 50;
starX[2] = x + 75;
starX[3] = x - 75;
starX[4] = x + 50;
starY[0] = y;
starY[1] = y + 130;
starY[2] = y + 50;
starY[3] = y + 50;
starY[4] = y + 130;
}
}
I have found a workaround for my problem but it is not an exact solution.
However adding another rotate() on the same AffineTransform object mimic the 2 rotations
Here is the code
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
AffineTransform origX = g2d.getTransform();
AffineTransform newX = (AffineTransform) origX.clone();
newX.rotate(Math.toRadians(angle), this.getWidth() / 2, this.getHeight() / 2); // Rotate from the center of screen
newX.rotate(Math.toRadians(angle), x, y + 65); // Rotate from the center of the shape
g2d.setTransform(newX);
g2d.setColor(Color.red);
g2d.fillPolygon(starX, starY, 5);
g2d.dispose();
g.dispose();
}

Drawing image on Graphics object does not work, but drawing lines work

For some reason painting an image does not appear to work on my Graphics object, please see the Triangle -> paintComponent() method.
Could I be missing something critical? Perhaps to do with not casting the Graphics object to Graphics2D? A transparency mask?
final JFrame jFrame = new JFrame();
jFrame.add(new Triangle());
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setSize(500,500);
jFrame.repaint();
jFrame.setVisible(true);
Triangle class:
public class Triangle extends JPanel {
private int x = 50;
private int y = 50;
private int width = 100;
private int length = 300;
private int direction = 0;
private BufferedImage renderedImage = null;
private Image getRenderedImage() {
if (renderedImage == null) {
BufferedImage image = new BufferedImage(100, 300, BufferedImage.TYPE_3BYTE_BGR);
drawGraphics(image.getGraphics());
}
return renderedImage;
}
private void drawGraphics(Graphics graphics) {
graphics.setColor(Color.BLUE);
// left
graphics.drawLine(
(int) Math.round(x - (width / 2.0)), y,
(int) x, y + length);
// right
graphics.drawLine(
(int) x, y + length,
(int) Math.round(x + (width / 2.0)), y);
// bottom
graphics.drawLine(
(int) Math.round(x - (width / 2.0)), y,
(int) Math.round(x + (width / 2.0)), y);
}
protected void paintComponent(Graphics graphics) {
// this works
//drawGraphics(graphics);
// this doesn't work
graphics.drawImage(getRenderedImage(), 0, 0, null);
}
}

How do I rotate a shape in the direction of the mouse in a java applet? [duplicate]

So far I have a java app where I draw a circle(player) and then draw a green rectangle on top(gun barrel). I have it so when the player moves, the barrel follows with it. I want it to find where the mouse is pointing and then rotate the barrel accordingly. For an example of what I mean look at this video I found http://www.youtube.com/watch?v=8W7WSkQq5SU See how the player image reacts when he moves the mouse around?
Here's an image of what the game looks like so far:
So how do I rotate it like this? Btw I don't like using affinetransform or Graphics2D rotation. I was hoping for a better way. Thanks
Using the Graphics2D rotation method is indeed the easiest way. Here's a simple implementation:
int centerX = width / 2;
int centerY = height / 2;
double angle = Math.atan2(centerY - mouseY, centerX - mouseX) - Math.PI / 2;
((Graphics2D)g).rotate(angle, centerX, centerY);
g.fillRect(...); // draw your rectangle
If you want to remove the rotation when you're done so you can continue drawing normally, use:
Graphics2D g2d = (Graphics2D)g;
AffineTransform transform = g2d.getTransform();
g2d.rotate(angle, centerX, centerY);
g2d.fillRect(...); // draw your rectangle
g2d.setTransform(transform);
It's a good idea to just use Graphics2D anyway for anti-aliasing, etc.
Using AffineTransform, sorry, only way I know how :P
public class RotatePane extends javax.swing.JPanel {
private BufferedImage img;
private Point mousePoint;
/**
* Creates new form RotatePane
*/
public RotatePane() {
try {
img = ImageIO.read(getClass().getResource("/MT02.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(), img.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
double rotation = 0f;
int width = getWidth() - 1;
int height = getHeight() - 1;
if (mousePoint != null) {
int x = width / 2;
int y = height / 2;
int deltaX = mousePoint.x - x;
int deltaY = mousePoint.y - y;
rotation = -Math.atan2(deltaX, deltaY);
rotation = Math.toDegrees(rotation) + 180;
}
int x = (width - img.getWidth()) / 2;
int y = (height - img.getHeight()) / 2;
g2d.rotate(Math.toRadians(rotation), width / 2, height / 2);
g2d.drawImage(img, x, y, this);
x = width / 2;
y = height / 2;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.RED);
g2d.drawLine(x, y, x, y - height / 4);
g2d.dispose();
}
}
Will produce this effect
The red line (point out from the center) will want to follow the cursor.

GUI freezes when drawing Wave from animation on JPanel though i used Swing Timer

plese look at my code snippets , wha is wrong with it , it frrezes GUI when the Swing timer stats which is repeteadly paints on the jpnael ??
class WaveformPanel extends JPanel {
Timer graphTimer = null;
AudioInfo helper = null;
WaveformPanel() {
setPreferredSize(new Dimension(200, 80));
setBorder(BorderFactory.createLineBorder(Color.BLACK));
graphTimer = new Timer(15, new TimerDrawing());
}
/**
*
*/
private static final long serialVersionUID = 969991141812736791L;
protected final Color BACKGROUND_COLOR = Color.white;
protected final Color REFERENCE_LINE_COLOR = Color.black;
protected final Color WAVEFORM_COLOR = Color.red;
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int lineHeight = getHeight() / 2;
g.setColor(REFERENCE_LINE_COLOR);
g.drawLine(0, lineHeight, (int) getWidth(), lineHeight);
if (helper == null) {
return;
}
drawWaveform(g, helper.getAudio(0));
}
protected void drawWaveform(Graphics g, int[] samples) {
if (samples == null) {
return;
}
int oldX = 0;
int oldY = (int) (getHeight() / 2);
int xIndex = 0;
int increment = helper.getIncrement(helper
.getXScaleFactor(getWidth()));
g.setColor(WAVEFORM_COLOR);
int t = 0;
for (t = 0; t < increment; t += increment) {
g.drawLine(oldX, oldY, xIndex, oldY);
xIndex++;
oldX = xIndex;
}
for (; t < samples.length; t += increment) {
double scaleFactor = helper.getYScaleFactor(getHeight());
double scaledSample = samples[t] * scaleFactor;
int y = (int) ((getHeight() / 2) - (scaledSample));
g.drawLine(oldX, oldY, xIndex, y);
xIndex++;
oldX = xIndex;
oldY = y;
}
}
public void setAnimation(boolean turnon) {
if (turnon) {
graphTimer.start();
} else {
graphTimer.stop();
}
}
class TimerDrawing implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
byte[] bytes = captureThread.getTempBuffer();
if (helper != null) {
helper.setBytes(bytes);
} else {
helper = new AudioInfo(bytes);
}
repaint();
}
}
}
I am calling setAnimation of WaveFormPanel from its parent class.when animation starts it does not draw anything but freezes. please , give me solution.
Thank You
Mihir Parekh
The java.swingx.Timer calls the ActionPerformed within the EDT. The question then is, what's taking the time to render. It could be the call to captureThread.getTempBuffer it could be the construction of the help, but I suspect it's just the share amount of data you are trying to paint.
Having playing with this recently, it takes quite a bit of time to process the waveform.
One suggestion might be to reduce the number of samples that you paint. Rather then painting each one, maybe paint every second or forth sample point depending on the width of the component. You should still get the same jest but without all the work...
UPDATED
All samples, 2.18 seconds
Every 4th sample, 0.711 seconds
Every 8th sample, 0.450 seconds
Rather then paint in response to the timer, maybe you need to paint in response to batches of data.
As your loader thread has a "chunk" of data, may be paint it then.
As HoverCraftFullOfEels suggested, you could paint this to a BufferedImage first and then paint that to the screen...
SwingWorker might be able to achieve this for you
UPDATED
This is the code I use to paint the above samples.
// Samples is a 2D int array (int[][]), where the first index is the channel, the second is the sample for that channel
if (samples != null) {
Graphics2D g2d = (Graphics2D) g;
int length = samples[0].length;
int width = getWidth() - 1;
int height = getHeight() - 1;
int oldX = 0;
int oldY = height / 2;
int frame = 0;
// min, max is the min/max range of the samples, ie the highest and lowest samples
int range = max + (min * -2);
float scale = (float) height / (float) range;
int minY = Math.round(((height / 2) + (min * scale)));
int maxY = Math.round(((height / 2) + (max * scale)));
LinearGradientPaint lgp = new LinearGradientPaint(
new Point2D.Float(0, minY),
new Point2D.Float(0, maxY),
new float[]{0f, 0.5f, 1f},
new Color[]{Color.BLUE, Color.RED, Color.BLUE});
g2d.setPaint(lgp);
for (int sample : samples[0]) {
if (sample % 64 == 0) {
int x = Math.round(((float) frame / (float) length) * width);
int y = Math.round((height / 2) + (sample * scale));
g2d.drawLine(oldX, oldY, x, y);
oldX = x;
oldY = y;
}
frame++;
}
}
I use an AudioStream stream to load a Wav file an produce the 2D samples.
I'm guessing that your wave drawing code, which is being called from within a paintComponent(...) method is taking longer than you think and is tying up both Swing painting and the EDT.
If this were my code, I'd consider drawing my waves to BufferedImages once, making ImageIcons from these images and then simply swapping icons in my Swing Timer.

Categories