|
Java example source code file (ProcessPath.java)
This example Java source code file (ProcessPath.java) is included in the alvinalexander.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Learn more about this Java project at its project page.
The ProcessPath.java Java example source code
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.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 static abstract 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 static abstract 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< 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 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 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};
float firstCoord[] = new float[2];
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) + 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 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 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();
}
}
}
}
Other Java examples (source code examples)
Here is a short list of links related to this Java ProcessPath.java source code file:
|