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 }