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 /test/lib 31 * @library ../../regtesthelpers/process/ 32 * @build jdk.test.lib.Platform ProcessResults ProcessCommunicator 33 * @author gas@sparc.spb.su area=Clipboard 34 * @run main/timeout=240 ImageTransferTest 35 */ 36 37 import jdk.test.lib.Platform; 38 import test.java.awt.regtesthelpers.process.ProcessCommunicator; 39 import test.java.awt.regtesthelpers.process.ProcessResults; 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 (Platform.isWindows() && !ln.contains("METAFILEPICT")) 178 { 179 // for test failing on JDK without this fix 180 ln.add("METAFILEPICT"); 181 } 182 return ln.toArray(new String[ln.size()]); 183 } 184 185 static void leaveFormat(String format) { 186 SystemFlavorMap sfm = 187 (SystemFlavorMap) SystemFlavorMap.getDefaultFlavorMap(); 188 sfm.setFlavorsForNative(format, 189 new DataFlavor[]{DataFlavor.imageFlavor}); 190 sfm.setNativesForFlavor(DataFlavor.imageFlavor, new String[]{format}); 191 } 192 193 194 boolean areImagesIdentical(Image im1, Image im2) { 195 if (formats[fi].equals("JFIF") || formats[fi].equals("image/jpeg") || 196 formats[fi].equals("GIF") || formats[fi].equals("image/gif")) { 197 // JFIF and GIF are lossy formats 198 return true; 199 } 200 int[] ib1 = getImageData(im1); 201 int[] ib2 = getImageData(im2); 202 203 if (ib1.length != ib2.length) { 204 return false; 205 } 206 207 if (formats[fi].equals("PNG") || 208 formats[fi].equals("image/png") || 209 formats[fi].equals("image/x-png")) { 210 // check alpha as well 211 for (int i = 0; i < ib1.length; i++) { 212 if (ib1[i] != ib2[i]) { 213 System.err.println("different pixels: " + 214 Integer.toHexString(ib1[i]) + " " + 215 Integer.toHexString(ib2[i])); 216 return false; 217 } 218 } 219 } else { 220 for (int i = 0; i < ib1.length; i++) { 221 if ((ib1[i] & 0x00FFFFFF) != (ib2[i] & 0x00FFFFFF)) { 222 System.err.println("different pixels: " + 223 Integer.toHexString(ib1[i]) + " " + 224 Integer.toHexString(ib2[i])); 225 return false; 226 } 227 } 228 } 229 return true; 230 } 231 232 private static int[] getImageData(Image image) { 233 int width = image.getWidth(null); 234 int height = image.getHeight(null); 235 BufferedImage bimage = 236 new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 237 Graphics2D g2d = bimage.createGraphics(); 238 try { 239 g2d.drawImage(image, 0, 0, width, height, null); 240 } finally { 241 g2d.dispose(); 242 } 243 return bimage.getRGB(0, 0, width, height, null, 0, width); 244 } 245 246 public static int sign(int n) { 247 return n < 0 ? -1 : n == 0 ? 0 : 1; 248 } 249 250 } 251 252 253 class ImageDragSource extends ImageTransferer { 254 boolean[] passedArray; 255 256 ImageDragSource() { 257 formats = retrieveFormatsToTest(); 258 passedArray = new boolean[formats.length]; 259 final DragSourceListener dsl = new DragSourceAdapter() { 260 public void dragDropEnd(DragSourceDropEvent e) { 261 System.err.println("Drop was successful=" + e.getDropSuccess()); 262 notifyTransferSuccess(e.getDropSuccess()); 263 if (++fi < formats.length) { 264 leaveFormat(formats[fi]); 265 } 266 } 267 }; 268 269 new DragSource().createDefaultDragGestureRecognizer(frame, 270 DnDConstants.ACTION_COPY, 271 dge -> dge.startDrag(null, new ImageSelection(image), dsl)); 272 leaveFormat(formats[fi]); 273 } 274 275 276 void notifyTransferSuccess(boolean status) { 277 passedArray[fi] = status; 278 } 279 } 280 281 282 class ImageDropTarget extends ImageTransferer { 283 private final Robot robot; 284 private static ImageDropTarget idt; 285 private static Point startPoint, endPoint = new Point(250, 150); 286 private static int dropCount = 0; 287 288 ImageDropTarget() throws AWTException { 289 DropTargetAdapter dropTargetAdapter = new DropTargetAdapter() { 290 @Override 291 public void drop(DropTargetDropEvent dtde) { 292 dropCount++; 293 checkImage(dtde); 294 startImageDrag(); 295 } 296 }; 297 new DropTarget(frame, dropTargetAdapter); 298 robot = new Robot(); 299 } 300 301 302 void checkImage(DropTargetDropEvent dtde) { 303 final Transferable t = dtde.getTransferable(); 304 if (t.isDataFlavorSupported(DataFlavor.imageFlavor)) { 305 dtde.acceptDrop(DnDConstants.ACTION_COPY); 306 Image im; 307 try { 308 im = (Image) t.getTransferData(DataFlavor.imageFlavor); 309 System.err.println("getTransferData was successful"); 310 } catch (Exception e) { 311 System.err.println("Can't getTransferData: " + e); 312 dtde.dropComplete(false); 313 notifyTransferSuccess(false); 314 return; 315 } 316 317 if (im == null) { 318 System.err.println("getTransferData returned null"); 319 dtde.dropComplete(false); 320 notifyTransferSuccess(false); 321 } else if (areImagesIdentical(image, im)) { 322 dtde.dropComplete(true); 323 notifyTransferSuccess(true); 324 } else { 325 System.err.println("transferred image is different from" + 326 " initial image"); 327 dtde.dropComplete(false); 328 notifyTransferSuccess(false); 329 } 330 331 } else { 332 System.err.println("imageFlavor is not supported by Transferable"); 333 dtde.rejectDrop(); 334 notifyTransferSuccess(false); 335 } 336 } 337 338 void startImageDrag() { 339 leaveFormat(formats[fi]); 340 new Thread(() -> { 341 try { 342 Thread.sleep(1000); 343 } catch (InterruptedException e) { 344 if (idt.frame != null) { 345 idt.frame.dispose(); 346 } 347 e.printStackTrace(); 348 // Exit from the child process 349 System.exit(1); 350 } 351 robot.mouseMove(startPoint.x, startPoint.y); 352 robot.mousePress(InputEvent.BUTTON1_MASK); 353 for (Point p = new Point(startPoint); !p.equals(endPoint); 354 p.translate(sign(endPoint.x - p.x), 355 sign(endPoint.y - p.y))) 356 { 357 robot.mouseMove(p.x, p.y); 358 try { 359 Thread.sleep(50); 360 } catch (InterruptedException e) { 361 e.printStackTrace(); 362 } 363 } 364 365 robot.mouseRelease(InputEvent.BUTTON1_MASK); 366 }).start(); 367 } 368 369 void notifyTransferSuccess(boolean status) { 370 if (status) { 371 System.err.println("format passed: " + formats[fi]); 372 } else { 373 if (idt.frame != null) { 374 idt.frame.dispose(); 375 } 376 System.err.println("format failed: " + formats[fi]); 377 System.exit(1); 378 } 379 if (fi < formats.length - 1) { 380 leaveFormat(formats[++fi]); 381 } else { 382 new Thread(() -> { 383 try { 384 Thread.sleep(500); 385 } catch (InterruptedException e) { 386 e.printStackTrace(); 387 } 388 System.exit(0); 389 }).start(); 390 } 391 } 392 393 394 public static void main(String[] args) throws Exception { 395 idt = new ImageDropTarget(); 396 try { 397 idt.frame.setUndecorated(true); 398 399 int x = Integer.parseInt(args[0]); 400 int y = Integer.parseInt(args[1]); 401 startPoint = new Point(Integer.parseInt(args[2]), 402 Integer.parseInt(args[3])); 403 404 idt.formats = new String[args.length - 4]; 405 System.arraycopy(args, 4, idt.formats, 0, args.length - 4); 406 leaveFormat(idt.formats[0]); 407 408 idt.frame.setLocation(x, y); 409 idt.frame.setVisible(true); 410 Util.sync(); 411 412 idt.startImageDrag(); 413 new Thread(() -> { 414 try { 415 Thread.sleep(120000); 416 } catch (InterruptedException e) { 417 e.printStackTrace(); 418 } 419 if (dropCount == 0) { 420 if (idt.frame != null) { 421 idt.frame.dispose(); 422 } 423 System.exit(1); 424 } 425 }).start(); 426 } catch (Throwable e) { 427 if (idt.frame != null) { 428 idt.frame.dispose(); 429 } 430 e.printStackTrace(); 431 System.exit(1); 432 } 433 } 434 435 } 436 437 438 class ImageSelection implements Transferable { 439 private static final int IMAGE = 0; 440 private static final DataFlavor[] flavors = {DataFlavor.imageFlavor}; 441 private Image data; 442 443 public ImageSelection(Image data) { 444 this.data = data; 445 } 446 447 @Override 448 public DataFlavor[] getTransferDataFlavors() { 449 // returning flavors itself would allow client code to modify 450 // our internal behavior 451 return flavors.clone(); 452 } 453 454 @Override 455 public boolean isDataFlavorSupported(DataFlavor flavor) { 456 return Stream.of(flavor).anyMatch(flavor::equals); 457 } 458 459 @Override 460 public Object getTransferData(DataFlavor flavor) 461 throws UnsupportedFlavorException 462 { 463 if (flavor.equals(flavors[IMAGE])) { 464 return data; 465 } else { 466 throw new UnsupportedFlavorException(flavor); 467 } 468 } 469 } 470