DrawBufImgOp.java revision 14851:980da45565c8
1/*
2 * Copyright (c) 2007, 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 * @test
25 * @key headful
26 * @bug 6514990
27 * @summary Verifies that calling
28 * Graphics2D.drawImage(BufferedImage, BufferedImageOp, x, y) to an
29 * OpenGL-accelerated destination produces the same results when performed
30 * in software via BufferedImageOp.filter().
31 * @run main/othervm -Dsun.java2d.opengl=True DrawBufImgOp -ignore
32 * @author campbelc
33 */
34
35import java.awt.*;
36import java.awt.image.*;
37import java.io.File;
38import javax.imageio.ImageIO;
39
40/**
41 * REMIND: This testcase was originally intended to automatically compare
42 * the results of the software BufferedImageOp implementations against
43 * the OGL-accelerated codepaths.  However, there are just too many open
44 * bugs in the mediaLib-based codepaths (see below), which means that
45 * creating the reference image may cause crashes or exceptions,
46 * and even if we work around those cases using the "-ignore" flag,
47 * the visual results of the reference image are often buggy as well
48 * (so the comparison will fail even though the OGL results are correct).
49 * Therefore, for now we will run the testcase with the "-ignore" flag
50 * but without the "-compare" flag, so at least it will be checking for
51 * any exceptions/crashes in the OGL code.  When we fix all of the
52 * outstanding bugs with the software codepaths, we can remove the
53 * "-ignore" flag and maybe even restore the "-compare" flag.  In the
54 * meantime, it stil functions well as a manual testcase (with either
55 * the "-show" or "-dump" options).
56 */
57public class DrawBufImgOp extends Canvas {
58
59    private static final int TESTW = 600;
60    private static final int TESTH = 500;
61    private static boolean done;
62
63    /*
64     * If true, skips tests that are known to trigger bugs (which in
65     * turn may cause crashes, exceptions, or other artifacts).
66     */
67    private static boolean ignore;
68
69    // Test both pow2 and non-pow2 sized images
70    private static final int[] srcSizes = { 32, 17 };
71    private static final int[] srcTypes = {
72        BufferedImage.TYPE_INT_RGB,
73        BufferedImage.TYPE_INT_ARGB,
74        BufferedImage.TYPE_INT_ARGB_PRE,
75        BufferedImage.TYPE_INT_BGR,
76        BufferedImage.TYPE_3BYTE_BGR,
77        BufferedImage.TYPE_4BYTE_ABGR,
78        BufferedImage.TYPE_USHORT_565_RGB,
79        BufferedImage.TYPE_BYTE_GRAY,
80        BufferedImage.TYPE_USHORT_GRAY,
81    };
82
83    private static final RescaleOp
84        rescale1band, rescale3band, rescale4band;
85    private static final LookupOp
86        lookup1bandbyte, lookup3bandbyte, lookup4bandbyte;
87    private static final LookupOp
88        lookup1bandshort, lookup3bandshort, lookup4bandshort;
89    private static final ConvolveOp
90        convolve3x3zero, convolve5x5zero, convolve7x7zero;
91    private static final ConvolveOp
92        convolve3x3noop, convolve5x5noop, convolve7x7noop;
93
94    static {
95        rescale1band = new RescaleOp(0.5f, 10.0f, null);
96        rescale3band = new RescaleOp(
97            new float[] {  0.6f,  0.4f, 0.6f },
98            new float[] { 10.0f, -3.0f, 5.0f },
99            null);
100        rescale4band = new RescaleOp(
101            new float[] {  0.6f, 0.4f, 0.6f, 0.9f },
102            new float[] { -1.0f, 5.0f, 3.0f, 1.0f },
103            null);
104
105        // REMIND: we should probably test non-zero offsets, but that
106        // would require massaging the source image data to avoid going
107        // outside the lookup table array bounds
108        int offset = 0;
109        {
110            byte invert[] = new byte[256];
111            byte halved[] = new byte[256];
112            for (int j = 0; j < 256 ; j++) {
113                invert[j] = (byte) (255-j);
114                halved[j] = (byte) (j / 2);
115            }
116            ByteLookupTable lut1 = new ByteLookupTable(offset, invert);
117            lookup1bandbyte = new LookupOp(lut1, null);
118            ByteLookupTable lut3 =
119                new ByteLookupTable(offset,
120                                    new byte[][] {invert, halved, invert});
121            lookup3bandbyte = new LookupOp(lut3, null);
122            ByteLookupTable lut4 =
123                new ByteLookupTable(offset,
124                               new byte[][] {invert, halved, invert, halved});
125            lookup4bandbyte = new LookupOp(lut4, null);
126        }
127
128        {
129            short invert[] = new short[256];
130            short halved[] = new short[256];
131            for (int j = 0; j < 256 ; j++) {
132                invert[j] = (short) ((255-j) * 255);
133                halved[j] = (short) ((j / 2) * 255);
134            }
135            ShortLookupTable lut1 = new ShortLookupTable(offset, invert);
136            lookup1bandshort = new LookupOp(lut1, null);
137            ShortLookupTable lut3 =
138                new ShortLookupTable(offset,
139                                     new short[][] {invert, halved, invert});
140            lookup3bandshort = new LookupOp(lut3, null);
141            ShortLookupTable lut4 =
142                new ShortLookupTable(offset,
143                              new short[][] {invert, halved, invert, halved});
144            lookup4bandshort = new LookupOp(lut4, null);
145        }
146
147        // 3x3 blur
148        float[] data3 = {
149            0.1f, 0.1f, 0.1f,
150            0.1f, 0.2f, 0.1f,
151            0.1f, 0.1f, 0.1f,
152        };
153        Kernel k3 = new Kernel(3, 3, data3);
154
155        // 5x5 edge
156        float[] data5 = {
157            -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
158            -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
159            -1.0f, -1.0f, 24.0f, -1.0f, -1.0f,
160            -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
161            -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
162        };
163        Kernel k5 = new Kernel(5, 5, data5);
164
165        // 7x7 blur
166        float[] data7 = {
167            0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
168            0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
169            0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
170            0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
171            0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
172            0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
173            0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f, 0.02f,
174        };
175        Kernel k7 = new Kernel(7, 7, data7);
176
177        convolve3x3zero = new ConvolveOp(k3, ConvolveOp.EDGE_ZERO_FILL, null);
178        convolve5x5zero = new ConvolveOp(k5, ConvolveOp.EDGE_ZERO_FILL, null);
179        convolve7x7zero = new ConvolveOp(k7, ConvolveOp.EDGE_ZERO_FILL, null);
180
181        convolve3x3noop = new ConvolveOp(k3, ConvolveOp.EDGE_NO_OP, null);
182        convolve5x5noop = new ConvolveOp(k5, ConvolveOp.EDGE_NO_OP, null);
183        convolve7x7noop = new ConvolveOp(k7, ConvolveOp.EDGE_NO_OP, null);
184    }
185
186    public void paint(Graphics g) {
187        synchronized (this) {
188            if (done) {
189                return;
190            }
191        }
192
193        VolatileImage vimg = createVolatileImage(TESTW, TESTH);
194        vimg.validate(getGraphicsConfiguration());
195
196        Graphics2D g2d = vimg.createGraphics();
197        renderTest(g2d);
198        g2d.dispose();
199
200        g.drawImage(vimg, 0, 0, null);
201
202        Toolkit.getDefaultToolkit().sync();
203
204        synchronized (this) {
205            done = true;
206            notifyAll();
207        }
208    }
209
210    /*
211     * foreach source image size (once with pow2, once with non-pow2)
212     *
213     *   foreach BufferedImage type
214     *
215     *     RescaleOp (1 band)
216     *     RescaleOp (3 bands, if src has 3 bands)
217     *     RescaleOp (4 bands, if src has 4 bands)
218     *
219     *     foreach LookupTable type (once with ByteLUT, once with ShortLUT)
220     *       LookupOp (1 band)
221     *       LookupOp (3 bands, if src has 3 bands)
222     *       LookupOp (4 bands, if src has 4 bands)
223     *
224     *     foreach edge condition (once with ZERO_FILL, once with EDGE_NO_OP)
225     *       ConvolveOp (3x3)
226     *       ConvolveOp (5x5)
227     *       ConvolveOp (7x7)
228     */
229    private void renderTest(Graphics2D g2d) {
230        g2d.setColor(Color.white);
231        g2d.fillRect(0, 0, TESTW, TESTH);
232
233        int yorig = 2;
234        int xinc = 34;
235        int yinc = srcSizes[0] + srcSizes[1] + 2 + 2;
236
237        for (int srcType : srcTypes) {
238            int y = yorig;
239
240            for (int srcSize : srcSizes) {
241                int x = 2;
242                System.out.printf("type=%d size=%d\n", srcType, srcSize);
243
244                BufferedImage srcImg = makeSourceImage(srcSize, srcType);
245                ColorModel srcCM = srcImg.getColorModel();
246
247                // RescaleOp
248                g2d.drawImage(srcImg, rescale1band, x, y);
249                x += xinc;
250                // REMIND: 3-band RescaleOp.filter() throws IAE for images
251                //         that contain an alpha channel (bug to be filed)
252                if (srcCM.getNumColorComponents() == 3 &&
253                    !(ignore && srcCM.hasAlpha()))
254                {
255                    g2d.drawImage(srcImg, rescale3band, x, y);
256                }
257                x += xinc;
258                if (srcCM.getNumComponents() == 4) {
259                    g2d.drawImage(srcImg, rescale4band, x, y);
260                }
261                x += xinc;
262
263                // LookupOp
264                // REMIND: Our LUTs are only 256 elements long, so won't
265                //         currently work with USHORT_GRAY data
266                if (srcType != BufferedImage.TYPE_USHORT_GRAY) {
267                    g2d.drawImage(srcImg, lookup1bandbyte, x, y);
268                    x += xinc;
269                    if (srcCM.getNumColorComponents() == 3) {
270                        g2d.drawImage(srcImg, lookup3bandbyte, x, y);
271                    }
272                    x += xinc;
273                    if (srcCM.getNumComponents() == 4) {
274                        g2d.drawImage(srcImg, lookup4bandbyte, x, y);
275                    }
276                    x += xinc;
277
278                    // REMIND: LookupOp.createCompatibleDestImage() throws
279                    //         IAE for 3BYTE_BGR/4BYTE_ABGR (bug to be filed)
280                    if (!(ignore &&
281                          (srcType == BufferedImage.TYPE_3BYTE_BGR ||
282                           srcType == BufferedImage.TYPE_4BYTE_ABGR)))
283                    {
284                        g2d.drawImage(srcImg, lookup1bandshort, x, y);
285                        x += xinc;
286                        // REMIND: 3-band LookupOp.filter() throws IAE for
287                        //         images that contain an alpha channel
288                        //         (bug to be filed)
289                        if (srcCM.getNumColorComponents() == 3 &&
290                            !(ignore && srcCM.hasAlpha()))
291                        {
292                            g2d.drawImage(srcImg, lookup3bandshort, x, y);
293                        }
294                        x += xinc;
295                        if (srcCM.getNumComponents() == 4) {
296                            g2d.drawImage(srcImg, lookup4bandshort, x, y);
297                        }
298                        x += xinc;
299                    } else {
300                        x += 3*xinc;
301                    }
302                } else {
303                    x += 6*xinc;
304                }
305
306                // ConvolveOp
307                // REMIND: ConvolveOp.filter() throws ImagingOpException
308                //         for 3BYTE_BGR (see 4957775)
309                if (srcType != BufferedImage.TYPE_3BYTE_BGR) {
310                    g2d.drawImage(srcImg, convolve3x3zero, x, y);
311                    x += xinc;
312                    g2d.drawImage(srcImg, convolve5x5zero, x, y);
313                    x += xinc;
314                    g2d.drawImage(srcImg, convolve7x7zero, x, y);
315                    x += xinc;
316
317                    g2d.drawImage(srcImg, convolve3x3noop, x, y);
318                    x += xinc;
319                    g2d.drawImage(srcImg, convolve5x5noop, x, y);
320                    x += xinc;
321                    g2d.drawImage(srcImg, convolve7x7noop, x, y);
322                    x += xinc;
323                } else {
324                    x += 6*xinc;
325                }
326
327                y += srcSize + 2;
328            }
329
330            yorig += yinc;
331        }
332    }
333
334    private BufferedImage makeSourceImage(int size, int type) {
335        int s2 = size/2;
336        BufferedImage img = new BufferedImage(size, size, type);
337        Graphics2D g2d = img.createGraphics();
338        g2d.setComposite(AlphaComposite.Src);
339        g2d.setColor(Color.orange);
340        g2d.fillRect(0, 0, size, size);
341        g2d.setColor(Color.red);
342        g2d.fillRect(0, 0, s2, s2);
343        g2d.setColor(Color.green);
344        g2d.fillRect(s2, 0, s2, s2);
345        g2d.setColor(Color.blue);
346        g2d.fillRect(0, s2, s2, s2);
347        g2d.setColor(new Color(255, 255, 0, 128));
348        g2d.fillRect(s2, s2, s2, s2);
349        g2d.setColor(Color.pink);
350        g2d.fillOval(s2-3, s2-3, 6, 6);
351        g2d.dispose();
352        return img;
353    }
354
355    public BufferedImage makeReferenceImage() {
356        BufferedImage img = new BufferedImage(TESTW, TESTH,
357                                              BufferedImage.TYPE_INT_RGB);
358        Graphics2D g2d = img.createGraphics();
359        renderTest(g2d);
360        g2d.dispose();
361        return img;
362    }
363
364    public Dimension getPreferredSize() {
365        return new Dimension(TESTW, TESTH);
366    }
367
368    private static void compareImages(BufferedImage refImg,
369                                      BufferedImage testImg,
370                                      int tolerance)
371    {
372        int x1 = 0;
373        int y1 = 0;
374        int x2 = refImg.getWidth();
375        int y2 = refImg.getHeight();
376
377        for (int y = y1; y < y2; y++) {
378            for (int x = x1; x < x2; x++) {
379                Color expected = new Color(refImg.getRGB(x, y));
380                Color actual   = new Color(testImg.getRGB(x, y));
381                if (!isSameColor(expected, actual, tolerance)) {
382                    throw new RuntimeException("Test failed at x="+x+" y="+y+
383                                               " (expected="+expected+
384                                               " actual="+actual+
385                                               ")");
386                }
387            }
388        }
389    }
390
391    private static boolean isSameColor(Color c1, Color c2, int e) {
392        int r1 = c1.getRed();
393        int g1 = c1.getGreen();
394        int b1 = c1.getBlue();
395        int r2 = c2.getRed();
396        int g2 = c2.getGreen();
397        int b2 = c2.getBlue();
398        int rmin = Math.max(r2-e, 0);
399        int gmin = Math.max(g2-e, 0);
400        int bmin = Math.max(b2-e, 0);
401        int rmax = Math.min(r2+e, 255);
402        int gmax = Math.min(g2+e, 255);
403        int bmax = Math.min(b2+e, 255);
404        if (r1 >= rmin && r1 <= rmax &&
405            g1 >= gmin && g1 <= gmax &&
406            b1 >= bmin && b1 <= bmax)
407        {
408            return true;
409        }
410        return false;
411    }
412
413    public static void main(String[] args) throws Exception {
414        boolean show = false;
415        boolean dump = false;
416        boolean compare = false;
417
418        for (String arg : args) {
419            if (arg.equals("-show")) {
420                show = true;
421            } else if (arg.equals("-dump")) {
422                dump = true;
423            } else if (arg.equals("-compare")) {
424                compare = true;
425            } else if (arg.equals("-ignore")) {
426                ignore = true;
427            }
428        }
429
430        DrawBufImgOp test = new DrawBufImgOp();
431        Frame frame = new Frame();
432        frame.add(test);
433        frame.pack();
434        frame.setVisible(true);
435
436        // Wait until the component's been painted
437        synchronized (test) {
438            while (!done) {
439                try {
440                    test.wait();
441                } catch (InterruptedException e) {
442                    throw new RuntimeException("Failed: Interrupted");
443                }
444            }
445        }
446
447        GraphicsConfiguration gc = frame.getGraphicsConfiguration();
448        if (gc.getColorModel() instanceof IndexColorModel) {
449            System.out.println("IndexColorModel detected: " +
450                               "test considered PASSED");
451            frame.dispose();
452            return;
453        }
454
455        // Grab the screen region
456        BufferedImage capture = null;
457        try {
458            Robot robot = new Robot();
459            Point pt1 = test.getLocationOnScreen();
460            Rectangle rect = new Rectangle(pt1.x, pt1.y, TESTW, TESTH);
461            capture = robot.createScreenCapture(rect);
462        } catch (Exception e) {
463            throw new RuntimeException("Problems creating Robot");
464        } finally {
465            if (!show) {
466                frame.dispose();
467            }
468        }
469
470        // Compare the images (allow for +/- 1 bit differences in color comps)
471        if (dump || compare) {
472            BufferedImage ref = test.makeReferenceImage();
473            if (dump) {
474                ImageIO.write(ref,     "png",
475                              new File("DrawBufImgOp.ref.png"));
476                ImageIO.write(capture, "png",
477                              new File("DrawBufImgOp.cap.png"));
478            }
479            if (compare) {
480                test.compareImages(ref, capture, 1);
481            }
482        }
483    }
484}
485