| /* | |
| * Copyright (c) 2005, 2023, 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.loops; | |
| import java.awt.geom.Path2D; | |
| import java.awt.geom.PathIterator; | |
| import java.awt.geom.QuadCurve2D; | |
| import sun.awt.SunHints; | |
| import java.util.*; | |
| /* This is the java implementation of the native code from | |
| * src/share/native/sun/java2d/loops/ProcessPath.[c,h] | |
| * This code is written to be as much similar to the native | |
| * as it possible. So, it sometimes does not follow java naming conventions. | |
| * | |
| * It's important to keep this code synchronized with native one. See more | |
| * comments, description and high level scheme of the rendering process in the | |
| * ProcessPath.c | |
| */ | |
| public class ProcessPath { | |
| /* Public interfaces and methods for drawing and filling general paths */ | |
| public abstract static class DrawHandler { | |
| public int xMin; | |
| public int yMin; | |
| public int xMax; | |
| public int yMax; | |
| public float xMinf; | |
| public float yMinf; | |
| public float xMaxf; | |
| public float yMaxf; | |
| public int strokeControl; | |
| public DrawHandler(int xMin, int yMin, int xMax, int yMax, | |
| int strokeControl) | |
| { | |
| setBounds(xMin, yMin, xMax, yMax, strokeControl); | |
| } | |
| public void setBounds(int xMin, int yMin, int xMax, int yMax) | |
| { | |
| this.xMin = xMin; | |
| this.yMin = yMin; | |
| this.xMax = xMax; | |
| this.yMax = yMax; | |
| /* Setting up fractional clipping box | |
| * | |
| * We are using following float -> int mapping: | |
| * | |
| * xi = floor(xf + 0.5) | |
| * | |
| * So, fractional values that hit the [xmin, xmax) integer interval | |
| * will be situated inside the [xmin-0.5, xmax - 0.5) fractional | |
| * interval. We are using EPSF constant to provide that upper | |
| * boundary is not included. | |
| */ | |
| xMinf = xMin - 0.5f; | |
| yMinf = yMin - 0.5f; | |
| xMaxf = xMax - 0.5f - EPSF; | |
| yMaxf = yMax - 0.5f - EPSF; | |
| } | |
| public void setBounds(int xMin, int yMin, int xMax, int yMax, | |
| int strokeControl) | |
| { | |
| this.strokeControl = strokeControl; | |
| setBounds(xMin, yMin, xMax, yMax); | |
| } | |
| public void adjustBounds(int bxMin, int byMin, int bxMax, int byMax) | |
| { | |
| if (xMin > bxMin) bxMin = xMin; | |
| if (xMax < bxMax) bxMax = xMax; | |
| if (yMin > byMin) byMin = yMin; | |
| if (yMax < byMax) byMax = yMax; | |
| setBounds(bxMin, byMin, bxMax, byMax); | |
| } | |
| public DrawHandler(int xMin, int yMin, int xMax, int yMax) { | |
| this(xMin, yMin, xMax, yMax, SunHints.INTVAL_STROKE_DEFAULT); | |
| } | |
| public abstract void drawLine(int x0, int y0, int x1, int y1); | |
| public abstract void drawPixel(int x0, int y0); | |
| public abstract void drawScanline(int x0, int x1, int y0); | |
| } | |
| public interface EndSubPathHandler { | |
| public void processEndSubPath(); | |
| } | |
| public static final int PH_MODE_DRAW_CLIP = 0; | |
| public static final int PH_MODE_FILL_CLIP = 1; | |
| public abstract static class ProcessHandler implements EndSubPathHandler { | |
| DrawHandler dhnd; | |
| int clipMode; | |
| public ProcessHandler(DrawHandler dhnd, | |
| int clipMode) { | |
| this.dhnd = dhnd; | |
| this.clipMode = clipMode; | |
| } | |
| public abstract void processFixedLine(int x1, int y1, | |
| int x2, int y2, int [] pixelInfo, | |
| boolean checkBounds, | |
| boolean endSubPath); | |
| } | |
| public static EndSubPathHandler noopEndSubPathHandler = | |
| new EndSubPathHandler() { | |
| public void processEndSubPath() { } | |
| }; | |
| public static boolean fillPath(DrawHandler dhnd, Path2D.Float p2df, | |
| int transX, int transY) | |
| { | |
| FillProcessHandler fhnd = new FillProcessHandler(dhnd); | |
| if (!doProcessPath(fhnd, p2df, transX, transY)) { | |
| return false; | |
| } | |
| FillPolygon(fhnd, p2df.getWindingRule()); | |
| return true; | |
| } | |
| public static boolean drawPath(DrawHandler dhnd, | |
| EndSubPathHandler endSubPath, | |
| Path2D.Float p2df, | |
| int transX, int transY) | |
| { | |
| return doProcessPath(new DrawProcessHandler(dhnd, endSubPath), | |
| p2df, transX, transY); | |
| } | |
| public static boolean drawPath(DrawHandler dhnd, | |
| Path2D.Float p2df, | |
| int transX, int transY) | |
| { | |
| return doProcessPath(new DrawProcessHandler(dhnd, | |
| noopEndSubPathHandler), | |
| p2df, transX, transY); | |
| } | |
| /* Private implementation of the rendering process */ | |
| /* Boundaries used for skipping huge path segments */ | |
| private static final float UPPER_BND = Float.MAX_VALUE/4.0f; | |
| private static final float LOWER_BND = -UPPER_BND; | |
| /* Precision (in bits) used in forward differencing */ | |
| private static final int FWD_PREC = 7; | |
| /* Precision (in bits) used for the rounding in the midpoint test */ | |
| private static final int MDP_PREC = 10; | |
| private static final int MDP_MULT = 1 << MDP_PREC; | |
| private static final int MDP_HALF_MULT = MDP_MULT >> 1; | |
| /* Boundaries used for clipping large path segments (those are inside | |
| * [UPPER/LOWER]_BND boundaries) | |
| */ | |
| private static final int UPPER_OUT_BND = 1 << (30 - MDP_PREC); | |
| private static final int LOWER_OUT_BND = -UPPER_OUT_BND; | |
| /* Calculation boundaries. They are used for switching to the more slow but | |
| * allowing larger input values method of calculation of the initial values | |
| * of the scan converted line segments inside the FillPolygon | |
| */ | |
| private static final float CALC_UBND = 1 << (30 - MDP_PREC); | |
| private static final float CALC_LBND = -CALC_UBND; | |
| /* Following constants are used for providing open boundaries of the | |
| * intervals | |
| */ | |
| public static final int EPSFX = 1; | |
| public static final float EPSF = ((float)EPSFX)/MDP_MULT; | |
| /* Bit mask used to separate whole part from the fraction part of the | |
| * number | |
| */ | |
| private static final int MDP_W_MASK = -MDP_MULT; | |
| /* Bit mask used to separate fractional part from the whole part of the | |
| * number | |
| */ | |
| private static final int MDP_F_MASK = MDP_MULT - 1; | |
| /* | |
| * Constants for the forward differencing | |
| * of the cubic and quad curves | |
| */ | |
| /* Maximum size of the cubic curve (calculated as the size of the bounding | |
| * box of the control points) which could be rendered without splitting | |
| */ | |
| private static final int MAX_CUB_SIZE = 256; | |
| /* Maximum size of the quad curve (calculated as the size of the bounding | |
| * box of the control points) which could be rendered without splitting | |
| */ | |
| private static final int MAX_QUAD_SIZE = 1024; | |
| /* Default power of 2 steps used in the forward differencing. Here DF prefix | |
| * stands for DeFault. Constants below are used as initial values for the | |
| * adaptive forward differencing algorithm. | |
| */ | |
| private static final int DF_CUB_STEPS = 3; | |
| private static final int DF_QUAD_STEPS = 2; | |
| /* Shift of the current point of the curve for preparing to the midpoint | |
| * rounding | |
| */ | |
| private static final int DF_CUB_SHIFT = FWD_PREC + DF_CUB_STEPS*3 - | |
| MDP_PREC; | |
| private static final int DF_QUAD_SHIFT = FWD_PREC + DF_QUAD_STEPS*2 - | |
| MDP_PREC; | |
| /* Default amount of steps of the forward differencing */ | |
| private static final int DF_CUB_COUNT = (1<<DF_CUB_STEPS); | |
| private static final int DF_QUAD_COUNT = (1<<DF_QUAD_STEPS); | |
| /* Default boundary constants used to check the necessity of the restepping | |
| */ | |
| private static final int DF_CUB_DEC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC + 2; | |
| private static final int DF_CUB_INC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC - 1; | |
| private static final int DF_QUAD_DEC_BND = 1<<DF_QUAD_STEPS*2 + | |
| FWD_PREC + 2; | |
| private static final int DF_QUAD_INC_BND = 1<<DF_QUAD_STEPS*2 + | |
| FWD_PREC - 1; | |
| /* Multipliers for the coefficients of the polynomial form of the cubic and | |
| * quad curves representation | |
| */ | |
| private static final int CUB_A_SHIFT = FWD_PREC; | |
| private static final int CUB_B_SHIFT = (DF_CUB_STEPS + FWD_PREC + 1); | |
| private static final int CUB_C_SHIFT = (DF_CUB_STEPS*2 + FWD_PREC); | |
| private static final int CUB_A_MDP_MULT = (1<<CUB_A_SHIFT); | |
| private static final int CUB_B_MDP_MULT = (1<<CUB_B_SHIFT); | |
| private static final int CUB_C_MDP_MULT = (1<<CUB_C_SHIFT); | |
| private static final int QUAD_A_SHIFT = FWD_PREC; | |
| private static final int QUAD_B_SHIFT = (DF_QUAD_STEPS + FWD_PREC); | |
| private static final int QUAD_A_MDP_MULT = (1<<QUAD_A_SHIFT); | |
| private static final int QUAD_B_MDP_MULT = (1<<QUAD_B_SHIFT); | |
| /* Clipping macros for drawing and filling algorithms */ | |
| private static float CLIP(float a1, float b1, float a2, float b2, | |
| double t) { | |
| return (float)(b1 + (t - a1)*(b2 - b1) / (a2 - a1)); | |
| } | |
| private static int CLIP(int a1, int b1, int a2, int b2, double t) { | |
| return (int)(b1 + (t - a1)*(b2 - b1) / (a2 - a1)); | |
| } | |
| private static final int CRES_MIN_CLIPPED = 0; | |
| private static final int CRES_MAX_CLIPPED = 1; | |
| private static final int CRES_NOT_CLIPPED = 3; | |
| private static final int CRES_INVISIBLE = 4; | |
| private static boolean IS_CLIPPED(int res) { | |
| return res == CRES_MIN_CLIPPED || res == CRES_MAX_CLIPPED; | |
| } | |
| /* This is java implementation of the macro from ProcessGeneralPath.c. | |
| * To keep the logic of the java code similar to the native one | |
| * array and set of indexes are used to point out the data. | |
| */ | |
| private static int TESTANDCLIP(float LINE_MIN, float LINE_MAX, float[] c, | |
| int a1, int b1, int a2, int b2) { | |
| double t; | |
| int res = CRES_NOT_CLIPPED; | |
| if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) { | |
| if (c[a1] < (LINE_MIN)) { | |
| if (c[a2] < (LINE_MIN)) { | |
| return CRES_INVISIBLE; | |
| } | |
| res = CRES_MIN_CLIPPED; | |
| t = (LINE_MIN); | |
| } else { | |
| if (c[a2] > (LINE_MAX)) { | |
| return CRES_INVISIBLE; | |
| } | |
| res = CRES_MAX_CLIPPED; | |
| t = (LINE_MAX); | |
| } | |
| c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t); | |
| c[a1] = (float)t; | |
| } | |
| return res; | |
| } | |
| /* Integer version of the method above */ | |
| private static int TESTANDCLIP(int LINE_MIN, int LINE_MAX, int[] c, | |
| int a1, int b1, int a2, int b2) { | |
| double t; | |
| int res = CRES_NOT_CLIPPED; | |
| if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) { | |
| if (c[a1] < (LINE_MIN)) { | |
| if (c[a2] < (LINE_MIN)) { | |
| return CRES_INVISIBLE; | |
| } | |
| res = CRES_MIN_CLIPPED; | |
| t = (LINE_MIN); | |
| } else { | |
| if (c[a2] > (LINE_MAX)) { | |
| return CRES_INVISIBLE; | |
| } | |
| res = CRES_MAX_CLIPPED; | |
| t = (LINE_MAX); | |
| } | |
| c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t); | |
| c[a1] = (int)t; | |
| } | |
| return res; | |
| } | |
| /* Following method is used for clipping and clumping filled shapes. | |
| * An example of this process is shown on the picture below: | |
| * ----+ ----+ | |
| * |/ | |/ | | |
| * + | + | | |
| * /| | I | | |
| * / | | I | | |
| * | | | ===> I | | |
| * \ | | I | | |
| * \| | I | | |
| * + | + | | |
| * |\ | |\ | | |
| * | ----+ | ----+ | |
| * boundary boundary | |
| * | |
| * We can only perform clipping in case of right side of the output area | |
| * because all segments passed out the right boundary don't influence on the | |
| * result of scan conversion algorithm (it correctly handles half open | |
| * contours). | |
| * | |
| * This is java implementation of the macro from ProcessGeneralPath.c. | |
| * To keep the logic of the java code similar to the native one | |
| * array and set of indexes are used to point out the data. | |
| * | |
| */ | |
| private static int CLIPCLAMP(float LINE_MIN, float LINE_MAX, float[] c, | |
| int a1, int b1, int a2, int b2, | |
| int a3, int b3) { | |
| c[a3] = c[a1]; | |
| c[b3] = c[b1]; | |
| int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2); | |
| if (res == CRES_MIN_CLIPPED) { | |
| c[a3] = c[a1]; | |
| } else if (res == CRES_MAX_CLIPPED) { | |
| c[a3] = c[a1]; | |
| res = CRES_MAX_CLIPPED; | |
| } else if (res == CRES_INVISIBLE) { | |
| if (c[a1] > LINE_MAX) { | |
| res = CRES_INVISIBLE; | |
| } else { | |
| c[a1] = LINE_MIN; | |
| c[a2] = LINE_MIN; | |
| res = CRES_NOT_CLIPPED; | |
| } | |
| } | |
| return res; | |
| } | |
| /* Integer version of the method above */ | |
| private static int CLIPCLAMP(int LINE_MIN, int LINE_MAX, int[] c, | |
| int a1, int b1, int a2, int b2, | |
| int a3, int b3) { | |
| c[a3] = c[a1]; | |
| c[b3] = c[b1]; | |
| int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2); | |
| if (res == CRES_MIN_CLIPPED) { | |
| c[a3] = c[a1]; | |
| } else if (res == CRES_MAX_CLIPPED) { | |
| c[a3] = c[a1]; | |
| res = CRES_MAX_CLIPPED; | |
| } else if (res == CRES_INVISIBLE) { | |
| if (c[a1] > LINE_MAX) { | |
| res = CRES_INVISIBLE; | |
| } else { | |
| c[a1] = LINE_MIN; | |
| c[a2] = LINE_MIN; | |
| res = CRES_NOT_CLIPPED; | |
| } | |
| } | |
| return res; | |
| } | |
| private static class DrawProcessHandler extends ProcessHandler { | |
| EndSubPathHandler processESP; | |
| public DrawProcessHandler(DrawHandler dhnd, | |
| EndSubPathHandler processESP) { | |
| super(dhnd, PH_MODE_DRAW_CLIP); | |
| this.dhnd = dhnd; | |
| this.processESP = processESP; | |
| } | |
| public void processEndSubPath() { | |
| processESP.processEndSubPath(); | |
| } | |
| void PROCESS_LINE(int fX0, int fY0, int fX1, int fY1, | |
| boolean checkBounds, int[] pixelInfo) { | |
| int X0 = fX0 >> MDP_PREC; | |
| int Y0 = fY0 >> MDP_PREC; | |
| int X1 = fX1 >> MDP_PREC; | |
| int Y1 = fY1 >> MDP_PREC; | |
| /* Handling lines having just one pixel */ | |
| if (((X0^X1) | (Y0^Y1)) == 0) { | |
| if (checkBounds && | |
| (dhnd.yMin > Y0 || | |
| dhnd.yMax <= Y0 || | |
| dhnd.xMin > X0 || | |
| dhnd.xMax <= X0)) return; | |
| if (pixelInfo[0] == 0) { | |
| pixelInfo[0] = 1; | |
| pixelInfo[1] = X0; | |
| pixelInfo[2] = Y0; | |
| pixelInfo[3] = X0; | |
| pixelInfo[4] = Y0; | |
| dhnd.drawPixel(X0, Y0); | |
| } else if ((X0 != pixelInfo[3] || Y0 != pixelInfo[4]) && | |
| (X0 != pixelInfo[1] || Y0 != pixelInfo[2])) { | |
| dhnd.drawPixel(X0, Y0); | |
| pixelInfo[3] = X0; | |
| pixelInfo[4] = Y0; | |
| } | |
| return; | |
| } | |
| if (!checkBounds || | |
| (dhnd.yMin <= Y0 && | |
| dhnd.yMax > Y0 && | |
| dhnd.xMin <= X0 && | |
| dhnd.xMax > X0)) | |
| { | |
| /* Switch off first pixel of the line before drawing */ | |
| if (pixelInfo[0] == 1 && | |
| ((pixelInfo[1] == X0 && pixelInfo[2] == Y0) || | |
| (pixelInfo[3] == X0 && pixelInfo[4] == Y0))) | |
| { | |
| dhnd.drawPixel(X0, Y0); | |
| } | |
| } | |
| dhnd.drawLine(X0, Y0, X1, Y1); | |
| if (pixelInfo[0] == 0) { | |
| pixelInfo[0] = 1; | |
| pixelInfo[1] = X0; | |
| pixelInfo[2] = Y0; | |
| pixelInfo[3] = X0; | |
| pixelInfo[4] = Y0; | |
| } | |
| /* Switch on last pixel of the line if it was already | |
| * drawn during rendering of the previous segments | |
| */ | |
| if ((pixelInfo[1] == X1 && pixelInfo[2] == Y1) || | |
| (pixelInfo[3] == X1 && pixelInfo[4] == Y1)) | |
| { | |
| if (checkBounds && | |
| (dhnd.yMin > Y1 || | |
| dhnd.yMax <= Y1 || | |
| dhnd.xMin > X1 || | |
| dhnd.xMax <= X1)) { | |
| return; | |
| } | |
| dhnd.drawPixel(X1, Y1); | |
| } | |
| pixelInfo[3] = X1; | |
| pixelInfo[4] = Y1; | |
| } | |
| void PROCESS_POINT(int fX, int fY, boolean checkBounds, | |
| int[] pixelInfo) { | |
| int _X = fX>> MDP_PREC; | |
| int _Y = fY>> MDP_PREC; | |
| if (checkBounds && | |
| (dhnd.yMin > _Y || | |
| dhnd.yMax <= _Y || | |
| dhnd.xMin > _X || | |
| dhnd.xMax <= _X)) return; | |
| /* | |
| * (_X,_Y) should be inside boundaries | |
| * | |
| * assert(dhnd.yMin <= _Y && | |
| * dhnd.yMax > _Y && | |
| * dhnd.xMin <= _X && | |
| * dhnd.xMax > _X); | |
| * | |
| */ | |
| if (pixelInfo[0] == 0) { | |
| pixelInfo[0] = 1; | |
| pixelInfo[1] = _X; | |
| pixelInfo[2] = _Y; | |
| pixelInfo[3] = _X; | |
| pixelInfo[4] = _Y; | |
| dhnd.drawPixel(_X, _Y); | |
| } else if ((_X != pixelInfo[3] || _Y != pixelInfo[4]) && | |
| (_X != pixelInfo[1] || _Y != pixelInfo[2])) { | |
| dhnd.drawPixel(_X, _Y); | |
| pixelInfo[3] = _X; | |
| pixelInfo[4] = _Y; | |
| } | |
| } | |
| /* Drawing line with subpixel endpoints | |
| * | |
| * (x1, y1), (x2, y2) - fixed point coordinates of the endpoints | |
| * with MDP_PREC bits for the fractional part | |
| * | |
| * pixelInfo - structure which keeps drawing info for avoiding | |
| * multiple drawing at the same position on the | |
| * screen (required for the XOR mode of drawing) | |
| * | |
| * pixelInfo[0] - state of the drawing | |
| * 0 - no pixel drawn between | |
| * moveTo/close of the path | |
| * 1 - there are drawn pixels | |
| * | |
| * pixelInfo[1,2] - first pixel of the path | |
| * between moveTo/close of the | |
| * path | |
| * | |
| * pixelInfo[3,4] - last drawn pixel between | |
| * moveTo/close of the path | |
| * | |
| * checkBounds - flag showing necessity of checking the clip | |
| * | |
| */ | |
| public void processFixedLine(int x1, int y1, int x2, int y2, | |
| int[] pixelInfo, boolean checkBounds, | |
| boolean endSubPath) { | |
| /* Checking if line is inside a (X,Y),(X+MDP_MULT,Y+MDP_MULT) box */ | |
| int c = ((x1 ^ x2) | (y1 ^ y2)); | |
| int rx1, ry1, rx2, ry2; | |
| if ((c & MDP_W_MASK) == 0) { | |
| /* Checking for the segments with integer coordinates having | |
| * the same start and end points | |
| */ | |
| if (c == 0) { | |
| PROCESS_POINT(x1 + MDP_HALF_MULT, y1 + MDP_HALF_MULT, | |
| checkBounds, pixelInfo); | |
| } | |
| return; | |
| } | |
| if (x1 == x2 || y1 == y2) { | |
| rx1 = x1 + MDP_HALF_MULT; | |
| rx2 = x2 + MDP_HALF_MULT; | |
| ry1 = y1 + MDP_HALF_MULT; | |
| ry2 = y2 + MDP_HALF_MULT; | |
| } else { | |
| /* Neither dx nor dy can be zero because of the check above */ | |
| int dx = x2 - x1; | |
| int dy = y2 - y1; | |
| /* Floor of x1, y1, x2, y2 */ | |
| int fx1 = x1 & MDP_W_MASK; | |
| int fy1 = y1 & MDP_W_MASK; | |
| int fx2 = x2 & MDP_W_MASK; | |
| int fy2 = y2 & MDP_W_MASK; | |
| /* Processing first endpoint */ | |
| if (fx1 == x1 || fy1 == y1) { | |
| /* Adding MDP_HALF_MULT to the [xy]1 if f[xy]1 == [xy]1 | |
| * will not affect the result | |
| */ | |
| rx1 = x1 + MDP_HALF_MULT; | |
| ry1 = y1 + MDP_HALF_MULT; | |
| } else { | |
| /* Boundary at the direction from (x1,y1) to (x2,y2) */ | |
| int bx1 = (x1 < x2) ? fx1 + MDP_MULT : fx1; | |
| int by1 = (y1 < y2) ? fy1 + MDP_MULT : fy1; | |
| /* intersection with column bx1 */ | |
| int cross = y1 + ((bx1 - x1)*dy)/dx; | |
| if (cross >= fy1 && cross <= fy1 + MDP_MULT) { | |
| rx1 = bx1; | |
| ry1 = cross + MDP_HALF_MULT; | |
| } else { | |
| /* intersection with row by1 */ | |
| cross = x1 + ((by1 - y1)*dx)/dy; | |
| rx1 = cross + MDP_HALF_MULT; | |
| ry1 = by1; | |
| } | |
| } | |
| /* Processing second endpoint */ | |
| if (fx2 == x2 || fy2 == y2) { | |
| /* Adding MDP_HALF_MULT to the [xy]2 if f[xy]2 == [xy]2 | |
| * will not affect the result | |
| */ | |
| rx2 = x2 + MDP_HALF_MULT; | |
| ry2 = y2 + MDP_HALF_MULT; | |
| } else { | |
| /* Boundary at the direction from (x2,y2) to (x1,y1) */ | |
| int bx2 = (x1 > x2) ? fx2 + MDP_MULT : fx2; | |
| int by2 = (y1 > y2) ? fy2 + MDP_MULT : fy2; | |
| /* intersection with column bx2 */ | |
| int cross = y2 + ((bx2 - x2)*dy)/dx; | |
| if (cross >= fy2 && cross <= fy2 + MDP_MULT) { | |
| rx2 = bx2; | |
| ry2 = cross + MDP_HALF_MULT; | |
| } else { | |
| /* intersection with row by2 */ | |
| cross = x2 + ((by2 - y2)*dx)/dy; | |
| rx2 = cross + MDP_HALF_MULT; | |
| ry2 = by2; | |
| } | |
| } | |
| } | |
| PROCESS_LINE(rx1, ry1, rx2, ry2, checkBounds, pixelInfo); | |
| } | |
| } | |
| /* Performing drawing of the monotonic in X and Y quadratic curves with | |
| * sizes less than MAX_QUAD_SIZE by using forward differencing method of | |
| * calculation. See comments to the DrawMonotonicQuad in the | |
| * ProcessGeneralPath.c | |
| */ | |
| private static void DrawMonotonicQuad(ProcessHandler hnd, | |
| float[] coords, | |
| boolean checkBounds, | |
| int[] pixelInfo) { | |
| int x0 = (int)(coords[0]*MDP_MULT); | |
| int y0 = (int)(coords[1]*MDP_MULT); | |
| int xe = (int)(coords[4]*MDP_MULT); | |
| int ye = (int)(coords[5]*MDP_MULT); | |
| /* Extracting fractional part of coordinates of first control point */ | |
| int px = (x0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT; | |
| int py = (y0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT; | |
| /* Setting default amount of steps */ | |
| int count = DF_QUAD_COUNT; | |
| /* Setting default shift for preparing to the midpoint rounding */ | |
| int shift = DF_QUAD_SHIFT; | |
| int ax = (int)((coords[0] - 2*coords[2] + | |
| coords[4])*QUAD_A_MDP_MULT); | |
| int ay = (int)((coords[1] - 2*coords[3] + | |
| coords[5])*QUAD_A_MDP_MULT); | |
| int bx = (int)((-2*coords[0] + 2*coords[2])*QUAD_B_MDP_MULT); | |
| int by = (int)((-2*coords[1] + 2*coords[3])*QUAD_B_MDP_MULT); | |
| int ddpx = 2*ax; | |
| int ddpy = 2*ay; | |
| int dpx = ax + bx; | |
| int dpy = ay + by; | |
| int x1, y1; | |
| int x2 = x0; | |
| int y2 = y0; | |
| int maxDD = Math.max(Math.abs(ddpx),Math.abs(ddpy)); | |
| int dx = xe - x0; | |
| int dy = ye - y0; | |
| int x0w = x0 & MDP_W_MASK; | |
| int y0w = y0 & MDP_W_MASK; | |
| /* Perform decreasing step in 2 times if slope of the first forward | |
| * difference changes too quickly (more than a pixel per step in X or Y | |
| * direction). We can perform adjusting of the step size before the | |
| * rendering loop because the curvature of the quad curve remains the | |
| * same along all the curve | |
| */ | |
| while (maxDD > DF_QUAD_DEC_BND) { | |
| dpx = (dpx<<1) - ax; | |
| dpy = (dpy<<1) - ay; | |
| count <<= 1; | |
| maxDD >>= 2; | |
| px <<=2; | |
| py <<=2; | |
| shift += 2; | |
| } | |
| while(count-- > 1) { | |
| px += dpx; | |
| py += dpy; | |
| dpx += ddpx; | |
| dpy += ddpy; | |
| x1 = x2; | |
| y1 = y2; | |
| x2 = x0w + (px >> shift); | |
| y2 = y0w + (py >> shift); | |
| /* Checking that we are not running out of the endpoint and bounding | |
| * violating coordinate. The check is pretty simple because the | |
| * curve passed to the DrawCubic already split into the | |
| * monotonic in X and Y pieces | |
| */ | |
| /* Bounding x2 by xe */ | |
| if (((xe-x2)^dx) < 0) { | |
| x2 = xe; | |
| } | |
| /* Bounding y2 by ye */ | |
| if (((ye-y2)^dy) < 0) { | |
| y2 = ye; | |
| } | |
| hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds, false); | |
| } | |
| /* We are performing one step less than necessary and use actual | |
| * (xe,ye) endpoint of the curve instead of calculated. This prevent us | |
| * from running above the curve endpoint due to the accumulated errors | |
| * during the iterations. | |
| */ | |
| hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds, false); | |
| } | |
| /* | |
| * Checking size of the quad curves and split them if necessary. | |
| * Calling DrawMonotonicQuad for the curves of the appropriate size. | |
| * Note: coords array could be changed | |
| */ | |
| private static void ProcessMonotonicQuad(ProcessHandler hnd, | |
| float[] coords, | |
| int[] pixelInfo) { | |
| float[] coords1 = new float[6]; | |
| float tx, ty; | |
| float xMin, yMin, xMax, yMax; | |
| xMin = xMax = coords[0]; | |
| yMin = yMax = coords[1]; | |
| for (int i = 2; i < 6; i += 2) { | |
| xMin = (xMin > coords[i])? coords[i] : xMin; | |
| xMax = (xMax < coords[i])? coords[i] : xMax; | |
| yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin; | |
| yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax; | |
| } | |
| if (hnd.clipMode == PH_MODE_DRAW_CLIP) { | |
| /* In case of drawing we could just skip curves which are | |
| * completely out of bounds | |
| */ | |
| if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax || | |
| hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) { | |
| return; | |
| } | |
| } else { | |
| /* In case of filling we could skip curves which are above, | |
| * below and behind the right boundary of the visible area | |
| */ | |
| if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax || | |
| hnd.dhnd.xMaxf < xMin) | |
| { | |
| return; | |
| } | |
| /* We could clamp x coordinates to the corresponding boundary | |
| * if the curve is completely behind the left one | |
| */ | |
| if (hnd.dhnd.xMinf > xMax) { | |
| coords[0] = coords[2] = coords[4] = hnd.dhnd.xMinf; | |
| } | |
| } | |
| if (xMax - xMin > MAX_QUAD_SIZE || yMax - yMin > MAX_QUAD_SIZE) { | |
| coords1[4] = coords[4]; | |
| coords1[5] = coords[5]; | |
| coords1[2] = (coords[2] + coords[4])/2.0f; | |
| coords1[3] = (coords[3] + coords[5])/2.0f; | |
| coords[2] = (coords[0] + coords[2])/2.0f; | |
| coords[3] = (coords[1] + coords[3])/2.0f; | |
| coords[4] = coords1[0] = (coords[2] + coords1[2])/2.0f; | |
| coords[5] = coords1[1] = (coords[3] + coords1[3])/2.0f; | |
| ProcessMonotonicQuad(hnd, coords, pixelInfo); | |
| ProcessMonotonicQuad(hnd, coords1, pixelInfo); | |
| } else { | |
| DrawMonotonicQuad(hnd, coords, | |
| /* Set checkBounds parameter if curve intersects | |
| * boundary of the visible area. We know that the | |
| * curve is visible, so the check is pretty | |
| * simple | |
| */ | |
| hnd.dhnd.xMinf >= xMin || | |
| hnd.dhnd.xMaxf <= xMax || | |
| hnd.dhnd.yMinf >= yMin || | |
| hnd.dhnd.yMaxf <= yMax, | |
| pixelInfo); | |
| } | |
| } | |
| /* | |
| * Split quadratic curve into monotonic in X and Y parts. Calling | |
| * ProcessMonotonicQuad for each monotonic piece of the curve. | |
| * Note: coords array could be changed | |
| */ | |
| private static void ProcessQuad(ProcessHandler hnd, float[] coords, | |
| int[] pixelInfo) { | |
| /* Temporary array for holding parameters corresponding to the extreme | |
| * in X and Y points | |
| */ | |
| double[] params = new double[2]; | |
| int cnt = 0; | |
| double param; | |
| /* Simple check for monotonicity in X before searching for the extreme | |
| * points of the X(t) function. We first check if the curve is | |
| * monotonic in X by seeing if all of the X coordinates are strongly | |
| * ordered. | |
| */ | |
| if ((coords[0] > coords[2] || coords[2] > coords[4]) && | |
| (coords[0] < coords[2] || coords[2] < coords[4])) | |
| { | |
| /* Searching for extreme points of the X(t) function by solving | |
| * dX(t) | |
| * ---- = 0 equation | |
| * dt | |
| */ | |
| double ax = coords[0] - 2*coords[2] + coords[4]; | |
| if (ax != 0) { | |
| /* Calculating root of the following equation | |
| * ax*t + bx = 0 | |
| */ | |
| double bx = coords[0] - coords[2]; | |
| param = bx/ax; | |
| if (param < 1.0 && param > 0.0) { | |
| params[cnt++] = param; | |
| } | |
| } | |
| } | |
| /* Simple check for monotonicity in Y before searching for the extreme | |
| * points of the Y(t) function. We first check if the curve is | |
| * monotonic in Y by seeing if all of the Y coordinates are strongly | |
| * ordered. | |
| */ | |
| if ((coords[1] > coords[3] || coords[3] > coords[5]) && | |
| (coords[1] < coords[3] || coords[3] < coords[5])) | |
| { | |
| /* Searching for extreme points of the Y(t) function by solving | |
| * dY(t) | |
| * ----- = 0 equation | |
| * dt | |
| */ | |
| double ay = coords[1] - 2*coords[3] + coords[5]; | |
| if (ay != 0) { | |
| /* Calculating root of the following equation | |
| * ay*t + by = 0 | |
| */ | |
| double by = coords[1] - coords[3]; | |
| param = by/ay; | |
| if (param < 1.0 && param > 0.0) { | |
| if (cnt > 0) { | |
| /* Inserting parameter only if it differs from | |
| * already stored | |
| */ | |
| if (params[0] > param) { | |
| params[cnt++] = params[0]; | |
| params[0] = param; | |
| } else if (params[0] < param) { | |
| params[cnt++] = param; | |
| } | |
| } else { | |
| params[cnt++] = param; | |
| } | |
| } | |
| } | |
| } | |
| /* Processing obtained monotonic parts */ | |
| switch(cnt) { | |
| case 0: | |
| break; | |
| case 1: | |
| ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo, | |
| (float)params[0]); | |
| break; | |
| case 2: | |
| ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo, | |
| (float)params[0]); | |
| param = params[1] - params[0]; | |
| if (param > 0) { | |
| ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo, | |
| /* Scale parameter to match with | |
| * rest of the curve | |
| */ | |
| (float)(param/(1.0 - params[0]))); | |
| } | |
| break; | |
| } | |
| ProcessMonotonicQuad(hnd,coords,pixelInfo); | |
| } | |
| /* | |
| * Bite the piece of the quadratic curve from start point till the point | |
| * corresponding to the specified parameter then call ProcessQuad for the | |
| * bitten part. | |
| * Note: coords array will be changed | |
| */ | |
| private static void ProcessFirstMonotonicPartOfQuad(ProcessHandler hnd, | |
| float[] coords, | |
| int[] pixelInfo, | |
| float t) { | |
| float[] coords1 = new float[6]; | |
| coords1[0] = coords[0]; | |
| coords1[1] = coords[1]; | |
| coords1[2] = coords[0] + t*(coords[2] - coords[0]); | |
| coords1[3] = coords[1] + t*(coords[3] - coords[1]); | |
| coords[2] = coords[2] + t*(coords[4] - coords[2]); | |
| coords[3] = coords[3] + t*(coords[5] - coords[3]); | |
| coords[0] = coords1[4] = coords1[2] + t*(coords[2] - coords1[2]); | |
| coords[1] = coords1[5] = coords1[3] + t*(coords[3] - coords1[3]); | |
| ProcessMonotonicQuad(hnd, coords1, pixelInfo); | |
| } | |
| /* Performing drawing of the monotonic in X and Y cubic curves with sizes | |
| * less than MAX_CUB_SIZE by using forward differencing method of | |
| * calculation. See comments to the DrawMonotonicCubic in the | |
| * ProcessGeneralPath.c | |
| */ | |
| private static void DrawMonotonicCubic(ProcessHandler hnd, | |
| float[] coords, | |
| boolean checkBounds, | |
| int[] pixelInfo) { | |
| int x0 = (int)(coords[0]*MDP_MULT); | |
| int y0 = (int)(coords[1]*MDP_MULT); | |
| int xe = (int)(coords[6]*MDP_MULT); | |
| int ye = (int)(coords[7]*MDP_MULT); | |
| /* Extracting fractional part of coordinates of first control point */ | |
| int px = (x0 & (~MDP_W_MASK)) << DF_CUB_SHIFT; | |
| int py = (y0 & (~MDP_W_MASK)) << DF_CUB_SHIFT; | |
| /* Setting default boundary values for checking first and second forward | |
| * difference for the necessity of the restepping. See comments to the | |
| * boundary values in ProcessQuad for more info. | |
| */ | |
| int incStepBnd = DF_CUB_INC_BND; | |
| int decStepBnd = DF_CUB_DEC_BND; | |
| /* Setting default amount of steps */ | |
| int count = DF_CUB_COUNT; | |
| /* Setting default shift for preparing to the midpoint rounding */ | |
| int shift = DF_CUB_SHIFT; | |
| int ax = (int)((-coords[0] + 3*coords[2] - 3*coords[4] + | |
| coords[6])*CUB_A_MDP_MULT); | |
| int ay = (int)((-coords[1] + 3*coords[3] - 3*coords[5] + | |
| coords[7])*CUB_A_MDP_MULT); | |
| int bx = (int)((3*coords[0] - 6*coords[2] + | |
| 3*coords[4])*CUB_B_MDP_MULT); | |
| int by = (int)((3*coords[1] - 6*coords[3] + | |
| 3*coords[5])*CUB_B_MDP_MULT); | |
| int cx = (int)((-3*coords[0] + 3*coords[2])*(CUB_C_MDP_MULT)); | |
| int cy = (int)((-3*coords[1] + 3*coords[3])*(CUB_C_MDP_MULT)); | |
| int dddpx = 6*ax; | |
| int dddpy = 6*ay; | |
| int ddpx = dddpx + bx; | |
| int ddpy = dddpy + by; | |
| int dpx = ax + (bx>>1) + cx; | |
| int dpy = ay + (by>>1) + cy; | |
| int x1, y1; | |
| int x2 = x0; | |
| int y2 = y0; | |
| /* Calculating whole part of the first point of the curve */ | |
| int x0w = x0 & MDP_W_MASK; | |
| int y0w = y0 & MDP_W_MASK; | |
| int dx = xe - x0; | |
| int dy = ye - y0; | |
| while (count > 0) { | |
| /* Perform decreasing step in 2 times if necessary */ | |
| while (Math.abs(ddpx) > decStepBnd || | |
| Math.abs(ddpy) > decStepBnd) { | |
| ddpx = (ddpx<<1) - dddpx; | |
| ddpy = (ddpy<<1) - dddpy; | |
| dpx = (dpx<<2) - (ddpx>>1); | |
| dpy = (dpy<<2) - (ddpy>>1); | |
| count <<=1; | |
| decStepBnd <<=3; | |
| incStepBnd <<=3; | |
| px <<=3; | |
| py <<=3; | |
| shift += 3; | |
| } | |
| /* Perform increasing step in 2 times if necessary. | |
| * Note: we could do it only in even steps | |
| */ | |
| while ((count & 1) == 0 && shift > DF_CUB_SHIFT && | |
| Math.abs(dpx) <= incStepBnd && | |
| Math.abs(dpy) <= incStepBnd) { | |
| dpx = (dpx>>2) + (ddpx>>3); | |
| dpy = (dpy>>2) + (ddpy>>3); | |
| ddpx = (ddpx + dddpx)>>1; | |
| ddpy = (ddpy + dddpy)>>1; | |
| count >>=1; | |
| decStepBnd >>=3; | |
| incStepBnd >>=3; | |
| px >>=3; | |
| py >>=3; | |
| shift -= 3; | |
| } | |
| count--; | |
| /* Performing one step less than necessary and use actual (xe,ye) | |
| * curve's endpoint instead of calculated. This prevent us from | |
| * running above the curve endpoint due to the accumulated errors | |
| * during the iterations. | |
| */ | |
| if (count > 0) { | |
| px += dpx; | |
| py += dpy; | |
| dpx += ddpx; | |
| dpy += ddpy; | |
| ddpx += dddpx; | |
| ddpy += dddpy; | |
| x1 = x2; | |
| y1 = y2; | |
| x2 = x0w + (px >> shift); | |
| y2 = y0w + (py >> shift); | |
| /* Checking that we are not running out of the endpoint and | |
| * bounding violating coordinate. The check is pretty simple | |
| * because the curve passed to the DrawCubic already split | |
| * into the monotonic in X and Y pieces | |
| */ | |
| /* Bounding x2 by xe */ | |
| if (((xe-x2)^dx) < 0) { | |
| x2 = xe; | |
| } | |
| /* Bounding y2 by ye */ | |
| if (((ye-y2)^dy) < 0) { | |
| y2 = ye; | |
| } | |
| hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds, | |
| false); | |
| } else { | |
| hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds, | |
| false); | |
| } | |
| } | |
| } | |
| /* | |
| * Checking size of the cubic curves and split them if necessary. | |
| * Calling DrawMonotonicCubic for the curves of the appropriate size. | |
| * Note: coords array could be changed | |
| */ | |
| private static void ProcessMonotonicCubic(ProcessHandler hnd, | |
| float[] coords, | |
| int[] pixelInfo) { | |
| float[] coords1 = new float[8]; | |
| float tx, ty; | |
| float xMin, xMax; | |
| float yMin, yMax; | |
| xMin = xMax = coords[0]; | |
| yMin = yMax = coords[1]; | |
| for (int i = 2; i < 8; i += 2) { | |
| xMin = (xMin > coords[i])? coords[i] : xMin; | |
| xMax = (xMax < coords[i])? coords[i] : xMax; | |
| yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin; | |
| yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax; | |
| } | |
| if (hnd.clipMode == PH_MODE_DRAW_CLIP) { | |
| /* In case of drawing we could just skip curves which are | |
| * completely out of bounds | |
| */ | |
| if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax || | |
| hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) { | |
| return; | |
| } | |
| } else { | |
| /* In case of filling we could skip curves which are above, | |
| * below and behind the right boundary of the visible area | |
| */ | |
| if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax || | |
| hnd.dhnd.xMaxf < xMin) | |
| { | |
| return; | |
| } | |
| /* We could clamp x coordinates to the corresponding boundary | |
| * if the curve is completely behind the left one | |
| */ | |
| if (hnd.dhnd.xMinf > xMax) { | |
| coords[0] = coords[2] = coords[4] = coords[6] = | |
| hnd.dhnd.xMinf; | |
| } | |
| } | |
| if (xMax - xMin > MAX_CUB_SIZE || yMax - yMin > MAX_CUB_SIZE) { | |
| coords1[6] = coords[6]; | |
| coords1[7] = coords[7]; | |
| coords1[4] = (coords[4] + coords[6])/2.0f; | |
| coords1[5] = (coords[5] + coords[7])/2.0f; | |
| tx = (coords[2] + coords[4])/2.0f; | |
| ty = (coords[3] + coords[5])/2.0f; | |
| coords1[2] = (tx + coords1[4])/2.0f; | |
| coords1[3] = (ty + coords1[5])/2.0f; | |
| coords[2] = (coords[0] + coords[2])/2.0f; | |
| coords[3] = (coords[1] + coords[3])/2.0f; | |
| coords[4] = (coords[2] + tx)/2.0f; | |
| coords[5] = (coords[3] + ty)/2.0f; | |
| coords[6]=coords1[0]=(coords[4] + coords1[2])/2.0f; | |
| coords[7]=coords1[1]=(coords[5] + coords1[3])/2.0f; | |
| ProcessMonotonicCubic(hnd, coords, pixelInfo); | |
| ProcessMonotonicCubic(hnd, coords1, pixelInfo); | |
| } else { | |
| DrawMonotonicCubic(hnd, coords, | |
| /* Set checkBounds parameter if curve intersects | |
| * boundary of the visible area. We know that | |
| * the curve is visible, so the check is pretty | |
| * simple | |
| */ | |
| hnd.dhnd.xMinf > xMin || | |
| hnd.dhnd.xMaxf < xMax || | |
| hnd.dhnd.yMinf > yMin || | |
| hnd.dhnd.yMaxf < yMax, | |
| pixelInfo); | |
| } | |
| } | |
| /* | |
| * Split cubic curve into monotonic in X and Y parts. Calling | |
| * ProcessMonotonicCubic for each monotonic piece of the curve. | |
| * | |
| * Note: coords array could be changed | |
| */ | |
| private static void ProcessCubic(ProcessHandler hnd, | |
| float[] coords, | |
| int[] pixelInfo) { | |
| /* Temporary array for holding parameters corresponding to the extreme | |
| * in X and Y points | |
| */ | |
| double[] params = new double[4]; | |
| double[] eqn = new double[3]; | |
| double[] res = new double[2]; | |
| int cnt = 0; | |
| /* Simple check for monotonicity in X before searching for the extreme | |
| * points of the X(t) function. We first check if the curve is | |
| * monotonic in X by seeing if all of the X coordinates are strongly | |
| * ordered. | |
| */ | |
| if ((coords[0] > coords[2] || coords[2] > coords[4] || | |
| coords[4] > coords[6]) && | |
| (coords[0] < coords[2] || coords[2] < coords[4] || | |
| coords[4] < coords[6])) | |
| { | |
| /* Searching for extreme points of the X(t) function by solving | |
| * dX(t) | |
| * ---- = 0 equation | |
| * dt | |
| */ | |
| eqn[2] = -coords[0] + 3*coords[2] - 3*coords[4] + coords[6]; | |
| eqn[1] = 2*(coords[0] - 2*coords[2] + coords[4]); | |
| eqn[0] = -coords[0] + coords[2]; | |
| int nr = QuadCurve2D.solveQuadratic(eqn, res); | |
| /* Following code also correctly works in degenerate case of | |
| * the quadratic equation (nr = -1) because we do not need | |
| * splitting in this case. | |
| */ | |
| for (int i = 0; i < nr; i++) { | |
| if (res[i] > 0 && res[i] < 1) { | |
| params[cnt++] = res[i]; | |
| } | |
| } | |
| } | |
| /* Simple check for monotonicity in Y before searching for the extreme | |
| * points of the Y(t) function. We first check if the curve is | |
| * monotonic in Y by seeing if all of the Y coordinates are strongly | |
| * ordered. | |
| */ | |
| if ((coords[1] > coords[3] || coords[3] > coords[5] || | |
| coords[5] > coords[7]) && | |
| (coords[1] < coords[3] || coords[3] < coords[5] || | |
| coords[5] < coords[7])) | |
| { | |
| /* Searching for extreme points of the Y(t) function by solving | |
| * dY(t) | |
| * ----- = 0 equation | |
| * dt | |
| */ | |
| eqn[2] = -coords[1] + 3*coords[3] - 3*coords[5] + coords[7]; | |
| eqn[1] = 2*(coords[1] - 2*coords[3] + coords[5]); | |
| eqn[0] = -coords[1] + coords[3]; | |
| int nr = QuadCurve2D.solveQuadratic(eqn, res); | |
| /* Following code also correctly works in degenerate case of | |
| * the quadratic equation (nr = -1) because we do not need | |
| * splitting in this case. | |
| */ | |
| for (int i = 0; i < nr; i++) { | |
| if (res[i] > 0 && res[i] < 1) { | |
| params[cnt++] = res[i]; | |
| } | |
| } | |
| } | |
| if (cnt > 0) { | |
| /* Sorting parameter values corresponding to the extreme points | |
| * of the curve | |
| */ | |
| Arrays.sort(params, 0, cnt); | |
| /* Processing obtained monotonic parts */ | |
| ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo, | |
| (float)params[0]); | |
| for (int i = 1; i < cnt; i++) { | |
| double param = params[i] - params[i-1]; | |
| if (param > 0) { | |
| ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo, | |
| /* Scale parameter to match with rest of the curve */ | |
| (float)(param/(1.0 - params[i - 1]))); | |
| } | |
| } | |
| } | |
| ProcessMonotonicCubic(hnd,coords,pixelInfo); | |
| } | |
| /* | |
| * Bite the piece of the cubic curve from start point till the point | |
| * corresponding to the specified parameter then call ProcessCubic for the | |
| * bitten part. | |
| * Note: coords array will be changed | |
| */ | |
| private static void ProcessFirstMonotonicPartOfCubic(ProcessHandler hnd, | |
| float[] coords, | |
| int[] pixelInfo, | |
| float t) | |
| { | |
| float[] coords1 = new float[8]; | |
| float tx, ty; | |
| coords1[0] = coords[0]; | |
| coords1[1] = coords[1]; | |
| tx = coords[2] + t*(coords[4] - coords[2]); | |
| ty = coords[3] + t*(coords[5] - coords[3]); | |
| coords1[2] = coords[0] + t*(coords[2] - coords[0]); | |
| coords1[3] = coords[1] + t*(coords[3] - coords[1]); | |
| coords1[4] = coords1[2] + t*(tx - coords1[2]); | |
| coords1[5] = coords1[3] + t*(ty - coords1[3]); | |
| coords[4] = coords[4] + t*(coords[6] - coords[4]); | |
| coords[5] = coords[5] + t*(coords[7] - coords[5]); | |
| coords[2] = tx + t*(coords[4] - tx); | |
| coords[3] = ty + t*(coords[5] - ty); | |
| coords[0]=coords1[6]=coords1[4] + t*(coords[2] - coords1[4]); | |
| coords[1]=coords1[7]=coords1[5] + t*(coords[3] - coords1[5]); | |
| ProcessMonotonicCubic(hnd, coords1, pixelInfo); | |
| } | |
| /* Note: | |
| * For more easy reading of the code below each java version of the macros | |
| * from the ProcessPath.c preceded by the commented origin call | |
| * containing verbose names of the parameters | |
| */ | |
| private static void ProcessLine(ProcessHandler hnd, float x1, float y1, | |
| float x2, float y2, int[] pixelInfo) { | |
| float xMin, yMin, xMax, yMax; | |
| int X1, Y1, X2, Y2, X3, Y3, res; | |
| boolean clipped = false; | |
| float x3,y3; | |
| float[] c = new float[]{x1, y1, x2, y2, 0, 0}; | |
| boolean lastClipped; | |
| xMin = hnd.dhnd.xMinf; | |
| yMin = hnd.dhnd.yMinf; | |
| xMax = hnd.dhnd.xMaxf; | |
| yMax = hnd.dhnd.yMaxf; | |
| // | |
| // TESTANDCLIP(yMin, yMax, y1, x1, y2, x2, res); | |
| // | |
| res = TESTANDCLIP(yMin, yMax, c, 1, 0, 3, 2); | |
| if (res == CRES_INVISIBLE) return; | |
| clipped = IS_CLIPPED(res); | |
| // | |
| // TESTANDCLIP(yMin, yMax, y2, x2, y1, x1, res); | |
| // | |
| res = TESTANDCLIP(yMin, yMax, c, 3, 2, 1, 0); | |
| if (res == CRES_INVISIBLE) return; | |
| lastClipped = IS_CLIPPED(res); | |
| clipped = clipped || lastClipped; | |
| if (hnd.clipMode == PH_MODE_DRAW_CLIP) { | |
| // | |
| // TESTANDCLIP(xMin, xMax, x1, y1, x2, y2, res); | |
| // | |
| res = TESTANDCLIP(xMin, xMax, c, 0, 1, 2, 3); | |
| if (res == CRES_INVISIBLE) return; | |
| clipped = clipped || IS_CLIPPED(res); | |
| // | |
| // TESTANDCLIP(xMin, xMax, x2, y2, x1, y1, res); | |
| // | |
| res = TESTANDCLIP(xMin, xMax, c, 2, 3, 0, 1); | |
| if (res == CRES_INVISIBLE) return; | |
| lastClipped = lastClipped || IS_CLIPPED(res); | |
| clipped = clipped || lastClipped; | |
| X1 = (int)(c[0]*MDP_MULT); | |
| Y1 = (int)(c[1]*MDP_MULT); | |
| X2 = (int)(c[2]*MDP_MULT); | |
| Y2 = (int)(c[3]*MDP_MULT); | |
| hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo, | |
| clipped, /* enable boundary checking in | |
| case of clipping to avoid | |
| entering out of bounds which | |
| could happens during rounding | |
| */ | |
| lastClipped /* Notify pProcessFixedLine | |
| that | |
| this is the end of the | |
| subpath (because of exiting | |
| out of boundaries) | |
| */ | |
| ); | |
| } else { | |
| /* Clamping starting from first vertex of the processed | |
| * segment | |
| * | |
| * CLIPCLAMP(xMin, xMax, x1, y1, x2, y2, x3, y3, res); | |
| */ | |
| res = CLIPCLAMP(xMin, xMax, c, 0, 1, 2, 3, 4, 5); | |
| X1 = (int)(c[0]*MDP_MULT); | |
| Y1 = (int)(c[1]*MDP_MULT); | |
| /* Clamping only by left boundary */ | |
| if (res == CRES_MIN_CLIPPED) { | |
| X3 = (int)(c[4]*MDP_MULT); | |
| Y3 = (int)(c[5]*MDP_MULT); | |
| hnd.processFixedLine(X3, Y3, X1, Y1, pixelInfo, | |
| false, lastClipped); | |
| } else if (res == CRES_INVISIBLE) { | |
| return; | |
| } | |
| /* Clamping starting from last vertex of the processed | |
| * segment | |
| * | |
| * CLIPCLAMP(xMin, xMax, x2, y2, x1, y1, x3, y3, res); | |
| */ | |
| res = CLIPCLAMP(xMin, xMax, c, 2, 3, 0, 1, 4, 5); | |
| /* Checking if there was a clip by right boundary */ | |
| lastClipped = lastClipped || (res == CRES_MAX_CLIPPED); | |
| X2 = (int)(c[2]*MDP_MULT); | |
| Y2 = (int)(c[3]*MDP_MULT); | |
| hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo, | |
| false, lastClipped); | |
| /* Clamping only by left boundary */ | |
| if (res == CRES_MIN_CLIPPED) { | |
| X3 = (int)(c[4]*MDP_MULT); | |
| Y3 = (int)(c[5]*MDP_MULT); | |
| hnd.processFixedLine(X2, Y2, X3, Y3, pixelInfo, | |
| false, lastClipped); | |
| } | |
| } | |
| } | |
| private static boolean doProcessPath(ProcessHandler hnd, | |
| Path2D.Float p2df, | |
| float transXf, float transYf) { | |
| float[] coords = new float[8]; | |
| float[] tCoords = new float[8]; | |
| float[] closeCoord = new float[] {0.0f, 0.0f}; | |
| int[] pixelInfo = new int[5]; | |
| boolean subpathStarted = false; | |
| boolean skip = false; | |
| float lastX, lastY; | |
| pixelInfo[0] = 0; | |
| /* Adjusting boundaries to the capabilities of the | |
| * ProcessPath code | |
| */ | |
| hnd.dhnd.adjustBounds(LOWER_OUT_BND, LOWER_OUT_BND, | |
| UPPER_OUT_BND, UPPER_OUT_BND); | |
| /* Adding support of the KEY_STROKE_CONTROL rendering hint. | |
| * Now we are supporting two modes: "pixels at centers" and | |
| * "pixels at corners". | |
| * First one is disabled by default but could be enabled by setting | |
| * VALUE_STROKE_PURE to the rendering hint. It means that pixel at the | |
| * screen (x,y) has (x + 0.5, y + 0.5) float coordinates. | |
| * | |
| * Second one is enabled by default and means straightforward mapping | |
| * (x,y) --> (x,y) | |
| */ | |
| if (hnd.dhnd.strokeControl == SunHints.INTVAL_STROKE_PURE) { | |
| closeCoord[0] = -0.5f; | |
| closeCoord[1] = -0.5f; | |
| transXf -= 0.5; | |
| transYf -= 0.5; | |
| } | |
| PathIterator pi = p2df.getPathIterator(null); | |
| while (!pi.isDone()) { | |
| switch (pi.currentSegment(coords)) { | |
| case PathIterator.SEG_MOVETO: | |
| /* Performing closing of the unclosed segments */ | |
| if (subpathStarted && !skip) { | |
| if (hnd.clipMode == PH_MODE_FILL_CLIP) { | |
| if (tCoords[0] != closeCoord[0] || | |
| tCoords[1] != closeCoord[1]) | |
| { | |
| ProcessLine(hnd, tCoords[0], tCoords[1], | |
| closeCoord[0], closeCoord[1], | |
| pixelInfo); | |
| } | |
| } | |
| hnd.processEndSubPath(); | |
| } | |
| tCoords[0] = coords[0] + transXf; | |
| tCoords[1] = coords[1] + transYf; | |
| /* Checking SEG_MOVETO coordinates if they are out of the | |
| * [LOWER_BND, UPPER_BND] range. This check also handles | |
| * NaN and Infinity values. Skipping next path segment in | |
| * case of invalid data. | |
| */ | |
| if (tCoords[0] < UPPER_BND && | |
| tCoords[0] > LOWER_BND && | |
| tCoords[1] < UPPER_BND && | |
| tCoords[1] > LOWER_BND) | |
| { | |
| subpathStarted = true; | |
| skip = false; | |
| closeCoord[0] = tCoords[0]; | |
| closeCoord[1] = tCoords[1]; | |
| } else { | |
| skip = true; | |
| } | |
| pixelInfo[0] = 0; | |
| break; | |
| case PathIterator.SEG_LINETO: | |
| lastX = tCoords[2] = coords[0] + transXf; | |
| lastY = tCoords[3] = coords[1] + transYf; | |
| /* Checking SEG_LINETO coordinates if they are out of the | |
| * [LOWER_BND, UPPER_BND] range. This check also handles | |
| * NaN and Infinity values. Ignoring current path segment | |
| * in case of invalid data. If segment is skipped its | |
| * endpoint (if valid) is used to begin new subpath. | |
| */ | |
| if (lastX < UPPER_BND && | |
| lastX > LOWER_BND && | |
| lastY < UPPER_BND && | |
| lastY > LOWER_BND) | |
| { | |
| if (skip) { | |
| tCoords[0] = closeCoord[0] = lastX; | |
| tCoords[1] = closeCoord[1] = lastY; | |
| subpathStarted = true; | |
| skip = false; | |
| } else { | |
| ProcessLine(hnd, tCoords[0], tCoords[1], | |
| tCoords[2], tCoords[3], pixelInfo); | |
| tCoords[0] = lastX; | |
| tCoords[1] = lastY; | |
| } | |
| } | |
| break; | |
| case PathIterator.SEG_QUADTO: | |
| tCoords[2] = coords[0] + transXf; | |
| tCoords[3] = coords[1] + transYf; | |
| lastX = tCoords[4] = coords[2] + transXf; | |
| lastY = tCoords[5] = coords[3] + transYf; | |
| /* Checking SEG_QUADTO coordinates if they are out of the | |
| * [LOWER_BND, UPPER_BND] range. This check also handles | |
| * NaN and Infinity values. Ignoring current path segment | |
| * in case of invalid endpoints's data. Equivalent to | |
| * the SEG_LINETO if endpoint coordinates are valid but | |
| * there are invalid data among other coordinates | |
| */ | |
| if (lastX < UPPER_BND && | |
| lastX > LOWER_BND && | |
| lastY < UPPER_BND && | |
| lastY > LOWER_BND) | |
| { | |
| if (skip) { | |
| tCoords[0] = closeCoord[0] = lastX; | |
| tCoords[1] = closeCoord[1] = lastY; | |
| subpathStarted = true; | |
| skip = false; | |
| } else { | |
| if (tCoords[2] < UPPER_BND && | |
| tCoords[2] > LOWER_BND && | |
| tCoords[3] < UPPER_BND && | |
| tCoords[3] > LOWER_BND) | |
| { | |
| ProcessQuad(hnd, tCoords, pixelInfo); | |
| } else { | |
| ProcessLine(hnd, tCoords[0], tCoords[1], | |
| tCoords[4], tCoords[5], | |
| pixelInfo); | |
| } | |
| tCoords[0] = lastX; | |
| tCoords[1] = lastY; | |
| } | |
| } | |
| break; | |
| case PathIterator.SEG_CUBICTO: | |
| tCoords[2] = coords[0] + transXf; | |
| tCoords[3] = coords[1] + transYf; | |
| tCoords[4] = coords[2] + transXf; | |
| tCoords[5] = coords[3] + transYf; | |
| lastX = tCoords[6] = coords[4] + transXf; | |
| lastY = tCoords[7] = coords[5] + transYf; | |
| /* Checking SEG_CUBICTO coordinates if they are out of the | |
| * [LOWER_BND, UPPER_BND] range. This check also handles | |
| * NaN and Infinity values. Ignoring current path segment | |
| * in case of invalid endpoints's data. Equivalent to | |
| * the SEG_LINETO if endpoint coordinates are valid but | |
| * there are invalid data among other coordinates | |
| */ | |
| if (lastX < UPPER_BND && | |
| lastX > LOWER_BND && | |
| lastY < UPPER_BND && | |
| lastY > LOWER_BND) | |
| { | |
| if (skip) { | |
| tCoords[0] = closeCoord[0] = tCoords[6]; | |
| tCoords[1] = closeCoord[1] = tCoords[7]; | |
| subpathStarted = true; | |
| skip = false; | |
| } else { | |
| if (tCoords[2] < UPPER_BND && | |
| tCoords[2] > LOWER_BND && | |
| tCoords[3] < UPPER_BND && | |
| tCoords[3] > LOWER_BND && | |
| tCoords[4] < UPPER_BND && | |
| tCoords[4] > LOWER_BND && | |
| tCoords[5] < UPPER_BND && | |
| tCoords[5] > LOWER_BND) | |
| { | |
| ProcessCubic(hnd, tCoords, pixelInfo); | |
| } else { | |
| ProcessLine(hnd, tCoords[0], tCoords[1], | |
| tCoords[6], tCoords[7], | |
| pixelInfo); | |
| } | |
| tCoords[0] = lastX; | |
| tCoords[1] = lastY; | |
| } | |
| } | |
| break; | |
| case PathIterator.SEG_CLOSE: | |
| if (subpathStarted && !skip) { | |
| skip = false; | |
| if (tCoords[0] != closeCoord[0] || | |
| tCoords[1] != closeCoord[1]) | |
| { | |
| ProcessLine(hnd, tCoords[0], tCoords[1], | |
| closeCoord[0], closeCoord[1], | |
| pixelInfo); | |
| /* Storing last path's point for using in following | |
| * segments without initial moveTo | |
| */ | |
| tCoords[0] = closeCoord[0]; | |
| tCoords[1] = closeCoord[1]; | |
| } | |
| hnd.processEndSubPath(); | |
| } | |
| break; | |
| } | |
| pi.next(); | |
| } | |
| /* Performing closing of the unclosed segments */ | |
| if (subpathStarted & !skip) { | |
| if (hnd.clipMode == PH_MODE_FILL_CLIP) { | |
| if (tCoords[0] != closeCoord[0] || | |
| tCoords[1] != closeCoord[1]) | |
| { | |
| ProcessLine(hnd, tCoords[0], tCoords[1], | |
| closeCoord[0], closeCoord[1], | |
| pixelInfo); | |
| } | |
| } | |
| hnd.processEndSubPath(); | |
| } | |
| return true; | |
| } | |
| private static class Point { | |
| public int x; | |
| public int y; | |
| public boolean lastPoint; | |
| public Point prev; | |
| public Point next; | |
| public Point nextByY; | |
| public Edge edge; | |
| public Point(int x, int y, boolean lastPoint) { | |
| this.x = x; | |
| this.y = y; | |
| this.lastPoint = lastPoint; | |
| } | |
| } | |
| private static class Edge { | |
| int x; | |
| int dx; | |
| Point p; | |
| int dir; | |
| Edge prev; | |
| Edge next; | |
| public Edge(Point p, int x, int dx, int dir) { | |
| this.p = p; | |
| this.x = x; | |
| this.dx = dx; | |
| this.dir = dir; | |
| } | |
| } | |
| /* Size of the default buffer in the FillData structure. This buffer is | |
| * replaced with heap allocated in case of large paths. | |
| */ | |
| private static final int DF_MAX_POINT = 256; | |
| /* Following class accumulates points of the non-continuous flattened | |
| * general path during iteration through the origin path's segments . The | |
| * end of the each subpath is marked as lastPoint flag set at the last | |
| * point | |
| */ | |
| private static class FillData { | |
| List<Point> plgPnts; | |
| public int plgYMin; | |
| public int plgYMax; | |
| public FillData() { | |
| plgPnts = new Vector<Point>(DF_MAX_POINT); | |
| } | |
| public void addPoint(int x, int y, boolean lastPoint) { | |
| if (plgPnts.size() == 0) { | |
| plgYMin = plgYMax = y; | |
| } else { | |
| plgYMin = (plgYMin > y)?y:plgYMin; | |
| plgYMax = (plgYMax < y)?y:plgYMax; | |
| } | |
| plgPnts.add(new Point(x, y, lastPoint)); | |
| } | |
| public boolean isEmpty() { | |
| return plgPnts.size() == 0; | |
| } | |
| public boolean isEnded() { | |
| return plgPnts.get(plgPnts.size() - 1).lastPoint; | |
| } | |
| public boolean setEnded() { | |
| return plgPnts.get(plgPnts.size() - 1).lastPoint = true; | |
| } | |
| } | |
| private static class ActiveEdgeList { | |
| Edge head; | |
| public boolean isEmpty() { | |
| return (head == null); | |
| } | |
| public void insert(Point pnt, int cy) { | |
| Point np = pnt.next; | |
| int X1 = pnt.x, Y1 = pnt.y; | |
| int X2 = np.x, Y2 = np.y; | |
| Edge ne; | |
| if (Y1 == Y2) { | |
| /* Skipping horizontal segments */ | |
| return; | |
| } else { | |
| int dX = X2 - X1; | |
| int dY = Y2 - Y1; | |
| int stepx, x0, dy, dir; | |
| if (Y1 < Y2) { | |
| x0 = X1; | |
| dy = cy - Y1; | |
| dir = -1; | |
| } else { // (Y1 > Y2) | |
| x0 = X2; | |
| dy = cy - Y2; | |
| dir = 1; | |
| } | |
| /* We need to worry only about dX because dY is in denominator | |
| * and abs(dy) < MDP_MULT (cy is a first scanline of the scan | |
| * converted segment and we subtract y coordinate of the | |
| * nearest segment's end from it to obtain dy) | |
| */ | |
| if (dX > CALC_UBND || dX < CALC_LBND) { | |
| stepx = (int)((((double)dX)*MDP_MULT)/dY); | |
| x0 = x0 + (int)((((double)dX)*dy)/dY); | |
| } else { | |
| stepx = (dX<<MDP_PREC)/dY; | |
| x0 += (dX*dy)/dY; | |
| } | |
| ne = new Edge(pnt, x0, stepx, dir); | |
| } | |
| ne.next = head; | |
| ne.prev = null; | |
| if (head != null) { | |
| head.prev = ne; | |
| } | |
| head = pnt.edge = ne; | |
| } | |
| public void delete(Edge e) { | |
| Edge prevp = e.prev; | |
| Edge nextp = e.next; | |
| if (prevp != null) { | |
| prevp.next = nextp; | |
| } else { | |
| head = nextp; | |
| } | |
| if (nextp != null) { | |
| nextp.prev = prevp; | |
| } | |
| } | |
| /** | |
| * Bubble sorting in the ascending order of the linked list. This | |
| * implementation stops processing the list if there were no changes | |
| * during the previous pass. | |
| * | |
| * We could not use O(N) Radix sort here because in most cases list of | |
| * edges almost sorted. So, bubble sort (O(N^2)) is working much | |
| * better. Note, in case of array of edges Shell sort is more | |
| * efficient. | |
| */ | |
| public void sort() { | |
| Edge p, q, r, s = null, temp; | |
| boolean wasSwap = true; | |
| // r precedes p and s points to the node up to which | |
| // comparisons are to be made | |
| while (s != head.next && wasSwap) { | |
| r = p = head; | |
| q = p.next; | |
| wasSwap = false; | |
| while (p != s) { | |
| if (p.x >= q.x) { | |
| wasSwap = true; | |
| if (p == head) { | |
| temp = q.next; | |
| q.next = p; | |
| p.next = temp; | |
| head = q; | |
| r = q; | |
| } else { | |
| temp = q.next; | |
| q.next = p; | |
| p.next = temp; | |
| r.next = q; | |
| r = q; | |
| } | |
| } else { | |
| r = p; | |
| p = p.next; | |
| } | |
| q = p.next; | |
| if (q == s) s = p; | |
| } | |
| } | |
| // correction of the back links in the double linked edge list | |
| p = head; | |
| q = null; | |
| while (p != null) { | |
| p.prev = q; | |
| q = p; | |
| p = p.next; | |
| } | |
| } | |
| } | |
| private static void FillPolygon(FillProcessHandler hnd, | |
| int fillRule) { | |
| int k, y, n; | |
| boolean drawing; | |
| Edge active; | |
| int rightBnd = hnd.dhnd.xMax - 1; | |
| FillData fd = hnd.fd; | |
| int yMin = fd.plgYMin; | |
| int yMax = fd.plgYMax; | |
| int hashSize = ((yMax - yMin)>>MDP_PREC) + 4; | |
| /* Because of support of the KEY_STROKE_CONTROL hint we are performing | |
| * shift of the coordinates at the higher level | |
| */ | |
| int hashOffset = ((yMin - 1) & MDP_W_MASK); | |
| /* Winding counter */ | |
| int counter; | |
| /* Calculating mask to be applied to the winding counter */ | |
| int counterMask = | |
| (fillRule == PathIterator.WIND_NON_ZERO)? -1:1; | |
| int pntOffset; | |
| List<Point> pnts = fd.plgPnts; | |
| n = pnts.size(); | |
| if (n <=1) return; | |
| Point[] yHash = new Point[hashSize]; | |
| /* Creating double linked list (prev, next links) describing path order | |
| * and hash table with points which fall between scanlines. nextByY | |
| * link is used for the points which are between same scanlines. | |
| * Scanlines are passed through the centers of the pixels. | |
| */ | |
| Point curpt = pnts.get(0); | |
| curpt.prev = null; | |
| for (int i = 0; i < n - 1; i++) { | |
| curpt = pnts.get(i); | |
| Point nextpt = pnts.get(i + 1); | |
| int curHashInd = (curpt.y - hashOffset - 1) >> MDP_PREC; | |
| curpt.nextByY = yHash[curHashInd]; | |
| yHash[curHashInd] = curpt; | |
| curpt.next = nextpt; | |
| nextpt.prev = curpt; | |
| } | |
| Point ept = pnts.get(n - 1); | |
| int curHashInd = (ept.y - hashOffset - 1) >> MDP_PREC; | |
| ept.nextByY = yHash[curHashInd]; | |
| yHash[curHashInd] = ept; | |
| ActiveEdgeList activeList = new ActiveEdgeList(); | |
| for (y=hashOffset + MDP_MULT,k = 0; | |
| y<=yMax && k < hashSize; y += MDP_MULT, k++) | |
| { | |
| for(Point pt = yHash[k];pt != null; pt=pt.nextByY) { | |
| /* pt.y should be inside hashed interval | |
| * assert(y-MDP_MULT <= pt.y && pt.y < y); | |
| */ | |
| if (pt.prev != null && !pt.prev.lastPoint) { | |
| if (pt.prev.edge != null && pt.prev.y <= y) { | |
| activeList.delete(pt.prev.edge); | |
| pt.prev.edge = null; | |
| } else if (pt.prev.y > y) { | |
| activeList.insert(pt.prev, y); | |
| } | |
| } | |
| if (!pt.lastPoint && pt.next != null) { | |
| if (pt.edge != null && pt.next.y <= y) { | |
| activeList.delete(pt.edge); | |
| pt.edge = null; | |
| } else if (pt.next.y > y) { | |
| activeList.insert(pt, y); | |
| } | |
| } | |
| } | |
| if (activeList.isEmpty()) continue; | |
| activeList.sort(); | |
| counter = 0; | |
| drawing = false; | |
| int xl, xr; | |
| xl = xr = hnd.dhnd.xMin; | |
| Edge curEdge = activeList.head; | |
| while (curEdge != null) { | |
| counter += curEdge.dir; | |
| if ((counter & counterMask) != 0 && !drawing) { | |
| xl = (curEdge.x + MDP_MULT - 1)>>MDP_PREC; | |
| drawing = true; | |
| } | |
| if ((counter & counterMask) == 0 && drawing) { | |
| xr = (curEdge.x - 1) >> MDP_PREC; | |
| if (xl <= xr) { | |
| hnd.dhnd.drawScanline(xl, xr, y >> MDP_PREC); | |
| } | |
| drawing = false; | |
| } | |
| curEdge.x += curEdge.dx; | |
| curEdge = curEdge.next; | |
| } | |
| /* Performing drawing till the right boundary (for correct | |
| * rendering shapes clipped at the right side) | |
| */ | |
| if (drawing && xl <= rightBnd) { | |
| /* Support of the strokeHint was added into the | |
| * draw and fill methods of the sun.java2d.pipe.LoopPipe | |
| */ | |
| hnd.dhnd.drawScanline(xl, rightBnd, y >> MDP_PREC); | |
| } | |
| } | |
| } | |
| private static class FillProcessHandler extends ProcessHandler { | |
| FillData fd; | |
| /* Note: For more easy reading of the code below each java version of | |
| * the macros from the ProcessPath.c preceded by the commented | |
| * origin call containing verbose names of the parameters | |
| */ | |
| public void processFixedLine(int x1, int y1, int x2, int y2, | |
| int[] pixelInfo, boolean checkBounds, | |
| boolean endSubPath) | |
| { | |
| int outXMin, outXMax, outYMin, outYMax; | |
| int res; | |
| /* There is no need to round line coordinates to the forward | |
| * differencing precision anymore. Such a rounding was used for | |
| * preventing the curve go out the endpoint (this sometimes does | |
| * not help). The problem was fixed in the forward differencing | |
| * loops. | |
| */ | |
| if (checkBounds) { | |
| boolean lastClipped; | |
| /* This function is used only for filling shapes, so there is no | |
| * check for the type of clipping | |
| */ | |
| int[] c = new int[]{x1, y1, x2, y2, 0, 0}; | |
| outXMin = (int)(dhnd.xMinf * MDP_MULT); | |
| outXMax = (int)(dhnd.xMaxf * MDP_MULT); | |
| outYMin = (int)(dhnd.yMinf * MDP_MULT); | |
| outYMax = (int)(dhnd.yMaxf * MDP_MULT); | |
| /* | |
| * TESTANDCLIP(outYMin, outYMax, y1, x1, y2, x2, res); | |
| */ | |
| res = TESTANDCLIP(outYMin, outYMax, c, 1, 0, 3, 2); | |
| if (res == CRES_INVISIBLE) return; | |
| /* | |
| * TESTANDCLIP(outYMin, outYMax, y2, x2, y1, x1, res); | |
| */ | |
| res = TESTANDCLIP(outYMin, outYMax, c, 3, 2, 1, 0); | |
| if (res == CRES_INVISIBLE) return; | |
| lastClipped = IS_CLIPPED(res); | |
| /* Clamping starting from first vertex of the processed | |
| * segment | |
| * | |
| * CLIPCLAMP(outXMin, outXMax, x1, y1, x2, y2, x3, y3, res); | |
| */ | |
| res = CLIPCLAMP(outXMin, outXMax, c, 0, 1, 2, 3, 4, 5); | |
| /* Clamping only by left boundary */ | |
| if (res == CRES_MIN_CLIPPED) { | |
| processFixedLine(c[4], c[5], c[0], c[1], pixelInfo, | |
| false, lastClipped); | |
| } else if (res == CRES_INVISIBLE) { | |
| return; | |
| } | |
| /* Clamping starting from last vertex of the processed | |
| * segment | |
| * | |
| * CLIPCLAMP(outXMin, outXMax, x2, y2, x1, y1, x3, y3, res); | |
| */ | |
| res = CLIPCLAMP(outXMin, outXMax, c, 2, 3, 0, 1, 4, 5); | |
| /* Checking if there was a clip by right boundary */ | |
| lastClipped = lastClipped || (res == CRES_MAX_CLIPPED); | |
| processFixedLine(c[0], c[1], c[2], c[3], pixelInfo, | |
| false, lastClipped); | |
| /* Clamping only by left boundary */ | |
| if (res == CRES_MIN_CLIPPED) { | |
| processFixedLine(c[2], c[3], c[4], c[5], pixelInfo, | |
| false, lastClipped); | |
| } | |
| return; | |
| } | |
| /* Adding first point of the line only in case of empty or just | |
| * finished path | |
| */ | |
| if (fd.isEmpty() || fd.isEnded()) { | |
| fd.addPoint(x1, y1, false); | |
| } | |
| fd.addPoint(x2, y2, false); | |
| if (endSubPath) { | |
| fd.setEnded(); | |
| } | |
| } | |
| FillProcessHandler(DrawHandler dhnd) { | |
| super(dhnd, PH_MODE_FILL_CLIP); | |
| this.fd = new FillData(); | |
| } | |
| public void processEndSubPath() { | |
| if (!fd.isEmpty()) { | |
| fd.setEnded(); | |
| } | |
| } | |
| } | |
| } |