提交 c8b503ac 编写于 作者: D dlila

7019861: Last scanline is skipped in pisces.Renderer.

Summary: not skipping it anymore.
Reviewed-by: flar
上级 9be78d53
...@@ -154,9 +154,6 @@ final class Helpers { ...@@ -154,9 +154,6 @@ final class Helpers {
// These use a hardcoded factor of 2 for increasing sizes. Perhaps this // These use a hardcoded factor of 2 for increasing sizes. Perhaps this
// should be provided as an argument. // should be provided as an argument.
static float[] widenArray(float[] in, final int cursize, final int numToAdd) { static float[] widenArray(float[] in, final int cursize, final int numToAdd) {
if (in == null) {
return new float[5 * numToAdd];
}
if (in.length >= cursize + numToAdd) { if (in.length >= cursize + numToAdd) {
return in; return in;
} }
......
...@@ -191,8 +191,7 @@ final class PiscesTileGenerator implements AATileGenerator { ...@@ -191,8 +191,7 @@ final class PiscesTileGenerator implements AATileGenerator {
System.out.println("len = "+runLen); System.out.println("len = "+runLen);
System.out.print(cache.toString()); System.out.print(cache.toString());
e0.printStackTrace(); e0.printStackTrace();
System.exit(1); throw e0;
return;
} }
int rx0 = cx; int rx0 = cx;
...@@ -215,8 +214,7 @@ final class PiscesTileGenerator implements AATileGenerator { ...@@ -215,8 +214,7 @@ final class PiscesTileGenerator implements AATileGenerator {
System.out.println("len = "+runLen); System.out.println("len = "+runLen);
System.out.print(cache.toString()); System.out.print(cache.toString());
e.printStackTrace(); e.printStackTrace();
System.exit(1); throw e;
return;
} }
} }
pos += 2; pos += 2;
...@@ -250,4 +248,5 @@ final class PiscesTileGenerator implements AATileGenerator { ...@@ -250,4 +248,5 @@ final class PiscesTileGenerator implements AATileGenerator {
* No further calls will be made on this instance. * No further calls will be made on this instance.
*/ */
public void dispose() {} public void dispose() {}
} }
\ No newline at end of file
...@@ -47,16 +47,16 @@ final class Renderer implements PathConsumer2D { ...@@ -47,16 +47,16 @@ final class Renderer implements PathConsumer2D {
private static final int INIT_CROSSINGS_SIZE = 10; private static final int INIT_CROSSINGS_SIZE = 10;
private ScanlineIterator() { // Preconditions: Only subpixel scanlines in the range
// (start <= subpixel_y <= end) will be evaluated. No
// edge may have a valid (i.e. inside the supplied clip)
// crossing that would be generated outside that range.
private ScanlineIterator(int start, int end) {
crossings = new int[INIT_CROSSINGS_SIZE]; crossings = new int[INIT_CROSSINGS_SIZE];
edgePtrs = new int[INIT_CROSSINGS_SIZE]; edgePtrs = new int[INIT_CROSSINGS_SIZE];
// We don't care if we clip some of the line off with ceil, since nextY = start;
// no scan line crossings will be eliminated (in fact, the ceil is maxY = end;
// the y of the first scan line crossing).
final int minY = getFirstScanLineCrossing();
nextY = minY;
maxY = getScanLineCrossingEnd()-1;
edgeCount = 0; edgeCount = 0;
} }
...@@ -148,6 +148,7 @@ final class Renderer implements PathConsumer2D { ...@@ -148,6 +148,7 @@ final class Renderer implements PathConsumer2D {
// don't just set NULL to -1, because we want NULL+NEXT to be negative. // don't just set NULL to -1, because we want NULL+NEXT to be negative.
private static final int NULL = -SIZEOF_EDGE; private static final int NULL = -SIZEOF_EDGE;
private float[] edges = null; private float[] edges = null;
private static final int INIT_NUM_EDGES = 8;
private int[] edgeBuckets = null; private int[] edgeBuckets = null;
private int[] edgeBucketCounts = null; // 2*newedges + (1 if pruning needed) private int[] edgeBucketCounts = null; // 2*newedges + (1 if pruning needed)
private int numEdges; private int numEdges;
...@@ -156,7 +157,7 @@ final class Renderer implements PathConsumer2D { ...@@ -156,7 +157,7 @@ final class Renderer implements PathConsumer2D {
private static final float INC_BND = 8f; private static final float INC_BND = 8f;
// each bucket is a linked list. this method adds eptr to the // each bucket is a linked list. this method adds eptr to the
// start "bucket"th linked list. // start of the "bucket"th linked list.
private void addEdgeToBucket(final int eptr, final int bucket) { private void addEdgeToBucket(final int eptr, final int bucket) {
edges[eptr+NEXT] = edgeBuckets[bucket]; edges[eptr+NEXT] = edgeBuckets[bucket];
edgeBuckets[bucket] = eptr; edgeBuckets[bucket] = eptr;
...@@ -168,7 +169,8 @@ final class Renderer implements PathConsumer2D { ...@@ -168,7 +169,8 @@ final class Renderer implements PathConsumer2D {
// X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings). // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
private void quadBreakIntoLinesAndAdd(float x0, float y0, private void quadBreakIntoLinesAndAdd(float x0, float y0,
final Curve c, final Curve c,
final float x2, final float y2) { final float x2, final float y2)
{
final float QUAD_DEC_BND = 32; final float QUAD_DEC_BND = 32;
final int countlg = 4; final int countlg = 4;
int count = 1 << countlg; int count = 1 << countlg;
...@@ -204,7 +206,8 @@ final class Renderer implements PathConsumer2D { ...@@ -204,7 +206,8 @@ final class Renderer implements PathConsumer2D {
// here, but then too many numbers are passed around. // here, but then too many numbers are passed around.
private void curveBreakIntoLinesAndAdd(float x0, float y0, private void curveBreakIntoLinesAndAdd(float x0, float y0,
final Curve c, final Curve c,
final float x3, final float y3) { final float x3, final float y3)
{
final int countlg = 3; final int countlg = 3;
int count = 1 << countlg; int count = 1 << countlg;
...@@ -259,8 +262,6 @@ final class Renderer implements PathConsumer2D { ...@@ -259,8 +262,6 @@ final class Renderer implements PathConsumer2D {
} }
} }
// Preconditions: y2 > y1 and the curve must cross some scanline
// i.e.: y1 <= y < y2 for some y such that boundsMinY <= y < boundsMaxY
private void addLine(float x1, float y1, float x2, float y2) { private void addLine(float x1, float y1, float x2, float y2) {
float or = 1; // orientation of the line. 1 if y increases, 0 otherwise. float or = 1; // orientation of the line. 1 if y increases, 0 otherwise.
if (y2 < y1) { if (y2 < y1) {
...@@ -272,12 +273,11 @@ final class Renderer implements PathConsumer2D { ...@@ -272,12 +273,11 @@ final class Renderer implements PathConsumer2D {
x1 = or; x1 = or;
or = 0; or = 0;
} }
final int firstCrossing = Math.max((int) Math.ceil(y1), boundsMinY); final int firstCrossing = Math.max((int)Math.ceil(y1), boundsMinY);
final int lastCrossing = Math.min((int)Math.ceil(y2), boundsMaxY); final int lastCrossing = Math.min((int)Math.ceil(y2), boundsMaxY);
if (firstCrossing >= lastCrossing) { if (firstCrossing >= lastCrossing) {
return; return;
} }
if (y1 < edgeMinY) { edgeMinY = y1; } if (y1 < edgeMinY) { edgeMinY = y1; }
if (y2 > edgeMaxY) { edgeMaxY = y2; } if (y2 > edgeMaxY) { edgeMaxY = y2; }
...@@ -297,22 +297,10 @@ final class Renderer implements PathConsumer2D { ...@@ -297,22 +297,10 @@ final class Renderer implements PathConsumer2D {
edges[ptr+OR] = or; edges[ptr+OR] = or;
edges[ptr+CURX] = x1 + (firstCrossing - y1) * slope; edges[ptr+CURX] = x1 + (firstCrossing - y1) * slope;
edges[ptr+SLOPE] = slope; edges[ptr+SLOPE] = slope;
edges[ptr+YMAX] = y2; edges[ptr+YMAX] = lastCrossing;
final int bucketIdx = firstCrossing - boundsMinY; final int bucketIdx = firstCrossing - boundsMinY;
addEdgeToBucket(ptr, bucketIdx); addEdgeToBucket(ptr, bucketIdx);
if (lastCrossing < boundsMaxY) { edgeBucketCounts[lastCrossing - boundsMinY] |= 1;
edgeBucketCounts[lastCrossing - boundsMinY] |= 1;
}
}
// preconditions: should not be called before the last line has been added
// to the edge list (even though it will return a correct answer at that
// point in time, it's not meant to be used that way).
private int getFirstScanLineCrossing() {
return Math.max(boundsMinY, (int)Math.ceil(edgeMinY));
}
private int getScanLineCrossingEnd() {
return Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY));
} }
// END EDGE LIST // END EDGE LIST
...@@ -366,9 +354,11 @@ final class Renderer implements PathConsumer2D { ...@@ -366,9 +354,11 @@ final class Renderer implements PathConsumer2D {
this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X; this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X;
this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y; this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y;
edges = new float[INIT_NUM_EDGES * SIZEOF_EDGE];
numEdges = 0;
edgeBuckets = new int[boundsMaxY - boundsMinY]; edgeBuckets = new int[boundsMaxY - boundsMinY];
java.util.Arrays.fill(edgeBuckets, NULL); java.util.Arrays.fill(edgeBuckets, NULL);
edgeBucketCounts = new int[edgeBuckets.length]; edgeBucketCounts = new int[edgeBuckets.length + 1];
} }
private float tosubpixx(float pix_x) { private float tosubpixx(float pix_x) {
...@@ -394,7 +384,7 @@ final class Renderer implements PathConsumer2D { ...@@ -394,7 +384,7 @@ final class Renderer implements PathConsumer2D {
y0 = y1; y0 = y1;
} }
Curve c = new Curve(); private Curve c = new Curve();
@Override public void curveTo(float x1, float y1, @Override public void curveTo(float x1, float y1,
float x2, float y2, float x2, float y2,
float x3, float y3) float x3, float y3)
...@@ -431,8 +421,8 @@ final class Renderer implements PathConsumer2D { ...@@ -431,8 +421,8 @@ final class Renderer implements PathConsumer2D {
throw new InternalError("Renderer does not use a native consumer."); throw new InternalError("Renderer does not use a native consumer.");
} }
private void _endRendering(final int pix_bboxx0, final int pix_bboxy0, private void _endRendering(final int pix_bboxx0, final int pix_bboxx1,
final int pix_bboxx1, final int pix_bboxy1) int ymin, int ymax)
{ {
// Mask to determine the relevant bit of the crossing sum // Mask to determine the relevant bit of the crossing sum
// 0x1 if EVEN_ODD, all bits if NON_ZERO // 0x1 if EVEN_ODD, all bits if NON_ZERO
...@@ -455,7 +445,7 @@ final class Renderer implements PathConsumer2D { ...@@ -455,7 +445,7 @@ final class Renderer implements PathConsumer2D {
int pix_minX = Integer.MAX_VALUE; int pix_minX = Integer.MAX_VALUE;
int y = boundsMinY; // needs to be declared here so we emit the last row properly. int y = boundsMinY; // needs to be declared here so we emit the last row properly.
ScanlineIterator it = this.new ScanlineIterator(); ScanlineIterator it = this.new ScanlineIterator(ymin, ymax);
for ( ; it.hasNext(); ) { for ( ; it.hasNext(); ) {
int numCrossings = it.next(); int numCrossings = it.next();
int[] crossings = it.crossings; int[] crossings = it.crossings;
...@@ -477,7 +467,7 @@ final class Renderer implements PathConsumer2D { ...@@ -477,7 +467,7 @@ final class Renderer implements PathConsumer2D {
int curxo = crossings[i]; int curxo = crossings[i];
int curx = curxo >> 1; int curx = curxo >> 1;
// to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1. // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1.
int crorientation = ((curxo & 0x1) << 1) -1; int crorientation = ((curxo & 0x1) << 1) - 1;
if ((sum & mask) != 0) { if ((sum & mask) != 0) {
int x0 = Math.max(prev, bboxx0); int x0 = Math.max(prev, bboxx0);
int x1 = Math.min(curx, bboxx1); int x1 = Math.min(curx, bboxx1);
...@@ -541,7 +531,7 @@ final class Renderer implements PathConsumer2D { ...@@ -541,7 +531,7 @@ final class Renderer implements PathConsumer2D {
} }
this.cache = new PiscesCache(pminX, pminY, pmaxX, pmaxY); this.cache = new PiscesCache(pminX, pminY, pmaxX, pmaxY);
_endRendering(pminX, pminY, pmaxX, pmaxY); _endRendering(pminX, pmaxX, spminY, spmaxY);
} }
public PiscesCache getCache() { public PiscesCache getCache() {
......
...@@ -764,6 +764,11 @@ final class Stroker implements PathConsumer2D { ...@@ -764,6 +764,11 @@ final class Stroker implements PathConsumer2D {
private static final int MAX_N_CURVES = 11; private static final int MAX_N_CURVES = 11;
private float[] subdivTs = new float[MAX_N_CURVES - 1]; private float[] subdivTs = new float[MAX_N_CURVES - 1];
// If this class is compiled with ecj, then Hotspot crashes when OSR
// compiling this function. See bugs 7004570 and 6675699
// TODO: until those are fixed, we should work around that by
// manually inlining this into curveTo and quadTo.
/******************************* WORKAROUND **********************************
private void somethingTo(final int type) { private void somethingTo(final int type) {
// need these so we can update the state at the end of this method // need these so we can update the state at the end of this method
final float xf = middle[type-2], yf = middle[type-1]; final float xf = middle[type-2], yf = middle[type-1];
...@@ -866,6 +871,7 @@ final class Stroker implements PathConsumer2D { ...@@ -866,6 +871,7 @@ final class Stroker implements PathConsumer2D {
this.cy0 = yf; this.cy0 = yf;
this.prev = DRAWING_OP_TO; this.prev = DRAWING_OP_TO;
} }
****************************** END WORKAROUND *******************************/
// finds values of t where the curve in pts should be subdivided in order // finds values of t where the curve in pts should be subdivided in order
// to get good offset curves a distance of w away from the middle curve. // to get good offset curves a distance of w away from the middle curve.
...@@ -932,18 +938,168 @@ final class Stroker implements PathConsumer2D { ...@@ -932,18 +938,168 @@ final class Stroker implements PathConsumer2D {
middle[2] = x1; middle[3] = y1; middle[2] = x1; middle[3] = y1;
middle[4] = x2; middle[5] = y2; middle[4] = x2; middle[5] = y2;
middle[6] = x3; middle[7] = y3; middle[6] = x3; middle[7] = y3;
somethingTo(8);
}
@Override public long getNativeConsumer() { // inlined version of somethingTo(8);
throw new InternalError("Stroker doesn't use a native consumer"); // See the TODO on somethingTo
// need these so we can update the state at the end of this method
final float xf = middle[6], yf = middle[7];
float dxs = middle[2] - middle[0];
float dys = middle[3] - middle[1];
float dxf = middle[6] - middle[4];
float dyf = middle[7] - middle[5];
boolean p1eqp2 = (dxs == 0f && dys == 0f);
boolean p3eqp4 = (dxf == 0f && dyf == 0f);
if (p1eqp2) {
dxs = middle[4] - middle[0];
dys = middle[5] - middle[1];
if (dxs == 0f && dys == 0f) {
dxs = middle[6] - middle[0];
dys = middle[7] - middle[1];
}
}
if (p3eqp4) {
dxf = middle[6] - middle[2];
dyf = middle[7] - middle[3];
if (dxf == 0f && dyf == 0f) {
dxf = middle[6] - middle[0];
dyf = middle[7] - middle[1];
}
}
if (dxs == 0f && dys == 0f) {
// this happens iff the "curve" is just a point
lineTo(middle[0], middle[1]);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
float len = (float)Math.sqrt(dxs*dxs + dys*dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
float len = (float)Math.sqrt(dxf*dxf + dyf*dyf);
dxf /= len;
dyf /= len;
}
computeOffset(dxs, dys, lineWidth2, offset[0]);
final float mx = offset[0][0];
final float my = offset[0][1];
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
int nSplits = findSubdivPoints(middle, subdivTs, 8, lineWidth2);
int kind = 0;
Iterator<Integer> it = Curve.breakPtsAtTs(middle, 8, subdivTs, nSplits);
while(it.hasNext()) {
int curCurveOff = it.next();
kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
if (kind != 0) {
emitLineTo(lp[0], lp[1]);
switch(kind) {
case 8:
emitCurveTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], lp[6], lp[7], false);
emitCurveTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7], true);
break;
case 4:
emitLineTo(lp[2], lp[3]);
emitLineTo(rp[0], rp[1], true);
break;
}
emitLineTo(rp[kind - 2], rp[kind - 1], true);
}
}
this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
this.cdx = dxf;
this.cdy = dyf;
this.cx0 = xf;
this.cy0 = yf;
this.prev = DRAWING_OP_TO;
} }
@Override public void quadTo(float x1, float y1, float x2, float y2) { @Override public void quadTo(float x1, float y1, float x2, float y2) {
middle[0] = cx0; middle[1] = cy0; middle[0] = cx0; middle[1] = cy0;
middle[2] = x1; middle[3] = y1; middle[2] = x1; middle[3] = y1;
middle[4] = x2; middle[5] = y2; middle[4] = x2; middle[5] = y2;
somethingTo(6);
// inlined version of somethingTo(8);
// See the TODO on somethingTo
// need these so we can update the state at the end of this method
final float xf = middle[4], yf = middle[5];
float dxs = middle[2] - middle[0];
float dys = middle[3] - middle[1];
float dxf = middle[4] - middle[2];
float dyf = middle[5] - middle[3];
if ((dxs == 0f && dys == 0f) || (dxf == 0f && dyf == 0f)) {
dxs = dxf = middle[4] - middle[0];
dys = dyf = middle[5] - middle[1];
}
if (dxs == 0f && dys == 0f) {
// this happens iff the "curve" is just a point
lineTo(middle[0], middle[1]);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
float len = (float)Math.sqrt(dxs*dxs + dys*dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
float len = (float)Math.sqrt(dxf*dxf + dyf*dyf);
dxf /= len;
dyf /= len;
}
computeOffset(dxs, dys, lineWidth2, offset[0]);
final float mx = offset[0][0];
final float my = offset[0][1];
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
int nSplits = findSubdivPoints(middle, subdivTs, 6, lineWidth2);
int kind = 0;
Iterator<Integer> it = Curve.breakPtsAtTs(middle, 6, subdivTs, nSplits);
while(it.hasNext()) {
int curCurveOff = it.next();
kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
if (kind != 0) {
emitLineTo(lp[0], lp[1]);
switch(kind) {
case 6:
emitQuadTo(lp[0], lp[1], lp[2], lp[3], lp[4], lp[5], false);
emitQuadTo(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], true);
break;
case 4:
emitLineTo(lp[2], lp[3]);
emitLineTo(rp[0], rp[1], true);
break;
}
emitLineTo(rp[kind - 2], rp[kind - 1], true);
}
}
this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
this.cdx = dxf;
this.cdy = dyf;
this.cx0 = xf;
this.cy0 = yf;
this.prev = DRAWING_OP_TO;
}
@Override public long getNativeConsumer() {
throw new InternalError("Stroker doesn't use a native consumer");
} }
// a stack of polynomial curves where each curve shares endpoints with // a stack of polynomial curves where each curve shares endpoints with
......
/*
* Copyright (c) 2011, 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
* @bug 7019861
*
* @summary Verifies that the last scanline isn't skipped when doing
* antialiased rendering.
*
* @run main Test7019861
*/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.Path2D;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import static java.awt.RenderingHints.*;
public class Test7019861 {
public static void main(String[] argv) throws Exception {
BufferedImage im = getWhiteImage(30, 30);
Graphics2D g2 = (Graphics2D)im.getGraphics();
g2.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
g2.setRenderingHint(KEY_STROKE_CONTROL, VALUE_STROKE_PURE);
g2.setStroke(new BasicStroke(10, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
g2.setBackground(Color.white);
g2.setColor(Color.black);
Path2D p = getPath(0, 0, 20);
g2.draw(p);
if (!(new Color(im.getRGB(20, 19))).equals(Color.black)) {
throw new Exception("This pixel should be black");
}
}
private static Path2D getPath(int x, int y, int len) {
Path2D p = new Path2D.Double();
p.moveTo(x, y);
p.quadTo(x + len, y, x + len, y + len);
return p;
}
private static BufferedImage getWhiteImage(int w, int h) {
BufferedImage ret = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
final int[] white = new int[w * h];
Arrays.fill(white, 0xffffff);
ret.setRGB(0, 0, w, h, white, 0, w);
return ret;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册