Path: blob/master/test/jdk/java/awt/Graphics/LineClipTest.java
41149 views
/*1* Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24* @test25* @key headful26* @bug 4780022 4862193 717952627* @summary Tests that clipped lines are drawn over the same pixels28* as unclipped lines (within the clip bounds)29* @run main/timeout=600/othervm -Dsun.java2d.ddforcevram=true LineClipTest30* @run main/timeout=600/othervm LineClipTest31*/323334/**35* This app tests whether we are drawing clipped lines the same36* as unclipped lines. The problem occurred when we started37* clipping d3d lines using simple integer clipping, which did not38* account for sub-pixel precision and ended up drawing very different39* pixels than the same line drawn unclipped. A supposed fix40* to that problem used floating-point clipping instead, but there41* was some problem with very limited precision inside of d3d42* (presumably in hardware) that caused some variation in pixels.43* We decided that whatever the fix was, we needed a serious44* line check test to make sure that all kinds of different45* lines would be drawn exactly the same inside the clip area,46* regardless of whether clipping was enabled. This test should47* check all kinds of different cases, such as lines that fall48* completely outside, completely inside, start outside and49* end inside, etc., and lines should end and originate in50* all quadrants of the space divided up by the clip box.51*52* The test works as follows:53* We create nine quadrants using the spaces bisected by the54* edges of the clip bounds (note that only one of these55* quadrants is actually visible when clipping is enabled).56* We create several points in each of these quadrants57* (three in each of the invisible quadrants, nine in the58* center/visible quadrant). Our resulting grid looks like59* this:60*61* x x|x x x|x x62* | |63* | |64* | |65* | |66* | |67* x | | x68* -----------------------------------69* x |x x x| x70* | |71* | |72* x |x x x| x73* | |74* | |75* x |x x x| x76* -----------------------------------77* x | | x78* | |79* | |80* | |81* | |82* | |83* x x|x x x|x x84*85* The test then draws lines from every point to every other86* point. First, we draw unclipped lines in blue and87* then we draw clipped lines in red.88* At certain times (after every point during the default89* test, after every quadrant of lines if you run with the -quick90* option), we check for errors and draw the current image91* to the screen. Error checking consists of copying the92* VolatileImage to a BufferedImage (because we need access93* to the pixels directly) and checking every pixel in the94* image. The check is simple: everything outside the95* clip bounds should be blue (or the background color) and96* everything inside the clip bounds should be red (or the97* background color). So any blue pixel inside or red98* pixel outside means that there was a drawing error and99* the test fails.100* There are 4 modes that the test can run in (dynamic mode is101* exclusive to the other modes, but the other modes are combinable):102*103* (default): the clip is set104* to a default size (100x100) and the test is run.105*106* -quick: The error107* check is run only after every quadrant of lines is108* drawn. This speeds up the test considerably with109* some less accuracy in error checking (because pixels110* from some lines may overdrawn pixels from other lines111* before we have verified the correctness of those112* pixels).113*114* -dynamic: There is no error checking, but this version115* of the test automatically resizes the clip bounds and116* reruns the test over and over. Nothing besides the117* visual check verifies that the test is running correctly.118*119* -rect: Instead of drawing lines, the test draws rectangles120* to/from all points in all quadrants. This tests similar121* clipping functionality for drawRect().122*123* n (where "n" is a number): sets the clip size to the124* given value. Just like the default test except that125* the clip size is as specified.126*127* Note: this test must be run with the -Dsun.java2d.ddforcevram=true128* option to force the test image to stay in VRAM. We currently129* punt VRAM images to system memory when we detect lots of130* reads. Since we read the whole buffer on every error check131* to copy it to the BufferedImage), this causes us to punt the132* buffer. A system memory surface will have no d3d capabilities,133* thus we are not testing the d3d line quality when this happens.134* By using the ddforcevram flag, we make sure the buffer135* stays put in VRAM and d3d is used to draw the lines.136*/137138import javax.swing.*;139import java.awt.*;140import java.awt.image.*;141142143public class LineClipTest extends Component implements Runnable {144145int clipBumpVal = 5;146static int clipSize = 100;147int clipX1;148int clipY1;149static final int NUM_QUADS = 9;150Point quadrants[][] = new Point[NUM_QUADS][];151static boolean dynamic = false;152BufferedImage imageChecker = null;153Color unclippedColor = Color.blue;154Color clippedColor = Color.red;155int testW = -1, testH = -1;156VolatileImage testImage = null;157static boolean keepRunning = false;158static boolean quickTest = false;159static boolean rectTest = false;160static boolean runTestDone = false;161static Frame f = null;162163/**164* Check for errors in the grid. This error check consists of165* copying the buffer into a BufferedImage and reading all pixels166* in that image. No pixel outside the clip bounds should be167* of the color clippedColor and no pixel inside should be168* of the color unclippedColor. Any wrong color returns an error.169*/170boolean gridError(Graphics g) {171boolean error = false;172if (imageChecker == null || (imageChecker.getWidth() != testW) ||173(imageChecker.getHeight() != testH))174{175// Recreate BufferedImage as necessary176GraphicsConfiguration gc = getGraphicsConfiguration();177ColorModel cm = gc.getColorModel();178WritableRaster wr =179cm.createCompatibleWritableRaster(getWidth(), getHeight());180imageChecker =181new BufferedImage(cm, wr,182cm.isAlphaPremultiplied(), null);183}184// Copy buffer to BufferedImage185Graphics gChecker = imageChecker.getGraphics();186gChecker.drawImage(testImage, 0, 0, this);187188// Set up pixel colors to check against189int clippedPixelColor = clippedColor.getRGB();190int unclippedPixelColor = unclippedColor.getRGB();191int wrongPixelColor = clippedPixelColor;192boolean insideClip = false;193for (int row = 0; row < getHeight(); ++row) {194for (int col = 0; col < getWidth(); ++col) {195if (row >= clipY1 && row < (clipY1 + clipSize) &&196col >= clipX1 && col < (clipX1 + clipSize))197{198// Inside clip bounds - should not see unclipped color199wrongPixelColor = unclippedPixelColor;200} else {201// Outside clip - should not see clipped color202wrongPixelColor = clippedPixelColor;203}204int pixel = imageChecker.getRGB(col, row);205if (pixel == wrongPixelColor) {206System.out.println("FAILED: pixel = " +207Integer.toHexString(pixel) +208" at (x, y) = " + col + ", " + row);209// Draw magenta rectangle around problem pixel in buffer210// for visual feedback to user211g.setColor(Color.magenta);212g.drawRect(col - 1, row - 1, 2, 2);213error = true;214}215}216}217return error;218}219220/**221* Draw all test lines and check for errors (unless running222* with -dynamic option)223*/224void drawLineGrid(Graphics screenGraphics, Graphics g) {225// Fill buffer with background color226g.setColor(Color.white);227g.fillRect(0, 0, getWidth(), getHeight());228229// Now, iterate through all quadrants230for (int srcQuad = 0; srcQuad < NUM_QUADS; ++srcQuad) {231// Draw lines to all other quadrants232for (int dstQuad = 0; dstQuad < NUM_QUADS; ++dstQuad) {233for (int srcPoint = 0;234srcPoint < quadrants[srcQuad].length;235++srcPoint)236{237// For every point in the source quadrant238int sx = quadrants[srcQuad][srcPoint].x;239int sy = quadrants[srcQuad][srcPoint].y;240for (int dstPoint = 0;241dstPoint < quadrants[dstQuad].length;242++dstPoint)243{244int dx = quadrants[dstQuad][dstPoint].x;245int dy = quadrants[dstQuad][dstPoint].y;246if (!rectTest) {247// Draw unclipped/clipped lines to every248// point in the dst quadrant249g.setColor(unclippedColor);250g.drawLine(sx, sy, dx, dy);251g.setClip(clipX1, clipY1, clipSize, clipSize);252g.setColor(clippedColor);253g.drawLine(sx,sy, dx, dy);254} else {255// Draw unclipped/clipped rectangles to every256// point in the dst quadrant257g.setColor(unclippedColor);258int w = dx - sx;259int h = dy - sy;260g.drawRect(sx, sy, w, h);261g.setClip(clipX1, clipY1, clipSize, clipSize);262g.setColor(clippedColor);263g.drawRect(sx, sy, w, h);264}265g.setClip(null);266}267if (!dynamic) {268// Draw screen update for visual feedback269screenGraphics.drawImage(testImage, 0, 0, this);270// On default test, check for errors after every271// src point272if (!quickTest && gridError(g)) {273throw new java.lang.RuntimeException("Failed");274}275}276}277}278if (!dynamic && quickTest && gridError(g)) {279// On quick test, check for errors only after every280// src quadrant281throw new java.lang.RuntimeException("Failed");282//return;283}284}285if (!dynamic) {286System.out.println("PASSED");287if (!keepRunning) {288f.dispose();289}290}291}292293/**294* If we have not yet run the test, or if the window size has295* changed, or if we are running the test in -dynamic mode,296* run the test. Then draw the test buffer to the screen297*/298public void paint(Graphics g) {299if (dynamic || testImage == null ||300getWidth() != testW || getHeight() != testH)301{302runTest(g);303}304if (testImage != null) {305g.drawImage(testImage, 0, 0, this);306}307}308309/*310* Create the quadrant of points and run the test to draw all the lines311*/312public void runTest(Graphics screenGraphics) {313if (getWidth() == 0 || getHeight() == 0) {314// May get here before window is really ready315return;316}317clipX1 = (getWidth() - clipSize) / 2;318clipY1 = (getHeight() - clipSize) / 2;319int clipX2 = clipX1 + clipSize;320int clipY2 = clipY1 + clipSize;321int centerX = getWidth()/2;322int centerY = getHeight()/2;323int leftX = 0;324int topY = 0;325int rightX = getWidth() - 1;326int bottomY = getHeight() - 1;327int quadIndex = 0;328// Offsets are used to force diagonal (versus hor/vert) lines329int xOffset = 0;330int yOffset = 0;331332if (quadrants[0] == null) {333for (int i = 0; i < 9; ++i) {334int numPoints = (i == 4) ? 9 : 3;335quadrants[i] = new Point[numPoints];336}337}338// Upper-left339quadrants[quadIndex] = new Point[] {340new Point(leftX + xOffset, clipY1 - 1 - yOffset),341new Point(leftX + xOffset, topY + yOffset),342new Point(clipX1 - 1 - xOffset, topY + yOffset),343};344345quadIndex++;346yOffset++;347// Upper-middle348quadrants[quadIndex] = new Point[] {349new Point(clipX1 + 1 + xOffset, topY + yOffset),350new Point(centerX + xOffset, topY + yOffset),351new Point(clipX2 - 1 - xOffset, topY + yOffset),352};353354quadIndex++;355++yOffset;356// Upper-right357quadrants[quadIndex] = new Point[] {358new Point(clipX2 + 1 + xOffset, topY + yOffset),359new Point(rightX - xOffset, topY + yOffset),360new Point(rightX - xOffset, clipY1 - 1 - yOffset),361};362363quadIndex++;364yOffset = 0;365++xOffset;366// Middle-left367quadrants[quadIndex] = new Point[] {368new Point(leftX + xOffset, clipY1 + 1 + yOffset),369new Point(leftX + xOffset, centerY + yOffset),370new Point(leftX + xOffset, clipY2 - 1 - yOffset),371};372373quadIndex++;374++yOffset;375// Middle-middle376quadrants[quadIndex] = new Point[] {377new Point(clipX1 + 1 + xOffset, clipY1 + 1 + yOffset),378new Point(centerX + xOffset, clipY1 + 1 + yOffset),379new Point(clipX2 - 1 - xOffset, clipY1 + 1 + yOffset),380new Point(clipX1 + 1 + xOffset, centerY + yOffset),381new Point(centerX + xOffset, centerY + yOffset),382new Point(clipX2 - 1 - xOffset, centerY + yOffset),383new Point(clipX1 + 1 + xOffset, clipY2 - 1 - yOffset),384new Point(centerX + xOffset, clipY2 - 1 - yOffset),385new Point(clipX2 - 1 - xOffset, clipY2 - 1 - yOffset),386};387388quadIndex++;389++yOffset;390// Middle-right391quadrants[quadIndex] = new Point[] {392new Point(rightX - xOffset, clipY1 + 1 + yOffset),393new Point(rightX - xOffset, centerY + yOffset),394new Point(rightX - xOffset, clipY2 - 1 - yOffset),395};396397quadIndex++;398yOffset = 0;399++xOffset;400// Lower-left401quadrants[quadIndex] = new Point[] {402new Point(leftX + xOffset, clipY2 + 1 + yOffset),403new Point(leftX + xOffset, bottomY - yOffset),404new Point(clipX1 - 1 - xOffset, bottomY - yOffset),405};406407quadIndex++;408++yOffset;409// Lower-middle410quadrants[quadIndex] = new Point[] {411new Point(clipX1 + 1 + xOffset, bottomY - yOffset),412new Point(centerX + xOffset, bottomY - yOffset),413new Point(clipX2 - 1 - xOffset, bottomY - yOffset),414};415416quadIndex++;417++yOffset;418// Lower-right419quadrants[quadIndex] = new Point[] {420new Point(clipX2 + 1 + xOffset, bottomY - yOffset),421new Point(rightX - xOffset, bottomY - yOffset),422new Point(rightX - xOffset, clipY2 + 1 + yOffset),423};424425426if (testImage != null) {427testImage.flush();428}429testW = getWidth();430testH = getHeight();431testImage = createVolatileImage(testW, testH);432Graphics g = testImage.getGraphics();433do {434int valCode = testImage.validate(getGraphicsConfiguration());435if (valCode == VolatileImage.IMAGE_INCOMPATIBLE) {436testImage.flush();437testImage = createVolatileImage(testW, testH);438g = testImage.getGraphics();439}440drawLineGrid(screenGraphics, g);441} while (testImage.contentsLost());442if (dynamic) {443// Draw clip box if dynamic444g.setClip(null);445g.setColor(Color.black);446g.drawRect(clipX1, clipY1, clipSize, clipSize);447screenGraphics.drawImage(testImage, 0, 0, this);448}449runTestDone = true;450}451452/**453* When running -dynamic, resize the clip bounds and run the test454* over and over455*/456public void run() {457while (true) {458clipSize += clipBumpVal;459if (clipSize > getWidth() || clipSize < 0) {460clipBumpVal = -clipBumpVal;461clipSize += clipBumpVal;462}463update(getGraphics());464try {465Thread.sleep(50);466} catch (Exception e) {}467}468}469470public static void main(String args[]) {471for (int i = 0; i < args.length; ++i) {472if (args[i].equals("-dynamic")) {473dynamic = true;474} else if (args[i].equals("-rect")) {475rectTest = true;476} else if (args[i].equals("-quick")) {477quickTest = true;478} else if (args[i].equals("-keep")) {479keepRunning = true;480} else {481// could be clipSize482try {483clipSize = Integer.parseInt(args[i]);484} catch (Exception e) {}485}486}487f = new Frame();488f.setSize(500, 500);489LineClipTest test = new LineClipTest();490f.add(test);491if (dynamic) {492Thread t = new Thread(test);493t.start();494}495f.setVisible(true);496while (!runTestDone) {497// need to make sure jtreg doesn't exit before the498// test is done...499try {500Thread.sleep(50);501} catch (Exception e) {}502}503}504}505506507