1 /* 2 * Copyright (c) 2014, 2016, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 @test 26 @key headful 27 @bug 4397404 4720930 28 @summary tests that images of all supported native image formats are transfered properly 29 @library ../../regtesthelpers/process/ 30 @build ProcessResults ProcessCommunicator 31 @author gas@sparc.spb.su area=Clipboard 32 @run main ImageTransferTest 33 */ 34 35 import sun.awt.OSInfo; 36 import sun.awt.SunToolkit; 37 import test.java.awt.regtesthelpers.process.ProcessCommunicator; 38 import test.java.awt.regtesthelpers.process.ProcessResults; 39 40 import java.awt.*; 41 import java.awt.datatransfer.DataFlavor; 42 import java.awt.datatransfer.SystemFlavorMap; 43 import java.awt.datatransfer.Transferable; 44 import java.awt.datatransfer.UnsupportedFlavorException; 45 import java.awt.dnd.DnDConstants; 46 import java.awt.dnd.DragSource; 47 import java.awt.dnd.DragSourceAdapter; 48 import java.awt.dnd.DragSourceDropEvent; 49 import java.awt.dnd.DragSourceListener; 50 import java.awt.dnd.DropTarget; 51 import java.awt.dnd.DropTargetAdapter; 52 import java.awt.dnd.DropTargetDropEvent; 53 import java.awt.event.InputEvent; 54 import java.awt.image.BufferedImage; 55 import java.awt.image.MemoryImageSource; 56 import java.util.stream.Stream; 57 58 public class ImageTransferTest { 59 public static void main(String[] arg) throws Exception { 60 ImageDragSource ids = new ImageDragSource(); 61 ids.frame.setLocation(100, 100); 62 ids.frame.setVisible(true); 63 Util.sync(); 64 String classpath = System.getProperty("java.class.path"); 65 String[] args = new String[ids.formats.length + 4]; 66 args[0] = "200"; 67 args[1] = "100"; 68 args[2] = args[3] = "150"; 69 70 System.arraycopy(ids.formats, 0, args, 4, ids.formats.length); 71 ProcessResults pres = ProcessCommunicator.executeChildProcess(ImageDropTarget.class, classpath, args); 72 73 if (pres.getStdErr() != null && pres.getStdErr().length() > 0) { 74 System.err.println("========= Child VM System.err ========"); 75 System.err.print(pres.getStdErr()); 76 System.err.println("======================================"); 77 } 78 79 if (pres.getStdOut() != null && pres.getStdOut().length() > 0) { 80 System.err.println("========= Child VM System.out ========"); 81 System.err.print(pres.getStdOut()); 82 System.err.println("======================================"); 83 } 84 85 boolean failed = false; 86 String passedFormats = ""; 87 String failedFormats = ""; 88 89 for (int i = 0; i < ids.passedArray.length; i++) { 90 if (ids.passedArray[i]) passedFormats += ids.formats[i] + " "; 91 else { 92 failed = true; 93 failedFormats += ids.formats[i] + " "; 94 } 95 } 96 97 if (failed) { 98 throw new RuntimeException("test failed: images in following " + 99 "native formats are not transferred properly: " + failedFormats); 100 } else { 101 System.err.println("images in following " + 102 "native formats are transferred properly: " + passedFormats); 103 } 104 } 105 } 106 107 108 class Util { 109 public static void sync() { 110 ((SunToolkit) Toolkit.getDefaultToolkit()).realSync(); 111 try { 112 Thread.sleep(500); 113 } catch (InterruptedException e) { 114 throw new RuntimeException(e); 115 } 116 } 117 } 118 119 abstract class ImageTransferer { 120 Image image; 121 String[] formats; 122 int fi; // current format index 123 Frame frame = new Frame(); 124 125 126 ImageTransferer() { 127 image = createImage(); 128 frame.setSize(100, 100); 129 } 130 131 private static Image createImage() { 132 int w = 100; 133 int h = 100; 134 int[] pix = new int[w * h]; 135 136 int index = 0; 137 for (int y = 0; y < h; y++) { 138 for (int x = 0; x < w; x++) { 139 int red = 127; 140 int green = 127; 141 int blue = y > h / 2 ? 127 : 0; 142 int alpha = 255; 143 if (x < w / 4 && y < h / 4) { 144 alpha = 0; 145 red = 0; 146 } 147 pix[index++] = (alpha << 24) | (red << 16) | (green << 8) | blue; 148 } 149 } 150 return Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(w, h, pix, 0, w)); 151 } 152 153 154 static String[] retrieveFormatsToTest() { 155 SystemFlavorMap sfm = (SystemFlavorMap) SystemFlavorMap.getDefaultFlavorMap(); 156 java.util.List<String> ln = sfm.getNativesForFlavor(DataFlavor.imageFlavor); 157 if (OSInfo.OSType.WINDOWS.equals(OSInfo.getOSType()) && !ln.contains("METAFILEPICT")) { 158 // for test failing on JDK without this fix 159 ln.add("METAFILEPICT"); 160 } 161 return ln.toArray(new String[ln.size()]); 162 } 163 164 static void leaveFormat(String format) { 165 SystemFlavorMap sfm = (SystemFlavorMap) SystemFlavorMap.getDefaultFlavorMap(); 166 sfm.setFlavorsForNative(format, new DataFlavor[]{DataFlavor.imageFlavor}); 167 sfm.setNativesForFlavor(DataFlavor.imageFlavor, new String[]{format}); 168 } 169 170 171 boolean areImagesIdentical(Image im1, Image im2) { 172 if (formats[fi].equals("JFIF") || formats[fi].equals("image/jpeg") || 173 formats[fi].equals("GIF") || formats[fi].equals("image/gif")) { 174 // JFIF and GIF are lossy formats 175 return true; 176 } 177 int[] ib1 = getImageData(im1); 178 int[] ib2 = getImageData(im2); 179 180 if (ib1.length != ib2.length) { 181 return false; 182 } 183 184 if (formats[fi].equals("PNG") || 185 formats[fi].equals("image/png") || 186 formats[fi].equals("image/x-png")) { 187 // check alpha as well 188 for (int i = 0; i < ib1.length; i++) { 189 if (ib1[i] != ib2[i]) { 190 System.err.println("different pixels: " + 191 Integer.toHexString(ib1[i]) + " " + 192 Integer.toHexString(ib2[i])); 193 return false; 194 } 195 } 196 } else { 197 for (int i = 0; i < ib1.length; i++) { 198 if ((ib1[i] & 0x00FFFFFF) != (ib2[i] & 0x00FFFFFF)) { 199 System.err.println("different pixels: " + 200 Integer.toHexString(ib1[i]) + " " + 201 Integer.toHexString(ib2[i])); 202 return false; 203 } 204 } 205 } 206 return true; 207 } 208 209 private static int[] getImageData(Image image) { 210 int width = image.getWidth(null); 211 int height = image.getHeight(null); 212 BufferedImage bimage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 213 Graphics2D g2d = bimage.createGraphics(); 214 try { 215 g2d.drawImage(image, 0, 0, width, height, null); 216 } finally { 217 g2d.dispose(); 218 } 219 return bimage.getRGB(0, 0, width, height, null, 0, width); 220 } 221 222 public static int sign(int n) { 223 return n < 0 ? -1 : n == 0 ? 0 : 1; 224 } 225 226 } 227 228 229 class ImageDragSource extends ImageTransferer { 230 boolean[] passedArray; 231 232 ImageDragSource() { 233 formats = retrieveFormatsToTest(); 234 passedArray = new boolean[formats.length]; 235 final DragSourceListener dsl = new DragSourceAdapter() { 236 public void dragDropEnd(DragSourceDropEvent e) { 237 System.err.println("Drop was successful=" + e.getDropSuccess()); 238 notifyTransferSuccess(e.getDropSuccess()); 239 if (++fi < formats.length) { 240 leaveFormat(formats[fi]); 241 } 242 } 243 }; 244 245 new DragSource().createDefaultDragGestureRecognizer(frame, 246 DnDConstants.ACTION_COPY, 247 dge -> dge.startDrag(null, new ImageSelection(image), dsl)); 248 leaveFormat(formats[fi]); 249 } 250 251 252 void notifyTransferSuccess(boolean status) { 253 passedArray[fi] = status; 254 } 255 } 256 257 258 class ImageDropTarget extends ImageTransferer { 259 private final Robot robot; 260 private static Point startPoint, endPoint = new Point(250, 150); 261 262 ImageDropTarget() throws AWTException { 263 DropTargetAdapter dropTargetAdapter = new DropTargetAdapter() { 264 @Override 265 public void drop(DropTargetDropEvent dtde) { 266 checkImage(dtde); 267 startImageDrag(); 268 } 269 }; 270 new DropTarget(frame, dropTargetAdapter); 271 robot = new Robot(); 272 } 273 274 275 void checkImage(DropTargetDropEvent dtde) { 276 final Transferable t = dtde.getTransferable(); 277 if (t.isDataFlavorSupported(DataFlavor.imageFlavor)) { 278 dtde.acceptDrop(DnDConstants.ACTION_COPY); 279 Image im; 280 try { 281 im = (Image) t.getTransferData(DataFlavor.imageFlavor); 282 System.err.println("getTransferData was successful"); 283 } catch (Exception e) { 284 System.err.println("Can't getTransferData: " + e); 285 dtde.dropComplete(false); 286 notifyTransferSuccess(false); 287 return; 288 } 289 290 if (im == null) { 291 System.err.println("getTransferData returned null"); 292 dtde.dropComplete(false); 293 notifyTransferSuccess(false); 294 } else if (areImagesIdentical(image, im)) { 295 dtde.dropComplete(true); 296 notifyTransferSuccess(true); 297 } else { 298 System.err.println("transferred image is different from initial image"); 299 dtde.dropComplete(false); 300 notifyTransferSuccess(false); 301 } 302 303 } else { 304 System.err.println("imageFlavor is not supported by Transferable"); 305 dtde.rejectDrop(); 306 notifyTransferSuccess(false); 307 } 308 } 309 310 void startImageDrag() { 311 leaveFormat(formats[fi]); 312 new Thread(() -> { 313 try { 314 Thread.sleep(1000); 315 } catch (InterruptedException e) { 316 e.printStackTrace(); 317 // Exit from the child process 318 System.exit(1); 319 } 320 robot.mouseMove(startPoint.x, startPoint.y); 321 robot.mousePress(InputEvent.BUTTON1_MASK); 322 for (Point p = new Point(startPoint); !p.equals(endPoint); 323 p.translate(sign(endPoint.x - p.x), sign(endPoint.y - p.y))) { 324 robot.mouseMove(p.x, p.y); 325 try { 326 Thread.sleep(50); 327 } catch (InterruptedException e) { 328 e.printStackTrace(); 329 } 330 } 331 332 robot.mouseRelease(InputEvent.BUTTON1_MASK); 333 }).start(); 334 } 335 336 void notifyTransferSuccess(boolean status) { 337 if (status) { 338 System.err.println("format passed: " + formats[fi]); 339 } else { 340 System.err.println("format failed: " + formats[fi]); 341 System.exit(1); 342 } 343 if (fi < formats.length - 1) { 344 leaveFormat(formats[++fi]); 345 } else { 346 new Thread(() -> { 347 try { 348 Thread.sleep(500); 349 } catch (InterruptedException e) { 350 e.printStackTrace(); 351 } 352 System.exit(0); 353 }).start(); 354 } 355 } 356 357 358 public static void main(String[] args) { 359 try { 360 ImageDropTarget idt = new ImageDropTarget(); 361 362 int x = Integer.parseInt(args[0]); 363 int y = Integer.parseInt(args[1]); 364 startPoint = new Point(Integer.parseInt(args[2]), Integer.parseInt(args[3])); 365 366 idt.formats = new String[args.length - 4]; 367 System.arraycopy(args, 4, idt.formats, 0, args.length - 4); 368 leaveFormat(idt.formats[0]); 369 370 idt.frame.setLocation(x, y); 371 idt.frame.setVisible(true); 372 Util.sync(); 373 374 idt.startImageDrag(); 375 } catch (Throwable e) { 376 e.printStackTrace(); 377 System.exit(1); 378 } 379 } 380 381 } 382 383 384 class ImageSelection implements Transferable { 385 private static final int IMAGE = 0; 386 private static final DataFlavor[] flavors = {DataFlavor.imageFlavor}; 387 private Image data; 388 389 public ImageSelection(Image data) { 390 this.data = data; 391 } 392 393 @Override 394 public DataFlavor[] getTransferDataFlavors() { 395 // returning flavors itself would allow client code to modify 396 // our internal behavior 397 return flavors.clone(); 398 } 399 400 @Override 401 public boolean isDataFlavorSupported(DataFlavor flavor) { 402 return Stream.of(flavor).anyMatch(flavor::equals); 403 } 404 405 @Override 406 public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { 407 if (flavor.equals(flavors[IMAGE])) { 408 return data; 409 } else { 410 throw new UnsupportedFlavorException(flavor); 411 } 412 } 413 }