001    /*
002     * MedianCutDialog
003     * 
004     * Copyright (c) 2001, 2002, 2003 Marco Schmidt.
005     * All rights reserved.
006     */
007    
008    package net.sourceforge.jiu.gui.awt.dialogs;
009    
010    import java.awt.BorderLayout;
011    import java.awt.Button;
012    import java.awt.Choice;
013    import java.awt.Dialog;
014    import java.awt.Dimension;
015    import java.awt.Frame;
016    import java.awt.GridLayout;
017    import java.awt.Label;
018    import java.awt.Panel;
019    import java.awt.Rectangle;
020    import java.awt.TextField;
021    import java.awt.Toolkit;
022    import java.awt.event.ActionEvent;
023    import java.awt.event.ActionListener;
024    import java.awt.event.ItemEvent;
025    import java.awt.event.ItemListener;
026    import java.awt.event.KeyEvent;
027    import java.awt.event.KeyListener;
028    import net.sourceforge.jiu.apps.Strings;
029    import net.sourceforge.jiu.color.dithering.ErrorDiffusionDithering;
030    import net.sourceforge.jiu.color.quantization.MedianCutQuantizer;
031    
032    /**
033     * A dialog to enter the parameters for a Median Cut color quantization operation.
034     * It also allows to enter the optional algorithms that can be applied in combination with Median Cut.
035     * @author Marco Schmidt
036     */
037    public class MedianCutDialog extends Dialog implements 
038            ActionListener, ItemListener, KeyListener
039    {
040            public final int[][] METHODS =
041            {
042                    {
043                     MedianCutQuantizer.METHOD_REPR_COLOR_WEIGHTED_AVERAGE,
044                     MedianCutQuantizer.METHOD_REPR_COLOR_AVERAGE,
045                     MedianCutQuantizer.METHOD_REPR_COLOR_MEDIAN
046                    },
047                    {
048                     Strings.METHOD_REPR_COLOR_WEIGHTED_AVERAGE,
049                     Strings.METHOD_REPR_COLOR_AVERAGE,
050                     Strings.METHOD_REPR_COLOR_MEDIAN
051                    }
052            };
053            public final int[] ERROR_DIFFUSION_STRINGS =
054            {
055                    Strings.FLOYD_STEINBERG_ERROR_DIFFUSION,
056                    Strings.STUCKI_ERROR_DIFFUSION,
057                    Strings.BURKES_ERROR_DIFFUSION,
058                    Strings.SIERRA_ERROR_DIFFUSION,
059                    Strings.JARVIS_JUDICE_NINKE_ERROR_DIFFUSION,
060                    Strings.STEVENSON_ARCE_ERROR_DIFFUSION
061            };
062            public final int[] ERROR_DIFFUSION_TYPES =
063            {
064                    ErrorDiffusionDithering.TYPE_FLOYD_STEINBERG,
065                    ErrorDiffusionDithering.TYPE_STUCKI,
066                    ErrorDiffusionDithering.TYPE_BURKES,
067                    ErrorDiffusionDithering.TYPE_SIERRA,
068                    ErrorDiffusionDithering.TYPE_JARVIS_JUDICE_NINKE,
069                    ErrorDiffusionDithering.TYPE_STEVENSON_ARCE
070            };
071            private Button ok;
072            private Button cancel;
073            private TextField numColorsField;
074            private Choice outputColorType;
075            private Choice reprColorMethod;
076            private Choice algorithms;
077            private Choice errorDiffusion;
078            private TextField numPassesField;
079            private TextField tauField;
080            private boolean pressedOk;
081    
082            /**
083             * Creates a modal dialog to enter the parameter.
084             * @param owner the parent of this modal dialog
085             * @param strings an object to get String constants in the current language
086             * @param numColors the number of colors in the resulting image
087             * @param representativeColorMethod the method to determine the representative color from a set of colors
088             * @param paletted if true, the output image will be paletted, otherwise truecolor
089             * @param numPasses number of contour removal iterations
090             * @param initialTau maximum distance for two colors to be considered similar in contour removal
091             */
092            public MedianCutDialog(Frame owner, Strings strings, int numColors, int representativeColorMethod, boolean paletted, int numPasses, double initialTau)
093            {
094                    super(owner, strings.get(Strings.MEDIAN_CUT_COLOR_QUANTIZATION), true);
095                    pressedOk = false;
096    
097                    Panel panel = new Panel();
098                    panel.setLayout(new GridLayout(0, 2));
099    
100                    panel.add(new Label(strings.get(Strings.NUM_COLORS)));
101                    numColorsField = new TextField(Integer.toString(numColors), 6);
102                    numColorsField.addKeyListener(this);
103                    panel.add(numColorsField);
104    
105                    panel.add(new Label(strings.get(Strings.OUTPUT_COLOR_TYPE)));
106                    outputColorType = new Choice();
107                    outputColorType.add(strings.get(Strings.OUTPUT_COLOR_TYPE_PALETTED));
108                    outputColorType.add(strings.get(Strings.OUTPUT_COLOR_TYPE_RGB));
109                    outputColorType.select(paletted ? 0 : 1);
110                    panel.add(outputColorType);
111    
112                    panel.add(new Label(strings.get(Strings.METHOD_REPR_COLOR)));
113                    reprColorMethod = new Choice();
114                    for (int i = 0; i < METHODS[0].length; i++)
115                    {
116                            reprColorMethod.add(strings.get(METHODS[1][i]));
117                            if (representativeColorMethod == METHODS[0][i])
118                            {
119                                    reprColorMethod.select(i);
120                            }
121                    }
122                    panel.add(reprColorMethod);
123    
124                    panel.add(new Label(strings.get(Strings.OUTPUT_QUALITY_IMPROVEMENT_ALGORITHM)));
125                    algorithms = new Choice();
126                    algorithms.add(strings.get(Strings.ALGORITHMS_NONE));
127                    algorithms.add(strings.get(Strings.ERROR_DIFFUSION));
128                    algorithms.add(strings.get(Strings.CONTOUR_REMOVAL));
129                    algorithms.select(1);
130                    algorithms.addItemListener(this);
131                    panel.add(algorithms);
132    
133                    panel.add(new Label(strings.get(Strings.ERROR_DIFFUSION)));
134                    errorDiffusion = new Choice();
135                    for (int i = 0; i < ERROR_DIFFUSION_STRINGS.length; i++)
136                    {
137                            errorDiffusion.add(strings.get(ERROR_DIFFUSION_STRINGS[i]));
138                    }
139                    errorDiffusion.select(0);
140                    panel.add(errorDiffusion);
141    
142                    panel.add(new Label(strings.get(Strings.CONTOUR_REMOVAL_NUM_PASSES)));
143                    numPassesField = new TextField(Integer.toString(numPasses));
144                    numPassesField.addKeyListener(this);
145                    panel.add(numPassesField);
146    
147                    panel.add(new Label(strings.get(Strings.CONTOUR_REMOVAL_TAU)));
148                    tauField = new TextField(Double.toString(initialTau));
149                    tauField.addKeyListener(this);
150                    panel.add(tauField);
151    
152                    add(panel, BorderLayout.CENTER);
153    
154                    ok = new Button(strings.get(Strings.OK));
155                    ok.addActionListener(this);
156                    cancel = new Button(strings.get(Strings.CANCEL));
157                    cancel.addActionListener(this);
158    
159                    updateStates();
160    
161                    panel = new Panel();
162                    panel.add(ok);
163                    panel.add(cancel);
164                    add(panel, BorderLayout.SOUTH);
165    
166                    pack();
167                    center();
168            }
169    
170            /**
171             * Hides (closes) this dialog if the OK button was source of the action event
172             * (e.g. if the button was pressed).
173             */
174            public void actionPerformed(ActionEvent e)
175            {
176                    if (e.getSource() == ok)
177                    {
178                            pressedOk = true;
179                            setVisible(false);
180                    }
181                    else
182                    if (e.getSource() == cancel)
183                    {
184                            setVisible(false);
185                    }
186            }
187    
188            /**
189             * Centers the dialog on screen.
190             */
191            public void center()
192            {
193                    Rectangle rect = getBounds();
194                    int width = rect.width;
195                    int height = rect.height;
196                    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
197                    setLocation((screenSize.width / 2) - (width / 2),
198                            (screenSize.height / 2) - (height / 2));
199            }
200    
201            public int getErrorDiffusion()
202            {
203                    if (algorithms.getSelectedIndex() == 1)
204                    {
205                            return ERROR_DIFFUSION_TYPES[errorDiffusion.getSelectedIndex()];
206                    }
207                    else
208                    {
209                            return -1;
210                    }
211            }
212    
213            private double getDoubleValue(TextField textField)
214            {
215                    try
216                    {
217                            Double d = new Double(textField.getText());
218                            return d.doubleValue();
219                    }
220                    catch (NumberFormatException nfe)
221                    {
222                            return Double.NaN;
223                    }
224            }       
225    
226    
227            private int getIntValue(TextField textField)
228            {
229                    try
230                    {
231                            return Integer.parseInt(textField.getText());
232                    }
233                    catch (NumberFormatException nfe)
234                    {
235                            return -1;
236                    }
237            }       
238    
239            public int getNumColors()
240            {
241                    return getIntValue(numColorsField);
242            }
243    
244            public int getNumPasses()
245            {
246                    return getIntValue(numPassesField);
247            }
248    
249            public int getReprColorMethod()
250            {
251                    return METHODS[0][reprColorMethod.getSelectedIndex()];
252            }
253    
254            public double getTau()
255            {
256                    return getDoubleValue(tauField);
257            }
258    
259            public boolean hasPressedOk()
260            {
261                    return pressedOk;
262            }
263    
264            public boolean isOutputTypePaletted()
265            {
266                    return outputColorType.getSelectedIndex() == 0;
267            }
268    
269            public void itemStateChanged(ItemEvent event)
270            {
271                    if (event.getSource() == algorithms)
272                    {
273                            updateStates();
274                    }
275            }
276    
277            public void keyPressed(KeyEvent e)
278            {
279                    updateOkButton();
280            }
281    
282            public void keyReleased(KeyEvent e)
283            {
284                    updateOkButton();
285            }
286    
287            public void keyTyped(KeyEvent e)
288            {
289                    updateOkButton();
290            }
291    
292            private void updateOkButton()
293            {
294                    int nc = getNumColors();
295                    boolean enabled = nc >= 1 && nc <= 256;
296                    if (enabled && algorithms.getSelectedIndex() == 2)
297                    {
298                            enabled = getTau() >= 0.0 && getNumPasses() >= 1;
299                    }
300                    ok.setEnabled(enabled);
301            }
302    
303            private void updateStates()
304            {
305                    int algorithmSelection = algorithms.getSelectedIndex();
306                    boolean ed = algorithmSelection == 1;
307                    errorDiffusion.setEnabled(ed);
308                    ed = algorithmSelection == 2;
309                    tauField.setEnabled(ed);
310                    numPassesField.setEnabled(ed);
311            }
312    
313            public boolean useContourRemoval()
314            {
315                    return algorithms.getSelectedIndex() == 2;
316            }
317    
318            public boolean useErrorDiffusion()
319            {
320                    return algorithms.getSelectedIndex() == 1;
321            }
322    }