alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (ShapeSpanIterator.c)

This example Java source code file (ShapeSpanIterator.c) 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.

Java - Java tags/keywords

getspandata, handleclose, jni_false, jni_true, jnicall, jnienv, jniexport, jnu_throwoutofmemoryerror, null, oomerr, pdboxpoint, state_have_rule, state_path_done, state_span_started

The ShapeSpanIterator.c Java example source code

/*
 * Copyright (c) 1998, 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.
 */

#include <stdlib.h>
#include <string.h>
#include <math.h>

#include "jni.h"
#include "jni_util.h"
#include <jlong.h>

#include "j2d_md.h"

#include "PathConsumer2D.h"
#include "SpanIterator.h"

#include "sun_java2d_pipe_ShapeSpanIterator.h"
#include "java_awt_geom_PathIterator.h"

/*
 * This structure holds all of the information needed to trace and
 * manage a single line segment of the shape's outline.
 */
typedef struct {
    jint curx;
    jint cury;
    jint lasty;
    jint error;
    jint bumpx;
    jint bumperr;
    jbyte windDir;
    jbyte pad0;
    jbyte pad1;
    jbyte pad2;
} segmentData;

/*
 * This structure holds all of the information needed to trace out
 * the entire span list of a single Shape object.
 */
typedef struct {
    PathConsumerVec funcs;      /* Native PathConsumer function vector */

    char state;                 /* Path delivery sequence state */
    char evenodd;               /* non-zero if path has EvenOdd winding rule */
    char first;                 /* non-zero if first path segment */
    char adjust;                /* normalize to nearest (0.25, 0.25) */

    jint lox;                   /* clip bbox low X */
    jint loy;                   /* clip bbox low Y */
    jint hix;                   /* clip bbox high X */
    jint hiy;                   /* clip bbox high Y */

    jfloat curx;                /* current path point X coordinate */
    jfloat cury;                /* current path point Y coordinate */
    jfloat movx;                /* last moveto X coordinate */
    jfloat movy;                /* last moveto Y coordinate */

    jfloat adjx;                /* last X coordinate adjustment */
    jfloat adjy;                /* last Y coordinate adjustment */

    jfloat pathlox;             /* lowest X coordinate in path */
    jfloat pathloy;             /* lowest Y coordinate in path */
    jfloat pathhix;             /* highest X coordinate in path */
    jfloat pathhiy;             /* highest Y coordinate in path */

    segmentData *segments;      /* pointer to array of path segments */
    int numSegments;            /* number of segments entries in array */
    int segmentsSize;           /* size of array of path segments */

    int lowSegment;             /* lower limit of segments in active range */
    int curSegment;             /* index of next active segment to return */
    int hiSegment;              /* upper limit of segments in active range */

    segmentData **segmentTable; /* pointers to segments being stepped */
} pathData;

#define STATE_INIT              0
#define STATE_HAVE_CLIP         1
#define STATE_HAVE_RULE         2
#define STATE_PATH_DONE         3
#define STATE_SPAN_STARTED      4

static jboolean subdivideLine(pathData *pd, int level,
                              jfloat x0, jfloat y0,
                              jfloat x1, jfloat y1);
static jboolean subdivideQuad(pathData *pd, int level,
                              jfloat x0, jfloat y0,
                              jfloat x1, jfloat y1,
                              jfloat x2, jfloat y2);
static jboolean subdivideCubic(pathData *pd, int level,
                               jfloat x0, jfloat y0,
                               jfloat x1, jfloat y1,
                               jfloat x2, jfloat y2,
                               jfloat x3, jfloat y3);
static jboolean appendSegment(pathData *pd,
                              jfloat x0, jfloat y0,
                              jfloat x1, jfloat y1);
static jboolean initSegmentTable(pathData *pd);

static void *ShapeSIOpen(JNIEnv *env, jobject iterator);
static void ShapeSIClose(JNIEnv *env, void *private);
static void ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[]);
static void ShapeSIIntersectClipBox(JNIEnv *env, void *private,
                                        jint lox, jint loy, jint hix, jint hiy);
static jboolean ShapeSINextSpan(void *state, jint spanbox[]);
static void ShapeSISkipDownTo(void *private, jint y);

static jfieldID pSpanDataID;

static SpanIteratorFuncs ShapeSIFuncs = {
    ShapeSIOpen,
    ShapeSIClose,
    ShapeSIGetPathBox,
    ShapeSIIntersectClipBox,
    ShapeSINextSpan,
    ShapeSISkipDownTo
};

static LineToFunc PCLineTo;
static MoveToFunc PCMoveTo;
static QuadToFunc PCQuadTo;
static CubicToFunc PCCubicTo;
static ClosePathFunc PCClosePath;
static PathDoneFunc PCPathDone;

#define PDBOXPOINT(pd, x, y)                                    \
    do {                                                        \
        if (pd->first) {                                        \
            pd->pathlox = pd->pathhix = x;                      \
            pd->pathloy = pd->pathhiy = y;                      \
            pd->first = 0;                                      \
        } else {                                                \
            if (pd->pathlox > x) pd->pathlox = x;               \
            if (pd->pathloy > y) pd->pathloy = y;               \
            if (pd->pathhix < x) pd->pathhix = x;               \
            if (pd->pathhiy < y) pd->pathhiy = y;               \
        }                                                       \
    } while (0)

/*
 * _ADJUST is the internal macro used to adjust a new endpoint
 * and then invoke the "additional" code from the callers below
 * which will adjust the control points as needed to match.
 *
 * When the "additional" code is executed, newadj[xy] will
 * contain the adjustment applied to the new endpoint and
 * pd->adj[xy] will still contain the previous adjustment
 * that was applied to the old endpoint.
 */
