AffineTransform.rotate() - 如何同时进行xlate,旋转和缩放?

时间:2012-08-11 03:17:52

标签: java graphics awt image-rotation affinetransform

我有以下代码(我的第一部分)想要绘制棋盘,上面有一些棋子。

              Image pieceImage = getImage(currentPiece);
              int pieceHeight = pieceImage.getHeight(null);
              double scale = (double)side/(double)pieceHeight;
              AffineTransform transform = new AffineTransform();
              transform.setToTranslation(xPos, yPos);
              transform.scale(scale, scale);
              realGraphics.drawImage(pieceImage, transform, this);

也就是说,它获得了棋子的图像和图像的高度,它将该图像的图形转换为图像所在的正方形,并将图像缩放到正方形的大小。

Llet说我想将黑色棋子旋转180度。在某个地方,我希望有类似的东西:

transform.rotate(Math.toRadians(180) /* ?, ? */);

但是我无法弄清楚要放入X和Y的内容。如果我什么都没放,那么图像会很好地围绕棋盘方块的0点旋转,将棋子倒置在广场上应该在哪里的东北部。我已经猜到了x,y的各种其他组合,但没有运气。

我已经在使用平移把这个块放在正方形中,旋转变换需要另一个x,y围绕它旋转东西,但是我不知道如何告诉变换将这个块旋转到一个x周围, y并将图像写入不同的x,y。有人可以帮助我使用旋转参数,还是指出一些能解释这些东西如何工作的东西?我找到了一些不能解释它们是如何工作的事情的例子,到目前为止我还没有想出如何根据我的情况改变它们......


主要编辑:添加工作代码。对不起,我不知道如何发布图片,请替换自己的图片。

当我运行以下内容时,我会得到一个2x2的棋盘,左上方有一个车,右下方有一个骑士。

如果我进入SmallChessboardComponent并从第一个旋转变换语句中删除注释delims,我会将原始位置的车辆倒置并且骑士不会出现。如果我而不是从第二个转换语句中删除注释,则根本不会显示任何一个。

我正在寻找一种方法将这些碎片倒置在它们无论如何都会出现的正方形上。我想把每一块都画在板上;我不希望代码翻转电路板。

主程序:

package main;

import java.awt.BorderLayout;

import javax.swing.JFrame;

import directredraw.SmallChessboardComponent;

public class SmallChessboardMain
{
  private static void dbg (String message) { System.out.println(message); }

  public static void main(String[] args)
  {
    //Create the top-level container and add contents to it.
    final JFrame frame = new JFrame("Small Chessboard");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // create the chessboard itself and set it in the component
    SmallChessboard chessboard = new SmallChessboard();

    // create the GUI component that will contain the chessboard
    SmallChessboardComponent chessboardComponent = new SmallChessboardComponent();
    chessboardComponent.setBoard (chessboard);

    frame.getContentPane().add(chessboardComponent, BorderLayout.CENTER);

    // pack and display all this
    frame.pack();
    frame.setVisible(true);
  }
}

棋盘类:

package main;

public class SmallChessboard
{
  Piece [][] squares = new Piece[2][2];

  public SmallChessboard()
  {
    squares[0][0] = new Piece(Piece.WHITECOLOR, Piece.ROOK);
    squares[1][1] = new Piece(Piece.WHITECOLOR, Piece.KNIGHT);
  }

  /**
   * get the piece at the given rank and file; null if
   * no piece exists there.
   */
  public Piece getPiece(int rank, int file)
  { 
    if (0 > rank || rank > 2 || 0 > file || file > 2) { return null; }
      else { return squares[rank][file]; }
  }
}

棋盘组件类:

package directredraw;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;

import javax.swing.JPanel;

import main.Piece;
import main.PieceImages;
import main.SmallChessboard;


