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 java.awt.Color;
61 import java.awt.Dimension;
62 import java.awt.Graphics;
63 import java.awt.Point;
64 import java.awt.Graphics2D;
65 import java.awt.Polygon;
66 import java.awt.Rectangle;
67 import java.awt.event.ActionEvent;
68 import java.awt.event.ActionListener;
69 import java.awt.event.MouseAdapter;
70 import java.awt.event.MouseEvent;
71 import java.awt.event.MouseMotionAdapter;
72 import java.awt.geom.AffineTransform;
73 import java.awt.geom.NoninvertibleTransformException;
74 import java.awt.geom.Point2D;
75 import java.awt.image.BufferedImage;
76 import java.awt.image.DataBufferInt;
77
78 public class Viewer extends JFrame {
79 public final MainPanel mainPanel;
80 public static class MainPanel extends JPanel {
81 protected BufferedImage image;
82 protected int[] rasterData;
83 protected S32Array2D s32Array2D;
84 protected AffineTransform transform = new AffineTransform();
85 protected float zoom = .95f; // set the zoom factor 1.0 = fit to screen
86
87 protected float xOffset = 0; // 0 is centered -1 is to the left;
88 protected float yOffset = 0; // 0 is centered -1 is to the top;
89
90 Point mousePressedPosition;
91 Point2D imageRelativeMouseDownPosition = new Point2D.Float();
92 Point2D imageRelativeMovePosition = new Point2D.Float();
93 volatile Selection selection = null;
94 volatile Point bestMatchOffset = null;
95
96
97 public MainPanel(Accelerator accelerator, BufferedImage image, Controls controls) {
98 this.image = image;
99 this.rasterData = ((DataBufferInt) (image.getRaster().getDataBuffer())).getData();
100 this.s32Array2D = S32Array2D.create(accelerator, image.getWidth(), image.getHeight());
101 s32Array2D.copyFrom(rasterData);
102 addMouseListener(new MouseAdapter() {
103
104 @Override
105 public void mouseReleased(MouseEvent e) {
106 if (SwingUtilities.isLeftMouseButton(e)) {
107 bestMatchOffset = ComputeHeal.getBestMatchOffset(accelerator, s32Array2D, selection.close(), controls.sevenSegmentDisplay);
108 ComputeHeal.heal(accelerator, s32Array2D, selection, bestMatchOffset);
109 Timer t = new Timer(1000, new ActionListener() {
110 @Override
111 public void actionPerformed(ActionEvent e) {
112 selection = null;
113 bestMatchOffset = null;
114 repaint();
115 }
116 });
117 t.setRepeats(false);
118 t.start();
119 repaint();
120 }
121 }
122
123 @Override
124 public void mousePressed(MouseEvent e) {
125 if (SwingUtilities.isLeftMouseButton(e)) {
126 try {
127 var ptDst = transform.inverseTransform(e.getPoint(), null);
128 selection = new Selection(ptDst);
129 } catch (NoninvertibleTransformException e1) {
130 e1.printStackTrace();
131 }
132 } else if (SwingUtilities.isRightMouseButton(e)) {
133 mousePressedPosition = e.getPoint();
134 try {
135 imageRelativeMouseDownPosition = transform.inverseTransform(e.getPoint(), null);
136 } catch (NoninvertibleTransformException e1) {
137 e1.printStackTrace();
138 }
139 }
140 }
141
142 });
143 addMouseWheelListener(e -> {
144 zoom = zoom * (1 + e.getWheelRotation() / 10f);
145 repaint();
146 });
147 addMouseMotionListener(new MouseMotionAdapter() {
148 @Override
149 public void mouseDragged(MouseEvent e) {
150 if (SwingUtilities.isRightMouseButton(e)) {
151 Point rightButonPoint = e.getPoint();
152 Dimension offsetFromInitialMousePress = new Dimension(rightButonPoint.x - mousePressedPosition.x, rightButonPoint.y - mousePressedPosition.y);
153 try {
154 imageRelativeMovePosition = transform.inverseTransform(e.getPoint(), null);
155 Dimension displaySize = getSize();
156 Dimension imageSize = new Dimension(s32Array2D.width(), s32Array2D.height());
157 float scale = zoom *
158 Math.min(displaySize.width / (float) imageSize.width,
159 displaySize.height / (float) imageSize.height);
160 xOffset = 2 * (offsetFromInitialMousePress.width / (displaySize.width - scale * imageSize.width));
161 yOffset = 2 * (offsetFromInitialMousePress.height / (displaySize.height - scale * imageSize.height));
162 xOffset = Math.max(Math.min(xOffset, 1), -1);
163 yOffset = Math.max(Math.min(yOffset, 1), -1);
164 repaint();
165 } catch (NoninvertibleTransformException e1) {
166 e1.printStackTrace();
167 }
168 } else if (SwingUtilities.isLeftMouseButton(e)) {
169 try {
170 var ptDst = transform.inverseTransform(e.getPoint(), null);
171 selection.add(ptDst);
172 repaint();
173 } catch (NoninvertibleTransformException e1) {
174 // TODO Auto-generated catch block
175 e1.printStackTrace();
176 }
177 }
178 }
179 });
180 }
181
182 @Override
183 public void paint(Graphics g) {
184 Graphics2D g2d = (Graphics2D) g;
185 g2d.setBackground(Color.BLACK);
186 g2d.fillRect(0, 0, getWidth(), getHeight());
187 if (s32Array2D != null) {
188 Dimension displaySize = getSize();
189 Dimension imageSize = new Dimension(s32Array2D.width(), s32Array2D.height());
190 AffineTransform safeTransform = g2d.getTransform();
191 transform.setToIdentity();
192 double scale = zoom *
193 Math.min(displaySize.width / (double) imageSize.width,
194 displaySize.height / (double) imageSize.height);
195 transform.translate((1 + xOffset) * (displaySize.width - imageSize.width * scale) / 2,
196 (1 + yOffset) * (displaySize.height - imageSize.height * scale) / 2);
197 transform.scale(scale, scale);
198 g2d.transform(transform);
199 s32Array2D.copyTo(rasterData);
200 g.drawImage(image, 0, 0, imageSize.width, imageSize.height, null);
201 paintInScale(g2d);
202 g2d.setTransform(safeTransform);
203 }
204 }
205
206 protected void paintInScale(Graphics2D g) {
207 if (selection != null) {
208 Polygon selectionPolygon = new Polygon();
209 Polygon solutionPolygon = new Polygon();
210 selection.pointList.forEach(point -> {
211 selectionPolygon.addPoint(point.x, point.y);
212 if (bestMatchOffset != null) {
213 solutionPolygon.addPoint(point.x + bestMatchOffset.x, point.y + bestMatchOffset.y);
214 }
215 });
216 g.setColor(Color.RED);
217 g.drawPolygon(selectionPolygon);
218 if (bestMatchOffset != null) {
219 g.setColor(Color.BLUE);
220 g.drawPolygon(solutionPolygon);
221 }
222 }
223 }
224 }
225 public static class Controls{
226 JTextField search;
227 // JTextField mask;
228 // JTextField heal;
229 JMenuBar menuBar;
230 SevenSegmentDisplay sevenSegmentDisplay;
231 Controls(){
232 menuBar = new JMenuBar();
233 ((JButton) menuBar.add(new JButton("Exit"))).addActionListener(_ -> System.exit(0));
234 menuBar.add(Box.createHorizontalStrut(40));
235 menuBar.add(new JLabel("Search"));
236 sevenSegmentDisplay = (SevenSegmentDisplay) menuBar.add(
237 new SevenSegmentDisplay(4,20, menuBar.getForeground(),menuBar.getBackground()));
238 // search = create ("Search ms");
239 // mask = create ("Mask ms");
240 // heal = create ("Heal ms");
241 }
242 /* JTextField create (String name){
243 menuBar.add(new JLabel(name));
244 JTextField textField = (JTextField) menuBar.add(new JTextField("",5));
245 textField.setEditable(false);
246 return textField;
247 } */
248 }
249
250 Viewer(Accelerator accelerator, BufferedImage image) {
251 super("Healing Brush ");
252 Controls controls = new Controls();
253 setJMenuBar(controls.menuBar);
254 this.mainPanel = new MainPanel(accelerator,image, controls);
255 setBounds(new Rectangle(image.getWidth(),image.getHeight()));
256 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
257 setContentPane(mainPanel);
258 validate();
259 setVisible(true);
260
261 }
262
263 }