-1
 package testIDE;

import java.awt.BorderLayout;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

import Utils.MyJFrame;

public class ExampleClass {

    public static void main(String[] args) {
        JFrame ballRotate = new BallRotate();
    }
}

class BallRotate extends MyJFrame {

    ArrayList<Integer> degree = new ArrayList<Integer>();
    BufferedImage backGroundImage = getBufferedImage("testIDE/buttonDefaultImage.jpg");
    JLabel backGroundLabel = new JLabel(new ImageIcon(backGroundImage));
    BufferedImage footballImage = getBufferedImage("testIDE/Tennis_Ball.png");
    int x = 0;

    public BallRotate() {

        footballImage=getScaledImage(250, 250, footballImage);
        BufferedImage rotatedImage = footballImage;

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new GridBagLayout());

        setLabel();
        add(backGroundLabel);

        pack();
        centeringWindow();
        setVisible(true);

        setArray();

        while (true) {
            setDelay(60);
            rotatedImage = rotateImage(rotatedImage, x);

            setMyFuckingLabel(rotatedImage);
            x += 10;
            if (x == 10000) {
                break;
            }
        }
    }

    private void setArray() {
        for (int i = 0; i <= 360; i += 40) {
            degree.add(i);
        }
    }

    private void setLabel() {
        JPanel footBallPanel = new JPanel(new BorderLayout());
        JLabel footBallLabel = new JLabel(new ImageIcon(footballImage));

        footBallPanel.add(footBallLabel);

        borderingJPanel(footBallPanel, null, null); 

        backGroundLabel.setLayout(new GridBagLayout());
        backGroundLabel.add(footBallPanel);
    }

    private BufferedImage rotateImage(BufferedImage buffImage, int degree) {
        BufferedImage rotatedImage = null;

        AffineTransform affineTransform = AffineTransform.getRotateInstance(
                Math.toRadians(15*Math.PI), buffImage.getWidth() / 2,
                buffImage.getHeight() / 2);
        System.out.println(degree*Math.toRadians(1));       
        rotatedImage = new BufferedImage(buffImage.getWidth(),
                buffImage.getHeight(), buffImage.getType());
        Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
        g.setTransform(affineTransform);
        g.drawImage(buffImage, 0, 0, null);
        return rotatedImage;
    }

    public void setMyLabel(BufferedImage rotatedBuffImage) {
        JLabel backgroundlabel = (JLabel) getContentPane().getComponent(0);
        JPanel footBallPanel = (JPanel) backgroundlabel.getComponent(0);
        JLabel footBallLabel = (JLabel) footBallPanel.getComponent(0);

        footBallLabel.setIcon(new ImageIcon(rotatedBuffImage));
    }
}

enter image description here enter image description here enter image description here

As you can see, my rotating tennis ball looses his form and his colouring. It seems to be that the colours are rotating also. Why? And is there a way to prevent this? Ive posted the code which generated the dialog above.

Thanks for any help.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
user3133542
  • 1,695
  • 4
  • 21
  • 42

1 Answers1

5

So a raft of issues...

  • Scaling an image in a single step is never a good idea (unless you only scaling by 50%). Java also isn't particular good at it. There are tricks you can employee, like using a multi-step scale (demonstrated within the example) or use an external library like imgscalr. See The Perils of Image.getScaledInstance() for more details.
  • You should avoid apply effects to the same image multiply times, this simply compounds the changes and will reduce the quality of the image. Instead, maintain a master image as close to the original as you can and use it, so you're always starting from the same starting point.
  • Swing is not thread safe. This means three things. First, you should never do anything within the context of the Event Dispatching Thread that might block it, like infinite loops. Second, you should only ever change the state of UI components from within the context of the EDT and third, you should make sure you are creating your UIs from within the context of the EDT. See Concurrency in Swing and Initial Threads for more details...
  • Any thing other then a straight horizontal or vertical line is going to look pretty...ordinary under the default rendering settings. You're going to need to supply some RenderingHints to enhance the result

This then raises the question of how do you do animation in Swing? Well, you have two basic options, you can use a Thread of some kind, which requires you to manually synchronise updates back to the EDT or you can use a Swing javax.swing.Timer, which allows you to schedule call backs at regular intervals which are triggered within the context of the EDT. Take a look at How to use Swing Timers for more details...

