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 {
thresholdOp(low, high)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")
ImageOps()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 
thresholdOp(int low, int high)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
render(int w, int h, Graphics2D g2)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
stateChanged(ChangeEvent e)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 
main(String[] s)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")
DemoControls(ImageOps demo)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
actionPerformed(ActionEvent e)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
getPreferredSize()237         public Dimension getPreferredSize() {
238             return new Dimension(200, 39);
239         }
240 
241         @Override
242         @SuppressWarnings("SleepWhileHoldingLock")
run()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 
278