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