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 8176556 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 BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 153 int index = 0; 154 for (int y = 0; y < h; y++) { 155 for (int x = 0; x < w; x++) { 156 int red = 127; 157 int green = 127; 158 int blue = y > h / 2 ? 127 : 0; 159 if (x < w / 4 && y < h / 4) { 160 red = 0; 161 } 162 pix[index] = 163 (red << 16) | (green << 8) | blue; 164 img.setRGB(x, y, pix[index]); 165 index++; 166 } 167 } 168 return (Image)img; 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 for (int i = 0; i < ib1.length; i++) { 208 if (ib1[i] != ib2[i]) { 209 System.err.println("different pixels: " + 210 Integer.toHexString(ib1[i]) + " " + 211 Integer.toHexString(ib2[i])); 212 return false; 213 } 214 } 215 return true; 216 } 217 218 private static int[] getImageData(Image image) { 219 int width = image.getWidth(null); 220 int height = image.getHeight(null); 221 BufferedImage bimage = 222 new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 223 Graphics2D g2d = bimage.createGraphics(); 224 try { 225 g2d.drawImage(image, 0, 0, width, height, null); 226 } finally { 227 g2d.dispose(); 228 } 229 return bimage.getRGB(0, 0, width, height, null, 0, width); 230 } 231 232 public static int sign(int n) { 233 return n < 0 ? -1 : n == 0 ? 0 : 1; 234 } 235 236 } 237 238 239 class ImageDragSource extends ImageTransferer { 240 boolean[] passedArray; 241 242 ImageDragSource() { 243 formats = retrieveFormatsToTest(); 244 passedArray = new boolean[formats.length]; 245 final DragSourceListener dsl = new DragSourceAdapter() { 246 public void dragDropEnd(DragSourceDropEvent e) { 247 System.err.println("Drop was successful=" + e.getDropSuccess()); 248 notifyTransferSuccess(e.getDropSuccess()); 249 if (++fi < formats.length) { 250 leaveFormat(formats[fi]); 251 } 252 } 253 }; 254 255 new DragSource().createDefaultDragGestureRecognizer(frame, 256 DnDConstants.ACTION_COPY, 257 dge -> dge.startDrag(null, new ImageSelection(image), dsl)); 258 leaveFormat(formats[fi]); 259 } 260 261 262 void notifyTransferSuccess(boolean status) { 263 passedArray[fi] = status; 264 } 265 } 266 267 268 class ImageDropTarget extends ImageTransferer { 269 private final Robot robot; 270 private static ImageDropTarget idt; 271 private static Point startPoint, endPoint = new Point(250, 150); 272 private static int dropCount = 0; 273 274 ImageDropTarget() throws AWTException { 275 DropTargetAdapter dropTargetAdapter = new DropTargetAdapter() { 276 @Override 277 public void drop(DropTargetDropEvent dtde) { 278 dropCount++; 279 checkImage(dtde); 280 startImageDrag(); 281 } 282 }; 283 new DropTarget(frame, dropTargetAdapter); 284 robot = new Robot(); 285 } 286 287 288 void checkImage(DropTargetDropEvent dtde) { 289 final Transferable t = dtde.getTransferable(); 290 if (t.isDataFlavorSupported(DataFlavor.imageFlavor)) { 291 dtde.acceptDrop(DnDConstants.ACTION_COPY); 292 Image im; 293 try { 294 im = (Image) t.getTransferData(DataFlavor.imageFlavor); 295 System.err.println("getTransferData was successful"); 296 } catch (Exception e) { 297 System.err.println("Can't getTransferData: " + e); 298 dtde.dropComplete(false); 299 notifyTransferSuccess(false); 300 return; 301 } 302 303 if (im == null) { 304 System.err.println("getTransferData returned null"); 305 dtde.dropComplete(false); 306 notifyTransferSuccess(false); 307 } else if (areImagesIdentical(image, im)) { 308 dtde.dropComplete(true); 309 notifyTransferSuccess(true); 310 } else { 311 System.err.println("transferred image is different from" + 312 " initial image"); 313 dtde.dropComplete(false); 314 notifyTransferSuccess(false); 315 } 316 317 } else { 318 System.err.println("imageFlavor is not supported by Transferable"); 319 dtde.rejectDrop(); 320 notifyTransferSuccess(false); 321 } 322 } 323 324 void startImageDrag() { 325 leaveFormat(formats[fi]); 326 new Thread(() -> { 327 try { 328 Thread.sleep(1000); 329 } catch (InterruptedException e) { 330 if (idt.frame != null) { 331 idt.frame.dispose(); 332 } 333 e.printStackTrace(); 334 // Exit from the child process 335 System.exit(1); 336 } 337 robot.mouseMove(startPoint.x, startPoint.y); 338 robot.mousePress(InputEvent.BUTTON1_MASK); 339 for (Point p = new Point(startPoint); !p.equals(endPoint); 340 p.translate(sign(endPoint.x - p.x), 341 sign(endPoint.y - p.y))) 342 { 343 robot.mouseMove(p.x, p.y); 344 try { 345 Thread.sleep(50); 346 } catch (InterruptedException e) { 347 e.printStackTrace(); 348 } 349 } 350 351 robot.mouseRelease(InputEvent.BUTTON1_MASK); 352 }).start(); 353 } 354 355 void notifyTransferSuccess(boolean status) { 356 if (status) { 357 System.err.println("format passed: " + formats[fi]); 358 } else { 359 if (idt.frame != null) { 360 idt.frame.dispose(); 361 } 362 System.err.println("format failed: " + formats[fi]); 363 System.exit(1); 364 } 365 if (fi < formats.length - 1) { 366 leaveFormat(formats[++fi]); 367 } else { 368 new Thread(() -> { 369 try { 370 Thread.sleep(500); 371 } catch (InterruptedException e) { 372 e.printStackTrace(); 373 } 374 System.exit(0); 375 }).start(); 376 } 377 } 378 379 380 public static void main(String[] args) throws Exception { 381 idt = new ImageDropTarget(); 382 try { 383 idt.frame.setUndecorated(true); 384 385 int x = Integer.parseInt(args[0]); 386 int y = Integer.parseInt(args[1]); 387 startPoint = new Point(Integer.parseInt(args[2]), 388 Integer.parseInt(args[3])); 389 390 idt.formats = new String[args.length - 4]; 391 System.arraycopy(args, 4, idt.formats, 0, args.length - 4); 392 leaveFormat(idt.formats[0]); 393 394 idt.frame.setLocation(x, y); 395 idt.frame.setVisible(true); 396 Util.sync(); 397 398 idt.startImageDrag(); 399 new Thread(() -> { 400 try { 401 Thread.sleep(120000); 402 } catch (InterruptedException e) { 403 e.printStackTrace(); 404 } 405 if (dropCount == 0) { 406 if (idt.frame != null) { 407 idt.frame.dispose(); 408 } 409 System.exit(1); 410 } 411 }).start(); 412 } catch (Throwable e) { 413 if (idt.frame != null) { 414 idt.frame.dispose(); 415 } 416 e.printStackTrace(); 417 System.exit(1); 418 } 419 } 420 421 } 422 423 424 class ImageSelection implements Transferable { 425 private static final int IMAGE = 0; 426 private static final DataFlavor[] flavors = {DataFlavor.imageFlavor}; 427 private Image data; 428 429 public ImageSelection(Image data) { 430 this.data = data; 431 } 432 433 @Override 434 public DataFlavor[] getTransferDataFlavors() { 435 // returning flavors itself would allow client code to modify 436 // our internal behavior 437 return flavors.clone(); 438 } 439 440 @Override 441 public boolean isDataFlavorSupported(DataFlavor flavor) { 442 return Stream.of(flavor).anyMatch(flavor::equals); 443 } 444 445 @Override 446 public Object getTransferData(DataFlavor flavor) 447 throws UnsupportedFlavorException 448 { 449 if (flavor.equals(flavors[IMAGE])) { 450 return data; 451 } else { 452 throw new UnsupportedFlavorException(flavor); 453 } 454 } 455 } 456