public class SmallChessboardComponent extends JPanel
  { 
    private static final long serialVersionUID = 1L;

    Color whiteSquareColor = Color.yellow;
    Color blackSquareColor = Color.blue;

    private static void dbg (String msg) { System.out.println(msg); }

    private SmallChessboard  chessboard = null;

    // currently playing with rotating images; this affine transform
    // should help
    AffineTransform rotationTransform = null;

    private final int DEFAULT_PREFERRED_SIDE = 400;
    int wholeSide = DEFAULT_PREFERRED_SIDE;
    int side = DEFAULT_PREFERRED_SIDE / 8;

    public void setBoard (SmallChessboard givenBoard)
    { chessboard = givenBoard;
    }

    /**
     * set either or both colors for this chessboard; if either of
     * the arguments are null, they do not change the existing color
     * setting.
     */
    public void setColors (Color darkSquare, Color lightSquare)
    {
      if (darkSquare != null) { blackSquareColor = darkSquare; }
      if (lightSquare != null) { whiteSquareColor = lightSquare; }
    }

    /**
     * return the preferred size for this component.s
     */
    public Dimension getPreferredSize()
    { return new Dimension(wholeSide, wholeSide);
    }

    /*
     * return the image object for the given piece
     */
    private Image getImage(Piece piece)
    { return PieceImages.getPieceImage(this, piece);
    }

    public void paintComponent (Graphics graphics)
    {
      Graphics2D realGraphics = (Graphics2D) graphics;

      // the image container might have been stretched.
      // calculate the largest square held by the current container,
      // and then 1/2 of that size for an individual square.
      int wholeWidth  = this.getWidth();
      int wholeHeight = this.getHeight();
      wholeSide   = (wholeWidth / 2) * 2;
      if (wholeHeight < wholeWidth) { wholeSide = (wholeHeight / 2) * 2; }
      side = wholeSide / 2; 

      Rectangle clip = realGraphics.getClipBounds();
      boolean firstColumnWhite = false;

      // for each file on the board:
      //    set whether top square is white
      //    set background color according to white/black square
      //    
      for (int fileIndex=0; fileIndex<8; fileIndex++)
        { boolean currentColorWhite = firstColumnWhite;
          firstColumnWhite = !firstColumnWhite;

          // draw the board and all the pieces
          int rankIndex = 2;
          for (rankIndex=2; rankIndex>=0; rankIndex--)
          { 

            currentColorWhite = !currentColorWhite;

            // x and y position of the top left corner of the square we're drawing,
            // and rect becomes the dimensions and position of the square itself.
            int xPos = fileIndex * side;
            int yPos = rankIndex * side;
            Rectangle rect = new Rectangle(xPos, yPos, side, side);

            // if this square intersects the clipping rectangle we're drawing,
            // then we'll draw the square and the piece on the square.
            if (rect.intersects(clip))
            {
              // this puts down the correct color of square 
              if (currentColorWhite) { realGraphics.setColor(whiteSquareColor); }
                                else { realGraphics.setColor(blackSquareColor); }
              realGraphics.fillRect(xPos, yPos, side, side); 

              // if there is a piece on this square and it isn't selected at the
              // moment, then draw it.
              Piece currentPiece = chessboard.getPiece(rankIndex, fileIndex);
              if (currentPiece != null)
                { 
                  Image pieceImage = getImage(currentPiece);
                  int pieceHeight = pieceImage.getHeight(null);
                  double scalePiece = (double)side/(double)pieceHeight;
                  AffineTransform transform = new AffineTransform();
//                  transform.setToRotation(Math.toRadians(180));
                  transform.setToRotation(Math.toRadians(180), side/2, side/2);
                  transform.scale(scalePiece, scalePiece);
                  transform.translate(xPos/scalePiece, yPos/scalePiece);
//                  if (currentPiece.isBlack()) 
//                  {
//                    transform.translate(xPos + (side+2), yPos + (side+2));
//                    transform.rotate(Math.toRadians(180) /*, ,*/ ); 
//                  }
//                  else
//                  {
//                    transform.translate(xPos, yPos);
//                  }
                  realGraphics.drawImage(pieceImage, transform, this);
                }
            }
          }
        }
    }
  }

Piece.java

package main;

