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