#define _ADJUST(pd, x, y, additional)                           \
    do {                                                        \
        if (pd->adjust) {                                       \
            jfloat newx = (jfloat) floor(x + 0.25f) + 0.25f;    \
            jfloat newy = (jfloat) floor(y + 0.25f) + 0.25f;    \
            jfloat newadjx = newx - x;                          \
            jfloat newadjy = newy - y;                          \
            x = newx;                                           \
            y = newy;                                           \
            additional;                                         \
            pd->adjx = newadjx;                                 \
            pd->adjy = newadjy;                                 \
        }                                                       \
    } while (0)

/*
 * Adjust a single endpoint with no control points.
 * "additional" code is a null statement.
 */
#define ADJUST1(pd, x1, y1)                                     \
    _ADJUST(pd, x1, y1,                                         \
            do {                                                \
            } while (0))

/*
 * Adjust a quadratic curve.  The _ADJUST macro takes care
 * of the new endpoint and the "additional" code adjusts
 * the single quadratic control point by the averge of
 * the prior and the new adjustment amounts.
 */
#define ADJUST2(pd, x1, y1, x2, y2)                             \
    _ADJUST(pd, x2, y2,                                         \
            do {                                                \
                x1 += (pd->adjx + newadjy) / 2;                 \
                y1 += (pd->adjy + newadjy) / 2;                 \
            } while (0))

/*
 * Adjust a cubic curve.  The _ADJUST macro takes care
 * of the new endpoint and the "additional" code adjusts
 * the first of the two cubic control points by the same
 * amount that the prior endpoint was adjusted and then
 * adjusts the second of the two control points by the
 * same amount as the new endpoint was adjusted.  This
 * keeps the tangent lines from xy0 to xy1 and xy3 to xy2
 * parallel before and after the adjustment.
 */
#define ADJUST3(pd, x1, y1, x2, y2, x3, y3)                     \
    _ADJUST(pd, x3, y3,                                         \
            do {                                                \
                x1 += pd->adjx;                                 \
                y1 += pd->adjy;                                 \
                x2 += newadjx;                                  \
                y2 += newadjy;                                  \
            } while (0))

#define HANDLEMOVETO(pd, x0, y0, OOMERR)                        \
    do {                                                        \
        HANDLECLOSE(pd, OOMERR);                                \
        ADJUST1(pd, x0, y0);                                    \
        pd->movx = x0;                                          \
        pd->movy = y0;                                          \
        PDBOXPOINT(pd, x0, y0);                                 \
        pd->curx = x0;                                          \
        pd->cury = y0;                                          \
    } while (0)

#define HANDLELINETO(pd, x1, y1, OOMERR)                        \
    do {                                                        \
        ADJUST1(pd, x1, y1);                                    \
        if (!subdivideLine(pd, 0,                               \
                           pd->curx, pd->cury,                  \
                           x1, y1)) {                           \
            OOMERR;                                             \
            break;                                              \
        }                                                       \
        PDBOXPOINT(pd, x1, y1);                                 \
        pd->curx = x1;                                          \
        pd->cury = y1;                                          \
    } while (0)

#define HANDLEQUADTO(pd, x1, y1, x2, y2, OOMERR)                \
    do {                                                        \
        ADJUST2(pd, x1, y1, x2, y2);                            \
        if (!subdivideQuad(pd, 0,                               \
                           pd->curx, pd->cury,                  \
                           x1, y1, x2, y2)) {                   \
            OOMERR;                                             \
            break;                                              \
        }                                                       \
        PDBOXPOINT(pd, x1, y1);                                 \
        PDBOXPOINT(pd, x2, y2);                                 \
        pd->curx = x2;                                          \
        pd->cury = y2;                                          \
    } while (0)

#define HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, OOMERR)       \
    do {                                                        \
        ADJUST3(pd, x1, y1, x2, y2, x3, y3);                    \
        if (!subdivideCubic(pd, 0,                              \
                            pd->curx, pd->cury,                 \
                            x1, y1, x2, y2, x3, y3)) {          \
            OOMERR;                                             \
            break;                                              \
        }                                                       \
        PDBOXPOINT(pd, x1, y1);                                 \
        PDBOXPOINT(pd, x2, y2);                                 \
        PDBOXPOINT(pd, x3, y3);                                 \
        pd->curx = x3;                                          \
        pd->cury = y3;                                          \
    } while (0)

#define HANDLECLOSE(pd, OOMERR)                                 \
    do {                                                        \
        if (pd->curx != pd->movx || pd->cury != pd->movy) {     \
            if (!subdivideLine(pd, 0,                           \
                               pd->curx, pd->cury,              \
                               pd->movx, pd->movy)) {           \
                OOMERR;                                         \
                break;                                          \
            }                                                   \
            pd->curx = pd->movx;                                \
            pd->cury = pd->movy;                                \
        }                                                       \
    } while (0)

#define HANDLEENDPATH(pd, OOMERR)                               \
    do {                                                        \
        HANDLECLOSE(pd, OOMERR);                                \
        pd->state = STATE_PATH_DONE;                            \
    } while (0)

static pathData *
GetSpanData(JNIEnv *env, jobject sr, int minState, int maxState)
{
    pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);

    if (pd == NULL) {
        JNU_ThrowNullPointerException(env, "private data");
    } else if (pd->state < minState || pd->state > maxState) {
        JNU_ThrowInternalError(env, "bad path delivery sequence");
        pd = NULL;
    }

    return pd;
}