RotateBall

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class BallRotate {

    public static void main(String[] args) {
        new BallRotate();
    }

    public BallRotate() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage master;
        private JLabel ball;

        private BufferedImage rotatedImage;
        private float angle = 0;
        private float delta = 5;

        public TestPane() {
            setLayout(new GridBagLayout());
            try {
                master = ImageIO.read(getClass().getResource("/Ball.png"));
                master = getScaledInstanceToFit(master, new Dimension(250, 250));
            } catch (IOException ex) {
                ex.printStackTrace();
            }

            ImageIcon icon = new ImageIcon(getRotatedImage(0));
            ball = new JLabel(icon);
            add(ball);

            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    angle += delta;
//                    ball.setIcon(new ImageIcon(getRotatedImage(delta)));
                    getRotatedImage(angle);
                    ball.repaint();
                    System.out.println(angle);
                }
            });

            addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    timer.start();
                }

            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        protected BufferedImage getRotatedImage(float degree) {

            if (rotatedImage == null) {
                rotatedImage = new BufferedImage(master.getWidth(),
                                master.getHeight(), BufferedImage.TYPE_INT_ARGB);
            }

            AffineTransform affineTransform = AffineTransform.getRotateInstance(
                            Math.toRadians(degree), rotatedImage.getWidth() / 2,
                            rotatedImage.getHeight() / 2);

            Graphics2D g = (Graphics2D) rotatedImage.getGraphics();
            g.setBackground(new Color(255, 255, 255, 0));
            g.clearRect(0, 0, rotatedImage.getWidth(), rotatedImage.getHeight());
            g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
            g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
            g.setTransform(affineTransform);
            g.drawImage(master, 0, 0, null);
            g.dispose();

            return rotatedImage;
        }
    }

    public static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {

        return getScaledInstance(img, dScaleFactor, RenderingHints.VALUE_INTERPOLATION_BILINEAR, true);

    }

    protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor, Object hint, boolean bHighQuality) {

        BufferedImage imgScale = img;

        int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
        int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);

//        System.out.println("Scale Size = " + iImageWidth + "x" + iImageHeight);
        if (dScaleFactor < 1.0d) {

            imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight, hint, bHighQuality);

        }

        return imgScale;

    }

    protected static BufferedImage getScaledDownInstance(BufferedImage img,
                    int targetWidth,
                    int targetHeight,
                    Object hint,
                    boolean higherQuality) {

        int type = (img.getTransparency() == Transparency.OPAQUE)
                        ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;

        BufferedImage ret = (BufferedImage) img;

        if (targetHeight > 0 || targetWidth > 0) {

            int w, h;
            if (higherQuality) {
                // Use multi-step technique: start with original size, then
                // scale down in multiple passes with drawImage()
                // until the target size is reached
                w = img.getWidth();
                h = img.getHeight();
            } else {
                // Use one-step technique: scale directly from original
                // size to target size with a single drawImage() call
                w = targetWidth;
                h = targetHeight;
            }

            do {
                if (higherQuality && w > targetWidth) {
                    w /= 2;
                    if (w < targetWidth) {
                        w = targetWidth;
                    }
                }

                if (higherQuality && h > targetHeight) {
                    h /= 2;
                    if (h < targetHeight) {
                        h = targetHeight;
                    }
                }

                //            if (w <= 0) w = 1;
                //            if (h <= 0) h = 1;
                BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
                Graphics2D g2 = tmp.createGraphics();
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
                g2.drawImage(ret, 0, 0, w, h, null);
                g2.dispose();

                ret = tmp;

            } while (w != targetWidth || h != targetHeight);

        } else {

            ret = new BufferedImage(1, 1, type);

        }

        return ret;

    }

    public static double getScaleFactor(int iMasterSize, int iTargetSize) {

        return (double) iTargetSize / (double) iMasterSize;

    }

    public static double getScaleFactorToFit(Dimension original, Dimension toFit) {

        double dScale = 1d;

        if (original != null && toFit != null) {

            double dScaleWidth = getScaleFactor(original.width, toFit.width);
            double dScaleHeight = getScaleFactor(original.height, toFit.height);

            dScale = Math.min(dScaleHeight, dScaleWidth);

        }

        return dScale;

    }

    public static double getScaleFactorToFit(BufferedImage img, Dimension size) {

        double dScale = 1;

        if (img != null) {

            int imageWidth = img.getWidth();
            int imageHeight = img.getHeight();

            dScale = getScaleFactorToFit(new Dimension(imageWidth, imageHeight), size);

        }

        return dScale;

    }

    public static BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) {

        double scaleFactor = getScaleFactorToFit(img, size);

        return getScaledInstance(img, scaleFactor);

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366