0N/A/*
3909N/A * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#include <math.h>
0N/A#include <assert.h>
0N/A#include <stdlib.h>
0N/A#include <string.h>
0N/A
0N/A#include "j2d_md.h"
0N/A#include "java_awt_geom_PathIterator.h"
0N/A
0N/A#include "ProcessPath.h"
0N/A
0N/A/*
0N/A * This framework performs filling and drawing of paths with sub-pixel
0N/A * precision. Also, it performs clipping by the specified view area.
0N/A *
0N/A * Drawing of the shapes is performed not pixel by pixel but segment by segment
0N/A * except several pixels near endpoints of the drawn line. This approach saves
0N/A * lot's of cpu cycles especially in case of large primitives (like ovals with
0N/A * sizes more than 50) and helps in achieving appropriate visual quality. Also,
0N/A * such method of drawing is useful for the accelerated pipelines where
0N/A * overhead of the per-pixel drawing could eliminate all benefits of the
0N/A * hardware acceleration.
0N/A *
0N/A * Filling of the path was taken from
0N/A *
0N/A * [Graphics Gems, edited by Andrew S Glassner. Academic Press 1990,
0N/A * ISBN 0-12-286165-5 (Concave polygon scan conversion), 87-91]
0N/A *
0N/A * and modified to work with sub-pixel precision and non-continuous paths.
0N/A * It's also speeded up by using hash table by rows of the filled objects.
0N/A *
0N/A * Here is high level scheme showing the rendering process:
0N/A *
0N/A * doDrawPath doFillPath
0N/A * \ /
0N/A * ProcessPath
0N/A * |
0N/A * CheckPathSegment
0N/A * |
0N/A * --------+------
0N/A * | |
0N/A * | |
0N/A * | |
0N/A * _->ProcessCurve |
0N/A * / / | |
0N/A * \___/ | |
0N/A * | |
0N/A * DrawCurve ProcessLine
0N/A * \ /
0N/A * \ /
0N/A * \ /
0N/A * \ /
0N/A * ------+------
0N/A * (filling) / \ (drawing)
0N/A * / \
0N/A * Clipping and Clipping
0N/A * clamping \
0N/A * | \
0N/A * StoreFixedLine ProcessFixedLine
0N/A * | / \
0N/A * | / \
0N/A * FillPolygon PROCESS_LINE PROCESS_POINT
0N/A *
0N/A *
0N/A *
0N/A * CheckPathSegment - rough checking and skipping path's segments in case of
0N/A * invalid or huge coordinates of the control points to
0N/A * avoid calculation problems with NaNs and values close
0N/A * to the FLT_MAX
0N/A *
0N/A * ProcessCurve - (ProcessQuad, ProcessCubic) Splitting the curve into
0N/A * monotonic parts having appropriate size (calculated as
0N/A * boundary box of the control points)
0N/A *
0N/A * DrawMonotonicCurve - (DrawMonotonicQuad, DrawMonotonicCubic) flattening
0N/A * monotonic curve using adaptive forward differencing
0N/A *
0N/A * StoreFixedLine - storing segment from the flattened path to the
0N/A * FillData structure. Performing clipping and clamping if
0N/A * necessary.
0N/A *
0N/A * PROCESS_LINE, PROCESS_POINT - Helpers for calling appropriate primitive from
0N/A * DrawHandler structure
0N/A *
0N/A * ProcessFixedLine - Drawing line segment with subpixel precision.
0N/A *
0N/A */
0N/A
0N/A#define PROCESS_LINE(hnd, fX0, fY0, fX1, fY1, checkBounds, pixelInfo) \
0N/A do { \
0N/A jint X0 = (fX0) >> MDP_PREC; \
0N/A jint Y0 = (fY0) >> MDP_PREC; \
0N/A jint X1 = (fX1) >> MDP_PREC; \
0N/A jint Y1 = (fY1) >> MDP_PREC; \
2957N/A jint res; \
2957N/A \
3717N/A /* Checking bounds and clipping if necessary. \
3717N/A * REMIND: It's temporary solution to avoid OOB in rendering code. \
3717N/A * Current approach uses float equations which are unreliable for \
3717N/A * clipping and makes assumptions about the line biases of the \
3717N/A * rendering algorithm. Also, clipping code should be moved down \
3717N/A * into only those output renderers that need it. \
3717N/A */ \
2957N/A if (checkBounds) { \
3717N/A jfloat xMinf = hnd->dhnd->xMinf + 0.5f; \
3717N/A jfloat yMinf = hnd->dhnd->yMinf + 0.5f; \
3717N/A jfloat xMaxf = hnd->dhnd->xMaxf + 0.5f; \
3717N/A jfloat yMaxf = hnd->dhnd->yMaxf + 0.5f; \
3717N/A TESTANDCLIP(yMinf, yMaxf, Y0, X0, Y1, X1, jint, res); \
2957N/A if (res == CRES_INVISIBLE) break; \
3717N/A TESTANDCLIP(yMinf, yMaxf, Y1, X1, Y0, X0, jint, res); \
2957N/A if (res == CRES_INVISIBLE) break; \
3717N/A TESTANDCLIP(xMinf, xMaxf, X0, Y0, X1, Y1, jint, res); \
2957N/A if (res == CRES_INVISIBLE) break; \
3717N/A TESTANDCLIP(xMinf, xMaxf, X1, Y1, X0, Y0, jint, res); \
2957N/A if (res == CRES_INVISIBLE) break; \
2957N/A } \
2957N/A \
2957N/A /* Handling lines having just one pixel */ \
0N/A if (((X0^X1) | (Y0^Y1)) == 0) { \
0N/A if (pixelInfo[0] == 0) { \
0N/A pixelInfo[0] = 1; \
0N/A pixelInfo[1] = X0; \
0N/A pixelInfo[2] = Y0; \
0N/A pixelInfo[3] = X0; \
0N/A pixelInfo[4] = Y0; \
0N/A hnd->dhnd->pDrawPixel(hnd->dhnd, X0, Y0); \
0N/A } else if ((X0 != pixelInfo[3] || Y0 != pixelInfo[4]) && \
0N/A (X0 != pixelInfo[1] || Y0 != pixelInfo[2])) { \
0N/A hnd->dhnd->pDrawPixel(hnd->dhnd, X0, Y0); \
0N/A pixelInfo[3] = X0; \
0N/A pixelInfo[4] = Y0; \
0N/A } \
0N/A break; \
0N/A } \
0N/A \
2957N/A if (pixelInfo[0] && \
2957N/A ((pixelInfo[1] == X0 && pixelInfo[2] == Y0) || \
2957N/A (pixelInfo[3] == X0 && pixelInfo[4] == Y0))) \
0N/A { \
2957N/A hnd->dhnd->pDrawPixel(hnd->dhnd, X0, Y0); \
0N/A } \
0N/A \
0N/A hnd->dhnd->pDrawLine(hnd->dhnd, X0, Y0, X1, Y1); \
0N/A \
0N/A if (pixelInfo[0] == 0) { \
0N/A pixelInfo[0] = 1; \
0N/A pixelInfo[1] = X0; \
0N/A pixelInfo[2] = Y0; \
0N/A pixelInfo[3] = X0; \
0N/A pixelInfo[4] = Y0; \
0N/A } \
0N/A \
0N/A /* Switch on last pixel of the line if it was already \
0N/A * drawn during rendering of the previous segments \
0N/A */ \
0N/A if ((pixelInfo[1] == X1 && pixelInfo[2] == Y1) || \
0N/A (pixelInfo[3] == X1 && pixelInfo[4] == Y1)) \
0N/A { \
0N/A hnd->dhnd->pDrawPixel(hnd->dhnd, X1, Y1); \
0N/A } \
0N/A pixelInfo[3] = X1; \
0N/A pixelInfo[4] = Y1; \
0N/A } while(0)
0N/A
0N/A#define PROCESS_POINT(hnd, fX, fY, checkBounds, pixelInfo) \
0N/A do { \
0N/A jint _X = (fX)>> MDP_PREC; \
0N/A jint _Y = (fY)>> MDP_PREC; \
0N/A if (checkBounds && \
0N/A (hnd->dhnd->yMin > _Y || \
0N/A hnd->dhnd->yMax <= _Y || \
0N/A hnd->dhnd->xMin > _X || \
0N/A hnd->dhnd->xMax <= _X)) break; \
0N/A/* \
0N/A * (_X,_Y) should be inside boundaries \
0N/A * \
0N/A * assert(hnd->dhnd->yMin <= _Y && \
0N/A * hnd->dhnd->yMax > _Y && \
0N/A * hnd->dhnd->xMin <= _X && \
0N/A * hnd->dhnd->xMax > _X); \
0N/A * \
0N/A */ \
0N/A if (pixelInfo[0] == 0) { \
0N/A pixelInfo[0] = 1; \
0N/A pixelInfo[1] = _X; \
0N/A pixelInfo[2] = _Y; \
0N/A pixelInfo[3] = _X; \
0N/A pixelInfo[4] = _Y; \
0N/A hnd->dhnd->pDrawPixel(hnd->dhnd, _X, _Y); \
0N/A } else if ((_X != pixelInfo[3] || _Y != pixelInfo[4]) && \
0N/A (_X != pixelInfo[1] || _Y != pixelInfo[2])) { \
0N/A hnd->dhnd->pDrawPixel(hnd->dhnd, _X, _Y); \
0N/A pixelInfo[3] = _X; \
0N/A pixelInfo[4] = _Y; \
0N/A } \
0N/A } while(0)
0N/A
0N/A
0N/A/*
0N/A * Constants for the forward differencing
0N/A * of the cubic and quad curves
0N/A */
0N/A
0N/A/* Maximum size of the cubic curve (calculated as the size of the bounding box
0N/A * of the control points) which could be rendered without splitting
0N/A */
0N/A#define MAX_CUB_SIZE 256
0N/A
0N/A/* Maximum size of the quad curve (calculated as the size of the bounding box
0N/A * of the control points) which could be rendered without splitting
0N/A */
0N/A#define MAX_QUAD_SIZE 1024
0N/A
0N/A/* Default power of 2 steps used in the forward differencing. Here DF prefix
0N/A * stands for DeFault. Constants below are used as initial values for the
0N/A * adaptive forward differencing algorithm.
0N/A */
0N/A#define DF_CUB_STEPS 3
0N/A#define DF_QUAD_STEPS 2
0N/A
0N/A/* Shift of the current point of the curve for preparing to the midpoint
0N/A * rounding
0N/A */
0N/A#define DF_CUB_SHIFT (FWD_PREC + DF_CUB_STEPS*3 - MDP_PREC)
0N/A#define DF_QUAD_SHIFT (FWD_PREC + DF_QUAD_STEPS*2 - MDP_PREC)
0N/A
0N/A/* Default amount of steps of the forward differencing */
0N/A#define DF_CUB_COUNT (1<<DF_CUB_STEPS)
0N/A#define DF_QUAD_COUNT (1<<DF_QUAD_STEPS)
0N/A
0N/A/* Default boundary constants used to check the necessity of the restepping */
0N/A#define DF_CUB_DEC_BND (1<<(DF_CUB_STEPS*3 + FWD_PREC + 2))
0N/A#define DF_CUB_INC_BND (1<<(DF_CUB_STEPS*3 + FWD_PREC - 1))
0N/A#define DF_QUAD_DEC_BND (1<<(DF_QUAD_STEPS*2 + FWD_PREC + 2))
0N/A
0N/A/* Multiplyers for the coefficients of the polynomial form of the cubic and
0N/A * quad curves representation
0N/A */
0N/A#define CUB_A_SHIFT FWD_PREC
0N/A#define CUB_B_SHIFT (DF_CUB_STEPS + FWD_PREC + 1)
0N/A#define CUB_C_SHIFT (DF_CUB_STEPS*2 + FWD_PREC)
0N/A
0N/A#define CUB_A_MDP_MULT (1<<CUB_A_SHIFT)
0N/A#define CUB_B_MDP_MULT (1<<CUB_B_SHIFT)
0N/A#define CUB_C_MDP_MULT (1<<CUB_C_SHIFT)
0N/A
0N/A#define QUAD_A_SHIFT FWD_PREC
0N/A#define QUAD_B_SHIFT (DF_QUAD_STEPS + FWD_PREC)
0N/A
0N/A#define QUAD_A_MDP_MULT (1<<QUAD_A_SHIFT)
0N/A#define QUAD_B_MDP_MULT (1<<QUAD_B_SHIFT)
0N/A
0N/A#define CALC_MAX(MAX, X) ((MAX)=((X)>(MAX))?(X):(MAX))
0N/A#define CALC_MIN(MIN, X) ((MIN)=((X)<(MIN))?(X):(MIN))
0N/A#define MAX(MAX, X) (((X)>(MAX))?(X):(MAX))
0N/A#define MIN(MIN, X) (((X)<(MIN))?(X):(MIN))
0N/A#define ABS32(X) (((X)^((X)>>31))-((X)>>31))
0N/A#define SIGN32(X) ((X) >> 31) | ((juint)(-(X)) >> 31)
0N/A
0N/A/* Boundaries used for clipping large path segments (those are inside
0N/A * [UPPER/LOWER]_BND boundaries)
0N/A */
0N/A#define UPPER_OUT_BND (1 << (30 - MDP_PREC))
0N/A#define LOWER_OUT_BND (-UPPER_OUT_BND)
0N/A
0N/A#define ADJUST(X, LBND, UBND) \
0N/A do { \
0N/A if ((X) < (LBND)) { \
0N/A (X) = (LBND); \
0N/A } else if ((X) > UBND) { \
0N/A (X) = (UBND); \
0N/A } \
0N/A } while(0)
0N/A
0N/A/* Following constants are used for providing open boundaries of the intervals
0N/A */
0N/A#define EPSFX 1
0N/A#define EPSF (((jfloat)EPSFX)/MDP_MULT)
0N/A
0N/A/* Calculation boundary. It is used for switching to the more slow but allowing
0N/A * larger input values method of calculation of the initial values of the scan
0N/A * converted line segments inside the FillPolygon.
0N/A */
0N/A#define CALC_BND (1 << (30 - MDP_PREC))
0N/A
0N/A/* Clipping macros for drawing and filling algorithms */
0N/A
0N/A#define CLIP(a1, b1, a2, b2, t) \
0N/A (b1 + ((jdouble)(t - a1)*(b2 - b1)) / (a2 - a1))
0N/A
0N/Aenum {
0N/A CRES_MIN_CLIPPED,
0N/A CRES_MAX_CLIPPED,
0N/A CRES_NOT_CLIPPED,
0N/A CRES_INVISIBLE
0N/A};
0N/A
0N/A#define IS_CLIPPED(res) (res == CRES_MIN_CLIPPED || res == CRES_MAX_CLIPPED)
0N/A
0N/A#define TESTANDCLIP(LINE_MIN, LINE_MAX, a1, b1, a2, b2, TYPE, res) \
0N/A do { \
0N/A jdouble t; \
0N/A res = CRES_NOT_CLIPPED; \
0N/A if (a1 < (LINE_MIN) || a1 > (LINE_MAX)) { \
0N/A if (a1 < (LINE_MIN)) { \
0N/A if (a2 < (LINE_MIN)) { \
0N/A res = CRES_INVISIBLE; \
0N/A break; \
0N/A }; \
0N/A res = CRES_MIN_CLIPPED; \
0N/A t = (LINE_MIN); \
0N/A } else { \
0N/A if (a2 > (LINE_MAX)) { \
0N/A res = CRES_INVISIBLE; \
0N/A break; \
0N/A }; \
0N/A res = CRES_MAX_CLIPPED; \
0N/A t = (LINE_MAX); \
0N/A } \
0N/A b1 = (TYPE)CLIP(a1, b1, a2, b2, t); \
0N/A a1 = (TYPE)t; \
0N/A } \
0N/A } while (0)
0N/A
0N/A/* Following macro is used for clipping and clumping filled shapes.
0N/A * An example of this process is shown on the picture below:
0N/A * ----+ ----+
0N/A * |/ | |/ |
0N/A * + | + |
0N/A * /| | I |
0N/A * / | | I |
0N/A * | | | ===> I |
0N/A * \ | | I |
0N/A * \| | I |
0N/A * + | + |
0N/A * |\ | |\ |
0N/A * | ----+ | ----+
0N/A * boundary boundary
0N/A *
0N/A * We can only perform clipping in case of right side of the output area
0N/A * because all segments passed out the right boundary don't influence on the
0N/A * result of scan conversion algorithm (it correctly handles half open
0N/A * contours).
0N/A *
0N/A */
0N/A#define CLIPCLAMP(LINE_MIN, LINE_MAX, a1, b1, a2, b2, a3, b3, TYPE, res) \
0N/A do { \
0N/A a3 = a1; \
0N/A b3 = b1; \
0N/A TESTANDCLIP(LINE_MIN, LINE_MAX, a1, b1, a2, b2, TYPE, res); \
0N/A if (res == CRES_MIN_CLIPPED) { \
0N/A a3 = a1; \
0N/A } else if (res == CRES_MAX_CLIPPED) { \
0N/A a3 = a1; \
0N/A res = CRES_MAX_CLIPPED; \
0N/A } else if (res == CRES_INVISIBLE) { \
0N/A if (a1 > LINE_MAX) { \
0N/A res = CRES_INVISIBLE; \
0N/A } else { \
0N/A a1 = (TYPE)LINE_MIN; \
0N/A a2 = (TYPE)LINE_MIN; \
0N/A res = CRES_NOT_CLIPPED; \
0N/A } \
0N/A } \
0N/A } while (0)
0N/A
0N/A/* Following macro is used for solving quadratic equations:
0N/A * A*t^2 + B*t + C = 0
0N/A * in (0,1) range. That means we put to the RES the only roots which
0N/A * belongs to the (0,1) range. Note: 0 and 1 are not included.
0N/A * See solveQuadratic method in
0N/A * src/share/classes/java/awt/geom/QuadCurve2D.java
0N/A * for more info about calculations
0N/A */
0N/A#define SOLVEQUADINRANGE(A,B,C,RES,RCNT) \
0N/A do { \
0N/A double param; \
0N/A if ((A) != 0) { \
0N/A /* Calculating roots of the following equation \
0N/A * A*t^2 + B*t + C = 0 \
0N/A */ \
0N/A double d = (B)*(B) - 4*(A)*(C); \
0N/A double q; \
0N/A if (d < 0) { \
0N/A break; \
0N/A } \
0N/A d = sqrt(d); \
0N/A /* For accuracy, calculate one root using: \
0N/A * (-B +/- d) / 2*A \
0N/A * and the other using: \
0N/A * 2*C / (-B +/- d) \
0N/A * Choose the sign of the +/- so that B+D gets larger \
0N/A * in magnitude \
0N/A */ \
0N/A if ((B) < 0) { \
0N/A d = -d; \
0N/A } \
0N/A q = ((B) + d) / -2.0; \
0N/A param = q/(A); \
0N/A if (param < 1.0 && param > 0.0) { \
0N/A (RES)[(RCNT)++] = param; \
0N/A } \
0N/A if (d == 0 || q == 0) { \
0N/A break; \
0N/A } \
0N/A param = (C)/q; \
0N/A if (param < 1.0 && param > 0.0) { \
0N/A (RES)[(RCNT)++] = param; \
0N/A } \
0N/A } else { \
0N/A /* Calculating root of the following equation \
0N/A * B*t + C = 0 \
0N/A */ \
0N/A if ((B) == 0) { \
0N/A break; \
0N/A } \
0N/A param = -(C)/(B); \
0N/A if (param < 1.0 && param > 0.0) { \
0N/A (RES)[(RCNT)++] = param; \
0N/A } \
0N/A } \
0N/A } while(0)
0N/A
0N/A/* Drawing line with subpixel endpoints
0N/A *
0N/A * (x1, y1), (x2, y2) - fixed point coordinates of the endpoints
0N/A * with MDP_PREC bits for the fractional part
0N/A *
0N/A * pixelInfo - structure which keeps drawing info for avoiding
0N/A * multiple drawing at the same position on the
0N/A * screen (required for the XOR mode of drawing)
0N/A *
0N/A * pixelInfo[0] - state of the drawing
0N/A * 0 - no pixel drawn between
0N/A * moveTo/close of the path
0N/A * 1 - there are drawn pixels
0N/A *
0N/A * pixelInfo[1,2] - first pixel of the path
0N/A * between moveTo/close of the
0N/A * path
0N/A *
0N/A * pixelInfo[3,4] - last drawn pixel between
0N/A * moveTo/close of the path
0N/A *
0N/A * checkBounds - flag showing necessity of checking the clip
0N/A *
0N/A */
0N/Avoid ProcessFixedLine(ProcessHandler* hnd,jint x1,jint y1,jint x2,jint y2,
0N/A jint* pixelInfo,jboolean checkBounds,
0N/A jboolean endSubPath)
0N/A{
0N/A /* Checking if line is inside a (X,Y),(X+MDP_MULT,Y+MDP_MULT) box */
0N/A jint c = ((x1 ^ x2) | (y1 ^ y2));
0N/A jint rx1, ry1, rx2, ry2;
0N/A if ((c & MDP_W_MASK) == 0) {
0N/A /* Checking for the segments with integer coordinates having
0N/A * the same start and end points
0N/A */
0N/A if (c == 0) {
0N/A PROCESS_POINT(hnd, x1 + MDP_HALF_MULT, y1 + MDP_HALF_MULT,
0N/A checkBounds, pixelInfo);
0N/A }
0N/A return;
0N/A }
0N/A
0N/A if (x1 == x2 || y1 == y2) {
0N/A rx1 = x1 + MDP_HALF_MULT;
0N/A rx2 = x2 + MDP_HALF_MULT;
0N/A ry1 = y1 + MDP_HALF_MULT;
0N/A ry2 = y2 + MDP_HALF_MULT;
0N/A } else {
0N/A /* Neither dx nor dy can be zero because of the check above */
0N/A jint dx = x2 - x1;
0N/A jint dy = y2 - y1;
0N/A
0N/A /* Floor of x1, y1, x2, y2 */
0N/A jint fx1 = x1 & MDP_W_MASK;
0N/A jint fy1 = y1 & MDP_W_MASK;
0N/A jint fx2 = x2 & MDP_W_MASK;
0N/A jint fy2 = y2 & MDP_W_MASK;
0N/A
0N/A /* Processing first endpoint */
0N/A if (fx1 == x1 || fy1 == y1) {
0N/A /* Adding MDP_HALF_MULT to the [xy]1 if f[xy]1 == [xy]1 will not
0N/A * affect the result
0N/A */
0N/A rx1 = x1 + MDP_HALF_MULT;
0N/A ry1 = y1 + MDP_HALF_MULT;
0N/A } else {
0N/A /* Boundary at the direction from (x1,y1) to (x2,y2) */
0N/A jint bx1 = (x1 < x2) ? fx1 + MDP_MULT : fx1;
0N/A jint by1 = (y1 < y2) ? fy1 + MDP_MULT : fy1;
0N/A
0N/A /* intersection with column bx1 */
0N/A jint cross = y1 + ((bx1 - x1)*dy)/dx;
0N/A if (cross >= fy1 && cross <= fy1 + MDP_MULT) {
0N/A rx1 = bx1;
0N/A ry1 = cross + MDP_HALF_MULT;
0N/A } else {
0N/A /* intersection with row by1 */
0N/A cross = x1 + ((by1 - y1)*dx)/dy;
0N/A rx1 = cross + MDP_HALF_MULT;
0N/A ry1 = by1;
0N/A }
0N/A }
0N/A
0N/A /* Processing second endpoint */
0N/A if (fx2 == x2 || fy2 == y2) {
0N/A /* Adding MDP_HALF_MULT to the [xy]2 if f[xy]2 == [xy]2 will not
0N/A * affect the result
0N/A */
0N/A rx2 = x2 + MDP_HALF_MULT;
0N/A ry2 = y2 + MDP_HALF_MULT;
0N/A } else {
0N/A /* Boundary at the direction from (x2,y2) to (x1,y1) */
0N/A jint bx2 = (x1 > x2) ? fx2 + MDP_MULT : fx2;
0N/A jint by2 = (y1 > y2) ? fy2 + MDP_MULT : fy2;
0N/A
0N/A /* intersection with column bx2 */
0N/A jint cross = y2 + ((bx2 - x2)*dy)/dx;
0N/A if (cross >= fy2 && cross <= fy2 + MDP_MULT) {
0N/A rx2 = bx2;
0N/A ry2 = cross + MDP_HALF_MULT;
0N/A } else {
0N/A /* intersection with row by2 */
0N/A cross = x2 + ((by2 - y2)*dx)/dy;
0N/A rx2 = cross + MDP_HALF_MULT;
0N/A ry2 = by2;
0N/A }
0N/A }
0N/A }
0N/A
0N/A PROCESS_LINE(hnd, rx1, ry1, rx2, ry2, checkBounds, pixelInfo);
0N/A}
0N/A
0N/A/* Performing drawing of the monotonic in X and Y quadratic curves with sizes
0N/A * less than MAX_QUAD_SIZE by using forward differencing method of calculation.
0N/A * See comments to the DrawMonotonicCubic.
0N/A */
0N/Astatic void DrawMonotonicQuad(ProcessHandler* hnd,
0N/A jfloat *coords,
0N/A jboolean checkBounds,
0N/A jint* pixelInfo)
0N/A{
0N/A jint x0 = (jint)(coords[0]*MDP_MULT);
0N/A jint y0 = (jint)(coords[1]*MDP_MULT);
0N/A
0N/A jint xe = (jint)(coords[4]*MDP_MULT);
0N/A jint ye = (jint)(coords[5]*MDP_MULT);
0N/A
0N/A /* Extracting fractional part of coordinates of first control point */
0N/A jint px = (x0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;
0N/A jint py = (y0 & (~MDP_W_MASK)) << DF_QUAD_SHIFT;
0N/A
0N/A /* Setting default amount of steps */
0N/A jint count = DF_QUAD_COUNT;
0N/A
0N/A /* Setting default shift for preparing to the midpoint rounding */
0N/A jint shift = DF_QUAD_SHIFT;
0N/A
0N/A jint ax = (jint)((coords[0] - 2*coords[2] +
0N/A coords[4])*QUAD_A_MDP_MULT);
0N/A jint ay = (jint)((coords[1] - 2*coords[3] +
0N/A coords[5])*QUAD_A_MDP_MULT);
0N/A
0N/A jint bx = (jint)((-2*coords[0] + 2*coords[2])*QUAD_B_MDP_MULT);
0N/A jint by = (jint)((-2*coords[1] + 2*coords[3])*QUAD_B_MDP_MULT);
0N/A
0N/A jint ddpx = 2*ax;
0N/A jint ddpy = 2*ay;
0N/A
0N/A jint dpx = ax + bx;
0N/A jint dpy = ay + by;
0N/A
0N/A jint x1, y1;
0N/A
0N/A jint x2 = x0;
0N/A jint y2 = y0;
0N/A
0N/A jint maxDD = MAX(ABS32(ddpx),ABS32(ddpy));
0N/A jint x0w = x0 & MDP_W_MASK;
0N/A jint y0w = y0 & MDP_W_MASK;
0N/A
0N/A jint dx = xe - x0;
0N/A jint dy = ye - y0;
0N/A
0N/A /* Perform decreasing step in 2 times if slope of the second forward
0N/A * difference changes too quickly (more than a pixel per step in X or Y
0N/A * direction). We can perform adjusting of the step size before the
0N/A * rendering loop because the curvature of the quad curve remains the same
0N/A * along all the curve
0N/A */
0N/A while (maxDD > DF_QUAD_DEC_BND) {
0N/A dpx = (dpx<<1) - ax;
0N/A dpy = (dpy<<1) - ay;
0N/A count <<= 1;
0N/A maxDD >>= 2;
0N/A px <<=2;
0N/A py <<=2;
0N/A shift += 2;
0N/A }
0N/A
0N/A while(count-- > 1) {
0N/A
0N/A px += dpx;
0N/A py += dpy;
0N/A
0N/A dpx += ddpx;
0N/A dpy += ddpy;
0N/A
0N/A x1 = x2;
0N/A y1 = y2;
0N/A
0N/A x2 = x0w + (px >> shift);
0N/A y2 = y0w + (py >> shift);
0N/A
0N/A /* Checking that we are not running out of the endpoint and bounding
0N/A * violating coordinate. The check is pretty simple because the curve
0N/A * passed to the DrawMonotonicQuad already splitted into the monotonic
0N/A * in X and Y pieces
0N/A */
0N/A
0N/A /* Bounding x2 by xe */
0N/A if (((xe-x2)^dx) < 0) {
0N/A x2 = xe;
0N/A }
0N/A
0N/A /* Bounding y2 by ye */
0N/A if (((ye-y2)^dy) < 0) {
0N/A y2 = ye;
0N/A }
0N/A
0N/A hnd->pProcessFixedLine(hnd, x1, y1, x2, y2, pixelInfo, checkBounds,
0N/A JNI_FALSE);
0N/A }
0N/A
0N/A /* We are performing one step less than necessary and use actual (xe,ye)
0N/A * curve's endpoint instead of calculated. This prevent us from accumulated
0N/A * errors at the last point.
0N/A */
0N/A
0N/A hnd->pProcessFixedLine(hnd, x2, y2, xe, ye, pixelInfo, checkBounds,
0N/A JNI_FALSE);
0N/A}
0N/A
0N/A/*
0N/A * Checking size of the quad curves and split them if necessary.
0N/A * Calling DrawMonotonicQuad for the curves of the appropriate size.
0N/A * Note: coords array could be changed
0N/A */
0N/Astatic void ProcessMonotonicQuad(ProcessHandler* hnd,
0N/A jfloat *coords,
0N/A jint* pixelInfo) {
0N/A
0N/A jfloat coords1[6];
0N/A jfloat xMin, xMax;
0N/A jfloat yMin, yMax;
0N/A
0N/A xMin = xMax = coords[0];
0N/A yMin = yMax = coords[1];
0N/A
0N/A CALC_MIN(xMin, coords[2]);
0N/A CALC_MAX(xMax, coords[2]);
0N/A CALC_MIN(yMin, coords[3]);
0N/A CALC_MAX(yMax, coords[3]);
0N/A CALC_MIN(xMin, coords[4]);
0N/A CALC_MAX(xMax, coords[4]);
0N/A CALC_MIN(yMin, coords[5]);
0N/A CALC_MAX(yMax, coords[5]);
0N/A
0N/A
0N/A if (hnd->clipMode == PH_MODE_DRAW_CLIP) {
0N/A
0N/A /* In case of drawing we could just skip curves which are completely
0N/A * out of bounds
0N/A */
0N/A if (hnd->dhnd->xMaxf < xMin || hnd->dhnd->xMinf > xMax ||
0N/A hnd->dhnd->yMaxf < yMin || hnd->dhnd->yMinf > yMax) {
0N/A return;
0N/A }
0N/A } else {
0N/A
0N/A /* In case of filling we could skip curves which are above,
0N/A * below and behind the right boundary of the visible area
0N/A */
0N/A
0N/A if (hnd->dhnd->yMaxf < yMin || hnd->dhnd->yMinf > yMax ||
0N/A hnd->dhnd->xMaxf < xMin)
0N/A {
0N/A return;
0N/A }
0N/A
0N/A /* We could clamp x coordinates to the corresponding boundary
0N/A * if the curve is completely behind the left one
0N/A */
0N/A
0N/A if (hnd->dhnd->xMinf > xMax) {
0N/A coords[0] = coords[2] = coords[4] = hnd->dhnd->xMinf;
0N/A }
0N/A }
0N/A
0N/A if (xMax - xMin > MAX_QUAD_SIZE || yMax - yMin > MAX_QUAD_SIZE) {
0N/A coords1[4] = coords[4];
0N/A coords1[5] = coords[5];
0N/A coords1[2] = (coords[2] + coords[4])/2.0f;
0N/A coords1[3] = (coords[3] + coords[5])/2.0f;
0N/A coords[2] = (coords[0] + coords[2])/2.0f;
0N/A coords[3] = (coords[1] + coords[3])/2.0f;
0N/A coords[4] = coords1[0] = (coords[2] + coords1[2])/2.0f;
0N/A coords[5] = coords1[1] = (coords[3] + coords1[3])/2.0f;
0N/A
0N/A ProcessMonotonicQuad(hnd, coords, pixelInfo);
0N/A
0N/A ProcessMonotonicQuad(hnd, coords1, pixelInfo);
0N/A } else {
0N/A DrawMonotonicQuad(hnd, coords,
0N/A /* Set checkBounds parameter if curve intersects
0N/A * boundary of the visible area. We know that the
0N/A * curve is visible, so the check is pretty simple
0N/A */
0N/A hnd->dhnd->xMinf >= xMin || hnd->dhnd->xMaxf <= xMax ||
0N/A hnd->dhnd->yMinf >= yMin || hnd->dhnd->yMaxf <= yMax,
0N/A pixelInfo);
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Bite the piece of the quadratic curve from start point till the point
0N/A * corresponding to the specified parameter then call ProcessQuad for the
0N/A * bitten part.
0N/A * Note: coords array will be changed
0N/A */
0N/Astatic void ProcessFirstMonotonicPartOfQuad(ProcessHandler* hnd, jfloat* coords,
0N/A jint* pixelInfo, jfloat t)
0N/A{
0N/A jfloat coords1[6];
0N/A
0N/A coords1[0] = coords[0];
0N/A coords1[1] = coords[1];
0N/A coords1[2] = coords[0] + t*(coords[2] - coords[0]);
0N/A coords1[3] = coords[1] + t*(coords[3] - coords[1]);
0N/A coords[2] = coords[2] + t*(coords[4] - coords[2]);
0N/A coords[3] = coords[3] + t*(coords[5] - coords[3]);
0N/A coords[0] = coords1[4] = coords1[2] + t*(coords[2] - coords1[2]);
0N/A coords[1] = coords1[5] = coords1[3] + t*(coords[3] - coords1[3]);
0N/A
0N/A ProcessMonotonicQuad(hnd, coords1, pixelInfo);
0N/A}
0N/A
0N/A/*
0N/A * Split quadratic curve into monotonic in X and Y parts. Calling
0N/A * ProcessMonotonicQuad for each monotonic piece of the curve.
0N/A * Note: coords array could be changed
0N/A */
0N/Astatic void ProcessQuad(ProcessHandler* hnd, jfloat* coords, jint* pixelInfo) {
0N/A
0N/A /* Temporary array for holding parameters corresponding to the extreme in X
0N/A * and Y points. The values are inside the (0,1) range (0 and 1 excluded)
0N/A * and in ascending order.
0N/A */
0N/A double params[2];
0N/A
0N/A jint cnt = 0;
0N/A double param;
0N/A
0N/A /* Simple check for monotonicity in X before searching for the extreme
0N/A * points of the X(t) function. We first check if the curve is monotonic
0N/A * in X by seeing if all of the X coordinates are strongly ordered.
0N/A */
0N/A if ((coords[0] > coords[2] || coords[2] > coords[4]) &&
0N/A (coords[0] < coords[2] || coords[2] < coords[4]))
0N/A {
0N/A /* Searching for extreme points of the X(t) function by solving
0N/A * dX(t)
0N/A * ---- = 0 equation
0N/A * dt
0N/A */
0N/A double ax = coords[0] - 2*coords[2] + coords[4];
0N/A if (ax != 0) {
0N/A /* Calculating root of the following equation
0N/A * ax*t + bx = 0
0N/A */
0N/A double bx = coords[0] - coords[2];
0N/A
0N/A param = bx/ax;
0N/A if (param < 1.0 && param > 0.0) {
0N/A params[cnt++] = param;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /* Simple check for monotonicity in Y before searching for the extreme
0N/A * points of the Y(t) function. We first check if the curve is monotonic
0N/A * in Y by seeing if all of the Y coordinates are strongly ordered.
0N/A */
0N/A if ((coords[1] > coords[3] || coords[3] > coords[5]) &&
0N/A (coords[1] < coords[3] || coords[3] < coords[5]))
0N/A {
0N/A /* Searching for extreme points of the Y(t) function by solving
0N/A * dY(t)
0N/A * ----- = 0 equation
0N/A * dt
0N/A */
0N/A double ay = coords[1] - 2*coords[3] + coords[5];
0N/A
0N/A if (ay != 0) {
0N/A /* Calculating root of the following equation
0N/A * ay*t + by = 0
0N/A */
0N/A double by = coords[1] - coords[3];
0N/A
0N/A param = by/ay;
0N/A if (param < 1.0 && param > 0.0) {
0N/A if (cnt > 0) {
0N/A /* Inserting parameter only if it differs from
0N/A * already stored
0N/A */
0N/A if (params[0] > param) {
0N/A params[cnt++] = params[0];
0N/A params[0] = param;
0N/A } else if (params[0] < param) {
0N/A params[cnt++] = param;
0N/A }
0N/A } else {
0N/A params[cnt++] = param;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A /* Processing obtained monotonic parts */
0N/A switch(cnt) {
0N/A case 0:
0N/A break;
0N/A case 1:
0N/A ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
0N/A (jfloat)params[0]);
0N/A break;
0N/A case 2:
0N/A ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
0N/A (jfloat)params[0]);
0N/A param = params[1] - params[0];
0N/A if (param > 0) {
0N/A ProcessFirstMonotonicPartOfQuad(hnd, coords, pixelInfo,
0N/A /* Scale parameter to match with rest of the curve */
0N/A (jfloat)(param/(1.0 - params[0])));
0N/A }
0N/A break;
0N/A }
0N/A
0N/A ProcessMonotonicQuad(hnd,coords,pixelInfo);
0N/A}
0N/A
0N/A/*
0N/A * Performing drawing of the monotonic in X and Y cubic curves with sizes less
0N/A * than MAX_CUB_SIZE by using forward differencing method of calculation.
0N/A *
0N/A * Here is some math used in the code below.
0N/A *
0N/A * If we express the parametric equation for the coordinates as
0N/A * simple polynomial:
0N/A *
0N/A * V(t) = a * t^3 + b * t^2 + c * t + d
0N/A *
0N/A * The equations for how we derive these polynomial coefficients
0N/A * from the Bezier control points can be found in the method comments
0N/A * for the CubicCurve.fillEqn Java method.
0N/A *
0N/A * From this polynomial, we can derive the forward differences to
0N/A * allow us to calculate V(t+K) from V(t) as follows:
0N/A *
0N/A * 1) V1(0)
0N/A * = V(K)-V(0)
0N/A * = aK^3 + bK^2 + cK + d - d
0N/A * = aK^3 + bK^2 + cK
0N/A *
0N/A * 2) V1(K)
0N/A * = V(2K)-V(K)
0N/A * = 8aK^3 + 4bK^2 + 2cK + d - aK^3 - bK^2 - cK - d
0N/A * = 7aK^3 + 3bK^2 + cK
0N/A *
0N/A * 3) V1(2K)
0N/A * = V(3K)-V(2K)
0N/A * = 27aK^3 + 9bK^2 + 3cK + d - 8aK^3 - 4bK^2 - 2cK - d
0N/A * = 19aK^3 + 5bK^2 + cK
0N/A *
0N/A * 4) V2(0)
0N/A * = V1(K) - V1(0)
0N/A * = 7aK^3 + 3bK^2 + cK - aK^3 - bK^2 - cK
0N/A * = 6aK^3 + 2bK^2
0N/A *
0N/A * 5) V2(K)
0N/A * = V1(2K) - V1(K)
0N/A * = 19aK^3 + 5bK^2 + cK - 7aK^3 - 3bK^2 - cK
0N/A * = 12aK^3 + 2bK^2
0N/A *
0N/A * 6) V3(0)
0N/A * = V2(K) - V2(0)
0N/A * = 12aK^3 + 2bK^2 - 6aK^3 - 2bK^2
0N/A * = 6aK^3
0N/A *
0N/A * Note that if we continue on to calculate V1(3K), V2(2K) and
0N/A * V3(K) we will see that V3(K) == V3(0) so we need at most
0N/A * 3 cascading forward differences to step through the cubic
0N/A * curve.
0N/A *
0N/A * Note, b coefficient calculating in the DrawCubic is actually twice the b
0N/A * coefficient seen above. It's been done for the better accuracy.
0N/A *
0N/A * In our case, initialy K is chosen as 1/(2^DF_CUB_STEPS) this value is taken
0N/A * with FWD_PREC bits precision. This means that we should do 2^DF_CUB_STEPS
0N/A * steps to pass through all the curve.
0N/A *
0N/A * On each step we examine how far we are stepping by examining our first(V1)
0N/A * and second (V2) order derivatives and verifying that they are met following
0N/A * conditions:
0N/A *
0N/A * abs(V2) <= DF_CUB_DEC_BND
0N/A * abs(V1) > DF_CUB_INC_BND
0N/A *
0N/A * So, ensures that we step through the curve more slowly when its curvature is
0N/A * high and faster when its curvature is lower. If the step size needs
0N/A * adjustment we adjust it so that we step either twice as fast, or twice as
0N/A * slow until our step size is within range. This modifies our stepping
0N/A * variables as follows:
0N/A *
0N/A * Decreasing step size
0N/A * (See Graphics Gems/by A.Glassner,(Tutorial on forward differencing),601-602)
0N/A *
0N/A * V3 = oV3/8
0N/A * V2 = oV2/4 - V3
0N/A * V1 = (oV1 - V2)/2
0N/A *
0N/A * Here V1-V3 stands for new values of the forward differencies and oV1 - oV3
0N/A * for the old ones
0N/A *
0N/A * Using the equations above it's easy to calculating stepping variables for
0N/A * the increasing step size:
0N/A *
0N/A * V1 = 2*oV1 + oV2
0N/A * V2 = 4*oV2 + 4*oV3
0N/A * V3 = 8*oV3
0N/A *
0N/A * And then for not to running out of 32 bit precision we are performing 3 bit
0N/A * shift of the forward differencing precision (keeping in shift variable) in
0N/A * left or right direction depending on what is happening (decreasing or
0N/A * increasing). So, all oV1 - oV3 variables should be thought as appropriately
0N/A * shifted in regard to the V1 - V3.
0N/A *
0N/A * Taking all of the above into account we will have following:
0N/A *
0N/A * Decreasing step size:
0N/A *
0N/A * shift = shift + 3
0N/A * V3 keeps the same
0N/A * V2 = 2*oV2 - V3
0N/A * V1 = 4*oV1 - V2/2
0N/A *
0N/A * Increasing step size:
0N/A *
0N/A * shift = shift - 3
0N/A * V1 = oV1/4 + oV2/8
0N/A * V2 = oV2/2 + oV3/2
0N/A * V3 keeps the same
0N/A *
0N/A */
0N/A
0N/Astatic void DrawMonotonicCubic(ProcessHandler* hnd,
0N/A jfloat *coords,
0N/A jboolean checkBounds,
0N/A jint* pixelInfo)
0N/A{
0N/A jint x0 = (jint)(coords[0]*MDP_MULT);
0N/A jint y0 = (jint)(coords[1]*MDP_MULT);
0N/A
0N/A jint xe = (jint)(coords[6]*MDP_MULT);
0N/A jint ye = (jint)(coords[7]*MDP_MULT);
0N/A
0N/A /* Extracting fractional part of coordinates of first control point */
0N/A jint px = (x0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;
0N/A jint py = (y0 & (~MDP_W_MASK)) << DF_CUB_SHIFT;
0N/A
0N/A /* Setting default boundary values for checking first and second forward
0N/A * difference for the necessity of the restepping. See comments to the
0N/A * boundary values in ProcessQuad for more info.
0N/A */
0N/A jint incStepBnd1 = DF_CUB_INC_BND;
0N/A jint incStepBnd2 = DF_CUB_INC_BND << 1;
0N/A jint decStepBnd1 = DF_CUB_DEC_BND;
0N/A jint decStepBnd2 = DF_CUB_DEC_BND << 1;
0N/A
0N/A /* Setting default amount of steps */
0N/A jint count = DF_CUB_COUNT;
0N/A
0N/A /* Setting default shift for preparing to the midpoint rounding */
0N/A jint shift = DF_CUB_SHIFT;
0N/A
0N/A jint ax = (jint)((-coords[0] + 3*coords[2] - 3*coords[4] +
0N/A coords[6])*CUB_A_MDP_MULT);
0N/A jint ay = (jint)((-coords[1] + 3*coords[3] - 3*coords[5] +
0N/A coords[7])*CUB_A_MDP_MULT);
0N/A
0N/A jint bx = (jint)((3*coords[0] - 6*coords[2] +
0N/A 3*coords[4])*CUB_B_MDP_MULT);
0N/A jint by = (jint)((3*coords[1] - 6*coords[3] +
0N/A 3*coords[5])*CUB_B_MDP_MULT);
0N/A
0N/A jint cx = (jint)((-3*coords[0] + 3*coords[2])*(CUB_C_MDP_MULT));
0N/A jint cy = (jint)((-3*coords[1] + 3*coords[3])*(CUB_C_MDP_MULT));
0N/A
0N/A jint dddpx = 6*ax;
0N/A jint dddpy = 6*ay;
0N/A
0N/A jint ddpx = dddpx + bx;
0N/A jint ddpy = dddpy + by;
0N/A
0N/A jint dpx = ax + (bx>>1) + cx;
0N/A jint dpy = ay + (by>>1) + cy;
0N/A
0N/A jint x1, y1;
0N/A
0N/A jint x2 = x0;
0N/A jint y2 = y0;
0N/A
0N/A /* Calculating whole part of the first point of the curve */
0N/A jint x0w = x0 & MDP_W_MASK;
0N/A jint y0w = y0 & MDP_W_MASK;
0N/A
0N/A jint dx = xe - x0;
0N/A jint dy = ye - y0;
0N/A
0N/A while (count > 0) {
0N/A /* Perform decreasing step in 2 times if necessary */
0N/A while (
0N/A /* The code below is an optimized version of the checks:
0N/A * abs(ddpx) > decStepBnd1 ||
0N/A * abs(ddpy) > decStepBnd1
0N/A */
0N/A (juint)(ddpx + decStepBnd1) > (juint)decStepBnd2 ||
0N/A (juint)(ddpy + decStepBnd1) > (juint)decStepBnd2)
0N/A {
0N/A ddpx = (ddpx<<1) - dddpx;
0N/A ddpy = (ddpy<<1) - dddpy;
0N/A dpx = (dpx<<2) - (ddpx>>1);
0N/A dpy = (dpy<<2) - (ddpy>>1);
0N/A count <<=1;
0N/A decStepBnd1 <<=3;
0N/A decStepBnd2 <<=3;
0N/A incStepBnd1 <<=3;
0N/A incStepBnd2 <<=3;
0N/A px <<=3;
0N/A py <<=3;
0N/A shift += 3;
0N/A }
0N/A
0N/A /* Perform increasing step in 2 times if necessary.
0N/A * Note: we could do it only in even steps
0N/A */
0N/A
0N/A while (((count & 1) ^ 1) && shift > DF_CUB_SHIFT &&
0N/A /* The code below is an optimized version of the check:
0N/A * abs(dpx) <= incStepBnd1 &&
0N/A * abs(dpy) <= incStepBnd1
0N/A */
0N/A (juint)(dpx + incStepBnd1) <= (juint)incStepBnd2 &&
0N/A (juint)(dpy + incStepBnd1) <= (juint)incStepBnd2)
0N/A {
0N/A dpx = (dpx>>2) + (ddpx>>3);
0N/A dpy = (dpy>>2) + (ddpy>>3);
0N/A ddpx = (ddpx + dddpx)>>1;
0N/A ddpy = (ddpy + dddpy)>>1;
0N/A count >>=1;
0N/A decStepBnd1 >>=3;
0N/A decStepBnd2 >>=3;
0N/A incStepBnd1 >>=3;
0N/A incStepBnd2 >>=3;
0N/A px >>=3;
0N/A py >>=3;
0N/A shift -= 3;
0N/A }
0N/A
0N/A count--;
0N/A
0N/A /* We are performing one step less than necessary and use actual
0N/A * (xe,ye) endpoint of the curve instead of calculated. This prevent
0N/A * us from accumulated errors at the last point.
0N/A */
0N/A if (count) {
0N/A
0N/A px += dpx;
0N/A py += dpy;
0N/A
0N/A dpx += ddpx;
0N/A dpy += ddpy;
0N/A ddpx += dddpx;
0N/A ddpy += dddpy;
0N/A
0N/A x1 = x2;
0N/A y1 = y2;
0N/A
0N/A x2 = x0w + (px >> shift);
0N/A y2 = y0w + (py >> shift);
0N/A
0N/A /* Checking that we are not running out of the endpoint and
0N/A * bounding violating coordinate. The check is pretty simple
0N/A * because the curve passed to the DrawMonotonicCubic already
0N/A * splitted into the monotonic in X and Y pieces
0N/A */
0N/A
0N/A /* Bounding x2 by xe */
0N/A if (((xe-x2)^dx) < 0) {
0N/A x2 = xe;
0N/A }
0N/A
0N/A /* Bounding y2 by ye */
0N/A if (((ye-y2)^dy) < 0) {
0N/A y2 = ye;
0N/A }
0N/A
0N/A hnd->pProcessFixedLine(hnd, x1, y1, x2, y2, pixelInfo, checkBounds,
0N/A JNI_FALSE);
0N/A } else {
0N/A hnd->pProcessFixedLine(hnd, x2, y2, xe, ye, pixelInfo, checkBounds,
0N/A JNI_FALSE);
0N/A }
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Checking size of the cubic curves and split them if necessary.
0N/A * Calling DrawMonotonicCubic for the curves of the appropriate size.
0N/A * Note: coords array could be changed
0N/A */
0N/Astatic void ProcessMonotonicCubic(ProcessHandler* hnd,
0N/A jfloat *coords,
0N/A jint* pixelInfo) {
0N/A
0N/A jfloat coords1[8];
0N/A jfloat tx, ty;
0N/A jfloat xMin, xMax;
0N/A jfloat yMin, yMax;
0N/A
0N/A xMin = xMax = coords[0];
0N/A yMin = yMax = coords[1];
0N/A
0N/A CALC_MIN(xMin, coords[2]);
0N/A CALC_MAX(xMax, coords[2]);
0N/A CALC_MIN(yMin, coords[3]);
0N/A CALC_MAX(yMax, coords[3]);
0N/A CALC_MIN(xMin, coords[4]);
0N/A CALC_MAX(xMax, coords[4]);
0N/A CALC_MIN(yMin, coords[5]);
0N/A CALC_MAX(yMax, coords[5]);
0N/A CALC_MIN(xMin, coords[6]);
0N/A CALC_MAX(xMax, coords[6]);
0N/A CALC_MIN(yMin, coords[7]);
0N/A CALC_MAX(yMax, coords[7]);
0N/A
0N/A if (hnd->clipMode == PH_MODE_DRAW_CLIP) {
0N/A
0N/A /* In case of drawing we could just skip curves which are completely
0N/A * out of bounds
0N/A */
0N/A if (hnd->dhnd->xMaxf < xMin || hnd->dhnd->xMinf > xMax ||
0N/A hnd->dhnd->yMaxf < yMin || hnd->dhnd->yMinf > yMax) {
0N/A return;
0N/A }
0N/A } else {
0N/A
0N/A /* In case of filling we could skip curves which are above,
0N/A * below and behind the right boundary of the visible area
0N/A */
0N/A
0N/A if (hnd->dhnd->yMaxf < yMin || hnd->dhnd->yMinf > yMax ||
0N/A hnd->dhnd->xMaxf < xMin)
0N/A {
0N/A return;
0N/A }
0N/A
0N/A /* We could clamp x coordinates to the corresponding boundary
0N/A * if the curve is completely behind the left one
0N/A */
0N/A
0N/A if (hnd->dhnd->xMinf > xMax) {
0N/A coords[0] = coords[2] = coords[4] = coords[6] =
0N/A hnd->dhnd->xMinf;
0N/A }
0N/A }
0N/A
0N/A if (xMax - xMin > MAX_CUB_SIZE || yMax - yMin > MAX_CUB_SIZE) {
0N/A coords1[6] = coords[6];
0N/A coords1[7] = coords[7];
0N/A coords1[4] = (coords[4] + coords[6])/2.0f;
0N/A coords1[5] = (coords[5] + coords[7])/2.0f;
0N/A tx = (coords[2] + coords[4])/2.0f;
0N/A ty = (coords[3] + coords[5])/2.0f;
0N/A coords1[2] = (tx + coords1[4])/2.0f;
0N/A coords1[3] = (ty + coords1[5])/2.0f;
0N/A coords[2] = (coords[0] + coords[2])/2.0f;
0N/A coords[3] = (coords[1] + coords[3])/2.0f;
0N/A coords[4] = (coords[2] + tx)/2.0f;
0N/A coords[5] = (coords[3] + ty)/2.0f;
0N/A coords[6]=coords1[0]=(coords[4] + coords1[2])/2.0f;
0N/A coords[7]=coords1[1]=(coords[5] + coords1[3])/2.0f;
0N/A
0N/A ProcessMonotonicCubic(hnd, coords, pixelInfo);
0N/A
0N/A ProcessMonotonicCubic(hnd, coords1, pixelInfo);
0N/A
0N/A } else {
0N/A DrawMonotonicCubic(hnd, coords,
0N/A /* Set checkBounds parameter if curve intersects
0N/A * boundary of the visible area. We know that the
0N/A * curve is visible, so the check is pretty simple
0N/A */
0N/A hnd->dhnd->xMinf > xMin || hnd->dhnd->xMaxf < xMax ||
0N/A hnd->dhnd->yMinf > yMin || hnd->dhnd->yMaxf < yMax,
0N/A pixelInfo);
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Bite the piece of the cubic curve from start point till the point
0N/A * corresponding to the specified parameter then call ProcessMonotonicCubic for
0N/A * the bitten part.
0N/A * Note: coords array will be changed
0N/A */
0N/Astatic void ProcessFirstMonotonicPartOfCubic(ProcessHandler* hnd,
0N/A jfloat* coords, jint* pixelInfo,
0N/A jfloat t)
0N/A{
0N/A jfloat coords1[8];
0N/A jfloat tx, ty;
0N/A
0N/A coords1[0] = coords[0];
0N/A coords1[1] = coords[1];
0N/A tx = coords[2] + t*(coords[4] - coords[2]);
0N/A ty = coords[3] + t*(coords[5] - coords[3]);
0N/A coords1[2] = coords[0] + t*(coords[2] - coords[0]);
0N/A coords1[3] = coords[1] + t*(coords[3] - coords[1]);
0N/A coords1[4] = coords1[2] + t*(tx - coords1[2]);
0N/A coords1[5] = coords1[3] + t*(ty - coords1[3]);
0N/A coords[4] = coords[4] + t*(coords[6] - coords[4]);
0N/A coords[5] = coords[5] + t*(coords[7] - coords[5]);
0N/A coords[2] = tx + t*(coords[4] - tx);
0N/A coords[3] = ty + t*(coords[5] - ty);
0N/A coords[0]=coords1[6]=coords1[4] + t*(coords[2] - coords1[4]);
0N/A coords[1]=coords1[7]=coords1[5] + t*(coords[3] - coords1[5]);
0N/A
0N/A ProcessMonotonicCubic(hnd, coords1, pixelInfo);
0N/A}
0N/A
0N/A/*
0N/A * Split cubic curve into monotonic in X and Y parts. Calling ProcessCubic for
0N/A * each monotonic piece of the curve.
0N/A *
0N/A * Note: coords array could be changed
0N/A */
0N/Astatic void ProcessCubic(ProcessHandler* hnd, jfloat* coords, jint* pixelInfo)
0N/A{
0N/A /* Temporary array for holding parameters corresponding to the extreme in X
0N/A * and Y points. The values are inside the (0,1) range (0 and 1 excluded)
0N/A * and in ascending order.
0N/A */
0N/A double params[4];
0N/A jint cnt = 0, i;
0N/A
0N/A /* Simple check for monotonicity in X before searching for the extreme
0N/A * points of the X(t) function. We first check if the curve is monotonic in
0N/A * X by seeing if all of the X coordinates are strongly ordered.
0N/A */
0N/A if ((coords[0] > coords[2] || coords[2] > coords[4] ||
0N/A coords[4] > coords[6]) &&
0N/A (coords[0] < coords[2] || coords[2] < coords[4] ||
0N/A coords[4] < coords[6]))
0N/A {
0N/A /* Searching for extreme points of the X(t) function by solving
0N/A * dX(t)
0N/A * ---- = 0 equation
0N/A * dt
0N/A */
0N/A double ax = -coords[0] + 3*coords[2] - 3*coords[4] + coords[6];
0N/A double bx = 2*(coords[0] - 2*coords[2] + coords[4]);
0N/A double cx = -coords[0] + coords[2];
0N/A
0N/A SOLVEQUADINRANGE(ax,bx,cx,params,cnt);
0N/A }
0N/A
0N/A /* Simple check for monotonicity in Y before searching for the extreme
0N/A * points of the Y(t) function. We first check if the curve is monotonic in
0N/A * Y by seeing if all of the Y coordinates are strongly ordered.
0N/A */
0N/A if ((coords[1] > coords[3] || coords[3] > coords[5] ||
0N/A coords[5] > coords[7]) &&
0N/A (coords[1] < coords[3] || coords[3] < coords[5] ||
0N/A coords[5] < coords[7]))
0N/A {
0N/A /* Searching for extreme points of the Y(t) function by solving
0N/A * dY(t)
0N/A * ----- = 0 equation
0N/A * dt
0N/A */
0N/A double ay = -coords[1] + 3*coords[3] - 3*coords[5] + coords[7];
0N/A double by = 2*(coords[1] - 2*coords[3] + coords[5]);
0N/A double cy = -coords[1] + coords[3];
0N/A
0N/A SOLVEQUADINRANGE(ay,by,cy,params,cnt);
0N/A }
0N/A
0N/A if (cnt > 0) {
0N/A /* Sorting parameter values corresponding to the extremum points of
0N/A * the curve. We are using insertion sort because of tiny size of the
0N/A * array.
0N/A */
0N/A jint j;
0N/A
0N/A for(i = 1; i < cnt; i++) {
0N/A double value = params[i];
0N/A for (j = i - 1; j >= 0 && params[j] > value; j--) {
0N/A params[j + 1] = params[j];
0N/A }
0N/A params[j + 1] = value;
0N/A }
0N/A
0N/A /* Processing obtained monotonic parts */
0N/A ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,
0N/A (jfloat)params[0]);
0N/A for (i = 1; i < cnt; i++) {
0N/A double param = params[i] - params[i-1];
0N/A if (param > 0) {
0N/A ProcessFirstMonotonicPartOfCubic(hnd, coords, pixelInfo,
0N/A /* Scale parameter to match with rest of the curve */
0N/A (float)(param/(1.0 - params[i - 1])));
0N/A }
0N/A }
0N/A }
0N/A
0N/A ProcessMonotonicCubic(hnd,coords,pixelInfo);
0N/A}
0N/A
0N/Astatic void ProcessLine(ProcessHandler* hnd,
0N/A jfloat *coord1, jfloat *coord2, jint* pixelInfo) {
0N/A
0N/A jfloat xMin, yMin, xMax, yMax;
0N/A jint X1, Y1, X2, Y2, X3, Y3, res;
0N/A jboolean clipped = JNI_FALSE;
0N/A jfloat x1 = coord1[0];
0N/A jfloat y1 = coord1[1];
0N/A jfloat x2 = coord2[0];
0N/A jfloat y2 = coord2[1];
0N/A jfloat x3,y3;
0N/A
0N/A jboolean lastClipped;
0N/A
0N/A xMin = hnd->dhnd->xMinf;
0N/A yMin = hnd->dhnd->yMinf;
0N/A xMax = hnd->dhnd->xMaxf;
0N/A yMax = hnd->dhnd->yMaxf;
0N/A
0N/A TESTANDCLIP(yMin, yMax, y1, x1, y2, x2, jfloat, res);
0N/A if (res == CRES_INVISIBLE) return;
0N/A clipped = IS_CLIPPED(res);
0N/A TESTANDCLIP(yMin, yMax, y2, x2, y1, x1, jfloat, res);
0N/A if (res == CRES_INVISIBLE) return;
0N/A lastClipped = IS_CLIPPED(res);
0N/A clipped = clipped || lastClipped;
0N/A
0N/A if (hnd->clipMode == PH_MODE_DRAW_CLIP) {
0N/A TESTANDCLIP(xMin, xMax,
0N/A x1, y1, x2, y2, jfloat, res);
0N/A if (res == CRES_INVISIBLE) return;
0N/A clipped = clipped || IS_CLIPPED(res);
0N/A TESTANDCLIP(xMin, xMax,
0N/A x2, y2, x1, y1, jfloat, res);
0N/A if (res == CRES_INVISIBLE) return;
0N/A lastClipped = lastClipped || IS_CLIPPED(res);
0N/A clipped = clipped || lastClipped;
0N/A X1 = (jint)(x1*MDP_MULT);
0N/A Y1 = (jint)(y1*MDP_MULT);
0N/A X2 = (jint)(x2*MDP_MULT);
0N/A Y2 = (jint)(y2*MDP_MULT);
0N/A
0N/A hnd->pProcessFixedLine(hnd, X1, Y1, X2, Y2, pixelInfo,
0N/A clipped, /* enable boundary checking in case
0N/A of clipping to avoid entering
0N/A out of bounds which could
0N/A happens during rounding
0N/A */
0N/A lastClipped /* Notify pProcessFixedLine that
0N/A this is the end of the
0N/A subpath (because of exiting
0N/A out of boundaries)
0N/A */
0N/A );
0N/A } else {
0N/A /* Clamping starting from first vertex of the the processed segment
0N/A */
0N/A CLIPCLAMP(xMin, xMax, x1, y1, x2, y2, x3, y3, jfloat, res);
0N/A X1 = (jint)(x1*MDP_MULT);
0N/A Y1 = (jint)(y1*MDP_MULT);
0N/A
0N/A /* Clamping only by left boundary */
0N/A if (res == CRES_MIN_CLIPPED) {
0N/A X3 = (jint)(x3*MDP_MULT);
0N/A Y3 = (jint)(y3*MDP_MULT);
0N/A hnd->pProcessFixedLine(hnd, X3, Y3, X1, Y1, pixelInfo,
0N/A JNI_FALSE, lastClipped);
0N/A
0N/A } else if (res == CRES_INVISIBLE) {
0N/A return;
0N/A }
0N/A
0N/A /* Clamping starting from last vertex of the the processed segment
0N/A */
0N/A CLIPCLAMP(xMin, xMax, x2, y2, x1, y1, x3, y3, jfloat, res);
0N/A
0N/A /* Checking if there was a clip by right boundary */
0N/A lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);
0N/A
0N/A X2 = (jint)(x2*MDP_MULT);
0N/A Y2 = (jint)(y2*MDP_MULT);
0N/A hnd->pProcessFixedLine(hnd, X1, Y1, X2, Y2, pixelInfo,
0N/A JNI_FALSE, lastClipped);
0N/A
0N/A /* Clamping only by left boundary */
0N/A if (res == CRES_MIN_CLIPPED) {
0N/A X3 = (jint)(x3*MDP_MULT);
0N/A Y3 = (jint)(y3*MDP_MULT);
0N/A hnd->pProcessFixedLine(hnd, X2, Y2, X3, Y3, pixelInfo,
0N/A JNI_FALSE, lastClipped);
0N/A }
0N/A }
0N/A}
0N/A
0N/Ajboolean ProcessPath(ProcessHandler* hnd,
0N/A jfloat transXf, jfloat transYf,
0N/A jfloat* coords, jint maxCoords,
0N/A jbyte* types, jint numTypes)
0N/A{
0N/A jfloat tCoords[8];
0N/A jfloat closeCoord[2];
0N/A jint pixelInfo[5];
0N/A jboolean skip = JNI_FALSE;
0N/A jboolean subpathStarted = JNI_FALSE;
0N/A jfloat lastX, lastY;
0N/A int i, index = 0;
0N/A
0N/A pixelInfo[0] = 0;
0N/A
0N/A /* Adding support of the KEY_STROKE_CONTROL rendering hint.
0N/A * Now we are supporting two modes: "pixels at centers" and
0N/A * "pixels at corners".
0N/A * First one is disabled by default but could be enabled by setting
0N/A * VALUE_STROKE_PURE to the rendering hint. It means that pixel at the
0N/A * screen (x,y) has (x + 0.5, y + 0.5) float coordinates.
0N/A *
0N/A * Second one is enabled by default and means straightforward mapping
0N/A * (x,y) --> (x,y)
0N/A *
0N/A */
0N/A if (hnd->stroke == PH_STROKE_PURE) {
0N/A closeCoord[0] = -0.5f;
0N/A closeCoord[1] = -0.5f;
0N/A transXf -= 0.5;
0N/A transYf -= 0.5;
0N/A } else {
0N/A closeCoord[0] = 0.0f;
0N/A closeCoord[1] = 0.0f;
0N/A }
0N/A
0N/A /* Adjusting boundaries to the capabilities of the ProcessPath code */
0N/A ADJUST(hnd->dhnd->xMin, LOWER_OUT_BND, UPPER_OUT_BND);
0N/A ADJUST(hnd->dhnd->yMin, LOWER_OUT_BND, UPPER_OUT_BND);
0N/A ADJUST(hnd->dhnd->xMax, LOWER_OUT_BND, UPPER_OUT_BND);
0N/A ADJUST(hnd->dhnd->yMax, LOWER_OUT_BND, UPPER_OUT_BND);
0N/A
0N/A
0N/A /* Setting up fractional clipping box
0N/A *
0N/A * We are using following float -> int mapping:
0N/A *
0N/A * xi = floor(xf + 0.5)
0N/A *
0N/A * So, fractional values that hit the [xmin, xmax) integer interval will be
0N/A * situated inside the [xmin-0.5, xmax - 0.5) fractional interval. We are
0N/A * using EPSF constant to provide that upper boundary is not included.
0N/A */
0N/A hnd->dhnd->xMinf = hnd->dhnd->xMin - 0.5f;
0N/A hnd->dhnd->yMinf = hnd->dhnd->yMin - 0.5f;
0N/A hnd->dhnd->xMaxf = hnd->dhnd->xMax - 0.5f - EPSF;
0N/A hnd->dhnd->yMaxf = hnd->dhnd->yMax - 0.5f - EPSF;
0N/A
0N/A
0N/A for (i = 0; i < numTypes; i++) {
0N/A switch (types[i]) {
0N/A case java_awt_geom_PathIterator_SEG_MOVETO:
0N/A if (index + 2 <= maxCoords) {
0N/A /* Performing closing of the unclosed segments */
0N/A if (subpathStarted & !skip) {
0N/A if (hnd->clipMode == PH_MODE_FILL_CLIP) {
0N/A if (tCoords[0] != closeCoord[0] ||
0N/A tCoords[1] != closeCoord[1])
0N/A {
0N/A ProcessLine(hnd, tCoords, closeCoord,
0N/A pixelInfo);
0N/A }
0N/A }
0N/A hnd->pProcessEndSubPath(hnd);
0N/A }
0N/A
0N/A tCoords[0] = coords[index++] + transXf;
0N/A tCoords[1] = coords[index++] + transYf;
0N/A
0N/A /* Checking SEG_MOVETO coordinates if they are out of the
0N/A * [LOWER_BND, UPPER_BND] range. This check also handles
0N/A * NaN and Infinity values. Skipping next path segment in
0N/A * case of invalid data.
0N/A */
0N/A
0N/A if (tCoords[0] < UPPER_BND &&
0N/A tCoords[0] > LOWER_BND &&
0N/A tCoords[1] < UPPER_BND &&
0N/A tCoords[1] > LOWER_BND)
0N/A {
0N/A subpathStarted = JNI_TRUE;
0N/A skip = JNI_FALSE;
0N/A closeCoord[0] = tCoords[0];
0N/A closeCoord[1] = tCoords[1];
0N/A } else {
0N/A skip = JNI_TRUE;
0N/A }
0N/A } else {
0N/A return JNI_FALSE;
0N/A }
0N/A break;
0N/A case java_awt_geom_PathIterator_SEG_LINETO:
0N/A if (index + 2 <= maxCoords) {
0N/A lastX = tCoords[2] = coords[index++] + transXf;
0N/A lastY = tCoords[3] = coords[index++] + transYf;
0N/A
0N/A /* Checking SEG_LINETO coordinates if they are out of the
0N/A * [LOWER_BND, UPPER_BND] range. This check also handles
0N/A * NaN and Infinity values. Ignoring current path segment
0N/A * in case of invalid data. If segment is skipped its
0N/A * endpoint (if valid) is used to begin new subpath.
0N/A */
0N/A
0N/A if (lastX < UPPER_BND &&
0N/A lastX > LOWER_BND &&
0N/A lastY < UPPER_BND &&
0N/A lastY > LOWER_BND)
0N/A {
0N/A if (skip) {
0N/A tCoords[0] = closeCoord[0] = lastX;
0N/A tCoords[1] = closeCoord[1] = lastY;
0N/A subpathStarted = JNI_TRUE;
0N/A skip = JNI_FALSE;
0N/A } else {
0N/A ProcessLine(hnd, tCoords, tCoords + 2,
0N/A pixelInfo);
0N/A tCoords[0] = lastX;
0N/A tCoords[1] = lastY;
0N/A }
0N/A }
0N/A } else {
0N/A return JNI_FALSE;
0N/A }
0N/A break;
0N/A case java_awt_geom_PathIterator_SEG_QUADTO:
0N/A if (index + 4 <= maxCoords) {
0N/A tCoords[2] = coords[index++] + transXf;
0N/A tCoords[3] = coords[index++] + transYf;
0N/A lastX = tCoords[4] = coords[index++] + transXf;
0N/A lastY = tCoords[5] = coords[index++] + transYf;
0N/A
0N/A /* Checking SEG_QUADTO coordinates if they are out of the
0N/A * [LOWER_BND, UPPER_BND] range. This check also handles
0N/A * NaN and Infinity values. Ignoring current path segment
0N/A * in case of invalid endpoints's data. Equivalent to
0N/A * the SEG_LINETO if endpoint coordinates are valid but
0N/A * there are invalid data among other coordinates
0N/A */
0N/A
0N/A if (lastX < UPPER_BND &&
0N/A lastX > LOWER_BND &&
0N/A lastY < UPPER_BND &&
0N/A lastY > LOWER_BND)
0N/A {
0N/A if (skip) {
0N/A tCoords[0] = closeCoord[0] = lastX;
0N/A tCoords[1] = closeCoord[1] = lastY;
0N/A subpathStarted = JNI_TRUE;
0N/A skip = JNI_FALSE;
0N/A } else {
0N/A if (tCoords[2] < UPPER_BND &&
0N/A tCoords[2] > LOWER_BND &&
0N/A tCoords[3] < UPPER_BND &&
0N/A tCoords[3] > LOWER_BND)
0N/A {
0N/A ProcessQuad(hnd, tCoords, pixelInfo);
0N/A } else {
0N/A ProcessLine(hnd, tCoords,
0N/A tCoords + 4, pixelInfo);
0N/A }
0N/A tCoords[0] = lastX;
0N/A tCoords[1] = lastY;
0N/A }
0N/A }
0N/A } else {
0N/A return JNI_FALSE;
0N/A }
0N/A break;
0N/A case java_awt_geom_PathIterator_SEG_CUBICTO:
0N/A if (index + 6 <= maxCoords) {
0N/A tCoords[2] = coords[index++] + transXf;
0N/A tCoords[3] = coords[index++] + transYf;
0N/A tCoords[4] = coords[index++] + transXf;
0N/A tCoords[5] = coords[index++] + transYf;
0N/A lastX = tCoords[6] = coords[index++] + transXf;
0N/A lastY = tCoords[7] = coords[index++] + transYf;
0N/A
0N/A /* Checking SEG_CUBICTO coordinates if they are out of the
0N/A * [LOWER_BND, UPPER_BND] range. This check also handles
0N/A * NaN and Infinity values. Ignoring current path segment
0N/A * in case of invalid endpoints's data. Equivalent to
0N/A * the SEG_LINETO if endpoint coordinates are valid but
0N/A * there are invalid data among other coordinates
0N/A */
0N/A
0N/A if (lastX < UPPER_BND &&
0N/A lastX > LOWER_BND &&
0N/A lastY < UPPER_BND &&
0N/A lastY > LOWER_BND)
0N/A {
0N/A if (skip) {
0N/A tCoords[0] = closeCoord[0] = tCoords[6];
0N/A tCoords[1] = closeCoord[1] = tCoords[7];
0N/A subpathStarted = JNI_TRUE;
0N/A skip = JNI_FALSE;
0N/A } else {
0N/A if (tCoords[2] < UPPER_BND &&
0N/A tCoords[2] > LOWER_BND &&
0N/A tCoords[3] < UPPER_BND &&
0N/A tCoords[3] > LOWER_BND &&
0N/A tCoords[4] < UPPER_BND &&
0N/A tCoords[4] > LOWER_BND &&
0N/A tCoords[5] < UPPER_BND &&
0N/A tCoords[5] > LOWER_BND)
0N/A {
0N/A ProcessCubic(hnd, tCoords, pixelInfo);
0N/A } else {
0N/A ProcessLine(hnd, tCoords, tCoords + 6,
0N/A pixelInfo);
0N/A }
0N/A tCoords[0] = lastX;
0N/A tCoords[1] = lastY;
0N/A }
0N/A }
0N/A } else {
0N/A return JNI_FALSE;
0N/A }
0N/A break;
0N/A case java_awt_geom_PathIterator_SEG_CLOSE:
0N/A if (subpathStarted && !skip) {
0N/A skip = JNI_FALSE;
0N/A if (tCoords[0] != closeCoord[0] ||
0N/A tCoords[1] != closeCoord[1])
0N/A {
0N/A ProcessLine(hnd, tCoords, closeCoord, pixelInfo);
0N/A /* Storing last path's point for using in
0N/A * following segments without initial moveTo
0N/A */
0N/A tCoords[0] = closeCoord[0];
0N/A tCoords[1] = closeCoord[1];
0N/A }
0N/A
0N/A hnd->pProcessEndSubPath(hnd);
0N/A }
0N/A
0N/A break;
0N/A }
0N/A }
0N/A
0N/A /* Performing closing of the unclosed segments */
0N/A if (subpathStarted & !skip) {
0N/A if (hnd->clipMode == PH_MODE_FILL_CLIP) {
0N/A if (tCoords[0] != closeCoord[0] ||
0N/A tCoords[1] != closeCoord[1])
0N/A {
0N/A ProcessLine(hnd, tCoords, closeCoord,
0N/A pixelInfo);
0N/A }
0N/A }
0N/A hnd->pProcessEndSubPath(hnd);
0N/A }
0N/A
0N/A return JNI_TRUE;
0N/A}
0N/A
0N/A/* TODO Add checking of the result of the malloc/realloc functions to handle
0N/A * out of memory error and don't leak earlier allocated data
0N/A */
0N/A
0N/A
0N/A#define ALLOC(ptr, type, n) \
0N/A ptr = (type *)malloc((n)*sizeof(type))
0N/A#define REALLOC(ptr, type, n) \
0N/A ptr = (type *)realloc(ptr, (n)*sizeof(type))
0N/A
0N/A
0N/Astruct _Edge;
0N/A
0N/Atypedef struct _Point {
0N/A jint x;
0N/A jint y;
0N/A jboolean lastPoint;
0N/A struct _Point* prev;
0N/A struct _Point* next;
0N/A struct _Point* nextByY;
0N/A jboolean endSL;
0N/A struct _Edge* edge;
0N/A} Point;
0N/A
0N/A
0N/Atypedef struct _Edge {
0N/A jint x;
0N/A jint dx;
0N/A Point* p;
0N/A jint dir;
0N/A struct _Edge* prev;
0N/A struct _Edge* next;
0N/A} Edge;
0N/A
0N/A/* Size of the default buffer in the FillData structure. This buffer is
0N/A * replaced with heap allocated in case of large paths.
0N/A */
0N/A#define DF_MAX_POINT 256
0N/A
0N/A/* Following structure accumulates points of the non-continuous flattened
0N/A * path during iteration through the origin path's segments . The end
0N/A * of the each subpath is marked as lastPoint flag set at the last point
0N/A */
0N/A
0N/Atypedef struct {
0N/A Point *plgPnts;
0N/A Point dfPlgPnts[DF_MAX_POINT];
0N/A jint plgSize;
0N/A jint plgMax;
0N/A jint plgYMin;
0N/A jint plgYMax;
0N/A} FillData;
0N/A
0N/A#define FD_INIT(PTR) \
0N/A do { \
0N/A (PTR)->plgPnts = (PTR)->dfPlgPnts; \
0N/A (PTR)->plgSize = 0; \
0N/A (PTR)->plgMax = DF_MAX_POINT; \
0N/A } while(0)
0N/A
0N/A#define FD_ADD_POINT(PTR, X, Y, LASTPT) \
0N/A do { \
0N/A Point* _pnts = (PTR)->plgPnts; \
0N/A jint _size = (PTR)->plgSize; \
0N/A if (_size >= (PTR)->plgMax) { \
0N/A jint newMax = (PTR)->plgMax*2; \
0N/A if ((PTR)->plgPnts == (PTR)->dfPlgPnts) { \
0N/A (PTR)->plgPnts = (Point*)malloc(newMax*sizeof(Point)); \
0N/A memcpy((PTR)->plgPnts, _pnts, _size*sizeof(Point)); \
0N/A } else { \
0N/A (PTR)->plgPnts = (Point*)realloc( \
0N/A _pnts, newMax*sizeof(Point)); \
0N/A } \
0N/A _pnts = (PTR)->plgPnts; \
0N/A (PTR)->plgMax = newMax; \
0N/A } \
0N/A _pnts += _size; \
0N/A _pnts->x = X; \
0N/A _pnts->y = Y; \
0N/A _pnts->lastPoint = LASTPT; \
0N/A if (_size) { \
0N/A if ((PTR)->plgYMin > Y) (PTR)->plgYMin = Y; \
0N/A if ((PTR)->plgYMax < Y) (PTR)->plgYMax = Y; \
0N/A } else { \
0N/A (PTR)->plgYMin = Y; \
0N/A (PTR)->plgYMax = Y; \
0N/A } \
0N/A (PTR)->plgSize = _size + 1; \
0N/A } while(0)
0N/A
0N/A
0N/A#define FD_FREE_POINTS(PTR) \
0N/A do { \
0N/A if ((PTR)->plgPnts != (PTR)->dfPlgPnts) { \
0N/A free((PTR)->plgPnts); \
0N/A } \
0N/A } while(0)
0N/A
0N/A#define FD_IS_EMPTY(PTR) (!((PTR)->plgSize))
0N/A
0N/A#define FD_IS_ENDED(PTR) ((PTR)->plgPnts[(PTR)->plgSize - 1].lastPoint)
0N/A
0N/A#define FD_SET_ENDED(PTR) \
0N/A do { \
0N/A (PTR)->plgPnts[(PTR)->plgSize - 1].lastPoint = JNI_TRUE; \
0N/A } while(0)
0N/A
0N/A#define PFD(HND) ((FillData*)(HND)->pData)
0N/A
0N/A/* Bubble sorting in the ascending order of the linked list. This
0N/A * implementation stops processing the list if there were no changes during the
0N/A * previous pass.
0N/A *
0N/A * LIST - ptr to the ptr to the first element of the list
0N/A * ETYPE - type of the element in the list
0N/A * NEXT - accessor to the next field in the list element
0N/A * GET_LKEY - accessor to the key of the list element
0N/A */
0N/A#define LBUBBLE_SORT(LIST, ETYPE, NEXT, GET_LKEY) \
0N/A do { \
0N/A ETYPE *p, *q, *r, *s = NULL, *temp ; \
0N/A jint wasSwap = 1; \
0N/A /* r precedes p and s points to the node up to which comparisons \
0N/A * are to be made */ \
0N/A while ( s != NEXT(*LIST) && wasSwap) { \
0N/A r = p = *LIST; \
0N/A q = NEXT(p); \
0N/A wasSwap = 0; \
0N/A while ( p != s ) { \
0N/A if (GET_LKEY(p) >= GET_LKEY(q)) { \
0N/A wasSwap = 1; \
0N/A if ( p == *LIST ) { \
0N/A temp = NEXT(q); \
0N/A NEXT(q) = p ; \
0N/A NEXT(p) = temp ; \
0N/A *LIST = q ; \
0N/A r = q ; \
0N/A } else { \
0N/A temp = NEXT(q); \
0N/A NEXT(q) = p ; \
0N/A NEXT(p) = temp ; \
0N/A NEXT(r) = q ; \
0N/A r = q ; \
0N/A } \
0N/A } else { \
0N/A r = p ; \
0N/A p = NEXT(p); \
0N/A } \
0N/A q = NEXT(p); \
0N/A if ( q == s ) s = p ; \
0N/A } \
0N/A } \
0N/A } while(0);
0N/A
0N/A/* Accessors for the Edge structure to work with LBUBBLE_SORT */
0N/A#define GET_ACTIVE_KEY(a) (a->x)
0N/A#define GET_ACTIVE_NEXT(a) ((a)->next)
0N/A
0N/A/* TODO: Implement stack/heap allocation technique for active edges
0N/A */
0N/A#define DELETE_ACTIVE(head,pnt) \
0N/Ado { \
0N/A Edge *prevp = pnt->prev; \
0N/A Edge *nextp = pnt->next; \
0N/A if (prevp) { \
0N/A prevp->next = nextp; \
0N/A } else { \
0N/A head = nextp; \
0N/A } \
0N/A if (nextp) { \
0N/A nextp->prev = prevp; \
0N/A } \
0N/A} while(0);
0N/A
0N/A#define INSERT_ACTIVE(head,pnt,cy) \
0N/Ado { \
0N/A Point *np = pnt->next; \
0N/A Edge *ne = active + nact; \
0N/A if (pnt->y == np->y) { \
0N/A /* Skipping horizontal segments */ \
0N/A break; \
0N/A } else { \
0N/A jint dX = np->x - pnt->x; \
0N/A jint dY = np->y - pnt->y; \
0N/A jint dy; \
0N/A if (pnt->y < np->y) { \
0N/A ne->dir = -1; \
0N/A ne->p = pnt; \
0N/A ne->x = pnt->x; \
0N/A dy = cy - pnt->y; \
0N/A } else { /* pnt->y > np->y */ \
0N/A ne->dir = 1; \
0N/A ne->p = np; \
0N/A ne->x = np->x; \
0N/A dy = cy - np->y; \
0N/A } \
0N/A \
0N/A /* We need to worry only about dX because dY is in */\
0N/A /* denominator and abs(dy) < MDP_MULT (cy is a first */\
0N/A /* scanline of the scan converted segment and we subtract */\
0N/A /* y coordinate of the nearest segment's end from it to */\
0N/A /* obtain dy) */\
0N/A if (ABS32(dX) > CALC_BND) { \
0N/A ne->dx = (jint)((((jdouble)dX)*MDP_MULT)/dY); \
0N/A ne->x += (jint)((((jdouble)dX)*dy)/dY); \
0N/A } else { \
0N/A ne->dx = ((dX)<<MDP_PREC)/dY; \
0N/A ne->x += (dX*dy)/dY; \
0N/A } \
0N/A } \
0N/A ne->next = head; \
0N/A ne->prev = NULL; \
0N/A if (head) { \
0N/A head->prev = ne; \
0N/A } \
0N/A head = active + nact; \
0N/A pnt->edge = head; \
0N/A nact++; \
0N/A} while(0);
0N/A
0N/Avoid FillPolygon(ProcessHandler* hnd,
0N/A jint fillRule) {
0N/A jint k, y, xl, xr;
0N/A jint drawing;
0N/A Edge* activeList, *active;
0N/A Edge* curEdge, *prevEdge;
0N/A jint nact;
0N/A jint n;
0N/A Point* pt, *curpt, *ept;
0N/A Point** yHash;
0N/A Point** curHash;
0N/A jint rightBnd = hnd->dhnd->xMax - 1;
0N/A FillData* pfd = (FillData*)(hnd->pData);
0N/A jint yMin = pfd->plgYMin;
0N/A jint yMax = pfd->plgYMax;
0N/A jint hashSize = ((yMax - yMin)>>MDP_PREC) + 4;
0N/A
0N/A /* Because of support of the KEY_STROKE_CONTROL hint we are performing
0N/A * shift of the coordinates at the higher level
0N/A */
0N/A jint hashOffset = ((yMin - 1) & MDP_W_MASK);
0N/A
0N/A// TODO creating lists using fake first element to avoid special casing of
0N/A// the first element in the list (which otherwise should be performed in each
0N/A// list operation)
0N/A
0N/A /* Winding counter */
0N/A jint counter;
0N/A
0N/A /* Calculating mask to be applied to the winding counter */
0N/A jint counterMask =
0N/A (fillRule == java_awt_geom_PathIterator_WIND_NON_ZERO)? -1:1;
0N/A pt = pfd->plgPnts;
0N/A n = pfd->plgSize;
0N/A
0N/A if (n <=1) return;
0N/A
0N/A ALLOC(yHash, Point*, hashSize);
0N/A for (k = 0; k < hashSize; k++) {
0N/A yHash[k] = NULL;
0N/A }
0N/A
0N/A ALLOC(active, Edge, n);
0N/A
0N/A /* Creating double linked list (prev, next links) describing path order and
0N/A * hash table with points which fall between scanlines. nextByY link is
0N/A * used for the points which are between same scanlines. Scanlines are
0N/A * passed through the centers of the pixels.
0N/A */
0N/A curpt = pt;
0N/A curpt->prev = NULL;
0N/A ept = pt + n - 1;
0N/A for (curpt = pt; curpt != ept; curpt++) {
0N/A Point* nextpt = curpt + 1;
0N/A curHash = yHash + ((curpt->y - hashOffset - 1) >> MDP_PREC);
0N/A curpt->nextByY = *curHash;
0N/A *curHash = curpt;
0N/A curpt->next = nextpt;
0N/A nextpt->prev = curpt;
0N/A curpt->edge = NULL;
0N/A }
0N/A
0N/A curHash = yHash + ((ept->y - hashOffset - 1) >> MDP_PREC);
0N/A ept->nextByY = *curHash;
0N/A *curHash = ept;
0N/A ept->next = NULL;
0N/A ept->edge = NULL;
0N/A nact = 0;
0N/A
0N/A activeList = NULL;
0N/A for (y=hashOffset + MDP_MULT,k = 0;
0N/A y<=yMax && k < hashSize; y += MDP_MULT, k++)
0N/A {
0N/A for(pt = yHash[k];pt; pt=pt->nextByY) {
0N/A /* pt->y should be inside hashed interval
0N/A * assert(y-MDP_MULT <= pt->y && pt->y < y);
0N/A */
0N/A if (pt->prev && !pt->prev->lastPoint) {
0N/A if (pt->prev->edge && pt->prev->y <= y) {
0N/A DELETE_ACTIVE(activeList, pt->prev->edge);
0N/A pt->prev->edge = NULL;
0N/A } else if (pt->prev->y > y) {
0N/A INSERT_ACTIVE(activeList, pt->prev, y);
0N/A }
0N/A }
0N/A
0N/A if (!pt->lastPoint && pt->next) {
0N/A if (pt->edge && pt->next->y <= y) {
0N/A DELETE_ACTIVE(activeList, pt->edge);
0N/A pt->edge = NULL;
0N/A } else if (pt->next->y > y) {
0N/A INSERT_ACTIVE(activeList, pt, y);
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (!activeList) continue;
0N/A
0N/A /* We could not use O(N) Radix sort here because in most cases list of
0N/A * edges almost sorted. So, bubble sort (O(N^2))is working much
0N/A * better. Note, in case of array of edges Shell sort is more
0N/A * efficient.
0N/A */
0N/A LBUBBLE_SORT((&activeList), Edge, GET_ACTIVE_NEXT, GET_ACTIVE_KEY);
0N/A
0N/A /* Correction of the back links in the double linked edge list */
0N/A curEdge=activeList;
0N/A prevEdge = NULL;
0N/A while (curEdge) {
0N/A curEdge->prev = prevEdge;
0N/A prevEdge = curEdge;
0N/A curEdge = curEdge->next;
0N/A }
0N/A
0N/A xl = xr = hnd->dhnd->xMin;
0N/A curEdge = activeList;
0N/A counter = 0;
0N/A drawing = 0;
0N/A for(;curEdge; curEdge = curEdge->next) {
0N/A counter += curEdge->dir;
0N/A if ((counter & counterMask) && !drawing) {
0N/A xl = (curEdge->x + MDP_MULT - 1)>>MDP_PREC;
0N/A drawing = 1;
0N/A }
0N/A
0N/A if (!(counter & counterMask) && drawing) {
0N/A xr = (curEdge->x - 1)>>MDP_PREC;
0N/A if (xl <= xr) {
0N/A hnd->dhnd->pDrawScanline(hnd->dhnd, xl, xr, y >> MDP_PREC);
0N/A }
0N/A drawing = 0;
0N/A }
0N/A
0N/A curEdge->x += curEdge->dx;
0N/A }
0N/A
0N/A /* Performing drawing till the right boundary (for correct rendering
0N/A * shapes clipped at the right side)
0N/A */
0N/A if (drawing && xl <= rightBnd) {
0N/A hnd->dhnd->pDrawScanline(hnd->dhnd, xl, rightBnd, y >> MDP_PREC);
0N/A }
0N/A }
0N/A free(active);
0N/A free(yHash);
0N/A}
0N/A
0N/A
0N/A
0N/Avoid StoreFixedLine(ProcessHandler* hnd,jint x1,jint y1,jint x2,jint y2,
0N/A jint* pixelInfo,jboolean checkBounds,
0N/A jboolean endSubPath) {
0N/A FillData* pfd;
0N/A jint outXMin, outXMax, outYMin, outYMax;
0N/A jint x3, y3, res;
0N/A
0N/A /* There is no need to round line coordinates to the forward differencing
0N/A * precision anymore. Such a rounding was used for preventing the curve go
0N/A * out the endpoint (this sometimes does not help). The problem was fixed
0N/A * in the forward differencing loops.
0N/A */
0N/A
0N/A if (checkBounds) {
0N/A jboolean lastClipped = JNI_FALSE;
0N/A
0N/A /* This function is used only for filling shapes, so there is no
0N/A * check for the type of clipping
0N/A */
0N/A outXMin = (jint)(hnd->dhnd->xMinf * MDP_MULT);
0N/A outXMax = (jint)(hnd->dhnd->xMaxf * MDP_MULT);
0N/A outYMin = (jint)(hnd->dhnd->yMinf * MDP_MULT);
0N/A outYMax = (jint)(hnd->dhnd->yMaxf * MDP_MULT);
0N/A
0N/A TESTANDCLIP(outYMin, outYMax, y1, x1, y2, x2, jint, res);
0N/A if (res == CRES_INVISIBLE) return;
0N/A TESTANDCLIP(outYMin, outYMax, y2, x2, y1, x1, jint, res);
0N/A if (res == CRES_INVISIBLE) return;
0N/A lastClipped = IS_CLIPPED(res);
0N/A
0N/A /* Clamping starting from first vertex of the the processed segment */
0N/A CLIPCLAMP(outXMin, outXMax, x1, y1, x2, y2, x3, y3, jint, res);
0N/A
0N/A /* Clamping only by left boundary */
0N/A if (res == CRES_MIN_CLIPPED) {
0N/A StoreFixedLine(hnd, x3, y3, x1, y1, pixelInfo,
0N/A JNI_FALSE, lastClipped);
0N/A
0N/A } else if (res == CRES_INVISIBLE) {
0N/A return;
0N/A }
0N/A
0N/A /* Clamping starting from last vertex of the the processed segment */
0N/A CLIPCLAMP(outXMin, outXMax, x2, y2, x1, y1, x3, y3, jint, res);
0N/A
0N/A /* Checking if there was a clip by right boundary */
0N/A lastClipped = lastClipped || (res == CRES_MAX_CLIPPED);
0N/A
0N/A StoreFixedLine(hnd, x1, y1, x2, y2, pixelInfo,
0N/A JNI_FALSE, lastClipped);
0N/A
0N/A /* Clamping only by left boundary */
0N/A if (res == CRES_MIN_CLIPPED) {
0N/A StoreFixedLine(hnd, x2, y2, x3, y3, pixelInfo,
0N/A JNI_FALSE, lastClipped);
0N/A }
0N/A
0N/A return;
0N/A }
0N/A pfd = (FillData*)(hnd->pData);
0N/A
0N/A /* Adding first point of the line only in case of empty or just finished
0N/A * path
0N/A */
0N/A if (FD_IS_EMPTY(pfd) || FD_IS_ENDED(pfd)) {
0N/A FD_ADD_POINT(pfd, x1, y1, JNI_FALSE);
0N/A }
0N/A
0N/A FD_ADD_POINT(pfd, x2, y2, JNI_FALSE);
0N/A
0N/A if (endSubPath) {
0N/A FD_SET_ENDED(pfd);
0N/A }
0N/A}
0N/A
0N/A
0N/Astatic void endSubPath(ProcessHandler* hnd) {
0N/A FillData* pfd = (FillData*)(hnd->pData);
0N/A if (!FD_IS_EMPTY(pfd)) {
0N/A FD_SET_ENDED(pfd);
0N/A }
0N/A}
0N/A
0N/Astatic void stubEndSubPath(ProcessHandler* hnd) {
0N/A}
0N/A
0N/Ajboolean doFillPath(DrawHandler* dhnd,
0N/A jint transX, jint transY,
0N/A jfloat* coords, jint maxCoords,
0N/A jbyte* types, jint numTypes,
0N/A PHStroke stroke, jint fillRule)
0N/A{
0N/A jint res;
0N/A
0N/A FillData fillData;
0N/A
0N/A ProcessHandler hnd =
0N/A {
0N/A &StoreFixedLine,
0N/A &endSubPath,
0N/A NULL,
0N/A PH_STROKE_DEFAULT,
0N/A PH_MODE_FILL_CLIP,
0N/A NULL
0N/A };
0N/A
0N/A /* Initialization of the following fields in the declaration of the hnd
0N/A * above causes warnings on sun studio compiler with -xc99=%none option
0N/A * applied (this option means compliance with C90 standard instead of C99)
0N/A */
0N/A hnd.dhnd = dhnd;
0N/A hnd.pData = &fillData;
0N/A hnd.stroke = stroke;
0N/A
0N/A FD_INIT(&fillData);
0N/A res = ProcessPath(&hnd, (jfloat)transX, (jfloat)transY,
0N/A coords, maxCoords, types, numTypes);
0N/A if (!res) {
0N/A FD_FREE_POINTS(&fillData);
0N/A return JNI_FALSE;
0N/A }
0N/A FillPolygon(&hnd, fillRule);
0N/A FD_FREE_POINTS(&fillData);
0N/A return JNI_TRUE;
0N/A}
0N/A
0N/Ajboolean doDrawPath(DrawHandler* dhnd,
0N/A void (*pProcessEndSubPath)(ProcessHandler*),
0N/A jint transX, jint transY,
0N/A jfloat* coords, jint maxCoords,
0N/A jbyte* types, jint numTypes, PHStroke stroke)
0N/A{
0N/A ProcessHandler hnd =
0N/A {
0N/A &ProcessFixedLine,
0N/A NULL,
0N/A NULL,
0N/A PH_STROKE_DEFAULT,
0N/A PH_MODE_DRAW_CLIP,
0N/A NULL
0N/A };
0N/A
0N/A /* Initialization of the following fields in the declaration of the hnd
0N/A * above causes warnings on sun studio compiler with -xc99=%none option
0N/A * applied (this option means compliance with C90 standard instead of C99)
0N/A */
0N/A hnd.dhnd = dhnd;
0N/A hnd.stroke = stroke;
0N/A
0N/A hnd.pProcessEndSubPath = (pProcessEndSubPath == NULL)?
0N/A stubEndSubPath : pProcessEndSubPath;
0N/A return ProcessPath(&hnd, (jfloat)transX, (jfloat)transY, coords, maxCoords,
0N/A types, numTypes);
0N/A}