1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 /*
26 * Based on code from HealingBrush renderscript example
27 *
28 * https://github.com/yongjhih/HealingBrush/tree/master
29 *
30 * Copyright (C) 2015 The Android Open Source Project
31 *
32 * Licensed under the Apache License, Version 2.0 (the "License");
33 * you may not use this file except in compliance with the License.
34 * You may obtain a copy of the License at
35 *
36 * http://www.apache.org/licenses/LICENSE-2.0
37 *
38 * Unless required by applicable law or agreed to in writing, software
39 * distributed under the License is distributed on an "AS IS" BASIS,
40 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
41 * See the License for the specific language governing permissions and
42 * limitations under the License.
43 */
44 package heal;
45
46
47 import hat.Accelerator;
48 import hat.buffer.S32Array2D;
49 import hat.util.ui.SevenSegmentDisplay;
50
51 import javax.swing.Box;
52 import javax.swing.JButton;
53 import javax.swing.JFrame;
54 import javax.swing.JLabel;
55 import javax.swing.JMenuBar;
56 import javax.swing.JPanel;
57 import javax.swing.JTextField;
58 import javax.swing.SwingUtilities;
59 import javax.swing.Timer;
60 import javax.swing.WindowConstants;
61 import java.awt.Color;
62 import java.awt.Dimension;
63 import java.awt.Graphics;
64 import java.awt.Point;
65 import java.awt.Graphics2D;
66 import java.awt.Polygon;
67 import java.awt.Rectangle;
68 import java.awt.event.ActionEvent;
69 import java.awt.event.ActionListener;
70 import java.awt.event.MouseAdapter;
71 import java.awt.event.MouseEvent;
72 import java.awt.event.MouseMotionAdapter;
73 import java.awt.geom.AffineTransform;
74 import java.awt.geom.NoninvertibleTransformException;
75 import java.awt.geom.Point2D;
76 import java.awt.image.BufferedImage;
77 import java.awt.image.DataBufferInt;
78
79 public class Viewer extends JFrame {
80 public final MainPanel mainPanel;
81 public static class MainPanel extends JPanel {
82 protected BufferedImage image;
83 protected int[] rasterData;
84 protected S32Array2D s32Array2D;
85 protected AffineTransform transform = new AffineTransform();
86 protected float zoom = .95f; // set the zoom factor 1.0 = fit to screen
87
88 protected float xOffset = 0; // 0 is centered -1 is to the left;
89 protected float yOffset = 0; // 0 is centered -1 is to the top;
90
91 Point mousePressedPosition;
92 Point2D imageRelativeMouseDownPosition = new Point2D.Float();
93 Point2D imageRelativeMovePosition = new Point2D.Float();
94 volatile Selection selection = null;
95 volatile Point bestMatchOffset = null;
96
97
98 public MainPanel(Accelerator accelerator, BufferedImage image, Controls controls) {
99 this.image = image;
100 this.rasterData = ((DataBufferInt) (image.getRaster().getDataBuffer())).getData();
101 this.s32Array2D = S32Array2D.create(accelerator, image.getWidth(), image.getHeight());
102 s32Array2D.copyFrom(rasterData);
103 addMouseListener(new MouseAdapter() {
104
105 @Override
106 public void mouseReleased(MouseEvent e) {
107 if (SwingUtilities.isLeftMouseButton(e)) {
108 bestMatchOffset = Compute.getBestMatchOffset(accelerator, s32Array2D, selection.close(), controls.sevenSegmentDisplay);
109 Compute.heal(accelerator, s32Array2D, selection, bestMatchOffset);
110 Timer t = new Timer(1000, new ActionListener() {
111 @Override
112 public void actionPerformed(ActionEvent e) {
113 selection = null;
114 bestMatchOffset = null;
115 repaint();
116 }
117 });
118 t.setRepeats(false);
119 t.start();
120 repaint();
121 }
122 }
123
124 @Override
125 public void mousePressed(MouseEvent e) {
126 if (SwingUtilities.isLeftMouseButton(e)) {
127 try {
128 var ptDst = transform.inverseTransform(e.getPoint(), null);
129 selection = new Selection(ptDst);
130 } catch (NoninvertibleTransformException e1) {
131 e1.printStackTrace();
132 }
133 } else if (SwingUtilities.isRightMouseButton(e)) {
134 mousePressedPosition = e.getPoint();
135 try {
136 imageRelativeMouseDownPosition = transform.inverseTransform(e.getPoint(), null);
137 } catch (NoninvertibleTransformException e1) {
138 e1.printStackTrace();
139 }
140 }
141 }
142
143 });
144 addMouseWheelListener(e -> {
145 zoom = zoom * (1 + e.getWheelRotation() / 10f);
146 repaint();
147 });
148 addMouseMotionListener(new MouseMotionAdapter() {
149 @Override
150 public void mouseDragged(MouseEvent e) {
151 if (SwingUtilities.isRightMouseButton(e)) {
152 Point rightButonPoint = e.getPoint();
153 Dimension offsetFromInitialMousePress = new Dimension(rightButonPoint.x - mousePressedPosition.x, rightButonPoint.y - mousePressedPosition.y);
154 try {
155 imageRelativeMovePosition = transform.inverseTransform(e.getPoint(), null);
156 Dimension displaySize = getSize();
157 Dimension imageSize = new Dimension(s32Array2D.width(), s32Array2D.height());
158 float scale = zoom *
159 Math.min(displaySize.width / (float) imageSize.width,
160 displaySize.height / (float) imageSize.height);
161 xOffset = 2 * (offsetFromInitialMousePress.width / (displaySize.width - scale * imageSize.width));
162 yOffset = 2 * (offsetFromInitialMousePress.height / (displaySize.height - scale * imageSize.height));
163 xOffset = Math.max(Math.min(xOffset, 1), -1);
164 yOffset = Math.max(Math.min(yOffset, 1), -1);
165 repaint();
166 } catch (NoninvertibleTransformException e1) {
167 e1.printStackTrace();
168 }
169 } else if (SwingUtilities.isLeftMouseButton(e)) {
170 try {
171 var ptDst = transform.inverseTransform(e.getPoint(), null);
172 selection.add(ptDst);
173 repaint();
174 } catch (NoninvertibleTransformException e1) {
175 // TODO Auto-generated catch block
176 e1.printStackTrace();
177 }
178 }
179 }
180 });
181 }
182
183 @Override
184 public void paint(Graphics g) {
185 Graphics2D g2d = (Graphics2D) g;
186 g2d.setBackground(Color.BLACK);
187 g2d.fillRect(0, 0, getWidth(), getHeight());
188 if (s32Array2D != null) {
189 Dimension displaySize = getSize();
190 Dimension imageSize = new Dimension(s32Array2D.width(), s32Array2D.height());
191 AffineTransform safeTransform = g2d.getTransform();
192 transform.setToIdentity();
193 double scale = zoom *
194 Math.min(displaySize.width / (double) imageSize.width,
195 displaySize.height / (double) imageSize.height);
196 transform.translate((1 + xOffset) * (displaySize.width - imageSize.width * scale) / 2,
197 (1 + yOffset) * (displaySize.height - imageSize.height * scale) / 2);
198 transform.scale(scale, scale);
199 g2d.transform(transform);
200 s32Array2D.copyTo(rasterData);
201 g.drawImage(image, 0, 0, imageSize.width, imageSize.height, null);
202 paintInScale(g2d);
203 g2d.setTransform(safeTransform);
204 }
205 }
206
207 protected void paintInScale(Graphics2D g) {
208 if (selection != null) {
209 Polygon selectionPolygon = new Polygon();
210 Polygon solutionPolygon = new Polygon();
211 selection.pointList.forEach(point -> {
212 selectionPolygon.addPoint(point.x, point.y);
213 if (bestMatchOffset != null) {
214 solutionPolygon.addPoint(point.x + bestMatchOffset.x, point.y + bestMatchOffset.y);
215 }
216 });
217 g.setColor(Color.RED);
218 g.drawPolygon(selectionPolygon);
219 if (bestMatchOffset != null) {
220 g.setColor(Color.BLUE);
221 g.drawPolygon(solutionPolygon);
222 }
223 }
224 }
225 }
226 public static class Controls{
227 JTextField search;
228 // JTextField mask;
229 // JTextField heal;
230 JMenuBar menuBar;
231 SevenSegmentDisplay sevenSegmentDisplay;
232 Controls(){
233 menuBar = new JMenuBar();
234 ((JButton) menuBar.add(new JButton("Exit"))).addActionListener(_ -> System.exit(0));
235 menuBar.add(Box.createHorizontalStrut(40));
236 menuBar.add(new JLabel("Search"));
237 sevenSegmentDisplay = (SevenSegmentDisplay) menuBar.add(
238 new SevenSegmentDisplay(4,20, menuBar.getForeground(),menuBar.getBackground()));
239 // search = create ("Search ms");
240 // mask = create ("Mask ms");
241 // heal = create ("Heal ms");
242 }
243 /* JTextField create (String name){
244 menuBar.add(new JLabel(name));
245 JTextField textField = (JTextField) menuBar.add(new JTextField("",5));
246 textField.setEditable(false);
247 return textField;
248 } */
249 }
250
251 Viewer(Accelerator accelerator, BufferedImage image) {
252 super("Healing Brush ");
253 Controls controls = new Controls();
254 setJMenuBar(controls.menuBar);
255 this.mainPanel = new MainPanel(accelerator,image, controls);
256 setBounds(new Rectangle(image.getWidth(),image.getHeight()));
257 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
258 setContentPane(mainPanel);
259 validate();
260 setVisible(true);
261
262 }
263
264 }