static pathData *
MakeSpanData(JNIEnv *env, jobject sr)
{
    pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);

    if (pd != NULL) {
        JNU_ThrowInternalError(env, "private data already initialized");
        return NULL;
    }

    pd = calloc(1, sizeof(pathData));

    if (pd == NULL) {
        JNU_ThrowOutOfMemoryError(env, "private data");
    } else {
        /* Initialize PathConsumer2D struct header */
        pd->funcs.moveTo = PCMoveTo;
        pd->funcs.lineTo = PCLineTo;
        pd->funcs.quadTo = PCQuadTo;
        pd->funcs.cubicTo = PCCubicTo;
        pd->funcs.closePath = PCClosePath;
        pd->funcs.pathDone = PCPathDone;

        /* Initialize ShapeSpanIterator fields */
        pd->first = 1;

        (*env)->SetLongField(env, sr, pSpanDataID, ptr_to_jlong(pd));
    }

    return pd;
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_initIDs
    (JNIEnv *env, jclass src)
{
    pSpanDataID = (*env)->GetFieldID(env, src, "pData", "J");
}

/*
 * Class:     sun_java2d_pipe_ShapeSpanIterator
 * Method:    setNormalize
 * Signature: (Z)V
 */
JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_setNormalize
    (JNIEnv *env, jobject sr, jboolean adjust)
{
    pathData *pd;

    pd = MakeSpanData(env, sr);
    if (pd == NULL) {
        return;
    }

    pd->adjust = adjust;
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_setOutputAreaXYXY
    (JNIEnv *env, jobject sr, jint lox, jint loy, jint hix, jint hiy)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_INIT, STATE_INIT);
    if (pd == NULL) {
        return;
    }

    pd->lox = lox;
    pd->loy = loy;
    pd->hix = hix;
    pd->hiy = hiy;
    pd->state = STATE_HAVE_CLIP;
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_setRule
    (JNIEnv *env, jobject sr, jint rule)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_HAVE_CLIP, STATE_HAVE_CLIP);
    if (pd == NULL) {
        return;
    }

    pd->evenodd = (rule == java_awt_geom_PathIterator_WIND_EVEN_ODD);
    pd->state = STATE_HAVE_RULE;
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_addSegment
    (JNIEnv *env, jobject sr, jint type, jfloatArray coordObj)
{
    jfloat coords[6];
    jfloat x1, y1, x2, y2, x3, y3;
    jboolean oom = JNI_FALSE;
    pathData *pd;
    int numpts = 0;

    pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
    if (pd == NULL) {
        return;
    }

    (*env)->GetFloatArrayRegion(env, coordObj, 0, 6, coords);
    if ((*env)->ExceptionCheck(env)) {
        return;
    }

    switch (type) {
    case java_awt_geom_PathIterator_SEG_MOVETO:
        x1 = coords[0]; y1 = coords[1];
        HANDLEMOVETO(pd, x1, y1, {oom = JNI_TRUE;});
        break;
    case java_awt_geom_PathIterator_SEG_LINETO:
        x1 = coords[0]; y1 = coords[1];
        HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;});
        break;
    case java_awt_geom_PathIterator_SEG_QUADTO:
        x1 = coords[0]; y1 = coords[1];
        x2 = coords[2]; y2 = coords[3];
        HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;});
        break;
    case java_awt_geom_PathIterator_SEG_CUBICTO:
        x1 = coords[0]; y1 = coords[1];
        x2 = coords[2]; y2 = coords[3];
        x3 = coords[4]; y3 = coords[5];
        HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;});
        break;
    case java_awt_geom_PathIterator_SEG_CLOSE:
        HANDLECLOSE(pd, {oom = JNI_TRUE;});
        break;
    default:
        JNU_ThrowInternalError(env, "bad path segment type");
        return;
    }

    if (oom) {
        JNU_ThrowOutOfMemoryError(env, "path segment data");
        return;
    }
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_getPathBox
    (JNIEnv *env, jobject sr, jintArray spanbox)
{
    pathData *pd;
    jint coords[4];

    pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_PATH_DONE);
    if (pd == NULL) {
        return;
    }

    ShapeSIGetPathBox(env, pd, coords);

    (*env)->SetIntArrayRegion(env, spanbox, 0, 4, coords);
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_intersectClipBox
    (JNIEnv *env, jobject ri, jint clox, jint cloy, jint chix, jint chiy)
{
    pathData *pd;

    pd = GetSpanData(env, ri, STATE_PATH_DONE, STATE_PATH_DONE);
    if (pd == NULL) {
        return;
    }

    ShapeSIIntersectClipBox(env, pd, clox, cloy, chix, chiy);
}

JNIEXPORT jboolean JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_nextSpan
    (JNIEnv *env, jobject sr, jintArray spanbox)
{
    pathData *pd;
    jboolean ret;
    jint coords[4];

    pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_SPAN_STARTED);
    if (pd == NULL) {
        return JNI_FALSE;
    }

    ret = ShapeSINextSpan(pd, coords);
    if (ret) {
        (*env)->SetIntArrayRegion(env, spanbox, 0, 4, coords);
    }

    return ret;
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_skipDownTo
    (JNIEnv *env, jobject sr, jint y)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_PATH_DONE, STATE_SPAN_STARTED);
    if (pd == NULL) {
        return;
    }

    ShapeSISkipDownTo(pd, y);
}

