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