/*
* $Id$
*
* Copyright (c) 2007-2008 by Joel Uckelman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.tools.imageop;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang.builder.HashCodeBuilder;
import VASSAL.tools.image.ImageUtils;
public class OrthoRotateOpBitmapImpl extends AbstractTiledOpImpl
implements RotateOp {
private final ImageOp sop;
private final int angle;
private final int hash;
public OrthoRotateOpBitmapImpl(ImageOp sop, int angle) {
if (sop == null) throw new IllegalArgumentException();
angle = (360 + (angle % 360)) % 360; // put angle in [0,360)
if (angle % 90 != 0) throw new IllegalArgumentException();
// angle is now in { 0, 90, 180, 270 }.
this.sop = sop;
this.angle = angle / 90;
hash = new HashCodeBuilder().append(sop)
.append(angle)
.toHashCode();
}
public List<VASSAL.tools.opcache.Op<?>> getSources() {
return Collections.<VASSAL.tools.opcache.Op<?>>singletonList(sop);
}
public BufferedImage eval() throws Exception {
final BufferedImage src = sop.getImage(null);
if (size == null) fixSize();
// remain opaque if our parent image is
final BufferedImage dst = ImageUtils.createCompatibleImage(
size.width, size.height, src.getTransparency() != BufferedImage.OPAQUE
);
final Graphics2D g = dst.createGraphics();
g.rotate(Math.PI/2.0*angle, src.getWidth()/2.0, src.getHeight()/2.0);
g.drawImage(src, 0, 0, null);
g.dispose();
return dst;
}
protected void fixSize() {
if ((size = getSizeFromCache()) == null) {
size = sop.getSize();
// transpose dimensions for 90- and 270-degree rotations
if (angle == 1 || angle == 3)
size.setSize(size.height, size.width);
}
}
public double getAngle() {
return angle * 90;
}
public RenderingHints getHints() {
// return ImageUtils.getDefaultHints();
return null;
}
protected ImageOp createTileOp(int tileX, int tileY) {
return new TileOp(this, tileX, tileY);
}
private static class TileOp extends AbstractTileOpImpl {
private final ImageOp sop;
private final int angle;
private final int hash;
public TileOp(OrthoRotateOpBitmapImpl rop, int tileX, int tileY) {
if (rop == null) throw new IllegalArgumentException();
if (tileX < 0 || tileX >= rop.getNumXTiles() ||
tileY < 0 || tileY >= rop.getNumYTiles())
throw new IndexOutOfBoundsException();
this.angle = rop.angle;
final int sx0, sy0, sx1, sy1;
switch (angle) {
case 0:
sx0 = tileX*rop.tileSize.width;
sy0 = tileY*rop.tileSize.height;
sx1 = Math.min((tileX+1)*rop.tileSize.width, rop.size.width);
sy1 = Math.min((tileY+1)*rop.tileSize.height, rop.size.height);
break;
case 1:
sx0 = tileY*rop.tileSize.height;
sy0 = tileX*rop.tileSize.width;
sx1 = Math.min((tileY+1)*rop.tileSize.height, rop.size.height);
sy1 = Math.min((tileX+1)*rop.tileSize.width, rop.size.width);
break;
case 2:
sx1 = rop.size.width - tileX*rop.tileSize.width;
sy1 = rop.size.height - tileY*rop.tileSize.height;
sx0 = rop.size.width -
Math.min((tileX+1)*rop.tileSize.width, rop.size.width);
sy0 = rop.size.height -
Math.min((tileY+1)*rop.tileSize.height, rop.size.height);
break;
case 3:
default:
sx1 = rop.size.height - tileY*rop.tileSize.height;
sy1 = rop.size.width - tileX*rop.tileSize.width;
sx0 = rop.size.height -
Math.min((tileY+1)*rop.tileSize.height, rop.size.height);
sy0 = rop.size.width -
Math.min((tileX+1)*rop.tileSize.width, rop.size.width);
break;
}
size = new Dimension(sx1-sx0, sy1-sy0);
sop = new CropOpBitmapImpl(rop.sop, sx0, sy0, sx1, sy1);
hash = new HashCodeBuilder().append(sop)
.append(angle)
.toHashCode();
}
public List<VASSAL.tools.opcache.Op<?>> getSources() {
return Collections.<VASSAL.tools.opcache.Op<?>>singletonList(sop);
}
public BufferedImage eval() throws Exception {
final BufferedImage src = sop.getImage(null);
// remain opaque if our parent image is
final BufferedImage dst = ImageUtils.createCompatibleImage(
size.width, size.height, src.getTransparency() != BufferedImage.OPAQUE
);
final Graphics2D g = dst.createGraphics();
g.rotate(Math.PI/2.0*angle, src.getWidth()/2.0, src.getHeight()/2.0);
g.drawImage(src, 0, 0, null);
g.dispose();
return dst;
}
protected void fixSize() { }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || o.getClass() != this.getClass()) return false;
final TileOp op = (TileOp) o;
return angle == op.angle &&
sop.equals(op.sop);
}
@Override
public int hashCode() {
return hash;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || o.getClass() != this.getClass()) return false;
final OrthoRotateOpBitmapImpl op = (OrthoRotateOpBitmapImpl) o;
return angle == op.getAngle() && sop.equals(op.sop);
}
@Override
public int hashCode() {
return hash;
}
}