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