// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.gui.converter;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.awt.image.IndexColorModel;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import org.infinity.gui.ViewerUtil;
import org.infinity.resource.graphics.PseudoBamDecoder.PseudoBamFrameEntry;
import org.infinity.util.Misc;
/**
* Transform filter: rotates each frame by a specified amount.
*/
public class BamFilterTransformRotate extends BamFilterBaseTransform implements ActionListener
{
private static final String FilterName = "Rotate BAM frames";
private static final String FilterDesc = "This filter allows you to rotate each BAM frame by a specified amount.";
private static final int ANGLE_90 = 0;
private static final int ANGLE_180 = 1;
private static final int ANGLE_270 = 2;
private static final String[] AngleItems = {"90\u00B0", "180\u00B0", "270\u00B0"};
private JRadioButton rbCW, rbCCW;
private JCheckBox cbAdjustCenter;
private JComboBox<String> cbAngle;
public static String getFilterName() { return FilterName; }
public static String getFilterDesc() { return FilterDesc; }
public BamFilterTransformRotate(ConvertToBam parent)
{
super(parent, FilterName, FilterDesc);
}
@Override
public PseudoBamFrameEntry process(PseudoBamFrameEntry entry) throws Exception
{
return applyEffect(entry);
}
@Override
public PseudoBamFrameEntry updatePreview(PseudoBamFrameEntry entry)
{
return applyEffect(entry);
}
@Override
public String getConfiguration()
{
StringBuilder sb = new StringBuilder();
sb.append(rbCW.isSelected() ? 0 : 1).append(';');
sb.append(cbAngle.getSelectedIndex()).append(';');
sb.append(cbAdjustCenter.isSelected());
return sb.toString();
}
@Override
public boolean setConfiguration(String config)
{
if (config != null) {
config = config.trim();
if (!config.isEmpty()) {
String[] params = config.split(";");
int orientation = -1;
int angle = -1;
boolean adjust = true;
if (params.length > 0) {
orientation = Misc.toNumber(params[0], -1);
if (orientation < 0 || orientation > 1) {
return false;
}
}
if (params.length > 1) {
angle = Misc.toNumber(params[1], -1);
if (angle < 0 || angle >= cbAngle.getModel().getSize()) {
return false;
}
}
if (params.length > 2) {
if (params[2].equalsIgnoreCase("true")) {
adjust = true;
} else if (params[2].equalsIgnoreCase("false")) {
adjust = false;
} else {
return false;
}
}
if (orientation >= 0) {
if (orientation == 0) {
rbCW.setSelected(true);
} else {
rbCCW.setSelected(true);
}
}
if (angle >= 0) {
cbAngle.setSelectedIndex(angle);
}
cbAdjustCenter.setSelected(adjust);
}
return true;
}
return false;
}
@Override
protected JPanel loadControls()
{
GridBagConstraints c = new GridBagConstraints();
JLabel l1 = new JLabel("Orientation:");
JLabel l2 = new JLabel("Angle:");
ButtonGroup bg = new ButtonGroup();
rbCW = new JRadioButton("Clockwise", true);
rbCW.addActionListener(this);
rbCCW = new JRadioButton("Counter clockwise");
rbCCW.addActionListener(this);
bg.add(rbCW);
bg.add(rbCCW);
cbAngle = new JComboBox<>(AngleItems);
cbAngle.addActionListener(this);
cbAdjustCenter = new JCheckBox("Adjust center position", true);
cbAdjustCenter.addActionListener(this);
JPanel p = new JPanel(new GridBagLayout());
ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
p.add(l1, c);
ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0);
p.add(rbCW, c);
ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0);
p.add(rbCCW, c);
ViewerUtil.setGBC(c, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END,
GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0);
p.add(l2, c);
ViewerUtil.setGBC(c, 1, 1, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(4, 8, 0, 0), 0, 0);
p.add(cbAngle, c);
ViewerUtil.setGBC(c, 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0);
p.add(new JPanel(), c);
ViewerUtil.setGBC(c, 1, 2, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START,
GridBagConstraints.NONE, new Insets(12, 4, 0, 0), 0, 0);
p.add(cbAdjustCenter, c);
JPanel panel = new JPanel(new GridBagLayout());
ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0);
panel.add(p, c);
return panel;
}
//--------------------- Begin Interface ActionListener ---------------------
@Override
public void actionPerformed(ActionEvent event)
{
if (event.getSource() == rbCW || event.getSource() == rbCCW || event.getSource() == cbAngle ||
event.getSource() == cbAdjustCenter) {
fireChangeListener();
}
}
//--------------------- Begin Interface ActionListener ---------------------
private PseudoBamFrameEntry applyEffect(PseudoBamFrameEntry entry)
{
if (entry != null && entry.getFrame() != null) {
int width = entry.getFrame().getWidth();
int height = entry.getFrame().getHeight();
BufferedImage dstImage = null;
int newWidth, newHeight;
switch (cbAngle.getSelectedIndex()) {
case ANGLE_90:
case ANGLE_270:
newWidth = height; newHeight = width;
break;
default:
newWidth = width; newHeight = height;
}
byte[] srcB = null, dstB = null;
int[] srcI = null, dstI = null;
if (entry.getFrame().getType() == BufferedImage.TYPE_BYTE_INDEXED) {
srcB = ((DataBufferByte)entry.getFrame().getRaster().getDataBuffer()).getData();
IndexColorModel cm = (IndexColorModel)entry.getFrame().getColorModel();
dstImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_BYTE_INDEXED, cm);
dstB = ((DataBufferByte)dstImage.getRaster().getDataBuffer()).getData();
} else if (entry.getFrame().getRaster().getDataBuffer().getDataType() == DataBuffer.TYPE_INT) {
srcI = ((DataBufferInt)entry.getFrame().getRaster().getDataBuffer()).getData();
dstImage = new BufferedImage(newWidth, newHeight, entry.getFrame().getType());
dstI = ((DataBufferInt)dstImage.getRaster().getDataBuffer()).getData();
} else {
return entry;
}
// normalizing rotation for easier processing
int angle = cbAngle.getSelectedIndex();
if (rbCCW.isSelected()) {
angle = ANGLE_270 - angle;
}
// rotating each pixel
int srcOfs = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++, srcOfs++) {
int nx, ny;
switch (angle) {
case ANGLE_90:
nx = newWidth - y - 1;
ny = x;
break;
case ANGLE_180:
nx = newWidth - x - 1;
ny = newHeight - y - 1;
break;
case ANGLE_270:
nx = y;
ny = newHeight - x - 1;
break;
default:
nx = x;
ny = y;
}
int dstOfs = ny*newWidth + nx;
if (srcB != null) {
dstB[dstOfs] = srcB[srcOfs];
}
if (srcI != null) {
dstI[dstOfs] = srcI[srcOfs];
}
}
}
entry.setFrame(dstImage);
// adjusting center
if (cbAdjustCenter.isSelected()) {
int cx = entry.getCenterX(), cy = entry.getCenterY();
switch (angle) {
case ANGLE_90:
cx = newWidth - entry.getCenterY() - 1;
cy = entry.getCenterX();
break;
case ANGLE_180:
cx = newWidth - entry.getCenterX() - 1;
cy = newHeight - entry.getCenterY() - 1;
break;
case ANGLE_270:
cx = entry.getCenterY();
cy = newHeight - entry.getCenterX() - 1;
break;
}
entry.setCenterX(cx);
entry.setCenterY(cy);
}
}
return entry;
}
}