| /* |
| * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /* |
| * @test |
| * @key headful |
| * @bug 6521533 6525997 7102282 |
| * @summary Verifies that the OGL-accelerated codepaths for GradientPaint, |
| * LinearGradientPaint, and RadialGradientPaint produce results that are |
| * sufficiently close to those produced by the software codepaths. |
| * @run main/othervm -Dsun.java2d.uiScale=1 -Dsun.java2d.opengl=True GradientPaints |
| * @author campbelc |
| */ |
| |
| import java.awt.*; |
| import java.awt.MultipleGradientPaint.ColorSpaceType; |
| import java.awt.MultipleGradientPaint.CycleMethod; |
| import java.awt.geom.*; |
| import java.awt.image.*; |
| import java.io.File; |
| import java.util.Arrays; |
| import javax.imageio.ImageIO; |
| |
| public class GradientPaints extends Canvas { |
| |
| private static final int TESTW = 600; |
| private static final int TESTH = 500; |
| |
| /* |
| * We expect slight differences in rendering between the OpenGL and |
| * software pipelines due to algorithmic and rounding differences. |
| * The purpose of this test is just to make sure that the OGL pipeline |
| * is producing results that are "reasonably" consistent with those |
| * produced in software, so we will allow +/-TOLERANCE differences |
| * in each component. When comparing the test and reference images, |
| * we add up the number of pixels that fall outside this tolerance |
| * range and if the sum is larger than some percentage of the total |
| * number of pixels. |
| * |
| * REMIND: Note that we have separate thresholds for linear and radial |
| * gradients because the visible differences between OGL and software |
| * are more apparent in the radial cases. In the future we should try |
| * to reduce the number of mismatches between the two approaches, but |
| * for now the visible differences are slight enough to not cause worry. |
| */ |
| private static final int TOLERANCE = 5; |
| private static final int ALLOWED_MISMATCHES_LINEAR = |
| (int)(TESTW * TESTH * 0.18); |
| private static final int ALLOWED_MISMATCHES_RADIAL = |
| (int)(TESTW * TESTH * 0.45); |
| |
| private static boolean done; |
| private static boolean verbose; |
| |
| private static final Color[] COLORS = { |
| new Color(0, 0, 0), |
| new Color(128, 128, 128), |
| new Color(255, 0, 0), |
| new Color(255, 255, 0), |
| new Color(0, 255, 0), |
| new Color(0, 255, 255), |
| new Color(128, 0, 255), |
| new Color(128, 128, 128), |
| }; |
| |
| private static enum PaintType {BASIC, LINEAR, RADIAL}; |
| private static enum XformType {IDENTITY, TRANSLATE, SCALE, SHEAR, ROTATE}; |
| private static final int[] numStopsArray = {2, 4, 7}; |
| private static final Object[] hints = { |
| RenderingHints.VALUE_ANTIALIAS_OFF, |
| RenderingHints.VALUE_ANTIALIAS_ON, |
| }; |
| |
| public void paint(Graphics g) { |
| synchronized (this) { |
| if (!done) { |
| done = true; |
| notifyAll(); |
| } |
| } |
| } |
| |
| private void testOne(BufferedImage refImg, VolatileImage testImg) { |
| Graphics2D gref = refImg.createGraphics(); |
| Graphics2D gtest = testImg.createGraphics(); |
| Paint paint = |
| makePaint(PaintType.RADIAL, CycleMethod.REPEAT, |
| ColorSpaceType.SRGB, XformType.IDENTITY, 7); |
| Object aahint = hints[0]; |
| renderTest(gref, paint, aahint); |
| renderTest(gtest, paint, aahint); |
| Toolkit.getDefaultToolkit().sync(); |
| compareImages(refImg, testImg.getSnapshot(), |
| TOLERANCE, 0, ""); |
| gref.dispose(); |
| gtest.dispose(); |
| } |
| |
| private void testAll(Graphics gscreen, |
| BufferedImage refImg, VolatileImage testImg) |
| { |
| Graphics2D gref = refImg.createGraphics(); |
| Graphics2D gtest = testImg.createGraphics(); |
| for (PaintType paintType : PaintType.values()) { |
| for (CycleMethod cycleMethod : CycleMethod.values()) { |
| for (ColorSpaceType colorSpace : ColorSpaceType.values()) { |
| for (XformType xform : XformType.values()) { |
| for (Object aahint : hints) { |
| for (int numStops : numStopsArray) { |
| Paint paint = |
| makePaint(paintType, cycleMethod, |
| colorSpace, xform, numStops); |
| String msg = |
| "type=" + paintType + |
| " cycleMethod=" + cycleMethod + |
| " colorSpace=" + colorSpace + |
| " xformType=" + xform + |
| " numStops=" + numStops + |
| " aa=" + aahint; |
| renderTest(gref, paint, aahint); |
| renderTest(gtest, paint, aahint); |
| gscreen.drawImage(testImg, 0, 0, null); |
| Toolkit.getDefaultToolkit().sync(); |
| int allowedMismatches = |
| paintType == PaintType.RADIAL ? |
| ALLOWED_MISMATCHES_RADIAL : |
| ALLOWED_MISMATCHES_LINEAR; |
| compareImages(refImg, testImg.getSnapshot(), |
| TOLERANCE, allowedMismatches, |
| msg); |
| } |
| } |
| } |
| } |
| } |
| } |
| gref.dispose(); |
| gtest.dispose(); |
| } |
| |
| private Paint makePaint(PaintType paintType, |
| CycleMethod cycleMethod, |
| ColorSpaceType colorSpace, |
| XformType xformType, int numStops) |
| { |
| int startX = TESTW/6; |
| int startY = TESTH/6; |
| int endX = TESTW/2; |
| int endY = TESTH/2; |
| int ctrX = TESTW/2; |
| int ctrY = TESTH/2; |
| int focusX = ctrX + 20; |
| int focusY = ctrY + 20; |
| float radius = 100.0f; |
| Paint paint; |
| AffineTransform transform; |
| |
| Color[] colors = Arrays.copyOf(COLORS, numStops); |
| float[] fractions = new float[colors.length]; |
| for (int i = 0; i < fractions.length; i++) { |
| fractions[i] = ((float)i) / (fractions.length-1); |
| } |
| |
| switch (xformType) { |
| default: |
| case IDENTITY: |
| transform = new AffineTransform(); |
| break; |
| case TRANSLATE: |
| transform = AffineTransform.getTranslateInstance(2, 2); |
| break; |
| case SCALE: |
| transform = AffineTransform.getScaleInstance(1.2, 1.4); |
| break; |
| case SHEAR: |
| transform = AffineTransform.getShearInstance(0.1, 0.1); |
| break; |
| case ROTATE: |
| transform = AffineTransform.getRotateInstance(Math.PI / 4, |
| getWidth()/2, |
| getHeight()/2); |
| break; |
| } |
| |
| switch (paintType) { |
| case BASIC: |
| boolean cyclic = (cycleMethod != CycleMethod.NO_CYCLE); |
| paint = |
| new GradientPaint(startX, startY, Color.RED, |
| endX, endY, Color.BLUE, cyclic); |
| break; |
| |
| default: |
| case LINEAR: |
| paint = |
| new LinearGradientPaint(new Point2D.Float(startX, startY), |
| new Point2D.Float(endX, endY), |
| fractions, colors, |
| cycleMethod, colorSpace, |
| transform); |
| break; |
| |
| case RADIAL: |
| paint = |
| new RadialGradientPaint(new Point2D.Float(ctrX, ctrY), |
| radius, |
| new Point2D.Float(focusX, focusY), |
| fractions, colors, |
| cycleMethod, colorSpace, |
| transform); |
| break; |
| } |
| |
| return paint; |
| } |
| |
| private void renderTest(Graphics2D g2d, Paint p, Object aahint) { |
| g2d.setColor(Color.white); |
| g2d.fillRect(0, 0, TESTW, TESTH); |
| g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, aahint); |
| g2d.setPaint(p); |
| g2d.fillOval(0, 0, TESTW, TESTH); |
| } |
| |
| public Dimension getPreferredSize() { |
| return new Dimension(TESTW, TESTH); |
| } |
| |
| private static void compareImages(BufferedImage refImg, |
| BufferedImage testImg, |
| int tolerance, int allowedMismatches, |
| String msg) |
| { |
| int numMismatches = 0; |
| int x1 = 0; |
| int y1 = 0; |
| int x2 = refImg.getWidth(); |
| int y2 = refImg.getHeight(); |
| |
| for (int y = y1; y < y2; y++) { |
| for (int x = x1; x < x2; x++) { |
| Color expected = new Color(refImg.getRGB(x, y)); |
| Color actual = new Color(testImg.getRGB(x, y)); |
| if (!isSameColor(expected, actual, tolerance)) { |
| numMismatches++; |
| } |
| } |
| } |
| |
| if (verbose) { |
| System.out.println(msg); |
| } |
| if (numMismatches > allowedMismatches) { |
| try { |
| ImageIO.write(refImg, "png", |
| new File("GradientPaints.ref.png")); |
| ImageIO.write(testImg, "png", |
| new File("GradientPaints.cap.png")); |
| } catch (Exception e) { |
| } |
| if (!verbose) { |
| System.err.println(msg); |
| } |
| throw new RuntimeException("Test failed: Number of mismatches (" + |
| numMismatches + |
| ") exceeds limit (" + |
| allowedMismatches + |
| ") with tolerance=" + |
| tolerance); |
| } |
| } |
| |
| private static boolean isSameColor(Color c1, Color c2, int e) { |
| int r1 = c1.getRed(); |
| int g1 = c1.getGreen(); |
| int b1 = c1.getBlue(); |
| int r2 = c2.getRed(); |
| int g2 = c2.getGreen(); |
| int b2 = c2.getBlue(); |
| int rmin = Math.max(r2-e, 0); |
| int gmin = Math.max(g2-e, 0); |
| int bmin = Math.max(b2-e, 0); |
| int rmax = Math.min(r2+e, 255); |
| int gmax = Math.min(g2+e, 255); |
| int bmax = Math.min(b2+e, 255); |
| if (r1 >= rmin && r1 <= rmax && |
| g1 >= gmin && g1 <= gmax && |
| b1 >= bmin && b1 <= bmax) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| public static void main(String[] args) { |
| if (args.length == 1 && args[0].equals("-verbose")) { |
| verbose = true; |
| } |
| |
| GradientPaints test = new GradientPaints(); |
| Frame frame = new Frame(); |
| frame.add(test); |
| frame.pack(); |
| frame.setVisible(true); |
| |
| // Wait until the component's been painted |
| synchronized (test) { |
| while (!done) { |
| try { |
| test.wait(); |
| } catch (InterruptedException e) { |
| throw new RuntimeException("Failed: Interrupted"); |
| } |
| } |
| } |
| |
| GraphicsConfiguration gc = frame.getGraphicsConfiguration(); |
| if (gc.getColorModel() instanceof IndexColorModel) { |
| System.out.println("IndexColorModel detected: " + |
| "test considered PASSED"); |
| frame.dispose(); |
| return; |
| } |
| |
| BufferedImage refImg = |
| new BufferedImage(TESTW, TESTH, BufferedImage.TYPE_INT_RGB); |
| VolatileImage testImg = frame.createVolatileImage(TESTW, TESTH); |
| testImg.validate(gc); |
| |
| try { |
| test.testAll(test.getGraphics(), refImg, testImg); |
| } finally { |
| frame.dispose(); |
| } |
| } |
| } |