Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/demo/applets/DitherTest/DitherTest.java
38827 views
/*1* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* - Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* - Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* - Neither the name of Oracle nor the names of its15* contributors may be used to endorse or promote products derived16* from this software without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS19* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,20* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR21* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR22* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,23* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,24* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR25* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS28* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031/*32* This source code is provided to illustrate the usage of a given feature33* or technique and has been deliberately simplified. Additional steps34* required for a production-quality application, such as security checks,35* input validation and proper error handling, might not be present in36* this sample code.37*/38394041import java.applet.Applet;42import java.awt.AWTEvent;43import java.awt.BorderLayout;44import java.awt.Button;45import java.awt.Canvas;46import java.awt.Choice;47import java.awt.Color;48import java.awt.Dimension;49import java.awt.FlowLayout;50import java.awt.FontMetrics;51import java.awt.Frame;52import java.awt.Graphics;53import java.awt.Image;54import java.awt.Label;55import java.awt.LayoutManager;56import java.awt.Panel;57import java.awt.TextField;58import java.awt.Toolkit;59import java.awt.event.ActionEvent;60import java.awt.event.ActionListener;61import java.awt.event.KeyEvent;62import java.awt.event.TextEvent;63import java.awt.image.ColorModel;64import java.awt.image.MemoryImageSource;656667enum DitherMethod {6869NOOP, RED, GREEN, BLUE, ALPHA, SATURATION70};717273@SuppressWarnings("serial")74public class DitherTest extends Applet implements Runnable {7576private Thread runner;77private DitherControls XControls;78private DitherControls YControls;79private DitherCanvas canvas;8081public static void main(String args[]) {82Frame f = new Frame("DitherTest");83DitherTest ditherTest = new DitherTest();84ditherTest.init();85f.add("Center", ditherTest);86f.pack();87f.setVisible(true);88ditherTest.start();89}9091@Override92public void init() {93String xspec = null, yspec = null;94int xvals[] = new int[2];95int yvals[] = new int[2];9697try {98xspec = getParameter("xaxis");99yspec = getParameter("yaxis");100} catch (NullPointerException ignored) {101//only occurs if run as application102}103104if (xspec == null) {105xspec = "red";106}107if (yspec == null) {108yspec = "blue";109}110DitherMethod xmethod = colorMethod(xspec, xvals);111DitherMethod ymethod = colorMethod(yspec, yvals);112113setLayout(new BorderLayout());114XControls = new DitherControls(this, xvals[0], xvals[1],115xmethod, false);116YControls = new DitherControls(this, yvals[0], yvals[1],117ymethod, true);118YControls.addRenderButton();119add("North", XControls);120add("South", YControls);121add("Center", canvas = new DitherCanvas());122}123124private DitherMethod colorMethod(String s, int vals[]) {125DitherMethod method = DitherMethod.NOOP;126if (s == null) {127s = "";128}129String lower = s.toLowerCase();130131for (DitherMethod m : DitherMethod.values()) {132if (lower.startsWith(m.toString().toLowerCase())) {133method = m;134lower = lower.substring(m.toString().length());135}136}137if (method == DitherMethod.NOOP) {138vals[0] = 0;139vals[1] = 0;140return method;141}142int begval = 0;143int endval = 255;144try {145int dash = lower.indexOf('-');146if (dash < 0) {147endval = Integer.parseInt(lower);148} else {149begval = Integer.parseInt(lower.substring(0, dash));150endval = Integer.parseInt(lower.substring(dash + 1));151}152} catch (NumberFormatException ignored) {153}154155if (begval < 0) {156begval = 0;157} else if (begval > 255) {158begval = 255;159}160161if (endval < 0) {162endval = 0;163} else if (endval > 255) {164endval = 255;165}166167vals[0] = begval;168vals[1] = endval;169return method;170}171172/**173* Calculates and returns the image. Halts the calculation and returns174* null if the Applet is stopped during the calculation.175*/176private Image calculateImage() {177Thread me = Thread.currentThread();178179int width = canvas.getSize().width;180int height = canvas.getSize().height;181int xvals[] = new int[2];182int yvals[] = new int[2];183int xmethod = XControls.getParams(xvals);184int ymethod = YControls.getParams(yvals);185int pixels[] = new int[width * height];186int c[] = new int[4]; //temporarily holds R,G,B,A information187int index = 0;188for (int j = 0; j < height; j++) {189for (int i = 0; i < width; i++) {190c[0] = c[1] = c[2] = 0;191c[3] = 255;192if (xmethod < ymethod) {193applyMethod(c, xmethod, i, width, xvals);194applyMethod(c, ymethod, j, height, yvals);195} else {196applyMethod(c, ymethod, j, height, yvals);197applyMethod(c, xmethod, i, width, xvals);198}199pixels[index++] = ((c[3] << 24) | (c[0] << 16) | (c[1] << 8)200| c[2]);201}202203// Poll once per row to see if we've been told to stop.204if (runner != me) {205return null;206}207}208return createImage(new MemoryImageSource(width, height,209ColorModel.getRGBdefault(), pixels, 0, width));210}211212private void applyMethod(int c[], int methodIndex, int step,213int total, int vals[]) {214DitherMethod method = DitherMethod.values()[methodIndex];215if (method == DitherMethod.NOOP) {216return;217}218int val = ((total < 2)219? vals[0]220: vals[0] + ((vals[1] - vals[0]) * step / (total - 1)));221switch (method) {222case RED:223c[0] = val;224break;225case GREEN:226c[1] = val;227break;228case BLUE:229c[2] = val;230break;231case ALPHA:232c[3] = val;233break;234case SATURATION:235int max = Math.max(Math.max(c[0], c[1]), c[2]);236int min = max * (255 - val) / 255;237if (c[0] == 0) {238c[0] = min;239}240if (c[1] == 0) {241c[1] = min;242}243if (c[2] == 0) {244c[2] = min;245}246break;247}248}249250@Override251public void start() {252runner = new Thread(this);253runner.start();254}255256@Override257public void run() {258canvas.setImage(null); // Wipe previous image259Image img = calculateImage();260if (img != null && runner == Thread.currentThread()) {261canvas.setImage(img);262}263}264265@Override266public void stop() {267runner = null;268}269270@Override271public void destroy() {272remove(XControls);273remove(YControls);274remove(canvas);275}276277@Override278public String getAppletInfo() {279return "An interactive demonstration of dithering.";280}281282@Override283public String[][] getParameterInfo() {284String[][] info = {285{ "xaxis", "{RED, GREEN, BLUE, ALPHA, SATURATION}",286"The color of the Y axis. Default is RED." },287{ "yaxis", "{RED, GREEN, BLUE, ALPHA, SATURATION}",288"The color of the X axis. Default is BLUE." }289};290return info;291}292}293294295@SuppressWarnings("serial")296class DitherCanvas extends Canvas {297298private Image img;299private static String calcString = "Calculating...";300301@Override302public void paint(Graphics g) {303int w = getSize().width;304int h = getSize().height;305if (img == null) {306super.paint(g);307g.setColor(Color.black);308FontMetrics fm = g.getFontMetrics();309int x = (w - fm.stringWidth(calcString)) / 2;310int y = h / 2;311g.drawString(calcString, x, y);312} else {313g.drawImage(img, 0, 0, w, h, this);314}315}316317@Override318public void update(Graphics g) {319paint(g);320}321322@Override323public Dimension getMinimumSize() {324return new Dimension(20, 20);325}326327@Override328public Dimension getPreferredSize() {329return new Dimension(200, 200);330}331332public Image getImage() {333return img;334}335336public void setImage(Image img) {337this.img = img;338repaint();339}340}341342343@SuppressWarnings("serial")344class DitherControls extends Panel implements ActionListener {345346private CardinalTextField start;347private CardinalTextField end;348private Button button;349private Choice choice;350private DitherTest applet;351private static LayoutManager dcLayout = new FlowLayout(FlowLayout.CENTER,35210, 5);353354public DitherControls(DitherTest app, int s, int e, DitherMethod type,355boolean vertical) {356applet = app;357setLayout(dcLayout);358add(new Label(vertical ? "Vertical" : "Horizontal"));359add(choice = new Choice());360for (DitherMethod m : DitherMethod.values()) {361choice.addItem(m.toString().substring(0, 1)362+ m.toString().substring(1).toLowerCase());363}364choice.select(type.ordinal());365add(start = new CardinalTextField(Integer.toString(s), 4));366add(end = new CardinalTextField(Integer.toString(e), 4));367}368369/* puts on the button */370public void addRenderButton() {371add(button = new Button("New Image"));372button.addActionListener(this);373}374375/* retrieves data from the user input fields */376public int getParams(int vals[]) {377try {378vals[0] = scale(Integer.parseInt(start.getText()));379} catch (NumberFormatException nfe) {380vals[0] = 0;381}382try {383vals[1] = scale(Integer.parseInt(end.getText()));384} catch (NumberFormatException nfe) {385vals[1] = 255;386}387return choice.getSelectedIndex();388}389390/* fits the number between 0 and 255 inclusive */391private int scale(int number) {392if (number < 0) {393number = 0;394} else if (number > 255) {395number = 255;396}397return number;398}399400/* called when user clicks the button */401@Override402public void actionPerformed(ActionEvent e) {403if (e.getSource() == button) {404applet.start();405}406}407}408409410@SuppressWarnings("serial")411class CardinalTextField extends TextField {412413String oldText = null;414415public CardinalTextField(String text, int columns) {416super(text, columns);417enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.TEXT_EVENT_MASK);418oldText = getText();419}420421// Consume non-digit KeyTyped events422// Note that processTextEvent kind of eliminates the need for this423// function, but this is neater, since ideally, it would prevent424// the text from appearing at all. Sigh. See bugid 4100317/4114565.425//426@Override427protected void processEvent(AWTEvent evt) {428int id = evt.getID();429if (id != KeyEvent.KEY_TYPED) {430super.processEvent(evt);431return;432}433434KeyEvent kevt = (KeyEvent) evt;435char c = kevt.getKeyChar();436437// Digits, backspace, and delete are okay438// Note that the minus sign is not allowed (neither is decimal)439if (Character.isDigit(c) || (c == '\b') || (c == '\u007f')) {440super.processEvent(evt);441return;442}443444Toolkit.getDefaultToolkit().beep();445kevt.consume();446}447448// Should consume TextEvents for non-integer Strings449// Store away the text in the tf for every TextEvent450// so we can revert to it on a TextEvent (paste, or451// legal key in the wrong location) with bad text452//453// Note: it would be easy to extend this to an eight-bit454// TextField (range 0-255), but I'll leave it as-is.455//456@Override457protected void processTextEvent(TextEvent te) {458// The empty string is okay, too459String newText = getText();460if (newText.equals("") || textIsCardinal(newText)) {461oldText = newText;462super.processTextEvent(te);463return;464}465466Toolkit.getDefaultToolkit().beep();467setText(oldText);468}469470// Returns true for Cardinal (non-negative) numbers471// Note that the empty string is not allowed472private boolean textIsCardinal(String textToCheck) {473try {474return Integer.parseInt(textToCheck, 10) >= 0;475} catch (NumberFormatException nfe) {476return false;477}478}479}480481482