// 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.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; 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: adjust color balance for red, green, blue individually. */ public class BamFilterColorBalance extends BamFilterBaseColor implements ChangeListener, ActionListener { private static final String FilterName = "Color Balance"; private static final String FilterDesc = "This filter provides controls for adjusting the " + "balance of each individual color channel."; private JSlider sliderRed, sliderGreen, sliderBlue; private JSpinner spinnerRed, spinnerGreen, spinnerBlue; private ButtonPopupWindow bpwExclude; private BamFilterBaseColor.ExcludeColorsPanel pExcludeColors; public static String getFilterName() { return FilterName; } public static String getFilterDesc() { return FilterDesc; } public BamFilterColorBalance(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(sliderRed.getValue()).append(';'); sb.append(sliderGreen.getValue()).append(';'); sb.append(sliderBlue.getValue()).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(";"); Integer redValue = Integer.MIN_VALUE; Integer greenValue = Integer.MIN_VALUE; Integer blueValue = Integer.MIN_VALUE; int[] indices = null; // parsing configuration data if (params.length > 0) { // set red value redValue = decodeNumber(params[0], sliderRed.getMinimum(), sliderRed.getMaximum(), Integer.MIN_VALUE); if (redValue == Integer.MIN_VALUE) { return false; } } if (params.length > 1) { // set green value greenValue = decodeNumber(params[1], sliderGreen.getMinimum(), sliderGreen.getMaximum(), Integer.MIN_VALUE); if (greenValue == Integer.MIN_VALUE) { return false; } } if (params.length > 2) { // set blue value blueValue = decodeNumber(params[2], sliderBlue.getMinimum(), sliderBlue.getMaximum(), Integer.MIN_VALUE); if (blueValue == Integer.MIN_VALUE) { return false; } } if (params.length > 3) { indices = decodeColorList(params[3]); if (indices == null) { return false; } } // applying configuration data if (redValue != Integer.MIN_VALUE) { sliderRed.setValue(redValue); } if (greenValue != Integer.MIN_VALUE) { sliderGreen.setValue(greenValue); } if (blueValue != Integer.MIN_VALUE) { sliderBlue.setValue(blueValue); } 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 lr = new JLabel("Red:"); JLabel lg = new JLabel("Green:"); JLabel lb = new JLabel("Blue:"); sliderRed = new JSlider(SwingConstants.HORIZONTAL, -255, 255, 0); sliderRed.addChangeListener(this); sliderGreen = new JSlider(SwingConstants.HORIZONTAL, -255, 255, 0); sliderGreen.addChangeListener(this); sliderBlue = new JSlider(SwingConstants.HORIZONTAL, -255, 255, 0); sliderBlue.addChangeListener(this); spinnerRed = new JSpinner(new SpinnerNumberModel(sliderRed.getValue(), sliderRed.getMinimum(), sliderRed.getMaximum(), 1)); spinnerRed.addChangeListener(this); spinnerGreen = new JSpinner(new SpinnerNumberModel(sliderGreen.getValue(), sliderGreen.getMinimum(), sliderGreen.getMaximum(), 1)); spinnerGreen.addChangeListener(this); spinnerBlue = new JSpinner(new SpinnerNumberModel(sliderBlue.getValue(), sliderBlue.getMinimum() , sliderBlue.getMaximum(), 1)); spinnerBlue.addChangeListener(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(lr, c); ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 0), 0, 0); p.add(sliderRed, c); ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 0), 0, 0); p.add(spinnerRed, 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); p.add(lg, c); ViewerUtil.setGBC(c, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); p.add(sliderGreen, c); ViewerUtil.setGBC(c, 2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); p.add(spinnerGreen, c); ViewerUtil.setGBC(c, 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0); p.add(lb, c); ViewerUtil.setGBC(c, 1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); p.add(sliderBlue, c); ViewerUtil.setGBC(c, 2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); p.add(spinnerBlue, c); ViewerUtil.setGBC(c, 0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 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.HORIZONTAL, 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(); } else if (event.getSource() == sliderRed) { spinnerRed.setValue(Integer.valueOf(sliderRed.getValue())); if (sliderRed.getModel().getValueIsAdjusting() == false) { fireChangeListener(); } } else if (event.getSource() == sliderGreen) { spinnerGreen.setValue(Integer.valueOf(sliderGreen.getValue())); if (sliderGreen.getModel().getValueIsAdjusting() == false) { fireChangeListener(); } } else if (event.getSource() == sliderBlue) { spinnerBlue.setValue(Integer.valueOf(sliderBlue.getValue())); if (sliderBlue.getModel().getValueIsAdjusting() == false) { fireChangeListener(); } } else if (event.getSource() == spinnerRed) { sliderRed.setValue(((Integer)spinnerRed.getValue()).intValue()); } else if (event.getSource() == spinnerGreen) { sliderGreen.setValue(((Integer)spinnerGreen.getValue()).intValue()); } else if (event.getSource() == spinnerBlue) { sliderBlue.setValue(((Integer)spinnerBlue.getValue()).intValue()); } } //--------------------- End Interface ChangeListener --------------------- //--------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == bpwExclude) { pExcludeColors.updatePalette(getConverter().getPaletteDialog().getPalette( getConverter().getPaletteDialog().getPaletteType())); } } //--------------------- End 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]; } // red/gree/blue in range [-255, 255] float red = ((Integer)spinnerRed.getValue()).floatValue() / 255.0f; float green = ((Integer)spinnerGreen.getValue()).floatValue() / 255.0f; float blue = ((Integer)spinnerBlue.getValue()).floatValue() / 255.0f; for (int i = 0; i < buffer.length; i++) { if ((cm == null || (cm != null && !pExcludeColors.isSelectedIndex(i))) && (buffer[i] & 0xff000000) != 0) { // extracting color channels float fa = (float)((buffer[i] >>> 24) & 0xff) / 255.0f; float fr = ((float)((buffer[i] >>> 16) & 0xff) / 255.0f) / fa; float fg = ((float)((buffer[i] >>> 8) & 0xff) / 255.0f) / fa; float fb = ((float)(buffer[i] & 0xff) / 255.0f) / fa; // applying color balance fr += (red / fa); fg += (green / fa); fb += (blue / fa); int ir = (int)((fr * fa) * 255.0f); if (ir < 0) ir = 0; else if (ir > 255) ir = 255; int ig = (int)((fg * fa) * 255.0f); if (ig < 0) ig = 0; else if (ig > 255) ig = 255; int ib = (int)((fb * fa) * 255.0f); if (ib < 0) ib = 0; else if (ib > 255) ib = 255; buffer[i] = (buffer[i] & 0xff000000) | (ir << 16) | (ig << 8) | 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; } }