public class Piece
{ 
  // piece types; the sum of the piece type and the
  // color gives a number unique to both type and color,
  // which is used for things like image indices.
  public static final int PAWN   = 0;
  public static final int KNIGHT = 1;
  public static final int BISHOP = 2;
  public static final int ROOK   = 3;
  public static final int QUEEN  = 4;
  public static final int KING   = 5;

  // one of these is the color of the current piece
  public static final int NOCOLOR = -1;
  // the sum of the piece type and the
  // color gives a number unique to both type and color,
  // which is used for things like image indices.
  public static final int BLACKCOLOR = 0;
  public static final int WHITECOLOR = 6;

  int color = NOCOLOR;
  int imageIndex;

  public Piece(int color, int pieceType)
  { 
    // dbg -- all pieces are white rooks for now...
    this.color  = color;
    imageIndex  = color + pieceType;
  }

  /**
   * return the integer associated with this piece's color;
   */
  int getPieceColor()
  { return color;
  }

  /**
   * return true if the piece is black
   */
  public boolean isBlack() 
  { 
    return (color == BLACKCOLOR); 
  }

  /**
   * set the color associated with this piece; constants
   * found in this class.
   */
  public void setPieceColor(int givenColor)
  { color = givenColor;
  }

  /**
   * return the integer designated for the image used for this piece.
   */
  int getImageIndex()
  { return imageIndex;
  }

}

和PieceImages.java

package main;

import java.awt.Component;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.net.URL;

public class PieceImages
{ static Image images[] = null;

private static void dbg (String msg) { System.out.println(msg); } 

  public static Image getPieceImage (Component target, Piece piece)
  {
    if (images == null)
    try
    { 
      MediaTracker tracker = new MediaTracker(target);
      images = new Image[12];
      images[Piece.BLACKCOLOR + Piece.PAWN] = getImage(tracker, "bPawn.gif");
      images[Piece.BLACKCOLOR + Piece.KNIGHT] = getImage(tracker, "bKnight.gif");
      images[Piece.BLACKCOLOR + Piece.BISHOP] = getImage(tracker, "bBishop.gif");
      images[Piece.BLACKCOLOR + Piece.ROOK] = getImage(tracker, "bRook.gif");
      images[Piece.BLACKCOLOR + Piece.QUEEN] = getImage(tracker, "bQueen.gif");
      images[Piece.BLACKCOLOR + Piece.KING] = getImage(tracker, "bKing.gif");

      images[Piece.WHITECOLOR + Piece.PAWN] = getImage(tracker, "wPawn.gif");
      images[Piece.WHITECOLOR + Piece.KNIGHT] = getImage(tracker, "wKnight.gif");
      images[Piece.WHITECOLOR + Piece.BISHOP] = getImage(tracker, "wBishop.gif");
      images[Piece.WHITECOLOR + Piece.ROOK] = getImage(tracker, "wRook.gif");
      images[Piece.WHITECOLOR + Piece.QUEEN] = getImage(tracker, "wQueen.gif");
      images[Piece.WHITECOLOR + Piece.KING] = getImage(tracker, "wKing.gif");
      if (!tracker.waitForAll(10000))
      { System.out.println("ERROR: not all piece main.images loaded");
      }
      dbg("piece images loaded");
    }
    catch (Exception xcp)
    { System.out.println("Error loading images");
      xcp.printStackTrace();
    }
    return images[piece.getImageIndex()];
  }

  private static Image getImage(MediaTracker tracker, String file)
  {
    URL url = PieceImages.class.getResource("images/" + file);
    Image image = Toolkit.getDefaultToolkit().getImage(url);
    tracker.addImage(image,  1);
    return image;
  }
}

2 个答案:

答案 0 :(得分:4)

好的,这有点轻微。示例代码仅适用于90度增量(它只是以这种方式设计),要做较小的增量,你可以使用一些触发来计算图像的宽度和高度(那里有一个答案;)< / p>

public class ImagePane extends JPanel {

    private BufferedImage masterImage;
    private BufferedImage renderedImage;

    public ImagePane(BufferedImage image) {
        masterImage = image;
        applyRotation(0);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(renderedImage.getWidth(), renderedImage.getHeight());
    }

    @Override
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