JNIEXPORT jlong JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_getNativeIterator
    (JNIEnv *env, jobject sr)
{
    return ptr_to_jlong(&ShapeSIFuncs);
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_dispose
    (JNIEnv *env, jobject sr)
{
    pathData *pd = (pathData *) JNU_GetLongFieldAsPtr(env, sr, pSpanDataID);

    if (pd == NULL) {
        return;
    }

    if (pd->segments != NULL) {
        free(pd->segments);
    }
    if (pd->segmentTable != NULL) {
        free(pd->segmentTable);
    }
    free(pd);

    (*env)->SetLongField(env, sr, pSpanDataID, jlong_zero);
}

#define OUT_XLO 1
#define OUT_XHI 2
#define OUT_YLO 4
#define OUT_YHI 8

#define CALCULATE_OUTCODES(pd, outc, x, y) \
    do { \
        if (y <= pd->loy) outc = OUT_YLO; \
        else if (y >= pd->hiy) outc = OUT_YHI; \
        else outc = 0; \
        if (x <= pd->lox) outc |= OUT_XLO; \
        else if (x >= pd->hix) outc |= OUT_XHI; \
    } while (0)

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_appendPoly
    (JNIEnv *env, jobject sr,
     jintArray xArray, jintArray yArray, jint nPoints,
     jint ixoff, jint iyoff)
{
    pathData *pd;
    int i;
    jint *xPoints, *yPoints;
    jboolean oom = JNI_FALSE;
    jfloat xoff = (jfloat) ixoff, yoff = (jfloat) iyoff;

    pd = GetSpanData(env, sr, STATE_HAVE_CLIP, STATE_HAVE_CLIP);
    if (pd == NULL) {
        return;
    }

    pd->evenodd = JNI_TRUE;
    pd->state = STATE_HAVE_RULE;
    if (pd->adjust) {
        xoff += 0.25f;
        yoff += 0.25f;
    }

    if (xArray == NULL || yArray == NULL) {
        JNU_ThrowNullPointerException(env, "polygon data arrays");
        return;
    }
    if ((*env)->GetArrayLength(env, xArray) < nPoints ||
        (*env)->GetArrayLength(env, yArray) < nPoints)
    {
        JNU_ThrowArrayIndexOutOfBoundsException(env, "polygon data arrays");
        return;
    }

    if (nPoints > 0) {
        xPoints = (*env)->GetPrimitiveArrayCritical(env, xArray, NULL);
        if (xPoints != NULL) {
            yPoints = (*env)->GetPrimitiveArrayCritical(env, yArray, NULL);
            if (yPoints != NULL) {
                jint outc0;
                jfloat x, y;

                x = xPoints[0] + xoff;
                y = yPoints[0] + yoff;
                CALCULATE_OUTCODES(pd, outc0, x, y);
                pd->movx = pd->curx = x;
                pd->movy = pd->cury = y;
                pd->pathlox = pd->pathhix = x;
                pd->pathloy = pd->pathhiy = y;
                pd->first = 0;
                for (i = 1; !oom && i < nPoints; i++) {
                    jint outc1;

                    x = xPoints[i] + xoff;
                    y = yPoints[i] + yoff;
                    if (y == pd->cury) {
                        /* Horizontal segment - do not append */
                        if (x != pd->curx) {
                            /* Not empty segment - track change in X */
                            CALCULATE_OUTCODES(pd, outc0, x, y);
                            pd->curx = x;
                            if (pd->pathlox > x) pd->pathlox = x;
                            if (pd->pathhix < x) pd->pathhix = x;
                        }
                        continue;
                    }
                    CALCULATE_OUTCODES(pd, outc1, x, y);
                    outc0 &= outc1;
                    if (outc0 == 0) {
                        oom = !appendSegment(pd, pd->curx, pd->cury, x, y);
                    } else if (outc0 == OUT_XLO) {
                        oom = !appendSegment(pd, (jfloat) pd->lox, pd->cury,
                                             (jfloat) pd->lox, y);
                    }
                    if (pd->pathlox > x) pd->pathlox = x;
                    if (pd->pathloy > y) pd->pathloy = y;
                    if (pd->pathhix < x) pd->pathhix = x;
                    if (pd->pathhiy < y) pd->pathhiy = y;
                    outc0 = outc1;
                    pd->curx = x;
                    pd->cury = y;
                }
                (*env)->ReleasePrimitiveArrayCritical(env, yArray,
                                                      yPoints, JNI_ABORT);
            }
            (*env)->ReleasePrimitiveArrayCritical(env, xArray,
                                                  xPoints, JNI_ABORT);
        }
        if (xPoints == NULL || yPoints == NULL) {
            return;
        }
    }
    if (!oom) {
        HANDLEENDPATH(pd, {oom = JNI_TRUE;});
    }
    if (oom) {
        JNU_ThrowOutOfMemoryError(env, "path segment data");
    }
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_moveTo
    (JNIEnv *env, jobject sr, jfloat x0, jfloat y0)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
    if (pd == NULL) {
        return;
    }

    HANDLEMOVETO(pd, x0, y0,
                 {JNU_ThrowOutOfMemoryError(env, "path segment data");});
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_lineTo
    (JNIEnv *env, jobject sr, jfloat x1, jfloat y1)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
    if (pd == NULL) {
        return;
    }

    HANDLELINETO(pd, x1, y1,
                 {JNU_ThrowOutOfMemoryError(env, "path segment data");});
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_quadTo
    (JNIEnv *env, jobject sr,
     jfloat xm, jfloat ym, jfloat x1, jfloat y1)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
    if (pd == NULL) {
        return;
    }

    HANDLEQUADTO(pd, xm, ym, x1, y1,
                 {JNU_ThrowOutOfMemoryError(env, "path segment data");});
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_curveTo
    (JNIEnv *env, jobject sr,
     jfloat xm, jfloat ym,
     jfloat xn, jfloat yn,
     jfloat x1, jfloat y1)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
    if (pd == NULL) {
        return;
    }

    HANDLECUBICTO(pd, xm, ym, xn, yn, x1, y1,
                  {JNU_ThrowOutOfMemoryError(env, "path segment data");});
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_closePath
    (JNIEnv *env, jobject sr)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
    if (pd == NULL) {
        return;
    }

    HANDLECLOSE(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");});
}

JNIEXPORT void JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_pathDone
    (JNIEnv *env, jobject sr)
{
    pathData *pd;

    pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);
    if (pd == NULL) {
        return;
    }

    HANDLEENDPATH(pd, {JNU_ThrowOutOfMemoryError(env, "path segment data");});
}

JNIEXPORT jlong JNICALL
Java_sun_java2d_pipe_ShapeSpanIterator_getNativeConsumer
    (JNIEnv *env, jobject sr)
{
    pathData *pd = GetSpanData(env, sr, STATE_HAVE_RULE, STATE_HAVE_RULE);

    if (pd == NULL) {
        return jlong_zero;
    }

    return ptr_to_jlong(&(pd->funcs));
}

static jboolean
PCMoveTo(PathConsumerVec *consumer,
         jfloat x0, jfloat y0)
{
    pathData *pd = (pathData *) consumer;
    jboolean oom = JNI_FALSE;

    HANDLEMOVETO(pd, x0, y0, {oom = JNI_TRUE;});

    return oom;
}

static jboolean
PCLineTo(PathConsumerVec *consumer,
         jfloat x1, jfloat y1)
{
    pathData *pd = (pathData *) consumer;
    jboolean oom = JNI_FALSE;

    HANDLELINETO(pd, x1, y1, {oom = JNI_TRUE;});

    return oom;
}

static jboolean
PCQuadTo(PathConsumerVec *consumer,
         jfloat x1, jfloat y1,
         jfloat x2, jfloat y2)
{
    pathData *pd = (pathData *) consumer;
    jboolean oom = JNI_FALSE;

    HANDLEQUADTO(pd, x1, y1, x2, y2, {oom = JNI_TRUE;});

    return oom;
}

static jboolean
PCCubicTo(PathConsumerVec *consumer,
          jfloat x1, jfloat y1,
          jfloat x2, jfloat y2,
          jfloat x3, jfloat y3)
{
    pathData *pd = (pathData *) consumer;
    jboolean oom = JNI_FALSE;

    HANDLECUBICTO(pd, x1, y1, x2, y2, x3, y3, {oom = JNI_TRUE;});

    return oom;
}

static jboolean
PCClosePath(PathConsumerVec *consumer)
{
    pathData *pd = (pathData *) consumer;
    jboolean oom = JNI_FALSE;

    HANDLECLOSE(pd, {oom = JNI_TRUE;});

    return oom;
}

static jboolean
PCPathDone(PathConsumerVec *consumer)
{
    pathData *pd = (pathData *) consumer;
    jboolean oom = JNI_FALSE;

    HANDLEENDPATH(pd, {oom = JNI_TRUE;});

    return oom;
}

/*
 * REMIND: CDECL needed for WIN32 "qsort"
 */

#ifdef _WIN32
#define CDECL __cdecl
#else
#define CDECL
#endif

#define SUBDIVIDE_MAX   10
#define MAX_FLAT_SQ     (1.0 * 1.0)
#define GROW_SIZE       20
#define ERRSTEP_MAX     (0x7fffffff)
#define FRACTTOJINT(f)  ((jint) ((f) * (double) ERRSTEP_MAX))

#define minmax2(v1, v2, min, max)       \
do {                                    \
    if (v1 < v2) {                      \
        min = v1;                       \
        max = v2;                       \
    } else {                            \
        min = v2;                       \
        max = v1;                       \
    }                                   \
} while(0)

#define minmax3(v1, v2, v3, min, max)   \
do {                                    \
    if (v1 < v2) {                      \
        if (v1 < v3) {                  \
            min = v1;                   \
            max = (v2 < v3) ? v3 : v2;  \
        } else {                        \
            max = v2;                   \
            min = v3;                   \
        }                               \
    } else {                            \
        if (v1 < v3) {                  \
            max = v3;                   \
            min = v2;                   \
        } else {                        \
            max = v1;                   \
            min = (v2 < v3) ? v2 : v3;  \
        }                               \
    }                                   \
} while (0)

#define minmax4(v1, v2, v3, v4, min, max)       \
do {                                            \
    if (v1 < v2) {                              \
        if (v3 < v4) {                          \
            max = (v2 < v4) ? v4 : v2;          \
            min = (v1 < v3) ? v1 : v3;          \
        } else {                                \
            max = (v2 < v3) ? v3 : v2;          \
            min = (v1 < v4) ? v1 : v4;          \
        }                                       \
    } else {                                    \
        if (v3 < v4) {                          \
            max = (v1 < v4) ? v4 : v1;          \
            min = (v2 < v3) ? v2 : v3;          \
        } else {                                \
            max = (v1 < v3) ? v3 : v1;          \
            min = (v2 < v4) ? v2 : v4;          \
        }                                       \
    }                                           \
} while(0)

static jfloat
ptSegDistSq(jfloat x0, jfloat y0,
            jfloat x1, jfloat y1,
            jfloat px, jfloat py)
{
    jfloat dotprod, projlenSq;

    /* Adjust vectors relative to x0,y0 */
    /* x1,y1 becomes relative vector from x0,y0 to end of segment */
    x1 -= x0;
    y1 -= y0;
    /* px,py becomes relative vector from x0,y0 to test point */
    px -= x0;
    py -= y0;
    dotprod = px * x1 + py * y1;
    if (dotprod <= 0.0) {
        /* px,py is on the side of x0,y0 away from x1,y1 */
        /* distance to segment is length of px,py vector */
        /* "length of its (clipped) projection" is now 0.0 */
        projlenSq = 0.0;
    } else {
        /* switch to backwards vectors relative to x1,y1 */
        /* x1,y1 are already the negative of x0,y0=>x1,y1 */
        /* to get px,py to be the negative of px,py=>x1,y1 */
        /* the dot product of two negated vectors is the same */
        /* as the dot product of the two normal vectors */
        px = x1 - px;
        py = y1 - py;
        dotprod = px * x1 + py * y1;
        if (dotprod <= 0.0) {
            /* px,py is on the side of x1,y1 away from x0,y0 */
            /* distance to segment is length of (backwards) px,py vector */
            /* "length of its (clipped) projection" is now 0.0 */
            projlenSq = 0.0;
        } else {
            /* px,py is between x0,y0 and x1,y1 */
            /* dotprod is the length of the px,py vector */
            /* projected on the x1,y1=>x0,y0 vector times the */
            /* length of the x1,y1=>x0,y0 vector */
            projlenSq = dotprod * dotprod / (x1 * x1 + y1 * y1);
        }
    }
    /* Distance to line is now the length of the relative point */
    /* vector minus the length of its projection onto the line */
    /* (which is zero if the projection falls outside the range */
    /*  of the line segment). */
    return px * px + py * py - projlenSq;
}

static jboolean
appendSegment(pathData *pd,
              jfloat x0, jfloat y0,
              jfloat x1, jfloat y1)
{
    jbyte windDir;
    jint istartx, istarty, ilasty;
    jfloat dx, dy, slope;
    jfloat ystartbump;
    jint bumpx, bumperr, error;
    segmentData *seg;

    if (y0 > y1) {
        jfloat t;
        t = x0; x0 = x1; x1 = t;
        t = y0; y0 = y1; y1 = t;
        windDir = -1;
    } else {
        windDir = 1;
    }
    /* We want to iterate at every horizontal pixel center (HPC) crossing. */
    /* First calculate next highest HPC we will cross at the start. */
    istarty = (jint) ceil(y0 - 0.5f);
    /* Then calculate next highest HPC we would cross at the end. */
    ilasty  = (jint) ceil(y1 - 0.5f);
    /* Ignore if we start and end outside clip, or on the same scanline. */
    if (istarty >= ilasty || istarty >= pd->hiy || ilasty <= pd->loy) {
        return JNI_TRUE;
    }

    /* We will need to insert this segment, check for room. */
    if (pd->numSegments >= pd->segmentsSize) {
        segmentData *newSegs;
        int newSize = pd->segmentsSize + GROW_SIZE;
        newSegs = (segmentData *) calloc(newSize, sizeof(segmentData));
        if (newSegs == NULL) {
            return JNI_FALSE;
        }
        if (pd->segments != NULL) {
            memcpy(newSegs, pd->segments,
                   sizeof(segmentData) * pd->segmentsSize);
            free(pd->segments);
        }
        pd->segments = newSegs;
        pd->segmentsSize = newSize;
    }

    dx = x1 - x0;
    dy = y1 - y0;
    slope = dx / dy;

    /*
     * The Y coordinate of the first HPC was calculated as istarty.  We
     * now need to calculate the corresponding X coordinate (both integer
     * version for span start coordinate and float version for sub-pixel
     * error calculation).
     */
    /* First, how far does y bump to get to next HPC? */
    ystartbump = istarty + 0.5f - y0;
    /* Now, bump the float x coordinate to get X sample at that HPC. */
    x0 += ystartbump * dx / dy;
    /* Now calculate the integer coordinate that such a span starts at. */
    /* NOTE: Span inclusion is based on vertical pixel centers (VPC). */
    istartx = (jint) ceil(x0 - 0.5f);
    /* What is the lower bound of the per-scanline change in the X coord? */
    bumpx = (jint) floor(slope);
    /* What is the subpixel amount by which the bumpx is off? */
    bumperr = FRACTTOJINT(slope - floor(slope));
    /* Finally, find out how far the x coordinate can go before next VPC. */
    error = FRACTTOJINT(x0 - (istartx - 0.5f));

    seg = &pd->segments[pd->numSegments++];
    seg->curx = istartx;
    seg->cury = istarty;
    seg->lasty = ilasty;
    seg->error = error;
    seg->bumpx = bumpx;
    seg->bumperr = bumperr;
    seg->windDir = windDir;
    return JNI_TRUE;
}

/*
 * Lines don't really need to be subdivided, but this function performs
 * the same trivial rejections and reductions that the curve subdivision
 * functions perform before it hands the coordinates off to the appendSegment
 * function.
 */
static jboolean
subdivideLine(pathData *pd, int level,
              jfloat x0, jfloat y0,
              jfloat x1, jfloat y1)
{
    jfloat miny, maxy;
    jfloat minx, maxx;

    minmax2(x0, x1, minx, maxx);
    minmax2(y0, y1, miny, maxy);

    if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
        return JNI_TRUE;
    }
    if (maxx <= pd->lox) {
        return appendSegment(pd, maxx, y0, maxx, y1);
    }

    return appendSegment(pd, x0, y0, x1, y1);
}

