Path: blob/master/src/java.desktop/share/classes/sun/java2d/loops/ProcessPath.java
41159 views
/*1* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.java2d.loops;2627import java.awt.geom.Path2D;28import java.awt.geom.PathIterator;29import java.awt.geom.QuadCurve2D;30import sun.awt.SunHints;31import java.util.*;3233/* This is the java implementation of the native code from34* src/share/native/sun/java2d/loops/ProcessPath.[c,h]35* This code is written to be as much similar to the native36* as it possible. So, it sometimes does not follow java naming conventions.37*38* It's important to keep this code synchronized with native one. See more39* comments, description and high level scheme of the rendering process in the40* ProcessPath.c41*/4243public class ProcessPath {4445/* Public interfaces and methods for drawing and filling general paths */4647public abstract static class DrawHandler {48public int xMin;49public int yMin;50public int xMax;51public int yMax;52public float xMinf;53public float yMinf;54public float xMaxf;55public float yMaxf;5657public int strokeControl;5859public DrawHandler(int xMin, int yMin, int xMax, int yMax,60int strokeControl)61{62setBounds(xMin, yMin, xMax, yMax, strokeControl);63}6465public void setBounds(int xMin, int yMin, int xMax, int yMax)66{67this.xMin = xMin;68this.yMin = yMin;69this.xMax = xMax;70this.yMax = yMax;7172/* Setting up fractional clipping box73*74* We are using following float -> int mapping:75*76* xi = floor(xf + 0.5)77*78* So, fractional values that hit the [xmin, xmax) integer interval79* will be situated inside the [xmin-0.5, xmax - 0.5) fractional80* interval. We are using EPSF constant to provide that upper81* boundary is not included.82*/83xMinf = xMin - 0.5f;84yMinf = yMin - 0.5f;85xMaxf = xMax - 0.5f - EPSF;86yMaxf = yMax - 0.5f - EPSF;87}8889public void setBounds(int xMin, int yMin, int xMax, int yMax,90int strokeControl)91{92this.strokeControl = strokeControl;93setBounds(xMin, yMin, xMax, yMax);94}9596public void adjustBounds(int bxMin, int byMin, int bxMax, int byMax)97{98if (xMin > bxMin) bxMin = xMin;99if (xMax < bxMax) bxMax = xMax;100if (yMin > byMin) byMin = yMin;101if (yMax < byMax) byMax = yMax;102setBounds(bxMin, byMin, bxMax, byMax);103}104105public DrawHandler(int xMin, int yMin, int xMax, int yMax) {106this(xMin, yMin, xMax, yMax, SunHints.INTVAL_STROKE_DEFAULT);107}108109public abstract void drawLine(int x0, int y0, int x1, int y1);110111public abstract void drawPixel(int x0, int y0);112113public abstract void drawScanline(int x0, int x1, int y0);114}115116public interface EndSubPathHandler {117public void processEndSubPath();118}119120public static final int PH_MODE_DRAW_CLIP = 0;121public static final int PH_MODE_FILL_CLIP = 1;122123public abstract static class ProcessHandler implements EndSubPathHandler {124DrawHandler dhnd;125int clipMode;126127public ProcessHandler(DrawHandler dhnd,128int clipMode) {129this.dhnd = dhnd;130this.clipMode = clipMode;131}132133public abstract void processFixedLine(int x1, int y1,134int x2, int y2, int [] pixelInfo,135boolean checkBounds,136boolean endSubPath);137}138139public static EndSubPathHandler noopEndSubPathHandler =140new EndSubPathHandler() {141public void processEndSubPath() { }142};143144public static boolean fillPath(DrawHandler dhnd, Path2D.Float p2df,145int transX, int transY)146{147FillProcessHandler fhnd = new FillProcessHandler(dhnd);148if (!doProcessPath(fhnd, p2df, transX, transY)) {149return false;150}151FillPolygon(fhnd, p2df.getWindingRule());152return true;153}154155public static boolean drawPath(DrawHandler dhnd,156EndSubPathHandler endSubPath,157Path2D.Float p2df,158int transX, int transY)159{160return doProcessPath(new DrawProcessHandler(dhnd, endSubPath),161p2df, transX, transY);162}163164public static boolean drawPath(DrawHandler dhnd,165Path2D.Float p2df,166int transX, int transY)167{168return doProcessPath(new DrawProcessHandler(dhnd,169noopEndSubPathHandler),170p2df, transX, transY);171}172173/* Private implementation of the rendering process */174175/* Boundaries used for skipping huge path segments */176private static final float UPPER_BND = Float.MAX_VALUE/4.0f;177private static final float LOWER_BND = -UPPER_BND;178179/* Precision (in bits) used in forward differencing */180private static final int FWD_PREC = 7;181182/* Precision (in bits) used for the rounding in the midpoint test */183private static final int MDP_PREC = 10;184185private static final int MDP_MULT = 1 << MDP_PREC;186private static final int MDP_HALF_MULT = MDP_MULT >> 1;187188/* Boundaries used for clipping large path segments (those are inside189* [UPPER/LOWER]_BND boundaries)190*/191private static final int UPPER_OUT_BND = 1 << (30 - MDP_PREC);192private static final int LOWER_OUT_BND = -UPPER_OUT_BND;193194195/* Calculation boundaries. They are used for switching to the more slow but196* allowing larger input values method of calculation of the initial values197* of the scan converted line segments inside the FillPolygon198*/199private static final float CALC_UBND = 1 << (30 - MDP_PREC);200private static final float CALC_LBND = -CALC_UBND;201202203/* Following constants are used for providing open boundaries of the204* intervals205*/206public static final int EPSFX = 1;207public static final float EPSF = ((float)EPSFX)/MDP_MULT;208209/* Bit mask used to separate whole part from the fraction part of the210* number211*/212private static final int MDP_W_MASK = -MDP_MULT;213214/* Bit mask used to separate fractional part from the whole part of the215* number216*/217private static final int MDP_F_MASK = MDP_MULT - 1;218219/*220* Constants for the forward differencing221* of the cubic and quad curves222*/223224/* Maximum size of the cubic curve (calculated as the size of the bounding225* box of the control points) which could be rendered without splitting226*/227private static final int MAX_CUB_SIZE = 256;228229/* Maximum size of the quad curve (calculated as the size of the bounding230* box of the control points) which could be rendered without splitting231*/232private static final int MAX_QUAD_SIZE = 1024;233234/* Default power of 2 steps used in the forward differencing. Here DF prefix235* stands for DeFault. Constants below are used as initial values for the236* adaptive forward differencing algorithm.237*/238private static final int DF_CUB_STEPS = 3;239private static final int DF_QUAD_STEPS = 2;240241/* Shift of the current point of the curve for preparing to the midpoint242* rounding243*/244private static final int DF_CUB_SHIFT = FWD_PREC + DF_CUB_STEPS*3 -245MDP_PREC;246private static final int DF_QUAD_SHIFT = FWD_PREC + DF_QUAD_STEPS*2 -247MDP_PREC;248249/* Default amount of steps of the forward differencing */250private static final int DF_CUB_COUNT = (1<<DF_CUB_STEPS);251private static final int DF_QUAD_COUNT = (1<<DF_QUAD_STEPS);252253/* Default boundary constants used to check the necessity of the restepping254*/255private static final int DF_CUB_DEC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC + 2;256private static final int DF_CUB_INC_BND = 1<<DF_CUB_STEPS*3 + FWD_PREC - 1;257private static final int DF_QUAD_DEC_BND = 1<<DF_QUAD_STEPS*2 +258FWD_PREC + 2;259private static final int DF_QUAD_INC_BND = 1<<DF_QUAD_STEPS*2 +260FWD_PREC - 1;261262/* Multipliers for the coefficients of the polynomial form of the cubic and263* quad curves representation264*/265private static final int CUB_A_SHIFT = FWD_PREC;266private static final int CUB_B_SHIFT = (DF_CUB_STEPS + FWD_PREC + 1);267private static final int CUB_C_SHIFT = (DF_CUB_STEPS*2 + FWD_PREC);268269private static final int CUB_A_MDP_MULT = (1<<CUB_A_SHIFT);270private static final int CUB_B_MDP_MULT = (1<<CUB_B_SHIFT);271private static final int CUB_C_MDP_MULT = (1<<CUB_C_SHIFT);272273private static final int QUAD_A_SHIFT = FWD_PREC;274private static final int QUAD_B_SHIFT = (DF_QUAD_STEPS + FWD_PREC);275276private static final int QUAD_A_MDP_MULT = (1<<QUAD_A_SHIFT);277private static final int QUAD_B_MDP_MULT = (1<<QUAD_B_SHIFT);278279/* Clipping macros for drawing and filling algorithms */280private static float CLIP(float a1, float b1, float a2, float b2,281double t) {282return (float)(b1 + (t - a1)*(b2 - b1) / (a2 - a1));283}284285private static int CLIP(int a1, int b1, int a2, int b2, double t) {286return (int)(b1 + (t - a1)*(b2 - b1) / (a2 - a1));287}288289290private static final int CRES_MIN_CLIPPED = 0;291private static final int CRES_MAX_CLIPPED = 1;292private static final int CRES_NOT_CLIPPED = 3;293private static final int CRES_INVISIBLE = 4;294295private static boolean IS_CLIPPED(int res) {296return res == CRES_MIN_CLIPPED || res == CRES_MAX_CLIPPED;297}298299/* This is java implementation of the macro from ProcessGeneralPath.c.300* To keep the logic of the java code similar to the native one301* array and set of indexes are used to point out the data.302*/303private static int TESTANDCLIP(float LINE_MIN, float LINE_MAX, float[] c,304int a1, int b1, int a2, int b2) {305double t;306int res = CRES_NOT_CLIPPED;307if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) {308if (c[a1] < (LINE_MIN)) {309if (c[a2] < (LINE_MIN)) {310return CRES_INVISIBLE;311};312res = CRES_MIN_CLIPPED;313t = (LINE_MIN);314} else {315if (c[a2] > (LINE_MAX)) {316return CRES_INVISIBLE;317};318res = CRES_MAX_CLIPPED;319t = (LINE_MAX);320}321c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t);322c[a1] = (float)t;323}324return res;325}326327/* Integer version of the method above */328private static int TESTANDCLIP(int LINE_MIN, int LINE_MAX, int[] c,329int a1, int b1, int a2, int b2) {330double t;331int res = CRES_NOT_CLIPPED;332if (c[a1] < (LINE_MIN) || c[a1] > (LINE_MAX)) {333if (c[a1] < (LINE_MIN)) {334if (c[a2] < (LINE_MIN)) {335return CRES_INVISIBLE;336};337res = CRES_MIN_CLIPPED;338t = (LINE_MIN);339} else {340if (c[a2] > (LINE_MAX)) {341return CRES_INVISIBLE;342};343res = CRES_MAX_CLIPPED;344t = (LINE_MAX);345}346c[b1] = CLIP(c[a1], c[b1], c[a2], c[b2], t);347c[a1] = (int)t;348}349return res;350}351352353354/* Following method is used for clipping and clumping filled shapes.355* An example of this process is shown on the picture below:356* ----+ ----+357* |/ | |/ |358* + | + |359* /| | I |360* / | | I |361* | | | ===> I |362* \ | | I |363* \| | I |364* + | + |365* |\ | |\ |366* | ----+ | ----+367* boundary boundary368*369* We can only perform clipping in case of right side of the output area370* because all segments passed out the right boundary don't influence on the371* result of scan conversion algorithm (it correctly handles half open372* contours).373*374* This is java implementation of the macro from ProcessGeneralPath.c.375* To keep the logic of the java code similar to the native one376* array and set of indexes are used to point out the data.377*378*/379private static int CLIPCLAMP(float LINE_MIN, float LINE_MAX, float[] c,380int a1, int b1, int a2, int b2,381int a3, int b3) {382c[a3] = c[a1];383c[b3] = c[b1];384int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2);385if (res == CRES_MIN_CLIPPED) {386c[a3] = c[a1];387} else if (res == CRES_MAX_CLIPPED) {388c[a3] = c[a1];389res = CRES_MAX_CLIPPED;390} else if (res == CRES_INVISIBLE) {391if (c[a1] > LINE_MAX) {392res = CRES_INVISIBLE;393} else {394c[a1] = LINE_MIN;395c[a2] = LINE_MIN;396res = CRES_NOT_CLIPPED;397}398}399return res;400}401402/* Integer version of the method above */403private static int CLIPCLAMP(int LINE_MIN, int LINE_MAX, int[] c,404int a1, int b1, int a2, int b2,405int a3, int b3) {406c[a3] = c[a1];407c[b3] = c[b1];408int res = TESTANDCLIP(LINE_MIN, LINE_MAX, c, a1, b1, a2, b2);409if (res == CRES_MIN_CLIPPED) {410c[a3] = c[a1];411} else if (res == CRES_MAX_CLIPPED) {412c[a3] = c[a1];413res = CRES_MAX_CLIPPED;414} else if (res == CRES_INVISIBLE) {415if (c[a1] > LINE_MAX) {416res = CRES_INVISIBLE;417} else {418c[a1] = LINE_MIN;419c[a2] = LINE_MIN;420res = CRES_NOT_CLIPPED;421}422}423return res;424}425426private static class DrawProcessHandler extends ProcessHandler {427428EndSubPathHandler processESP;429430public DrawProcessHandler(DrawHandler dhnd,431EndSubPathHandler processESP) {432super(dhnd, PH_MODE_DRAW_CLIP);433this.dhnd = dhnd;434this.processESP = processESP;435}436437public void processEndSubPath() {438processESP.processEndSubPath();439}440441void PROCESS_LINE(int fX0, int fY0, int fX1, int fY1,442boolean checkBounds, int[] pixelInfo) {443int X0 = fX0 >> MDP_PREC;444int Y0 = fY0 >> MDP_PREC;445int X1 = fX1 >> MDP_PREC;446int Y1 = fY1 >> MDP_PREC;447448/* Handling lines having just one pixel */449if (((X0^X1) | (Y0^Y1)) == 0) {450if (checkBounds &&451(dhnd.yMin > Y0 ||452dhnd.yMax <= Y0 ||453dhnd.xMin > X0 ||454dhnd.xMax <= X0)) return;455456if (pixelInfo[0] == 0) {457pixelInfo[0] = 1;458pixelInfo[1] = X0;459pixelInfo[2] = Y0;460pixelInfo[3] = X0;461pixelInfo[4] = Y0;462dhnd.drawPixel(X0, Y0);463} else if ((X0 != pixelInfo[3] || Y0 != pixelInfo[4]) &&464(X0 != pixelInfo[1] || Y0 != pixelInfo[2])) {465dhnd.drawPixel(X0, Y0);466pixelInfo[3] = X0;467pixelInfo[4] = Y0;468}469return;470}471472if (!checkBounds ||473(dhnd.yMin <= Y0 &&474dhnd.yMax > Y0 &&475dhnd.xMin <= X0 &&476dhnd.xMax > X0))477{478/* Switch off first pixel of the line before drawing */479if (pixelInfo[0] == 1 &&480((pixelInfo[1] == X0 && pixelInfo[2] == Y0) ||481(pixelInfo[3] == X0 && pixelInfo[4] == Y0)))482{483dhnd.drawPixel(X0, Y0);484}485}486487dhnd.drawLine(X0, Y0, X1, Y1);488489if (pixelInfo[0] == 0) {490pixelInfo[0] = 1;491pixelInfo[1] = X0;492pixelInfo[2] = Y0;493pixelInfo[3] = X0;494pixelInfo[4] = Y0;495}496497/* Switch on last pixel of the line if it was already498* drawn during rendering of the previous segments499*/500if ((pixelInfo[1] == X1 && pixelInfo[2] == Y1) ||501(pixelInfo[3] == X1 && pixelInfo[4] == Y1))502{503if (checkBounds &&504(dhnd.yMin > Y1 ||505dhnd.yMax <= Y1 ||506dhnd.xMin > X1 ||507dhnd.xMax <= X1)) {508return;509}510511dhnd.drawPixel(X1, Y1);512}513pixelInfo[3] = X1;514pixelInfo[4] = Y1;515}516517void PROCESS_POINT(int fX, int fY, boolean checkBounds,518int[] pixelInfo) {519int _X = fX>> MDP_PREC;520int _Y = fY>> MDP_PREC;521if (checkBounds &&522(dhnd.yMin > _Y ||523dhnd.yMax <= _Y ||524dhnd.xMin > _X ||525dhnd.xMax <= _X)) return;526/*527* (_X,_Y) should be inside boundaries528*529* assert(dhnd.yMin <= _Y &&530* dhnd.yMax > _Y &&531* dhnd.xMin <= _X &&532* dhnd.xMax > _X);533*534*/535if (pixelInfo[0] == 0) {536pixelInfo[0] = 1;537pixelInfo[1] = _X;538pixelInfo[2] = _Y;539pixelInfo[3] = _X;540pixelInfo[4] = _Y;541dhnd.drawPixel(_X, _Y);542} else if ((_X != pixelInfo[3] || _Y != pixelInfo[4]) &&543(_X != pixelInfo[1] || _Y != pixelInfo[2])) {544dhnd.drawPixel(_X, _Y);545pixelInfo[3] = _X;546pixelInfo[4] = _Y;547}548}549550/* Drawing line with subpixel endpoints551*552* (x1, y1), (x2, y2) - fixed point coordinates of the endpoints553* with MDP_PREC bits for the fractional part554*555* pixelInfo - structure which keeps drawing info for avoiding556* multiple drawing at the same position on the557* screen (required for the XOR mode of drawing)558*559* pixelInfo[0] - state of the drawing560* 0 - no pixel drawn between561* moveTo/close of the path562* 1 - there are drawn pixels563*564* pixelInfo[1,2] - first pixel of the path565* between moveTo/close of the566* path567*568* pixelInfo[3,4] - last drawn pixel between569* moveTo/close of the path570*571* checkBounds - flag showing necessity of checking the clip572*573*/574public void processFixedLine(int x1, int y1, int x2, int y2,575int[] pixelInfo, boolean checkBounds,576boolean endSubPath) {577578/* Checking if line is inside a (X,Y),(X+MDP_MULT,Y+MDP_MULT) box */579int c = ((x1 ^ x2) | (y1 ^ y2));580int rx1, ry1, rx2, ry2;581if ((c & MDP_W_MASK) == 0) {582/* Checking for the segments with integer coordinates having583* the same start and end points584*/585if (c == 0) {586PROCESS_POINT(x1 + MDP_HALF_MULT, y1 + MDP_HALF_MULT,587checkBounds, pixelInfo);588}589return;590}591592if (x1 == x2 || y1 == y2) {593rx1 = x1 + MDP_HALF_MULT;594rx2 = x2 + MDP_HALF_MULT;595ry1 = y1 + MDP_HALF_MULT;596ry2 = y2 + MDP_HALF_MULT;597} else {598/* Neither dx nor dy can be zero because of the check above */599int dx = x2 - x1;600int dy = y2 - y1;601602/* Floor of x1, y1, x2, y2 */603int fx1 = x1 & MDP_W_MASK;604int fy1 = y1 & MDP_W_MASK;605int fx2 = x2 & MDP_W_MASK;606int fy2 = y2 & MDP_W_MASK;607608/* Processing first endpoint */609if (fx1 == x1 || fy1 == y1) {610/* Adding MDP_HALF_MULT to the [xy]1 if f[xy]1 == [xy]1611* will not affect the result612*/613rx1 = x1 + MDP_HALF_MULT;614ry1 = y1 + MDP_HALF_MULT;615} else {616/* Boundary at the direction from (x1,y1) to (x2,y2) */617int bx1 = (x1 < x2) ? fx1 + MDP_MULT : fx1;618int by1 = (y1 < y2) ? fy1 + MDP_MULT : fy1;619620/* intersection with column bx1 */621int cross = y1 + ((bx1 - x1)*dy)/dx;622if (cross >= fy1 && cross <= fy1 + MDP_MULT) {623rx1 = bx1;624ry1 = cross + MDP_HALF_MULT;625} else {626/* intersection with row by1 */627cross = x1 + ((by1 - y1)*dx)/dy;628rx1 = cross + MDP_HALF_MULT;629ry1 = by1;630}631}632633/* Processing second endpoint */634if (fx2 == x2 || fy2 == y2) {635/* Adding MDP_HALF_MULT to the [xy]2 if f[xy]2 == [xy]2636* will not affect the result637*/638rx2 = x2 + MDP_HALF_MULT;639ry2 = y2 + MDP_HALF_MULT;640} else {641/* Boundary at the direction from (x2,y2) to (x1,y1) */642int bx2 = (x1 > x2) ? fx2 + MDP_MULT : fx2;643int by2 = (y1 > y2) ? fy2 + MDP_MULT : fy2;644645/* intersection with column bx2 */646int cross = y2 + ((bx2 - x2)*dy)/dx;647if (cross >= fy2 && cross <= fy2 + MDP_MULT) {648rx2 = bx2;649ry2 = cross + MDP_HALF_MULT;650} else {651/* intersection with row by2 */652cross = x2 + ((by2 - y2)*dx)/dy;653rx2 = cross + MDP_HALF_MULT;654ry2 = by2;655}656}657}658PROCESS_LINE(rx1, ry1, rx2, ry2, checkBounds, pixelInfo);659}660}661662/* Performing drawing of the monotonic in X and Y quadratic curves with663* sizes less than MAX_QUAD_SIZE by using forward differencing method of664* calculation. See comments to the DrawMonotonicQuad in the665* ProcessGeneralPath.c666*/667private static void DrawMonotonicQuad(ProcessHandler hnd,668float[] coords,669boolean checkBounds,670int[] pixelInfo) {671672int x0 = (int)(coords[0]*MDP_MULT);673int y0 = (int)(coords[1]*MDP_MULT);674675int xe = (int)(coords[4]*MDP_MULT);676int ye = (int)(coords[5]*MDP_MULT);677678/* Extracting fractional part of coordinates of first control point */679int px = (x0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;680int py = (y0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;681682/* Setting default amount of steps */683int count = DF_QUAD_COUNT;684685/* Setting default shift for preparing to the midpoint rounding */686int shift = DF_QUAD_SHIFT;687688int ax = (int)((coords[0] - 2*coords[2] +689coords[4])*QUAD_A_MDP_MULT);690int ay = (int)((coords[1] - 2*coords[3] +691coords[5])*QUAD_A_MDP_MULT);692693int bx = (int)((-2*coords[0] + 2*coords[2])*QUAD_B_MDP_MULT);694int by = (int)((-2*coords[1] + 2*coords[3])*QUAD_B_MDP_MULT);695696int ddpx = 2*ax;697int ddpy = 2*ay;698699int dpx = ax + bx;700int dpy = ay + by;701702int x1, y1;703704int x2 = x0;705int y2 = y0;706707int maxDD = Math.max(Math.abs(ddpx),Math.abs(ddpy));708709int dx = xe - x0;710int dy = ye - y0;711712int x0w = x0 & MDP_W_MASK;713int y0w = y0 & MDP_W_MASK;714715/* Perform decreasing step in 2 times if slope of the first forward716* difference changes too quickly (more than a pixel per step in X or Y717* direction). We can perform adjusting of the step size before the718* rendering loop because the curvature of the quad curve remains the719* same along all the curve720*/721while (maxDD > DF_QUAD_DEC_BND) {722dpx = (dpx<<1) - ax;723dpy = (dpy<<1) - ay;724count <<= 1;725maxDD >>= 2;726px <<=2;727py <<=2;728shift += 2;729}730731while(count-- > 1) {732px += dpx;733py += dpy;734735dpx += ddpx;736dpy += ddpy;737738x1 = x2;739y1 = y2;740741x2 = x0w + (px >> shift);742y2 = y0w + (py >> shift);743744/* Checking that we are not running out of the endpoint and bounding745* violating coordinate. The check is pretty simple because the746* curve passed to the DrawCubic already split into the747* monotonic in X and Y pieces748*/749750/* Bounding x2 by xe */751if (((xe-x2)^dx) < 0) {752x2 = xe;753}754755/* Bounding y2 by ye */756if (((ye-y2)^dy) < 0) {757y2 = ye;758}759760hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds, false);761}762763/* We are performing one step less than necessary and use actual764* (xe,ye) endpoint of the curve instead of calculated. This prevent us765* from running above the curve endpoint due to the accumulated errors766* during the iterations.767*/768769hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds, false);770}771772/*773* Checking size of the quad curves and split them if necessary.774* Calling DrawMonotonicQuad for the curves of the appropriate size.775* Note: coords array could be changed776*/777private static void ProcessMonotonicQuad(ProcessHandler hnd,778float[] coords,779int[] pixelInfo) {780781float[] coords1 = new float[6];782float tx, ty;783float xMin, yMin, xMax, yMax;784785xMin = xMax = coords[0];786yMin = yMax = coords[1];787for (int i = 2; i < 6; i += 2) {788xMin = (xMin > coords[i])? coords[i] : xMin;789xMax = (xMax < coords[i])? coords[i] : xMax;790yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin;791yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax;792}793794if (hnd.clipMode == PH_MODE_DRAW_CLIP) {795796/* In case of drawing we could just skip curves which are797* completely out of bounds798*/799if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax ||800hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) {801return;802}803} else {804805/* In case of filling we could skip curves which are above,806* below and behind the right boundary of the visible area807*/808809if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax ||810hnd.dhnd.xMaxf < xMin)811{812return;813}814815/* We could clamp x coordinates to the corresponding boundary816* if the curve is completely behind the left one817*/818819if (hnd.dhnd.xMinf > xMax) {820coords[0] = coords[2] = coords[4] = hnd.dhnd.xMinf;821}822}823824if (xMax - xMin > MAX_QUAD_SIZE || yMax - yMin > MAX_QUAD_SIZE) {825coords1[4] = coords[4];826coords1[5] = coords[5];827coords1[2] = (coords[2] + coords[4])/2.0f;828coords1[3] = (coords[3] + coords[5])/2.0f;829coords[2] = (coords[0] + coords[2])/2.0f;830coords[3] = (coords[1] + coords[3])/2.0f;831coords[4] = coords1[0] = (coords[2] + coords1[2])/2.0f;832coords[5] = coords1[1] = (coords[3] + coords1[3])/2.0f;833834ProcessMonotonicQuad(hnd, coords, pixelInfo);835836ProcessMonotonicQuad(hnd, coords1, pixelInfo);837} else {838DrawMonotonicQuad(hnd, coords,839/* Set checkBounds parameter if curve intersects840* boundary of the visible area. We know that the841* curve is visible, so the check is pretty842* simple843*/844hnd.dhnd.xMinf >= xMin ||845hnd.dhnd.xMaxf <= xMax ||846hnd.dhnd.yMinf >= yMin ||847hnd.dhnd.yMaxf <= yMax,848pixelInfo);849}850}851852/*853* Split quadratic curve into monotonic in X and Y parts. Calling854* ProcessMonotonicQuad for each monotonic piece of the curve.855* Note: coords array could be changed856*/857private static void ProcessQuad(ProcessHandler hnd, float[] coords,858int[] pixelInfo) {859/* Temporary array for holding parameters corresponding to the extreme860* in X and Y points861*/862double[] params = new double[2];863int cnt = 0;864double param;865866/* Simple check for monotonicity in X before searching for the extreme867* points of the X(t) function. We first check if the curve is868* monotonic in X by seeing if all of the X coordinates are strongly869* ordered.870*/871if ((coords[0] > coords[2] || coords[2] > coords[4]) &&872(coords[0] < coords[2] || coords[2] < coords[4]))873{874/* Searching for extreme points of the X(t) function by solving875* dX(t)876* ---- = 0 equation877* dt878*/879double ax = coords[0] - 2*coords[2] + coords[4];880if (ax != 0) {881/* Calculating root of the following equation882* ax*t + bx = 0883*/884double bx = coords[0] - coords[2];885886param = bx/ax;887if (param < 1.0 && param > 0.0) {888params[cnt++] = param;889}890}891}892893/* Simple check for monotonicity in Y before searching for the extreme894* points of the Y(t) function. We first check if the curve is895* monotonic in Y by seeing if all of the Y coordinates are strongly896* ordered.897*/898if ((coords[1] > coords[3] || coords[3] > coords[5]) &&899(coords[1] < coords[3] || coords[3] < coords[5]))900{901/* Searching for extreme points of the Y(t) function by solving902* dY(t)903* ----- = 0 equation904* dt905*/906double ay = coords[1] - 2*coords[3] + coords[5];907908if (ay != 0) {909/* Calculating root of the following equation910* ay*t + by = 0911*/912double by = coords[1] - coords[3];913914param = by/ay;915if (param < 1.0 && param > 0.0) {916if (cnt > 0) {917/* Inserting parameter only if it differs from918* already stored919*/920if (params[0] > param) {921params[cnt++] = params[0];922params[0] = param;923} else if (params[0] < param) {924params[cnt++] = param;925}926} else {927params[cnt++] = param;928}929}930}931}932933/* Processing obtained monotonic parts */934switch(cnt) {935case 0:936break;937case 1:938ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,939(float)params[0]);940break;941case 2:942ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,943(float)params[0]);944param = params[1] - params[0];945if (param > 0) {946ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,947/* Scale parameter to match with948* rest of the curve949*/950(float)(param/(1.0 - params[0])));951}952break;953}954955ProcessMonotonicQuad(hnd,coords,pixelInfo);956}957958/*959* Bite the piece of the quadratic curve from start point till the point960* corresponding to the specified parameter then call ProcessQuad for the961* bitten part.962* Note: coords array will be changed963*/964private static void ProcessFirstMonotonicPartOfQuad(ProcessHandler hnd,965float[] coords,966int[] pixelInfo,967float t) {968float[] coords1 = new float[6];969970coords1[0] = coords[0];971coords1[1] = coords[1];972coords1[2] = coords[0] + t*(coords[2] - coords[0]);973coords1[3] = coords[1] + t*(coords[3] - coords[1]);974coords[2] = coords[2] + t*(coords[4] - coords[2]);975coords[3] = coords[3] + t*(coords[5] - coords[3]);976coords[0] = coords1[4] = coords1[2] + t*(coords[2] - coords1[2]);977coords[1] = coords1[5] = coords1[3] + t*(coords[3] - coords1[3]);978979ProcessMonotonicQuad(hnd, coords1, pixelInfo);980}981982/* Performing drawing of the monotonic in X and Y cubic curves with sizes983* less than MAX_CUB_SIZE by using forward differencing method of984* calculation. See comments to the DrawMonotonicCubic in the985* ProcessGeneralPath.c986*/987private static void DrawMonotonicCubic(ProcessHandler hnd,988float[] coords,989boolean checkBounds,990int[] pixelInfo) {991int x0 = (int)(coords[0]*MDP_MULT);992int y0 = (int)(coords[1]*MDP_MULT);993994int xe = (int)(coords[6]*MDP_MULT);995int ye = (int)(coords[7]*MDP_MULT);996997/* Extracting fractional part of coordinates of first control point */998int px = (x0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;999int py = (y0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;10001001/* Setting default boundary values for checking first and second forward1002* difference for the necessity of the restepping. See comments to the1003* boundary values in ProcessQuad for more info.1004*/1005int incStepBnd = DF_CUB_INC_BND;1006int decStepBnd = DF_CUB_DEC_BND;10071008/* Setting default amount of steps */1009int count = DF_CUB_COUNT;10101011/* Setting default shift for preparing to the midpoint rounding */1012int shift = DF_CUB_SHIFT;10131014int ax = (int)((-coords[0] + 3*coords[2] - 3*coords[4] +1015coords[6])*CUB_A_MDP_MULT);1016int ay = (int)((-coords[1] + 3*coords[3] - 3*coords[5] +1017coords[7])*CUB_A_MDP_MULT);10181019int bx = (int)((3*coords[0] - 6*coords[2] +10203*coords[4])*CUB_B_MDP_MULT);1021int by = (int)((3*coords[1] - 6*coords[3] +10223*coords[5])*CUB_B_MDP_MULT);10231024int cx = (int)((-3*coords[0] + 3*coords[2])*(CUB_C_MDP_MULT));1025int cy = (int)((-3*coords[1] + 3*coords[3])*(CUB_C_MDP_MULT));10261027int dddpx = 6*ax;1028int dddpy = 6*ay;10291030int ddpx = dddpx + bx;1031int ddpy = dddpy + by;10321033int dpx = ax + (bx>>1) + cx;1034int dpy = ay + (by>>1) + cy;10351036int x1, y1;10371038int x2 = x0;1039int y2 = y0;10401041/* Calculating whole part of the first point of the curve */1042int x0w = x0 & MDP_W_MASK;1043int y0w = y0 & MDP_W_MASK;10441045int dx = xe - x0;1046int dy = ye - y0;10471048while (count > 0) {1049/* Perform decreasing step in 2 times if necessary */1050while (Math.abs(ddpx) > decStepBnd ||1051Math.abs(ddpy) > decStepBnd) {1052ddpx = (ddpx<<1) - dddpx;1053ddpy = (ddpy<<1) - dddpy;1054dpx = (dpx<<2) - (ddpx>>1);1055dpy = (dpy<<2) - (ddpy>>1);1056count <<=1;1057decStepBnd <<=3;1058incStepBnd <<=3;1059px <<=3;1060py <<=3;1061shift += 3;1062}10631064/* Perform increasing step in 2 times if necessary.1065* Note: we could do it only in even steps1066*/10671068while ((count & 1) == 0 && shift > DF_CUB_SHIFT &&1069Math.abs(dpx) <= incStepBnd &&1070Math.abs(dpy) <= incStepBnd) {1071dpx = (dpx>>2) + (ddpx>>3);1072dpy = (dpy>>2) + (ddpy>>3);1073ddpx = (ddpx + dddpx)>>1;1074ddpy = (ddpy + dddpy)>>1;1075count >>=1;1076decStepBnd >>=3;1077incStepBnd >>=3;1078px >>=3;1079py >>=3;1080shift -= 3;1081}10821083count--;10841085/* Performing one step less than necessary and use actual (xe,ye)1086* curve's endpoint instead of calculated. This prevent us from1087* running above the curve endpoint due to the accumulated errors1088* during the iterations.1089*/1090if (count > 0) {1091px += dpx;1092py += dpy;10931094dpx += ddpx;1095dpy += ddpy;1096ddpx += dddpx;1097ddpy += dddpy;10981099x1 = x2;1100y1 = y2;11011102x2 = x0w + (px >> shift);1103y2 = y0w + (py >> shift);11041105/* Checking that we are not running out of the endpoint and1106* bounding violating coordinate. The check is pretty simple1107* because the curve passed to the DrawCubic already split1108* into the monotonic in X and Y pieces1109*/11101111/* Bounding x2 by xe */1112if (((xe-x2)^dx) < 0) {1113x2 = xe;1114}11151116/* Bounding y2 by ye */1117if (((ye-y2)^dy) < 0) {1118y2 = ye;1119}11201121hnd.processFixedLine(x1, y1, x2, y2, pixelInfo, checkBounds,1122false);1123} else {1124hnd.processFixedLine(x2, y2, xe, ye, pixelInfo, checkBounds,1125false);1126}1127}1128}11291130/*1131* Checking size of the cubic curves and split them if necessary.1132* Calling DrawMonotonicCubic for the curves of the appropriate size.1133* Note: coords array could be changed1134*/1135private static void ProcessMonotonicCubic(ProcessHandler hnd,1136float[] coords,1137int[] pixelInfo) {11381139float[] coords1 = new float[8];1140float tx, ty;1141float xMin, xMax;1142float yMin, yMax;11431144xMin = xMax = coords[0];1145yMin = yMax = coords[1];11461147for (int i = 2; i < 8; i += 2) {1148xMin = (xMin > coords[i])? coords[i] : xMin;1149xMax = (xMax < coords[i])? coords[i] : xMax;1150yMin = (yMin > coords[i + 1])? coords[i + 1] : yMin;1151yMax = (yMax < coords[i + 1])? coords[i + 1] : yMax;1152}11531154if (hnd.clipMode == PH_MODE_DRAW_CLIP) {1155/* In case of drawing we could just skip curves which are1156* completely out of bounds1157*/1158if (hnd.dhnd.xMaxf < xMin || hnd.dhnd.xMinf > xMax ||1159hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax) {1160return;1161}1162} else {11631164/* In case of filling we could skip curves which are above,1165* below and behind the right boundary of the visible area1166*/11671168if (hnd.dhnd.yMaxf < yMin || hnd.dhnd.yMinf > yMax ||1169hnd.dhnd.xMaxf < xMin)1170{1171return;1172}11731174/* We could clamp x coordinates to the corresponding boundary1175* if the curve is completely behind the left one1176*/11771178if (hnd.dhnd.xMinf > xMax) {1179coords[0] = coords[2] = coords[4] = coords[6] =1180hnd.dhnd.xMinf;1181}1182}11831184if (xMax - xMin > MAX_CUB_SIZE || yMax - yMin > MAX_CUB_SIZE) {1185coords1[6] = coords[6];1186coords1[7] = coords[7];1187coords1[4] = (coords[4] + coords[6])/2.0f;1188coords1[5] = (coords[5] + coords[7])/2.0f;1189tx = (coords[2] + coords[4])/2.0f;1190ty = (coords[3] + coords[5])/2.0f;1191coords1[2] = (tx + coords1[4])/2.0f;1192coords1[3] = (ty + coords1[5])/2.0f;1193coords[2] = (coords[0] + coords[2])/2.0f;1194coords[3] = (coords[1] + coords[3])/2.0f;1195coords[4] = (coords[2] + tx)/2.0f;1196coords[5] = (coords[3] + ty)/2.0f;1197coords[6]=coords1[0]=(coords[4] + coords1[2])/2.0f;1198coords[7]=coords1[1]=(coords[5] + coords1[3])/2.0f;11991200ProcessMonotonicCubic(hnd, coords, pixelInfo);12011202ProcessMonotonicCubic(hnd, coords1, pixelInfo);1203} else {1204DrawMonotonicCubic(hnd, coords,1205/* Set checkBounds parameter if curve intersects1206* boundary of the visible area. We know that1207* the curve is visible, so the check is pretty1208* simple1209*/1210hnd.dhnd.xMinf > xMin ||1211hnd.dhnd.xMaxf < xMax ||1212hnd.dhnd.yMinf > yMin ||1213hnd.dhnd.yMaxf < yMax,1214pixelInfo);1215}1216}12171218/*1219* Split cubic curve into monotonic in X and Y parts. Calling1220* ProcessMonotonicCubic for each monotonic piece of the curve.1221*1222* Note: coords array could be changed1223*/1224private static void ProcessCubic(ProcessHandler hnd,1225float[] coords,1226int[] pixelInfo) {1227/* Temporary array for holding parameters corresponding to the extreme1228* in X and Y points1229*/1230double[] params = new double[4];1231double[] eqn = new double[3];1232double[] res = new double[2];1233int cnt = 0;12341235/* Simple check for monotonicity in X before searching for the extreme1236* points of the X(t) function. We first check if the curve is1237* monotonic in X by seeing if all of the X coordinates are strongly1238* ordered.1239*/1240if ((coords[0] > coords[2] || coords[2] > coords[4] ||1241coords[4] > coords[6]) &&1242(coords[0] < coords[2] || coords[2] < coords[4] ||1243coords[4] < coords[6]))1244{1245/* Searching for extreme points of the X(t) function by solving1246* dX(t)1247* ---- = 0 equation1248* dt1249*/1250eqn[2] = -coords[0] + 3*coords[2] - 3*coords[4] + coords[6];1251eqn[1] = 2*(coords[0] - 2*coords[2] + coords[4]);1252eqn[0] = -coords[0] + coords[2];12531254int nr = QuadCurve2D.solveQuadratic(eqn, res);12551256/* Following code also correctly works in degenerate case of1257* the quadratic equation (nr = -1) because we do not need1258* splitting in this case.1259*/1260for (int i = 0; i < nr; i++) {1261if (res[i] > 0 && res[i] < 1) {1262params[cnt++] = res[i];1263}1264}1265}12661267/* Simple check for monotonicity in Y before searching for the extreme1268* points of the Y(t) function. We first check if the curve is1269* monotonic in Y by seeing if all of the Y coordinates are strongly1270* ordered.1271*/1272if ((coords[1] > coords[3] || coords[3] > coords[5] ||1273coords[5] > coords[7]) &&1274(coords[1] < coords[3] || coords[3] < coords[5] ||1275coords[5] < coords[7]))1276{1277/* Searching for extreme points of the Y(t) function by solving1278* dY(t)1279* ----- = 0 equation1280* dt1281*/1282eqn[2] = -coords[1] + 3*coords[3] - 3*coords[5] + coords[7];1283eqn[1] = 2*(coords[1] - 2*coords[3] + coords[5]);1284eqn[0] = -coords[1] + coords[3];12851286int nr = QuadCurve2D.solveQuadratic(eqn, res);12871288/* Following code also correctly works in degenerate case of1289* the quadratic equation (nr = -1) because we do not need1290* splitting in this case.1291*/1292for (int i = 0; i < nr; i++) {1293if (res[i] > 0 && res[i] < 1) {1294params[cnt++] = res[i];1295}1296}1297}12981299if (cnt > 0) {1300/* Sorting parameter values corresponding to the extreme points1301* of the curve1302*/1303Arrays.sort(params, 0, cnt);13041305/* Processing obtained monotonic parts */1306ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,1307(float)params[0]);1308for (int i = 1; i < cnt; i++) {1309double param = params[i] - params[i-1];1310if (param > 0) {1311ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,1312/* Scale parameter to match with rest of the curve */1313(float)(param/(1.0 - params[i - 1])));1314}1315}1316}13171318ProcessMonotonicCubic(hnd,coords,pixelInfo);1319}13201321/*1322* Bite the piece of the cubic curve from start point till the point1323* corresponding to the specified parameter then call ProcessCubic for the1324* bitten part.1325* Note: coords array will be changed1326*/1327private static void ProcessFirstMonotonicPartOfCubic(ProcessHandler hnd,1328float[] coords,1329int[] pixelInfo,1330float t)1331{1332float[] coords1 = new float[8];1333float tx, ty;13341335coords1[0] = coords[0];1336coords1[1] = coords[1];1337tx = coords[2] + t*(coords[4] - coords[2]);1338ty = coords[3] + t*(coords[5] - coords[3]);1339coords1[2] = coords[0] + t*(coords[2] - coords[0]);1340coords1[3] = coords[1] + t*(coords[3] - coords[1]);1341coords1[4] = coords1[2] + t*(tx - coords1[2]);1342coords1[5] = coords1[3] + t*(ty - coords1[3]);1343coords[4] = coords[4] + t*(coords[6] - coords[4]);1344coords[5] = coords[5] + t*(coords[7] - coords[5]);1345coords[2] = tx + t*(coords[4] - tx);1346coords[3] = ty + t*(coords[5] - ty);1347coords[0]=coords1[6]=coords1[4] + t*(coords[2] - coords1[4]);1348coords[1]=coords1[7]=coords1[5] + t*(coords[3] - coords1[5]);13491350ProcessMonotonicCubic(hnd, coords1, pixelInfo);1351}13521353/* Note:1354* For more easy reading of the code below each java version of the macros1355* from the ProcessPath.c preceded by the commented origin call1356* containing verbose names of the parameters1357*/1358private static void ProcessLine(ProcessHandler hnd, float x1, float y1,1359float x2, float y2, int[] pixelInfo) {1360float xMin, yMin, xMax, yMax;1361int X1, Y1, X2, Y2, X3, Y3, res;1362boolean clipped = false;1363float x3,y3;1364float[] c = new float[]{x1, y1, x2, y2, 0, 0};13651366boolean lastClipped;13671368xMin = hnd.dhnd.xMinf;1369yMin = hnd.dhnd.yMinf;1370xMax = hnd.dhnd.xMaxf;1371yMax = hnd.dhnd.yMaxf;13721373//1374// TESTANDCLIP(yMin, yMax, y1, x1, y2, x2, res);1375//1376res = TESTANDCLIP(yMin, yMax, c, 1, 0, 3, 2);1377if (res == CRES_INVISIBLE) return;1378clipped = IS_CLIPPED(res);1379//1380// TESTANDCLIP(yMin, yMax, y2, x2, y1, x1, res);1381//1382res = TESTANDCLIP(yMin, yMax, c, 3, 2, 1, 0);1383if (res == CRES_INVISIBLE) return;1384lastClipped = IS_CLIPPED(res);1385clipped = clipped || lastClipped;13861387if (hnd.clipMode == PH_MODE_DRAW_CLIP) {1388//1389// TESTANDCLIP(xMin, xMax, x1, y1, x2, y2, res);1390//1391res = TESTANDCLIP(xMin, xMax, c, 0, 1, 2, 3);1392if (res == CRES_INVISIBLE) return;1393clipped = clipped || IS_CLIPPED(res);1394//1395// TESTANDCLIP(xMin, xMax, x2, y2, x1, y1, res);1396//1397res = TESTANDCLIP(xMin, xMax, c, 2, 3, 0, 1);1398if (res == CRES_INVISIBLE) return;1399lastClipped = lastClipped || IS_CLIPPED(res);1400clipped = clipped || lastClipped;1401X1 = (int)(c[0]*MDP_MULT);1402Y1 = (int)(c[1]*MDP_MULT);1403X2 = (int)(c[2]*MDP_MULT);1404Y2 = (int)(c[3]*MDP_MULT);14051406hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo,1407clipped, /* enable boundary checking in1408case of clipping to avoid1409entering out of bounds which1410could happens during rounding1411*/1412lastClipped /* Notify pProcessFixedLine1413that1414this is the end of the1415subpath (because of exiting1416out of boundaries)1417*/1418);1419} else {1420/* Clamping starting from first vertex of the processed1421* segment1422*1423* CLIPCLAMP(xMin, xMax, x1, y1, x2, y2, x3, y3, res);1424*/1425res = CLIPCLAMP(xMin, xMax, c, 0, 1, 2, 3, 4, 5);1426X1 = (int)(c[0]*MDP_MULT);1427Y1 = (int)(c[1]*MDP_MULT);14281429/* Clamping only by left boundary */1430if (res == CRES_MIN_CLIPPED) {1431X3 = (int)(c[4]*MDP_MULT);1432Y3 = (int)(c[5]*MDP_MULT);1433hnd.processFixedLine(X3, Y3, X1, Y1, pixelInfo,1434false, lastClipped);14351436} else if (res == CRES_INVISIBLE) {1437return;1438}14391440/* Clamping starting from last vertex of the processed1441* segment1442*1443* CLIPCLAMP(xMin, xMax, x2, y2, x1, y1, x3, y3, res);1444*/1445res = CLIPCLAMP(xMin, xMax, c, 2, 3, 0, 1, 4, 5);14461447/* Checking if there was a clip by right boundary */1448lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);14491450X2 = (int)(c[2]*MDP_MULT);1451Y2 = (int)(c[3]*MDP_MULT);1452hnd.processFixedLine(X1, Y1, X2, Y2, pixelInfo,1453false, lastClipped);14541455/* Clamping only by left boundary */1456if (res == CRES_MIN_CLIPPED) {1457X3 = (int)(c[4]*MDP_MULT);1458Y3 = (int)(c[5]*MDP_MULT);1459hnd.processFixedLine(X2, Y2, X3, Y3, pixelInfo,1460false, lastClipped);1461}1462}1463}14641465private static boolean doProcessPath(ProcessHandler hnd,1466Path2D.Float p2df,1467float transXf, float transYf) {1468float[] coords = new float[8];1469float[] tCoords = new float[8];1470float[] closeCoord = new float[] {0.0f, 0.0f};1471float[] firstCoord = new float[2];1472int[] pixelInfo = new int[5];1473boolean subpathStarted = false;1474boolean skip = false;1475float lastX, lastY;1476pixelInfo[0] = 0;14771478/* Adjusting boundaries to the capabilities of the1479* ProcessPath code1480*/1481hnd.dhnd.adjustBounds(LOWER_OUT_BND, LOWER_OUT_BND,1482UPPER_OUT_BND, UPPER_OUT_BND);14831484/* Adding support of the KEY_STROKE_CONTROL rendering hint.1485* Now we are supporting two modes: "pixels at centers" and1486* "pixels at corners".1487* First one is disabled by default but could be enabled by setting1488* VALUE_STROKE_PURE to the rendering hint. It means that pixel at the1489* screen (x,y) has (x + 0.5, y + 0.5) float coordinates.1490*1491* Second one is enabled by default and means straightforward mapping1492* (x,y) --> (x,y)1493*/1494if (hnd.dhnd.strokeControl == SunHints.INTVAL_STROKE_PURE) {1495closeCoord[0] = -0.5f;1496closeCoord[1] = -0.5f;1497transXf -= 0.5;1498transYf -= 0.5;1499}15001501PathIterator pi = p2df.getPathIterator(null);15021503while (!pi.isDone()) {1504switch (pi.currentSegment(coords)) {1505case PathIterator.SEG_MOVETO:1506/* Performing closing of the unclosed segments */1507if (subpathStarted && !skip) {1508if (hnd.clipMode == PH_MODE_FILL_CLIP) {1509if (tCoords[0] != closeCoord[0] ||1510tCoords[1] != closeCoord[1])1511{1512ProcessLine(hnd, tCoords[0], tCoords[1],1513closeCoord[0], closeCoord[1],1514pixelInfo);1515}1516}1517hnd.processEndSubPath();1518}15191520tCoords[0] = coords[0] + transXf;1521tCoords[1] = coords[1] + transYf;15221523/* Checking SEG_MOVETO coordinates if they are out of the1524* [LOWER_BND, UPPER_BND] range. This check also handles1525* NaN and Infinity values. Skipping next path segment in1526* case of invalid data.1527*/15281529if (tCoords[0] < UPPER_BND &&1530tCoords[0] > LOWER_BND &&1531tCoords[1] < UPPER_BND &&1532tCoords[1] > LOWER_BND)1533{1534subpathStarted = true;1535skip = false;1536closeCoord[0] = tCoords[0];1537closeCoord[1] = tCoords[1];1538} else {1539skip = true;1540}1541pixelInfo[0] = 0;1542break;1543case PathIterator.SEG_LINETO:1544lastX = tCoords[2] = coords[0] + transXf;1545lastY = tCoords[3] = coords[1] + transYf;15461547/* Checking SEG_LINETO coordinates if they are out of the1548* [LOWER_BND, UPPER_BND] range. This check also handles1549* NaN and Infinity values. Ignoring current path segment1550* in case of invalid data. If segment is skipped its1551* endpoint (if valid) is used to begin new subpath.1552*/15531554if (lastX < UPPER_BND &&1555lastX > LOWER_BND &&1556lastY < UPPER_BND &&1557lastY > LOWER_BND)1558{1559if (skip) {1560tCoords[0] = closeCoord[0] = lastX;1561tCoords[1] = closeCoord[1] = lastY;1562subpathStarted = true;1563skip = false;1564} else {1565ProcessLine(hnd, tCoords[0], tCoords[1],1566tCoords[2], tCoords[3], pixelInfo);1567tCoords[0] = lastX;1568tCoords[1] = lastY;1569}1570}1571break;1572case PathIterator.SEG_QUADTO:1573tCoords[2] = coords[0] + transXf;1574tCoords[3] = coords[1] + transYf;1575lastX = tCoords[4] = coords[2] + transXf;1576lastY = tCoords[5] = coords[3] + transYf;15771578/* Checking SEG_QUADTO coordinates if they are out of the1579* [LOWER_BND, UPPER_BND] range. This check also handles1580* NaN and Infinity values. Ignoring current path segment1581* in case of invalid endpoints's data. Equivalent to1582* the SEG_LINETO if endpoint coordinates are valid but1583* there are invalid data among other coordinates1584*/15851586if (lastX < UPPER_BND &&1587lastX > LOWER_BND &&1588lastY < UPPER_BND &&1589lastY > LOWER_BND)1590{1591if (skip) {1592tCoords[0] = closeCoord[0] = lastX;1593tCoords[1] = closeCoord[1] = lastY;1594subpathStarted = true;1595skip = false;1596} else {1597if (tCoords[2] < UPPER_BND &&1598tCoords[2] > LOWER_BND &&1599tCoords[3] < UPPER_BND &&1600tCoords[3] > LOWER_BND)1601{1602ProcessQuad(hnd, tCoords, pixelInfo);1603} else {1604ProcessLine(hnd, tCoords[0], tCoords[1],1605tCoords[4], tCoords[5],1606pixelInfo);1607}1608tCoords[0] = lastX;1609tCoords[1] = lastY;1610}1611}1612break;1613case PathIterator.SEG_CUBICTO:1614tCoords[2] = coords[0] + transXf;1615tCoords[3] = coords[1] + transYf;1616tCoords[4] = coords[2] + transXf;1617tCoords[5] = coords[3] + transYf;1618lastX = tCoords[6] = coords[4] + transXf;1619lastY = tCoords[7] = coords[5] + transYf;16201621/* Checking SEG_CUBICTO coordinates if they are out of the1622* [LOWER_BND, UPPER_BND] range. This check also handles1623* NaN and Infinity values. Ignoring current path segment1624* in case of invalid endpoints's data. Equivalent to1625* the SEG_LINETO if endpoint coordinates are valid but1626* there are invalid data among other coordinates1627*/16281629if (lastX < UPPER_BND &&1630lastX > LOWER_BND &&1631lastY < UPPER_BND &&1632lastY > LOWER_BND)1633{1634if (skip) {1635tCoords[0] = closeCoord[0] = tCoords[6];1636tCoords[1] = closeCoord[1] = tCoords[7];1637subpathStarted = true;1638skip = false;1639} else {1640if (tCoords[2] < UPPER_BND &&1641tCoords[2] > LOWER_BND &&1642tCoords[3] < UPPER_BND &&1643tCoords[3] > LOWER_BND &&1644tCoords[4] < UPPER_BND &&1645tCoords[4] > LOWER_BND &&1646tCoords[5] < UPPER_BND &&1647tCoords[5] > LOWER_BND)1648{1649ProcessCubic(hnd, tCoords, pixelInfo);1650} else {1651ProcessLine(hnd, tCoords[0], tCoords[1],1652tCoords[6], tCoords[7],1653pixelInfo);1654}1655tCoords[0] = lastX;1656tCoords[1] = lastY;1657}1658}1659break;1660case PathIterator.SEG_CLOSE:1661if (subpathStarted && !skip) {1662skip = false;1663if (tCoords[0] != closeCoord[0] ||1664tCoords[1] != closeCoord[1])1665{1666ProcessLine(hnd, tCoords[0], tCoords[1],1667closeCoord[0], closeCoord[1],1668pixelInfo);16691670/* Storing last path's point for using in following1671* segments without initial moveTo1672*/1673tCoords[0] = closeCoord[0];1674tCoords[1] = closeCoord[1];1675}1676hnd.processEndSubPath();1677}1678break;1679}1680pi.next();1681}16821683/* Performing closing of the unclosed segments */1684if (subpathStarted & !skip) {1685if (hnd.clipMode == PH_MODE_FILL_CLIP) {1686if (tCoords[0] != closeCoord[0] ||1687tCoords[1] != closeCoord[1])1688{1689ProcessLine(hnd, tCoords[0], tCoords[1],1690closeCoord[0], closeCoord[1],1691pixelInfo);1692}1693}1694hnd.processEndSubPath();1695}1696return true;1697}16981699private static class Point {1700public int x;1701public int y;1702public boolean lastPoint;1703public Point prev;1704public Point next;1705public Point nextByY;1706public Edge edge;1707public Point(int x, int y, boolean lastPoint) {1708this.x = x;1709this.y = y;1710this.lastPoint = lastPoint;1711}1712};17131714private static class Edge {1715int x;1716int dx;1717Point p;1718int dir;1719Edge prev;1720Edge next;17211722public Edge(Point p, int x, int dx, int dir) {1723this.p = p;1724this.x = x;1725this.dx = dx;1726this.dir = dir;1727}1728};17291730/* Size of the default buffer in the FillData structure. This buffer is1731* replaced with heap allocated in case of large paths.1732*/1733private static final int DF_MAX_POINT = 256;17341735/* Following class accumulates points of the non-continuous flattened1736* general path during iteration through the origin path's segments . The1737* end of the each subpath is marked as lastPoint flag set at the last1738* point1739*/1740private static class FillData {1741List<Point> plgPnts;1742public int plgYMin;1743public int plgYMax;17441745public FillData() {1746plgPnts = new Vector<Point>(DF_MAX_POINT);1747}17481749public void addPoint(int x, int y, boolean lastPoint) {1750if (plgPnts.size() == 0) {1751plgYMin = plgYMax = y;1752} else {1753plgYMin = (plgYMin > y)?y:plgYMin;1754plgYMax = (plgYMax < y)?y:plgYMax;1755}17561757plgPnts.add(new Point(x, y, lastPoint));1758}17591760public boolean isEmpty() {1761return plgPnts.size() == 0;1762}17631764public boolean isEnded() {1765return plgPnts.get(plgPnts.size() - 1).lastPoint;1766}17671768public boolean setEnded() {1769return plgPnts.get(plgPnts.size() - 1).lastPoint = true;1770}1771}17721773private static class ActiveEdgeList {1774Edge head;17751776public boolean isEmpty() {1777return (head == null);1778}17791780public void insert(Point pnt, int cy) {1781Point np = pnt.next;1782int X1 = pnt.x, Y1 = pnt.y;1783int X2 = np.x, Y2 = np.y;1784Edge ne;1785if (Y1 == Y2) {1786/* Skipping horizontal segments */1787return;1788} else {1789int dX = X2 - X1;1790int dY = Y2 - Y1;1791int stepx, x0, dy, dir;17921793if (Y1 < Y2) {1794x0 = X1;1795dy = cy - Y1;1796dir = -1;1797} else { // (Y1 > Y2)1798x0 = X2;1799dy = cy - Y2;1800dir = 1;1801}18021803/* We need to worry only about dX because dY is in denominator1804* and abs(dy) < MDP_MULT (cy is a first scanline of the scan1805* converted segment and we subtract y coordinate of the1806* nearest segment's end from it to obtain dy)1807*/1808if (dX > CALC_UBND || dX < CALC_LBND) {1809stepx = (int)((((double)dX)*MDP_MULT)/dY);1810x0 = x0 + (int)((((double)dX)*dy)/dY);1811} else {1812stepx = (dX<<MDP_PREC)/dY;1813x0 += (dX*dy)/dY;1814}18151816ne = new Edge(pnt, x0, stepx, dir);1817}18181819ne.next = head;1820ne.prev = null;1821if (head != null) {1822head.prev = ne;1823}1824head = pnt.edge = ne;1825}18261827public void delete(Edge e) {1828Edge prevp = e.prev;1829Edge nextp = e.next;1830if (prevp != null) {1831prevp.next = nextp;1832} else {1833head = nextp;1834}1835if (nextp != null) {1836nextp.prev = prevp;1837}1838}18391840/**1841* Bubble sorting in the ascending order of the linked list. This1842* implementation stops processing the list if there were no changes1843* during the previous pass.1844*1845* We could not use O(N) Radix sort here because in most cases list of1846* edges almost sorted. So, bubble sort (O(N^2)) is working much1847* better. Note, in case of array of edges Shell sort is more1848* efficient.1849*/1850public void sort() {1851Edge p, q, r, s = null, temp;1852boolean wasSwap = true;18531854// r precedes p and s points to the node up to which1855// comparisons are to be made1856while (s != head.next && wasSwap) {1857r = p = head;1858q = p.next;1859wasSwap = false;1860while (p != s) {1861if (p.x >= q.x) {1862wasSwap = true;1863if (p == head) {1864temp = q.next;1865q.next = p;1866p.next = temp;1867head = q;1868r = q;1869} else {1870temp = q.next;1871q.next = p;1872p.next = temp;1873r.next = q;1874r = q;1875}1876} else {1877r = p;1878p = p.next;1879}1880q = p.next;1881if (q == s) s = p;1882}1883}18841885// correction of the back links in the double linked edge list1886p = head;1887q = null;1888while (p != null) {1889p.prev = q;1890q = p;1891p = p.next;1892}1893}1894}18951896private static void FillPolygon(FillProcessHandler hnd,1897int fillRule) {1898int k, y, n;1899boolean drawing;1900Edge active;1901int rightBnd = hnd.dhnd.xMax - 1;1902FillData fd = hnd.fd;1903int yMin = fd.plgYMin;1904int yMax = fd.plgYMax;1905int hashSize = ((yMax - yMin)>>MDP_PREC) + 4;19061907/* Because of support of the KEY_STROKE_CONTROL hint we are performing1908* shift of the coordinates at the higher level1909*/1910int hashOffset = ((yMin - 1) & MDP_W_MASK);19111912/* Winding counter */1913int counter;19141915/* Calculating mask to be applied to the winding counter */1916int counterMask =1917(fillRule == PathIterator.WIND_NON_ZERO)? -1:1;19181919int pntOffset;1920List<Point> pnts = fd.plgPnts;1921n = pnts.size();19221923if (n <=1) return;19241925Point[] yHash = new Point[hashSize];19261927/* Creating double linked list (prev, next links) describing path order1928* and hash table with points which fall between scanlines. nextByY1929* link is used for the points which are between same scanlines.1930* Scanlines are passed through the centers of the pixels.1931*/1932Point curpt = pnts.get(0);1933curpt.prev = null;1934for (int i = 0; i < n - 1; i++) {1935curpt = pnts.get(i);1936Point nextpt = pnts.get(i + 1);1937int curHashInd = (curpt.y - hashOffset - 1) >> MDP_PREC;1938curpt.nextByY = yHash[curHashInd];1939yHash[curHashInd] = curpt;1940curpt.next = nextpt;1941nextpt.prev = curpt;1942}19431944Point ept = pnts.get(n - 1);1945int curHashInd = (ept.y - hashOffset - 1) >> MDP_PREC;1946ept.nextByY = yHash[curHashInd];1947yHash[curHashInd] = ept;19481949ActiveEdgeList activeList = new ActiveEdgeList();19501951for (y=hashOffset + MDP_MULT,k = 0;1952y<=yMax && k < hashSize; y += MDP_MULT, k++)1953{1954for(Point pt = yHash[k];pt != null; pt=pt.nextByY) {1955/* pt.y should be inside hashed interval1956* assert(y-MDP_MULT <= pt.y && pt.y < y);1957*/1958if (pt.prev != null && !pt.prev.lastPoint) {1959if (pt.prev.edge != null && pt.prev.y <= y) {1960activeList.delete(pt.prev.edge);1961pt.prev.edge = null;1962} else if (pt.prev.y > y) {1963activeList.insert(pt.prev, y);1964}1965}19661967if (!pt.lastPoint && pt.next != null) {1968if (pt.edge != null && pt.next.y <= y) {1969activeList.delete(pt.edge);1970pt.edge = null;1971} else if (pt.next.y > y) {1972activeList.insert(pt, y);1973}1974}1975}19761977if (activeList.isEmpty()) continue;19781979activeList.sort();19801981counter = 0;1982drawing = false;1983int xl, xr;1984xl = xr = hnd.dhnd.xMin;1985Edge curEdge = activeList.head;1986while (curEdge != null) {1987counter += curEdge.dir;1988if ((counter & counterMask) != 0 && !drawing) {1989xl = (curEdge.x + MDP_MULT - 1)>>MDP_PREC;1990drawing = true;1991}19921993if ((counter & counterMask) == 0 && drawing) {1994xr = (curEdge.x - 1) >> MDP_PREC;1995if (xl <= xr) {1996hnd.dhnd.drawScanline(xl, xr, y >> MDP_PREC);1997}1998drawing = false;1999}20002001curEdge.x += curEdge.dx;2002curEdge = curEdge.next;2003}20042005/* Performing drawing till the right boundary (for correct2006* rendering shapes clipped at the right side)2007*/2008if (drawing && xl <= rightBnd) {20092010/* Support of the strokeHint was added into the2011* draw and fill methods of the sun.java2d.pipe.LoopPipe2012*/2013hnd.dhnd.drawScanline(xl, rightBnd, y >> MDP_PREC);2014}2015}2016}20172018private static class FillProcessHandler extends ProcessHandler {20192020FillData fd;20212022/* Note: For more easy reading of the code below each java version of2023* the macros from the ProcessPath.c preceded by the commented2024* origin call containing verbose names of the parameters2025*/2026public void processFixedLine(int x1, int y1, int x2, int y2,2027int[] pixelInfo, boolean checkBounds,2028boolean endSubPath)2029{2030int outXMin, outXMax, outYMin, outYMax;2031int res;20322033/* There is no need to round line coordinates to the forward2034* differencing precision anymore. Such a rounding was used for2035* preventing the curve go out the endpoint (this sometimes does2036* not help). The problem was fixed in the forward differencing2037* loops.2038*/2039if (checkBounds) {2040boolean lastClipped;20412042/* This function is used only for filling shapes, so there is no2043* check for the type of clipping2044*/2045int[] c = new int[]{x1, y1, x2, y2, 0, 0};2046outXMin = (int)(dhnd.xMinf * MDP_MULT);2047outXMax = (int)(dhnd.xMaxf * MDP_MULT);2048outYMin = (int)(dhnd.yMinf * MDP_MULT);2049outYMax = (int)(dhnd.yMaxf * MDP_MULT);20502051/*2052* TESTANDCLIP(outYMin, outYMax, y1, x1, y2, x2, res);2053*/2054res = TESTANDCLIP(outYMin, outYMax, c, 1, 0, 3, 2);2055if (res == CRES_INVISIBLE) return;20562057/*2058* TESTANDCLIP(outYMin, outYMax, y2, x2, y1, x1, res);2059*/2060res = TESTANDCLIP(outYMin, outYMax, c, 3, 2, 1, 0);2061if (res == CRES_INVISIBLE) return;2062lastClipped = IS_CLIPPED(res);20632064/* Clamping starting from first vertex of the processed2065* segment2066*2067* CLIPCLAMP(outXMin, outXMax, x1, y1, x2, y2, x3, y3, res);2068*/2069res = CLIPCLAMP(outXMin, outXMax, c, 0, 1, 2, 3, 4, 5);20702071/* Clamping only by left boundary */2072if (res == CRES_MIN_CLIPPED) {2073processFixedLine(c[4], c[5], c[0], c[1], pixelInfo,2074false, lastClipped);20752076} else if (res == CRES_INVISIBLE) {2077return;2078}20792080/* Clamping starting from last vertex of the processed2081* segment2082*2083* CLIPCLAMP(outXMin, outXMax, x2, y2, x1, y1, x3, y3, res);2084*/2085res = CLIPCLAMP(outXMin, outXMax, c, 2, 3, 0, 1, 4, 5);20862087/* Checking if there was a clip by right boundary */2088lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);20892090processFixedLine(c[0], c[1], c[2], c[3], pixelInfo,2091false, lastClipped);20922093/* Clamping only by left boundary */2094if (res == CRES_MIN_CLIPPED) {2095processFixedLine(c[2], c[3], c[4], c[5], pixelInfo,2096false, lastClipped);2097}20982099return;2100}21012102/* Adding first point of the line only in case of empty or just2103* finished path2104*/2105if (fd.isEmpty() || fd.isEnded()) {2106fd.addPoint(x1, y1, false);2107}21082109fd.addPoint(x2, y2, false);21102111if (endSubPath) {2112fd.setEnded();2113}2114}21152116FillProcessHandler(DrawHandler dhnd) {2117super(dhnd, PH_MODE_FILL_CLIP);2118this.fd = new FillData();2119}21202121public void processEndSubPath() {2122if (!fd.isEmpty()) {2123fd.setEnded();2124}2125}2126}2127}212821292130