// 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 java.util.Arrays; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.infinity.gui.ViewerUtil; import org.infinity.resource.graphics.PseudoBamDecoder.PseudoBamFrameEntry; /** * Transform filter: automatically trims excess space from each frame. */ public class BamFilterTransformTrim extends BamFilterBaseTransform implements ActionListener, ChangeListener { private static final String FilterName = "Trim BAM frames"; private static final String FilterDesc = "This filter attempts to remove unused space around each " + "BAM frame. Center positions will be adjusted accordingly."; private static final int EDGE_TOP = 0; private static final int EDGE_BOTTOM = 1; private static final int EDGE_LEFT = 2; private static final int EDGE_RIGHT = 3; private static final String[] EdgeLabels = {"Top", "Bottom", "Left", "Right"}; private JCheckBox[] cbEdges; private JSpinner spinnerMargin; private JCheckBox cbAdjustCenter; public static String getFilterName() { return FilterName; } public static String getFilterDesc() { return FilterDesc; } public BamFilterTransformTrim(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(cbEdges[EDGE_TOP].isSelected()).append(';'); sb.append(cbEdges[EDGE_LEFT].isSelected()).append(';'); sb.append(cbEdges[EDGE_BOTTOM].isSelected()).append(';'); sb.append(cbEdges[EDGE_RIGHT].isSelected()).append(';'); sb.append(((SpinnerNumberModel)spinnerMargin.getModel()).getNumber().intValue()).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(";"); boolean t = true, l = true, b = true, r = true; Integer margin = Integer.MIN_VALUE; boolean a = true; if (params.length > 0) { if (params[0].equalsIgnoreCase("true")) { t = true; } else if (params[0].equalsIgnoreCase("false")) { t = false; } else { return false; } } if (params.length > 1) { if (params[1].equalsIgnoreCase("true")) { l = true; } else if (params[1].equalsIgnoreCase("false")) { l = false; } else { return false; } } if (params.length > 2) { if (params[2].equalsIgnoreCase("true")) { b = true; } else if (params[2].equalsIgnoreCase("false")) { b = false; } else { return false; } } if (params.length > 3) { if (params[3].equalsIgnoreCase("true")) { r = true; } else if (params[3].equalsIgnoreCase("false")) { r = false; } else { return false; } } if (params.length > 4) { int min = ((Number)((SpinnerNumberModel)spinnerMargin.getModel()).getMinimum()).intValue(); int max = ((Number)((SpinnerNumberModel)spinnerMargin.getModel()).getMaximum()).intValue(); margin = decodeNumber(params[4], min, max, Integer.MIN_VALUE); if (margin == Integer.MIN_VALUE) { return false; } } if (params.length > 5) { if (params[5].equalsIgnoreCase("true")) { a = true; } else if (params[5].equalsIgnoreCase("false")) { a = false; } else { return false; } } cbEdges[EDGE_TOP].setSelected(t); cbEdges[EDGE_LEFT].setSelected(l); cbEdges[EDGE_BOTTOM].setSelected(b); cbEdges[EDGE_RIGHT].setSelected(r); if (margin != Integer.MIN_VALUE) { spinnerMargin.setValue(margin); } cbAdjustCenter.setSelected(a); } return true; } return false; } @Override protected JPanel loadControls() { GridBagConstraints c = new GridBagConstraints(); JLabel l1 = new JLabel("Edges:"); JLabel l2 = new JLabel("Margin:"); JLabel l3 = new JLabel("pixels"); cbEdges = new JCheckBox[4]; for (int i = 0; i < cbEdges.length; i++) { cbEdges[i] = new JCheckBox(EdgeLabels[i], true); cbEdges[i].addActionListener(this); } spinnerMargin = new JSpinner(new SpinnerNumberModel(0, 0, 255, 1)); spinnerMargin.addChangeListener(this); cbAdjustCenter = new JCheckBox("Adjust center position", true); cbAdjustCenter.addActionListener(this); JPanel p1 = 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); p1.add(cbEdges[EDGE_TOP], c); ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); p1.add(cbEdges[EDGE_LEFT], 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); p1.add(cbEdges[EDGE_BOTTOM], c); ViewerUtil.setGBC(c, 3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); p1.add(cbEdges[EDGE_RIGHT], c); JPanel p2 = new JPanel(new GridBagLayout()); ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 0), 0, 0); p2.add(spinnerMargin, 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); p2.add(l3, c); JPanel pMain = 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); pMain.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); pMain.add(p1, c); ViewerUtil.setGBC(c, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0); pMain.add(l2, c); ViewerUtil.setGBC(c, 1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0); pMain.add(p2, 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); pMain.add(new JPanel(), c); ViewerUtil.setGBC(c, 1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 4, 0, 0), 0, 0); pMain.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(pMain, c); return panel; } //--------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == cbAdjustCenter) { fireChangeListener(); } else { for (int i = 0; i < cbEdges.length; i++) { if (cbEdges[i] == event.getSource()) { fireChangeListener(); return; } } } } //--------------------- End Interface ActionListener --------------------- //--------------------- Begin Interface ChangeListener --------------------- @Override public void stateChanged(ChangeEvent event) { if (event.getSource() == spinnerMargin) { fireChangeListener(); } } //--------------------- End Interface ChangeListener --------------------- 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; byte[] srcB = null, dstB = null; int[] srcI = null, dstI = null; int transIndex = 0; IndexColorModel cm = null; if (entry.getFrame().getType() == BufferedImage.TYPE_BYTE_INDEXED) { srcB = ((DataBufferByte)entry.getFrame().getRaster().getDataBuffer()).getData(); cm = (IndexColorModel)entry.getFrame().getColorModel(); // fetching transparent palette entry (default: 0) if (cm.getTransparentPixel() >= 0) { transIndex = cm.getTransparentPixel(); } else { int[] colors = new int[1 << cm.getPixelSize()]; cm.getRGBs(colors); final int Green = 0x0000ff00; for (int i = 0; i < colors.length; i++) { if ((colors[i] & 0x00ffffff) == Green) { transIndex = i; break; } } } } else if (entry.getFrame().getRaster().getDataBuffer().getDataType() == DataBuffer.TYPE_INT) { srcI = ((DataBufferInt)entry.getFrame().getRaster().getDataBuffer()).getData(); } else { return entry; } // calculating the properties of the resulting image int left = 0, right = width - 1, top = 0, bottom = height - 1; boolean edgeLeft = !cbEdges[EDGE_LEFT].isSelected(), edgeRight = !cbEdges[EDGE_RIGHT].isSelected(), edgeTop = !cbEdges[EDGE_TOP].isSelected(), edgeBottom = !cbEdges[EDGE_BOTTOM].isSelected(); while ((left < right || top < bottom) && (!edgeLeft || !edgeRight || !edgeTop || !edgeBottom)) { int ofs, step; // checking top edge if (cbEdges[EDGE_TOP].isSelected() && !edgeTop) { ofs = top*width; step = 1; for (int x = 0; x < width; x++, ofs += step) { if (srcB != null) { if ((srcB[ofs] & 0xff) != transIndex) { edgeTop = true; break; } } else { if ((srcI[ofs] & 0xff000000) != 0) { edgeTop = true; break; } } } } // checking bottom edge if (cbEdges[EDGE_BOTTOM].isSelected() && !edgeBottom) { ofs = bottom*width; step = 1; for (int x = 0; x < width; x++, ofs += step) { if (srcB != null) { if ((srcB[ofs] & 0xff) != transIndex) { edgeBottom = true; break; } } else { if ((srcI[ofs] & 0xff000000) != 0) { edgeBottom = true; break; } } } } // checking left edge if (cbEdges[EDGE_LEFT].isSelected() && !edgeLeft) { ofs = left; step = width; for (int y = 0; y < height; y++, ofs += step) { if (srcB != null) { if ((srcB[ofs] & 0xff) != transIndex) { edgeLeft = true; break; } } else { if ((srcI[ofs] & 0xff000000) != 0) { edgeLeft = true; break; } } } } // checking right edge if (cbEdges[EDGE_RIGHT].isSelected() && !edgeRight) { ofs = right; step = width; for (int y = 0; y < height; y++, ofs += step) { if (srcB != null) { if ((srcB[ofs] & 0xff) != transIndex) { edgeRight = true; break; } } else { if ((srcI[ofs] & 0xff000000) != 0) { edgeRight = true; break; } } } } if (!edgeLeft) left++; if (!edgeRight) right--; if (!edgeTop) top++; if (!edgeBottom) bottom--; } // creating new image int margin = ((Integer)spinnerMargin.getValue()).intValue(); int dstX = 0; int dstY = 0; newWidth = right - left + 1; newHeight = bottom - top + 1; if (cbEdges[EDGE_LEFT].isSelected()) { newWidth += margin; dstX += margin; } if (cbEdges[EDGE_RIGHT].isSelected()) { newWidth += margin; } if (cbEdges[EDGE_TOP].isSelected()) { newHeight += margin; dstY += margin; } if (cbEdges[EDGE_BOTTOM].isSelected()) { newHeight += margin; } if (srcB != null) { // paletted image dstImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_BYTE_INDEXED, cm); dstB = ((DataBufferByte)dstImage.getRaster().getDataBuffer()).getData(); Arrays.fill(dstB, (byte)transIndex); } else { // truecolor image dstImage = new BufferedImage(newWidth, newHeight, entry.getFrame().getType()); dstI = ((DataBufferInt)dstImage.getRaster().getDataBuffer()).getData(); Arrays.fill(dstI, 0); } int srcOfs = top*width + left; int dstOfs = dstY*newWidth + dstX; for (int y = 0; y < bottom - top + 1; y++, srcOfs += width, dstOfs += newWidth) { if (srcB != null) { System.arraycopy(srcB, srcOfs, dstB, dstOfs, right - left + 1); } if (srcI != null) { System.arraycopy(srcI, srcOfs, dstI, dstOfs, right - left + 1); } } entry.setFrame(dstImage); // updating center information if (cbAdjustCenter.isSelected()) { int centerX = entry.getCenterX() - left + dstX; int centerY = entry.getCenterY() - top + dstY; entry.setCenterX(centerX); entry.setCenterY(centerY); } } return entry; } }