static jboolean
subdivideQuad(pathData *pd, int level,
              jfloat x0, jfloat y0,
              jfloat x1, jfloat y1,
              jfloat x2, jfloat y2)
{
    jfloat miny, maxy;
    jfloat minx, maxx;

    minmax3(x0, x1, x2, minx, maxx);
    minmax3(y0, y1, y2, miny, maxy);

    if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
        return JNI_TRUE;
    }
    if (maxx <= pd->lox) {
        return appendSegment(pd, maxx, y0, maxx, y2);
    }

    if (level < SUBDIVIDE_MAX) {
        /* Test if the curve is flat enough for insertion. */
        if (ptSegDistSq(x0, y0, x2, y2, x1, y1) > MAX_FLAT_SQ) {
            jfloat cx1, cx2;
            jfloat cy1, cy2;

            cx1 = (x0 + x1) / 2.0f;
            cx2 = (x1 + x2) / 2.0f;
            x1 = (cx1 + cx2) / 2.0f;

            cy1 = (y0 + y1) / 2.0f;
            cy2 = (y1 + y2) / 2.0f;
            y1 = (cy1 + cy2) / 2.0f;

            level++;
            return (subdivideQuad(pd, level, x0, y0, cx1, cy1, x1, y1) &&
                    subdivideQuad(pd, level, x1, y1, cx2, cy2, x2, y2));
        }
    }

    return appendSegment(pd, x0, y0, x2, y2);
}

