1 /*
   2  *
   3  * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  *
   9  *   - Redistributions of source code must retain the above copyright
  10  *     notice, this list of conditions and the following disclaimer.
  11  *
  12  *   - Redistributions in binary form must reproduce the above copyright
  13  *     notice, this list of conditions and the following disclaimer in the
  14  *     documentation and/or other materials provided with the distribution.
  15  *
  16  *   - Neither the name of Oracle nor the names of its
  17  *     contributors may be used to endorse or promote products derived
  18  *     from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  21  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package java2d.demos.Images;
  33 
  34 
  35 import java.awt.BorderLayout;
  36 import java.awt.Color;
  37 import java.awt.Component;
  38 import java.awt.Dimension;
  39 import java.awt.Font;
  40 import java.awt.Graphics2D;
  41 import java.awt.Image;
  42 import java.awt.event.ActionEvent;
  43 import java.awt.event.ActionListener;
  44 import java.awt.geom.AffineTransform;
  45 import java.awt.image.BufferedImage;
  46 import java.awt.image.BufferedImageOp;
  47 import java.awt.image.ByteLookupTable;
  48 import java.awt.image.ConvolveOp;
  49 import java.awt.image.Kernel;
  50 import java.awt.image.LookupOp;
  51 import java.awt.image.RescaleOp;
  52 import java2d.ControlsSurface;
  53 import java2d.CustomControls;
  54 import javax.swing.JComboBox;
  55 import javax.swing.JSlider;
  56 import javax.swing.SwingConstants;
  57 import javax.swing.event.ChangeEvent;
  58 import javax.swing.event.ChangeListener;
  59 
  60 
  61 /**
  62  * Images drawn using operators such as ConvolveOp LowPass & Sharpen,
  63  * LookupOp and RescaleOp.
  64  */
  65 @SuppressWarnings("serial")
  66 public class ImageOps extends ControlsSurface implements ChangeListener {
  67 
  68     protected JSlider slider1, slider2;
  69     private static final String[] imgName = { "bld.jpg", "boat.png" };
  70     private static final BufferedImage[] img = new BufferedImage[imgName.length];
  71     private static final String[] opsName = {
  72         "Threshold", "RescaleOp", "Invert", "Yellow Invert", "3x3 Blur",
  73         "3x3 Sharpen", "3x3 Edge", "5x5 Edge" };
  74     private static final BufferedImageOp[] biop =
  75             new BufferedImageOp[opsName.length];
  76     private static int rescaleFactor = 128;
  77     private static float rescaleOffset = 0;
  78     private static final int low = 100, high = 200;
  79     private int opsIndex, imgIndex;
  80 
  81     static {
  82         thresholdOp(low, high);
  83         int i = 1;
  84         biop[i++] = new RescaleOp(1.0f, 0, null);
  85         byte[] invert = new byte[256];
  86         byte[] ordered = new byte[256];
  87         for (int j = 0; j < 256; j++) {
  88             invert[j] = (byte) (256 - j);
  89             ordered[j] = (byte) j;
  90         }
  91         biop[i++] = new LookupOp(new ByteLookupTable(0, invert), null);
  92         byte[][] yellowInvert = new byte[][] { invert, invert, ordered };
  93         biop[i++] = new LookupOp(new ByteLookupTable(0, yellowInvert), null);
  94         int[][] dim = { { 3, 3 }, { 3, 3 }, { 3, 3 }, { 5, 5 } };
  95         float[][] data = { { 0.1f, 0.1f, 0.1f, // 3x3 blur
  96                 0.1f, 0.2f, 0.1f,
  97                 0.1f, 0.1f, 0.1f },
  98             { -1.0f, -1.0f, -1.0f, // 3x3 sharpen
  99                 -1.0f, 9.0f, -1.0f,
 100                 -1.0f, -1.0f, -1.0f },
 101             { 0.f, -1.f, 0.f, // 3x3 edge
 102                 -1.f, 5.f, -1.f,
 103                 0.f, -1.f, 0.f },
 104             { -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, // 5x5 edge
 105                 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
 106                 -1.0f, -1.0f, 24.0f, -1.0f, -1.0f,
 107                 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
 108                 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f } };
 109         for (int j = 0; j < data.length; j++, i++) {
 110             biop[i] = new ConvolveOp(new Kernel(dim[j][0], dim[j][1], data[j]));
 111         }
 112     }
 113 
 114     @SuppressWarnings("LeakingThisInConstructor")
 115     public ImageOps() {
 116         setDoubleBuffered(true);
 117         setBackground(Color.white);
 118         for (int i = 0; i < imgName.length; i++) {
 119             Image image = getImage(imgName[i]);
 120             int iw = image.getWidth(this);
 121             int ih = image.getHeight(this);
 122             img[i] = new BufferedImage(iw, ih, BufferedImage.TYPE_INT_RGB);
 123             img[i].createGraphics().drawImage(image, 0, 0, null);
 124         }
 125         slider1 = new JSlider(SwingConstants.VERTICAL, 0, 255, low);
 126         slider1.setPreferredSize(new Dimension(15, 100));
 127         slider1.addChangeListener(this);
 128         slider2 = new JSlider(SwingConstants.VERTICAL, 0, 255, high);
 129         slider2.setPreferredSize(new Dimension(15, 100));
 130         slider2.addChangeListener(this);
 131         setControls(new Component[] { new DemoControls(this), slider1, slider2 });
 132         setConstraints(new String[] {
 133                     BorderLayout.NORTH, BorderLayout.WEST, BorderLayout.EAST });
 134     }
 135 
 136     public static void thresholdOp(int low, int high) {
 137         byte[] threshold = new byte[256];
 138         for (int j = 0; j < 256; j++) {
 139             if (j > high) {
 140                 threshold[j] = (byte) 255;
 141             } else if (j < low) {
 142                 threshold[j] = (byte) 0;
 143             } else {
 144                 threshold[j] = (byte) j;
 145             }
 146         }
 147         biop[0] = new LookupOp(new ByteLookupTable(0, threshold), null);
 148     }
 149 
 150     @Override
 151     public void render(int w, int h, Graphics2D g2) {
 152         int iw = img[imgIndex].getWidth(null);
 153         int ih = img[imgIndex].getHeight(null);
 154         AffineTransform oldXform = g2.getTransform();
 155         g2.scale(((double) w) / iw, ((double) h) / ih);
 156         g2.drawImage(img[imgIndex], biop[opsIndex], 0, 0);
 157         g2.setTransform(oldXform);
 158     }
 159 
 160     @Override
 161     public void stateChanged(ChangeEvent e) {
 162         if (e.getSource().equals(slider1)) {
 163             if (opsIndex == 0) {
 164                 thresholdOp(slider1.getValue(), high);
 165             } else {
 166                 rescaleFactor = slider1.getValue();
 167                 biop[1] = new RescaleOp(rescaleFactor / 128.0f, rescaleOffset,
 168                         null);
 169             }
 170         } else {
 171             if (opsIndex == 0) {
 172                 thresholdOp(low, slider2.getValue());
 173             } else {
 174                 rescaleOffset = slider2.getValue();
 175                 biop[1] = new RescaleOp(rescaleFactor / 128.0f, rescaleOffset,
 176                         null);
 177             }
 178 
 179         }
 180         repaint();
 181     }
 182 
 183     public static void main(String[] s) {
 184         createDemoFrame(new ImageOps());
 185     }
 186 
 187 
 188     static class DemoControls extends CustomControls implements ActionListener {
 189 
 190         ImageOps demo;
 191         JComboBox<String> imgCombo, opsCombo;
 192         Font font = new Font(Font.SERIF, Font.PLAIN, 10);
 193 
 194         @SuppressWarnings("LeakingThisInConstructor")
 195         public DemoControls(ImageOps demo) {
 196             super(demo.name);
 197             this.demo = demo;
 198             add(imgCombo = new JComboBox<>());
 199             imgCombo.setFont(font);
 200             for (String name : ImageOps.imgName) {
 201                 imgCombo.addItem(name);
 202             }
 203             imgCombo.addActionListener(this);
 204             add(opsCombo = new JComboBox<>());
 205             opsCombo.setFont(font);
 206             for (String name : ImageOps.opsName) {
 207                 opsCombo.addItem(name);
 208             }
 209             opsCombo.addActionListener(this);
 210         }
 211 
 212         @Override
 213         public void actionPerformed(ActionEvent e) {
 214             if (e.getSource().equals(opsCombo)) {
 215                 demo.opsIndex = opsCombo.getSelectedIndex();
 216                 if (demo.opsIndex == 0) {
 217                     demo.slider1.setValue(ImageOps.low);
 218                     demo.slider2.setValue(ImageOps.high);
 219                     demo.slider1.setEnabled(true);
 220                     demo.slider2.setEnabled(true);
 221                 } else if (demo.opsIndex == 1) {
 222                     demo.slider1.setValue(ImageOps.rescaleFactor);
 223                     demo.slider2.setValue((int) ImageOps.rescaleOffset);
 224                     demo.slider1.setEnabled(true);
 225                     demo.slider2.setEnabled(true);
 226                 } else {
 227                     demo.slider1.setEnabled(false);
 228                     demo.slider2.setEnabled(false);
 229                 }
 230             } else if (e.getSource().equals(imgCombo)) {
 231                 demo.imgIndex = imgCombo.getSelectedIndex();
 232             }
 233             demo.repaint(10);
 234         }
 235 
 236         @Override
 237         public Dimension getPreferredSize() {
 238             return new Dimension(200, 39);
 239         }
 240 
 241         @Override
 242         @SuppressWarnings("SleepWhileHoldingLock")
 243         public void run() {
 244             try {
 245                 Thread.sleep(1111);
 246             } catch (Exception e) {
 247                 return;
 248             }
 249             Thread me = Thread.currentThread();
 250             while (thread == me) {
 251                 for (int i = 0; i < ImageOps.imgName.length; i++) {
 252                     imgCombo.setSelectedIndex(i);
 253                     for (int j = 0; j < ImageOps.opsName.length; j++) {
 254                         opsCombo.setSelectedIndex(j);
 255                         if (j <= 1) {
 256                             for (int k = 50; k <= 200; k += 10) {
 257                                 demo.slider1.setValue(k);
 258                                 try {
 259                                     Thread.sleep(200);
 260                                 } catch (InterruptedException e) {
 261                                     return;
 262                                 }
 263                             }
 264                         }
 265                         try {
 266                             Thread.sleep(4444);
 267                         } catch (InterruptedException e) {
 268                             return;
 269                         }
 270                     }
 271                 }
 272             }
 273             thread = null;
 274         }
 275     } // End DemoControls
 276 } // End ImageOps
 277