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; }Lines 1501–2000 of 2128. Previous lines Load more
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; }