    protected int getVirtualAngle(int angle) {
        float fRotations = (float) angle / 360f;
        int rotations = (int) (fRotations - (fRotations / 1000));

        int virtual = angle - (rotations * 360);

        if (virtual < 0) {
            virtual = 360 + virtual;
        }

        return virtual;
    }

    public void applyRotation(int angle) {
        // This will only work for angles of 90 degrees...

        // Normalize the angle to make sure it's only between 0-360 degrees
        int virtualAngle = getVirtualAngle(angle);
        Dimension size = new Dimension(masterImage.getWidth(), masterImage.getHeight());
        int masterWidth = masterImage.getWidth();
        int masterHeight = masterImage.getHeight();

        double x = 0; //masterWidth / 2.0;
        double y = 0; //masterHeight / 2.0;

        switch (virtualAngle) {
            case 0:
                break;
            case 180:
                break;
            case 90:
            case 270:
                size = new Dimension(masterImage.getHeight(), masterImage.getWidth());
                x = (masterHeight - masterWidth) / 2.0;
                y = (masterWidth - masterHeight) / 2.0;
                break;
        }
        renderedImage = new BufferedImage(size.width, size.height, masterImage.getTransparency());
        Graphics2D g2d = renderedImage.createGraphics();

        AffineTransform at = AffineTransform.getTranslateInstance(x, y);

        at.rotate(Math.toRadians(virtualAngle), masterWidth / 2.0, masterHeight / 2.0);
        g2d.drawImage(masterImage, at, null);

        g2d.dispose();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;
        int width = getWidth() - 1;
        int height = getHeight() - 1;

        int x = (width - renderedImage.getWidth()) / 2;
        int y = (height - renderedImage.getHeight()) / 2;

        g2d.drawImage(renderedImage, x, y, this);
    }

}

现在,你可以简单地&#34;翻转&#34;垂直图像,如果这对您更有效

public class FlipPane extends JPanel {

    private BufferedImage masterImage;
    private BufferedImage renderedImage;

    public FlipPane(BufferedImage image) {
        masterImage = image;
        flipMaster();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(renderedImage.getWidth(), renderedImage.getHeight());
    }

    @Override
    public Dimension getMinimumSize() {
        return getPreferredSize();
    }

    protected void flipMaster() {
        renderedImage = new BufferedImage(masterImage.getWidth(), masterImage.getHeight(), masterImage.getTransparency());
        Graphics2D g2d = renderedImage.createGraphics();
        g2d.setTransform(AffineTransform.getScaleInstance(1, -1));
        g2d.drawImage(masterImage, 0, -masterImage.getHeight(), this);
        g2d.dispose();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g;
        int width = getWidth() - 1;
        int height = getHeight() - 1;

        int x = (width - renderedImage.getWidth()) / 2;
        int y = (height - renderedImage.getHeight()) / 2;

        g2d.drawImage(renderedImage, x, y, this);
    }
}

这基本上导致:

Image rotation example

原创| 180度旋转|垂直反转......

现在,如果您将flipMaster方法更改为:

g2d.setTransform(AffineTransform.getScaleInstance(-1, -1));
g2d.drawImage(masterImage, -masterImage.getWidth(), -masterImage.getHeight(), this);

你将获得与180度旋转相同的效果;)

答案 1 :(得分:2)

尝试在将其旋转到正确位置之前执行旋转。只需重新排序转换,然后首先缩放,然后旋转(围绕图像的中心点),然后翻译:

transform.scale(scale, scale);
transform.rotate(Math.PI, pieceWidth / 2, pieceHeight /2);
transform.translation(xPos, yPos);

顺便说一下,棋盘上的黑棋子通常不会旋转。 :)

<强>更新

它以什么方式不起作用?我提供的解决方案也与您的代码的不同之处在于,在翻译之前执行缩放。您可以尝试旋转,平移,然后缩放。

我强烈建议您修改代码,以便最后执行翻译。如果你这样做,一切都会变得复杂得多。完成后,您只需缩放一次即可自动处理旋转。

transform.scale(scale, scale); // or transform.scale(scale, -scale); to rotate
transform.translate(xPos, yPos);