旋转后图像不在适当位置(图形) [英] Image not at proper place after rotating (graphics)

查看:155
本文介绍了旋转后图像不在适当位置(图形)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图以不同的速率显示直径为512un的两个旋转轮,但我无法删除先前绘制的图像图形,并将旋转的图形设置在正确的位置。
现在我正在做一个任意角度的旋转。
我尝试了affineTransform并得到了旋转,但它像所有像素点都很奇怪。
Im使用带有thread.sleep()的while循环。以下是代码:
// drawSmallCircle和drawBigCircle返回两个图像。

  class MyFramePart2 extends JFrame 

{

字符串名称;
JPanel big_obj_panel,small_obj_panel;
JLabel bigLabel,smallLabel; BufferedImage imgRet,imgRetSmall;
静态双弧度,角度,转速,fps,smallAngle,smallRadians;
int numLines,i = 0;

MyFramePart2(String frameName,int numStrokes,double revolutions,double frameps)
{
numLines = numStrokes;
smallAngle = smallRadians = angle = radians = Math.toRadians(360 / numLines);
rev =转数;
fps = frameps;

setSize(1240,720);
setLocation(0,0);
setLayout(null);
getContentPane()。setBackground(Color.WHITE);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

big_obj_panel = new JPanel();
big_obj_panel.setLayout(null);
big_obj_panel.setSize(512,512);
big_obj_panel.setLocation(100,100);
big_obj_panel.setBackground(Color.white);
add(big_obj_panel);


imgRet = drawBigCircle();
bigLabel =新的JLabel(新的ImageIcon(imgRet));
bigLabel.setLayout(null);
bigLabel.setLocation(0,0);
bigLabel.setSize(512,512);
bigLabel.setOpaque(true);
bigLabel.setBackground(Color.white);
big_obj_panel.add(bigLabel);

small_obj_panel = new JPanel();
small_obj_panel.setLayout(null);
small_obj_panel.setSize(512,512);
small_obj_panel.setLocation(700,100);
small_obj_panel.setBackground(Color.white);
add(small_obj_panel);

imgRetSmall = drawSmallCircle();

smallLabel = new JLabel(new ImageIcon(imgRetSmall));
smallLabel.setLayout(null);
smallLabel.setLocation(0,0);
smallLabel.setSize(512,512);
smallLabel.setOpaque(true);
smallLabel.setBackground(Color.white);
small_obj_panel.add(smallLabel);

setVisible(true);

while(i!= 5)//假设为true,只需检查
{
setVisible(true);
bigLabel.setIcon(new ImageIcon(imgRet));
smallLabel.setIcon(新ImageIcon(imgRetSmall));
尝试{
Thread.sleep(10);
}
catch(Exception e)
{}
i ++;
}
}

@Override
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d =(Graphics2D)g;
g2d.translate(256,256);
g2d.rotate(Math.toRadians(20));
g2d.translate(-256,-256);
g2d.drawImage(imgRet,0,0,null);
g2d.dispose();

super.paint(g);
g2d =(Graphics2D)g;
g2d.translate(256,256);
g2d.rotate(Math.toRadians(30));
g2d.translate(-256,-256);
g2d.drawImage(imgRetSmall,0,0,null);
g2d.dispose();
}

public static BufferedImage drawBigCircle()
{
BufferedImage img = new BufferedImage(512,512,BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.black);
g2d.drawOval(0,0,511,511);

Line2D l2d;
while(angle <= 2 * Math.PI)
{
l2d = new Line2D.Double(256,256,256 + 256 * Math.cos(angle),256 + 256 * Math.sin (角度));
g2d.draw(l2d);
angle = angle +弧度;
}
return img;
}
}


解决方案

Swing规则。不要阻塞事件调度线程。这样做会使你的应用程序看起来像挂起,并阻止EDT处理任何重新绘制请求。



这意味着,您需要某种方法来安排不阻止更新EDT



Swing的第二条规则。通常,您应该避免重写 paint 。不要在任何线程创建或修改EDT以外的任何线程组件。

$ c>顶级容器的方法,比如 JFrame ,除了其他所有内容外,它们不是双缓冲的,这意味着您的绘画在更新时会闪烁。相反,您应该使用 Swing 容器之一,例如 JPanel



有很多不同的方法来实现这一点。基本上,在这里我使用了三个列表,但如果我认真的话,我会创建一个可以保留所有必需信息的对象(图像,角度和三角洲)。

为了实现实际的动画,我使用了 javax.swing.Timer 。这至少会在每个 n 时段触发一个事件,但更重要的是,它会在事件派发线程的上下文中进行。这可以确保对角度所做的所有更改都可以防止在更新值时发生绘画的可能性。



