--- old/make/mapfiles/libawt/mapfile-mawt-vers 2017-11-06 15:02:33.084239433 -0800 +++ new/make/mapfiles/libawt/mapfile-mawt-vers 2017-11-06 15:02:32.840239439 -0800 @@ -177,7 +177,6 @@ Java_sun_java2d_xr_XRBackendNative_setGCMode; Java_sun_java2d_xr_XRBackendNative_GCRectanglesNative; Java_sun_java2d_xr_XRUtils_initFormatPtrs; - Java_sun_java2d_xr_XRBackendNative_renderCompositeTrapezoidsNative; XRT_DrawGlyphList; Java_sun_java2d_opengl_OGLContext_getOGLIdString; --- old/make/mapfiles/libawt_xawt/mapfile-vers 2017-11-06 15:02:33.508239421 -0800 +++ new/make/mapfiles/libawt_xawt/mapfile-vers 2017-11-06 15:02:33.304239427 -0800 @@ -408,7 +408,6 @@ Java_sun_java2d_xr_XRBackendNative_XRenderCompositeTextNative; Java_sun_java2d_xr_XRBackendNative_setGCMode; Java_sun_java2d_xr_XRBackendNative_GCRectanglesNative; - Java_sun_java2d_xr_XRBackendNative_renderCompositeTrapezoidsNative; Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1arrow; Java_com_sun_java_swing_plaf_gtk_GTKEngine_native_1paint_1box; --- old/src/java.desktop/unix/classes/sun/java2d/xr/XRBackend.java 2017-11-06 15:02:33.964239409 -0800 +++ new/src/java.desktop/unix/classes/sun/java2d/xr/XRBackend.java 2017-11-06 15:02:33.784239413 -0800 @@ -36,7 +36,6 @@ import java.util.*; import sun.font.*; -import sun.java2d.jules.*; import sun.java2d.pipe.*; public interface XRBackend { @@ -110,7 +109,4 @@ public void setGCMode(long gc, boolean copy); - public void renderCompositeTrapezoids(byte op, int src, int maskFormat, - int dst, int srcX, int srcY, - TrapezoidList trapList); } --- old/src/java.desktop/unix/classes/sun/java2d/xr/XRBackendNative.java 2017-11-06 15:02:34.384239397 -0800 +++ new/src/java.desktop/unix/classes/sun/java2d/xr/XRBackendNative.java 2017-11-06 15:02:34.168239403 -0800 @@ -29,7 +29,6 @@ import java.util.*; import sun.font.*; -import sun.java2d.jules.*; import sun.java2d.pipe.*; import static sun.java2d.xr.XRUtils.XDoubleToFixed; @@ -315,16 +314,4 @@ int sx, int sy, int dx, int dy, int w, int h); - public void renderCompositeTrapezoids(byte op, int src, int maskFormat, - int dst, int srcX, int srcY, - TrapezoidList trapList) { - renderCompositeTrapezoidsNative(op, src, getFormatPtr(maskFormat), - dst, srcX, srcY, - trapList.getTrapArray()); - } - - private static native void - renderCompositeTrapezoidsNative(byte op, int src, long maskFormat, - int dst, int srcX, int srcY, - int[] trapezoids); } --- old/src/java.desktop/unix/classes/sun/java2d/xr/XRCompositeManager.java 2017-11-06 15:02:34.844239384 -0800 +++ new/src/java.desktop/unix/classes/sun/java2d/xr/XRCompositeManager.java 2017-11-06 15:02:34.636239390 -0800 @@ -33,7 +33,6 @@ import sun.font.*; import sun.java2d.*; -import sun.java2d.jules.*; import sun.java2d.loops.*; /** @@ -253,29 +252,6 @@ maskX, maskY, dstX, dstY, width, height); } - public void XRCompositeTraps(int dst, int srcX, int srcY, - TrapezoidList trapList) { - int renderReferenceX = 0; - int renderReferenceY = 0; - - if (trapList.getP1YLeft(0) < trapList.getP2YLeft(0)) { - renderReferenceX = trapList.getP1XLeft(0); - renderReferenceY = trapList.getP1YLeft(0); - } else { - renderReferenceX = trapList.getP2XLeft(0); - renderReferenceY = trapList.getP2YLeft(0); - } - - renderReferenceX = (int) Math.floor(XRUtils - .XFixedToDouble(renderReferenceX)); - renderReferenceY = (int) Math.floor(XRUtils - .XFixedToDouble(renderReferenceY)); - - con.renderCompositeTrapezoids(compRule, getCurrentSource().picture, - XRUtils.PictStandardA8, dst, renderReferenceX, - renderReferenceY, trapList); - } - public void XRRenderRectangles(XRSurfaceData dst, GrowableRectArray rects) { if (xorEnabled) { con.GCRectangles(dst.getXid(), dst.getGC(), rects); --- old/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java 2017-11-06 15:02:35.364239370 -0800 +++ new/src/java.desktop/unix/classes/sun/java2d/xr/XRSurfaceData.java 2017-11-06 15:02:35.152239376 -0800 @@ -33,7 +33,6 @@ import sun.java2d.SunGraphics2D; import sun.java2d.SurfaceData; import sun.java2d.SurfaceDataProxy; -import sun.java2d.jules.*; import sun.java2d.loops.*; import sun.java2d.pipe.*; import sun.java2d.x11.*; @@ -146,29 +145,21 @@ } } - if (sg2d.antialiasHint == SunHints.INTVAL_ANTIALIAS_ON && - JulesPathBuf.isCairoAvailable()) - { - sg2d.shapepipe = aaShapePipe; - sg2d.drawpipe = aaPixelToShapeConv; - sg2d.fillpipe = aaPixelToShapeConv; - } else { - if (txPipe != null) { - if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { - sg2d.drawpipe = txPipe; - sg2d.fillpipe = txPipe; - } else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) { - sg2d.drawpipe = txPipe; - sg2d.fillpipe = nonTxPipe; - } else { - sg2d.drawpipe = nonTxPipe; - sg2d.fillpipe = nonTxPipe; - } - sg2d.shapepipe = nonTxPipe; + if (txPipe != null) { + if (sg2d.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { + sg2d.drawpipe = txPipe; + sg2d.fillpipe = txPipe; + } else if (sg2d.strokeState != SunGraphics2D.STROKE_THIN) { + sg2d.drawpipe = txPipe; + sg2d.fillpipe = nonTxPipe; } else { - if (!validated) { - super.validatePipe(sg2d); - } + sg2d.drawpipe = nonTxPipe; + sg2d.fillpipe = nonTxPipe; + } + sg2d.shapepipe = nonTxPipe; + } else { + if (!validated) { + super.validatePipe(sg2d); } } @@ -528,11 +519,6 @@ xrtextpipe = maskBuffer.getTextRenderer(); xrDrawImage = new XRDrawImage(); - if (JulesPathBuf.isCairoAvailable()) { - aaShapePipe = - new JulesShapePipe(XRCompositeManager.getInstance(this)); - aaPixelToShapeConv = new PixelToShapeConverter(aaShapePipe); - } } finally { SunToolkit.awtUnlock(); } --- old/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c 2017-11-06 15:02:35.912239355 -0800 +++ new/src/java.desktop/unix/native/libawt_xawt/java2d/x11/XRBackendNative.c 2017-11-06 15:02:35.664239362 -0800 @@ -1110,20 +1110,3 @@ free(xRects); } } - -JNIEXPORT void JNICALL -Java_sun_java2d_xr_XRBackendNative_renderCompositeTrapezoidsNative - (JNIEnv *env, jclass cls, jbyte op, jint src, jlong maskFmt, - jint dst, jint srcX, jint srcY, jintArray trapArray) { - jint *traps; - - if ((traps = (jint *) (*env)->GetPrimitiveArrayCritical(env, trapArray, NULL)) == NULL) { - return; - } - - XRenderCompositeTrapezoids(awt_display, op, (Picture) src, (Picture) dst, - (XRenderPictFormat *) jlong_to_ptr(maskFmt), - srcX, srcY, (XTrapezoid *) (traps+5), traps[0]); - - (*env)->ReleasePrimitiveArrayCritical(env, trapArray, traps, JNI_ABORT); -} --- old/src/java.desktop/share/classes/sun/java2d/pisces/Curve.java 2017-11-06 15:02:36.352239343 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2007, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import java.util.Iterator; - -final class Curve { - - float ax, ay, bx, by, cx, cy, dx, dy; - float dax, day, dbx, dby; - - Curve() { - } - - void set(float[] points, int type) { - switch(type) { - case 8: - set(points[0], points[1], - points[2], points[3], - points[4], points[5], - points[6], points[7]); - break; - case 6: - set(points[0], points[1], - points[2], points[3], - points[4], points[5]); - break; - default: - throw new InternalError("Curves can only be cubic or quadratic"); - } - } - - void set(float x1, float y1, - float x2, float y2, - float x3, float y3, - float x4, float y4) - { - ax = 3 * (x2 - x3) + x4 - x1; - ay = 3 * (y2 - y3) + y4 - y1; - bx = 3 * (x1 - 2 * x2 + x3); - by = 3 * (y1 - 2 * y2 + y3); - cx = 3 * (x2 - x1); - cy = 3 * (y2 - y1); - dx = x1; - dy = y1; - dax = 3 * ax; day = 3 * ay; - dbx = 2 * bx; dby = 2 * by; - } - - void set(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - ax = ay = 0f; - - bx = x1 - 2 * x2 + x3; - by = y1 - 2 * y2 + y3; - cx = 2 * (x2 - x1); - cy = 2 * (y2 - y1); - dx = x1; - dy = y1; - dax = 0; day = 0; - dbx = 2 * bx; dby = 2 * by; - } - - float xat(float t) { - return t * (t * (t * ax + bx) + cx) + dx; - } - float yat(float t) { - return t * (t * (t * ay + by) + cy) + dy; - } - - float dxat(float t) { - return t * (t * dax + dbx) + cx; - } - - float dyat(float t) { - return t * (t * day + dby) + cy; - } - - int dxRoots(float[] roots, int off) { - return Helpers.quadraticRoots(dax, dbx, cx, roots, off); - } - - int dyRoots(float[] roots, int off) { - return Helpers.quadraticRoots(day, dby, cy, roots, off); - } - - int infPoints(float[] pts, int off) { - // inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0 - // Fortunately, this turns out to be quadratic, so there are at - // most 2 inflection points. - final float a = dax * dby - dbx * day; - final float b = 2 * (cy * dax - day * cx); - final float c = cy * dbx - cx * dby; - - return Helpers.quadraticRoots(a, b, c, pts, off); - } - - // finds points where the first and second derivative are - // perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where - // * is a dot product). Unfortunately, we have to solve a cubic. - private int perpendiculardfddf(float[] pts, int off) { - assert pts.length >= off + 4; - - // these are the coefficients of some multiple of g(t) (not g(t), - // because the roots of a polynomial are not changed after multiplication - // by a constant, and this way we save a few multiplications). - final float a = 2*(dax*dax + day*day); - final float b = 3*(dax*dbx + day*dby); - final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby; - final float d = dbx*cx + dby*cy; - return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f); - } - - // Tries to find the roots of the function ROC(t)-w in [0, 1). It uses - // a variant of the false position algorithm to find the roots. False - // position requires that 2 initial values x0,x1 be given, and that the - // function must have opposite signs at those values. To find such - // values, we need the local extrema of the ROC function, for which we - // need the roots of its derivative; however, it's harder to find the - // roots of the derivative in this case than it is to find the roots - // of the original function. So, we find all points where this curve's - // first and second derivative are perpendicular, and we pretend these - // are our local extrema. There are at most 3 of these, so we will check - // at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection - // points, so roc-w can have at least 6 roots. This shouldn't be a - // problem for what we're trying to do (draw a nice looking curve). - int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) { - // no OOB exception, because by now off<=6, and roots.length >= 10 - assert off <= 6 && roots.length >= 10; - int ret = off; - int numPerpdfddf = perpendiculardfddf(roots, off); - float t0 = 0, ft0 = ROCsq(t0) - w*w; - roots[off + numPerpdfddf] = 1f; // always check interval end points - numPerpdfddf++; - for (int i = off; i < off + numPerpdfddf; i++) { - float t1 = roots[i], ft1 = ROCsq(t1) - w*w; - if (ft0 == 0f) { - roots[ret++] = t0; - } else if (ft1 * ft0 < 0f) { // have opposite signs - // (ROC(t)^2 == w^2) == (ROC(t) == w) is true because - // ROC(t) >= 0 for all t. - roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err); - } - t0 = t1; - ft0 = ft1; - } - - return ret - off; - } - - private static float eliminateInf(float x) { - return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE : - (x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x)); - } - - // A slight modification of the false position algorithm on wikipedia. - // This only works for the ROCsq-x functions. It might be nice to have - // the function as an argument, but that would be awkward in java6. - // TODO: It is something to consider for java8 (or whenever lambda - // expressions make it into the language), depending on how closures - // and turn out. Same goes for the newton's method - // algorithm in Helpers.java - private float falsePositionROCsqMinusX(float x0, float x1, - final float x, final float err) - { - final int iterLimit = 100; - int side = 0; - float t = x1, ft = eliminateInf(ROCsq(t) - x); - float s = x0, fs = eliminateInf(ROCsq(s) - x); - float r = s, fr; - for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) { - r = (fs * t - ft * s) / (fs - ft); - fr = ROCsq(r) - x; - if (sameSign(fr, ft)) { - ft = fr; t = r; - if (side < 0) { - fs /= (1 << (-side)); - side--; - } else { - side = -1; - } - } else if (fr * fs > 0) { - fs = fr; s = r; - if (side > 0) { - ft /= (1 << side); - side++; - } else { - side = 1; - } - } else { - break; - } - } - return r; - } - - private static boolean sameSign(double x, double y) { - // another way is to test if x*y > 0. This is bad for small x, y. - return (x < 0 && y < 0) || (x > 0 && y > 0); - } - - // returns the radius of curvature squared at t of this curve - // see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications) - private float ROCsq(final float t) { - // dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency - final float dx = t * (t * dax + dbx) + cx; - final float dy = t * (t * day + dby) + cy; - final float ddx = 2 * dax * t + dbx; - final float ddy = 2 * day * t + dby; - final float dx2dy2 = dx*dx + dy*dy; - final float ddx2ddy2 = ddx*ddx + ddy*ddy; - final float ddxdxddydy = ddx*dx + ddy*dy; - return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy)); - } - - // curve to be broken should be in pts - // this will change the contents of pts but not Ts - // TODO: There's no reason for Ts to be an array. All we need is a sequence - // of t values at which to subdivide. An array statisfies this condition, - // but is unnecessarily restrictive. Ts should be an Iterator instead. - // Doing this will also make dashing easier, since we could easily make - // LengthIterator an Iterator and feed it to this function to simplify - // the loop in Dasher.somethingTo. - static Iterator breakPtsAtTs(final float[] pts, final int type, - final float[] Ts, final int numTs) - { - assert pts.length >= 2*type && numTs <= Ts.length; - return new Iterator() { - // these prevent object creation and destruction during autoboxing. - // Because of this, the compiler should be able to completely - // eliminate the boxing costs. - final Integer i0 = 0; - final Integer itype = type; - int nextCurveIdx = 0; - Integer curCurveOff = i0; - float prevT = 0; - - @Override public boolean hasNext() { - return nextCurveIdx < numTs + 1; - } - - @Override public Integer next() { - Integer ret; - if (nextCurveIdx < numTs) { - float curT = Ts[nextCurveIdx]; - float splitT = (curT - prevT) / (1 - prevT); - Helpers.subdivideAt(splitT, - pts, curCurveOff, - pts, 0, - pts, type, type); - prevT = curT; - ret = i0; - curCurveOff = itype; - } else { - ret = curCurveOff; - } - nextCurveIdx++; - return ret; - } - - @Override public void remove() {} - }; - } -} - --- old/src/java.desktop/share/classes/sun/java2d/pisces/Dasher.java 2017-11-06 15:02:36.664239335 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,575 +0,0 @@ -/* - * Copyright (c) 2007, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import sun.awt.geom.PathConsumer2D; - -/** - * The {@code Dasher} class takes a series of linear commands - * ({@code moveTo}, {@code lineTo}, {@code close} and - * {@code end}) and breaks them into smaller segments according to a - * dash pattern array and a starting dash phase. - * - *

Issues: in J2Se, a zero length dash segment as drawn as a very - * short dash, whereas Pisces does not draw anything. The PostScript - * semantics are unclear. - * - */ -final class Dasher implements sun.awt.geom.PathConsumer2D { - - private final PathConsumer2D out; - private final float[] dash; - private final float startPhase; - private final boolean startDashOn; - private final int startIdx; - - private boolean starting; - private boolean needsMoveTo; - - private int idx; - private boolean dashOn; - private float phase; - - private float sx, sy; - private float x0, y0; - - // temporary storage for the current curve - private float[] curCurvepts; - - /** - * Constructs a {@code Dasher}. - * - * @param out an output {@code PathConsumer2D}. - * @param dash an array of {@code float}s containing the dash pattern - * @param phase a {@code float} containing the dash phase - */ - public Dasher(PathConsumer2D out, float[] dash, float phase) { - if (phase < 0) { - throw new IllegalArgumentException("phase < 0 !"); - } - - this.out = out; - - // Normalize so 0 <= phase < dash[0] - int idx = 0; - dashOn = true; - float d; - while (phase >= (d = dash[idx])) { - phase -= d; - idx = (idx + 1) % dash.length; - dashOn = !dashOn; - } - - this.dash = dash; - this.startPhase = this.phase = phase; - this.startDashOn = dashOn; - this.startIdx = idx; - this.starting = true; - - // we need curCurvepts to be able to contain 2 curves because when - // dashing curves, we need to subdivide it - curCurvepts = new float[8 * 2]; - } - - public void moveTo(float x0, float y0) { - if (firstSegidx > 0) { - out.moveTo(sx, sy); - emitFirstSegments(); - } - needsMoveTo = true; - this.idx = startIdx; - this.dashOn = this.startDashOn; - this.phase = this.startPhase; - this.sx = this.x0 = x0; - this.sy = this.y0 = y0; - this.starting = true; - } - - private void emitSeg(float[] buf, int off, int type) { - switch (type) { - case 8: - out.curveTo(buf[off+0], buf[off+1], - buf[off+2], buf[off+3], - buf[off+4], buf[off+5]); - break; - case 6: - out.quadTo(buf[off+0], buf[off+1], - buf[off+2], buf[off+3]); - break; - case 4: - out.lineTo(buf[off], buf[off+1]); - } - } - - private void emitFirstSegments() { - for (int i = 0; i < firstSegidx; ) { - emitSeg(firstSegmentsBuffer, i+1, (int)firstSegmentsBuffer[i]); - i += (((int)firstSegmentsBuffer[i]) - 1); - } - firstSegidx = 0; - } - - // We don't emit the first dash right away. If we did, caps would be - // drawn on it, but we need joins to be drawn if there's a closePath() - // So, we store the path elements that make up the first dash in the - // buffer below. - private float[] firstSegmentsBuffer = new float[7]; - private int firstSegidx = 0; - // precondition: pts must be in relative coordinates (relative to x0,y0) - // fullCurve is true iff the curve in pts has not been split. - private void goTo(float[] pts, int off, final int type) { - float x = pts[off + type - 4]; - float y = pts[off + type - 3]; - if (dashOn) { - if (starting) { - firstSegmentsBuffer = Helpers.widenArray(firstSegmentsBuffer, - firstSegidx, type - 2 + 1); - firstSegmentsBuffer[firstSegidx++] = type; - System.arraycopy(pts, off, firstSegmentsBuffer, firstSegidx, type - 2); - firstSegidx += type - 2; - } else { - if (needsMoveTo) { - out.moveTo(x0, y0); - needsMoveTo = false; - } - emitSeg(pts, off, type); - } - } else { - starting = false; - needsMoveTo = true; - } - this.x0 = x; - this.y0 = y; - } - - public void lineTo(float x1, float y1) { - float dx = x1 - x0; - float dy = y1 - y0; - - float len = (float) Math.sqrt(dx*dx + dy*dy); - - if (len == 0) { - return; - } - - // The scaling factors needed to get the dx and dy of the - // transformed dash segments. - float cx = dx / len; - float cy = dy / len; - - while (true) { - float leftInThisDashSegment = dash[idx] - phase; - if (len <= leftInThisDashSegment) { - curCurvepts[0] = x1; - curCurvepts[1] = y1; - goTo(curCurvepts, 0, 4); - // Advance phase within current dash segment - phase += len; - if (len == leftInThisDashSegment) { - phase = 0f; - idx = (idx + 1) % dash.length; - dashOn = !dashOn; - } - return; - } - - float dashdx = dash[idx] * cx; - float dashdy = dash[idx] * cy; - if (phase == 0) { - curCurvepts[0] = x0 + dashdx; - curCurvepts[1] = y0 + dashdy; - } else { - float p = leftInThisDashSegment / dash[idx]; - curCurvepts[0] = x0 + p * dashdx; - curCurvepts[1] = y0 + p * dashdy; - } - - goTo(curCurvepts, 0, 4); - - len -= leftInThisDashSegment; - // Advance to next dash segment - idx = (idx + 1) % dash.length; - dashOn = !dashOn; - phase = 0; - } - } - - private LengthIterator li = null; - - // preconditions: curCurvepts must be an array of length at least 2 * type, - // that contains the curve we want to dash in the first type elements - private void somethingTo(int type) { - if (pointCurve(curCurvepts, type)) { - return; - } - if (li == null) { - li = new LengthIterator(4, 0.01f); - } - li.initializeIterationOnCurve(curCurvepts, type); - - int curCurveoff = 0; // initially the current curve is at curCurvepts[0...type] - float lastSplitT = 0; - float t = 0; - float leftInThisDashSegment = dash[idx] - phase; - while ((t = li.next(leftInThisDashSegment)) < 1) { - if (t != 0) { - Helpers.subdivideAt((t - lastSplitT) / (1 - lastSplitT), - curCurvepts, curCurveoff, - curCurvepts, 0, - curCurvepts, type, type); - lastSplitT = t; - goTo(curCurvepts, 2, type); - curCurveoff = type; - } - // Advance to next dash segment - idx = (idx + 1) % dash.length; - dashOn = !dashOn; - phase = 0; - leftInThisDashSegment = dash[idx]; - } - goTo(curCurvepts, curCurveoff+2, type); - phase += li.lastSegLen(); - if (phase >= dash[idx]) { - phase = 0f; - idx = (idx + 1) % dash.length; - dashOn = !dashOn; - } - } - - private static boolean pointCurve(float[] curve, int type) { - for (int i = 2; i < type; i++) { - if (curve[i] != curve[i-2]) { - return false; - } - } - return true; - } - - // Objects of this class are used to iterate through curves. They return - // t values where the left side of the curve has a specified length. - // It does this by subdividing the input curve until a certain error - // condition has been met. A recursive subdivision procedure would - // return as many as 1< 0) { - this.sides[0] = Side.LEFT; - this.done = false; - } else { - // the root of the tree is a leaf so we're done. - this.sides[0] = Side.RIGHT; - this.done = true; - } - this.lastSegLen = 0; - } - - // 0 == false, 1 == true, -1 == invalid cached value. - private int cachedHaveLowAcceleration = -1; - - private boolean haveLowAcceleration(float err) { - if (cachedHaveLowAcceleration == -1) { - final float len1 = curLeafCtrlPolyLengths[0]; - final float len2 = curLeafCtrlPolyLengths[1]; - // the test below is equivalent to !within(len1/len2, 1, err). - // It is using a multiplication instead of a division, so it - // should be a bit faster. - if (!Helpers.within(len1, len2, err*len2)) { - cachedHaveLowAcceleration = 0; - return false; - } - if (curveType == 8) { - final float len3 = curLeafCtrlPolyLengths[2]; - // if len1 is close to 2 and 2 is close to 3, that probably - // means 1 is close to 3 so the second part of this test might - // not be needed, but it doesn't hurt to include it. - if (!(Helpers.within(len2, len3, err*len3) && - Helpers.within(len1, len3, err*len3))) { - cachedHaveLowAcceleration = 0; - return false; - } - } - cachedHaveLowAcceleration = 1; - return true; - } - - return (cachedHaveLowAcceleration == 1); - } - - // we want to avoid allocations/gc so we keep this array so we - // can put roots in it, - private float[] nextRoots = new float[4]; - - // caches the coefficients of the current leaf in its flattened - // form (see inside next() for what that means). The cache is - // invalid when it's third element is negative, since in any - // valid flattened curve, this would be >= 0. - private float[] flatLeafCoefCache = new float[] {0, 0, -1, 0}; - // returns the t value where the remaining curve should be split in - // order for the left subdivided curve to have length len. If len - // is >= than the length of the uniterated curve, it returns 1. - public float next(final float len) { - final float targetLength = lenAtLastSplit + len; - while(lenAtNextT < targetLength) { - if (done) { - lastSegLen = lenAtNextT - lenAtLastSplit; - return 1; - } - goToNextLeaf(); - } - lenAtLastSplit = targetLength; - final float leaflen = lenAtNextT - lenAtLastT; - float t = (targetLength - lenAtLastT) / leaflen; - - // cubicRootsInAB is a fairly expensive call, so we just don't do it - // if the acceleration in this section of the curve is small enough. - if (!haveLowAcceleration(0.05f)) { - // We flatten the current leaf along the x axis, so that we're - // left with a, b, c which define a 1D Bezier curve. We then - // solve this to get the parameter of the original leaf that - // gives us the desired length. - - if (flatLeafCoefCache[2] < 0) { - float x = 0+curLeafCtrlPolyLengths[0], - y = x+curLeafCtrlPolyLengths[1]; - if (curveType == 8) { - float z = y + curLeafCtrlPolyLengths[2]; - flatLeafCoefCache[0] = 3*(x - y) + z; - flatLeafCoefCache[1] = 3*(y - 2*x); - flatLeafCoefCache[2] = 3*x; - flatLeafCoefCache[3] = -z; - } else if (curveType == 6) { - flatLeafCoefCache[0] = 0f; - flatLeafCoefCache[1] = y - 2*x; - flatLeafCoefCache[2] = 2*x; - flatLeafCoefCache[3] = -y; - } - } - float a = flatLeafCoefCache[0]; - float b = flatLeafCoefCache[1]; - float c = flatLeafCoefCache[2]; - float d = t*flatLeafCoefCache[3]; - - // we use cubicRootsInAB here, because we want only roots in 0, 1, - // and our quadratic root finder doesn't filter, so it's just a - // matter of convenience. - int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0, 1); - if (n == 1 && !Float.isNaN(nextRoots[0])) { - t = nextRoots[0]; - } - } - // t is relative to the current leaf, so we must make it a valid parameter - // of the original curve. - t = t * (nextT - lastT) + lastT; - if (t >= 1) { - t = 1; - done = true; - } - // even if done = true, if we're here, that means targetLength - // is equal to, or very, very close to the total length of the - // curve, so lastSegLen won't be too high. In cases where len - // overshoots the curve, this method will exit in the while - // loop, and lastSegLen will still be set to the right value. - lastSegLen = len; - return t; - } - - public float lastSegLen() { - return lastSegLen; - } - - // go to the next leaf (in an inorder traversal) in the recursion tree - // preconditions: must be on a leaf, and that leaf must not be the root. - private void goToNextLeaf() { - // We must go to the first ancestor node that has an unvisited - // right child. - recLevel--; - while(sides[recLevel] == Side.RIGHT) { - if (recLevel == 0) { - done = true; - return; - } - recLevel--; - } - - sides[recLevel] = Side.RIGHT; - System.arraycopy(recCurveStack[recLevel], 0, recCurveStack[recLevel+1], 0, curveType); - recLevel++; - goLeft(); - } - - // go to the leftmost node from the current node. Return its length. - private void goLeft() { - float len = onLeaf(); - if (len >= 0) { - lastT = nextT; - lenAtLastT = lenAtNextT; - nextT += (1 << (limit - recLevel)) * minTincrement; - lenAtNextT += len; - // invalidate caches - flatLeafCoefCache[2] = -1; - cachedHaveLowAcceleration = -1; - } else { - Helpers.subdivide(recCurveStack[recLevel], 0, - recCurveStack[recLevel+1], 0, - recCurveStack[recLevel], 0, curveType); - sides[recLevel] = Side.LEFT; - recLevel++; - goLeft(); - } - } - - // this is a bit of a hack. It returns -1 if we're not on a leaf, and - // the length of the leaf if we are on a leaf. - private float onLeaf() { - float[] curve = recCurveStack[recLevel]; - float polyLen = 0; - - float x0 = curve[0], y0 = curve[1]; - for (int i = 2; i < curveType; i += 2) { - final float x1 = curve[i], y1 = curve[i+1]; - final float len = Helpers.linelen(x0, y0, x1, y1); - polyLen += len; - curLeafCtrlPolyLengths[i/2 - 1] = len; - x0 = x1; - y0 = y1; - } - - final float lineLen = Helpers.linelen(curve[0], curve[1], curve[curveType-2], curve[curveType-1]); - if (polyLen - lineLen < ERR || recLevel == limit) { - return (polyLen + lineLen)/2; - } - return -1; - } - } - - @Override - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - curCurvepts[0] = x0; curCurvepts[1] = y0; - curCurvepts[2] = x1; curCurvepts[3] = y1; - curCurvepts[4] = x2; curCurvepts[5] = y2; - curCurvepts[6] = x3; curCurvepts[7] = y3; - somethingTo(8); - } - - @Override - public void quadTo(float x1, float y1, float x2, float y2) { - curCurvepts[0] = x0; curCurvepts[1] = y0; - curCurvepts[2] = x1; curCurvepts[3] = y1; - curCurvepts[4] = x2; curCurvepts[5] = y2; - somethingTo(6); - } - - public void closePath() { - lineTo(sx, sy); - if (firstSegidx > 0) { - if (!dashOn || needsMoveTo) { - out.moveTo(sx, sy); - } - emitFirstSegments(); - } - moveTo(sx, sy); - } - - public void pathDone() { - if (firstSegidx > 0) { - out.moveTo(sx, sy); - emitFirstSegments(); - } - out.pathDone(); - } - - @Override - public long getNativeConsumer() { - throw new InternalError("Dasher does not use a native consumer"); - } -} - --- old/src/java.desktop/share/classes/sun/java2d/pisces/Helpers.java 2017-11-06 15:02:36.992239326 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,458 +0,0 @@ -/* - * Copyright (c) 2007, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import java.util.Arrays; -import static java.lang.Math.PI; -import static java.lang.Math.cos; -import static java.lang.Math.sqrt; -import static java.lang.Math.cbrt; -import static java.lang.Math.acos; - - -final class Helpers { - private Helpers() { - throw new Error("This is a non instantiable class"); - } - - static boolean within(final float x, final float y, final float err) { - final float d = y - x; - return (d <= err && d >= -err); - } - - static boolean within(final double x, final double y, final double err) { - final double d = y - x; - return (d <= err && d >= -err); - } - - static int quadraticRoots(final float a, final float b, - final float c, float[] zeroes, final int off) - { - int ret = off; - float t; - if (a != 0f) { - final float dis = b*b - 4*a*c; - if (dis > 0) { - final float sqrtDis = (float)Math.sqrt(dis); - // depending on the sign of b we use a slightly different - // algorithm than the traditional one to find one of the roots - // so we can avoid adding numbers of different signs (which - // might result in loss of precision). - if (b >= 0) { - zeroes[ret++] = (2 * c) / (-b - sqrtDis); - zeroes[ret++] = (-b - sqrtDis) / (2 * a); - } else { - zeroes[ret++] = (-b + sqrtDis) / (2 * a); - zeroes[ret++] = (2 * c) / (-b + sqrtDis); - } - } else if (dis == 0f) { - t = (-b) / (2 * a); - zeroes[ret++] = t; - } - } else { - if (b != 0f) { - t = (-c) / b; - zeroes[ret++] = t; - } - } - return ret - off; - } - - // find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B) - static int cubicRootsInAB(float d, float a, float b, float c, - float[] pts, final int off, - final float A, final float B) - { - if (d == 0) { - int num = quadraticRoots(a, b, c, pts, off); - return filterOutNotInAB(pts, off, num, A, B) - off; - } - // From Graphics Gems: - // http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c - // (also from awt.geom.CubicCurve2D. But here we don't need as - // much accuracy and we don't want to create arrays so we use - // our own customized version). - - /* normal form: x^3 + ax^2 + bx + c = 0 */ - a /= d; - b /= d; - c /= d; - - // substitute x = y - A/3 to eliminate quadratic term: - // x^3 +Px + Q = 0 - // - // Since we actually need P/3 and Q/2 for all of the - // calculations that follow, we will calculate - // p = P/3 - // q = Q/2 - // instead and use those values for simplicity of the code. - double sq_A = a * a; - double p = 1.0/3 * (-1.0/3 * sq_A + b); - double q = 1.0/2 * (2.0/27 * a * sq_A - 1.0/3 * a * b + c); - - /* use Cardano's formula */ - - double cb_p = p * p * p; - double D = q * q + cb_p; - - int num; - if (D < 0) { - // see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method - final double phi = 1.0/3 * acos(-q / sqrt(-cb_p)); - final double t = 2 * sqrt(-p); - - pts[ off+0 ] = (float)( t * cos(phi)); - pts[ off+1 ] = (float)(-t * cos(phi + PI / 3)); - pts[ off+2 ] = (float)(-t * cos(phi - PI / 3)); - num = 3; - } else { - final double sqrt_D = sqrt(D); - final double u = cbrt(sqrt_D - q); - final double v = - cbrt(sqrt_D + q); - - pts[ off ] = (float)(u + v); - num = 1; - - if (within(D, 0, 1e-8)) { - pts[off+1] = -(pts[off] / 2); - num = 2; - } - } - - final float sub = 1.0f/3 * a; - - for (int i = 0; i < num; ++i) { - pts[ off+i ] -= sub; - } - - return filterOutNotInAB(pts, off, num, A, B) - off; - } - - // These use a hardcoded factor of 2 for increasing sizes. Perhaps this - // should be provided as an argument. - static float[] widenArray(float[] in, final int cursize, final int numToAdd) { - if (in.length >= cursize + numToAdd) { - return in; - } - return Arrays.copyOf(in, 2 * (cursize + numToAdd)); - } - - static int[] widenArray(int[] in, final int cursize, final int numToAdd) { - if (in.length >= cursize + numToAdd) { - return in; - } - return Arrays.copyOf(in, 2 * (cursize + numToAdd)); - } - - static float evalCubic(final float a, final float b, - final float c, final float d, - final float t) - { - return t * (t * (t * a + b) + c) + d; - } - - static float evalQuad(final float a, final float b, - final float c, final float t) - { - return t * (t * a + b) + c; - } - - // returns the index 1 past the last valid element remaining after filtering - static int filterOutNotInAB(float[] nums, final int off, final int len, - final float a, final float b) - { - int ret = off; - for (int i = off; i < off + len; i++) { - if (nums[i] >= a && nums[i] < b) { - nums[ret++] = nums[i]; - } - } - return ret; - } - - static float polyLineLength(float[] poly, final int off, final int nCoords) { - assert nCoords % 2 == 0 && poly.length >= off + nCoords : ""; - float acc = 0; - for (int i = off + 2; i < off + nCoords; i += 2) { - acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]); - } - return acc; - } - - static float linelen(float x1, float y1, float x2, float y2) { - final float dx = x2 - x1; - final float dy = y2 - y1; - return (float)Math.sqrt(dx*dx + dy*dy); - } - - static void subdivide(float[] src, int srcoff, float[] left, int leftoff, - float[] right, int rightoff, int type) - { - switch(type) { - case 6: - Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff); - break; - case 8: - Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff); - break; - default: - throw new InternalError("Unsupported curve type"); - } - } - - static void isort(float[] a, int off, int len) { - for (int i = off + 1; i < off + len; i++) { - float ai = a[i]; - int j = i - 1; - for (; j >= off && a[j] > ai; j--) { - a[j+1] = a[j]; - } - a[j+1] = ai; - } - } - - // Most of these are copied from classes in java.awt.geom because we need - // float versions of these functions, and Line2D, CubicCurve2D, - // QuadCurve2D don't provide them. - /** - * Subdivides the cubic curve specified by the coordinates - * stored in the {@code src} array at indices {@code srcoff} - * through ({@code srcoff} + 7) and stores the - * resulting two subdivided curves into the two result arrays at the - * corresponding indices. - * Either or both of the {@code left} and {@code right} - * arrays may be {@code null} or a reference to the same array - * as the {@code src} array. - * Note that the last point in the first subdivided curve is the - * same as the first point in the second subdivided curve. Thus, - * it is possible to pass the same array for {@code left} - * and {@code right} and to use offsets, such as {@code rightoff} - * equals ({@code leftoff} + 6), in order - * to avoid allocating extra storage for this common point. - * @param src the array holding the coordinates for the source curve - * @param srcoff the offset into the array of the beginning of the - * the 6 source coordinates - * @param left the array for storing the coordinates for the first - * half of the subdivided curve - * @param leftoff the offset into the array of the beginning of the - * the 6 left coordinates - * @param right the array for storing the coordinates for the second - * half of the subdivided curve - * @param rightoff the offset into the array of the beginning of the - * the 6 right coordinates - * @since 1.7 - */ - static void subdivideCubic(float src[], int srcoff, - float left[], int leftoff, - float right[], int rightoff) - { - float x1 = src[srcoff + 0]; - float y1 = src[srcoff + 1]; - float ctrlx1 = src[srcoff + 2]; - float ctrly1 = src[srcoff + 3]; - float ctrlx2 = src[srcoff + 4]; - float ctrly2 = src[srcoff + 5]; - float x2 = src[srcoff + 6]; - float y2 = src[srcoff + 7]; - if (left != null) { - left[leftoff + 0] = x1; - left[leftoff + 1] = y1; - } - if (right != null) { - right[rightoff + 6] = x2; - right[rightoff + 7] = y2; - } - x1 = (x1 + ctrlx1) / 2.0f; - y1 = (y1 + ctrly1) / 2.0f; - x2 = (x2 + ctrlx2) / 2.0f; - y2 = (y2 + ctrly2) / 2.0f; - float centerx = (ctrlx1 + ctrlx2) / 2.0f; - float centery = (ctrly1 + ctrly2) / 2.0f; - ctrlx1 = (x1 + centerx) / 2.0f; - ctrly1 = (y1 + centery) / 2.0f; - ctrlx2 = (x2 + centerx) / 2.0f; - ctrly2 = (y2 + centery) / 2.0f; - centerx = (ctrlx1 + ctrlx2) / 2.0f; - centery = (ctrly1 + ctrly2) / 2.0f; - if (left != null) { - left[leftoff + 2] = x1; - left[leftoff + 3] = y1; - left[leftoff + 4] = ctrlx1; - left[leftoff + 5] = ctrly1; - left[leftoff + 6] = centerx; - left[leftoff + 7] = centery; - } - if (right != null) { - right[rightoff + 0] = centerx; - right[rightoff + 1] = centery; - right[rightoff + 2] = ctrlx2; - right[rightoff + 3] = ctrly2; - right[rightoff + 4] = x2; - right[rightoff + 5] = y2; - } - } - - - static void subdivideCubicAt(float t, float src[], int srcoff, - float left[], int leftoff, - float right[], int rightoff) - { - float x1 = src[srcoff + 0]; - float y1 = src[srcoff + 1]; - float ctrlx1 = src[srcoff + 2]; - float ctrly1 = src[srcoff + 3]; - float ctrlx2 = src[srcoff + 4]; - float ctrly2 = src[srcoff + 5]; - float x2 = src[srcoff + 6]; - float y2 = src[srcoff + 7]; - if (left != null) { - left[leftoff + 0] = x1; - left[leftoff + 1] = y1; - } - if (right != null) { - right[rightoff + 6] = x2; - right[rightoff + 7] = y2; - } - x1 = x1 + t * (ctrlx1 - x1); - y1 = y1 + t * (ctrly1 - y1); - x2 = ctrlx2 + t * (x2 - ctrlx2); - y2 = ctrly2 + t * (y2 - ctrly2); - float centerx = ctrlx1 + t * (ctrlx2 - ctrlx1); - float centery = ctrly1 + t * (ctrly2 - ctrly1); - ctrlx1 = x1 + t * (centerx - x1); - ctrly1 = y1 + t * (centery - y1); - ctrlx2 = centerx + t * (x2 - centerx); - ctrly2 = centery + t * (y2 - centery); - centerx = ctrlx1 + t * (ctrlx2 - ctrlx1); - centery = ctrly1 + t * (ctrly2 - ctrly1); - if (left != null) { - left[leftoff + 2] = x1; - left[leftoff + 3] = y1; - left[leftoff + 4] = ctrlx1; - left[leftoff + 5] = ctrly1; - left[leftoff + 6] = centerx; - left[leftoff + 7] = centery; - } - if (right != null) { - right[rightoff + 0] = centerx; - right[rightoff + 1] = centery; - right[rightoff + 2] = ctrlx2; - right[rightoff + 3] = ctrly2; - right[rightoff + 4] = x2; - right[rightoff + 5] = y2; - } - } - - static void subdivideQuad(float src[], int srcoff, - float left[], int leftoff, - float right[], int rightoff) - { - float x1 = src[srcoff + 0]; - float y1 = src[srcoff + 1]; - float ctrlx = src[srcoff + 2]; - float ctrly = src[srcoff + 3]; - float x2 = src[srcoff + 4]; - float y2 = src[srcoff + 5]; - if (left != null) { - left[leftoff + 0] = x1; - left[leftoff + 1] = y1; - } - if (right != null) { - right[rightoff + 4] = x2; - right[rightoff + 5] = y2; - } - x1 = (x1 + ctrlx) / 2.0f; - y1 = (y1 + ctrly) / 2.0f; - x2 = (x2 + ctrlx) / 2.0f; - y2 = (y2 + ctrly) / 2.0f; - ctrlx = (x1 + x2) / 2.0f; - ctrly = (y1 + y2) / 2.0f; - if (left != null) { - left[leftoff + 2] = x1; - left[leftoff + 3] = y1; - left[leftoff + 4] = ctrlx; - left[leftoff + 5] = ctrly; - } - if (right != null) { - right[rightoff + 0] = ctrlx; - right[rightoff + 1] = ctrly; - right[rightoff + 2] = x2; - right[rightoff + 3] = y2; - } - } - - static void subdivideQuadAt(float t, float src[], int srcoff, - float left[], int leftoff, - float right[], int rightoff) - { - float x1 = src[srcoff + 0]; - float y1 = src[srcoff + 1]; - float ctrlx = src[srcoff + 2]; - float ctrly = src[srcoff + 3]; - float x2 = src[srcoff + 4]; - float y2 = src[srcoff + 5]; - if (left != null) { - left[leftoff + 0] = x1; - left[leftoff + 1] = y1; - } - if (right != null) { - right[rightoff + 4] = x2; - right[rightoff + 5] = y2; - } - x1 = x1 + t * (ctrlx - x1); - y1 = y1 + t * (ctrly - y1); - x2 = ctrlx + t * (x2 - ctrlx); - y2 = ctrly + t * (y2 - ctrly); - ctrlx = x1 + t * (x2 - x1); - ctrly = y1 + t * (y2 - y1); - if (left != null) { - left[leftoff + 2] = x1; - left[leftoff + 3] = y1; - left[leftoff + 4] = ctrlx; - left[leftoff + 5] = ctrly; - } - if (right != null) { - right[rightoff + 0] = ctrlx; - right[rightoff + 1] = ctrly; - right[rightoff + 2] = x2; - right[rightoff + 3] = y2; - } - } - - static void subdivideAt(float t, float src[], int srcoff, - float left[], int leftoff, - float right[], int rightoff, int size) - { - switch(size) { - case 8: - subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff); - break; - case 6: - subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff); - break; - } - } -} --- old/src/java.desktop/share/classes/sun/java2d/pisces/PiscesCache.java 2017-11-06 15:02:37.380239315 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2007, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import java.util.Arrays; - -/** - * An object used to cache pre-rendered complex paths. - */ -final class PiscesCache { - - final int bboxX0, bboxY0, bboxX1, bboxY1; - - // rowAARLE[i] holds the encoding of the pixel row with y = bboxY0+i. - // The format of each of the inner arrays is: rowAARLE[i][0,1] = (x0, n) - // where x0 is the first x in row i with nonzero alpha, and n is the - // number of RLE entries in this row. rowAARLE[i][j,j+1] for j>1 is - // (val,runlen) - final int[][] rowAARLE; - - // RLE encodings are added in increasing y rows and then in increasing - // x inside those rows. Therefore, at any one time there is a well - // defined position (x,y) where a run length is about to be added (or - // the row terminated). x0,y0 is this (x,y)-(bboxX0,bboxY0). They - // are used to get indices into the current tile. - private int x0 = Integer.MIN_VALUE, y0 = Integer.MIN_VALUE; - - // touchedTile[i][j] is the sum of all the alphas in the tile with - // y=i*TILE_SIZE+bboxY0 and x=j*TILE_SIZE+bboxX0. - private final int[][] touchedTile; - - static final int TILE_SIZE_LG = 5; - static final int TILE_SIZE = 1 << TILE_SIZE_LG; // 32 - private static final int INIT_ROW_SIZE = 8; // enough for 3 run lengths - - PiscesCache(int minx, int miny, int maxx, int maxy) { - assert maxy >= miny && maxx >= minx; - bboxX0 = minx; - bboxY0 = miny; - bboxX1 = maxx + 1; - bboxY1 = maxy + 1; - // we could just leave the inner arrays as null and allocate them - // lazily (which would be beneficial for shapes with gaps), but we - // assume there won't be too many of those so we allocate everything - // up front (which is better for other cases) - rowAARLE = new int[bboxY1 - bboxY0 + 1][INIT_ROW_SIZE]; - x0 = 0; - y0 = -1; // -1 makes the first assert in startRow succeed - // the ceiling of (maxy - miny + 1) / TILE_SIZE; - int nyTiles = (maxy - miny + TILE_SIZE) >> TILE_SIZE_LG; - int nxTiles = (maxx - minx + TILE_SIZE) >> TILE_SIZE_LG; - - touchedTile = new int[nyTiles][nxTiles]; - } - - void addRLERun(int val, int runLen) { - if (runLen > 0) { - addTupleToRow(y0, val, runLen); - if (val != 0) { - // the x and y of the current row, minus bboxX0, bboxY0 - int tx = x0 >> TILE_SIZE_LG; - int ty = y0 >> TILE_SIZE_LG; - int tx1 = (x0 + runLen - 1) >> TILE_SIZE_LG; - // while we forbid rows from starting before bboxx0, our users - // can still store rows that go beyond bboxx1 (although this - // shouldn't happen), so it's a good idea to check that i - // is not going out of bounds in touchedTile[ty] - if (tx1 >= touchedTile[ty].length) { - tx1 = touchedTile[ty].length - 1; - } - if (tx <= tx1) { - int nextTileXCoord = (tx + 1) << TILE_SIZE_LG; - if (nextTileXCoord > x0+runLen) { - touchedTile[ty][tx] += val * runLen; - } else { - touchedTile[ty][tx] += val * (nextTileXCoord - x0); - } - tx++; - } - // don't go all the way to tx1 - we need to handle the last - // tile as a special case (just like we did with the first - for (; tx < tx1; tx++) { -// try { - touchedTile[ty][tx] += (val << TILE_SIZE_LG); -// } catch (RuntimeException e) { -// System.out.println("x0, y0: " + x0 + ", " + y0); -// System.out.printf("tx, ty, tx1: %d, %d, %d %n", tx, ty, tx1); -// System.out.printf("bboxX/Y0/1: %d, %d, %d, %d %n", -// bboxX0, bboxY0, bboxX1, bboxY1); -// throw e; -// } - } - // they will be equal unless x0>>TILE_SIZE_LG == tx1 - if (tx == tx1) { - int lastXCoord = Math.min(x0 + runLen, (tx + 1) << TILE_SIZE_LG); - int txXCoord = tx << TILE_SIZE_LG; - touchedTile[ty][tx] += val * (lastXCoord - txXCoord); - } - } - x0 += runLen; - } - } - - void startRow(int y, int x) { - // rows are supposed to be added by increasing y. - assert y - bboxY0 > y0; - assert y <= bboxY1; // perhaps this should be < instead of <= - - y0 = y - bboxY0; - // this should be a new, uninitialized row. - assert rowAARLE[y0][1] == 0; - - x0 = x - bboxX0; - assert x0 >= 0 : "Input must not be to the left of bbox bounds"; - - // the way addTupleToRow is implemented it would work for this but it's - // not a good idea to use it because it is meant for adding - // RLE tuples, not the first tuple (which is special). - rowAARLE[y0][0] = x; - rowAARLE[y0][1] = 2; - } - - int alphaSumInTile(int x, int y) { - x -= bboxX0; - y -= bboxY0; - return touchedTile[y>>TILE_SIZE_LG][x>>TILE_SIZE_LG]; - } - - int minTouched(int rowidx) { - return rowAARLE[rowidx][0]; - } - - int rowLength(int rowidx) { - return rowAARLE[rowidx][1]; - } - - private void addTupleToRow(int row, int a, int b) { - int end = rowAARLE[row][1]; - rowAARLE[row] = Helpers.widenArray(rowAARLE[row], end, 2); - rowAARLE[row][end++] = a; - rowAARLE[row][end++] = b; - rowAARLE[row][1] = end; - } - - void getBBox(int bbox[]) { - // Since we add +1 to bboxX1,bboxY1 so when PTG asks for bbox, - // we will give after -1 - bbox[0] = bboxX0; - bbox[1] = bboxY0; - bbox[2] = bboxX1 - 1; - bbox[3] = bboxY1 - 1; - } - - @Override - public String toString() { - String ret = "bbox = ["+ - bboxX0+", "+bboxY0+" => "+ - bboxX1+", "+bboxY1+"]\n"; - for (int[] row : rowAARLE) { - if (row != null) { - ret += ("minTouchedX=" + row[0] + - "\tRLE Entries: " + Arrays.toString( - Arrays.copyOfRange(row, 2, row[1])) + "\n"); - } else { - ret += "[]\n"; - } - } - return ret; - } -} --- old/src/java.desktop/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java 2017-11-06 15:02:37.788239304 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,656 +0,0 @@ -/* - * Copyright (c) 2007, 2014, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import java.awt.Shape; -import java.awt.BasicStroke; -import java.awt.geom.Path2D; -import java.awt.geom.AffineTransform; -import java.awt.geom.PathIterator; - -import sun.awt.geom.PathConsumer2D; -import sun.java2d.pipe.Region; -import sun.java2d.pipe.RenderingEngine; -import sun.java2d.pipe.AATileGenerator; - -public class PiscesRenderingEngine extends RenderingEngine { - private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA} - - /** - * Create a widened path as specified by the parameters. - *

- * The specified {@code src} {@link Shape} is widened according - * to the specified attribute parameters as per the - * {@link BasicStroke} specification. - * - * @param src the source path to be widened - * @param width the width of the widened path as per {@code BasicStroke} - * @param caps the end cap decorations as per {@code BasicStroke} - * @param join the segment join decorations as per {@code BasicStroke} - * @param miterlimit the miter limit as per {@code BasicStroke} - * @param dashes the dash length array as per {@code BasicStroke} - * @param dashphase the initial dash phase as per {@code BasicStroke} - * @return the widened path stored in a new {@code Shape} object - * @since 1.7 - */ - public Shape createStrokedShape(Shape src, - float width, - int caps, - int join, - float miterlimit, - float dashes[], - float dashphase) - { - final Path2D p2d = new Path2D.Float(); - - strokeTo(src, - null, - width, - NormMode.OFF, - caps, - join, - miterlimit, - dashes, - dashphase, - new PathConsumer2D() { - public void moveTo(float x0, float y0) { - p2d.moveTo(x0, y0); - } - public void lineTo(float x1, float y1) { - p2d.lineTo(x1, y1); - } - public void closePath() { - p2d.closePath(); - } - public void pathDone() {} - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) { - p2d.curveTo(x1, y1, x2, y2, x3, y3); - } - public void quadTo(float x1, float y1, float x2, float y2) { - p2d.quadTo(x1, y1, x2, y2); - } - public long getNativeConsumer() { - throw new InternalError("Not using a native peer"); - } - }); - return p2d; - } - - /** - * Sends the geometry for a widened path as specified by the parameters - * to the specified consumer. - *

- * The specified {@code src} {@link Shape} is widened according - * to the parameters specified by the {@link BasicStroke} object. - * Adjustments are made to the path as appropriate for the - * {@link java.awt.RenderingHints#VALUE_STROKE_NORMALIZE} hint if the - * {@code normalize} boolean parameter is true. - * Adjustments are made to the path as appropriate for the - * {@link java.awt.RenderingHints#VALUE_ANTIALIAS_ON} hint if the - * {@code antialias} boolean parameter is true. - *

- * The geometry of the widened path is forwarded to the indicated - * {@link PathConsumer2D} object as it is calculated. - * - * @param src the source path to be widened - * @param bs the {@code BasicSroke} object specifying the - * decorations to be applied to the widened path - * @param normalize indicates whether stroke normalization should - * be applied - * @param antialias indicates whether or not adjustments appropriate - * to antialiased rendering should be applied - * @param consumer the {@code PathConsumer2D} instance to forward - * the widened geometry to - * @since 1.7 - */ - public void strokeTo(Shape src, - AffineTransform at, - BasicStroke bs, - boolean thin, - boolean normalize, - boolean antialias, - final PathConsumer2D consumer) - { - NormMode norm = (normalize) ? - ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA) - : NormMode.OFF; - strokeTo(src, at, bs, thin, norm, antialias, consumer); - } - - void strokeTo(Shape src, - AffineTransform at, - BasicStroke bs, - boolean thin, - NormMode normalize, - boolean antialias, - PathConsumer2D pc2d) - { - float lw; - if (thin) { - if (antialias) { - lw = userSpaceLineWidth(at, 0.5f); - } else { - lw = userSpaceLineWidth(at, 1.0f); - } - } else { - lw = bs.getLineWidth(); - } - strokeTo(src, - at, - lw, - normalize, - bs.getEndCap(), - bs.getLineJoin(), - bs.getMiterLimit(), - bs.getDashArray(), - bs.getDashPhase(), - pc2d); - } - - private float userSpaceLineWidth(AffineTransform at, float lw) { - - double widthScale; - - if ((at.getType() & (AffineTransform.TYPE_GENERAL_TRANSFORM | - AffineTransform.TYPE_GENERAL_SCALE)) != 0) { - widthScale = Math.sqrt(at.getDeterminant()); - } else { - /* First calculate the "maximum scale" of this transform. */ - double A = at.getScaleX(); // m00 - double C = at.getShearX(); // m01 - double B = at.getShearY(); // m10 - double D = at.getScaleY(); // m11 - - /* - * Given a 2 x 2 affine matrix [ A B ] such that - * [ C D ] - * v' = [x' y'] = [Ax + Cy, Bx + Dy], we want to - * find the maximum magnitude (norm) of the vector v' - * with the constraint (x^2 + y^2 = 1). - * The equation to maximize is - * |v'| = sqrt((Ax+Cy)^2+(Bx+Dy)^2) - * or |v'| = sqrt((AA+BB)x^2 + 2(AC+BD)xy + (CC+DD)y^2). - * Since sqrt is monotonic we can maximize |v'|^2 - * instead and plug in the substitution y = sqrt(1 - x^2). - * Trigonometric equalities can then be used to get - * rid of most of the sqrt terms. - */ - - double EA = A*A + B*B; // x^2 coefficient - double EB = 2*(A*C + B*D); // xy coefficient - double EC = C*C + D*D; // y^2 coefficient - - /* - * There is a lot of calculus omitted here. - * - * Conceptually, in the interests of understanding the - * terms that the calculus produced we can consider - * that EA and EC end up providing the lengths along - * the major axes and the hypot term ends up being an - * adjustment for the additional length along the off-axis - * angle of rotated or sheared ellipses as well as an - * adjustment for the fact that the equation below - * averages the two major axis lengths. (Notice that - * the hypot term contains a part which resolves to the - * difference of these two axis lengths in the absence - * of rotation.) - * - * In the calculus, the ratio of the EB and (EA-EC) terms - * ends up being the tangent of 2*theta where theta is - * the angle that the long axis of the ellipse makes - * with the horizontal axis. Thus, this equation is - * calculating the length of the hypotenuse of a triangle - * along that axis. - */ - - double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC)); - /* sqrt omitted, compare to squared limits below. */ - double widthsquared = ((EA + EC + hypot)/2.0); - - widthScale = Math.sqrt(widthsquared); - } - - return (float) (lw / widthScale); - } - - void strokeTo(Shape src, - AffineTransform at, - float width, - NormMode normalize, - int caps, - int join, - float miterlimit, - float dashes[], - float dashphase, - PathConsumer2D pc2d) - { - // We use strokerat and outat so that in Stroker and Dasher we can work only - // with the pre-transformation coordinates. This will repeat a lot of - // computations done in the path iterator, but the alternative is to - // work with transformed paths and compute untransformed coordinates - // as needed. This would be faster but I do not think the complexity - // of working with both untransformed and transformed coordinates in - // the same code is worth it. - // However, if a path's width is constant after a transformation, - // we can skip all this untransforming. - - // If normalization is off we save some transformations by not - // transforming the input to pisces. Instead, we apply the - // transformation after the path processing has been done. - // We can't do this if normalization is on, because it isn't a good - // idea to normalize before the transformation is applied. - AffineTransform strokerat = null; - AffineTransform outat = null; - - PathIterator pi = null; - - if (at != null && !at.isIdentity()) { - final double a = at.getScaleX(); - final double b = at.getShearX(); - final double c = at.getShearY(); - final double d = at.getScaleY(); - final double det = a * d - c * b; - if (Math.abs(det) <= 2 * Float.MIN_VALUE) { - // this rendering engine takes one dimensional curves and turns - // them into 2D shapes by giving them width. - // However, if everything is to be passed through a singular - // transformation, these 2D shapes will be squashed down to 1D - // again so, nothing can be drawn. - - // Every path needs an initial moveTo and a pathDone. If these - // are not there this causes a SIGSEGV in libawt.so (at the time - // of writing of this comment (September 16, 2010)). Actually, - // I am not sure if the moveTo is necessary to avoid the SIGSEGV - // but the pathDone is definitely needed. - pc2d.moveTo(0, 0); - pc2d.pathDone(); - return; - } - - // If the transform is a constant multiple of an orthogonal transformation - // then every length is just multiplied by a constant, so we just - // need to transform input paths to stroker and tell stroker - // the scaled width. This condition is satisfied if - // a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we - // leave a bit of room for error. - if (nearZero(a*b + c*d, 2) && nearZero(a*a+c*c - (b*b+d*d), 2)) { - double scale = Math.sqrt(a*a + c*c); - if (dashes != null) { - dashes = java.util.Arrays.copyOf(dashes, dashes.length); - for (int i = 0; i < dashes.length; i++) { - dashes[i] = (float)(scale * dashes[i]); - } - dashphase = (float)(scale * dashphase); - } - width = (float)(scale * width); - pi = src.getPathIterator(at); - if (normalize != NormMode.OFF) { - pi = new NormalizingPathIterator(pi, normalize); - } - // by now strokerat == null && outat == null. Input paths to - // stroker (and maybe dasher) will have the full transform at - // applied to them and nothing will happen to the output paths. - } else { - if (normalize != NormMode.OFF) { - strokerat = at; - pi = src.getPathIterator(at); - pi = new NormalizingPathIterator(pi, normalize); - // by now strokerat == at && outat == null. Input paths to - // stroker (and maybe dasher) will have the full transform at - // applied to them, then they will be normalized, and then - // the inverse of *only the non translation part of at* will - // be applied to the normalized paths. This won't cause problems - // in stroker, because, suppose at = T*A, where T is just the - // translation part of at, and A is the rest. T*A has already - // been applied to Stroker/Dasher's input. Then Ainv will be - // applied. Ainv*T*A is not equal to T, but it is a translation, - // which means that none of stroker's assumptions about its - // input will be violated. After all this, A will be applied - // to stroker's output. - } else { - outat = at; - pi = src.getPathIterator(null); - // outat == at && strokerat == null. This is because if no - // normalization is done, we can just apply all our - // transformations to stroker's output. - } - } - } else { - // either at is null or it's the identity. In either case - // we don't transform the path. - pi = src.getPathIterator(null); - if (normalize != NormMode.OFF) { - pi = new NormalizingPathIterator(pi, normalize); - } - } - - // by now, at least one of outat and strokerat will be null. Unless at is not - // a constant multiple of an orthogonal transformation, they will both be - // null. In other cases, outat == at if normalization is off, and if - // normalization is on, strokerat == at. - pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat); - pc2d = TransformingPathConsumer2D.deltaTransformConsumer(pc2d, strokerat); - pc2d = new Stroker(pc2d, width, caps, join, miterlimit); - if (dashes != null) { - pc2d = new Dasher(pc2d, dashes, dashphase); - } - pc2d = TransformingPathConsumer2D.inverseDeltaTransformConsumer(pc2d, strokerat); - pathTo(pi, pc2d); - } - - private static boolean nearZero(double num, int nulps) { - return Math.abs(num) < nulps * Math.ulp(num); - } - - private static class NormalizingPathIterator implements PathIterator { - - private final PathIterator src; - - // the adjustment applied to the current position. - private float curx_adjust, cury_adjust; - // the adjustment applied to the last moveTo position. - private float movx_adjust, movy_adjust; - - // constants used in normalization computations - private final float lval, rval; - - NormalizingPathIterator(PathIterator src, NormMode mode) { - this.src = src; - switch (mode) { - case ON_NO_AA: - // round to nearest (0.25, 0.25) pixel - lval = rval = 0.25f; - break; - case ON_WITH_AA: - // round to nearest pixel center - lval = 0f; - rval = 0.5f; - break; - case OFF: - throw new InternalError("A NormalizingPathIterator should " + - "not be created if no normalization is being done"); - default: - throw new InternalError("Unrecognized normalization mode"); - } - } - - public int currentSegment(float[] coords) { - int type = src.currentSegment(coords); - - int lastCoord; - switch(type) { - case PathIterator.SEG_CUBICTO: - lastCoord = 4; - break; - case PathIterator.SEG_QUADTO: - lastCoord = 2; - break; - case PathIterator.SEG_LINETO: - case PathIterator.SEG_MOVETO: - lastCoord = 0; - break; - case PathIterator.SEG_CLOSE: - // we don't want to deal with this case later. We just exit now - curx_adjust = movx_adjust; - cury_adjust = movy_adjust; - return type; - default: - throw new InternalError("Unrecognized curve type"); - } - - // normalize endpoint - float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + - rval - coords[lastCoord]; - float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + - rval - coords[lastCoord + 1]; - - coords[lastCoord ] += x_adjust; - coords[lastCoord + 1] += y_adjust; - - // now that the end points are done, normalize the control points - switch(type) { - case PathIterator.SEG_CUBICTO: - coords[0] += curx_adjust; - coords[1] += cury_adjust; - coords[2] += x_adjust; - coords[3] += y_adjust; - break; - case PathIterator.SEG_QUADTO: - coords[0] += (curx_adjust + x_adjust) / 2; - coords[1] += (cury_adjust + y_adjust) / 2; - break; - case PathIterator.SEG_LINETO: - break; - case PathIterator.SEG_MOVETO: - movx_adjust = x_adjust; - movy_adjust = y_adjust; - break; - case PathIterator.SEG_CLOSE: - throw new InternalError("This should be handled earlier."); - } - curx_adjust = x_adjust; - cury_adjust = y_adjust; - return type; - } - - public int currentSegment(double[] coords) { - float[] tmp = new float[6]; - int type = this.currentSegment(tmp); - for (int i = 0; i < 6; i++) { - coords[i] = tmp[i]; - } - return type; - } - - public int getWindingRule() { - return src.getWindingRule(); - } - - public boolean isDone() { - return src.isDone(); - } - - public void next() { - src.next(); - } - } - - static void pathTo(PathIterator pi, PathConsumer2D pc2d) { - RenderingEngine.feedConsumer(pi, pc2d); - pc2d.pathDone(); - } - - /** - * Construct an antialiased tile generator for the given shape with - * the given rendering attributes and store the bounds of the tile - * iteration in the bbox parameter. - * The {@code at} parameter specifies a transform that should affect - * both the shape and the {@code BasicStroke} attributes. - * The {@code clip} parameter specifies the current clip in effect - * in device coordinates and can be used to prune the data for the - * operation, but the renderer is not required to perform any - * clipping. - * If the {@code BasicStroke} parameter is null then the shape - * should be filled as is, otherwise the attributes of the - * {@code BasicStroke} should be used to specify a draw operation. - * The {@code thin} parameter indicates whether or not the - * transformed {@code BasicStroke} represents coordinates smaller - * than the minimum resolution of the antialiasing rasterizer as - * specified by the {@code getMinimumAAPenWidth()} method. - *

- * Upon returning, this method will fill the {@code bbox} parameter - * with 4 values indicating the bounds of the iteration of the - * tile generator. - * The iteration order of the tiles will be as specified by the - * pseudo-code: - *

-     *     for (y = bbox[1]; y < bbox[3]; y += tileheight) {
-     *         for (x = bbox[0]; x < bbox[2]; x += tilewidth) {
-     *         }
-     *     }
-     * 
- * If there is no output to be rendered, this method may return - * null. - * - * @param s the shape to be rendered (fill or draw) - * @param at the transform to be applied to the shape and the - * stroke attributes - * @param clip the current clip in effect in device coordinates - * @param bs if non-null, a {@code BasicStroke} whose attributes - * should be applied to this operation - * @param thin true if the transformed stroke attributes are smaller - * than the minimum dropout pen width - * @param normalize true if the {@code VALUE_STROKE_NORMALIZE} - * {@code RenderingHint} is in effect - * @param bbox returns the bounds of the iteration - * @return the {@code AATileGenerator} instance to be consulted - * for tile coverages, or null if there is no output to render - * @since 1.7 - */ - public AATileGenerator getAATileGenerator(Shape s, - AffineTransform at, - Region clip, - BasicStroke bs, - boolean thin, - boolean normalize, - int bbox[]) - { - Renderer r; - NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF; - if (bs == null) { - PathIterator pi; - if (normalize) { - pi = new NormalizingPathIterator(s.getPathIterator(at), norm); - } else { - pi = s.getPathIterator(at); - } - r = new Renderer(3, 3, - clip.getLoX(), clip.getLoY(), - clip.getWidth(), clip.getHeight(), - pi.getWindingRule()); - pathTo(pi, r); - } else { - r = new Renderer(3, 3, - clip.getLoX(), clip.getLoY(), - clip.getWidth(), clip.getHeight(), - PathIterator.WIND_NON_ZERO); - strokeTo(s, at, bs, thin, norm, true, r); - } - r.endRendering(); - PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA); - ptg.getBbox(bbox); - return ptg; - } - - public AATileGenerator getAATileGenerator(double x, double y, - double dx1, double dy1, - double dx2, double dy2, - double lw1, double lw2, - Region clip, - int bbox[]) - { - // REMIND: Deal with large coordinates! - double ldx1, ldy1, ldx2, ldy2; - boolean innerpgram = (lw1 > 0 && lw2 > 0); - - if (innerpgram) { - ldx1 = dx1 * lw1; - ldy1 = dy1 * lw1; - ldx2 = dx2 * lw2; - ldy2 = dy2 * lw2; - x -= (ldx1 + ldx2) / 2.0; - y -= (ldy1 + ldy2) / 2.0; - dx1 += ldx1; - dy1 += ldy1; - dx2 += ldx2; - dy2 += ldy2; - if (lw1 > 1 && lw2 > 1) { - // Inner parallelogram was entirely consumed by stroke... - innerpgram = false; - } - } else { - ldx1 = ldy1 = ldx2 = ldy2 = 0; - } - - Renderer r = new Renderer(3, 3, - clip.getLoX(), clip.getLoY(), - clip.getWidth(), clip.getHeight(), - PathIterator.WIND_EVEN_ODD); - - r.moveTo((float) x, (float) y); - r.lineTo((float) (x+dx1), (float) (y+dy1)); - r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2)); - r.lineTo((float) (x+dx2), (float) (y+dy2)); - r.closePath(); - - if (innerpgram) { - x += ldx1 + ldx2; - y += ldy1 + ldy2; - dx1 -= 2.0 * ldx1; - dy1 -= 2.0 * ldy1; - dx2 -= 2.0 * ldx2; - dy2 -= 2.0 * ldy2; - r.moveTo((float) x, (float) y); - r.lineTo((float) (x+dx1), (float) (y+dy1)); - r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2)); - r.lineTo((float) (x+dx2), (float) (y+dy2)); - r.closePath(); - } - - r.pathDone(); - - r.endRendering(); - PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA); - ptg.getBbox(bbox); - return ptg; - } - - /** - * Returns the minimum pen width that the antialiasing rasterizer - * can represent without dropouts occurring. - * @since 1.7 - */ - public float getMinimumAAPenSize() { - return 0.5f; - } - - static { - if (PathIterator.WIND_NON_ZERO != Renderer.WIND_NON_ZERO || - PathIterator.WIND_EVEN_ODD != Renderer.WIND_EVEN_ODD || - BasicStroke.JOIN_MITER != Stroker.JOIN_MITER || - BasicStroke.JOIN_ROUND != Stroker.JOIN_ROUND || - BasicStroke.JOIN_BEVEL != Stroker.JOIN_BEVEL || - BasicStroke.CAP_BUTT != Stroker.CAP_BUTT || - BasicStroke.CAP_ROUND != Stroker.CAP_ROUND || - BasicStroke.CAP_SQUARE != Stroker.CAP_SQUARE) - { - throw new InternalError("mismatched renderer constants"); - } - } -} - --- old/src/java.desktop/share/classes/sun/java2d/pisces/PiscesTileGenerator.java 2017-11-06 15:02:38.176239293 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,249 +0,0 @@ -/* - * Copyright (c) 2007, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import sun.java2d.pipe.AATileGenerator; - -final class PiscesTileGenerator implements AATileGenerator { - public static final int TILE_SIZE = PiscesCache.TILE_SIZE; - - // perhaps we should be using weak references here, but right now - // that's not necessary. The way the renderer is, this map will - // never contain more than one element - the one with key 64, since - // we only do 8x8 supersampling. - private static final Map alphaMapsCache = new - ConcurrentHashMap(); - - PiscesCache cache; - int x, y; - final int maxalpha; - private final int maxTileAlphaSum; - - // The alpha map used by this object (taken out of our map cache) to convert - // pixel coverage counts gotten from PiscesCache (which are in the range - // [0, maxalpha]) into alpha values, which are in [0,256). - byte alphaMap[]; - - public PiscesTileGenerator(Renderer r, int maxalpha) { - this.cache = r.getCache(); - this.x = cache.bboxX0; - this.y = cache.bboxY0; - this.alphaMap = getAlphaMap(maxalpha); - this.maxalpha = maxalpha; - this.maxTileAlphaSum = TILE_SIZE*TILE_SIZE*maxalpha; - } - - private static byte[] buildAlphaMap(int maxalpha) { - byte[] alMap = new byte[maxalpha+1]; - int halfmaxalpha = maxalpha>>2; - for (int i = 0; i <= maxalpha; i++) { - alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha); - } - return alMap; - } - - public static byte[] getAlphaMap(int maxalpha) { - if (!alphaMapsCache.containsKey(maxalpha)) { - alphaMapsCache.put(maxalpha, buildAlphaMap(maxalpha)); - } - return alphaMapsCache.get(maxalpha); - } - - public void getBbox(int bbox[]) { - cache.getBBox(bbox); - //System.out.println("bbox["+bbox[0]+", "+bbox[1]+" => "+bbox[2]+", "+bbox[3]+"]"); - } - - /** - * Gets the width of the tiles that the generator batches output into. - * @return the width of the standard alpha tile - */ - public int getTileWidth() { - return TILE_SIZE; - } - - /** - * Gets the height of the tiles that the generator batches output into. - * @return the height of the standard alpha tile - */ - public int getTileHeight() { - return TILE_SIZE; - } - - /** - * Gets the typical alpha value that will characterize the current - * tile. - * The answer may be 0x00 to indicate that the current tile has - * no coverage in any of its pixels, or it may be 0xff to indicate - * that the current tile is completely covered by the path, or any - * other value to indicate non-trivial coverage cases. - * @return 0x00 for no coverage, 0xff for total coverage, or any other - * value for partial coverage of the tile - */ - public int getTypicalAlpha() { - int al = cache.alphaSumInTile(x, y); - // Note: if we have a filled rectangle that doesn't end on a tile - // border, we could still return 0xff, even though al!=maxTileAlphaSum - // This is because if we return 0xff, our users will fill a rectangle - // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x), - // and height min(TILE_SIZE,bboxY1-y), which is what should happen. - // However, to support this, we would have to use 2 Math.min's - // and 2 multiplications per tile, instead of just 2 multiplications - // to compute maxTileAlphaSum. The savings offered would probably - // not be worth it, considering how rare this case is. - // Note: I have not tested this, so in the future if it is determined - // that it is worth it, it should be implemented. Perhaps this method's - // interface should be changed to take arguments the width and height - // of the current tile. This would eliminate the 2 Math.min calls that - // would be needed here, since our caller needs to compute these 2 - // values anyway. - return (al == 0x00 ? 0x00 : - (al == maxTileAlphaSum ? 0xff : 0x80)); - } - - /** - * Skips the current tile and moves on to the next tile. - * Either this method, or the getAlpha() method should be called - * once per tile, but not both. - */ - public void nextTile() { - if ((x += TILE_SIZE) >= cache.bboxX1) { - x = cache.bboxX0; - y += TILE_SIZE; - } - } - - /** - * Gets the alpha coverage values for the current tile. - * Either this method, or the nextTile() method should be called - * once per tile, but not both. - */ - public void getAlpha(byte tile[], int offset, int rowstride) { - // Decode run-length encoded alpha mask data - // The data for row j begins at cache.rowOffsetsRLE[j] - // and is encoded as a set of 2-byte pairs (val, runLen) - // terminated by a (0, 0) pair. - - int x0 = this.x; - int x1 = x0 + TILE_SIZE; - int y0 = this.y; - int y1 = y0 + TILE_SIZE; - if (x1 > cache.bboxX1) x1 = cache.bboxX1; - if (y1 > cache.bboxY1) y1 = cache.bboxY1; - y0 -= cache.bboxY0; - y1 -= cache.bboxY0; - - int idx = offset; - for (int cy = y0; cy < y1; cy++) { - int[] row = cache.rowAARLE[cy]; - assert row != null; - int cx = cache.minTouched(cy); - if (cx > x1) cx = x1; - - for (int i = x0; i < cx; i++) { - tile[idx++] = 0x00; - } - - int pos = 2; - while (cx < x1 && pos < row[1]) { - byte val; - int runLen = 0; - assert row[1] > 2; - try { - val = alphaMap[row[pos]]; - runLen = row[pos + 1]; - assert runLen > 0; - } catch (RuntimeException e0) { - System.out.println("maxalpha = "+maxalpha); - System.out.println("tile["+x0+", "+y0+ - " => "+x1+", "+y1+"]"); - System.out.println("cx = "+cx+", cy = "+cy); - System.out.println("idx = "+idx+", pos = "+pos); - System.out.println("len = "+runLen); - System.out.print(cache.toString()); - e0.printStackTrace(); - throw e0; - } - - int rx0 = cx; - cx += runLen; - int rx1 = cx; - if (rx0 < x0) rx0 = x0; - if (rx1 > x1) rx1 = x1; - runLen = rx1 - rx0; - //System.out.println("M["+runLen+"]"); - while (--runLen >= 0) { - try { - tile[idx++] = val; - } catch (RuntimeException e) { - System.out.println("maxalpha = "+maxalpha); - System.out.println("tile["+x0+", "+y0+ - " => "+x1+", "+y1+"]"); - System.out.println("cx = "+cx+", cy = "+cy); - System.out.println("idx = "+idx+", pos = "+pos); - System.out.println("rx0 = "+rx0+", rx1 = "+rx1); - System.out.println("len = "+runLen); - System.out.print(cache.toString()); - e.printStackTrace(); - throw e; - } - } - pos += 2; - } - if (cx < x0) { cx = x0; } - while (cx < x1) { - tile[idx++] = 0x00; - cx++; - } - /* - for (int i = idx - (x1-x0); i < idx; i++) { - System.out.print(hex(tile[i], 2)); - } - System.out.println(); - */ - idx += (rowstride - (x1-x0)); - } - nextTile(); - } - - static String hex(int v, int d) { - String s = Integer.toHexString(v); - while (s.length() < d) { - s = "0"+s; - } - return s.substring(0, d); - } - - /** - * Disposes this tile generator. - * No further calls will be made on this instance. - */ - public void dispose() {} -} - --- old/src/java.desktop/share/classes/sun/java2d/pisces/Renderer.java 2017-11-06 15:02:38.576239282 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,571 +0,0 @@ -/* - * Copyright (c) 2007, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import sun.awt.geom.PathConsumer2D; - -final class Renderer implements PathConsumer2D { - - private class ScanlineIterator { - - private int[] crossings; - - // crossing bounds. The bounds are not necessarily tight (the scan line - // at minY, for example, might have no crossings). The x bounds will - // be accumulated as crossings are computed. - private final int maxY; - private int nextY; - - // indices into the segment pointer lists. They indicate the "active" - // sublist in the segment lists (the portion of the list that contains - // all the segments that cross the next scan line). - private int edgeCount; - private int[] edgePtrs; - - private static final int INIT_CROSSINGS_SIZE = 10; - - // 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]; - edgePtrs = new int[INIT_CROSSINGS_SIZE]; - - nextY = start; - maxY = end; - edgeCount = 0; - } - - private int next() { - int cury = nextY++; - int bucket = cury - boundsMinY; - int count = this.edgeCount; - int ptrs[] = this.edgePtrs; - int bucketcount = edgeBucketCounts[bucket]; - if ((bucketcount & 0x1) != 0) { - int newCount = 0; - for (int i = 0; i < count; i++) { - int ecur = ptrs[i]; - if (edges[ecur+YMAX] > cury) { - ptrs[newCount++] = ecur; - } - } - count = newCount; - } - ptrs = Helpers.widenArray(ptrs, count, bucketcount >> 1); - for (int ecur = edgeBuckets[bucket]; ecur != NULL; ecur = (int)edges[ecur+NEXT]) { - ptrs[count++] = ecur; - // REMIND: Adjust start Y if necessary - } - this.edgePtrs = ptrs; - this.edgeCount = count; -// if ((count & 0x1) != 0) { -// System.out.println("ODD NUMBER OF EDGES!!!!"); -// } - int xings[] = this.crossings; - if (xings.length < count) { - this.crossings = xings = new int[ptrs.length]; - } - for (int i = 0; i < count; i++) { - int ecur = ptrs[i]; - float curx = edges[ecur+CURX]; - int cross = ((int) curx) << 1; - edges[ecur+CURX] = curx + edges[ecur+SLOPE]; - if (edges[ecur+OR] > 0) { - cross |= 1; - } - int j = i; - while (--j >= 0) { - int jcross = xings[j]; - if (jcross <= cross) { - break; - } - xings[j+1] = jcross; - ptrs[j+1] = ptrs[j]; - } - xings[j+1] = cross; - ptrs[j+1] = ecur; - } - return count; - } - - private boolean hasNext() { - return nextY < maxY; - } - - private int curY() { - return nextY - 1; - } - } - - -////////////////////////////////////////////////////////////////////////////// -// EDGE LIST -////////////////////////////////////////////////////////////////////////////// -// TODO(maybe): very tempting to use fixed point here. A lot of opportunities -// for shifts and just removing certain operations altogether. - - // common to all types of input path segments. - private static final int YMAX = 0; - private static final int CURX = 1; - // NEXT and OR are meant to be indices into "int" fields, but arrays must - // be homogenous, so every field is a float. However floats can represent - // exactly up to 26 bit ints, so we're ok. - private static final int OR = 2; - private static final int SLOPE = 3; - private static final int NEXT = 4; - - private float edgeMinY = Float.POSITIVE_INFINITY; - private float edgeMaxY = Float.NEGATIVE_INFINITY; - private float edgeMinX = Float.POSITIVE_INFINITY; - private float edgeMaxX = Float.NEGATIVE_INFINITY; - - private static final int SIZEOF_EDGE = 5; - // don't just set NULL to -1, because we want NULL+NEXT to be negative. - private static final int NULL = -SIZEOF_EDGE; - private float[] edges = null; - private static final int INIT_NUM_EDGES = 8; - private int[] edgeBuckets = null; - private int[] edgeBucketCounts = null; // 2*newedges + (1 if pruning needed) - private int numEdges; - - private static final float DEC_BND = 20f; - private static final float INC_BND = 8f; - - // each bucket is a linked list. this method adds eptr to the - // start of the "bucket"th linked list. - private void addEdgeToBucket(final int eptr, final int bucket) { - edges[eptr+NEXT] = edgeBuckets[bucket]; - edgeBuckets[bucket] = eptr; - edgeBucketCounts[bucket] += 2; - } - - // Flattens using adaptive forward differencing. This only carries out - // one iteration of the AFD loop. All it does is update AFD variables (i.e. - // X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings). - private void quadBreakIntoLinesAndAdd(float x0, float y0, - final Curve c, - final float x2, final float y2) - { - final float QUAD_DEC_BND = 32; - final int countlg = 4; - int count = 1 << countlg; - int countsq = count * count; - float maxDD = Math.max(c.dbx / countsq, c.dby / countsq); - while (maxDD > QUAD_DEC_BND) { - maxDD /= 4; - count <<= 1; - } - - countsq = count * count; - final float ddx = c.dbx / countsq; - final float ddy = c.dby / countsq; - float dx = c.bx / countsq + c.cx / count; - float dy = c.by / countsq + c.cy / count; - - while (count-- > 1) { - float x1 = x0 + dx; - dx += ddx; - float y1 = y0 + dy; - dy += ddy; - addLine(x0, y0, x1, y1); - x0 = x1; - y0 = y1; - } - addLine(x0, y0, x2, y2); - } - - // x0, y0 and x3,y3 are the endpoints of the curve. We could compute these - // using c.xat(0),c.yat(0) and c.xat(1),c.yat(1), but this might introduce - // numerical errors, and our callers already have the exact values. - // Another alternative would be to pass all the control points, and call c.set - // here, but then too many numbers are passed around. - private void curveBreakIntoLinesAndAdd(float x0, float y0, - final Curve c, - final float x3, final float y3) - { - final int countlg = 3; - int count = 1 << countlg; - - // the dx and dy refer to forward differencing variables, not the last - // coefficients of the "points" polynomial - float dddx, dddy, ddx, ddy, dx, dy; - dddx = 2f * c.dax / (1 << (3 * countlg)); - dddy = 2f * c.day / (1 << (3 * countlg)); - - ddx = dddx + c.dbx / (1 << (2 * countlg)); - ddy = dddy + c.dby / (1 << (2 * countlg)); - dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg); - dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg); - - // we use x0, y0 to walk the line - float x1 = x0, y1 = y0; - while (count > 0) { - while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) { - dddx /= 8; - dddy /= 8; - ddx = ddx/4 - dddx; - ddy = ddy/4 - dddy; - dx = (dx - ddx) / 2; - dy = (dy - ddy) / 2; - count <<= 1; - } - // can only do this on even "count" values, because we must divide count by 2 - while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) { - dx = 2 * dx + ddx; - dy = 2 * dy + ddy; - ddx = 4 * (ddx + dddx); - ddy = 4 * (ddy + dddy); - dddx = 8 * dddx; - dddy = 8 * dddy; - count >>= 1; - } - count--; - if (count > 0) { - x1 += dx; - dx += ddx; - ddx += dddx; - y1 += dy; - dy += ddy; - ddy += dddy; - } else { - x1 = x3; - y1 = y3; - } - addLine(x0, y0, x1, y1); - x0 = x1; - y0 = y1; - } - } - - private void addLine(float x1, float y1, float x2, float y2) { - float or = 1; // orientation of the line. 1 if y increases, 0 otherwise. - if (y2 < y1) { - or = y2; // no need to declare a temp variable. We have or. - y2 = y1; - y1 = or; - or = x2; - x2 = x1; - x1 = or; - or = 0; - } - final int firstCrossing = Math.max((int)Math.ceil(y1), boundsMinY); - final int lastCrossing = Math.min((int)Math.ceil(y2), boundsMaxY); - if (firstCrossing >= lastCrossing) { - return; - } - if (y1 < edgeMinY) { edgeMinY = y1; } - if (y2 > edgeMaxY) { edgeMaxY = y2; } - - final float slope = (x2 - x1) / (y2 - y1); - - if (slope > 0) { // <==> x1 < x2 - if (x1 < edgeMinX) { edgeMinX = x1; } - if (x2 > edgeMaxX) { edgeMaxX = x2; } - } else { - if (x2 < edgeMinX) { edgeMinX = x2; } - if (x1 > edgeMaxX) { edgeMaxX = x1; } - } - - final int ptr = numEdges * SIZEOF_EDGE; - edges = Helpers.widenArray(edges, ptr, SIZEOF_EDGE); - numEdges++; - edges[ptr+OR] = or; - edges[ptr+CURX] = x1 + (firstCrossing - y1) * slope; - edges[ptr+SLOPE] = slope; - edges[ptr+YMAX] = lastCrossing; - final int bucketIdx = firstCrossing - boundsMinY; - addEdgeToBucket(ptr, bucketIdx); - edgeBucketCounts[lastCrossing - boundsMinY] |= 1; - } - -// END EDGE LIST -////////////////////////////////////////////////////////////////////////////// - - - public static final int WIND_EVEN_ODD = 0; - public static final int WIND_NON_ZERO = 1; - - // Antialiasing - private final int SUBPIXEL_LG_POSITIONS_X; - private final int SUBPIXEL_LG_POSITIONS_Y; - private final int SUBPIXEL_POSITIONS_X; - private final int SUBPIXEL_POSITIONS_Y; - private final int SUBPIXEL_MASK_X; - private final int SUBPIXEL_MASK_Y; - final int MAX_AA_ALPHA; - - // Cache to store RLE-encoded coverage mask of the current primitive - PiscesCache cache; - - // Bounds of the drawing region, at subpixel precision. - private final int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY; - - // Current winding rule - private final int windingRule; - - // Current drawing position, i.e., final point of last segment - private float x0, y0; - - // Position of most recent 'moveTo' command - private float pix_sx0, pix_sy0; - - public Renderer(int subpixelLgPositionsX, int subpixelLgPositionsY, - int pix_boundsX, int pix_boundsY, - int pix_boundsWidth, int pix_boundsHeight, - int windingRule) - { - this.SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX; - this.SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY; - this.SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1; - this.SUBPIXEL_MASK_Y = (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1; - this.SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X); - this.SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y); - this.MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y); - - this.windingRule = windingRule; - - this.boundsMinX = pix_boundsX * SUBPIXEL_POSITIONS_X; - this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y; - this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X; - this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y; - - edges = new float[INIT_NUM_EDGES * SIZEOF_EDGE]; - numEdges = 0; - edgeBuckets = new int[boundsMaxY - boundsMinY]; - java.util.Arrays.fill(edgeBuckets, NULL); - edgeBucketCounts = new int[edgeBuckets.length + 1]; - } - - private float tosubpixx(float pix_x) { - return pix_x * SUBPIXEL_POSITIONS_X; - } - private float tosubpixy(float pix_y) { - return pix_y * SUBPIXEL_POSITIONS_Y; - } - - public void moveTo(float pix_x0, float pix_y0) { - closePath(); - this.pix_sx0 = pix_x0; - this.pix_sy0 = pix_y0; - this.y0 = tosubpixy(pix_y0); - this.x0 = tosubpixx(pix_x0); - } - - public void lineTo(float pix_x1, float pix_y1) { - float x1 = tosubpixx(pix_x1); - float y1 = tosubpixy(pix_y1); - addLine(x0, y0, x1, y1); - x0 = x1; - y0 = y1; - } - - private Curve c = new Curve(); - @Override public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - final float xe = tosubpixx(x3); - final float ye = tosubpixy(y3); - c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), tosubpixx(x2), tosubpixy(y2), xe, ye); - curveBreakIntoLinesAndAdd(x0, y0, c, xe, ye); - x0 = xe; - y0 = ye; - } - - @Override public void quadTo(float x1, float y1, float x2, float y2) { - final float xe = tosubpixx(x2); - final float ye = tosubpixy(y2); - c.set(x0, y0, tosubpixx(x1), tosubpixy(y1), xe, ye); - quadBreakIntoLinesAndAdd(x0, y0, c, xe, ye); - x0 = xe; - y0 = ye; - } - - public void closePath() { - // lineTo expects its input in pixel coordinates. - lineTo(pix_sx0, pix_sy0); - } - - public void pathDone() { - closePath(); - } - - - @Override - public long getNativeConsumer() { - throw new InternalError("Renderer does not use a native consumer."); - } - - private void _endRendering(final int pix_bboxx0, final int pix_bboxx1, - int ymin, int ymax) - { - // Mask to determine the relevant bit of the crossing sum - // 0x1 if EVEN_ODD, all bits if NON_ZERO - int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0; - - // add 2 to better deal with the last pixel in a pixel row. - int width = pix_bboxx1 - pix_bboxx0; - int[] alpha = new int[width+2]; - - int bboxx0 = pix_bboxx0 << SUBPIXEL_LG_POSITIONS_X; - int bboxx1 = pix_bboxx1 << SUBPIXEL_LG_POSITIONS_X; - - // Now we iterate through the scanlines. We must tell emitRow the coord - // of the first non-transparent pixel, so we must keep accumulators for - // the first and last pixels of the section of the current pixel row - // that we will emit. - // We also need to accumulate pix_bbox*, but the iterator does it - // for us. We will just get the values from it once this loop is done - int pix_maxX = Integer.MIN_VALUE; - int pix_minX = Integer.MAX_VALUE; - - int y = boundsMinY; // needs to be declared here so we emit the last row properly. - ScanlineIterator it = this.new ScanlineIterator(ymin, ymax); - for ( ; it.hasNext(); ) { - int numCrossings = it.next(); - int[] crossings = it.crossings; - y = it.curY(); - - if (numCrossings > 0) { - int lowx = crossings[0] >> 1; - int highx = crossings[numCrossings - 1] >> 1; - int x0 = Math.max(lowx, bboxx0); - int x1 = Math.min(highx, bboxx1); - - pix_minX = Math.min(pix_minX, x0 >> SUBPIXEL_LG_POSITIONS_X); - pix_maxX = Math.max(pix_maxX, x1 >> SUBPIXEL_LG_POSITIONS_X); - } - - int sum = 0; - int prev = bboxx0; - for (int i = 0; i < numCrossings; i++) { - int curxo = crossings[i]; - int curx = curxo >> 1; - // to turn {0, 1} into {-1, 1}, multiply by 2 and subtract 1. - int crorientation = ((curxo & 0x1) << 1) - 1; - if ((sum & mask) != 0) { - int x0 = Math.max(prev, bboxx0); - int x1 = Math.min(curx, bboxx1); - if (x0 < x1) { - x0 -= bboxx0; // turn x0, x1 from coords to indeces - x1 -= bboxx0; // in the alpha array. - - int pix_x = x0 >> SUBPIXEL_LG_POSITIONS_X; - int pix_xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X; - - if (pix_x == pix_xmaxm1) { - // Start and end in same pixel - alpha[pix_x] += (x1 - x0); - alpha[pix_x+1] -= (x1 - x0); - } else { - int pix_xmax = x1 >> SUBPIXEL_LG_POSITIONS_X; - alpha[pix_x] += SUBPIXEL_POSITIONS_X - (x0 & SUBPIXEL_MASK_X); - alpha[pix_x+1] += (x0 & SUBPIXEL_MASK_X); - alpha[pix_xmax] -= SUBPIXEL_POSITIONS_X - (x1 & SUBPIXEL_MASK_X); - alpha[pix_xmax+1] -= (x1 & SUBPIXEL_MASK_X); - } - } - } - sum += crorientation; - prev = curx; - } - - // even if this last row had no crossings, alpha will be zeroed - // from the last emitRow call. But this doesn't matter because - // maxX < minX, so no row will be emitted to the cache. - if ((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) { - emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX); - pix_minX = Integer.MAX_VALUE; - pix_maxX = Integer.MIN_VALUE; - } - } - - // Emit final row - if (pix_maxX >= pix_minX) { - emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX); - } - } - - public void endRendering() { - int spminX = Math.max((int)Math.ceil(edgeMinX), boundsMinX); - int spmaxX = Math.min((int)Math.ceil(edgeMaxX), boundsMaxX); - int spminY = Math.max((int)Math.ceil(edgeMinY), boundsMinY); - int spmaxY = Math.min((int)Math.ceil(edgeMaxY), boundsMaxY); - - int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X; - int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X; - int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y; - int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y; - - if (pminX > pmaxX || pminY > pmaxY) { - this.cache = new PiscesCache(boundsMinX >> SUBPIXEL_LG_POSITIONS_X, - boundsMinY >> SUBPIXEL_LG_POSITIONS_Y, - boundsMaxX >> SUBPIXEL_LG_POSITIONS_X, - boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y); - return; - } - - this.cache = new PiscesCache(pminX, pminY, pmaxX, pmaxY); - _endRendering(pminX, pmaxX, spminY, spmaxY); - } - - public PiscesCache getCache() { - if (cache == null) { - throw new InternalError("cache not yet initialized"); - } - return cache; - } - - private void emitRow(int[] alphaRow, int pix_y, int pix_from, int pix_to) { - // Copy rowAA data into the cache if one is present - if (cache != null) { - if (pix_to >= pix_from) { - cache.startRow(pix_y, pix_from); - - // Perform run-length encoding and store results in the cache - int from = pix_from - cache.bboxX0; - int to = pix_to - cache.bboxX0; - - int runLen = 1; - int startVal = alphaRow[from]; - for (int i = from + 1; i <= to; i++) { - int nextVal = startVal + alphaRow[i]; - if (nextVal == startVal) { - runLen++; - } else { - cache.addRLERun(startVal, runLen); - runLen = 1; - startVal = nextVal; - } - } - cache.addRLERun(startVal, runLen); - } - } - java.util.Arrays.fill(alphaRow, 0); - } -} --- old/src/java.desktop/share/classes/sun/java2d/pisces/Stroker.java 2017-11-06 15:02:38.868239274 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,1231 +0,0 @@ -/* - * Copyright (c) 2007, 2015, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import java.util.Arrays; -import java.util.Iterator; -import static java.lang.Math.ulp; -import static java.lang.Math.sqrt; - -import sun.awt.geom.PathConsumer2D; - -// TODO: some of the arithmetic here is too verbose and prone to hard to -// debug typos. We should consider making a small Point/Vector class that -// has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such -final class Stroker implements PathConsumer2D { - - private static final int MOVE_TO = 0; - private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad - private static final int CLOSE = 2; - - /** - * Constant value for join style. - */ - public static final int JOIN_MITER = 0; - - /** - * Constant value for join style. - */ - public static final int JOIN_ROUND = 1; - - /** - * Constant value for join style. - */ - public static final int JOIN_BEVEL = 2; - - /** - * Constant value for end cap style. - */ - public static final int CAP_BUTT = 0; - - /** - * Constant value for end cap style. - */ - public static final int CAP_ROUND = 1; - - /** - * Constant value for end cap style. - */ - public static final int CAP_SQUARE = 2; - - private final PathConsumer2D out; - - private final int capStyle; - private final int joinStyle; - - private final float lineWidth2; - - private final float[][] offset = new float[3][2]; - private final float[] miter = new float[2]; - private final float miterLimitSq; - - private int prev; - - // The starting point of the path, and the slope there. - private float sx0, sy0, sdx, sdy; - // the current point and the slope there. - private float cx0, cy0, cdx, cdy; // c stands for current - // vectors that when added to (sx0,sy0) and (cx0,cy0) respectively yield the - // first and last points on the left parallel path. Since this path is - // parallel, it's slope at any point is parallel to the slope of the - // original path (thought they may have different directions), so these - // could be computed from sdx,sdy and cdx,cdy (and vice versa), but that - // would be error prone and hard to read, so we keep these anyway. - private float smx, smy, cmx, cmy; - - private final PolyStack reverse = new PolyStack(); - - /** - * Constructs a {@code Stroker}. - * - * @param pc2d an output {@code PathConsumer2D}. - * @param lineWidth the desired line width in pixels - * @param capStyle the desired end cap style, one of - * {@code CAP_BUTT}, {@code CAP_ROUND} or - * {@code CAP_SQUARE}. - * @param joinStyle the desired line join style, one of - * {@code JOIN_MITER}, {@code JOIN_ROUND} or - * {@code JOIN_BEVEL}. - * @param miterLimit the desired miter limit - */ - public Stroker(PathConsumer2D pc2d, - float lineWidth, - int capStyle, - int joinStyle, - float miterLimit) - { - this.out = pc2d; - - this.lineWidth2 = lineWidth / 2; - this.capStyle = capStyle; - this.joinStyle = joinStyle; - - float limit = miterLimit * lineWidth2; - this.miterLimitSq = limit*limit; - - this.prev = CLOSE; - } - - private static void computeOffset(final float lx, final float ly, - final float w, final float[] m) - { - final float len = (float) sqrt(lx*lx + ly*ly); - if (len == 0) { - m[0] = m[1] = 0; - } else { - m[0] = (ly * w)/len; - m[1] = -(lx * w)/len; - } - } - - // Returns true if the vectors (dx1, dy1) and (dx2, dy2) are - // clockwise (if dx1,dy1 needs to be rotated clockwise to close - // the smallest angle between it and dx2,dy2). - // This is equivalent to detecting whether a point q is on the right side - // of a line passing through points p1, p2 where p2 = p1+(dx1,dy1) and - // q = p2+(dx2,dy2), which is the same as saying p1, p2, q are in a - // clockwise order. - // NOTE: "clockwise" here assumes coordinates with 0,0 at the bottom left. - private static boolean isCW(final float dx1, final float dy1, - final float dx2, final float dy2) - { - return dx1 * dy2 <= dy1 * dx2; - } - - // pisces used to use fixed point arithmetic with 16 decimal digits. I - // didn't want to change the values of the constant below when I converted - // it to floating point, so that's why the divisions by 2^16 are there. - private static final float ROUND_JOIN_THRESHOLD = 1000/65536f; - - private void drawRoundJoin(float x, float y, - float omx, float omy, float mx, float my, - boolean rev, - float threshold) - { - if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) { - return; - } - - float domx = omx - mx; - float domy = omy - my; - float len = domx*domx + domy*domy; - if (len < threshold) { - return; - } - - if (rev) { - omx = -omx; - omy = -omy; - mx = -mx; - my = -my; - } - drawRoundJoin(x, y, omx, omy, mx, my, rev); - } - - private void drawRoundJoin(float cx, float cy, - float omx, float omy, - float mx, float my, - boolean rev) - { - // The sign of the dot product of mx,my and omx,omy is equal to the - // the sign of the cosine of ext - // (ext is the angle between omx,omy and mx,my). - final float cosext = omx * mx + omy * my; - // If it is >=0, we know that abs(ext) is <= 90 degrees, so we only - // need 1 curve to approximate the circle section that joins omx,omy - // and mx,my. - final int numCurves = (cosext >= 0f) ? 1 : 2; - - switch (numCurves) { - case 1: - drawBezApproxForArc(cx, cy, omx, omy, mx, my, rev); - break; - case 2: - // we need to split the arc into 2 arcs spanning the same angle. - // The point we want will be one of the 2 intersections of the - // perpendicular bisector of the chord (omx,omy)->(mx,my) and the - // circle. We could find this by scaling the vector - // (omx+mx, omy+my)/2 so that it has length=lineWidth2 (and thus lies - // on the circle), but that can have numerical problems when the angle - // between omx,omy and mx,my is close to 180 degrees. So we compute a - // normal of (omx,omy)-(mx,my). This will be the direction of the - // perpendicular bisector. To get one of the intersections, we just scale - // this vector that its length is lineWidth2 (this works because the - // perpendicular bisector goes through the origin). This scaling doesn't - // have numerical problems because we know that lineWidth2 divided by - // this normal's length is at least 0.5 and at most sqrt(2)/2 (because - // we know the angle of the arc is > 90 degrees). - float nx = my - omy, ny = omx - mx; - float nlen = (float) sqrt(nx*nx + ny*ny); - float scale = lineWidth2/nlen; - float mmx = nx * scale, mmy = ny * scale; - - // if (isCW(omx, omy, mx, my) != isCW(mmx, mmy, mx, my)) then we've - // computed the wrong intersection so we get the other one. - // The test above is equivalent to if (rev). - if (rev) { - mmx = -mmx; - mmy = -mmy; - } - drawBezApproxForArc(cx, cy, omx, omy, mmx, mmy, rev); - drawBezApproxForArc(cx, cy, mmx, mmy, mx, my, rev); - break; - } - } - - // the input arc defined by omx,omy and mx,my must span <= 90 degrees. - private void drawBezApproxForArc(final float cx, final float cy, - final float omx, final float omy, - final float mx, final float my, - boolean rev) - { - final float cosext2 = (omx * mx + omy * my) / (2f * lineWidth2 * lineWidth2); - - // check round off errors producing cos(ext) > 1 and a NaN below - // cos(ext) == 1 implies colinear segments and an empty join anyway - if (cosext2 >= 0.5f) { - // just return to avoid generating a flat curve: - return; - } - - // cv is the length of P1-P0 and P2-P3 divided by the radius of the arc - // (so, cv assumes the arc has radius 1). P0, P1, P2, P3 are the points that - // define the bezier curve we're computing. - // It is computed using the constraints that P1-P0 and P3-P2 are parallel - // to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|. - float cv = (float) ((4.0 / 3.0) * sqrt(0.5 - cosext2) / - (1.0 + sqrt(cosext2 + 0.5))); - // if clockwise, we need to negate cv. - if (rev) { // rev is equivalent to isCW(omx, omy, mx, my) - cv = -cv; - } - final float x1 = cx + omx; - final float y1 = cy + omy; - final float x2 = x1 - cv * omy; - final float y2 = y1 + cv * omx; - - final float x4 = cx + mx; - final float y4 = cy + my; - final float x3 = x4 + cv * my; - final float y3 = y4 - cv * mx; - - emitCurveTo(x1, y1, x2, y2, x3, y3, x4, y4, rev); - } - - private void drawRoundCap(float cx, float cy, float mx, float my) { - final float C = 0.5522847498307933f; - // the first and second arguments of the following two calls - // are really will be ignored by emitCurveTo (because of the false), - // but we put them in anyway, as opposed to just giving it 4 zeroes, - // because it's just 4 additions and it's not good to rely on this - // sort of assumption (right now it's true, but that may change). - emitCurveTo(cx+mx, cy+my, - cx+mx-C*my, cy+my+C*mx, - cx-my+C*mx, cy+mx+C*my, - cx-my, cy+mx, - false); - emitCurveTo(cx-my, cy+mx, - cx-my-C*mx, cy+mx-C*my, - cx-mx-C*my, cy-my+C*mx, - cx-mx, cy-my, - false); - } - - // Put the intersection point of the lines (x0, y0) -> (x1, y1) - // and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]. - // If the lines are parallel, it will put a non finite number in m. - private void computeIntersection(final float x0, final float y0, - final float x1, final float y1, - final float x0p, final float y0p, - final float x1p, final float y1p, - final float[] m, int off) - { - float x10 = x1 - x0; - float y10 = y1 - y0; - float x10p = x1p - x0p; - float y10p = y1p - y0p; - - float den = x10*y10p - x10p*y10; - float t = x10p*(y0-y0p) - y10p*(x0-x0p); - t /= den; - m[off++] = x0 + t*x10; - m[off] = y0 + t*y10; - } - - private void drawMiter(final float pdx, final float pdy, - final float x0, final float y0, - final float dx, final float dy, - float omx, float omy, float mx, float my, - boolean rev) - { - if ((mx == omx && my == omy) || - (pdx == 0 && pdy == 0) || - (dx == 0 && dy == 0)) - { - return; - } - - if (rev) { - omx = -omx; - omy = -omy; - mx = -mx; - my = -my; - } - - computeIntersection((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy, - (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, - miter, 0); - - float lenSq = (miter[0]-x0)*(miter[0]-x0) + (miter[1]-y0)*(miter[1]-y0); - - // If the lines are parallel, lenSq will be either NaN or +inf - // (actually, I'm not sure if the latter is possible. The important - // thing is that -inf is not possible, because lenSq is a square). - // For both of those values, the comparison below will fail and - // no miter will be drawn, which is correct. - if (lenSq < miterLimitSq) { - emitLineTo(miter[0], miter[1], rev); - } - } - - public void moveTo(float x0, float y0) { - if (prev == DRAWING_OP_TO) { - finish(); - } - this.sx0 = this.cx0 = x0; - this.sy0 = this.cy0 = y0; - this.cdx = this.sdx = 1; - this.cdy = this.sdy = 0; - this.prev = MOVE_TO; - } - - public void lineTo(float x1, float y1) { - float dx = x1 - cx0; - float dy = y1 - cy0; - if (dx == 0f && dy == 0f) { - dx = 1; - } - computeOffset(dx, dy, lineWidth2, offset[0]); - float mx = offset[0][0]; - float my = offset[0][1]; - - drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my); - - emitLineTo(cx0 + mx, cy0 + my); - emitLineTo(x1 + mx, y1 + my); - - emitLineTo(cx0 - mx, cy0 - my, true); - emitLineTo(x1 - mx, y1 - my, true); - - this.cmx = mx; - this.cmy = my; - this.cdx = dx; - this.cdy = dy; - this.cx0 = x1; - this.cy0 = y1; - this.prev = DRAWING_OP_TO; - } - - public void closePath() { - if (prev != DRAWING_OP_TO) { - if (prev == CLOSE) { - return; - } - emitMoveTo(cx0, cy0 - lineWidth2); - this.cmx = this.smx = 0; - this.cmy = this.smy = -lineWidth2; - this.cdx = this.sdx = 1; - this.cdy = this.sdy = 0; - finish(); - return; - } - - if (cx0 != sx0 || cy0 != sy0) { - lineTo(sx0, sy0); - } - - drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy); - - emitLineTo(sx0 + smx, sy0 + smy); - - emitMoveTo(sx0 - smx, sy0 - smy); - emitReverse(); - - this.prev = CLOSE; - emitClose(); - } - - private void emitReverse() { - while(!reverse.isEmpty()) { - reverse.pop(out); - } - } - - public void pathDone() { - if (prev == DRAWING_OP_TO) { - finish(); - } - - out.pathDone(); - // this shouldn't matter since this object won't be used - // after the call to this method. - this.prev = CLOSE; - } - - private void finish() { - if (capStyle == CAP_ROUND) { - drawRoundCap(cx0, cy0, cmx, cmy); - } else if (capStyle == CAP_SQUARE) { - emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy); - emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy); - } - - emitReverse(); - - if (capStyle == CAP_ROUND) { - drawRoundCap(sx0, sy0, -smx, -smy); - } else if (capStyle == CAP_SQUARE) { - emitLineTo(sx0 + smy - smx, sy0 - smx - smy); - emitLineTo(sx0 + smy + smx, sy0 - smx + smy); - } - - emitClose(); - } - - private void emitMoveTo(final float x0, final float y0) { - out.moveTo(x0, y0); - } - - private void emitLineTo(final float x1, final float y1) { - out.lineTo(x1, y1); - } - - private void emitLineTo(final float x1, final float y1, - final boolean rev) - { - if (rev) { - reverse.pushLine(x1, y1); - } else { - emitLineTo(x1, y1); - } - } - - private void emitQuadTo(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2, final boolean rev) - { - if (rev) { - reverse.pushQuad(x0, y0, x1, y1); - } else { - out.quadTo(x1, y1, x2, y2); - } - } - - private void emitCurveTo(final float x0, final float y0, - final float x1, final float y1, - final float x2, final float y2, - final float x3, final float y3, final boolean rev) - { - if (rev) { - reverse.pushCubic(x0, y0, x1, y1, x2, y2); - } else { - out.curveTo(x1, y1, x2, y2, x3, y3); - } - } - - private void emitClose() { - out.closePath(); - } - - private void drawJoin(float pdx, float pdy, - float x0, float y0, - float dx, float dy, - float omx, float omy, - float mx, float my) - { - if (prev != DRAWING_OP_TO) { - emitMoveTo(x0 + mx, y0 + my); - this.sdx = dx; - this.sdy = dy; - this.smx = mx; - this.smy = my; - } else { - boolean cw = isCW(pdx, pdy, dx, dy); - if (joinStyle == JOIN_MITER) { - drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw); - } else if (joinStyle == JOIN_ROUND) { - drawRoundJoin(x0, y0, - omx, omy, - mx, my, cw, - ROUND_JOIN_THRESHOLD); - } - emitLineTo(x0, y0, !cw); - } - prev = DRAWING_OP_TO; - } - - private static boolean within(final float x1, final float y1, - final float x2, final float y2, - final float ERR) - { - assert ERR > 0 : ""; - // compare taxicab distance. ERR will always be small, so using - // true distance won't give much benefit - return (Helpers.within(x1, x2, ERR) && // we want to avoid calling Math.abs - Helpers.within(y1, y2, ERR)); // this is just as good. - } - - private void getLineOffsets(float x1, float y1, - float x2, float y2, - float[] left, float[] right) { - computeOffset(x2 - x1, y2 - y1, lineWidth2, offset[0]); - left[0] = x1 + offset[0][0]; - left[1] = y1 + offset[0][1]; - left[2] = x2 + offset[0][0]; - left[3] = y2 + offset[0][1]; - right[0] = x1 - offset[0][0]; - right[1] = y1 - offset[0][1]; - right[2] = x2 - offset[0][0]; - right[3] = y2 - offset[0][1]; - } - - private int computeOffsetCubic(float[] pts, final int off, - float[] leftOff, float[] rightOff) - { - // if p1=p2 or p3=p4 it means that the derivative at the endpoint - // vanishes, which creates problems with computeOffset. Usually - // this happens when this stroker object is trying to winden - // a curve with a cusp. What happens is that curveTo splits - // the input curve at the cusp, and passes it to this function. - // because of inaccuracies in the splitting, we consider points - // equal if they're very close to each other. - final float x1 = pts[off + 0], y1 = pts[off + 1]; - final float x2 = pts[off + 2], y2 = pts[off + 3]; - final float x3 = pts[off + 4], y3 = pts[off + 5]; - final float x4 = pts[off + 6], y4 = pts[off + 7]; - - float dx4 = x4 - x3; - float dy4 = y4 - y3; - float dx1 = x2 - x1; - float dy1 = y2 - y1; - - // if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4, - // in which case ignore if p1 == p2 - final boolean p1eqp2 = within(x1,y1,x2,y2, 6 * ulp(y2)); - final boolean p3eqp4 = within(x3,y3,x4,y4, 6 * ulp(y4)); - if (p1eqp2 && p3eqp4) { - getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); - return 4; - } else if (p1eqp2) { - dx1 = x3 - x1; - dy1 = y3 - y1; - } else if (p3eqp4) { - dx4 = x4 - x2; - dy4 = y4 - y2; - } - - // if p2-p1 and p4-p3 are parallel, that must mean this curve is a line - float dotsq = (dx1 * dx4 + dy1 * dy4); - dotsq = dotsq * dotsq; - float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4; - if (Helpers.within(dotsq, l1sq * l4sq, 4 * ulp(dotsq))) { - getLineOffsets(x1, y1, x4, y4, leftOff, rightOff); - return 4; - } - -// What we're trying to do in this function is to approximate an ideal -// offset curve (call it I) of the input curve B using a bezier curve Bp. -// The constraints I use to get the equations are: -// -// 1. The computed curve Bp should go through I(0) and I(1). These are -// x1p, y1p, x4p, y4p, which are p1p and p4p. We still need to find -// 4 variables: the x and y components of p2p and p3p (i.e. x2p, y2p, x3p, y3p). -// -// 2. Bp should have slope equal in absolute value to I at the endpoints. So, -// (by the way, the operator || in the comments below means "aligned with". -// It is defined on vectors, so when we say I'(0) || Bp'(0) we mean that -// vectors I'(0) and Bp'(0) are aligned, which is the same as saying -// that the tangent lines of I and Bp at 0 are parallel. Mathematically -// this means (I'(t) || Bp'(t)) <==> (I'(t) = c * Bp'(t)) where c is some -// nonzero constant.) -// I'(0) || Bp'(0) and I'(1) || Bp'(1). Obviously, I'(0) || B'(0) and -// I'(1) || B'(1); therefore, Bp'(0) || B'(0) and Bp'(1) || B'(1). -// We know that Bp'(0) || (p2p-p1p) and Bp'(1) || (p4p-p3p) and the same -// is true for any bezier curve; therefore, we get the equations -// (1) p2p = c1 * (p2-p1) + p1p -// (2) p3p = c2 * (p4-p3) + p4p -// We know p1p, p4p, p2, p1, p3, and p4; therefore, this reduces the number -// of unknowns from 4 to 2 (i.e. just c1 and c2). -// To eliminate these 2 unknowns we use the following constraint: -// -// 3. Bp(0.5) == I(0.5). Bp(0.5)=(x,y) and I(0.5)=(xi,yi), and I should note -// that I(0.5) is *the only* reason for computing dxm,dym. This gives us -// (3) Bp(0.5) = (p1p + 3 * (p2p + p3p) + p4p)/8, which is equivalent to -// (4) p2p + p3p = (Bp(0.5)*8 - p1p - p4p) / 3 -// We can substitute (1) and (2) from above into (4) and we get: -// (5) c1*(p2-p1) + c2*(p4-p3) = (Bp(0.5)*8 - p1p - p4p)/3 - p1p - p4p -// which is equivalent to -// (6) c1*(p2-p1) + c2*(p4-p3) = (4/3) * (Bp(0.5) * 2 - p1p - p4p) -// -// The right side of this is a 2D vector, and we know I(0.5), which gives us -// Bp(0.5), which gives us the value of the right side. -// The left side is just a matrix vector multiplication in disguise. It is -// -// [x2-x1, x4-x3][c1] -// [y2-y1, y4-y3][c2] -// which, is equal to -// [dx1, dx4][c1] -// [dy1, dy4][c2] -// At this point we are left with a simple linear system and we solve it by -// getting the inverse of the matrix above. Then we use [c1,c2] to compute -// p2p and p3p. - - float x = 0.125f * (x1 + 3 * (x2 + x3) + x4); - float y = 0.125f * (y1 + 3 * (y2 + y3) + y4); - // (dxm,dym) is some tangent of B at t=0.5. This means it's equal to - // c*B'(0.5) for some constant c. - float dxm = x3 + x4 - x1 - x2, dym = y3 + y4 - y1 - y2; - - // this computes the offsets at t=0, 0.5, 1, using the property that - // for any bezier curve the vectors p2-p1 and p4-p3 are parallel to - // the (dx/dt, dy/dt) vectors at the endpoints. - computeOffset(dx1, dy1, lineWidth2, offset[0]); - computeOffset(dxm, dym, lineWidth2, offset[1]); - computeOffset(dx4, dy4, lineWidth2, offset[2]); - float x1p = x1 + offset[0][0]; // start - float y1p = y1 + offset[0][1]; // point - float xi = x + offset[1][0]; // interpolation - float yi = y + offset[1][1]; // point - float x4p = x4 + offset[2][0]; // end - float y4p = y4 + offset[2][1]; // point - - float invdet43 = 4f / (3f * (dx1 * dy4 - dy1 * dx4)); - - float two_pi_m_p1_m_p4x = 2*xi - x1p - x4p; - float two_pi_m_p1_m_p4y = 2*yi - y1p - y4p; - float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y); - float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x); - - float x2p, y2p, x3p, y3p; - x2p = x1p + c1*dx1; - y2p = y1p + c1*dy1; - x3p = x4p + c2*dx4; - y3p = y4p + c2*dy4; - - leftOff[0] = x1p; leftOff[1] = y1p; - leftOff[2] = x2p; leftOff[3] = y2p; - leftOff[4] = x3p; leftOff[5] = y3p; - leftOff[6] = x4p; leftOff[7] = y4p; - - x1p = x1 - offset[0][0]; y1p = y1 - offset[0][1]; - xi = xi - 2 * offset[1][0]; yi = yi - 2 * offset[1][1]; - x4p = x4 - offset[2][0]; y4p = y4 - offset[2][1]; - - two_pi_m_p1_m_p4x = 2*xi - x1p - x4p; - two_pi_m_p1_m_p4y = 2*yi - y1p - y4p; - c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y); - c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x); - - x2p = x1p + c1*dx1; - y2p = y1p + c1*dy1; - x3p = x4p + c2*dx4; - y3p = y4p + c2*dy4; - - rightOff[0] = x1p; rightOff[1] = y1p; - rightOff[2] = x2p; rightOff[3] = y2p; - rightOff[4] = x3p; rightOff[5] = y3p; - rightOff[6] = x4p; rightOff[7] = y4p; - return 8; - } - - // return the kind of curve in the right and left arrays. - private int computeOffsetQuad(float[] pts, final int off, - float[] leftOff, float[] rightOff) - { - final float x1 = pts[off + 0], y1 = pts[off + 1]; - final float x2 = pts[off + 2], y2 = pts[off + 3]; - final float x3 = pts[off + 4], y3 = pts[off + 5]; - - final float dx3 = x3 - x2; - final float dy3 = y3 - y2; - final float dx1 = x2 - x1; - final float dy1 = y2 - y1; - - // this computes the offsets at t = 0, 1 - computeOffset(dx1, dy1, lineWidth2, offset[0]); - computeOffset(dx3, dy3, lineWidth2, offset[1]); - - leftOff[0] = x1 + offset[0][0]; leftOff[1] = y1 + offset[0][1]; - leftOff[4] = x3 + offset[1][0]; leftOff[5] = y3 + offset[1][1]; - rightOff[0] = x1 - offset[0][0]; rightOff[1] = y1 - offset[0][1]; - rightOff[4] = x3 - offset[1][0]; rightOff[5] = y3 - offset[1][1]; - - float x1p = leftOff[0]; // start - float y1p = leftOff[1]; // point - float x3p = leftOff[4]; // end - float y3p = leftOff[5]; // point - - // Corner cases: - // 1. If the two control vectors are parallel, we'll end up with NaN's - // in leftOff (and rightOff in the body of the if below), so we'll - // do getLineOffsets, which is right. - // 2. If the first or second two points are equal, then (dx1,dy1)==(0,0) - // or (dx3,dy3)==(0,0), so (x1p, y1p)==(x1p+dx1, y1p+dy1) - // or (x3p, y3p)==(x3p-dx3, y3p-dy3), which means that - // computeIntersection will put NaN's in leftOff and right off, and - // we will do getLineOffsets, which is right. - computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2); - float cx = leftOff[2]; - float cy = leftOff[3]; - - if (!(isFinite(cx) && isFinite(cy))) { - // maybe the right path is not degenerate. - x1p = rightOff[0]; - y1p = rightOff[1]; - x3p = rightOff[4]; - y3p = rightOff[5]; - computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2); - cx = rightOff[2]; - cy = rightOff[3]; - if (!(isFinite(cx) && isFinite(cy))) { - // both are degenerate. This curve is a line. - getLineOffsets(x1, y1, x3, y3, leftOff, rightOff); - return 4; - } - // {left,right}Off[0,1,4,5] are already set to the correct values. - leftOff[2] = 2*x2 - cx; - leftOff[3] = 2*y2 - cy; - return 6; - } - - // rightOff[2,3] = (x2,y2) - ((left_x2, left_y2) - (x2, y2)) - // == 2*(x2, y2) - (left_x2, left_y2) - rightOff[2] = 2*x2 - cx; - rightOff[3] = 2*y2 - cy; - return 6; - } - - private static boolean isFinite(float x) { - return (Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY); - } - - // This is where the curve to be processed is put. We give it - // enough room to store 2 curves: one for the current subdivision, the - // other for the rest of the curve. - private float[] middle = new float[2*8]; - private float[] lp = new float[8]; - private float[] rp = new float[8]; - private static final int MAX_N_CURVES = 11; - 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) { - // need these so we can update the state at the end of this method - final float xf = middle[type-2], yf = middle[type-1]; - float dxs = middle[2] - middle[0]; - float dys = middle[3] - middle[1]; - float dxf = middle[type - 2] - middle[type - 4]; - float dyf = middle[type - 1] - middle[type - 3]; - switch(type) { - case 6: - if ((dxs == 0f && dys == 0f) || - (dxf == 0f && dyf == 0f)) { - dxs = dxf = middle[4] - middle[0]; - dys = dyf = middle[5] - middle[1]; - } - break; - case 8: - 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) sqrt(dxs*dxs + dys*dys); - dxs /= len; - dys /= len; - } - if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) { - float len = (float) 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, type, lineWidth2); - - int kind = 0; - Iterator it = Curve.breakPtsAtTs(middle, type, subdivTs, nSplits); - while(it.hasNext()) { - int curCurveOff = it.next(); - - switch (type) { - case 8: - kind = computeOffsetCubic(middle, curCurveOff, lp, rp); - break; - case 6: - kind = computeOffsetQuad(middle, curCurveOff, lp, rp); - break; - } - 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 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; - } -****************************** END WORKAROUND *******************************/ - - // 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. - // Stores the points in ts, and returns how many of them there were. - private static Curve c = new Curve(); - private static int findSubdivPoints(float[] pts, float[] ts, final int type, final float w) - { - final float x12 = pts[2] - pts[0]; - final float y12 = pts[3] - pts[1]; - // if the curve is already parallel to either axis we gain nothing - // from rotating it. - if (y12 != 0f && x12 != 0f) { - // we rotate it so that the first vector in the control polygon is - // parallel to the x-axis. This will ensure that rotated quarter - // circles won't be subdivided. - final float hypot = (float) sqrt(x12 * x12 + y12 * y12); - final float cos = x12 / hypot; - final float sin = y12 / hypot; - final float x1 = cos * pts[0] + sin * pts[1]; - final float y1 = cos * pts[1] - sin * pts[0]; - final float x2 = cos * pts[2] + sin * pts[3]; - final float y2 = cos * pts[3] - sin * pts[2]; - final float x3 = cos * pts[4] + sin * pts[5]; - final float y3 = cos * pts[5] - sin * pts[4]; - switch(type) { - case 8: - final float x4 = cos * pts[6] + sin * pts[7]; - final float y4 = cos * pts[7] - sin * pts[6]; - c.set(x1, y1, x2, y2, x3, y3, x4, y4); - break; - case 6: - c.set(x1, y1, x2, y2, x3, y3); - break; - } - } else { - c.set(pts, type); - } - - int ret = 0; - // we subdivide at values of t such that the remaining rotated - // curves are monotonic in x and y. - ret += c.dxRoots(ts, ret); - ret += c.dyRoots(ts, ret); - // subdivide at inflection points. - if (type == 8) { - // quadratic curves can't have inflection points - ret += c.infPoints(ts, ret); - } - - // now we must subdivide at points where one of the offset curves will have - // a cusp. This happens at ts where the radius of curvature is equal to w. - ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f); - - ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f); - Helpers.isort(ts, 0, ret); - return ret; - } - - @Override public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - middle[0] = cx0; middle[1] = cy0; - middle[2] = x1; middle[3] = y1; - middle[4] = x2; middle[5] = y2; - middle[6] = x3; middle[7] = y3; - - // 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[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) sqrt(dxs*dxs + dys*dys); - dxs /= len; - dys /= len; - } - if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) { - float len = (float) 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 it = Curve.breakPtsAtTs(middle, 8, subdivTs, nSplits); - while(it.hasNext()) { - int curCurveOff = it.next(); - - kind = computeOffsetCubic(middle, curCurveOff, lp, rp); - 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) { - middle[0] = cx0; middle[1] = cy0; - middle[2] = x1; middle[3] = y1; - middle[4] = x2; middle[5] = y2; - - // 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) sqrt(dxs*dxs + dys*dys); - dxs /= len; - dys /= len; - } - if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) { - float len = (float) 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 it = Curve.breakPtsAtTs(middle, 6, subdivTs, nSplits); - while(it.hasNext()) { - int curCurveOff = it.next(); - - kind = computeOffsetQuad(middle, curCurveOff, lp, rp); - 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 - // adjacent ones. - private static final class PolyStack { - float[] curves; - int end; - int[] curveTypes; - int numCurves; - - private static final int INIT_SIZE = 50; - - PolyStack() { - curves = new float[8 * INIT_SIZE]; - curveTypes = new int[INIT_SIZE]; - end = 0; - numCurves = 0; - } - - public boolean isEmpty() { - return numCurves == 0; - } - - private void ensureSpace(int n) { - if (end + n >= curves.length) { - int newSize = (end + n) * 2; - curves = Arrays.copyOf(curves, newSize); - } - if (numCurves >= curveTypes.length) { - int newSize = numCurves * 2; - curveTypes = Arrays.copyOf(curveTypes, newSize); - } - } - - public void pushCubic(float x0, float y0, - float x1, float y1, - float x2, float y2) - { - ensureSpace(6); - curveTypes[numCurves++] = 8; - // assert(x0 == lastX && y0 == lastY) - - // we reverse the coordinate order to make popping easier - curves[end++] = x2; curves[end++] = y2; - curves[end++] = x1; curves[end++] = y1; - curves[end++] = x0; curves[end++] = y0; - } - - public void pushQuad(float x0, float y0, - float x1, float y1) - { - ensureSpace(4); - curveTypes[numCurves++] = 6; - // assert(x0 == lastX && y0 == lastY) - curves[end++] = x1; curves[end++] = y1; - curves[end++] = x0; curves[end++] = y0; - } - - public void pushLine(float x, float y) { - ensureSpace(2); - curveTypes[numCurves++] = 4; - // assert(x0 == lastX && y0 == lastY) - curves[end++] = x; curves[end++] = y; - } - - @SuppressWarnings("unused") - public int pop(float[] pts) { - int ret = curveTypes[numCurves - 1]; - numCurves--; - end -= (ret - 2); - System.arraycopy(curves, end, pts, 0, ret - 2); - return ret; - } - - public void pop(PathConsumer2D io) { - numCurves--; - int type = curveTypes[numCurves]; - end -= (type - 2); - switch(type) { - case 8: - io.curveTo(curves[end+0], curves[end+1], - curves[end+2], curves[end+3], - curves[end+4], curves[end+5]); - break; - case 6: - io.quadTo(curves[end+0], curves[end+1], - curves[end+2], curves[end+3]); - break; - case 4: - io.lineTo(curves[end], curves[end+1]); - } - } - - @Override - public String toString() { - String ret = ""; - int nc = numCurves; - int end = this.end; - while (nc > 0) { - nc--; - int type = curveTypes[numCurves]; - end -= (type - 2); - switch(type) { - case 8: - ret += "cubic: "; - break; - case 6: - ret += "quad: "; - break; - case 4: - ret += "line: "; - break; - } - ret += Arrays.toString(Arrays.copyOfRange(curves, end, end+type-2)) + "\n"; - } - return ret; - } - } -} --- old/src/java.desktop/share/classes/sun/java2d/pisces/TransformingPathConsumer2D.java 2017-11-06 15:02:39.260239264 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,393 +0,0 @@ -/* - * Copyright (c) 2007, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.pisces; - -import sun.awt.geom.PathConsumer2D; -import java.awt.geom.AffineTransform; - -final class TransformingPathConsumer2D { - public static PathConsumer2D - transformConsumer(PathConsumer2D out, - AffineTransform at) - { - if (at == null) { - return out; - } - float Mxx = (float) at.getScaleX(); - float Mxy = (float) at.getShearX(); - float Mxt = (float) at.getTranslateX(); - float Myx = (float) at.getShearY(); - float Myy = (float) at.getScaleY(); - float Myt = (float) at.getTranslateY(); - if (Mxy == 0f && Myx == 0f) { - if (Mxx == 1f && Myy == 1f) { - if (Mxt == 0f && Myt == 0f) { - return out; - } else { - return new TranslateFilter(out, Mxt, Myt); - } - } else { - if (Mxt == 0f && Myt == 0f) { - return new DeltaScaleFilter(out, Mxx, Myy); - } else { - return new ScaleFilter(out, Mxx, Myy, Mxt, Myt); - } - } - } else if (Mxt == 0f && Myt == 0f) { - return new DeltaTransformFilter(out, Mxx, Mxy, Myx, Myy); - } else { - return new TransformFilter(out, Mxx, Mxy, Mxt, Myx, Myy, Myt); - } - } - - public static PathConsumer2D - deltaTransformConsumer(PathConsumer2D out, - AffineTransform at) - { - if (at == null) { - return out; - } - float Mxx = (float) at.getScaleX(); - float Mxy = (float) at.getShearX(); - float Myx = (float) at.getShearY(); - float Myy = (float) at.getScaleY(); - if (Mxy == 0f && Myx == 0f) { - if (Mxx == 1f && Myy == 1f) { - return out; - } else { - return new DeltaScaleFilter(out, Mxx, Myy); - } - } else { - return new DeltaTransformFilter(out, Mxx, Mxy, Myx, Myy); - } - } - - public static PathConsumer2D - inverseDeltaTransformConsumer(PathConsumer2D out, - AffineTransform at) - { - if (at == null) { - return out; - } - float Mxx = (float) at.getScaleX(); - float Mxy = (float) at.getShearX(); - float Myx = (float) at.getShearY(); - float Myy = (float) at.getScaleY(); - if (Mxy == 0f && Myx == 0f) { - if (Mxx == 1f && Myy == 1f) { - return out; - } else { - return new DeltaScaleFilter(out, 1.0f/Mxx, 1.0f/Myy); - } - } else { - float det = Mxx * Myy - Mxy * Myx; - return new DeltaTransformFilter(out, - Myy / det, - -Mxy / det, - -Myx / det, - Mxx / det); - } - } - - static final class TranslateFilter implements PathConsumer2D { - private final PathConsumer2D out; - private final float tx; - private final float ty; - - TranslateFilter(PathConsumer2D out, - float tx, float ty) - { - this.out = out; - this.tx = tx; - this.ty = ty; - } - - public void moveTo(float x0, float y0) { - out.moveTo(x0 + tx, y0 + ty); - } - - public void lineTo(float x1, float y1) { - out.lineTo(x1 + tx, y1 + ty); - } - - public void quadTo(float x1, float y1, - float x2, float y2) - { - out.quadTo(x1 + tx, y1 + ty, - x2 + tx, y2 + ty); - } - - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - out.curveTo(x1 + tx, y1 + ty, - x2 + tx, y2 + ty, - x3 + tx, y3 + ty); - } - - public void closePath() { - out.closePath(); - } - - public void pathDone() { - out.pathDone(); - } - - public long getNativeConsumer() { - return 0; - } - } - - static final class ScaleFilter implements PathConsumer2D { - private final PathConsumer2D out; - private final float sx; - private final float sy; - private final float tx; - private final float ty; - - ScaleFilter(PathConsumer2D out, - float sx, float sy, float tx, float ty) - { - this.out = out; - this.sx = sx; - this.sy = sy; - this.tx = tx; - this.ty = ty; - } - - public void moveTo(float x0, float y0) { - out.moveTo(x0 * sx + tx, y0 * sy + ty); - } - - public void lineTo(float x1, float y1) { - out.lineTo(x1 * sx + tx, y1 * sy + ty); - } - - public void quadTo(float x1, float y1, - float x2, float y2) - { - out.quadTo(x1 * sx + tx, y1 * sy + ty, - x2 * sx + tx, y2 * sy + ty); - } - - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - out.curveTo(x1 * sx + tx, y1 * sy + ty, - x2 * sx + tx, y2 * sy + ty, - x3 * sx + tx, y3 * sy + ty); - } - - public void closePath() { - out.closePath(); - } - - public void pathDone() { - out.pathDone(); - } - - public long getNativeConsumer() { - return 0; - } - } - - static final class TransformFilter implements PathConsumer2D { - private final PathConsumer2D out; - private final float Mxx; - private final float Mxy; - private final float Mxt; - private final float Myx; - private final float Myy; - private final float Myt; - - TransformFilter(PathConsumer2D out, - float Mxx, float Mxy, float Mxt, - float Myx, float Myy, float Myt) - { - this.out = out; - this.Mxx = Mxx; - this.Mxy = Mxy; - this.Mxt = Mxt; - this.Myx = Myx; - this.Myy = Myy; - this.Myt = Myt; - } - - public void moveTo(float x0, float y0) { - out.moveTo(x0 * Mxx + y0 * Mxy + Mxt, - x0 * Myx + y0 * Myy + Myt); - } - - public void lineTo(float x1, float y1) { - out.lineTo(x1 * Mxx + y1 * Mxy + Mxt, - x1 * Myx + y1 * Myy + Myt); - } - - public void quadTo(float x1, float y1, - float x2, float y2) - { - out.quadTo(x1 * Mxx + y1 * Mxy + Mxt, - x1 * Myx + y1 * Myy + Myt, - x2 * Mxx + y2 * Mxy + Mxt, - x2 * Myx + y2 * Myy + Myt); - } - - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - out.curveTo(x1 * Mxx + y1 * Mxy + Mxt, - x1 * Myx + y1 * Myy + Myt, - x2 * Mxx + y2 * Mxy + Mxt, - x2 * Myx + y2 * Myy + Myt, - x3 * Mxx + y3 * Mxy + Mxt, - x3 * Myx + y3 * Myy + Myt); - } - - public void closePath() { - out.closePath(); - } - - public void pathDone() { - out.pathDone(); - } - - public long getNativeConsumer() { - return 0; - } - } - - static final class DeltaScaleFilter implements PathConsumer2D { - private final float sx, sy; - private final PathConsumer2D out; - - public DeltaScaleFilter(PathConsumer2D out, float Mxx, float Myy) { - sx = Mxx; - sy = Myy; - this.out = out; - } - - public void moveTo(float x0, float y0) { - out.moveTo(x0 * sx, y0 * sy); - } - - public void lineTo(float x1, float y1) { - out.lineTo(x1 * sx, y1 * sy); - } - - public void quadTo(float x1, float y1, - float x2, float y2) - { - out.quadTo(x1 * sx, y1 * sy, - x2 * sx, y2 * sy); - } - - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - out.curveTo(x1 * sx, y1 * sy, - x2 * sx, y2 * sy, - x3 * sx, y3 * sy); - } - - public void closePath() { - out.closePath(); - } - - public void pathDone() { - out.pathDone(); - } - - public long getNativeConsumer() { - return 0; - } - } - - static final class DeltaTransformFilter implements PathConsumer2D { - private PathConsumer2D out; - private final float Mxx; - private final float Mxy; - private final float Myx; - private final float Myy; - - DeltaTransformFilter(PathConsumer2D out, - float Mxx, float Mxy, - float Myx, float Myy) - { - this.out = out; - this.Mxx = Mxx; - this.Mxy = Mxy; - this.Myx = Myx; - this.Myy = Myy; - } - - public void moveTo(float x0, float y0) { - out.moveTo(x0 * Mxx + y0 * Mxy, - x0 * Myx + y0 * Myy); - } - - public void lineTo(float x1, float y1) { - out.lineTo(x1 * Mxx + y1 * Mxy, - x1 * Myx + y1 * Myy); - } - - public void quadTo(float x1, float y1, - float x2, float y2) - { - out.quadTo(x1 * Mxx + y1 * Mxy, - x1 * Myx + y1 * Myy, - x2 * Mxx + y2 * Mxy, - x2 * Myx + y2 * Myy); - } - - public void curveTo(float x1, float y1, - float x2, float y2, - float x3, float y3) - { - out.curveTo(x1 * Mxx + y1 * Mxy, - x1 * Myx + y1 * Myy, - x2 * Mxx + y2 * Mxy, - x2 * Myx + y2 * Myy, - x3 * Mxx + y3 * Mxy, - x3 * Myx + y3 * Myy); - } - - public void closePath() { - out.closePath(); - } - - public void pathDone() { - out.pathDone(); - } - - public long getNativeConsumer() { - return 0; - } - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/IdleTileCache.java 2017-11-06 15:02:39.592239254 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2010, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -import java.util.*; - -public class IdleTileCache { - static final int IDLE_TILE_SYNC_GRANULARITY = 16; - static final ArrayList idleBuffers = new ArrayList(); - - ArrayList idleTileWorkerCacheList = new ArrayList(); - ArrayList idleTileConsumerCacheList = - new ArrayList(IDLE_TILE_SYNC_GRANULARITY); - - /** - * Return a cached Tile, if possible from cache. - * Allowed caller: Rasterizer/Producer-Thread - * - * @param: maxCache - Specify the maximum amount of tiles needed - */ - public JulesTile getIdleTileWorker(int maxCache) { - /* Try to fetch idle tiles from the global cache list */ - if (idleTileWorkerCacheList.size() == 0) { - idleTileWorkerCacheList.ensureCapacity(maxCache); - - synchronized (idleBuffers) { - for (int i = 0; i < maxCache && idleBuffers.size() > 0; i++) { - idleTileWorkerCacheList.add( - idleBuffers.remove(idleBuffers.size() - 1)); - } - } - } - - if (idleTileWorkerCacheList.size() > 0) { - return idleTileWorkerCacheList.remove(idleTileWorkerCacheList.size() - 1); - } - - return new JulesTile(); - } - - /** - * Release tile and allow it to be re-used by another thread. Allowed - * Allowed caller: MaskBlit/Consumer-Thread - */ - public void releaseTile(JulesTile tile) { - if (tile != null && tile.hasBuffer()) { - idleTileConsumerCacheList.add(tile); - - if (idleTileConsumerCacheList.size() > IDLE_TILE_SYNC_GRANULARITY) { - synchronized (idleBuffers) { - idleBuffers.addAll(idleTileConsumerCacheList); - } - idleTileConsumerCacheList.clear(); - } - } - } - - /** - * Releases thread-local tiles cached for use by the rasterizing thread. - * Allowed caller: Rasterizer/Producer-Thread - */ - public void disposeRasterizerResources() { - releaseTiles(idleTileWorkerCacheList); - } - - /** - * Releases thread-local tiles cached for performance reasons. Allowed - * Allowed caller: MaskBlit/Consumer-Thread - */ - public void disposeConsumerResources() { - releaseTiles(idleTileConsumerCacheList); - } - - /** - * Release a list of tiles and allow it to be re-used by another thread. - * Thread safe. - */ - public void releaseTiles(List tileList) { - if (tileList.size() > 0) { - synchronized (idleBuffers) { - idleBuffers.addAll(tileList); - } - tileList.clear(); - } - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/JulesAATileGenerator.java 2017-11-06 15:02:39.920239245 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2010, 2013, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -import java.awt.*; -import java.awt.geom.*; -import java.util.concurrent.*; -import sun.java2d.pipe.*; -import sun.java2d.xr.*; - -public class JulesAATileGenerator implements AATileGenerator { - /* Threading stuff */ - static final ExecutorService rasterThreadPool = - Executors.newCachedThreadPool(); - static final int CPU_CNT = Runtime.getRuntime().availableProcessors(); - - static final boolean ENABLE_THREADING = false; - static final int THREAD_MIN = 16; - static final int THREAD_BEGIN = 16; - - IdleTileCache tileCache; - TileWorker worker; - boolean threaded = false; - int rasterTileCnt; - - /* Tiling */ - static final int TILE_SIZE = 32; - static final int TILE_SIZE_FP = 32 << 16; - int left, right, top, bottom, width, height; - int leftFP, topFP; - int tileCnt, tilesX, tilesY; - int currTilePos = 0; - TrapezoidList traps; - TileTrapContainer[] tiledTrapArray; - JulesTile mainTile; - - public JulesAATileGenerator(Shape s, AffineTransform at, Region clip, - BasicStroke bs, boolean thin, - boolean normalize, int[] bbox) { - JulesPathBuf buf = new JulesPathBuf(); - - if (bs == null) { - traps = buf.tesselateFill(s, at, clip); - } else { - traps = buf.tesselateStroke(s, bs, thin, false, true, at, clip); - } - - calculateArea(bbox); - bucketSortTraps(); - calculateTypicalAlpha(); - - threaded = ENABLE_THREADING && - rasterTileCnt >= THREAD_MIN && CPU_CNT >= 2; - if (threaded) { - tileCache = new IdleTileCache(); - worker = new TileWorker(this, THREAD_BEGIN, tileCache); - rasterThreadPool.execute(worker); - } - - mainTile = new JulesTile(); - } - - private static native long - rasterizeTrapezoidsNative(long pixmanImagePtr, int[] traps, - int[] trapPos, int trapCnt, - byte[] buffer, int xOff, int yOff); - - private static native void freePixmanImgPtr(long pixmanImgPtr); - - private void calculateArea(int[] bbox) { - tilesX = 0; - tilesY = 0; - tileCnt = 0; - bbox[0] = 0; - bbox[1] = 0; - bbox[2] = 0; - bbox[3] = 0; - - if (traps.getSize() > 0) { - left = traps.getLeft(); - right = traps.getRight(); - top = traps.getTop(); - bottom = traps.getBottom(); - leftFP = left << 16; - topFP = top << 16; - - bbox[0] = left; - bbox[1] = top; - bbox[2] = right; - bbox[3] = bottom; - - width = right - left; - height = bottom - top; - - if (width > 0 && height > 0) { - tilesX = (int) Math.ceil(((double) width) / TILE_SIZE); - tilesY = (int) Math.ceil(((double) height) / TILE_SIZE); - tileCnt = tilesY * tilesX; - tiledTrapArray = new TileTrapContainer[tileCnt]; - } else { - // If there is no area touched by the traps, don't - // render them. - traps.setSize(0); - } - } - } - - - private void bucketSortTraps() { - - for (int i = 0; i < traps.getSize(); i++) { - int top = traps.getTop(i) - XRUtils.XDoubleToFixed(this.top); - int bottom = traps.getBottom(i) - topFP; - int p1xLeft = traps.getP1XLeft(i) - leftFP; - int p2xLeft = traps.getP2XLeft(i) - leftFP; - int p1xRight = traps.getP1XRight(i) - leftFP; - int p2xRight = traps.getP2XRight(i) - leftFP; - - int minLeft = Math.min(p1xLeft, p2xLeft); - int maxRight = Math.max(p1xRight, p2xRight); - - maxRight = maxRight > 0 ? maxRight - 1 : maxRight; - bottom = bottom > 0 ? bottom - 1 : bottom; - - int startTileY = top / TILE_SIZE_FP; - int endTileY = bottom / TILE_SIZE_FP; - int startTileX = minLeft / TILE_SIZE_FP; - int endTileX = maxRight / TILE_SIZE_FP; - - for (int n = startTileY; n <= endTileY; n++) { - - for (int m = startTileX; m <= endTileX; m++) { - int trapArrayPos = n * tilesX + m; - TileTrapContainer trapTileList = tiledTrapArray[trapArrayPos]; - if (trapTileList == null) { - trapTileList = new TileTrapContainer(new GrowableIntArray(1, 16)); - tiledTrapArray[trapArrayPos] = trapTileList; - } - - trapTileList.getTraps().addInt(i); - } - } - } - } - - public void getAlpha(byte[] tileBuffer, int offset, int rowstride) { - JulesTile tile = null; - - if (threaded) { - tile = worker.getPreRasterizedTile(currTilePos); - } - - if (tile != null) { - System.arraycopy(tile.getImgBuffer(), 0, - tileBuffer, 0, tileBuffer.length); - tileCache.releaseTile(tile); - } else { - mainTile.setImgBuffer(tileBuffer); - rasterizeTile(currTilePos, mainTile); - } - - nextTile(); - } - - public void calculateTypicalAlpha() { - rasterTileCnt = 0; - - for (int index = 0; index < tileCnt; index++) { - - TileTrapContainer trapCont = tiledTrapArray[index]; - if (trapCont != null) { - GrowableIntArray trapList = trapCont.getTraps(); - - int tileAlpha = 127; - if (trapList == null || trapList.getSize() == 0) { - tileAlpha = 0; - } else if (doTrapsCoverTile(trapList, index)) { - tileAlpha = 0xff; - } - - if (tileAlpha == 127 || tileAlpha == 0xff) { - rasterTileCnt++; - } - - trapCont.setTileAlpha(tileAlpha); - } - } - } - - /* - * Optimization for large fills. Foutunatly cairo does generate an y-sorted - * list of trapezoids. This makes it quite simple to check whether a tile is - * fully covered by traps by: - Checking whether the tile is fully covered by - * traps vertically (trap 2 starts where trap 1 ended) - Checking whether all - * traps cover the tile horizontally This also works, when a single tile - * coveres the whole tile. - */ - protected boolean doTrapsCoverTile(GrowableIntArray trapList, int tileIndex) { - - // Don't bother optimizing tiles with lots of traps, usually it won't - // succeed anyway. - if (trapList.getSize() > TILE_SIZE) { - return false; - } - - int tileStartX = getXPos(tileIndex) * TILE_SIZE_FP + leftFP; - int tileStartY = getYPos(tileIndex) * TILE_SIZE_FP + topFP; - int tileEndX = tileStartX + TILE_SIZE_FP; - int tileEndY = tileStartY + TILE_SIZE_FP; - - // Check whether first tile covers the beginning of the tile vertically - int firstTop = traps.getTop(trapList.getInt(0)); - int firstBottom = traps.getBottom(trapList.getInt(0)); - if (firstTop > tileStartY || firstBottom < tileStartY) { - return false; - } - - // Initialize lastBottom with top, in order to pass the checks for the - // first iteration - int lastBottom = firstTop; - - for (int i = 0; i < trapList.getSize(); i++) { - int trapPos = trapList.getInt(i); - if (traps.getP1XLeft(trapPos) > tileStartX || - traps.getP2XLeft(trapPos) > tileStartX || - traps.getP1XRight(trapPos) < tileEndX || - traps.getP2XRight(trapPos) < tileEndX || - traps.getTop(trapPos) != lastBottom) - { - return false; - } - lastBottom = traps.getBottom(trapPos); - } - - // When the last trap covered the tileEnd vertically, the tile is fully - // covered - return lastBottom >= tileEndY; - } - - public int getTypicalAlpha() { - if (tiledTrapArray[currTilePos] == null) { - return 0; - } else { - return tiledTrapArray[currTilePos].getTileAlpha(); - } - } - - public void dispose() { - freePixmanImgPtr(mainTile.getPixmanImgPtr()); - - if (threaded) { - tileCache.disposeConsumerResources(); - worker.disposeConsumerResources(); - } - } - - protected JulesTile rasterizeTile(int tileIndex, JulesTile tile) { - int tileOffsetX = left + getXPos(tileIndex) * TILE_SIZE; - int tileOffsetY = top + getYPos(tileIndex) * TILE_SIZE; - TileTrapContainer trapCont = tiledTrapArray[tileIndex]; - GrowableIntArray trapList = trapCont.getTraps(); - - if (trapCont.getTileAlpha() == 127) { - long pixmanImgPtr = - rasterizeTrapezoidsNative(tile.getPixmanImgPtr(), - traps.getTrapArray(), - trapList.getArray(), - trapList.getSize(), - tile.getImgBuffer(), - tileOffsetX, tileOffsetY); - tile.setPixmanImgPtr(pixmanImgPtr); - } - - tile.setTilePos(tileIndex); - return tile; - } - - protected int getXPos(int arrayPos) { - return arrayPos % tilesX; - } - - protected int getYPos(int arrayPos) { - return arrayPos / tilesX; - } - - public void nextTile() { - currTilePos++; - } - - public int getTileHeight() { - return TILE_SIZE; - } - - public int getTileWidth() { - return TILE_SIZE; - } - - public int getTileCount() { - return tileCnt; - } - - public TileTrapContainer getTrapContainer(int index) { - return tiledTrapArray[index]; - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/JulesPathBuf.java 2017-11-06 15:02:40.228239237 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,271 +0,0 @@ -/* - * Copyright (c) 2010, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -import java.awt.*; -import java.awt.geom.*; -import sun.awt.X11GraphicsEnvironment; -import sun.java2d.pipe.*; -import sun.java2d.xr.*; - -public class JulesPathBuf { - static final double[] emptyDash = new double[0]; - - private static final byte CAIRO_PATH_OP_MOVE_TO = 0; - private static final byte CAIRO_PATH_OP_LINE_TO = 1; - private static final byte CAIRO_PATH_OP_CURVE_TO = 2; - private static final byte CAIRO_PATH_OP_CLOSE_PATH = 3; - - private static final int CAIRO_FILL_RULE_WINDING = 0; - private static final int CAIRO_FILL_RULE_EVEN_ODD = 1; - - GrowablePointArray points = new GrowablePointArray(128); - GrowableByteArray ops = new GrowableByteArray(1, 128); - int[] xTrapArray = new int[512]; - - private static final boolean isCairoAvailable; - - static { - isCairoAvailable = - java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public Boolean run() { - boolean loadSuccess = false; - if (X11GraphicsEnvironment.isXRenderAvailable()) { - try { - System.loadLibrary("jules"); - loadSuccess = true; - if (X11GraphicsEnvironment.isXRenderVerbose()) { - System.out.println( - "Xrender: INFO: Jules library loaded"); - } - } catch (UnsatisfiedLinkError ex) { - loadSuccess = false; - if (X11GraphicsEnvironment.isXRenderVerbose()) { - System.out.println( - "Xrender: INFO: Jules library not installed."); - } - } - } - return Boolean.valueOf(loadSuccess); - } - }); - } - - public static boolean isCairoAvailable() { - return isCairoAvailable; - } - - public TrapezoidList tesselateFill(Shape s, AffineTransform at, Region clip) { - int windingRule = convertPathData(s, at); - xTrapArray[0] = 0; - - xTrapArray = tesselateFillNative(points.getArray(), ops.getArray(), - points.getSize(), ops.getSize(), - xTrapArray, xTrapArray.length, - getCairoWindingRule(windingRule), - clip.getLoX(), clip.getLoY(), - clip.getHiX(), clip.getHiY()); - - return new TrapezoidList(xTrapArray); - } - - public TrapezoidList tesselateStroke(Shape s, BasicStroke bs, boolean thin, - boolean adjust, boolean antialias, - AffineTransform at, Region clip) { - - float lw; - if (thin) { - if (antialias) { - lw = 0.5f; - } else { - lw = 1.0f; - } - } else { - lw = bs.getLineWidth(); - } - - convertPathData(s, at); - - double[] dashArray = floatToDoubleArray(bs.getDashArray()); - xTrapArray[0] = 0; - - xTrapArray = - tesselateStrokeNative(points.getArray(), ops.getArray(), - points.getSize(), ops.getSize(), - xTrapArray, xTrapArray.length, lw, - bs.getEndCap(), bs.getLineJoin(), - bs.getMiterLimit(), dashArray, - dashArray.length, bs.getDashPhase(), - 1, 0, 0, 0, 1, 0, - clip.getLoX(), clip.getLoY(), - clip.getHiX(), clip.getHiY()); - - return new TrapezoidList(xTrapArray); - } - - protected double[] floatToDoubleArray(float[] dashArrayFloat) { - double[] dashArrayDouble = emptyDash; - if (dashArrayFloat != null) { - dashArrayDouble = new double[dashArrayFloat.length]; - - for (int i = 0; i < dashArrayFloat.length; i++) { - dashArrayDouble[i] = dashArrayFloat[i]; - } - } - - return dashArrayDouble; - } - - protected int convertPathData(Shape s, AffineTransform at) { - PathIterator pi = s.getPathIterator(at); - - double[] coords = new double[6]; - double currX = 0; - double currY = 0; - - while (!pi.isDone()) { - int curOp = pi.currentSegment(coords); - - int pointIndex; - switch (curOp) { - - case PathIterator.SEG_MOVETO: - ops.addByte(CAIRO_PATH_OP_MOVE_TO); - pointIndex = points.getNextIndex(); - points.setX(pointIndex, DoubleToCairoFixed(coords[0])); - points.setY(pointIndex, DoubleToCairoFixed(coords[1])); - currX = coords[0]; - currY = coords[1]; - break; - - case PathIterator.SEG_LINETO: - ops.addByte(CAIRO_PATH_OP_LINE_TO); - pointIndex = points.getNextIndex(); - points.setX(pointIndex, DoubleToCairoFixed(coords[0])); - points.setY(pointIndex, DoubleToCairoFixed(coords[1])); - currX = coords[0]; - currY = coords[1]; - break; - - /** - * q0 = p0 - * q1 = (p0+2*p1)/3 - * q2 = (p2+2*p1)/3 - * q3 = p2 - */ - case PathIterator.SEG_QUADTO: - double x1 = coords[0]; - double y1 = coords[1]; - double x2, y2; - double x3 = coords[2]; - double y3 = coords[3]; - - x2 = x1 + (x3 - x1) / 3; - y2 = y1 + (y3 - y1) / 3; - x1 = currX + 2 * (x1 - currX) / 3; - y1 =currY + 2 * (y1 - currY) / 3; - - ops.addByte(CAIRO_PATH_OP_CURVE_TO); - pointIndex = points.getNextIndex(); - points.setX(pointIndex, DoubleToCairoFixed(x1)); - points.setY(pointIndex, DoubleToCairoFixed(y1)); - pointIndex = points.getNextIndex(); - points.setX(pointIndex, DoubleToCairoFixed(x2)); - points.setY(pointIndex, DoubleToCairoFixed(y2)); - pointIndex = points.getNextIndex(); - points.setX(pointIndex, DoubleToCairoFixed(x3)); - points.setY(pointIndex, DoubleToCairoFixed(y3)); - currX = x3; - currY = y3; - break; - - case PathIterator.SEG_CUBICTO: - ops.addByte(CAIRO_PATH_OP_CURVE_TO); - pointIndex = points.getNextIndex(); - points.setX(pointIndex, DoubleToCairoFixed(coords[0])); - points.setY(pointIndex, DoubleToCairoFixed(coords[1])); - pointIndex = points.getNextIndex(); - points.setX(pointIndex, DoubleToCairoFixed(coords[2])); - points.setY(pointIndex, DoubleToCairoFixed(coords[3])); - pointIndex = points.getNextIndex(); - points.setX(pointIndex, DoubleToCairoFixed(coords[4])); - points.setY(pointIndex, DoubleToCairoFixed(coords[5])); - currX = coords[4]; - currY = coords[5]; - break; - - case PathIterator.SEG_CLOSE: - ops.addByte(CAIRO_PATH_OP_CLOSE_PATH); - break; - } - - pi.next(); - } - - return pi.getWindingRule(); - } - - private static native int[] - tesselateStrokeNative(int[] pointArray, byte[] ops, - int pointCnt, int opCnt, - int[] xTrapArray, int xTrapArrayLength, - double lineWidth, int lineCap, int lineJoin, - double miterLimit, double[] dashArray, - int dashCnt, double offset, - double m00, double m01, double m02, - double m10, double m11, double m12, - int clipLowX, int clipLowY, - int clipWidth, int clipHeight); - - private static native int[] - tesselateFillNative(int[] pointArray, byte[] ops, int pointCnt, - int opCnt, int[] xTrapArray, int xTrapArrayLength, - int windingRule, int clipLowX, int clipLowY, int clipWidth, int clipHeight); - - public void clear() { - points.clear(); - ops.clear(); - xTrapArray[0] = 0; - } - - private static int DoubleToCairoFixed(double dbl) { - return (int) (dbl * 256); - } - - private static int getCairoWindingRule(int j2dWindingRule) { - switch(j2dWindingRule) { - case PathIterator.WIND_EVEN_ODD: - return CAIRO_FILL_RULE_EVEN_ODD; - - case PathIterator.WIND_NON_ZERO: - return CAIRO_FILL_RULE_WINDING; - - default: - throw new IllegalArgumentException("Illegal Java2D winding rule specified"); - } - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/JulesRenderingEngine.java 2017-11-06 15:02:40.504239229 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2010, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -import java.awt.*; - -import java.awt.geom.*; -import sun.java2d.pipe.*; -import sun.java2d.pisces.*; - -public class JulesRenderingEngine extends PiscesRenderingEngine { - - @Override - public AATileGenerator - getAATileGenerator(Shape s, AffineTransform at, Region clip, - BasicStroke bs, boolean thin, - boolean normalize, int[] bbox) { - - if (JulesPathBuf.isCairoAvailable()) { - return new JulesAATileGenerator(s, at, clip, bs, thin, - normalize, bbox); - } else { - return super.getAATileGenerator(s, at, clip, bs, thin, - normalize, bbox); - } - } - - public float getMinimumAAPenSize() { - return 0.5f; - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/JulesShapePipe.java 2017-11-06 15:02:40.772239222 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2010, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -import java.awt.*; -import sun.awt.*; -import sun.java2d.*; -import sun.java2d.pipe.*; -import sun.java2d.xr.*; - -public class JulesShapePipe implements ShapeDrawPipe { - - XRCompositeManager compMan; - JulesPathBuf buf = new JulesPathBuf(); - - public JulesShapePipe(XRCompositeManager compMan) { - this.compMan = compMan; - } - - /** - * Common validate method, used by all XRRender functions to validate the - * destination context. - */ - private final void validateSurface(SunGraphics2D sg2d) { - XRSurfaceData xrsd = (XRSurfaceData) sg2d.surfaceData; - xrsd.validateAsDestination(sg2d, sg2d.getCompClip()); - xrsd.maskBuffer.validateCompositeState(sg2d.composite, sg2d.transform, - sg2d.paint, sg2d); - } - - public void draw(SunGraphics2D sg2d, Shape s) { - try { - SunToolkit.awtLock(); - validateSurface(sg2d); - XRSurfaceData xrsd = (XRSurfaceData) sg2d.surfaceData; - - BasicStroke bs; - - if (sg2d.stroke instanceof BasicStroke) { - bs = (BasicStroke) sg2d.stroke; - } else { //TODO: What happens in the case of a !BasicStroke?? - s = sg2d.stroke.createStrokedShape(s); - bs = null; - } - - boolean adjust = - (bs != null && sg2d.strokeHint != SunHints.INTVAL_STROKE_PURE); - boolean thin = (sg2d.strokeState <= SunGraphics2D.STROKE_THINDASHED); - - TrapezoidList traps = - buf.tesselateStroke(s, bs, thin, adjust, true, - sg2d.transform, sg2d.getCompClip()); - compMan.XRCompositeTraps(xrsd.picture, - sg2d.transX, sg2d.transY, traps); - - buf.clear(); - - } finally { - SunToolkit.awtUnlock(); - } - } - - public void fill(SunGraphics2D sg2d, Shape s) { - try { - SunToolkit.awtLock(); - validateSurface(sg2d); - - XRSurfaceData xrsd = (XRSurfaceData) sg2d.surfaceData; - - TrapezoidList traps = buf.tesselateFill(s, sg2d.transform, - sg2d.getCompClip()); - compMan.XRCompositeTraps(xrsd.picture, 0, 0, traps); - - buf.clear(); - } finally { - SunToolkit.awtUnlock(); - } - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/JulesTile.java 2017-11-06 15:02:41.076239214 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2010, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -public class JulesTile { - byte[] imgBuffer; - long pixmanImgPtr = 0; - int tilePos; - - public JulesTile() { - } - - public byte[] getImgBuffer() { - if(imgBuffer == null) { - imgBuffer = new byte[1024]; - } - - return imgBuffer; - } - - public long getPixmanImgPtr() { - return pixmanImgPtr; - } - - public void setPixmanImgPtr(long pixmanImgPtr) { - this.pixmanImgPtr = pixmanImgPtr; - } - - public boolean hasBuffer() { - return imgBuffer != null; - } - - public int getTilePos() { - return tilePos; - } - - public void setTilePos(int tilePos) { - this.tilePos = tilePos; - } - - public void setImgBuffer(byte[] imgBuffer){ - this.imgBuffer = imgBuffer; - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/TileTrapContainer.java 2017-11-06 15:02:41.384239205 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2010, 2012, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -import sun.java2d.xr.GrowableIntArray; - -class TileTrapContainer { - int tileAlpha; - GrowableIntArray traps; - - public TileTrapContainer(GrowableIntArray traps) { - this.traps = traps; - } - - public void setTileAlpha(int tileAlpha) { - this.tileAlpha = tileAlpha; - } - - public int getTileAlpha() { - return tileAlpha; - } - - public GrowableIntArray getTraps() { - return traps; - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/TileWorker.java 2017-11-06 15:02:41.696239197 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2010, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -import java.util.*; - -public class TileWorker implements Runnable { - static final int RASTERIZED_TILE_SYNC_GRANULARITY = 8; - final ArrayList rasterizedTileConsumerCache = - new ArrayList(); - final LinkedList rasterizedBuffers = new LinkedList(); - - IdleTileCache tileCache; - JulesAATileGenerator tileGenerator; - int workerStartIndex; - volatile int consumerPos = 0; - - /* Threading statistics */ - int mainThreadCnt = 0; - int workerCnt = 0; - int doubled = 0; - - public TileWorker(JulesAATileGenerator tileGenerator, int workerStartIndex, IdleTileCache tileCache) { - this.tileGenerator = tileGenerator; - this.workerStartIndex = workerStartIndex; - this.tileCache = tileCache; - } - - public void run() { - ArrayList tiles = new ArrayList(16); - - for (int i = workerStartIndex; i < tileGenerator.getTileCount(); i++) { - TileTrapContainer tile = tileGenerator.getTrapContainer(i); - - if (tile != null && tile.getTileAlpha() == 127) { - JulesTile rasterizedTile = - tileGenerator.rasterizeTile(i, - tileCache.getIdleTileWorker( - tileGenerator.getTileCount() - i - 1)); - tiles.add(rasterizedTile); - - if (tiles.size() > RASTERIZED_TILE_SYNC_GRANULARITY) { - addRasterizedTiles(tiles); - tiles.clear(); - } - } - - i = Math.max(i, consumerPos + RASTERIZED_TILE_SYNC_GRANULARITY / 2); - } - addRasterizedTiles(tiles); - - tileCache.disposeRasterizerResources(); - } - - /** - * Returns a rasterized tile for the specified tilePos, - * or null if it isn't available. - * Allowed caller: MaskBlit/Consumer-Thread - */ - public JulesTile getPreRasterizedTile(int tilePos) { - JulesTile tile = null; - - if (rasterizedTileConsumerCache.size() == 0 && - tilePos >= workerStartIndex) - { - synchronized (rasterizedBuffers) { - rasterizedTileConsumerCache.addAll(rasterizedBuffers); - rasterizedBuffers.clear(); - } - } - - while (tile == null && rasterizedTileConsumerCache.size() > 0) { - JulesTile t = rasterizedTileConsumerCache.get(0); - - if (t.getTilePos() > tilePos) { - break; - } - - if (t.getTilePos() < tilePos) { - tileCache.releaseTile(t); - doubled++; - } - - if (t.getTilePos() <= tilePos) { - rasterizedTileConsumerCache.remove(0); - } - - if (t.getTilePos() == tilePos) { - tile = t; - } - } - - if (tile == null) { - mainThreadCnt++; - - // If there are no tiles left, tell the producer the current - // position. This avoids producing tiles twice. - consumerPos = tilePos; - } else { - workerCnt++; - } - - return tile; - } - - private void addRasterizedTiles(ArrayList tiles) { - synchronized (rasterizedBuffers) { - rasterizedBuffers.addAll(tiles); - } - } - - /** - * Releases cached tiles. - * Allowed caller: MaskBlit/Consumer-Thread - */ - public void disposeConsumerResources() { - synchronized (rasterizedBuffers) { - tileCache.releaseTiles(rasterizedBuffers); - } - - tileCache.releaseTiles(rasterizedTileConsumerCache); - } -} --- old/src/java.desktop/unix/classes/sun/java2d/jules/TrapezoidList.java 2017-11-06 15:02:42.064239187 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2010, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package sun.java2d.jules; - -public class TrapezoidList { - public static final int TRAP_START_INDEX = 5; - public static final int TRAP_SIZE = 10; - - int[] trapArray; - - public TrapezoidList(int[] trapArray) { - this.trapArray = trapArray; - } - - public final int[] getTrapArray() { - return trapArray; - } - - public final int getSize() { - return trapArray[0]; - } - - public final void setSize(int size) { - trapArray[0] = 0; - } - - public final int getLeft() { - return trapArray[1]; - } - - public final int getTop() { - return trapArray[2]; - } - - public final int getRight() { - return trapArray[3]; - } - - public final int getBottom() { - return trapArray[4]; - } - - - private final int getTrapStartAddresse(int pos) { - return TRAP_START_INDEX + TRAP_SIZE * pos; - } - - public final int getTop(int pos) { - return trapArray[getTrapStartAddresse(pos) + 0]; - } - - public final int getBottom(int pos) { - return trapArray[getTrapStartAddresse(pos) + 1]; - } - - public final int getP1XLeft(int pos) { - return trapArray[getTrapStartAddresse(pos) + 2]; - } - - public final int getP1YLeft(int pos) { - return trapArray[getTrapStartAddresse(pos) + 3]; - } - - public final int getP2XLeft(int pos) { - return trapArray[getTrapStartAddresse(pos) + 4]; - } - - public final int getP2YLeft(int pos) { - return trapArray[getTrapStartAddresse(pos) + 5]; - } - - public final int getP1XRight(int pos) { - return trapArray[getTrapStartAddresse(pos) + 6]; - } - - public final int getP1YRight(int pos) { - return trapArray[getTrapStartAddresse(pos) + 7]; - } - - public final int getP2XRight(int pos) { - return trapArray[getTrapStartAddresse(pos) + 8]; - } - - public final int getP2YRight(int pos) { - return trapArray[getTrapStartAddresse(pos) + 9]; - } -} --- old/test/jdk/TEST.ROOT 2017-11-06 15:02:42.344239179 -0800 +++ new/test/jdk/TEST.ROOT 2017-11-06 15:02:42.168239184 -0800 @@ -17,7 +17,7 @@ keys=2d dnd headful i18n intermittent printer randomness # Tests that must run in othervm mode -othervm.dirs=java/awt java/beans javax/accessibility javax/imageio javax/sound javax/print javax/management com/sun/awt sun/awt sun/java2d sun/pisces javax/xml/jaxp/testng/validation java/lang/ProcessHandle +othervm.dirs=java/awt java/beans javax/accessibility javax/imageio javax/sound javax/print javax/management com/sun/awt sun/awt sun/java2d javax/xml/jaxp/testng/validation java/lang/ProcessHandle # Tests that cannot run concurrently exclusiveAccess.dirs=java/rmi/Naming java/util/prefs sun/management/jmxremote sun/tools/jstatd sun/security/mscapi java/util/stream java/util/Arrays/largeMemory java/util/BitSet/stream javax/rmi com/sun/corba/cachedSocket --- old/test/jdk/TEST.groups 2017-11-06 15:02:42.716239169 -0800 +++ new/test/jdk/TEST.groups 2017-11-06 15:02:42.552239173 -0800 @@ -324,7 +324,6 @@ jdk_2d = \ javax/print \ - sun/pisces \ sun/java2d jdk_beans = \ @@ -426,7 +425,6 @@ :jdk_sctp \ javax/accessibility \ com/sun/java/swing \ - sun/pisces \ com/sun/awt --- old/test/jdk/java/awt/BasicStroke/DashStrokeTest.java 2017-11-06 15:02:43.092239159 -0800 +++ new/test/jdk/java/awt/BasicStroke/DashStrokeTest.java 2017-11-06 15:02:42.920239163 -0800 @@ -24,7 +24,6 @@ * @bug 8075942 8080932 * @summary test there is no exception rendering a dashed stroke * @run main DashStrokeTest - * @run main/othervm -Dsun.java2d.renderer=sun.java2d.pisces.PiscesRenderingEngine DashStrokeTest */ import java.awt.BasicStroke; --- old/test/jdk/sun/pisces/DashStrokeTest.java 2017-11-06 15:02:43.544239146 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2009, 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 - * @summary verify that first element is a dash - * @bug 6793344 - */ - -import java.awt.*; -import java.awt.image.*; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.SwingUtilities; -import javax.swing.WindowConstants; - -public class DashStrokeTest extends Component { - - static BufferedImage bi; - static boolean printed = false; - - public Dimension getPreferredSize() { - return new Dimension(200,200); - } - - public static void drawGui() { - bi = new BufferedImage(200, 20, BufferedImage.TYPE_INT_RGB); - Graphics2D g2d = bi.createGraphics(); - BasicStroke dashStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, - BasicStroke.JOIN_ROUND, 1.0f, new float[] { 0.0f, 200 }, - 1.0f); - - g2d.setStroke(dashStroke); - g2d.setColor(Color.RED); - g2d.drawLine(5,10, 100,10); - printed =true; - } - - public static void main(String[] args) { - try { - SwingUtilities.invokeAndWait(new Runnable() { - - @Override - public void run() { - drawGui(); - } - - }); - } catch (Exception e) { - } - - if (printed) { - checkBI(bi, Color.RED); - } - } - - static void checkBI(BufferedImage bi, Color badColor) { - int badrgb = badColor.getRGB(); - - int col = bi.getRGB(6, 9); - if (col == badrgb) { - throw new RuntimeException("A pixel was turned on. "); - } - } -} - --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/DashStrokeTest.java 2017-11-06 15:02:43.308239153 -0800 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2009, 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 + * @summary verify that first element is a dash + * @bug 6793344 + */ + +import java.awt.*; +import java.awt.image.*; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.SwingUtilities; +import javax.swing.WindowConstants; + +public class DashStrokeTest extends Component { + + static BufferedImage bi; + static boolean printed = false; + + public Dimension getPreferredSize() { + return new Dimension(200,200); + } + + public static void drawGui() { + bi = new BufferedImage(200, 20, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bi.createGraphics(); + BasicStroke dashStroke = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND, 1.0f, new float[] { 0.0f, 200 }, + 1.0f); + + g2d.setStroke(dashStroke); + g2d.setColor(Color.RED); + g2d.drawLine(5,10, 100,10); + printed =true; + } + + public static void main(String[] args) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + + @Override + public void run() { + drawGui(); + } + + }); + } catch (Exception e) { + } + + if (printed) { + checkBI(bi, Color.RED); + } + } + + static void checkBI(BufferedImage bi, Color badColor) { + int badrgb = badColor.getRGB(); + + int col = bi.getRGB(6, 9); + if (col == badrgb) { + throw new RuntimeException("A pixel was turned on. "); + } + } +} + --- old/test/jdk/sun/pisces/JoinMiterTest.java 2017-11-06 15:02:43.892239137 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2009, 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 - * @summary Pass if no RuntimeException. - * @bug 6812600 - */ -import java.awt.*; -import java.awt.image.BufferedImage; - -public class JoinMiterTest { - - public static void main(String[] args) throws Exception { - BufferedImage image = new BufferedImage(200, 200, -BufferedImage.TYPE_INT_RGB); - Graphics2D g = image.createGraphics(); - g.setPaint(Color.WHITE); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - g.translate(25, 100); - g.setPaint(Color.BLACK); - g.setStroke(new BasicStroke(20, BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER)); - g.draw(new Polygon(new int[] {0, 150, 0}, new int[] {75, 0, -75}, 3)); - if (image.getRGB(16, 10) == Color.WHITE.getRGB()) { - throw new RuntimeException("Miter is not rendered."); - } - } -} --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/JoinMiterTest.java 2017-11-06 15:02:43.660239143 -0800 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2009, 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 + * @summary Pass if no RuntimeException. + * @bug 6812600 + */ +import java.awt.*; +import java.awt.image.BufferedImage; + +public class JoinMiterTest { + + public static void main(String[] args) throws Exception { + BufferedImage image = new BufferedImage(200, 200, +BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + g.setPaint(Color.WHITE); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + g.translate(25, 100); + g.setPaint(Color.BLACK); + g.setStroke(new BasicStroke(20, BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER)); + g.draw(new Polygon(new int[] {0, 150, 0}, new int[] {75, 0, -75}, 3)); + if (image.getRGB(16, 10) == Color.WHITE.getRGB()) { + throw new RuntimeException("Miter is not rendered."); + } + } +} --- old/test/jdk/sun/java2d/pisces/OpenJDKFillBug.java 2017-11-06 15:02:44.288239126 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2015, 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. - */ -import java.awt.Color; -import java.awt.Composite; -import java.awt.CompositeContext; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.geom.AffineTransform; -import java.awt.geom.GeneralPath; -import java.awt.image.BufferedImage; -import java.awt.image.ColorModel; -import java.awt.image.Raster; -import java.awt.image.WritableRaster; -import java.awt.image.RasterFormatException; - -/** - * @test - * @bug 8048782 - * @summary Test program that demonstrates PiscesRendering bug in - * OpenJDK 1.7.0.60 (and probably in all other OpenJDK versions, too). - */ - -public class OpenJDKFillBug -{ - /** - * Test program that demonstrates a bug in OpenJDK 1.7.0.60 (and - * probably in all other OpenJDK versions, too). To see the bug, simply run - * the 'main' program with OpenJDK. The bug makes the 'g2d.fill' - * method fail with the following exception: - * - * This bug is found in OpenJDK but also is present in OracleJDK - * if run with - * -Dsun.java2d.renderer=sun.java2d.pisces.PiscesRenderingEngine - * - * The bug is related to sun.java2d.pisces.PiscesCache constructor - * that accepts '(int minx,int miny,int maxx,int maxy)' arguments: - * the internal 'bboxX1' and 'bboxY1' are set to values one greater - * than given maximum X and Y values. Those maximum values are then - * later used in AAShapePipe' class 'renderTiles' method, where a - * Y/X loop eventually calls 'GeneralCompositePipe' class - * 'renderPathTile' method. In that method, the operation will - * eventually call 'IntegerInterleavedRaster' class - * 'createWritableChild' method with arguments: - * - *
    - *
  • x=800 - *
  • y=0 - *
  • width=2 (this value is too high: should be 1) - *
  • height=32 - *
  • x0=0 - *
  • y0=0 - *
  • bandList[]=null - *
- * - * This calls for a sub-raster with bounds that fall outside the - * original raster, and therefore the 'createWritableChild' method - * correctly throws 'RasterFormatException'. - * - * The bug is closely related to the use of a custom Composite - * implementation, which are quite rare. The application where this - * bug was first detected implements a high-quality PDF rendering - * engine that needs custom Composite operations to properly - * implement PDF advanced color blending and masking operators. - */ - - public static void main(String args[]) - { - BufferedImage bi = new BufferedImage(801,1202, - BufferedImage.TYPE_INT_ARGB); - Graphics2D g2d = bi.createGraphics(); - GeneralPath gp = new GeneralPath(); - AffineTransform m = new AffineTransform(2.483489907915543, - 0.0, - 0.0, - -2.4844977263331955, - 0.0, - 1202.0); - Composite c = new CustomComposite(); - - gp.moveTo(-4.511, -14.349); - gp.lineTo(327.489, -14.349); - gp.lineTo(327.489, 494.15); - gp.lineTo(-4.511, 494.15); - gp.closePath(); - - g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, - RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); - g2d.setRenderingHint(RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY); - g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, - RenderingHints.VALUE_COLOR_RENDER_QUALITY); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST, - Integer.valueOf(140)); - g2d.setRenderingHint(RenderingHints.KEY_DITHERING, - RenderingHints.VALUE_DITHER_ENABLE); - g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, - RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, - RenderingHints.VALUE_STROKE_NORMALIZE); - g2d.setPaint(Color.red); - g2d.setComposite(c); - g2d.setTransform(m); - try { - g2d.fill(gp); - } catch (RasterFormatException rfe) { - System.out.println("Test failed"); - throw new RuntimeException("xmax/ymax rounding cause RasterFormatException: " + rfe); - } - g2d.dispose(); - System.out.println("Test passed"); - } - - // === CustomComposite === - - /** - * Dummy custom Composite implementation. - */ - - public static class CustomComposite implements Composite - { - @Override - public CompositeContext createContext(ColorModel srcColorModel, - ColorModel dstColorModel, - RenderingHints hints) - { - return new CustomCompositeContext(); - } - - // === CustomCompositeContext === - - /** - * Dummy custom CompositeContext implementation. - */ - - public static class CustomCompositeContext implements CompositeContext - { - - @Override - public void dispose() - { - // NOP - } - - @Override - public void compose(Raster src,Raster dstIn,WritableRaster dstOut) - { - // NOP - } - } - } -} --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/OpenJDKFillBug.java 2017-11-06 15:02:44.044239133 -0800 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015, 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. + */ +import java.awt.Color; +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.GeneralPath; +import java.awt.image.BufferedImage; +import java.awt.image.ColorModel; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.awt.image.RasterFormatException; + +/** + * @test + * @bug 8048782 + * @summary Test program that demonstrates PiscesRendering bug in + * OpenJDK 1.7.0.60 (and probably in all other OpenJDK versions, too). + */ + +public class OpenJDKFillBug +{ + /** + * Test program that demonstrates a bug in OpenJDK 1.7.0.60 (and + * probably in all other OpenJDK versions, too). To see the bug, simply run + * the 'main' program with OpenJDK. The bug makes the 'g2d.fill' + * method fail with the following exception: + * + * This bug is found in OpenJDK but also is present in OracleJDK + * if run with + * -Dsun.java2d.renderer=sun.java2d.pisces.PiscesRenderingEngine + * + * The bug is related to sun.java2d.pisces.PiscesCache constructor + * that accepts '(int minx,int miny,int maxx,int maxy)' arguments: + * the internal 'bboxX1' and 'bboxY1' are set to values one greater + * than given maximum X and Y values. Those maximum values are then + * later used in AAShapePipe' class 'renderTiles' method, where a + * Y/X loop eventually calls 'GeneralCompositePipe' class + * 'renderPathTile' method. In that method, the operation will + * eventually call 'IntegerInterleavedRaster' class + * 'createWritableChild' method with arguments: + * + *
    + *
  • x=800 + *
  • y=0 + *
  • width=2 (this value is too high: should be 1) + *
  • height=32 + *
  • x0=0 + *
  • y0=0 + *
  • bandList[]=null + *
