在使用Java Swing将缩放操作拆分为一个转换操作时,我发现了一些奇怪的东西。也许我做了一些愚蠢的事情,但我不确定在哪里。
在第一个版本中,我将图像居中,缩放,然后将其转换为所需位置。 在第二个版本中,我直接缩放图像,然后转换到所需位置,补偿具有非居中图像。 这两种解决方案应该是等效的。当考虑一个点周围的旋转和另一个点的运动时,这也是很重要的。我的代码也是这样做的......但为什么这不起作用?
以下是代码的两个版本。他们应该做同样的事情,但事实并非如此。以下是截图:
首先产生:screenshot1
第二次产生:screenshot2
我认为draw1中围绕scale操作的两个转换操作应该等同于draw2中的scale转换操作。
有什么建议吗?
MCVE:
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.net.URL;
public class Asteroid extends JComponent implements ActionListener {
public static final Dimension FRAME_SIZE = new Dimension(640, 480);
public double x = 200;
public double y = 200;
public int radius = 40;
private AffineTransform bgTransfo;
private final BufferedImage im2;
private JCheckBox draw1Check = new JCheckBox("Draw 1", true);
Asteroid() {
BufferedImage img = null;
try {
img = ImageIO.read(new URL("https://i.stack.imgur.com/CWJdo.png"));
} catch (Exception e) {
e.printStackTrace();
}
im2 = img;
initUI();
}
private final void initUI() {
draw1Check.addActionListener(this);
JFrame frame = new JFrame("FrameDemo");
frame.add(BorderLayout.CENTER, this);
frame.add(BorderLayout.PAGE_START, draw1Check);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
Asteroid asteroid = new Asteroid();
}
@Override
public Dimension getPreferredSize() {
return FRAME_SIZE;
}
@Override
public void paintComponent(Graphics g0) {
Graphics2D g = (Graphics2D) g0;
g.setColor(Color.white);
g.fillRect(0, 0, 640, 480);
if (draw1Check.isSelected()) {
draw1(g);
} else {
draw2(g);
}
}
public void draw1(Graphics2D g) {//Draw method - draws asteroid
double imWidth = im2.getWidth();
double imHeight = im2.getHeight();
double stretchx = (2.0 * radius) / imWidth;
double stretchy = (2.0 * radius) / imHeight;
bgTransfo = new AffineTransform();
//centering
bgTransfo.translate(-imWidth / 2.0, -imHeight / 2.0);
//scaling
bgTransfo.scale(stretchx, stretchy);
//translation
bgTransfo.translate(x / stretchx, y / stretchy);
//draw correct position
g.setColor(Color.CYAN);
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));
//draw sprite
g.drawImage(im2, bgTransfo, this);
}
public void draw2(Graphics2D g) {//Draw method - draws asteroid
double imWidth = im2.getWidth();
double imHeight = im2.getHeight();
double stretchx = (2.0 * radius) / imWidth;
double stretchy = (2.0 * radius) / imHeight;
bgTransfo = new AffineTransform();
//scale
bgTransfo.scale(stretchx, stretchy);
//translate and center
bgTransfo.translate((x - radius) / stretchx, (y - radius) / stretchy);
//draw correct position
g.setColor(Color.CYAN);
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));
//draw sprite
g.drawImage(im2, bgTransfo, this);
}
@Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}
答案 0 :(得分:0)
不确定这个问题是否真的开放。无论如何,这是我的答案。
我认为理解这种行为的关键部分是AffineTransform.concatenate
和AffineTransform.preConcatenate
方法之间的区别。问题是结果转换取决于子转换的应用顺序。
引用concatenate
JavaDoc
以最常用的方式将AffineTransform
Tx
与此AffineTransformCx
连接,以提供Tx
映射到以前用户空间的新用户空间。更新Cx
以执行组合转换。通过更新后的转换p
转换点Cx'
相当于首先将p
转换为Tx
,然后将结果转换为原始转换Cx
,如下所示:Cx'(p) = Cx(Tx(p))
将其与preConcatenate
进行比较:
以不太常用的方式将AffineTransform
Tx
与此AffineTransformCx
连接,以便Tx
修改相对于绝对像素空间的坐标转换,而不是相对于现有用户空间。更新Cx
以执行组合转换。通过更新后的转换p
转换点Cx'
相当于首先使用原始转换p
转换Cx
,然后将结果转换为Tx,如下所示:{{1} }
scale
和translate
方法实际上是Cx'(p) = Tx(Cx(p))
。让我们在concatenate
方法draw1
(中心),C
(比例)和S
(翻译)中调用3次转换。因此,您的复合转换实际上是T
。特别是,这意味着C(S(T(p)))
已应用于S
,但未应用于T
,因此您的C
并未真正将图像置于中心位置。一个简单的解决方法是更改C
和S
的顺序,但我认为更合适的解决方案是这样的:
C
我认为这种方法的最大优点是您无需使用public void draw3(Graphics2D g) {
//Draw method - draws asteroid
double imWidth = im2.getWidth();
double imHeight = im2.getHeight();
double stretchx = (2.0 * radius) / imWidth;
double stretchy = (2.0 * radius) / imHeight;
AffineTransform bgTransfo = new AffineTransform();
//translation
bgTransfo.translate(x, y);
//scaling
bgTransfo.scale(stretchx, stretchy);
//centering
bgTransfo.translate(-imWidth / 2.0, -imHeight / 2.0);
//draw correct position
g.setColor(Color.CYAN);
g.fillOval((int) (x - radius), (int) (y - radius), (int) (2 * radius), (int) (2 * radius));
//draw sprite
g.drawImage(im2, bgTransfo, this);
}
/ T
stretchx