static jboolean
subdivideCubic(pathData *pd, int level,
               jfloat x0, jfloat y0,
               jfloat x1, jfloat y1,
               jfloat x2, jfloat y2,
               jfloat x3, jfloat y3)
{
    jfloat miny, maxy;
    jfloat minx, maxx;

    minmax4(x0, x1, x2, x3, minx, maxx);
    minmax4(y0, y1, y2, y3, miny, maxy);

    if (maxy <= pd->loy || miny >= pd->hiy || minx >= pd->hix) {
        return JNI_TRUE;
    }
    if (maxx <= pd->lox) {
        return appendSegment(pd, maxx, y0, maxx, y3);
    }

    if (level < SUBDIVIDE_MAX) {
        /* Test if the curve is flat enough for insertion. */
        if (ptSegDistSq(x0, y0, x3, y3, x1, y1) > MAX_FLAT_SQ ||
            ptSegDistSq(x0, y0, x3, y3, x2, y2) > MAX_FLAT_SQ)
        {
            jfloat ctrx, cx12, cx21;
            jfloat ctry, cy12, cy21;

            ctrx = (x1 + x2) / 2.0f;
            x1 = (x0 + x1) / 2.0f;
            x2 = (x2 + x3) / 2.0f;
            cx12 = (x1 + ctrx) / 2.0f;
            cx21 = (ctrx + x2) / 2.0f;
            ctrx = (cx12 + cx21) / 2.0f;

            ctry = (y1 + y2) / 2.0f;
            y1 = (y0 + y1) / 2.0f;
            y2 = (y2 + y3) / 2.0f;
            cy12 = (y1 + ctry) / 2.0f;
            cy21 = (ctry + y2) / 2.0f;
            ctry = (cy12 + cy21) / 2.0f;

            level++;
            return (subdivideCubic(pd, level, x0, y0, x1, y1,
                                   cx12, cy12, ctrx, ctry) &&
                    subdivideCubic(pd, level, ctrx, ctry, cx21, cy21,
                                   x2, y2, x3, y3));
        }
    }

    return appendSegment(pd, x0, y0, x3, y3);
}