+ * + * This calls for a sub-raster with bounds that fall outside the + * original raster, and therefore the 'createWritableChild' method + * correctly throws 'RasterFormatException'. + * + * The bug is closely related to the use of a custom Composite + * implementation, which are quite rare. The application where this + * bug was first detected implements a high-quality PDF rendering + * engine that needs custom Composite operations to properly + * implement PDF advanced color blending and masking operators. + */ + + public static void main(String args[]) + { + BufferedImage bi = new BufferedImage(801,1202, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g2d = bi.createGraphics(); + GeneralPath gp = new GeneralPath(); + AffineTransform m = new AffineTransform(2.483489907915543, + 0.0, + 0.0, + -2.4844977263331955, + 0.0, + 1202.0); + Composite c = new CustomComposite(); + + gp.moveTo(-4.511, -14.349); + gp.lineTo(327.489, -14.349); + gp.lineTo(327.489, 494.15); + gp.lineTo(-4.511, 494.15); + gp.closePath(); + + g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, + RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, + RenderingHints.VALUE_COLOR_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_LCD_CONTRAST, + Integer.valueOf(140)); + g2d.setRenderingHint(RenderingHints.KEY_DITHERING, + RenderingHints.VALUE_DITHER_ENABLE); + g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, + RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, + RenderingHints.VALUE_STROKE_NORMALIZE); + g2d.setPaint(Color.red); + g2d.setComposite(c); + g2d.setTransform(m); + try { + g2d.fill(gp); + } catch (RasterFormatException rfe) { + System.out.println("Test failed"); + throw new RuntimeException("xmax/ymax rounding cause RasterFormatException: " + rfe); + } + g2d.dispose(); + System.out.println("Test passed"); + } + + // === CustomComposite === + + /** + * Dummy custom Composite implementation. + */ + + public static class CustomComposite implements Composite + { + @Override + public CompositeContext createContext(ColorModel srcColorModel, + ColorModel dstColorModel, + RenderingHints hints) + { + return new CustomCompositeContext(); + } + + // === CustomCompositeContext === + + /** + * Dummy custom CompositeContext implementation. + */ + + public static class CustomCompositeContext implements CompositeContext + { + + @Override + public void dispose() + { + // NOP + } + + @Override + public void compose(Raster src,Raster dstIn,WritableRaster dstOut) + { + // NOP + } + } + } +} --- old/test/jdk/sun/java2d/pisces/Renderer/Test7019861.java 2017-11-06 15:02:44.648239116 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,76 +0,0 @@ -/* - * 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; - } -} --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/Renderer/Test7019861.java 2017-11-06 15:02:44.420239122 -0800 @@ -0,0 +1,76 @@ +/* + * 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; + } +} --- old/test/jdk/sun/java2d/pisces/Renderer/TestNPE.java 2017-11-06 15:02:45.108239103 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2009, 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 6887494 - * - * @summary Verifies that no NullPointerException is thrown in Pisces Renderer - * under certain circumstances. - * - * @run main TestNPE - */ - -import java.awt.*; -import java.awt.geom.*; -import java.awt.image.BufferedImage; - -public class TestNPE { - - private static void paint(Graphics g) { - Graphics2D g2d = (Graphics2D) g; - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - g2d.setClip(0, 0, 0, 0); - g2d.setTransform( - new AffineTransform(4.0f, 0.0f, 0.0f, 4.0f, -1248.0f, -744.0f)); - g2d.draw(new Line2D.Float(131.21428571428572f, 33.0f, - 131.21428571428572f, 201.0f)); - } - - public static void main(String[] args) { - BufferedImage im = new BufferedImage(100, 100, - BufferedImage.TYPE_INT_ARGB); - - // Trigger exception in main thread. - Graphics g = im.getGraphics(); - paint(g); - } -} --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/Renderer/TestNPE.java 2017-11-06 15:02:44.800239112 -0800 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2009, 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 6887494 + * + * @summary Verifies that no NullPointerException is thrown in Pisces Renderer + * under certain circumstances. + * + * @run main TestNPE + */ + +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.BufferedImage; + +public class TestNPE { + + private static void paint(Graphics g) { + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setClip(0, 0, 0, 0); + g2d.setTransform( + new AffineTransform(4.0f, 0.0f, 0.0f, 4.0f, -1248.0f, -744.0f)); + g2d.draw(new Line2D.Float(131.21428571428572f, 33.0f, + 131.21428571428572f, 201.0f)); + } + + public static void main(String[] args) { + BufferedImage im = new BufferedImage(100, 100, + BufferedImage.TYPE_INT_ARGB); + + // Trigger exception in main thread. + Graphics g = im.getGraphics(); + paint(g); + } +} --- old/test/jdk/sun/pisces/ScaleTest.java 2017-11-06 15:02:45.520239092 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2009, 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. - */ - -import java.awt.*; -import java.awt.geom.Ellipse2D; -import java.awt.image.BufferedImage; -import java.io.File; -import javax.imageio.ImageIO; - - -public class ScaleTest { - public static void main(String[] args) throws Exception { - BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); - Graphics2D g = image.createGraphics(); - - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setPaint(Color.WHITE); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - g.scale(.9, .9); - g.setPaint(Color.BLACK); - g.setStroke(new BasicStroke(0.5f)); - g.draw(new Ellipse2D.Double(25, 25, 150, 150)); - - // To visually check it - //ImageIO.write(image, "PNG", new File(args[0])); - - boolean nonWhitePixelFound = false; - for (int x = 100; x < 200; ++x) { - if (image.getRGB(x, 90) != Color.WHITE.getRGB()) { - nonWhitePixelFound = true; - break; - } - } - if (!nonWhitePixelFound) { - throw new RuntimeException("A circle is rendered like a 'C' shape."); - } - } -} --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/ScaleTest.java 2017-11-06 15:02:45.256239099 -0800 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2009, 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. + */ + +import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.awt.image.BufferedImage; +import java.io.File; +import javax.imageio.ImageIO; + + +public class ScaleTest { + public static void main(String[] args) throws Exception { + BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setPaint(Color.WHITE); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + g.scale(.9, .9); + g.setPaint(Color.BLACK); + g.setStroke(new BasicStroke(0.5f)); + g.draw(new Ellipse2D.Double(25, 25, 150, 150)); + + // To visually check it + //ImageIO.write(image, "PNG", new File(args[0])); + + boolean nonWhitePixelFound = false; + for (int x = 100; x < 200; ++x) { + if (image.getRGB(x, 90) != Color.WHITE.getRGB()) { + nonWhitePixelFound = true; + break; + } + } + if (!nonWhitePixelFound) { + throw new RuntimeException("A circle is rendered like a 'C' shape."); + } + } +} --- old/test/jdk/sun/pisces/StrokeShapeTest.java 2017-11-06 15:02:45.872239082 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2009, 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. - */ -import java.awt.*; -import java.awt.geom.Ellipse2D; -import java.awt.geom.GeneralPath; -import java.awt.image.BufferedImage; -import java.io.File; - -import javax.imageio.ImageIO; - -/** - * @author chrisn@google.com (Chris Nokleberg) - * @author yamauchi@google.com (Hiroshi Yamauchi) - */ -public class StrokeShapeTest { - public static void main(String[] args) throws Exception { - BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); - Graphics2D g = image.createGraphics(); - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setPaint(Color.WHITE); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - g.translate(25, 100); - - Stroke stroke = new BasicStroke(200, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); - Shape shape = new Polygon(new int[] {0, 1500, 0}, new int[] {750, 0, -750}, 3); - - g.scale(.1, .1); - g.setPaint(Color.BLACK); - g.setStroke(stroke); - g.draw(shape); - g.setPaint(Color.RED); - g.fill(stroke.createStrokedShape(shape)); - - // To visually check it - //ImageIO.write(image, "PNG", new File(args[0])); - - boolean blackPixelFound = false; - outer: - for (int x = 0; x < 200; ++x) { - for (int y = 0; y < 200; ++y) { - if (image.getRGB(x, y) == Color.BLACK.getRGB()) { - blackPixelFound = true; - break outer; - } - } - } - if (blackPixelFound) { - throw new RuntimeException("The shape hasn't been filled in red."); - } - } -} --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/StrokeShapeTest.java 2017-11-06 15:02:45.624239089 -0800 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2009, 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. + */ +import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.awt.geom.GeneralPath; +import java.awt.image.BufferedImage; +import java.io.File; + +import javax.imageio.ImageIO; + +/** + * @author chrisn@google.com (Chris Nokleberg) + * @author yamauchi@google.com (Hiroshi Yamauchi) + */ +public class StrokeShapeTest { + public static void main(String[] args) throws Exception { + BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setPaint(Color.WHITE); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + g.translate(25, 100); + + Stroke stroke = new BasicStroke(200, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); + Shape shape = new Polygon(new int[] {0, 1500, 0}, new int[] {750, 0, -750}, 3); + + g.scale(.1, .1); + g.setPaint(Color.BLACK); + g.setStroke(stroke); + g.draw(shape); + g.setPaint(Color.RED); + g.fill(stroke.createStrokedShape(shape)); + + // To visually check it + //ImageIO.write(image, "PNG", new File(args[0])); + + boolean blackPixelFound = false; + outer: + for (int x = 0; x < 200; ++x) { + for (int y = 0; y < 200; ++y) { + if (image.getRGB(x, y) == Color.BLACK.getRGB()) { + blackPixelFound = true; + break outer; + } + } + } + if (blackPixelFound) { + throw new RuntimeException("The shape hasn't been filled in red."); + } + } +} --- old/test/jdk/sun/java2d/pisces/Test7036754.java 2017-11-06 15:02:46.300239071 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,58 +0,0 @@ -/* - * 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 7036754 - * - * @summary Verifies that there are no non-finite numbers when stroking - * certain quadratic curves. - * - * @author Jim Graham - * @run main Test7036754 - */ - -import java.awt.*; -import java.awt.geom.*; - -public class Test7036754 { - public static void main(String argv[]) { - Shape s = new QuadCurve2D.Float(839.24677f, 508.97888f, - 839.2953f, 508.97122f, - 839.3438f, 508.96353f); - s = new BasicStroke(10f).createStrokedShape(s); - float nsegs[] = {2, 2, 4, 6, 0}; - float coords[] = new float[6]; - PathIterator pi = s.getPathIterator(null); - while (!pi.isDone()) { - int type = pi.currentSegment(coords); - for (int i = 0; i < nsegs[type]; i++) { - float c = coords[i]; - if (Float.isNaN(c) || Float.isInfinite(c)) { - throw new RuntimeException("bad value in stroke"); - } - } - pi.next(); - } - } -} --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/Test7036754.java 2017-11-06 15:02:46.008239079 -0800 @@ -0,0 +1,58 @@ +/* + * 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 7036754 + * + * @summary Verifies that there are no non-finite numbers when stroking + * certain quadratic curves. + * + * @author Jim Graham + * @run main Test7036754 + */ + +import java.awt.*; +import java.awt.geom.*; + +public class Test7036754 { + public static void main(String argv[]) { + Shape s = new QuadCurve2D.Float(839.24677f, 508.97888f, + 839.2953f, 508.97122f, + 839.3438f, 508.96353f); + s = new BasicStroke(10f).createStrokedShape(s); + float nsegs[] = {2, 2, 4, 6, 0}; + float coords[] = new float[6]; + PathIterator pi = s.getPathIterator(null); + while (!pi.isDone()) { + int type = pi.currentSegment(coords); + for (int i = 0; i < nsegs[type]; i++) { + float c = coords[i]; + if (Float.isNaN(c) || Float.isInfinite(c)) { + throw new RuntimeException("bad value in stroke"); + } + } + pi.next(); + } + } +} --- old/test/jdk/sun/pisces/ThinLineTest.java 2017-11-06 15:02:46.652239061 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2009, 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. - */ -import java.awt.*; -import java.awt.geom.Ellipse2D; -import java.awt.image.BufferedImage; -import java.io.File; -import javax.imageio.ImageIO; - -/** - * @author chrisn@google.com (Chris Nokleberg) - * @author yamauchi@google.com (Hiroshi Yamauchi) - */ -public class ThinLineTest { - private static final int PIXEL = 381; - - public static void main(String[] args) throws Exception { - BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); - Graphics2D g = image.createGraphics(); - - g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - g.setPaint(Color.WHITE); - g.fill(new Rectangle(image.getWidth(), image.getHeight())); - - g.scale(0.5 / PIXEL, 0.5 / PIXEL); - g.setPaint(Color.BLACK); - g.setStroke(new BasicStroke(PIXEL)); - g.draw(new Ellipse2D.Double(PIXEL * 50, PIXEL * 50, PIXEL * 300, PIXEL * 300)); - - // To visually check it - //ImageIO.write(image, "PNG", new File(args[0])); - - boolean nonWhitePixelFound = false; - for (int x = 0; x < 200; ++x) { - if (image.getRGB(x, 100) != Color.WHITE.getRGB()) { - nonWhitePixelFound = true; - break; - } - } - if (!nonWhitePixelFound) { - throw new RuntimeException("The thin line disappeared."); - } - } -} --- /dev/null 2017-08-10 09:28:49.381064065 -0700 +++ new/test/jdk/sun/java2d/marlin/ThinLineTest.java 2017-11-06 15:02:46.420239067 -0800 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2009, 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. + */ +import java.awt.*; +import java.awt.geom.Ellipse2D; +import java.awt.image.BufferedImage; +import java.io.File; +import javax.imageio.ImageIO; + +/** + * @author chrisn@google.com (Chris Nokleberg) + * @author yamauchi@google.com (Hiroshi Yamauchi) + */ +public class ThinLineTest { + private static final int PIXEL = 381; + + public static void main(String[] args) throws Exception { + BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setPaint(Color.WHITE); + g.fill(new Rectangle(image.getWidth(), image.getHeight())); + + g.scale(0.5 / PIXEL, 0.5 / PIXEL); + g.setPaint(Color.BLACK); + g.setStroke(new BasicStroke(PIXEL)); + g.draw(new Ellipse2D.Double(PIXEL * 50, PIXEL * 50, PIXEL * 300, PIXEL * 300)); + + // To visually check it + //ImageIO.write(image, "PNG", new File(args[0])); + + boolean nonWhitePixelFound = false; + for (int x = 0; x < 200; ++x) { + if (image.getRGB(x, 100) != Color.WHITE.getRGB()) { + nonWhitePixelFound = true; + break; + } + } + if (!nonWhitePixelFound) { + throw new RuntimeException("The thin line disappeared."); + } + } +} --- old/test/jdk/sun/pisces/TEST.properties 2017-11-06 15:02:46.984239052 -0800 +++ /dev/null 2017-08-10 09:28:49.381064065 -0700 @@ -1 +0,0 @@ -modules=java.desktop