0001: /*
0002: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.java2d.loops;
0027:
0028: import java.awt.geom.Path2D;
0029: import java.awt.geom.PathIterator;
0030: import java.awt.geom.QuadCurve2D;
0031: import sun.awt.SunHints;
0032: import java.util.*;
0033:
0034: /* This is the java implementation of the native code from
0035: * src/share/native/sun/java2d/loops/ProcessPath.[c,h]
0036: * This code is written to be as much similar to the native
0037: * as it possible. So, it sometimes does not follow java naming conventions.
0038: *
0039: * It's important to keep this code synchronized with native one. See more
0040: * comments, description and high level scheme of the rendering process in the
0041: * ProcessPath.c
0042: */
0043:
0044: public class ProcessPath {
0045:
0046: /* Public interfaces and methods for drawing and filling general paths */
0047:
0048: public static abstract class DrawHandler {
0049: public int xMin;
0050: public int yMin;
0051: public int xMax;
0052: public int yMax;
0053: public float xMinf;
0054: public float yMinf;
0055: public float xMaxf;
0056: public float yMaxf;
0057:
0058: public int strokeControl;
0059:
0060: public DrawHandler(int xMin, int yMin, int xMax, int yMax,
0061: int strokeControl) {
0062: setBounds(xMin, yMin, xMax, yMax, strokeControl);
0063: }
0064:
0065: public void setBounds(int xMin, int yMin, int xMax, int yMax) {
0066: this .xMin = xMin;
0067: this .yMin = yMin;
0068: this .xMax = xMax;
0069: this .yMax = yMax;
0070:
0071: /* Setting up fractional clipping box
0072: *
0073: * We are using following float -> int mapping:
0074: *
0075: * xi = floor(xf + 0.5)
0076: *
0077: * So, fractional values that hit the [xmin, xmax) integer interval
0078: * will be situated inside the [xmin-0.5, xmax - 0.5) fractional
0079: * interval. We are using EPSF constant to provide that upper
0080: * boundary is not included.
0081: */
0082: xMinf = xMin - 0.5f;
0083: yMinf = yMin - 0.5f;
0084: xMaxf = xMax - 0.5f - EPSF;
0085: yMaxf = yMax - 0.5f - EPSF;
0086: }
0087:
0088: public void setBounds(int xMin, int yMin, int xMax, int yMax,
0089: int strokeControl) {
0090: this .strokeControl = strokeControl;
0091: setBounds(xMin, yMin, xMax, yMax);
0092: }
0093:
0094: public void adjustBounds(int bxMin, int byMin, int bxMax,
0095: int byMax) {
0096: if (xMin > bxMin)
0097: bxMin = xMin;
0098: if (xMax < bxMax)
0099: bxMax = xMax;
0100: if (yMin > byMin)
0101: byMin = yMin;
0102: if (yMax < byMax)
0103: byMax = yMax;
0104: setBounds(bxMin, byMin, bxMax, byMax);
0105: }
0106:
0107: public DrawHandler(int xMin, int yMin, int xMax, int yMax) {
0108: this (xMin, yMin, xMax, yMax, SunHints.INTVAL_STROKE_DEFAULT);
0109: }
0110:
0111: public abstract void drawLine(int x0, int y0, int x1, int y1);
0112:
0113: public abstract void drawPixel(int x0, int y0);
0114:
0115: public abstract void drawScanline(int x0, int x1, int y0);
0116: }
0117:
0118: public interface EndSubPathHandler {
0119: public void processEndSubPath();
0120: }
0121:
0122: public static final int PH_MODE_DRAW_CLIP = 0;
0123: public static final int PH_MODE_FILL_CLIP = 1;
0124:
0125: public static abstract class ProcessHandler implements
0126: EndSubPathHandler {
0127: DrawHandler dhnd;
0128: int clipMode;
0129:
0130: public ProcessHandler(DrawHandler dhnd, int clipMode) {
0131: this .dhnd = dhnd;
0132: this .clipMode = clipMode;
0133: }
0134:
0135: public abstract void processFixedLine(int x1, int y1, int x2,
0136: int y2, int[] pixelInfo, boolean checkBounds,
0137: boolean endSubPath);
0138: }
0139:
0140: public static EndSubPathHandler noopEndSubPathHandler = new EndSubPathHandler() {
0141: public void processEndSubPath() {
0142: }
0143: };
0144:
0145: public static boolean fillPath(DrawHandler dhnd, Path2D.Float p2df,
0146: int transX, int transY) {
0147: FillProcessHandler fhnd = new FillProcessHandler(dhnd);
0148: if (!doProcessPath(fhnd, p2df, transX, transY)) {
0149: return false;
0150: }
0151: FillPolygon(fhnd, p2df.getWindingRule());
0152: return true;
0153: }
0154:
0155: public static boolean drawPath(DrawHandler dhnd,
0156: EndSubPathHandler endSubPath, Path2D.Float p2df,
0157: int transX, int transY) {
0158: return doProcessPath(new DrawProcessHandler(dhnd, endSubPath),
0159: p2df, transX, transY);
0160: }
0161:
0162: public static boolean drawPath(DrawHandler dhnd, Path2D.Float p2df,
0163: int transX, int transY) {
0164: return doProcessPath(new DrawProcessHandler(dhnd,
0165: noopEndSubPathHandler), p2df, transX, transY);
0166: }
0167:
0168: /* Private implementation of the rendering process */
0169:
0170: /* Boundaries used for skipping huge path segments */
0171: private static final float UPPER_BND = Float.MAX_VALUE / 4.0f;
0172: private static final float LOWER_BND = -UPPER_BND;
0173:
0174: /* Precision (in bits) used in forward differencing */
0175: private static final int FWD_PREC = 7;
0176:
0177: /* Precision (in bits) used for the rounding in the midpoint test */
0178: private static final int MDP_PREC = 10;
0179:
0180: private static final int MDP_MULT = 1 << MDP_PREC;
0181: private static final int MDP_HALF_MULT = MDP_MULT >> 1;
0182:
0183: /* Boundaries used for clipping large path segments (those are inside
0184: * [UPPER/LOWER]_BND boundaries)
0185: */
0186: private static final int UPPER_OUT_BND = 1 << (30 - MDP_PREC);
0187: private static final int LOWER_OUT_BND = -UPPER_OUT_BND;
0188:
0189: /* Calculation boundaries. They are used for switching to the more slow but
0190: * allowing larger input values method of calculation of the initial values
0191: * of the scan converted line segments inside the FillPolygon
0192: */
0193: private static final float CALC_UBND = 1 << (30 - MDP_PREC);
0194: private static final float CALC_LBND = -CALC_UBND;
0195:
0196: /* Following constants are used for providing open boundaries of the
0197: * intervals
0198: */
0199: public static final int EPSFX = 1;
0200: public static final float EPSF = ((float) EPSFX) / MDP_MULT;
0201:
0202: /* Bit mask used to separate whole part from the fraction part of the
0203: * number
0204: */
0205: private static final int MDP_W_MASK = -MDP_MULT;
0206:
0207: /* Bit mask used to separate fractional part from the whole part of the
0208: * number
0209: */
0210: private static final int MDP_F_MASK = MDP_MULT - 1;
0211:
0212: /*
0213: * Constants for the forward differencing
0214: * of the cubic and quad curves
0215: */
0216:
0217: /* Maximum size of the cubic curve (calculated as the size of the bounding
0218: * box of the control points) which could be rendered without splitting
0219: */
0220: private static final int MAX_CUB_SIZE = 256;
0221:
0222: /* Maximum size of the quad curve (calculated as the size of the bounding
0223: * box of the control points) which could be rendered without splitting
0224: */
0225: private static final int MAX_QUAD_SIZE = 1024;
0226:
0227: /* Default power of 2 steps used in the forward differencing. Here DF prefix
0228: * stands for DeFault. Constants below are used as initial values for the
0229: * adaptive forward differencing algorithm.
0230: */
0231: private static final int DF_CUB_STEPS = 3;
0232: private static final int DF_QUAD_STEPS = 2;
0233:
0234: /* Shift of the current point of the curve for preparing to the midpoint
0235: * rounding
0236: */
0237: private static final int DF_CUB_SHIFT = FWD_PREC + DF_CUB_STEPS * 3
0238: - MDP_PREC;
0239: private static final int DF_QUAD_SHIFT = FWD_PREC + DF_QUAD_STEPS
0240: * 2 - MDP_PREC;
0241:
0242: /* Default amount of steps of the forward differencing */
0243: private static final int DF_CUB_COUNT = (1 << DF_CUB_STEPS);
0244: private static final int DF_QUAD_COUNT = (1 << DF_QUAD_STEPS);
0245:
0246: /* Default boundary constants used to check the necessity of the restepping
0247: */
0248: private static final int DF_CUB_DEC_BND = 1 << DF_CUB_STEPS * 3
0249: + FWD_PREC + 2;
0250: private static final int DF_CUB_INC_BND = 1 << DF_CUB_STEPS * 3
0251: + FWD_PREC - 1;
0252: private static final int DF_QUAD_DEC_BND = 1 << DF_QUAD_STEPS * 2
0253: + FWD_PREC + 2;
0254: private static final int DF_QUAD_INC_BND = 1 << DF_QUAD_STEPS * 2
0255: + FWD_PREC - 1;
0256:
0257: /* Multipliers for the coefficients of the polynomial form of the cubic and
0258: * quad curves representation
0259: */
0260: private static final int CUB_A_SHIFT = FWD_PREC;
0261: private static final int CUB_B_SHIFT = (DF_CUB_STEPS + FWD_PREC + 1);
0262: private static final int CUB_C_SHIFT = (DF_CUB_STEPS * 2 + FWD_PREC);
0263:
0264: private static final int CUB_A_MDP_MULT = (1 << CUB_A_SHIFT);
0265: private static final int CUB_B_MDP_MULT = (1 << CUB_B_SHIFT);
0266: private static final int CUB_C_MDP_MULT = (1 << CUB_C_SHIFT);
0267:
0268: private static final int QUAD_A_SHIFT = FWD_PREC;
0269: private static final int QUAD_B_SHIFT = (DF_QUAD_STEPS + FWD_PREC);
0270:
0271: private static final int QUAD_A_MDP_MULT = (1 << QUAD_A_SHIFT);
0272: private static final int QUAD_B_MDP_MULT = (1 << QUAD_B_SHIFT);
0273:
0274: /* Clipping macros for drawing and filling algorithms */
0275: private static float CLIP(float a1, float b1, float a2, float b2,
0276: double t) {
0277: return (float) (b1 + (double) (t - a1) * (b2 - b1) / (a2 - a1));
0278: }
0279:
0280: private static int CLIP(int a1, int b1, int a2, int b2, double t) {
0281: return (int) (b1 + (double) (t - a1) * (b2 - b1) / (a2 - a1));
0282: }
0283:
0284: private static final int CRES_MIN_CLIPPED = 0;
0285: private static final int CRES_MAX_CLIPPED = 1;
0286: private static final int CRES_NOT_CLIPPED = 3;
0287: private static final int CRES_INVISIBLE = 4;
0288:
0289: private static boolean IS_CLIPPED(int res) {
0290: return res == CRES_MIN_CLIPPED || res == CRES_MAX_CLIPPED;
0291: }
0292:
0293: /* This is java implementation of the macro from ProcessGeneralPath.c.
0294: * To keep the logic of the java code similar to the native one
0295: * array and set of indexes are used to point out the data.
0296: */
0297: private static int TESTANDCLIP(float LINE_MIN, float LINE_MAX,
0298: float[] c, int a1, int b1, int a2, int b2) {
0299: double t;
0300: int res = CRES_NOT_CLIPPED;
0301: if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) {
0302: if (c[a1] < (LINE_MIN)) {
0303: if (c[a2] < (LINE_MIN)) {
0304: return CRES_INVISIBLE;
0305: }
0306: ;
0307: res = CRES_MIN_CLIPPED;
0308: t = (LINE_MIN);
0309: } else {
0310: if (c[a2] > (LINE_MAX)) {
0311: return CRES_INVISIBLE;
0312: }
0313: ;
0314: res = CRES_MAX_CLIPPED;
0315: t = (LINE_MAX);
0316: }
0317: c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t);
0318: c[a1] = (float) t;
0319: }
0320: return res;
0321: }
0322:
0323: /* Integer version of the method above */
0324: private static int TESTANDCLIP(int LINE_MIN, int LINE_MAX, int[] c,
0325: int a1, int b1, int a2, int b2) {
0326: double t;
0327: int res = CRES_NOT_CLIPPED;
0328: if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) {
0329: if (c[a1] < (LINE_MIN)) {
0330: if (c[a2] < (LINE_MIN)) {
0331: return CRES_INVISIBLE;
0332: }
0333: ;
0334: res = CRES_MIN_CLIPPED;
0335: t = (LINE_MIN);
0336: } else {
0337: if (c[a2] > (LINE_MAX)) {
0338: return CRES_INVISIBLE;
0339: }
0340: ;
0341: res = CRES_MAX_CLIPPED;
0342: t = (LINE_MAX);
0343: }
0344: c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t);
0345: c[a1] = (int) t;
0346: }
0347: return res;
0348: }
0349:
0350: /* Following method is used for clipping and clumping filled shapes.
0351: * An example of this process is shown on the picture below:
0352: * ----+ ----+
0353: * |/ | |/ |
0354: * + | + |
0355: * /| | I |
0356: * / | | I |
0357: * | | | ===> I |
0358: * \ | | I |
0359: * \| | I |
0360: * + | + |
0361: * |\ | |\ |
0362: * | ----+ | ----+
0363: * boundary boundary
0364: *
0365: * We can only perform clipping in case of right side of the output area
0366: * because all segments passed out the right boundary don't influence on the
0367: * result of scan conversion algorithm (it correctly handles half open
0368: * contours).
0369: *
0370: * This is java implementation of the macro from ProcessGeneralPath.c.
0371: * To keep the logic of the java code similar to the native one
0372: * array and set of indexes are used to point out the data.
0373: *
0374: */
0375: private static int CLIPCLAMP(float LINE_MIN, float LINE_MAX,
0376: float[] c, int a1, int b1, int a2, int b2, int a3, int b3) {
0377: c[a3] = c[a1];
0378: c[b3] = c[b1];
0379: int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2);
0380: if (res == CRES_MIN_CLIPPED) {
0381: c[a3] = c[a1];
0382: } else if (res == CRES_MAX_CLIPPED) {
0383: c[a3] = c[a1];
0384: res = CRES_MAX_CLIPPED;
0385: } else if (res == CRES_INVISIBLE) {
0386: if (c[a1] > LINE_MAX) {
0387: res = CRES_INVISIBLE;
0388: } else {
0389: c[a1] = LINE_MIN;
0390: c[a2] = LINE_MIN;
0391: res = CRES_NOT_CLIPPED;
0392: }
0393: }
0394: return res;
0395: }
0396:
0397: /* Integer version of the method above */
0398: private static int CLIPCLAMP(int LINE_MIN, int LINE_MAX, int[] c,
0399: int a1, int b1, int a2, int b2, int a3, int b3) {
0400: c[a3] = c[a1];
0401: c[b3] = c[b1];
0402: int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2);
0403: if (res == CRES_MIN_CLIPPED) {
0404: c[a3] = c[a1];
0405: } else if (res == CRES_MAX_CLIPPED) {
0406: c[a3] = c[a1];
0407: res = CRES_MAX_CLIPPED;
0408: } else if (res == CRES_INVISIBLE) {
0409: if (c[a1] > LINE_MAX) {
0410: res = CRES_INVISIBLE;
0411: } else {
0412: c[a1] = LINE_MIN;
0413: c[a2] = LINE_MIN;
0414: res = CRES_NOT_CLIPPED;
0415: }
0416: }
0417: return res;
0418: }
0419:
0420: private static class DrawProcessHandler extends ProcessHandler {
0421:
0422: EndSubPathHandler processESP;
0423:
0424: public DrawProcessHandler(DrawHandler dhnd,
0425: EndSubPathHandler processESP) {
0426: super (dhnd, PH_MODE_DRAW_CLIP);
0427: this .dhnd = dhnd;
0428: this .processESP = processESP;
0429: }
0430:
0431: public void processEndSubPath() {
0432: processESP.processEndSubPath();
0433: }
0434:
0435: void PROCESS_LINE(int fX0, int fY0, int fX1, int fY1,
0436: boolean checkBounds, int[] pixelInfo) {
0437: int X0 = fX0 >> MDP_PREC;
0438: int Y0 = fY0 >> MDP_PREC;
0439: int X1 = fX1 >> MDP_PREC;
0440: int Y1 = fY1 >> MDP_PREC;
0441:
0442: /* Handling lines having just one pixel */
0443: if (((X0 ^ X1) | (Y0 ^ Y1)) == 0) {
0444: if (checkBounds
0445: && (dhnd.yMin > Y0 || dhnd.yMax <= Y0
0446: || dhnd.xMin > X0 || dhnd.xMax <= X0))
0447: return;
0448:
0449: if (pixelInfo[0] == 0) {
0450: pixelInfo[0] = 1;
0451: pixelInfo[1] = X0;
0452: pixelInfo[2] = Y0;
0453: pixelInfo[3] = X0;
0454: pixelInfo[4] = Y0;
0455: dhnd.drawPixel(X0, Y0);
0456: } else if ((X0 != pixelInfo[3] || Y0 != pixelInfo[4])
0457: && (X0 != pixelInfo[1] || Y0 != pixelInfo[2])) {
0458: dhnd.drawPixel(X0, Y0);
0459: pixelInfo[3] = X0;
0460: pixelInfo[4] = Y0;
0461: }
0462: return;
0463: }
0464:
0465: if (!checkBounds
0466: || (dhnd.yMin <= Y0 && dhnd.yMax > Y0
0467: && dhnd.xMin <= X0 && dhnd.xMax > X0)) {
0468: /* Switch off first pixel of the line before drawing */
0469: if (pixelInfo[0] == 1
0470: && ((pixelInfo[1] == X0 && pixelInfo[2] == Y0) || (pixelInfo[3] == X0 && pixelInfo[4] == Y0))) {
0471: dhnd.drawPixel(X0, Y0);
0472: }
0473: }
0474:
0475: dhnd.drawLine(X0, Y0, X1, Y1);
0476:
0477: if (pixelInfo[0] == 0) {
0478: pixelInfo[0] = 1;
0479: pixelInfo[1] = X0;
0480: pixelInfo[2] = Y0;
0481: pixelInfo[3] = X0;
0482: pixelInfo[4] = Y0;
0483: }
0484:
0485: /* Switch on last pixel of the line if it was already
0486: * drawn during rendering of the previous segments
0487: */
0488: if ((pixelInfo[1] == X1 && pixelInfo[2] == Y1)
0489: || (pixelInfo[3] == X1 && pixelInfo[4] == Y1)) {
0490: if (checkBounds
0491: && (dhnd.yMin > Y1 || dhnd.yMax <= Y1
0492: || dhnd.xMin > X1 || dhnd.xMax <= X1)) {
0493: return;
0494: }
0495:
0496: dhnd.drawPixel(X1, Y1);
0497: }
0498: pixelInfo[3] = X1;
0499: pixelInfo[4] = Y1;
0500: }
0501:
0502: void PROCESS_POINT(int fX, int fY, boolean checkBounds,
0503: int[] pixelInfo) {
0504: int _X = fX >> MDP_PREC;
0505: int _Y = fY >> MDP_PREC;
0506: if (checkBounds
0507: && (dhnd.yMin > _Y || dhnd.yMax <= _Y
0508: || dhnd.xMin > _X || dhnd.xMax <= _X))
0509: return;
0510: /*
0511: * (_X,_Y) should be inside boundaries
0512: *
0513: * assert(dhnd.yMin <= _Y &&
0514: * dhnd.yMax > _Y &&
0515: * dhnd.xMin <= _X &&
0516: * dhnd.xMax > _X);
0517: *
0518: */
0519: if (pixelInfo[0] == 0) {
0520: pixelInfo[0] = 1;
0521: pixelInfo[1] = _X;
0522: pixelInfo[2] = _Y;
0523: pixelInfo[3] = _X;
0524: pixelInfo[4] = _Y;
0525: dhnd.drawPixel(_X, _Y);
0526: } else if ((_X != pixelInfo[3] || _Y != pixelInfo[4])
0527: && (_X != pixelInfo[1] || _Y != pixelInfo[2])) {
0528: dhnd.drawPixel(_X, _Y);
0529: pixelInfo[3] = _X;
0530: pixelInfo[4] = _Y;
0531: }
0532: }
0533:
0534: /* Drawing line with subpixel endpoints
0535: *
0536: * (x1, y1), (x2, y2) - fixed point coordinates of the endpoints
0537: * with MDP_PREC bits for the fractional part
0538: *
0539: * pixelInfo - structure which keeps drawing info for avoiding
0540: * multiple drawing at the same position on the
0541: * screen (required for the XOR mode of drawing)
0542: *
0543: * pixelInfo[0] - state of the drawing
0544: * 0 - no pixel drawn between
0545: * moveTo/close of the path
0546: * 1 - there are drawn pixels
0547: *
0548: * pixelInfo[1,2] - first pixel of the path
0549: * between moveTo/close of the
0550: * path
0551: *
0552: * pixelInfo[3,4] - last drawn pixel between
0553: * moveTo/close of the path
0554: *
0555: * checkBounds - flag showing necessity of checking the clip
0556: *
0557: */
0558: public void processFixedLine(int x1, int y1, int x2, int y2,
0559: int[] pixelInfo, boolean checkBounds, boolean endSubPath) {
0560:
0561: /* Checking if line is inside a (X,Y),(X+MDP_MULT,Y+MDP_MULT) box */
0562: int c = ((x1 ^ x2) | (y1 ^ y2));
0563: int rx1, ry1, rx2, ry2;
0564: if ((c & MDP_W_MASK) == 0) {
0565: /* Checking for the segments with integer coordinates having
0566: * the same start and end points
0567: */
0568: if (c == 0) {
0569: PROCESS_POINT(x1 + MDP_HALF_MULT, y1
0570: + MDP_HALF_MULT, checkBounds, pixelInfo);
0571: }
0572: return;
0573: }
0574:
0575: if (x1 == x2 || y1 == y2) {
0576: rx1 = x1 + MDP_HALF_MULT;
0577: rx2 = x2 + MDP_HALF_MULT;
0578: ry1 = y1 + MDP_HALF_MULT;
0579: ry2 = y2 + MDP_HALF_MULT;
0580: } else {
0581: /* Neither dx nor dy can be zero because of the check above */
0582: int dx = x2 - x1;
0583: int dy = y2 - y1;
0584:
0585: /* Floor of x1, y1, x2, y2 */
0586: int fx1 = x1 & MDP_W_MASK;
0587: int fy1 = y1 & MDP_W_MASK;
0588: int fx2 = x2 & MDP_W_MASK;
0589: int fy2 = y2 & MDP_W_MASK;
0590:
0591: /* Processing first endpoint */
0592: if (fx1 == x1 || fy1 == y1) {
0593: /* Adding MDP_HALF_MULT to the [xy]1 if f[xy]1 == [xy]1
0594: * will not affect the result
0595: */
0596: rx1 = x1 + MDP_HALF_MULT;
0597: ry1 = y1 + MDP_HALF_MULT;
0598: } else {
0599: /* Boundary at the direction from (x1,y1) to (x2,y2) */
0600: int bx1 = (x1 < x2) ? fx1 + MDP_MULT : fx1;
0601: int by1 = (y1 < y2) ? fy1 + MDP_MULT : fy1;
0602:
0603: /* intersection with column bx1 */
0604: int cross = y1 + ((bx1 - x1) * dy) / dx;
0605: if (cross >= fy1 && cross <= fy1 + MDP_MULT) {
0606: rx1 = bx1;
0607: ry1 = cross + MDP_HALF_MULT;
0608: } else {
0609: /* intersection with row by1 */
0610: cross = x1 + ((by1 - y1) * dx) / dy;
0611: rx1 = cross + MDP_HALF_MULT;
0612: ry1 = by1;
0613: }
0614: }
0615:
0616: /* Processing second endpoint */
0617: if (fx2 == x2 || fy2 == y2) {
0618: /* Adding MDP_HALF_MULT to the [xy]2 if f[xy]2 == [xy]2
0619: * will not affect the result
0620: */
0621: rx2 = x2 + MDP_HALF_MULT;
0622: ry2 = y2 + MDP_HALF_MULT;
0623: } else {
0624: /* Boundary at the direction from (x2,y2) to (x1,y1) */
0625: int bx2 = (x1 > x2) ? fx2 + MDP_MULT : fx2;
0626: int by2 = (y1 > y2) ? fy2 + MDP_MULT : fy2;
0627:
0628: /* intersection with column bx2 */
0629: int cross = y2 + ((bx2 - x2) * dy) / dx;
0630: if (cross >= fy2 && cross <= fy2 + MDP_MULT) {
0631: rx2 = bx2;
0632: ry2 = cross + MDP_HALF_MULT;
0633: } else {
0634: /* intersection with row by2 */
0635: cross = x2 + ((by2 - y2) * dx) / dy;
0636: rx2 = cross + MDP_HALF_MULT;
0637: ry2 = by2;
0638: }
0639: }
0640: }
0641: PROCESS_LINE(rx1, ry1, rx2, ry2, checkBounds, pixelInfo);
0642: }
0643: }
0644:
0645: /* Performing drawing of the monotonic in X and Y quadratic curves with
0646: * sizes less than MAX_QUAD_SIZE by using forward differencing method of
0647: * calculation. See comments to the DrawMonotonicQuad in the
0648: * ProcessGeneralPath.c
0649: */
0650: private static void DrawMonotonicQuad(ProcessHandler hnd,
0651: float[] coords, boolean checkBounds, int[] pixelInfo) {
0652:
0653: int x0 = (int) (coords[0] * MDP_MULT);
0654: int y0 = (int) (coords[1] * MDP_MULT);
0655:
0656: int xe = (int) (coords[4] * MDP_MULT);
0657: int ye = (int) (coords[5] * MDP_MULT);
0658:
0659: /* Extracting fractional part of coordinates of first control point */
0660: int px = (x0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;
0661: int py = (y0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;
0662:
0663: /* Setting default amount of steps */
0664: int count = DF_QUAD_COUNT;
0665:
0666: /* Setting default shift for preparing to the midpoint rounding */
0667: int shift = DF_QUAD_SHIFT;
0668:
0669: int ax = (int) ((coords[0] - 2 * coords[2] + coords[4]) * QUAD_A_MDP_MULT);
0670: int ay = (int) ((coords[1] - 2 * coords[3] + coords[5]) * QUAD_A_MDP_MULT);
0671:
0672: int bx = (int) ((-2 * coords[0] + 2 * coords[2]) * QUAD_B_MDP_MULT);
0673: int by = (int) ((-2 * coords[1] + 2 * coords[3]) * QUAD_B_MDP_MULT);
0674:
0675: int ddpx = 2 * ax;
0676: int ddpy = 2 * ay;
0677:
0678: int dpx = ax + bx;
0679: int dpy = ay + by;
0680:
0681: int x1, y1;
0682:
0683: int x2 = x0;
0684: int y2 = y0;
0685:
0686: int maxDD = Math.max(Math.abs(ddpx), Math.abs(ddpy));
0687:
0688: int dx = xe - x0;
0689: int dy = ye - y0;
0690:
0691: int x0w = x0 & MDP_W_MASK;
0692: int y0w = y0 & MDP_W_MASK;
0693:
0694: /* Perform decreasing step in 2 times if slope of the first forward
0695: * difference changes too quickly (more than a pixel per step in X or Y
0696: * direction). We can perform adjusting of the step size before the
0697: * rendering loop because the curvature of the quad curve remains the
0698: * same along all the curve
0699: */
0700: while (maxDD > DF_QUAD_DEC_BND) {
0701: dpx = (dpx << 1) - ax;
0702: dpy = (dpy << 1) - ay;
0703: count <<= 1;
0704: maxDD >>= 2;
0705: px <<= 2;
0706: py <<= 2;
0707: shift += 2;
0708: }
0709:
0710: while (count-- > 1) {
0711: px += dpx;
0712: py += dpy;
0713:
0714: dpx += ddpx;
0715: dpy += ddpy;
0716:
0717: x1 = x2;
0718: y1 = y2;
0719:
0720: x2 = x0w + (px >> shift);
0721: y2 = y0w + (py >> shift);
0722:
0723: /* Checking that we are not running out of the endpoint and bounding
0724: * violating coordinate. The check is pretty simple because the
0725: * curve passed to the DrawCubic already splitted into the
0726: * monotonic in X and Y pieces
0727: */
0728:
0729: /* Bounding x2 by xe */
0730: if (((xe - x2) ^ dx) < 0) {
0731: x2 = xe;
0732: }
0733:
0734: /* Bounding y2 by ye */
0735: if (((ye - y2) ^ dy) < 0) {
0736: y2 = ye;
0737: }
0738:
0739: hnd.processFixedLine(x1, y1, x2, y2, pixelInfo,
0740: checkBounds, false);
0741: }
0742:
0743: /* We are performing one step less than necessary and use actual
0744: * (xe,ye) endpoint of the curve instead of calculated. This prevent us
0745: * from running above the curve endpoint due to the accumulated errors
0746: * during the iterations.
0747: */
0748:
0749: hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds,
0750: false);
0751: }
0752:
0753: /*
0754: * Checking size of the quad curves and split them if necessary.
0755: * Calling DrawMonotonicQuad for the curves of the appropriate size.
0756: * Note: coords array could be changed
0757: */
0758: private static void ProcessMonotonicQuad(ProcessHandler hnd,
0759: float[] coords, int[] pixelInfo) {
0760:
0761: float[] coords1 = new float[6];
0762: float tx, ty;
0763: float xMin, yMin, xMax, yMax;
0764:
0765: xMin = xMax = coords[0];
0766: yMin = yMax = coords[1];
0767: for (int i = 2; i < 6; i += 2) {
0768: xMin = (xMin > coords[i]) ? coords[i] : xMin;
0769: xMax = (xMax < coords[i]) ? coords[i] : xMax;
0770: yMin = (yMin > coords[i + 1]) ? coords[i + 1] : yMin;
0771: yMax = (yMax < coords[i + 1]) ? coords[i + 1] : yMax;
0772: }
0773:
0774: if (hnd.clipMode == PH_MODE_DRAW_CLIP) {
0775:
0776: /* In case of drawing we could just skip curves which are
0777: * completely out of bounds
0778: */
0779: if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax
0780: || hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) {
0781: return;
0782: }
0783: } else {
0784:
0785: /* In case of filling we could skip curves which are above,
0786: * below and behind the right boundary of the visible area
0787: */
0788:
0789: if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax
0790: || hnd.dhnd.xMaxf < xMin) {
0791: return;
0792: }
0793:
0794: /* We could clamp x coordinates to the corresponding boundary
0795: * if the curve is completely behind the left one
0796: */
0797:
0798: if (hnd.dhnd.xMinf > xMax) {
0799: coords[0] = coords[2] = coords[4] = hnd.dhnd.xMinf;
0800: }
0801: }
0802:
0803: if (xMax - xMin > MAX_QUAD_SIZE || yMax - yMin > MAX_QUAD_SIZE) {
0804: coords1[4] = coords[4];
0805: coords1[5] = coords[5];
0806: coords1[2] = (coords[2] + coords[4]) / 2.0f;
0807: coords1[3] = (coords[3] + coords[5]) / 2.0f;
0808: coords[2] = (coords[0] + coords[2]) / 2.0f;
0809: coords[3] = (coords[1] + coords[3]) / 2.0f;
0810: coords[4] = coords1[0] = (coords[2] + coords1[2]) / 2.0f;
0811: coords[5] = coords1[1] = (coords[3] + coords1[3]) / 2.0f;
0812:
0813: ProcessMonotonicQuad(hnd, coords, pixelInfo);
0814:
0815: ProcessMonotonicQuad(hnd, coords1, pixelInfo);
0816: } else {
0817: DrawMonotonicQuad(hnd, coords,
0818: /* Set checkBounds parameter if curve intersects
0819: * boundary of the visible area. We know that the
0820: * curve is visible, so the check is pretty
0821: * simple
0822: */
0823: hnd.dhnd.xMinf >= xMin || hnd.dhnd.xMaxf <= xMax
0824: || hnd.dhnd.yMinf >= yMin
0825: || hnd.dhnd.yMaxf <= yMax, pixelInfo);
0826: }
0827: }
0828:
0829: /*
0830: * Split quadratic curve into monotonic in X and Y parts. Calling
0831: * ProcessMonotonicQuad for each monotonic piece of the curve.
0832: * Note: coords array could be changed
0833: */
0834: private static void ProcessQuad(ProcessHandler hnd, float[] coords,
0835: int[] pixelInfo) {
0836: /* Temporary array for holding parameters corresponding to the extreme
0837: * in X and Y points
0838: */
0839: double params[] = new double[2];
0840: int cnt = 0;
0841: double param;
0842:
0843: /* Simple check for monotonicity in X before searching for the extreme
0844: * points of the X(t) function. We first check if the curve is
0845: * monotonic in X by seeing if all of the X coordinates are strongly
0846: * ordered.
0847: */
0848: if ((coords[0] > coords[2] || coords[2] > coords[4])
0849: && (coords[0] < coords[2] || coords[2] < coords[4])) {
0850: /* Searching for extreme points of the X(t) function by solving
0851: * dX(t)
0852: * ---- = 0 equation
0853: * dt
0854: */
0855: double ax = coords[0] - 2 * coords[2] + coords[4];
0856: if (ax != 0) {
0857: /* Calculating root of the following equation
0858: * ax*t + bx = 0
0859: */
0860: double bx = coords[0] - coords[2];
0861:
0862: param = bx / ax;
0863: if (param < 1.0 && param > 0.0) {
0864: params[cnt++] = param;
0865: }
0866: }
0867: }
0868:
0869: /* Simple check for monotonicity in Y before searching for the extreme
0870: * points of the Y(t) function. We first check if the curve is
0871: * monotonic in Y by seeing if all of the Y coordinates are strongly
0872: * ordered.
0873: */
0874: if ((coords[1] > coords[3] || coords[3] > coords[5])
0875: && (coords[1] < coords[3] || coords[3] < coords[5])) {
0876: /* Searching for extreme points of the Y(t) function by solving
0877: * dY(t)
0878: * ----- = 0 equation
0879: * dt
0880: */
0881: double ay = coords[1] - 2 * coords[3] + coords[5];
0882:
0883: if (ay != 0) {
0884: /* Calculating root of the following equation
0885: * ay*t + by = 0
0886: */
0887: double by = coords[1] - coords[3];
0888:
0889: param = by / ay;
0890: if (param < 1.0 && param > 0.0) {
0891: if (cnt > 0) {
0892: /* Inserting parameter only if it differs from
0893: * already stored
0894: */
0895: if (params[0] > param) {
0896: params[cnt++] = params[0];
0897: params[0] = param;
0898: } else if (params[0] < param) {
0899: params[cnt++] = param;
0900: }
0901: } else {
0902: params[cnt++] = param;
0903: }
0904: }
0905: }
0906: }
0907:
0908: /* Processing obtained monotonic parts */
0909: switch (cnt) {
0910: case 0:
0911: break;
0912: case 1:
0913: ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
0914: (float) params[0]);
0915: break;
0916: case 2:
0917: ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
0918: (float) params[0]);
0919: param = params[1] - params[0];
0920: if (param > 0) {
0921: ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
0922: /* Scale parameter to match with
0923: * rest of the curve
0924: */
0925: (float) (param / (1.0 - params[0])));
0926: }
0927: break;
0928: }
0929:
0930: ProcessMonotonicQuad(hnd, coords, pixelInfo);
0931: }
0932:
0933: /*
0934: * Bite the piece of the quadratic curve from start point till the point
0935: * corresponding to the specified parameter then call ProcessQuad for the
0936: * bitten part.
0937: * Note: coords array will be changed
0938: */
0939: private static void ProcessFirstMonotonicPartOfQuad(
0940: ProcessHandler hnd, float[] coords, int[] pixelInfo, float t) {
0941: float[] coords1 = new float[6];
0942:
0943: coords1[0] = coords[0];
0944: coords1[1] = coords[1];
0945: coords1[2] = coords[0] + t * (coords[2] - coords[0]);
0946: coords1[3] = coords[1] + t * (coords[3] - coords[1]);
0947: coords[2] = coords[2] + t * (coords[4] - coords[2]);
0948: coords[3] = coords[3] + t * (coords[5] - coords[3]);
0949: coords[0] = coords1[4] = coords1[2] + t
0950: * (coords[2] - coords1[2]);
0951: coords[1] = coords1[5] = coords1[3] + t
0952: * (coords[3] - coords1[3]);
0953:
0954: ProcessMonotonicQuad(hnd, coords1, pixelInfo);
0955: }
0956:
0957: /* Performing drawing of the monotonic in X and Y cubic curves with sizes
0958: * less than MAX_CUB_SIZE by using forward differencing method of
0959: * calculation. See comments to the DrawMonotonicCubic in the
0960: * ProcessGeneralPath.c
0961: */
0962: private static void DrawMonotonicCubic(ProcessHandler hnd,
0963: float[] coords, boolean checkBounds, int[] pixelInfo) {
0964: int x0 = (int) (coords[0] * MDP_MULT);
0965: int y0 = (int) (coords[1] * MDP_MULT);
0966:
0967: int xe = (int) (coords[6] * MDP_MULT);
0968: int ye = (int) (coords[7] * MDP_MULT);
0969:
0970: /* Extracting fractional part of coordinates of first control point */
0971: int px = (x0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;
0972: int py = (y0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;
0973:
0974: /* Setting default boundary values for checking first and second forward
0975: * difference for the necessity of the restepping. See comments to the
0976: * boundary values in ProcessQuad for more info.
0977: */
0978: int incStepBnd = DF_CUB_INC_BND;
0979: int decStepBnd = DF_CUB_DEC_BND;
0980:
0981: /* Setting default amount of steps */
0982: int count = DF_CUB_COUNT;
0983:
0984: /* Setting default shift for preparing to the midpoint rounding */
0985: int shift = DF_CUB_SHIFT;
0986:
0987: int ax = (int) ((-coords[0] + 3 * coords[2] - 3 * coords[4] + coords[6]) * CUB_A_MDP_MULT);
0988: int ay = (int) ((-coords[1] + 3 * coords[3] - 3 * coords[5] + coords[7]) * CUB_A_MDP_MULT);
0989:
0990: int bx = (int) ((3 * coords[0] - 6 * coords[2] + 3 * coords[4]) * CUB_B_MDP_MULT);
0991: int by = (int) ((3 * coords[1] - 6 * coords[3] + 3 * coords[5]) * CUB_B_MDP_MULT);
0992:
0993: int cx = (int) ((-3 * coords[0] + 3 * coords[2]) * (CUB_C_MDP_MULT));
0994: int cy = (int) ((-3 * coords[1] + 3 * coords[3]) * (CUB_C_MDP_MULT));
0995:
0996: int dddpx = 6 * ax;
0997: int dddpy = 6 * ay;
0998:
0999: int ddpx = dddpx + bx;
1000: int ddpy = dddpy + by;
1001:
1002: int dpx = ax + (bx >> 1) + cx;
1003: int dpy = ay + (by >> 1) + cy;
1004:
1005: int x1, y1;
1006:
1007: int x2 = x0;
1008: int y2 = y0;
1009:
1010: /* Calculating whole part of the first point of the curve */
1011: int x0w = x0 & MDP_W_MASK;
1012: int y0w = y0 & MDP_W_MASK;
1013:
1014: int dx = xe - x0;
1015: int dy = ye - y0;
1016:
1017: while (count > 0) {
1018: /* Perform decreasing step in 2 times if necessary */
1019: while (Math.abs(ddpx) > decStepBnd
1020: || Math.abs(ddpy) > decStepBnd) {
1021: ddpx = (ddpx << 1) - dddpx;
1022: ddpy = (ddpy << 1) - dddpy;
1023: dpx = (dpx << 2) - (ddpx >> 1);
1024: dpy = (dpy << 2) - (ddpy >> 1);
1025: count <<= 1;
1026: decStepBnd <<= 3;
1027: incStepBnd <<= 3;
1028: px <<= 3;
1029: py <<= 3;
1030: shift += 3;
1031: }
1032:
1033: /* Perform increasing step in 2 times if necessary.
1034: * Note: we could do it only in even steps
1035: */
1036:
1037: while ((count & 1) == 0 && shift > DF_CUB_SHIFT
1038: && Math.abs(dpx) <= incStepBnd
1039: && Math.abs(dpy) <= incStepBnd) {
1040: dpx = (dpx >> 2) + (ddpx >> 3);
1041: dpy = (dpy >> 2) + (ddpy >> 3);
1042: ddpx = (ddpx + dddpx) >> 1;
1043: ddpy = (ddpy + dddpy) >> 1;
1044: count >>= 1;
1045: decStepBnd >>= 3;
1046: incStepBnd >>= 3;
1047: px >>= 3;
1048: py >>= 3;
1049: shift -= 3;
1050: }
1051:
1052: count--;
1053:
1054: /* Performing one step less than necessary and use actual (xe,ye)
1055: * curve's endpoint instead of calculated. This prevent us from
1056: * running above the curve endpoint due to the accumulated errors
1057: * during the iterations.
1058: */
1059: if (count > 0) {
1060: px += dpx;
1061: py += dpy;
1062:
1063: dpx += ddpx;
1064: dpy += ddpy;
1065: ddpx += dddpx;
1066: ddpy += dddpy;
1067:
1068: x1 = x2;
1069: y1 = y2;
1070:
1071: x2 = x0w + (px >> shift);
1072: y2 = y0w + (py >> shift);
1073:
1074: /* Checking that we are not running out of the endpoint and
1075: * bounding violating coordinate. The check is pretty simple
1076: * because the curve passed to the DrawCubic already splitted
1077: * into the monotonic in X and Y pieces
1078: */
1079:
1080: /* Bounding x2 by xe */
1081: if (((xe - x2) ^ dx) < 0) {
1082: x2 = xe;
1083: }
1084:
1085: /* Bounding y2 by ye */
1086: if (((ye - y2) ^ dy) < 0) {
1087: y2 = ye;
1088: }
1089:
1090: hnd.processFixedLine(x1, y1, x2, y2, pixelInfo,
1091: checkBounds, false);
1092: } else {
1093: hnd.processFixedLine(x2, y2, xe, ye, pixelInfo,
1094: checkBounds, false);
1095: }
1096: }
1097: }
1098:
1099: /*
1100: * Checking size of the cubic curves and split them if necessary.
1101: * Calling DrawMonotonicCubic for the curves of the appropriate size.
1102: * Note: coords array could be changed
1103: */
1104: private static void ProcessMonotonicCubic(ProcessHandler hnd,
1105: float[] coords, int[] pixelInfo) {
1106:
1107: float[] coords1 = new float[8];
1108: float tx, ty;
1109: float xMin, xMax;
1110: float yMin, yMax;
1111:
1112: xMin = xMax = coords[0];
1113: yMin = yMax = coords[1];
1114:
1115: for (int i = 2; i < 8; i += 2) {
1116: xMin = (xMin > coords[i]) ? coords[i] : xMin;
1117: xMax = (xMax < coords[i]) ? coords[i] : xMax;
1118: yMin = (yMin > coords[i + 1]) ? coords[i + 1] : yMin;
1119: yMax = (yMax < coords[i + 1]) ? coords[i + 1] : yMax;
1120: }
1121:
1122: if (hnd.clipMode == PH_MODE_DRAW_CLIP) {
1123: /* In case of drawing we could just skip curves which are
1124: * completely out of bounds
1125: */
1126: if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax
1127: || hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) {
1128: return;
1129: }
1130: } else {
1131:
1132: /* In case of filling we could skip curves which are above,
1133: * below and behind the right boundary of the visible area
1134: */
1135:
1136: if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax
1137: || hnd.dhnd.xMaxf < xMin) {
1138: return;
1139: }
1140:
1141: /* We could clamp x coordinates to the corresponding boundary
1142: * if the curve is completely behind the left one
1143: */
1144:
1145: if (hnd.dhnd.xMinf > xMax) {
1146: coords[0] = coords[2] = coords[4] = coords[6] = hnd.dhnd.xMinf;
1147: }
1148: }
1149:
1150: if (xMax - xMin > MAX_CUB_SIZE || yMax - yMin > MAX_CUB_SIZE) {
1151: coords1[6] = coords[6];
1152: coords1[7] = coords[7];
1153: coords1[4] = (coords[4] + coords[6]) / 2.0f;
1154: coords1[5] = (coords[5] + coords[7]) / 2.0f;
1155: tx = (coords[2] + coords[4]) / 2.0f;
1156: ty = (coords[3] + coords[5]) / 2.0f;
1157: coords1[2] = (tx + coords1[4]) / 2.0f;
1158: coords1[3] = (ty + coords1[5]) / 2.0f;
1159: coords[2] = (coords[0] + coords[2]) / 2.0f;
1160: coords[3] = (coords[1] + coords[3]) / 2.0f;
1161: coords[4] = (coords[2] + tx) / 2.0f;
1162: coords[5] = (coords[3] + ty) / 2.0f;
1163: coords[6] = coords1[0] = (coords[4] + coords1[2]) / 2.0f;
1164: coords[7] = coords1[1] = (coords[5] + coords1[3]) / 2.0f;
1165:
1166: ProcessMonotonicCubic(hnd, coords, pixelInfo);
1167:
1168: ProcessMonotonicCubic(hnd, coords1, pixelInfo);
1169: } else {
1170: DrawMonotonicCubic(hnd, coords,
1171: /* Set checkBounds parameter if curve intersects
1172: * boundary of the visible area. We know that
1173: * the curve is visible, so the check is pretty
1174: * simple
1175: */
1176: hnd.dhnd.xMinf > xMin || hnd.dhnd.xMaxf < xMax
1177: || hnd.dhnd.yMinf > yMin || hnd.dhnd.yMaxf < yMax,
1178: pixelInfo);
1179: }
1180: }
1181:
1182: /*
1183: * Split cubic curve into monotonic in X and Y parts. Calling
1184: * ProcessMonotonicCubic for each monotonic piece of the curve.
1185: *
1186: * Note: coords array could be changed
1187: */
1188: private static void ProcessCubic(ProcessHandler hnd,
1189: float[] coords, int[] pixelInfo) {
1190: /* Temporary array for holding parameters corresponding to the extreme
1191: * in X and Y points
1192: */
1193: double params[] = new double[4];
1194: double eqn[] = new double[3];
1195: double res[] = new double[2];
1196: int cnt = 0;
1197:
1198: /* Simple check for monotonicity in X before searching for the extreme
1199: * points of the X(t) function. We first check if the curve is
1200: * monotonic in X by seeing if all of the X coordinates are strongly
1201: * ordered.
1202: */
1203: if ((coords[0] > coords[2] || coords[2] > coords[4] || coords[4] > coords[6])
1204: && (coords[0] < coords[2] || coords[2] < coords[4] || coords[4] < coords[6])) {
1205: /* Searching for extreme points of the X(t) function by solving
1206: * dX(t)
1207: * ---- = 0 equation
1208: * dt
1209: */
1210: eqn[2] = -coords[0] + 3 * coords[2] - 3 * coords[4]
1211: + coords[6];
1212: eqn[1] = 2 * (coords[0] - 2 * coords[2] + coords[4]);
1213: eqn[0] = -coords[0] + coords[2];
1214:
1215: int nr = QuadCurve2D.solveQuadratic(eqn, res);
1216:
1217: /* Following code also correctly works in degenerate case of
1218: * the quadratic equation (nr = -1) because we do not need
1219: * splitting in this case.
1220: */
1221: for (int i = 0; i < nr; i++) {
1222: if (res[i] > 0 && res[i] < 1) {
1223: params[cnt++] = res[i];
1224: }
1225: }
1226: }
1227:
1228: /* Simple check for monotonicity in Y before searching for the extreme
1229: * points of the Y(t) function. We first check if the curve is
1230: * monotonic in Y by seeing if all of the Y coordinates are strongly
1231: * ordered.
1232: */
1233: if ((coords[1] > coords[3] || coords[3] > coords[5] || coords[5] > coords[7])
1234: && (coords[1] < coords[3] || coords[3] < coords[5] || coords[5] < coords[7])) {
1235: /* Searching for extreme points of the Y(t) function by solving
1236: * dY(t)
1237: * ----- = 0 equation
1238: * dt
1239: */
1240: eqn[2] = -coords[1] + 3 * coords[3] - 3 * coords[5]
1241: + coords[7];
1242: eqn[1] = 2 * (coords[1] - 2 * coords[3] + coords[5]);
1243: eqn[0] = -coords[1] + coords[3];
1244:
1245: int nr = QuadCurve2D.solveQuadratic(eqn, res);
1246:
1247: /* Following code also correctly works in degenerate case of
1248: * the quadratic equation (nr = -1) because we do not need
1249: * splitting in this case.
1250: */
1251: for (int i = 0; i < nr; i++) {
1252: if (res[i] > 0 && res[i] < 1) {
1253: params[cnt++] = res[i];
1254: }
1255: }
1256: }
1257:
1258: if (cnt > 0) {
1259: /* Sorting parameter values corresponding to the extreme points
1260: * of the curve
1261: */
1262: Arrays.sort(params, 0, cnt);
1263:
1264: /* Processing obtained monotonic parts */
1265: ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,
1266: (float) params[0]);
1267: for (int i = 1; i < cnt; i++) {
1268: double param = params[i] - params[i - 1];
1269: if (param > 0) {
1270: ProcessFirstMonotonicPartOfCubic(hnd, coords,
1271: pixelInfo,
1272: /* Scale parameter to match with rest of the curve */
1273: (float) (param / (1.0 - params[i - 1])));
1274: }
1275: }
1276: }
1277:
1278: ProcessMonotonicCubic(hnd, coords, pixelInfo);
1279: }
1280:
1281: /*
1282: * Bite the piece of the cubic curve from start point till the point
1283: * corresponding to the specified parameter then call ProcessCubic for the
1284: * bitten part.
1285: * Note: coords array will be changed
1286: */
1287: private static void ProcessFirstMonotonicPartOfCubic(
1288: ProcessHandler hnd, float[] coords, int[] pixelInfo, float t) {
1289: float[] coords1 = new float[8];
1290: float tx, ty;
1291:
1292: coords1[0] = coords[0];
1293: coords1[1] = coords[1];
1294: tx = coords[2] + t * (coords[4] - coords[2]);
1295: ty = coords[3] + t * (coords[5] - coords[3]);
1296: coords1[2] = coords[0] + t * (coords[2] - coords[0]);
1297: coords1[3] = coords[1] + t * (coords[3] - coords[1]);
1298: coords1[4] = coords1[2] + t * (tx - coords1[2]);
1299: coords1[5] = coords1[3] + t * (ty - coords1[3]);
1300: coords[4] = coords[4] + t * (coords[6] - coords[4]);
1301: coords[5] = coords[5] + t * (coords[7] - coords[5]);
1302: coords[2] = tx + t * (coords[4] - tx);
1303: coords[3] = ty + t * (coords[5] - ty);
1304: coords[0] = coords1[6] = coords1[4] + t
1305: * (coords[2] - coords1[4]);
1306: coords[1] = coords1[7] = coords1[5] + t
1307: * (coords[3] - coords1[5]);
1308:
1309: ProcessMonotonicCubic(hnd, coords1, pixelInfo);
1310: }
1311:
1312: /* Note:
1313: * For more easy reading of the code below each java version of the macros
1314: * from the ProcessPath.c preceded by the commented origin call
1315: * containing verbose names of the parameters
1316: */
1317: private static void ProcessLine(ProcessHandler hnd, float x1,
1318: float y1, float x2, float y2, int[] pixelInfo) {
1319: float xMin, yMin, xMax, yMax;
1320: int X1, Y1, X2, Y2, X3, Y3, res;
1321: boolean clipped = false;
1322: float x3, y3;
1323: float c[] = new float[] { x1, y1, x2, y2, 0, 0 };
1324:
1325: boolean lastClipped;
1326:
1327: xMin = hnd.dhnd.xMinf;
1328: yMin = hnd.dhnd.yMinf;
1329: xMax = hnd.dhnd.xMaxf;
1330: yMax = hnd.dhnd.yMaxf;
1331:
1332: //
1333: // TESTANDCLIP(yMin, yMax, y1, x1, y2, x2, res);
1334: //
1335: res = TESTANDCLIP(yMin, yMax, c, 1, 0, 3, 2);
1336: if (res == CRES_INVISIBLE)
1337: return;
1338: clipped = IS_CLIPPED(res);
1339: //
1340: // TESTANDCLIP(yMin, yMax, y2, x2, y1, x1, res);
1341: //
1342: res = TESTANDCLIP(yMin, yMax, c, 3, 2, 1, 0);
1343: if (res == CRES_INVISIBLE)
1344: return;
1345: lastClipped = IS_CLIPPED(res);
1346: clipped = clipped || lastClipped;
1347:
1348: if (hnd.clipMode == PH_MODE_DRAW_CLIP) {
1349: //
1350: // TESTANDCLIP(xMin, xMax, x1, y1, x2, y2, res);
1351: //
1352: res = TESTANDCLIP(xMin, xMax, c, 0, 1, 2, 3);
1353: if (res == CRES_INVISIBLE)
1354: return;
1355: clipped = clipped || IS_CLIPPED(res);
1356: //
1357: // TESTANDCLIP(xMin, xMax, x2, y2, x1, y1, res);
1358: //
1359: res = TESTANDCLIP(xMin, xMax, c, 2, 3, 0, 1);
1360: if (res == CRES_INVISIBLE)
1361: return;
1362: lastClipped = lastClipped || IS_CLIPPED(res);
1363: clipped = clipped || lastClipped;
1364: X1 = (int) (c[0] * MDP_MULT);
1365: Y1 = (int) (c[1] * MDP_MULT);
1366: X2 = (int) (c[2] * MDP_MULT);
1367: Y2 = (int) (c[3] * MDP_MULT);
1368:
1369: hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo, clipped, /* enable boundary checking in
1370: case of clipping to avoid
1371: entering out of bounds which
1372: could happens during rounding
1373: */
1374: lastClipped /* Notify pProcessFixedLine
1375: that
1376: this is the end of the
1377: subpath (because of exiting
1378: out of boundaries)
1379: */
1380: );
1381: } else {
1382: /* Clamping starting from first vertex of the the processed
1383: * segment
1384: *
1385: * CLIPCLAMP(xMin, xMax, x1, y1, x2, y2, x3, y3, res);
1386: */
1387: res = CLIPCLAMP(xMin, xMax, c, 0, 1, 2, 3, 4, 5);
1388: X1 = (int) (c[0] * MDP_MULT);
1389: Y1 = (int) (c[1] * MDP_MULT);
1390:
1391: /* Clamping only by left boundary */
1392: if (res == CRES_MIN_CLIPPED) {
1393: X3 = (int) (c[4] * MDP_MULT);
1394: Y3 = (int) (c[5] * MDP_MULT);
1395: hnd.processFixedLine(X3, Y3, X1, Y1, pixelInfo, false,
1396: lastClipped);
1397:
1398: } else if (res == CRES_INVISIBLE) {
1399: return;
1400: }
1401:
1402: /* Clamping starting from last vertex of the the processed
1403: * segment
1404: *
1405: * CLIPCLAMP(xMin, xMax, x2, y2, x1, y1, x3, y3, res);
1406: */
1407: res = CLIPCLAMP(xMin, xMax, c, 2, 3, 0, 1, 4, 5);
1408:
1409: /* Checking if there was a clip by right boundary */
1410: lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);
1411:
1412: X2 = (int) (c[2] * MDP_MULT);
1413: Y2 = (int) (c[3] * MDP_MULT);
1414: hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo, false,
1415: lastClipped);
1416:
1417: /* Clamping only by left boundary */
1418: if (res == CRES_MIN_CLIPPED) {
1419: X3 = (int) (c[4] * MDP_MULT);
1420: Y3 = (int) (c[5] * MDP_MULT);
1421: hnd.processFixedLine(X2, Y2, X3, Y3, pixelInfo, false,
1422: lastClipped);
1423: }
1424: }
1425: }
1426:
1427: private static boolean doProcessPath(ProcessHandler hnd,
1428: Path2D.Float p2df, float transXf, float transYf) {
1429: float coords[] = new float[8];
1430: float tCoords[] = new float[8];
1431: float closeCoord[] = new float[] { 0.0f, 0.0f };
1432: float firstCoord[] = new float[2];
1433: int pixelInfo[] = new int[5];
1434: boolean subpathStarted = false;
1435: boolean skip = false;
1436: float lastX, lastY;
1437: pixelInfo[0] = 0;
1438:
1439: /* Adjusting boundaries to the capabilities of the
1440: * ProcessPath code
1441: */
1442: hnd.dhnd.adjustBounds(LOWER_OUT_BND, LOWER_OUT_BND,
1443: UPPER_OUT_BND, UPPER_OUT_BND);
1444:
1445: /* Adding support of the KEY_STROKE_CONTROL rendering hint.
1446: * Now we are supporting two modes: "pixels at centers" and
1447: * "pixels at corners".
1448: * First one is disabled by default but could be enabled by setting
1449: * VALUE_STROKE_PURE to the rendering hint. It means that pixel at the
1450: * screen (x,y) has (x + 0.5, y + 0.5) float coordinates.
1451: *
1452: * Second one is enabled by default and means straightforward mapping
1453: * (x,y) --> (x,y)
1454: */
1455: if (hnd.dhnd.strokeControl == SunHints.INTVAL_STROKE_PURE) {
1456: closeCoord[0] = -0.5f;
1457: closeCoord[1] = -0.5f;
1458: transXf -= 0.5;
1459: transYf -= 0.5;
1460: }
1461:
1462: PathIterator pi = p2df.getPathIterator(null);
1463:
1464: while (!pi.isDone()) {
1465: switch (pi.currentSegment(coords)) {
1466: case PathIterator.SEG_MOVETO:
1467: /* Performing closing of the unclosed segments */
1468: if (subpathStarted && !skip) {
1469: if (hnd.clipMode == PH_MODE_FILL_CLIP) {
1470: if (tCoords[0] != closeCoord[0]
1471: || tCoords[1] != closeCoord[1]) {
1472: ProcessLine(hnd, tCoords[0], tCoords[1],
1473: closeCoord[0], closeCoord[1],
1474: pixelInfo);
1475:
1476: /* Storing last path's point for using in
1477: * following segments without initial moveTo
1478: */
1479: tCoords[0] = closeCoord[0];
1480: tCoords[1] = closeCoord[1];
1481: }
1482: }
1483: hnd.processEndSubPath();
1484: }
1485:
1486: tCoords[0] = coords[0] + transXf;
1487: tCoords[1] = coords[1] + transYf;
1488:
1489: /* Checking SEG_MOVETO coordinates if they are out of the
1490: * [LOWER_BND, UPPER_BND] range. This check also handles
1491: * NaN and Infinity values. Skipping next path segment in
1492: * case of invalid data.
1493: */
1494:
1495: if (tCoords[0] < UPPER_BND && tCoords[0] > LOWER_BND
1496: && tCoords[1] < UPPER_BND
1497: && tCoords[1] > LOWER_BND) {
1498: subpathStarted = true;
1499: skip = false;
1500: closeCoord[0] = tCoords[0];
1501: closeCoord[1] = tCoords[1];
1502: } else {
1503: skip = true;
1504: }
1505: pixelInfo[0] = 0;
1506: break;
1507: case PathIterator.SEG_LINETO:
1508: lastX = tCoords[2] = coords[0] + transXf;
1509: lastY = tCoords[3] = coords[1] + transYf;
1510:
1511: /* Checking SEG_LINETO coordinates if they are out of the
1512: * [LOWER_BND, UPPER_BND] range. This check also handles
1513: * NaN and Infinity values. Ignoring current path segment
1514: * in case of invalid data. If segment is skipped its
1515: * endpoint (if valid) is used to begin new subpath.
1516: */
1517:
1518: if (lastX < UPPER_BND && lastX > LOWER_BND
1519: && lastY < UPPER_BND && lastY > LOWER_BND) {
1520: if (skip) {
1521: tCoords[0] = closeCoord[0] = lastX;
1522: tCoords[1] = closeCoord[1] = lastY;
1523: subpathStarted = true;
1524: skip = false;
1525: } else {
1526: ProcessLine(hnd, tCoords[0], tCoords[1],
1527: tCoords[2], tCoords[3], pixelInfo);
1528: tCoords[0] = lastX;
1529: tCoords[1] = lastY;
1530: }
1531: }
1532: break;
1533: case PathIterator.SEG_QUADTO:
1534: tCoords[2] = coords[0] + transXf;
1535: tCoords[3] = coords[1] + transYf;
1536: lastX = tCoords[4] = coords[2] + transXf;
1537: lastY = tCoords[5] = coords[3] + transYf;
1538:
1539: /* Checking SEG_QUADTO coordinates if they are out of the
1540: * [LOWER_BND, UPPER_BND] range. This check also handles
1541: * NaN and Infinity values. Ignoring current path segment
1542: * in case of invalid endpoints's data. Equivalent to
1543: * the SEG_LINETO if endpoint coordinates are valid but
1544: * there are invalid data among other coordinates
1545: */
1546:
1547: if (lastX < UPPER_BND && lastX > LOWER_BND
1548: && lastY < UPPER_BND && lastY > LOWER_BND) {
1549: if (skip) {
1550: tCoords[0] = closeCoord[0] = lastX;
1551: tCoords[1] = closeCoord[1] = lastY;
1552: subpathStarted = true;
1553: skip = false;
1554: } else {
1555: if (tCoords[2] < UPPER_BND
1556: && tCoords[2] > LOWER_BND
1557: && tCoords[3] < UPPER_BND
1558: && tCoords[3] > LOWER_BND) {
1559: ProcessQuad(hnd, tCoords, pixelInfo);
1560: } else {
1561: ProcessLine(hnd, tCoords[0], tCoords[1],
1562: tCoords[4], tCoords[5], pixelInfo);
1563: }
1564: tCoords[0] = lastX;
1565: tCoords[1] = lastY;
1566: }
1567: }
1568: break;
1569: case PathIterator.SEG_CUBICTO:
1570: tCoords[2] = coords[0] + transXf;
1571: tCoords[3] = coords[1] + transYf;
1572: tCoords[4] = coords[2] + transXf;
1573: tCoords[5] = coords[3] + transYf;
1574: lastX = tCoords[6] = coords[4] + transXf;
1575: lastY = tCoords[7] = coords[5] + transYf;
1576:
1577: /* Checking SEG_CUBICTO coordinates if they are out of the
1578: * [LOWER_BND, UPPER_BND] range. This check also handles
1579: * NaN and Infinity values. Ignoring current path segment
1580: * in case of invalid endpoints's data. Equivalent to
1581: * the SEG_LINETO if endpoint coordinates are valid but
1582: * there are invalid data among other coordinates
1583: */
1584:
1585: if (lastX < UPPER_BND && lastX > LOWER_BND
1586: && lastY < UPPER_BND && lastY > LOWER_BND) {
1587: if (skip) {
1588: tCoords[0] = closeCoord[0] = tCoords[6];
1589: tCoords[1] = closeCoord[1] = tCoords[7];
1590: subpathStarted = true;
1591: skip = false;
1592: } else {
1593: if (tCoords[2] < UPPER_BND
1594: && tCoords[2] > LOWER_BND
1595: && tCoords[3] < UPPER_BND
1596: && tCoords[3] > LOWER_BND
1597: && tCoords[4] < UPPER_BND
1598: && tCoords[4] > LOWER_BND
1599: && tCoords[5] < UPPER_BND
1600: && tCoords[5] > LOWER_BND) {
1601: ProcessCubic(hnd, tCoords, pixelInfo);
1602: } else {
1603: ProcessLine(hnd, tCoords[0], tCoords[1],
1604: tCoords[6], tCoords[7], pixelInfo);
1605: }
1606: tCoords[0] = lastX;
1607: tCoords[1] = lastY;
1608: }
1609: }
1610: break;
1611: case PathIterator.SEG_CLOSE:
1612: if (subpathStarted && !skip) {
1613: skip = false;
1614: if (tCoords[0] != closeCoord[0]
1615: || tCoords[1] != closeCoord[1]) {
1616: ProcessLine(hnd, tCoords[0], tCoords[1],
1617: closeCoord[0], closeCoord[1], pixelInfo);
1618:
1619: /* Storing last path's point for using in following
1620: * segments without initial moveTo
1621: */
1622: tCoords[0] = closeCoord[0];
1623: tCoords[1] = closeCoord[1];
1624: }
1625: hnd.processEndSubPath();
1626: }
1627: break;
1628: }
1629: pi.next();
1630: }
1631:
1632: /* Performing closing of the unclosed segments */
1633: if (subpathStarted & !skip) {
1634: if (hnd.clipMode == PH_MODE_FILL_CLIP) {
1635: if (tCoords[0] != closeCoord[0]
1636: || tCoords[1] != closeCoord[1]) {
1637: ProcessLine(hnd, tCoords[0], tCoords[1],
1638: closeCoord[0], closeCoord[1], pixelInfo);
1639: }
1640: }
1641: hnd.processEndSubPath();
1642: }
1643: return true;
1644: }
1645:
1646: private static class Point {
1647: public int x;
1648: public int y;
1649: public boolean lastPoint;
1650: public Point prev;
1651: public Point next;
1652: public Point nextByY;
1653: public Edge edge;
1654:
1655: public Point(int x, int y, boolean lastPoint) {
1656: this .x = x;
1657: this .y = y;
1658: this .lastPoint = lastPoint;
1659: }
1660: };
1661:
1662: private static class Edge {
1663: int x;
1664: int dx;
1665: Point p;
1666: int dir;
1667: Edge prev;
1668: Edge next;
1669:
1670: public Edge(Point p, int x, int dx, int dir) {
1671: this .p = p;
1672: this .x = x;
1673: this .dx = dx;
1674: this .dir = dir;
1675: }
1676: };
1677:
1678: /* Size of the default buffer in the FillData structure. This buffer is
1679: * replaced with heap allocated in case of large paths.
1680: */
1681: private static final int DF_MAX_POINT = 256;
1682:
1683: /* Following class accumulates points of the non-continuous flattened
1684: * general path during iteration through the origin path's segments . The
1685: * end of the each subpath is marked as lastPoint flag set at the last
1686: * point
1687: */
1688: private static class FillData {
1689: List<Point> plgPnts;
1690: public int plgYMin;
1691: public int plgYMax;
1692:
1693: public FillData() {
1694: plgPnts = new Vector<Point>(DF_MAX_POINT);
1695: }
1696:
1697: public void addPoint(int x, int y, boolean lastPoint) {
1698: if (plgPnts.size() == 0) {
1699: plgYMin = plgYMax = y;
1700: } else {
1701: plgYMin = (plgYMin > y) ? y : plgYMin;
1702: plgYMax = (plgYMax < y) ? y : plgYMax;
1703: }
1704:
1705: plgPnts.add(new Point(x, y, lastPoint));
1706: }
1707:
1708: public boolean isEmpty() {
1709: return plgPnts.size() == 0;
1710: }
1711:
1712: public boolean isEnded() {
1713: return plgPnts.get(plgPnts.size() - 1).lastPoint;
1714: }
1715:
1716: public boolean setEnded() {
1717: return plgPnts.get(plgPnts.size() - 1).lastPoint = true;
1718: }
1719: }
1720:
1721: private static class ActiveEdgeList {
1722: Edge head;
1723:
1724: public boolean isEmpty() {
1725: return (head == null);
1726: }
1727:
1728: public void insert(Point pnt, int cy) {
1729: Point np = pnt.next;
1730: int X1 = pnt.x, Y1 = pnt.y;
1731: int X2 = np.x, Y2 = np.y;
1732: Edge ne;
1733: if (Y1 == Y2) {
1734: /* Skipping horizontal segments */
1735: return;
1736: } else {
1737: int dX = X2 - X1;
1738: int dY = Y2 - Y1;
1739: int stepx, x0, dy, dir;
1740:
1741: if (Y1 < Y2) {
1742: x0 = X1;
1743: dy = cy - Y1;
1744: dir = -1;
1745: } else { // (Y1 > Y2)
1746: x0 = X2;
1747: dy = cy - Y2;
1748: dir = 1;
1749: }
1750:
1751: /* We need to worry only about dX because dY is in denominator
1752: * and abs(dy) < MDP_MULT (cy is a first scanline of the scan
1753: * converted segment and we subtract y coordinate of the
1754: * nearest segment's end from it to obtain dy)
1755: */
1756: if (dX > CALC_UBND || dX < CALC_LBND) {
1757: stepx = (int) ((((double) dX) * MDP_MULT) / dY);
1758: x0 = x0 + (int) ((((double) dX) * dy) / dY);
1759: } else {
1760: stepx = (dX << MDP_PREC) / dY;
1761: x0 += (dX * dy) / dY;
1762: }
1763:
1764: ne = new Edge(pnt, x0, stepx, dir);
1765: }
1766:
1767: ne.next = head;
1768: ne.prev = null;
1769: if (head != null) {
1770: head.prev = ne;
1771: }
1772: head = pnt.edge = ne;
1773: }
1774:
1775: public void delete(Edge e) {
1776: Edge prevp = e.prev;
1777: Edge nextp = e.next;
1778: if (prevp != null) {
1779: prevp.next = nextp;
1780: } else {
1781: head = nextp;
1782: }
1783: if (nextp != null) {
1784: nextp.prev = prevp;
1785: }
1786: }
1787:
1788: /**
1789: * Bubble sorting in the ascending order of the linked list. This
1790: * implementation stops processing the list if there were no changes
1791: * during the previous pass.
1792: *
1793: * We could not use O(N) Radix sort here because in most cases list of
1794: * edges almost sorted. So, bubble sort (O(N^2)) is working much
1795: * better. Note, in case of array of edges Shell sort is more
1796: * efficient.
1797: */
1798: public void sort() {
1799: Edge p, q, r, s = null, temp;
1800: boolean wasSwap = true;
1801:
1802: // r precedes p and s points to the node up to which
1803: // comparisons are to be made
1804: while (s != head.next && wasSwap) {
1805: r = p = head;
1806: q = p.next;
1807: wasSwap = false;
1808: while (p != s) {
1809: if (p.x >= q.x) {
1810: wasSwap = true;
1811: if (p == head) {
1812: temp = q.next;
1813: q.next = p;
1814: p.next = temp;
1815: head = q;
1816: r = q;
1817: } else {
1818: temp = q.next;
1819: q.next = p;
1820: p.next = temp;
1821: r.next = q;
1822: r = q;
1823: }
1824: } else {
1825: r = p;
1826: p = p.next;
1827: }
1828: q = p.next;
1829: if (q == s)
1830: s = p;
1831: }
1832: }
1833:
1834: // correction of the back links in the double linked edge list
1835: p = head;
1836: q = null;
1837: while (p != null) {
1838: p.prev = q;
1839: q = p;
1840: p = p.next;
1841: }
1842: }
1843: }
1844:
1845: private static void FillPolygon(FillProcessHandler hnd, int fillRule) {
1846: int k, y, n;
1847: boolean drawing;
1848: Edge active;
1849: int rightBnd = hnd.dhnd.xMax - 1;
1850: FillData fd = hnd.fd;
1851: int yMin = fd.plgYMin;
1852: int yMax = fd.plgYMax;
1853: int hashSize = ((yMax - yMin) >> MDP_PREC) + 4;
1854:
1855: /* Because of support of the KEY_STROKE_CONTROL hint we are performing
1856: * shift of the coordinates at the higher level
1857: */
1858: int hashOffset = ((yMin - 1) & MDP_W_MASK);
1859:
1860: /* Winding counter */
1861: int counter;
1862:
1863: /* Calculating mask to be applied to the winding counter */
1864: int counterMask = (fillRule == PathIterator.WIND_NON_ZERO) ? -1
1865: : 1;
1866:
1867: int pntOffset;
1868: List<Point> pnts = fd.plgPnts;
1869: n = pnts.size();
1870:
1871: if (n <= 1)
1872: return;
1873:
1874: Point[] yHash = new Point[hashSize];
1875:
1876: /* Creating double linked list (prev, next links) describing path order
1877: * and hash table with points which fall between scanlines. nextByY
1878: * link is used for the points which are between same scanlines.
1879: * Scanlines are passed through the centers of the pixels.
1880: */
1881: Point curpt = pnts.get(0);
1882: curpt.prev = null;
1883: for (int i = 0; i < n - 1; i++) {
1884: curpt = pnts.get(i);
1885: Point nextpt = pnts.get(i + 1);
1886: int curHashInd = (curpt.y - hashOffset - 1) >> MDP_PREC;
1887: curpt.nextByY = yHash[curHashInd];
1888: yHash[curHashInd] = curpt;
1889: curpt.next = nextpt;
1890: nextpt.prev = curpt;
1891: }
1892:
1893: Point ept = pnts.get(n - 1);
1894: int curHashInd = (ept.y - hashOffset - 1) >> MDP_PREC;
1895: ept.nextByY = yHash[curHashInd];
1896: yHash[curHashInd] = ept;
1897:
1898: ActiveEdgeList activeList = new ActiveEdgeList();
1899:
1900: for (y = hashOffset + MDP_MULT, k = 0; y <= yMax
1901: && k < hashSize; y += MDP_MULT, k++) {
1902: for (Point pt = yHash[k]; pt != null; pt = pt.nextByY) {
1903: /* pt.y should be inside hashed interval
1904: * assert(y-MDP_MULT <= pt.y && pt.y < y);
1905: */
1906: if (pt.prev != null && !pt.prev.lastPoint) {
1907: if (pt.prev.edge != null && pt.prev.y <= y) {
1908: activeList.delete(pt.prev.edge);
1909: pt.prev.edge = null;
1910: } else if (pt.prev.y > y) {
1911: activeList.insert(pt.prev, y);
1912: }
1913: }
1914:
1915: if (!pt.lastPoint && pt.next != null) {
1916: if (pt.edge != null && pt.next.y <= y) {
1917: activeList.delete(pt.edge);
1918: pt.edge = null;
1919: } else if (pt.next.y > y) {
1920: activeList.insert(pt, y);
1921: }
1922: }
1923: }
1924:
1925: if (activeList.isEmpty())
1926: continue;
1927:
1928: activeList.sort();
1929:
1930: counter = 0;
1931: drawing = false;
1932: int xl, xr;
1933: xl = xr = hnd.dhnd.xMin;
1934: Edge curEdge = activeList.head;
1935: while (curEdge != null) {
1936: counter += curEdge.dir;
1937: if ((counter & counterMask) != 0 && !drawing) {
1938: xl = (curEdge.x + MDP_MULT - 1) >> MDP_PREC;
1939: drawing = true;
1940: }
1941:
1942: if ((counter & counterMask) == 0 && drawing) {
1943: xr = (curEdge.x - 1) >> MDP_PREC;
1944: if (xl <= xr) {
1945: hnd.dhnd.drawScanline(xl, xr, y >> MDP_PREC);
1946: }
1947: drawing = false;
1948: }
1949:
1950: curEdge.x += curEdge.dx;
1951: curEdge = curEdge.next;
1952: }
1953:
1954: /* Performing drawing till the right boundary (for correct
1955: * rendering shapes clipped at the right side)
1956: */
1957: if (drawing && xl <= rightBnd) {
1958:
1959: /* Support of the strokeHint was added into the
1960: * draw and fill methods of the sun.java2d.pipe.LoopPipe
1961: */
1962: hnd.dhnd.drawScanline(xl, rightBnd, y >> MDP_PREC);
1963: }
1964: }
1965: }
1966:
1967: private static class FillProcessHandler extends ProcessHandler {
1968:
1969: FillData fd;
1970:
1971: /* Note: For more easy reading of the code below each java version of
1972: * the macros from the ProcessPath.c preceded by the commented
1973: * origin call containing verbose names of the parameters
1974: */
1975: public void processFixedLine(int x1, int y1, int x2, int y2,
1976: int[] pixelInfo, boolean checkBounds, boolean endSubPath) {
1977: int outXMin, outXMax, outYMin, outYMax;
1978: int res;
1979:
1980: /* There is no need to round line coordinates to the forward
1981: * differencing precision anymore. Such a rounding was used for
1982: * preventing the curve go out the endpoint (this sometimes does
1983: * not help). The problem was fixed in the forward differencing
1984: * loops.
1985: */
1986: if (checkBounds) {
1987: boolean lastClipped;
1988:
1989: /* This function is used only for filling shapes, so there is no
1990: * check for the type of clipping
1991: */
1992: int c[] = new int[] { x1, y1, x2, y2, 0, 0 };
1993: outXMin = (int) (dhnd.xMinf * MDP_MULT);
1994: outXMax = (int) (dhnd.xMaxf * MDP_MULT);
1995: outYMin = (int) (dhnd.yMinf * MDP_MULT);
1996: outYMax = (int) (dhnd.yMaxf * MDP_MULT);
1997:
1998: /*
1999: * TESTANDCLIP(outYMin, outYMax, y1, x1, y2, x2, res);
2000: */
2001: res = TESTANDCLIP(outYMin, outYMax, c, 1, 0, 3, 2);
2002: if (res == CRES_INVISIBLE)
2003: return;
2004:
2005: /*
2006: * TESTANDCLIP(outYMin, outYMax, y2, x2, y1, x1, res);
2007: */
2008: res = TESTANDCLIP(outYMin, outYMax, c, 3, 2, 1, 0);
2009: if (res == CRES_INVISIBLE)
2010: return;
2011: lastClipped = IS_CLIPPED(res);
2012:
2013: /* Clamping starting from first vertex of the the processed
2014: * segment
2015: *
2016: * CLIPCLAMP(outXMin, outXMax, x1, y1, x2, y2, x3, y3, res);
2017: */
2018: res = CLIPCLAMP(outXMin, outXMax, c, 0, 1, 2, 3, 4, 5);
2019:
2020: /* Clamping only by left boundary */
2021: if (res == CRES_MIN_CLIPPED) {
2022: processFixedLine(c[4], c[5], c[0], c[1], pixelInfo,
2023: false, lastClipped);
2024:
2025: } else if (res == CRES_INVISIBLE) {
2026: return;
2027: }
2028:
2029: /* Clamping starting from last vertex of the the processed
2030: * segment
2031: *
2032: * CLIPCLAMP(outXMin, outXMax, x2, y2, x1, y1, x3, y3, res);
2033: */
2034: res = CLIPCLAMP(outXMin, outXMax, c, 2, 3, 0, 1, 4, 5);
2035:
2036: /* Checking if there was a clip by right boundary */
2037: lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);
2038:
2039: processFixedLine(c[0], c[1], c[2], c[3], pixelInfo,
2040: false, lastClipped);
2041:
2042: /* Clamping only by left boundary */
2043: if (res == CRES_MIN_CLIPPED) {
2044: processFixedLine(c[2], c[3], c[4], c[5], pixelInfo,
2045: false, lastClipped);
2046: }
2047:
2048: return;
2049: }
2050:
2051: /* Adding first point of the line only in case of empty or just
2052: * finished path
2053: */
2054: if (fd.isEmpty() || fd.isEnded()) {
2055: fd.addPoint(x1, y1, false);
2056: }
2057:
2058: fd.addPoint(x2, y2, false);
2059:
2060: if (endSubPath) {
2061: fd.setEnded();
2062: }
2063: }
2064:
2065: FillProcessHandler(DrawHandler dhnd) {
2066: super (dhnd, PH_MODE_FILL_CLIP);
2067: this .fd = new FillData();
2068: }
2069:
2070: public void processEndSubPath() {
2071: if (!fd.isEmpty()) {
2072: fd.setEnded();
2073: }
2074: }
2075: }
2076: }
|