此示例旋转以不同的(随机)速度拍摄三张照片......


  public class TestRotation {

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

$ b $ public TestRotation(){
EventQueue.invokeLater(new Runnable(){
@Override
public void run(){
尝试{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch(ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex){
}

JFrame框架= new JFrame(Test);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new AnimationPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

});
}

public class AnimationPane extends JPanel {

private List< BufferedImage>图片;
私人列表< Double>角;
私人列表< Double>速度;

public AnimationPane(){
images = new ArrayList<>(5);
images.add(createWheel(50,4));
images.add(createWheel(50,3));
images.add(createWheel(50,6));
angles = new ArrayList<>();
speed = new ArrayList<>();
for(int index = 0; index< images.size(); index ++){
angles.add(0d);
speed.add(Math.random()* 5d);


定时器定时器=新定时器(40,新的ActionListener(){
@Override
public void actionPerformed(ActionEvent e){
for int index = 0; index< angles.size(); index ++){
double angle = angles.get(index);
double delta = speed.get(index);
angle + = delta;
angles.set(index,angle);
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();


$ b @Override
public Dimension getPreferredSize(){
return new Dimension(200,200);


@Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
int x = 0;
int y = 0;
for(int index = 0; index< images.size(); index ++){
BufferedImage image = images.get(index);
double angle = angles.get(index);

//这很重要。基本上我们要抓取当前图形上下文的独立快照
//。这意味着我们做
//所做的任何更改都不会影响原始图形上下文(除绘图之外)
Graphics2D g2d =(Graphics2D)g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
AffineTransform at = new AffineTransform();
at.translate(x,y);
at.rotate(Math.toRadians(angle),image.getWidth()/ 2,image.getHeight()/ 2);
g2d.setTransform(at);
g2d.drawImage(image,0,0,this);
g2d.dispose();

x + = image.getWidth();





保护Point2D calculateOutterPoint(int radius,double angel){

int x = Math.round(radius / 2);
int y = Math.round(radius / 2);

double rads = Math.toRadians((angel + 90));

//这决定了tick的长度,从
//圆的中心开始计算。原始代码从此衍生得到
//用于从cirlce中心开始的可变长度线,我们
//实际上需要相反的值,所以我们首先计算外部限制
double fullLength =(radius / 2d);

//计算行的外部点
double xPosy =(x + Math.cos(rads)* fullLength);
double yPosy =(y - Math.sin(rads)* fullLength);

返回新的Point2D.Double(xPosy,yPosy);


$ b public BufferedImage createWheel(int radius,int spoke){
BufferedImage img = new BufferedImage(radius,radius,BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.black);
g2d.drawOval(0,0,radius - 1,radius - 1);

Point2D center = new Point2D.Double(radius / 2d,radius / 2d);
双角度= 360d /辐条;
for(int index = 0; index< spokes; index ++){
Point2D p = calculateOutterPoint(radius,index * angle);
g2d.draw(new Line2D.Double(center,p));
}

g2d.dispose();
return img;

}

}


I am trying to display two rotating wheels with diameter 512untis at different rates but i am not able to remove the previous drawn image graphics and set the rotated graphics at the correct position. For now i am doing a rotation with arbitrary angle. I tried affineTransform and got the rotations but it was weird like all pixels spread away. Im using a while loop with thread.sleep(). The following is the code : //The drawSmallCircle and drawBigCircle return two images.

class MyFramePart2 extends JFrame

{ 

    String name;
    JPanel big_obj_panel,small_obj_panel;    
    JLabel bigLabel,smallLabel;BufferedImage imgRet,imgRetSmall;
    static double radians,angle,rev,fps,smallAngle,smallRadians;               
    int numLines,i=0;    

    MyFramePart2(String frameName,int numStrokes,double revolutions,double frameps)
    {       
        numLines=numStrokes;
        smallAngle=smallRadians=angle=radians=Math.toRadians(360/numLines);        
        rev=revolutions;
        fps=frameps; 

        setSize(1240,720);       
        setLocation(0,0);
        setLayout(null); 
        getContentPane().setBackground(Color.WHITE);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

        big_obj_panel=new JPanel();
        big_obj_panel.setLayout(null);
        big_obj_panel.setSize(512,512);
        big_obj_panel.setLocation(100,100); 
        big_obj_panel.setBackground(Color.white);
        add(big_obj_panel);   


        imgRet=drawBigCircle();
        bigLabel = new JLabel(new ImageIcon(imgRet));
        bigLabel.setLayout(null);        
        bigLabel.setLocation(0,0);
        bigLabel.setSize(512,512);
        bigLabel.setOpaque(true);
        bigLabel.setBackground(Color.white);  
        big_obj_panel.add(bigLabel);

        small_obj_panel=new JPanel();
        small_obj_panel.setLayout(null);
        small_obj_panel.setSize(512,512);
        small_obj_panel.setLocation(700,100); 
        small_obj_panel.setBackground(Color.white);
        add(small_obj_panel);   

        imgRetSmall=drawSmallCircle();

        smallLabel = new JLabel(new ImageIcon(imgRetSmall));
        smallLabel.setLayout(null);        
        smallLabel.setLocation(0,0);
        smallLabel.setSize(512,512);
        smallLabel.setOpaque(true);
        smallLabel.setBackground(Color.white);  
        small_obj_panel.add(smallLabel);

        setVisible(true);               

        while(i!=5) // suppose to be while true, just checking 
      {                 
        setVisible(true);          
        bigLabel.setIcon(new ImageIcon(imgRet));         
        smallLabel.setIcon(new ImageIcon(imgRetSmall));
        try{
        Thread.sleep(10);        
        }
        catch(Exception e)
        {}
        i++;               
     }                         
 }

    @Override
    public void paint(Graphics g)
    {        
        super.paint(g);
        Graphics2D g2d=(Graphics2D)g;           
        g2d.translate(256,256);
        g2d.rotate(Math.toRadians(20));                       
        g2d.translate(-256,-256); 
        g2d.drawImage(imgRet,0,0,null);
        g2d.dispose();        

        super.paint(g);
        g2d=(Graphics2D)g;
        g2d.translate(256,256);
        g2d.rotate(Math.toRadians(30));
        g2d.translate(-256,-256);        
        g2d.drawImage(imgRetSmall,0,0,null);        
        g2d.dispose();
    }

    public static BufferedImage drawBigCircle()
    {
        BufferedImage img=new BufferedImage(512,512,BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d=img.createGraphics();                
        g2d.setColor(Color.black);
        g2d.drawOval(0,0,511,511);

        Line2D l2d;
        while(angle <= 2*Math.PI)
        {
            l2d=new Line2D.Double(256,256,256+256*Math.cos(angle),256+256*Math.sin(angle));
            g2d.draw(l2d);
            angle=angle+radians;
        }               
        return img;
    }        
}

解决方案

First rule of Swing. Don't block the Event Dispatching Thread. Doing so will make you application look like it's hung and prevent the EDT from processing any repaint requests.

This means, you need some way to schedule updates that doesn't block the EDT

Second rule of Swing. Don't create or modify any UI component from any thread other then the EDT.

Generally, you should avoid overriding the paint method of top level containers like JFrame, apart from everything else, they're not double buffered, meaning your painting will flicker as it's updated. Instead, you should use one of the Swing containers, like JPanel

There are lots of different ways to achieve this. Basically, here I've used three lists, but if I was serious, I would create an object that could maintain all the required information (image, angle an delta)

In order to achieve the actual animation, I've used a javax.swing.Timer. This will trigger an event at least every n periods, but more importantly, it does it within the context of the Event Dispatching Thread. This ensures that all the changes made to the angles are done in way that will prevent any possibility of painting occurring while we're updating the values...

This example rotates the three images at different (random) speeds...

public class TestRotation {

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

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

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new AnimationPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class AnimationPane extends JPanel {

        private List<BufferedImage> images;
        private List<Double> angles;
        private List<Double> speed;

        public AnimationPane() {
            images = new ArrayList<>(5);
            images.add(createWheel(50, 4));
            images.add(createWheel(50, 3));
            images.add(createWheel(50, 6));
            angles = new ArrayList<>();
            speed = new ArrayList<>();
            for (int index = 0; index < images.size(); index++) {
                angles.add(0d);
                speed.add(Math.random() * 5d);
            }

            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (int index = 0; index < angles.size(); index++) {
                        double angle = angles.get(index);
                        double delta = speed.get(index);
                        angle += delta;
                        angles.set(index, angle);
                    }
                    repaint();
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();

        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int x = 0;
            int y = 0;
            for (int index = 0; index < images.size(); index++) {
                BufferedImage image = images.get(index);
                double angle = angles.get(index);

                // This is important.  Basically we going to grab a isolated snap shot
                // of the current graphics context.  This means any changes we make
                // will not affect the original graphics context (other then painting)
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                AffineTransform at = new AffineTransform();
                at.translate(x, y);
                at.rotate(Math.toRadians(angle), image.getWidth() / 2, image.getHeight() / 2);
                g2d.setTransform(at);
                g2d.drawImage(image, 0, 0, this);
                g2d.dispose();

                x += image.getWidth();

            }
        }

    }

    protected Point2D calculateOutterPoint(int radius, double angel) {

        int x = Math.round(radius / 2);
        int y = Math.round(radius / 2);

        double rads = Math.toRadians((angel + 90));

        // This determins the length of tick as calculate from the center of
        // the circle.  The original code from which this derived allowed
        // for a varible length line from the center of the cirlce, we
        // actually want the opposite, so we calculate the outter limit first
        double fullLength = (radius / 2d);

        // Calculate the outter point of the line
        double xPosy = (x + Math.cos(rads) * fullLength);
        double yPosy = (y - Math.sin(rads) * fullLength);

        return new Point2D.Double(xPosy, yPosy);

    }

    public BufferedImage createWheel(int radius, int spokes) {
        BufferedImage img = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = img.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(Color.black);
        g2d.drawOval(0, 0, radius - 1, radius - 1);

        Point2D center = new Point2D.Double(radius / 2d, radius / 2d);
        double angle = 360d / spokes;
        for (int index = 0; index < spokes; index++) {
            Point2D p = calculateOutterPoint(radius, index * angle);
            g2d.draw(new Line2D.Double(center, p));
        }

        g2d.dispose();
        return img;

    }

}

这篇关于旋转后图像不在适当位置(图形)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