static int CDECL
sortSegmentsByLeadingY(const void *elem1, const void *elem2)
{
    segmentData *seg1 = *(segmentData **)elem1;
    segmentData *seg2 = *(segmentData **)elem2;

    if (seg1->cury < seg2->cury) {
        return -1;
    }
    if (seg1->cury > seg2->cury) {
        return 1;
    }
    if (seg1->curx < seg2->curx) {
        return -1;
    }
    if (seg1->curx > seg2->curx) {
        return 1;
    }
    if (seg1->lasty < seg2->lasty) {
        return -1;
    }
    if (seg1->lasty > seg2->lasty) {
        return 1;
    }
    return 0;
}

static void *
ShapeSIOpen(JNIEnv *env, jobject iterator)
{
    return GetSpanData(env, iterator, STATE_PATH_DONE, STATE_PATH_DONE);
}

static void
ShapeSIClose(JNIEnv *env, void *private)
{
}

static void
ShapeSIGetPathBox(JNIEnv *env, void *private, jint pathbox[])
{
    pathData *pd = (pathData *)private;

    pathbox[0] = (jint) floor(pd->pathlox);
    pathbox[1] = (jint) floor(pd->pathloy);
    pathbox[2] = (jint) ceil(pd->pathhix);
    pathbox[3] = (jint) ceil(pd->pathhiy);
}

/*  Adjust the clip box from the given bounds. Used to constrain
    the output to a device clip
*/
static void
ShapeSIIntersectClipBox(JNIEnv *env, void *private,
                            jint clox, jint cloy, jint chix, jint chiy)
{
    pathData *pd = (pathData *)private;

    if (clox > pd->lox) {
        pd->lox = clox;
    }
    if (cloy > pd->loy) {
        pd->loy = cloy;
    }
    if (chix < pd->hix) {
        pd->hix = chix;
    }
    if (chiy < pd->hiy) {
        pd->hiy = chiy;
    }
}

