blob: 65ef368dd3bb4ba5406e538e5fe17591f5a8d880 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of Sun Microsystems nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 */
34
35import java.applet.Applet;
36import java.awt.event.*;
37import java.awt.*;
38import java.awt.image.ColorModel;
39import java.awt.image.MemoryImageSource;
40
41public class DitherTest extends Applet implements Runnable {
42 final private static int NOOP = 0;
43 final private static int RED = 1;
44 final private static int GREEN = 2;
45 final private static int BLUE = 3;
46 final private static int ALPHA = 4;
47 final private static int SATURATION = 5;
48
49 private Thread runner;
50
51 private DitherControls XControls;
52 private DitherControls YControls;
53 private DitherCanvas canvas;
54
55 public static void main(String args[]) {
56 Frame f = new Frame("DitherTest");
57 DitherTest ditherTest = new DitherTest();
58 ditherTest.init();
59 f.add("Center", ditherTest);
60 f.pack();
61 f.setVisible(true);
62 ditherTest.start();
63 }
64
65 public void init() {
66 String xspec = null, yspec = null;
67 int xvals[] = new int[2];
68 int yvals[] = new int[2];
69
70 try {
71 xspec = getParameter("xaxis");
72 yspec = getParameter("yaxis");
73 } catch (NullPointerException npe) {
74 //only occurs if run as application
75 }
76
77 if (xspec == null) {
78 xspec = "red";
79 }
80 if (yspec == null) {
81 yspec = "blue";
82 }
83 int xmethod = colormethod(xspec, xvals);
84 int ymethod = colormethod(yspec, yvals);
85
86 setLayout(new BorderLayout());
87 XControls = new DitherControls(this, xvals[0], xvals[1],
88 xmethod, false);
89 YControls = new DitherControls(this, yvals[0], yvals[1],
90 ymethod, true);
91 YControls.addRenderButton();
92 add("North", XControls);
93 add("South", YControls);
94 add("Center", canvas = new DitherCanvas());
95 }
96
97 private int colormethod(String s, int vals[]) {
98 int method = NOOP;
99 if (s == null) {
100 s = "";
101 }
102 String lower = s.toLowerCase();
103 int len = 0;
104 if (lower.startsWith("red")) {
105 method = RED;
106 lower = lower.substring(3);
107 } else if (lower.startsWith("green")) {
108 method = GREEN;
109 lower = lower.substring(5);
110 } else if (lower.startsWith("blue")) {
111 method = BLUE;
112 lower = lower.substring(4);
113 } else if (lower.startsWith("alpha")) {
114 method = ALPHA;
115 lower = lower.substring(5);
116 } else if (lower.startsWith("saturation")) {
117 method = SATURATION;
118 lower = lower.substring(10);
119 }
120 if (method == NOOP) {
121 vals[0] = 0;
122 vals[1] = 0;
123 return method;
124 }
125 int begval = 0;
126 int endval = 255;
127 try {
128 int dash = lower.indexOf('-');
129 if (dash < 0) {
130 endval = Integer.parseInt(lower);
131 } else {
132 begval = Integer.parseInt(lower.substring(0, dash));
133 endval = Integer.parseInt(lower.substring(dash + 1));
134 }
135 } catch (NumberFormatException nfe) {
136 }
137
138 if (begval < 0) {
139 begval = 0;
140 } else if (begval > 255) {
141 begval = 255;
142 }
143
144 if (endval < 0) {
145 endval = 0;
146 } else if (endval > 255) {
147 endval = 255;
148 }
149
150 vals[0] = begval;
151 vals[1] = endval;
152 return method;
153 }
154
155 /**
156 * Calculates and returns the image. Halts the calculation and returns
157 * null if the Applet is stopped during the calculation.
158 */
159 private Image calculateImage() {
160 Thread me = Thread.currentThread();
161
162 int width = canvas.getSize().width;
163 int height = canvas.getSize().height;
164 int xvals[] = new int[2];
165 int yvals[] = new int[2];
166 int xmethod = XControls.getParams(xvals);
167 int ymethod = YControls.getParams(yvals);
168 int pixels[] = new int[width * height];
169 int c[] = new int[4]; //temporarily holds R,G,B,A information
170 int index = 0;
171 for (int j = 0; j < height; j++) {
172 for (int i = 0; i < width; i++) {
173 c[0] = c[1] = c[2] = 0;
174 c[3] = 255;
175 if (xmethod < ymethod) {
176 applymethod(c, xmethod, i, width, xvals);
177 applymethod(c, ymethod, j, height, yvals);
178 } else {
179 applymethod(c, ymethod, j, height, yvals);
180 applymethod(c, xmethod, i, width, xvals);
181 }
182 pixels[index++] = ((c[3] << 24) |
183 (c[0] << 16) |
184 (c[1] << 8) |
185 c[2]);
186 }
187
188 // Poll once per row to see if we've been told to stop.
189 if (runner != me) {
190 return null;
191 }
192 }
193 return createImage(new MemoryImageSource(width, height,
194 ColorModel.getRGBdefault(), pixels, 0, width));
195 }
196
197 private void applymethod(int c[], int method, int step,
198 int total, int vals[]) {
199 if (method == NOOP) {
200 return;
201 }
202 int val = ((total < 2)
203 ? vals[0]
204 : vals[0] + ((vals[1] - vals[0]) * step / (total - 1)));
205 switch (method) {
206 case RED:
207 c[0] = val;
208 break;
209 case GREEN:
210 c[1] = val;
211 break;
212 case BLUE:
213 c[2] = val;
214 break;
215 case ALPHA:
216 c[3] = val;
217 break;
218 case SATURATION:
219 int max = Math.max(Math.max(c[0], c[1]), c[2]);
220 int min = max * (255 - val) / 255;
221 if (c[0] == 0) {
222 c[0] = min;
223 }
224 if (c[1] == 0) {
225 c[1] = min;
226 }
227 if (c[2] == 0) {
228 c[2] = min;
229 }
230 break;
231 }
232 }
233
234 public void start() {
235 runner = new Thread(this);
236 runner.start();
237 }
238
239 public void run() {
240 canvas.setImage(null); // Wipe previous image
241 Image img = calculateImage();
242 if (img != null && runner == Thread.currentThread()) {
243 canvas.setImage(img);
244 }
245 }
246
247 public void stop() {
248 runner = null;
249 }
250
251 public void destroy() {
252 remove(XControls);
253 remove(YControls);
254 remove(canvas);
255 }
256
257 public String getAppletInfo() {
258 return "An interactive demonstration of dithering.";
259 }
260
261 public String[][] getParameterInfo() {
262 String[][] info = {
263 {"xaxis", "{RED, GREEN, BLUE, ALPHA, SATURATION}",
264 "The color of the Y axis. Default is RED."},
265 {"yaxis", "{RED, GREEN, BLUE, ALPHA, SATURATION}",
266 "The color of the X axis. Default is BLUE."}
267 };
268 return info;
269 }
270}
271
272class DitherCanvas extends Canvas {
273 private Image img;
274 private static String calcString = "Calculating...";
275
276 public void paint(Graphics g) {
277 int w = getSize().width;
278 int h = getSize().height;
279 if (img == null) {
280 super.paint(g);
281 g.setColor(Color.black);
282 FontMetrics fm = g.getFontMetrics();
283 int x = (w - fm.stringWidth(calcString)) / 2;
284 int y = h / 2;
285 g.drawString(calcString, x, y);
286 } else {
287 g.drawImage(img, 0, 0, w, h, this);
288 }
289 }
290
291 public void update(Graphics g) {
292 paint(g);
293 }
294
295 public Dimension getMinimumSize() {
296 return new Dimension(20, 20);
297 }
298
299 public Dimension getPreferredSize() {
300 return new Dimension(200, 200);
301 }
302
303 public Image getImage() {
304 return img;
305 }
306
307 public void setImage(Image img) {
308 this.img = img;
309 repaint();
310 }
311}
312
313class DitherControls extends Panel implements ActionListener {
314 private CardinalTextField start;
315 private CardinalTextField end;
316 private Button button;
317 private Choice choice;
318 private DitherTest applet;
319
320 private static LayoutManager dcLayout = new FlowLayout(FlowLayout.CENTER,
321 10, 5);
322
323 public DitherControls(DitherTest app, int s, int e, int type,
324 boolean vertical) {
325 applet = app;
326 setLayout(dcLayout);
327 add(new Label(vertical ? "Vertical" : "Horizontal"));
328 add(choice = new Choice());
329 choice.addItem("Noop");
330 choice.addItem("Red");
331 choice.addItem("Green");
332 choice.addItem("Blue");
333 choice.addItem("Alpha");
334 choice.addItem("Saturation");
335 choice.select(type);
336 add(start = new CardinalTextField(Integer.toString(s), 4));
337 add(end = new CardinalTextField(Integer.toString(e), 4));
338 }
339
340 /* puts on the button */
341 public void addRenderButton() {
342 add(button = new Button("New Image"));
343 button.addActionListener(this);
344 }
345
346 /* retrieves data from the user input fields */
347 public int getParams(int vals[]) {
348 try {
349 vals[0] = scale(Integer.parseInt(start.getText()));
350 } catch (NumberFormatException nfe) {
351 vals[0] = 0;
352 }
353 try {
354 vals[1] = scale(Integer.parseInt(end.getText()));
355 } catch (NumberFormatException nfe) {
356 vals[1] = 255;
357 }
358 return choice.getSelectedIndex();
359 }
360
361 /* fits the number between 0 and 255 inclusive */
362 private int scale(int number) {
363 if (number < 0) {
364 number = 0;
365 } else if (number > 255) {
366 number = 255;
367 }
368 return number;
369 }
370
371 /* called when user clicks the button */
372 public void actionPerformed(ActionEvent e) {
373 if (e.getSource() == button) {
374 applet.start();
375 }
376 }
377}
378
379class CardinalTextField extends TextField {
380
381 String oldText = null;
382
383 public CardinalTextField(String text, int columns) {
384 super(text, columns);
385 enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.TEXT_EVENT_MASK);
386 oldText = getText();
387 }
388
389 // Consume non-digit KeyTyped events
390 // Note that processTextEvent kind of eliminates the need for this
391 // function, but this is neater, since ideally, it would prevent
392 // the text from appearing at all. Sigh. See bugid 4100317/4114565.
393 //
394 protected void processEvent(AWTEvent evt) {
395 int id = evt.getID();
396 if (id != KeyEvent.KEY_TYPED) {
397 super.processEvent(evt);
398 return;
399 }
400
401 KeyEvent kevt = (KeyEvent) evt;
402 char c = kevt.getKeyChar();
403
404 // Digits, backspace, and delete are okay
405 // Note that the minus sign is not allowed (neither is decimal)
406 if (Character.isDigit(c) || (c == '\b') || (c == '\u007f')) {
407 super.processEvent(evt);
408 return;
409 }
410
411 Toolkit.getDefaultToolkit().beep();
412 kevt.consume();
413 }
414
415 // Should consume TextEvents for non-integer Strings
416 // Store away the text in the tf for every TextEvent
417 // so we can revert to it on a TextEvent (paste, or
418 // legal key in the wrong location) with bad text
419 //
420 // Note: it would be easy to extend this to an eight-bit
421 // TextField (range 0-255), but I'll leave it as-is.
422 //
423 protected void processTextEvent(TextEvent te) {
424 // The empty string is okay, too
425 String newText = getText();
426 if (newText.equals("") || textIsCardinal(newText)) {
427 oldText = newText;
428 super.processTextEvent(te);
429 return;
430 }
431
432 Toolkit.getDefaultToolkit().beep();
433 setText(oldText);
434 }
435
436 // Returns true for Cardinal (non-negative) numbers
437 // Note that the empty string is not allowed
438 private boolean textIsCardinal(String textToCheck) {
439 int value = -1;
440
441 try {
442 value = Integer.parseInt(textToCheck, 10);
443 return (value >= 0);
444 } catch (NumberFormatException nfe) {
445 return false;
446 }
447 }
448}