// 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.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.infinity.gui.ButtonPopupWindow; import org.infinity.gui.ViewerUtil; import org.infinity.icon.Icons; import org.infinity.resource.graphics.PseudoBamDecoder.PseudoBamFrameEntry; /** * Color filter: swaps individual color channels. */ public class BamFilterColorSwap extends BamFilterBaseColor implements ChangeListener, ActionListener { private static final String FilterName = "Swap color channels"; private static final String FilterDesc = "This filter provides controls for swapping color " + "channels in any desired order."; // Supported swap combinations private static final String[] SwapTypeItems = {"RBG", "GRB", "GBR", "BGR", "BRG"}; private static final int[][] SwapTypeShift = { {0, -8, 8}, {-8, 8, 0}, {-16, 8, 8}, {-16, 0, 16}, {-8, -8, 16} }; private JComboBox<String> cbSwapType; private ButtonPopupWindow bpwExclude; private BamFilterBaseColor.ExcludeColorsPanel pExcludeColors; public static String getFilterName() { return FilterName; } public static String getFilterDesc() { return FilterDesc; } public BamFilterColorSwap(ConvertToBam parent) { super(parent, FilterName, FilterDesc); } @Override public BufferedImage process(BufferedImage frame) throws Exception { return applyEffect(frame); } @Override public PseudoBamFrameEntry updatePreview(PseudoBamFrameEntry entry) { if (entry != null) { entry.setFrame(applyEffect(entry.getFrame())); } return entry; } @Override public void updateControls() { bpwExclude.setEnabled(getConverter().isBamV1Selected()); } @Override public String getConfiguration() { StringBuilder sb = new StringBuilder(); sb.append(cbSwapType.getSelectedIndex()).append(';'); sb.append(encodeColorList(pExcludeColors.getSelectedIndices())); return sb.toString(); } @Override public boolean setConfiguration(String config) { if (config != null) { config = config.trim(); if (!config.isEmpty()) { String[] params = config.trim().split(";"); int type = -1; int[] indices = null; // parsing configuration data if (params.length > 0) { // set swap type type = decodeNumber(params[0], 0, cbSwapType.getModel().getSize() - 1, -1); if (type == -1) { return false; } } if (params.length > 1) { indices = decodeColorList(params[1]); if (indices == null) { return false; } } // applying configuration data if (type >= 0) { cbSwapType.setSelectedIndex(type); } if (indices != null) { pExcludeColors.setSelectedIndices(indices); } } return true; } return false; } @Override protected JPanel loadControls() { GridBagConstraints c = new GridBagConstraints(); JLabel l1 = new JLabel("Exclude colors:"); pExcludeColors = new BamFilterBaseColor.ExcludeColorsPanel( getConverter().getPaletteDialog().getPalette(getConverter().getPaletteDialog().getPaletteType())); pExcludeColors.addChangeListener(this); bpwExclude = new ButtonPopupWindow("Palette", Icons.getIcon(Icons.ICON_ARROW_DOWN_15), pExcludeColors); bpwExclude.setIconTextGap(8); bpwExclude.addActionListener(this); bpwExclude.setEnabled(getConverter().isBamV1Selected()); JPanel pExclude = 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); pExclude.add(l1, 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); pExclude.add(bpwExclude, c); ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); pExclude.add(new JPanel(), c); JLabel l = new JLabel("RGB =>"); cbSwapType = new JComboBox<>(SwapTypeItems); cbSwapType.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(l, c); ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 4, 0); p.add(cbSwapType, c); ViewerUtil.setGBC(c, 0, 1, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 4, 0); p.add(pExclude, 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 ChangeListener --------------------- @Override public void stateChanged(ChangeEvent event) { if (event.getSource() == pExcludeColors) { fireChangeListener(); } } //--------------------- End Interface ChangeListener --------------------- //--------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == cbSwapType) { fireChangeListener(); } else if (event.getSource() == bpwExclude) { pExcludeColors.updatePalette(getConverter().getPaletteDialog().getPalette( getConverter().getPaletteDialog().getPaletteType())); } } //--------------------- Begin Interface ActionListener --------------------- private BufferedImage applyEffect(BufferedImage srcImage) { if (srcImage != null) { int[] buffer; IndexColorModel cm = null; if (srcImage.getType() == BufferedImage.TYPE_BYTE_INDEXED) { // paletted image cm = (IndexColorModel)srcImage.getColorModel(); buffer = new int[1 << cm.getPixelSize()]; cm.getRGBs(buffer); // applying proper alpha if (!cm.hasAlpha()) { final int Green = 0x0000ff00; boolean greenFound = false; for (int i = 0; i < buffer.length; i++) { if (!greenFound && buffer[i] == Green) { greenFound = true; buffer[i] &= 0x00ffffff; } else { buffer[i] |= 0xff000000; } } } } else if (srcImage.getRaster().getDataBuffer().getDataType() == DataBuffer.TYPE_INT) { // truecolor image buffer = ((DataBufferInt)srcImage.getRaster().getDataBuffer()).getData(); } else { buffer = new int[0]; } // shift contains shift values for r, g, b int idx = cbSwapType.getSelectedIndex(); int[] shift = SwapTypeShift[idx]; for (int i = 0; i < buffer.length; i++) { if ((cm == null || (cm != null && !pExcludeColors.isSelectedIndex(i))) && (buffer[i] & 0xff000000) != 0) { // extracting color channels int ir = buffer[i] & 0x00ff0000; int ig = buffer[i] & 0x0000ff00; int ib = buffer[i] & 0x000000ff; // applying effect ir = (shift[0] < 0) ? (ir >>> -shift[0]) : (ir << shift[0]); ig = (shift[1] < 0) ? (ig >>> -shift[1]) : (ig << shift[1]); ib = (shift[2] < 0) ? (ib >>> -shift[2]) : (ib << shift[2]); buffer[i] = (buffer[i] & 0xff000000) | ir | ig | ib; } } if (cm != null) { // recreating paletted image IndexColorModel cm2 = new IndexColorModel(cm.getPixelSize(), buffer.length, buffer, 0, cm.hasAlpha(), cm.getTransparentPixel(), DataBuffer.TYPE_BYTE); int width = srcImage.getWidth(); int height = srcImage.getHeight(); BufferedImage dstImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED, cm2); byte[] srcPixels = ((DataBufferByte)srcImage.getRaster().getDataBuffer()).getData(); byte[] dstPixels = ((DataBufferByte)dstImage.getRaster().getDataBuffer()).getData(); System.arraycopy(srcPixels, 0, dstPixels, 0, srcPixels.length); srcImage = dstImage; srcPixels = null; dstPixels = null; } } return srcImage; } }