static jboolean
ShapeSINextSpan(void *state, jint spanbox[])
{
    pathData *pd = (pathData *)state;
    int lo, cur, new, hi;
    int num = pd->numSegments;
    jint x0, x1, y0, err;
    jint loy;
    int ret = JNI_FALSE;
    segmentData **segmentTable;
    segmentData *seg;

    if (pd->state != STATE_SPAN_STARTED) {
        if (!initSegmentTable(pd)) {
            /* REMIND: - throw exception? */
            pd->lowSegment = num;
            return JNI_FALSE;
        }
    }

    lo = pd->lowSegment;
    cur = pd->curSegment;
    hi = pd->hiSegment;
    num = pd->numSegments;
    loy = pd->loy;
    segmentTable = pd->segmentTable;

    while (lo < num) {
        if (cur < hi) {
            seg = segmentTable[cur];
            x0 = seg->curx;
            if (x0 >= pd->hix) {
                cur = hi;
                continue;
            }
            if (x0 < pd->lox) {
                x0 = pd->lox;
            }

            if (pd->evenodd) {
                cur += 2;
                if (cur <= hi) {
                    x1 = segmentTable[cur - 1]->curx;
                } else {
                    x1 = pd->hix;
                }
            } else {
                int wind = seg->windDir;
                cur++;

                while (JNI_TRUE) {
                    if (cur >= hi) {
                        x1 = pd->hix;
                        break;
                    }
                    seg = segmentTable[cur++];
                    wind += seg->windDir;
                    if (wind == 0) {
                        x1 = seg->curx;
                        break;
                    }
                }
            }

            if (x1 > pd->hix) {
                x1 = pd->hix;
            }
            if (x1 <= x0) {
                continue;
            }
            spanbox[0] = x0;
            spanbox[1] = loy;
            spanbox[2] = x1;
            spanbox[3] = loy + 1;
            ret = JNI_TRUE;
            break;
        }

        if (++loy >= pd->hiy) {
            lo = cur = hi = num;
            break;
        }

        /* Go through active segments and toss which end "above" loy */
        cur = new = hi;
        while (--cur >= lo) {
            seg = segmentTable[cur];
            if (seg->lasty > loy) {
                segmentTable[--new] = seg;
            }
        }

        lo = new;
        if (lo == hi && lo < num) {
            /* The current list of segments is empty so we need to
             * jump to the beginning of the next set of segments.
             * Since the segments are not clipped to the output
             * area we need to make sure we don't jump "backwards"
             */
            seg = segmentTable[lo];
            if (loy < seg->cury) {
                loy = seg->cury;
            }
        }

        /* Go through new segments and accept any which start "above" loy */
        while (hi < num && segmentTable[hi]->cury <= loy) {
            hi++;
        }

        /* Update and sort the active segments by x0 */
        for (cur = lo; cur < hi; cur++) {
            seg = segmentTable[cur];

            /* First update the x0, y0 of the segment */
            x0 = seg->curx;
            y0 = seg->cury;
            err = seg->error;
            if (++y0 == loy) {
                x0 += seg->bumpx;
                err += seg->bumperr;
                x0 -= (err >> 31);
                err &= ERRSTEP_MAX;
            } else {
                jlong steps = loy;
                steps -= y0 - 1;
                y0 = loy;
                x0 += (jint) (steps * seg->bumpx);
                steps = err + (steps * seg->bumperr);
                x0 += (jint) (steps >> 31);
                err = ((jint) steps) & ERRSTEP_MAX;
            }
            seg->curx = x0;
            seg->cury = y0;
            seg->error = err;

            /* Then make sure the segment is sorted by x0 */
            for (new = cur; new > lo; new--) {
                segmentData *seg2 = segmentTable[new - 1];
                if (seg2->curx <= x0) {
                    break;
                }
                segmentTable[new] = seg2;
            }
            segmentTable[new] = seg;
        }
        cur = lo;
    }

    pd->lowSegment = lo;
    pd->hiSegment = hi;
    pd->curSegment = cur;
    pd->loy = loy;
    return ret;
}

static void
ShapeSISkipDownTo(void *private, jint y)
{
    pathData *pd = (pathData *)private;

    if (pd->state != STATE_SPAN_STARTED) {
        if (!initSegmentTable(pd)) {
            /* REMIND: - throw exception? */
            pd->lowSegment = pd->numSegments;
            return;
        }
    }

    /* Make sure we are jumping forward */
    if (pd->loy < y) {
        /* Pretend like we just finished with the span line y-1... */
        pd->loy = y - 1;
        pd->curSegment = pd->hiSegment; /* no more segments on that line */
    }
}

static jboolean
initSegmentTable(pathData *pd)
{
    int i, cur, num, loy;
    segmentData **segmentTable;
    segmentTable = malloc(pd->numSegments * sizeof(segmentData *));
    if (segmentTable == NULL) {
        return JNI_FALSE;
    }
    pd->state = STATE_SPAN_STARTED;
    for (i = 0; i < pd->numSegments; i++) {
        segmentTable[i] = &pd->segments[i];
    }
    qsort(segmentTable, pd->numSegments, sizeof(segmentData *),
          sortSegmentsByLeadingY);

    pd->segmentTable = segmentTable;

    /* Skip to the first segment that ends below the top clip edge */
    cur = 0;
    num = pd->numSegments;
    loy = pd->loy;
    while (cur < num && segmentTable[cur]->lasty <= loy) {
        cur++;
    }
    pd->lowSegment = pd->curSegment = pd->hiSegment = cur;

    /* Prepare for next action to increment loy and prepare new segments */
    pd->loy--;

    return JNI_TRUE;
}

Other Java examples (source code examples)

Here is a short list of links related to this Java ShapeSpanIterator.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.