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

Java example source code file (QuartzSurfaceData.m)

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

bool, cgaffinetransform, cgcolorspaceref, cgcontextispathempty, cgcontextref, cgfloat, null, print, sd_fill, sdrendertype, stateshadinginfo, yes

The QuartzSurfaceData.m Java example source code

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

#import "QuartzSurfaceData.h"

#import "java_awt_BasicStroke.h"
#import "java_awt_AlphaComposite.h"
#import "java_awt_geom_PathIterator.h"
#import "java_awt_image_BufferedImage.h"
#import "sun_awt_SunHints.h"
#import "sun_java2d_CRenderer.h"
#import "sun_java2d_OSXSurfaceData.h"
#import "sun_lwawt_macosx_CPrinterSurfaceData.h"
#import "ImageSurfaceData.h"

#import <JavaNativeFoundation/JavaNativeFoundation.h>

#import <AppKit/AppKit.h>
#import "ThreadUtilities.h"

//#define DEBUG
#if defined DEBUG
    #define PRINT(msg) {fprintf(stderr, "%s\n", msg);}
#else
    #define PRINT(msg) {}
#endif

#define kOffset (0.5f)

BOOL gAdjustForJavaDrawing;

#pragma mark
#pragma mark --- Color Cache ---

// Creating and deleting CGColorRefs can be expensive, therefore we have a color cache.
// The color cache was first introduced with <rdar://problem/3923927>
// With <rdar://problem/4280514>, the hashing function was improved
// With <rdar://problem/4012223>, the color cache became global (per process) instead of per surface.

// Must be power of 2. 1024 is the least power of 2 number that makes SwingSet2 run without any non-empty cache misses
#define gColorCacheSize 1024
struct _ColorCacheInfo
{
    UInt32        keys[gColorCacheSize];
    CGColorRef    values[gColorCacheSize];
};
static struct _ColorCacheInfo colorCacheInfo;

static pthread_mutex_t gColorCacheLock = PTHREAD_MUTEX_INITIALIZER;

// given a UInt32 color, it tries to find that find the corresponding CGColorRef in the hash cache. If the CGColorRef
// doesn't exist or there is a collision, it creates a new one CGColorRef and put's in the cache. Then,
// it sets with current fill/stroke color for the the CGContext passed in (qsdo->cgRef).
void setCachedColor(QuartzSDOps *qsdo, UInt32 color)
{
    static const CGFloat kColorConversionMultiplier = 1.0f/255.0f;

    pthread_mutex_lock(&gColorCacheLock);

    static CGColorSpaceRef colorspace = NULL;
    if (colorspace == NULL)
    {
        colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    }

    CGColorRef cgColor = NULL;

    // The colors passed have low randomness. That means we need to scramble the bits of the color
    // to produce a good hash key. After some analysis, it looks like Thomas's Wang integer hasing algorithm
    // seems a nice trade off between performance and effectivness.
    UInt32 index = color;
    index += ~(index << 15);
    index ^=  (index >> 10);
    index +=  (index << 3);
    index ^=  (index >> 6);
    index += ~(index << 11);
    index ^=  (index >> 16);
    index = index & (gColorCacheSize - 1);   // The bits are scrambled, we just need to make sure it fits inside our table

    UInt32 key = colorCacheInfo.keys[index];
    CGColorRef value = colorCacheInfo.values[index];
    if ((key == color) && (value != NULL))
    {
        //fprintf(stderr, "+");fflush(stderr);//hit
        cgColor = value;
    }
    else
    {
        if (value != NULL)
        {
            //fprintf(stderr, "!");fflush(stderr);//miss and replace - double ouch
            CGColorRelease(value);
        }
        //fprintf(stderr, "-");fflush(stderr);// miss

        CGFloat alpha = ((color>>24)&0xff)*kColorConversionMultiplier;
        CGFloat red = ((color>>16)&0xff)*kColorConversionMultiplier;
        CGFloat green = ((color>>8)&0xff)*kColorConversionMultiplier;
        CGFloat blue = ((color>>0)&0xff)*kColorConversionMultiplier;
        const CGFloat components[] = {red, green, blue, alpha, 1.0f};
        value = CGColorCreate(colorspace, components);

        colorCacheInfo.keys[index] = color;
        colorCacheInfo.values[index] = value;

        cgColor = value;
    }

    CGContextSetStrokeColorWithColor(qsdo->cgRef, cgColor);
    CGContextSetFillColorWithColor(qsdo->cgRef, cgColor);

    pthread_mutex_unlock(&gColorCacheLock);
}

#pragma mark
#pragma mark --- Gradient ---

// this function MUST NOT be inlined!
void gradientLinearPaintEvaluateFunction(void *info, const CGFloat *in, CGFloat *out)
{
    StateShadingInfo *shadingInfo = (StateShadingInfo *)info;
    CGFloat *colors = shadingInfo->colors;
    CGFloat range = *in;
    CGFloat c1, c2;
    jint k;

//fprintf(stderr, "range=%f\n", range);
    for (k=0; k<4; k++)
    {
        c1 = colors[k];
//fprintf(stderr, "    c1=%f", c1);
        c2 = colors[k+4];
//fprintf(stderr, ", c2=%f", c2);
        if (c1 == c2)
        {
            *out++ = c2;
//fprintf(stderr, ", %f", *(out-1));
        }
        else if (c1 > c2)
        {
            *out++ = c1 - ((c1-c2)*range);
//fprintf(stderr, ", %f", *(out-1));
        }
        else// if (c1 < c2)
        {
            *out++ = c1 + ((c2-c1)*range);
//fprintf(stderr, ", %f", *(out-1));
        }
//fprintf(stderr, "\n");
    }
}

// this function MUST NOT be inlined!
void gradientCyclicPaintEvaluateFunction(void *info, const CGFloat *in, CGFloat *out)
{
    StateShadingInfo *shadingInfo = (StateShadingInfo *)info;
    CGFloat length = shadingInfo->length ;
    CGFloat period = shadingInfo->period;
    CGFloat offset = shadingInfo->offset;
    CGFloat periodLeft = offset;
    CGFloat periodRight = periodLeft+period;
    CGFloat *colors = shadingInfo->colors;
    CGFloat range = *in;
    CGFloat c1, c2;
    jint k;
    jint count = 0;

    range *= length;

    // put the range within the period
    if (range < periodLeft)
    {
        while (range < periodLeft)
        {
            range += period;
            count++;
        }

        range = range-periodLeft;
    }
    else if (range > periodRight)
    {
        count = 1;

        while (range > periodRight)
        {
            range -= period;
            count++;
        }

        range = periodRight-range;
    }
    else
    {
        range = range - offset;
    }
    range = range/period;

    // cycle up or down
    if (count%2 == 0)
    {
        for (k=0; k<4; k++)
        {
            c1 = colors[k];
            c2 = colors[k+4];
            if (c1 == c2)
            {
                *out++ = c2;
            }
            else if (c1 > c2)
            {
                *out++ = c1 - ((c1-c2)*range);
            }
            else// if (c1 < c2)
            {
                *out++ = c1 + ((c2-c1)*range);
            }
        }
    }
    else
    {
        for (k=0; k<4; k++)
        {
            c1 = colors[k+4];
            c2 = colors[k];
            if (c1 == c2)
            {
                *out++ = c2;
            }
            else if (c1 > c2)
            {
                *out++ = c1 - ((c1-c2)*range);
            }
            else// if (c1 < c2)
            {
                *out++ = c1 + ((c2-c1)*range);
            }
        }
    }
 }

// this function MUST NOT be inlined!
void gradientPaintReleaseFunction(void *info)
{
PRINT("    gradientPaintReleaseFunction")
    free(info);
}

static inline void contextGradientPath(QuartzSDOps* qsdo)
{
PRINT("    ContextGradientPath")
    CGContextRef cgRef = qsdo->cgRef;
    StateShadingInfo* shadingInfo = qsdo->shadingInfo;

    CGRect bounds = CGContextGetClipBoundingBox(cgRef);

    static const CGFloat domain[2] = {0.0f, 1.0f};
    static const CGFloat range[8] = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};
    CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    CGFunctionRef shadingFunc = NULL;
    CGShadingRef shading = NULL;
    if (shadingInfo->cyclic == NO)
    {
        static const CGFunctionCallbacks callbacks = {0, &gradientLinearPaintEvaluateFunction, &gradientPaintReleaseFunction};
        shadingFunc = CGFunctionCreate((void *)shadingInfo, 1, domain, 4, range, &callbacks);
        shading = CGShadingCreateAxial(colorspace, shadingInfo->start, shadingInfo->end, shadingFunc, 1, 1);
    }
    else
    {
//fprintf(stderr, "BOUNDING BOX x1=%f, y1=%f x2=%f, y2=%f\n", bounds.origin.x, bounds.origin.y, bounds.origin.x+bounds.size.width, bounds.origin.y+bounds.size.height);
        // need to extend the line start-end

        CGFloat x1 = shadingInfo->start.x;
        CGFloat y1 = shadingInfo->start.y;
        CGFloat x2 = shadingInfo->end.x;
        CGFloat y2 = shadingInfo->end.y;
//fprintf(stderr, "GIVEN x1=%f, y1=%f      x2=%f, y2=%f\n", x1, y1, x2, y2);

        if (x1 == x2)
        {
            y1 = bounds.origin.y;
            y2 = y1 + bounds.size.height;
        }
        else if (y1 == y2)
        {
            x1 = bounds.origin.x;
            x2 = x1 + bounds.size.width;
        }
        else
        {
            // find the original line function y = mx + c
            CGFloat m1 = (y2-y1)/(x2-x1);
            CGFloat c1 = y1 - m1*x1;
//fprintf(stderr, "         m1=%f, c1=%f\n", m1, c1);

            // a line perpendicular to the original one will have the slope
            CGFloat m2 = -(1/m1);
//fprintf(stderr, "         m2=%f\n", m2);

            // find the only 2 possible lines perpendicular to the original line, passing the two top corners of the bounding box
            CGFloat x1A = bounds.origin.x;
            CGFloat y1A = bounds.origin.y;
            CGFloat c1A = y1A - m2*x1A;
//fprintf(stderr, "         x1A=%f, y1A=%f, c1A=%f\n", x1A, y1A, c1A);
            CGFloat x1B = bounds.origin.x+bounds.size.width;
            CGFloat y1B = bounds.origin.y;
            CGFloat c1B = y1B - m2*x1B;
//fprintf(stderr, "         x1B=%f, y1B=%f, c1B=%f\n", x1B, y1B, c1B);

            // find the crossing points of the original line and the two lines we computed above to find the new possible starting points
            CGFloat x1Anew = (c1A-c1)/(m1-m2);
            CGFloat y1Anew = m2*x1Anew + c1A;
            CGFloat x1Bnew = (c1B-c1)/(m1-m2);
            CGFloat y1Bnew = m2*x1Bnew + c1B;
//fprintf(stderr, "NEW x1Anew=%f, y1Anew=%f      x1Bnew=%f, y1Bnew=%f\n", x1Anew, y1Anew, x1Bnew, y1Bnew);

            // select the new starting point
            if (y1Anew <= y1Bnew)
            {
                x1 = x1Anew;
                y1 = y1Anew;
            }
            else
            {
                x1 = x1Bnew;
                y1 = y1Bnew;
            }
//fprintf(stderr, "--- NEW x1=%f, y1=%f\n", x1, y1);

            // find the only 2 possible lines perpendicular to the original line, passing the two bottom corners of the bounding box
            CGFloat x2A = bounds.origin.x;
            CGFloat y2A = bounds.origin.y+bounds.size.height;
            CGFloat c2A = y2A - m2*x2A;
//fprintf(stderr, "         x2A=%f, y2A=%f, c2A=%f\n", x2A, y2A, c2A);
            CGFloat x2B = bounds.origin.x+bounds.size.width;
            CGFloat y2B = bounds.origin.y+bounds.size.height;
            CGFloat c2B = y2B - m2*x2B;
//fprintf(stderr, "         x2B=%f, y2B=%f, c2B=%f\n", x2B, y2B, c2B);

            // find the crossing points of the original line and the two lines we computed above to find the new possible ending points
            CGFloat x2Anew = (c2A-c1)/(m1-m2);
            CGFloat y2Anew = m2*x2Anew + c2A;
            CGFloat x2Bnew = (c2B-c1)/(m1-m2);
            CGFloat y2Bnew = m2*x2Bnew + c2B;
//fprintf(stderr, "NEW x2Anew=%f, y2Anew=%f      x2Bnew=%f, y2Bnew=%f\n", x2Anew, y2Anew, x2Bnew, y2Bnew);

            // select the new ending point
            if (y2Anew >= y2Bnew)
            {
                x2 = x2Anew;
                y2 = y2Anew;
            }
            else
            {
                x2 = x2Bnew;
                y2 = y2Bnew;
            }
//fprintf(stderr, "--- NEW x2=%f, y2=%f\n", x2, y2);
        }

        qsdo->shadingInfo->period = sqrt(pow(shadingInfo->end.x-shadingInfo->start.x, 2.0) + pow(shadingInfo->end.y-shadingInfo->start.y, 2.0));
        if ((qsdo->shadingInfo->period != 0))
        {
            // compute segment lengths that we will need for the gradient function
            qsdo->shadingInfo->length = sqrt(pow(x2-x1, 2.0) + pow(y2-y1, 2.0));
            qsdo->shadingInfo->offset = sqrt(pow(shadingInfo->start.x-x1, 2.0) + pow(shadingInfo->start.y-y1, 2.0));
//fprintf(stderr, "length=%f, period=%f, offset=%f\n", qsdo->shadingInfo->length, qsdo->shadingInfo->period, qsdo->shadingInfo->offset);

            CGPoint newStart = {x1, y1};
            CGPoint newEnd = {x2, y2};

            static const CGFunctionCallbacks callbacks = {0, &gradientCyclicPaintEvaluateFunction, &gradientPaintReleaseFunction};
            shadingFunc = CGFunctionCreate((void *)shadingInfo, 1, domain, 4, range, &callbacks);
            shading = CGShadingCreateAxial(colorspace, newStart, newEnd, shadingFunc, 0, 0);
        }
    }
    CGColorSpaceRelease(colorspace);

    if (shadingFunc != NULL)
    {
        CGContextSaveGState(cgRef);

        // rdar://problem/5214320
        // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
        if (qsdo->isEvenOddFill) {
            CGContextEOClip(cgRef);
        } else {
            CGContextClip(cgRef);
        }
        CGContextDrawShading(cgRef, shading);

        CGContextRestoreGState(cgRef);
        CGShadingRelease(shading);
        CGFunctionRelease(shadingFunc);
        qsdo->shadingInfo = NULL;
    }
}

#pragma mark
#pragma mark --- Texture ---

// this function MUST NOT be inlined!
void texturePaintEvaluateFunction(void *info, CGContextRef cgRef)
{
    JNIEnv* env = [ThreadUtilities getJNIEnvUncached];

    StatePatternInfo* patternInfo = (StatePatternInfo*)info;
    ImageSDOps* isdo = LockImage(env, patternInfo->sdata);

    makeSureImageIsCreated(isdo);
    CGContextDrawImage(cgRef, CGRectMake(0.0f, 0.0f, patternInfo->width, patternInfo->height), isdo->imgRef);

    UnlockImage(env, isdo);
}

// this function MUST NOT be inlined!
void texturePaintReleaseFunction(void *info)
{
    PRINT("    texturePaintReleaseFunction")
    JNIEnv* env = [ThreadUtilities getJNIEnvUncached];

    StatePatternInfo* patternInfo = (StatePatternInfo*)info;
    (*env)->DeleteGlobalRef(env, patternInfo->sdata);

    free(info);
}

static inline void contextTexturePath(JNIEnv* env, QuartzSDOps* qsdo)
{
    PRINT("    ContextTexturePath")
    CGContextRef cgRef = qsdo->cgRef;
    StatePatternInfo* patternInfo = qsdo->patternInfo;

    CGAffineTransform ctm = CGContextGetCTM(cgRef);
    CGAffineTransform ptm = {patternInfo->sx, 0.0f, 0.0f, -patternInfo->sy, patternInfo->tx, patternInfo->ty};
    CGAffineTransform tm = CGAffineTransformConcat(ptm, ctm);
    CGFloat xStep = (CGFloat)qsdo->patternInfo->width;
    CGFloat yStep = (CGFloat)qsdo->patternInfo->height;
    CGPatternTiling tiling = kCGPatternTilingNoDistortion;
    BOOL isColored = YES;
    static const CGPatternCallbacks callbacks = {0, &texturePaintEvaluateFunction, &texturePaintReleaseFunction};
    CGPatternRef pattern = CGPatternCreate((void*)patternInfo, CGRectMake(0.0f, 0.0f, xStep, yStep), tm, xStep, yStep, tiling, isColored, &callbacks);

    CGColorSpaceRef colorspace = CGColorSpaceCreatePattern(NULL);
    static const CGFloat alpha = 1.0f;

    CGContextSaveGState(cgRef);

    CGContextSetFillColorSpace(cgRef, colorspace);
    CGContextSetFillPattern(cgRef, pattern, &alpha);
    CGContextSetRGBStrokeColor(cgRef, 0.0f, 0.0f, 0.0f, 1.0f);
    CGContextSetPatternPhase(cgRef, CGSizeMake(0.0f, 0.0f));
    // rdar://problem/5214320
    // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
    if (qsdo->isEvenOddFill) {
        CGContextEOFillPath(cgRef);
    } else {
        CGContextFillPath(cgRef);
    }

    CGContextRestoreGState(cgRef);

    CGColorSpaceRelease(colorspace);
    CGPatternRelease(pattern);

    qsdo->patternInfo = NULL;
}

#pragma mark
#pragma mark --- Context Setup ---

static inline void setDefaultColorSpace(CGContextRef cgRef)
{
    static CGColorSpaceRef colorspace = NULL;
    if (colorspace == NULL)
    {
        colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    }
    CGContextSetStrokeColorSpace(cgRef, colorspace);
    CGContextSetFillColorSpace(cgRef, colorspace);
}

void SetUpCGContext(JNIEnv *env, QuartzSDOps *qsdo, SDRenderType renderType)
{
PRINT(" SetUpCGContext")
    CGContextRef cgRef = qsdo->cgRef;
//fprintf(stderr, "%p ", cgRef);
    jint *javaGraphicsStates = qsdo->javaGraphicsStates;
    jfloat *javaFloatGraphicsStates = (jfloat*)(qsdo->javaGraphicsStates);

    jint changeFlags            = javaGraphicsStates[sun_java2d_OSXSurfaceData_kChangeFlagIndex];
    BOOL everyThingChanged        = qsdo->newContext || (changeFlags == sun_java2d_OSXSurfaceData_kEverythingChangedFlag);
    BOOL clipChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kClipChangedBit) != 0);
    BOOL transformChanged        = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kCTMChangedBit) != 0);
    BOOL paintChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kColorChangedBit) != 0);
    BOOL compositeChanged        = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kCompositeChangedBit) != 0);
    BOOL strokeChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kStrokeChangedBit) != 0);
//    BOOL fontChanged            = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kFontChangedBit) != 0);
    BOOL renderingHintsChanged  = everyThingChanged || ((changeFlags&sun_java2d_OSXSurfaceData_kHintsChangedBit) != 0);

//fprintf(stderr, "SetUpCGContext cgRef=%p new=%d changeFlags=%d, everyThingChanged=%d clipChanged=%d transformChanged=%d\n",
//                    cgRef, qsdo->newContext, changeFlags, everyThingChanged, clipChanged, transformChanged);

    if ((everyThingChanged == YES) || (clipChanged == YES) || (transformChanged == YES))
    {
        everyThingChanged = YES; // in case clipChanged or transformChanged

        CGContextRestoreGState(cgRef);  // restore to the original state

        CGContextSaveGState(cgRef);        // make our local copy of the state

        setDefaultColorSpace(cgRef);
    }

    if ((everyThingChanged == YES) || (clipChanged == YES))
    {
        if (javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipStateIndex] == sun_java2d_OSXSurfaceData_kClipRect)
        {
            CGFloat x = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipXIndex];
            CGFloat y = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipYIndex];
            CGFloat w = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipWidthIndex];
            CGFloat h = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kClipHeightIndex];
            CGContextClipToRect(cgRef, CGRectMake(x, y, w, h));
        }
        else
        {
            BOOL eoFill = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipWindingRuleIndex] == java_awt_geom_PathIterator_WIND_EVEN_ODD);
            jint numtypes = javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipNumTypesIndex];

            jobject coordsarray = (jobject)((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kClipCoordinatesIndex));
            jobject typesarray = (jobject)((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kClipTypesIndex));

            jfloat* coords = (jfloat*)(*env)->GetDirectBufferAddress(env, coordsarray);
            jint* types = (jint*)(*env)->GetDirectBufferAddress(env, typesarray);

            DoShapeUsingCG(cgRef, types, coords, numtypes, NO, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);

            if (CGContextIsPathEmpty(cgRef) == 0)
            {
                if (eoFill)
                {
                    CGContextEOClip(cgRef);
                }
                else
                {
                    CGContextClip(cgRef);
                }
            }
            else
            {
                CGContextClipToRect(cgRef, CGRectZero);
            }
        }
    }
// for debugging
//CGContextResetClip(cgRef);

    if ((everyThingChanged == YES) || (transformChanged == YES))
    {
        CGFloat a = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMaIndex];
        CGFloat b = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMbIndex];
        CGFloat c = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMcIndex];
        CGFloat d = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMdIndex];
        CGFloat tx = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMtxIndex];
        CGFloat ty = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCTMtyIndex];

        CGContextConcatCTM(cgRef, CGAffineTransformMake(a, b, c, d, tx, ty));

        if (gAdjustForJavaDrawing == YES)
        {
            // find the offsets in the device corrdinate system
            CGAffineTransform ctm = CGContextGetCTM(cgRef);
            if ((qsdo->graphicsStateInfo.ctm.a != ctm.a) ||
                    (qsdo->graphicsStateInfo.ctm.b != ctm.b) ||
                        (qsdo->graphicsStateInfo.ctm.c != ctm.c) ||
                            (qsdo->graphicsStateInfo.ctm.d != ctm.d))
            {
                qsdo->graphicsStateInfo.ctm = ctm;
                // In CG affine xforms y' = bx+dy+ty
                // We need to flip both y coefficeints to flip the offset point into the java coordinate system.
                ctm.b = -ctm.b; ctm.d = -ctm.d; ctm.tx = 0.0f; ctm.ty = 0.0f;
                CGPoint offsets = {kOffset, kOffset};
                CGAffineTransform inverse = CGAffineTransformInvert(ctm);
                offsets = CGPointApplyAffineTransform(offsets, inverse);
                qsdo->graphicsStateInfo.offsetX = offsets.x;
                qsdo->graphicsStateInfo.offsetY = offsets.y;
            }
        }
        else
        {
            qsdo->graphicsStateInfo.offsetX = 0.0f;
            qsdo->graphicsStateInfo.offsetY = 0.0f;
        }
    }

// for debugging
//CGContextResetCTM(cgRef);

    if ((everyThingChanged == YES) || (compositeChanged == YES))
    {
        jint alphaCompositeRule = javaGraphicsStates[sun_java2d_OSXSurfaceData_kCompositeRuleIndex];
        CGFloat alphaCompositeValue = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCompositeValueIndex];

        NSCompositingOperation op;
        switch (alphaCompositeRule)
        {
                case java_awt_AlphaComposite_CLEAR:
                op = NSCompositeClear;
                break;
            case java_awt_AlphaComposite_SRC:
                op = NSCompositeCopy;
                break;
            case java_awt_AlphaComposite_SRC_OVER:
                op = NSCompositeSourceOver;
                break;
            case java_awt_AlphaComposite_DST_OVER:
                op = NSCompositeDestinationOver;
                break;
            case java_awt_AlphaComposite_SRC_IN:
                op = NSCompositeSourceIn;
                break;
            case java_awt_AlphaComposite_DST_IN:
                op = NSCompositeDestinationIn;
                break;
            case java_awt_AlphaComposite_SRC_OUT:
                op = NSCompositeSourceOut;
                break;
            case java_awt_AlphaComposite_DST_OUT:
                op = NSCompositeDestinationOut;
                break;
            case java_awt_AlphaComposite_DST:
                // Alpha must be set to 0 because we're using the kCGCompositeSover rule
                op = NSCompositeSourceOver;
                alphaCompositeValue = 0.0f;
                break;
            case java_awt_AlphaComposite_SRC_ATOP:
                op = NSCompositeSourceAtop;
                break;
            case java_awt_AlphaComposite_DST_ATOP:
                op = NSCompositeDestinationAtop;
                break;
            case java_awt_AlphaComposite_XOR:
                op = NSCompositeXOR;
                break;
            default:
                op = NSCompositeSourceOver;
                alphaCompositeValue = 1.0f;
                break;
        }

        NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithGraphicsPort:cgRef flipped:NO];
        //CGContextSetCompositeOperation(cgRef, op);
        [context setCompositingOperation:op];
        CGContextSetAlpha(cgRef, alphaCompositeValue);
    }

    if ((everyThingChanged == YES) || (renderingHintsChanged == YES))
    {
        jint antialiasHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsAntialiasIndex];
//        jint textAntialiasHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsTextAntialiasIndex];
        jint renderingHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsRenderingIndex];
        jint interpolationHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsInterpolationIndex];
//        jint textFractionalMetricsHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsFractionalMetricsIndex];

        // 10-10-02 VL: since CoreGraphics supports only an interpolation quality attribute we have to map
        // both interpolationHint and renderingHint to an attribute value that best represents their combination.
        // (See Radar 3071704.) We'll go for the best quality. CG maps interpolation quality values as follows:
        // kCGInterpolationNone - nearest_neighbor
        // kCGInterpolationLow - bilinear
        // kCGInterpolationHigh - Lanczos (better than bicubic)
        CGInterpolationQuality interpolationQuality = kCGInterpolationDefault;
        // First check if the interpolation hint is suggesting to turn off interpolation:
        if (interpolationHint == sun_awt_SunHints_INTVAL_INTERPOLATION_NEAREST_NEIGHBOR)
        {
            interpolationQuality = kCGInterpolationNone;
        }
        else if ((interpolationHint >= sun_awt_SunHints_INTVAL_INTERPOLATION_BICUBIC) || (renderingHint >= sun_awt_SunHints_INTVAL_RENDER_QUALITY))
        {
            // Use >= just in case Sun adds some hint values in the future - this check wouldn't fall apart then:
            interpolationQuality = kCGInterpolationHigh;
        }
        else if (interpolationHint == sun_awt_SunHints_INTVAL_INTERPOLATION_BILINEAR)
        {
            interpolationQuality = kCGInterpolationLow;
        }
        else if (renderingHint == sun_awt_SunHints_INTVAL_RENDER_SPEED)
        {
            interpolationQuality = kCGInterpolationNone;
        }
        // else interpolationHint == -1 || renderingHint == sun_awt_SunHints_INTVAL_CSURFACE_DEFAULT --> kCGInterpolationDefault
        CGContextSetInterpolationQuality(cgRef, interpolationQuality);
        qsdo->graphicsStateInfo.interpolation = interpolationQuality;

        // antialiasing
        BOOL antialiased = (antialiasHint == sun_awt_SunHints_INTVAL_ANTIALIAS_ON);
        CGContextSetShouldAntialias(cgRef, antialiased);
        qsdo->graphicsStateInfo.antialiased = antialiased;
    }

    if ((everyThingChanged == YES) || (strokeChanged == YES))
    {
        qsdo->graphicsStateInfo.simpleStroke = YES;

        CGFloat linewidth = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeWidthIndex];
        jint linejoin = javaGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeJoinIndex];
        jint linecap = javaGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeCapIndex];
        CGFloat miterlimit = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeLimitIndex];
        jobject dasharray = ((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kStrokeDashArrayIndex));
        CGFloat dashphase = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kStrokeDashPhaseIndex];

        if (linewidth == 0.0f)
        {
            linewidth = (CGFloat)-109.05473e+14; // Don't ask !
        }
        CGContextSetLineWidth(cgRef, linewidth);

        CGLineCap cap;
        switch (linecap)
        {
            case java_awt_BasicStroke_CAP_BUTT:
                qsdo->graphicsStateInfo.simpleStroke = NO;
                cap = kCGLineCapButt;
                break;
            case java_awt_BasicStroke_CAP_ROUND:
                qsdo->graphicsStateInfo.simpleStroke = NO;
                cap = kCGLineCapRound;
                break;
            case java_awt_BasicStroke_CAP_SQUARE:
            default:
                cap = kCGLineCapSquare;
                break;
        }
        CGContextSetLineCap(cgRef, cap);

        CGLineJoin join;
        switch (linejoin)
        {
            case java_awt_BasicStroke_JOIN_ROUND:
                qsdo->graphicsStateInfo.simpleStroke = NO;
                join = kCGLineJoinRound;
                break;
            case java_awt_BasicStroke_JOIN_BEVEL:
                qsdo->graphicsStateInfo.simpleStroke = NO;
                join = kCGLineJoinBevel;
                break;
            case java_awt_BasicStroke_JOIN_MITER:
            default:
                join = kCGLineJoinMiter;
                break;
        }
        CGContextSetLineJoin(cgRef, join);
        CGContextSetMiterLimit(cgRef, miterlimit);

        if (dasharray != NULL)
        {
            qsdo->graphicsStateInfo.simpleStroke = NO;
            jint length = (*env)->GetArrayLength(env, dasharray);
            jfloat* jdashes = (jfloat*)(*env)->GetPrimitiveArrayCritical(env, dasharray, NULL);
            CGFloat* dashes = (CGFloat*)malloc(sizeof(CGFloat)*length);
            if (dashes != NULL)
            {
                jint i;
                for (i=0; i<length; i++)
                {
                    dashes[i] = (CGFloat)jdashes[i];
                }
            }
            else
            {
                dashphase = 0;
                length = 0;
            }
            CGContextSetLineDash(cgRef, dashphase, dashes, length);
            if (dashes != NULL)
            {
                free(dashes);
            }
            (*env)->ReleasePrimitiveArrayCritical(env, dasharray, jdashes, 0);
        }
        else
        {
            CGContextSetLineDash(cgRef, 0, NULL, 0);
        }
    }

    BOOL cocoaPaint = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorSystem);
    BOOL complexPaint = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorGradient) ||
                        (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex] == sun_java2d_OSXSurfaceData_kColorTexture);
    if ((everyThingChanged == YES) || (paintChanged == YES) || (cocoaPaint == YES) || (complexPaint == YES))
    {
        // rdar://problem/5214320
        // Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
        // Notice the side effect of the stmt after this if-block.
        if (renderType == SD_EOFill) {
            qsdo->isEvenOddFill = YES;
        }

        renderType = SetUpPaint(env, qsdo, renderType);
    }

    qsdo->renderType = renderType;
}

SDRenderType SetUpPaint(JNIEnv *env, QuartzSDOps *qsdo, SDRenderType renderType)
{
    CGContextRef cgRef = qsdo->cgRef;

    jint *javaGraphicsStates = qsdo->javaGraphicsStates;
    jfloat *javaFloatGraphicsStates = (jfloat*)(qsdo->javaGraphicsStates);

    static const CGFloat kColorConversionMultiplier = 1.0f/255.0f;
    jint colorState = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorStateIndex];

    switch (colorState)
    {
        case sun_java2d_OSXSurfaceData_kColorSimple:
        {
            if (qsdo->graphicsStateInfo.simpleColor == NO)
            {
                setDefaultColorSpace(cgRef);
            }
            qsdo->graphicsStateInfo.simpleColor = YES;

            // sets the color on the CGContextRef (CGContextSetStrokeColorWithColor/CGContextSetFillColorWithColor)
            setCachedColor(qsdo, javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValueIndex]);

            break;
        }
        case sun_java2d_OSXSurfaceData_kColorSystem:
        {
            qsdo->graphicsStateInfo.simpleStroke = NO;
            // All our custom Colors are NSPatternColorSpace so we are complex colors!
            qsdo->graphicsStateInfo.simpleColor = NO;

            NSColor *color = nil;
            /* TODO:BG
            {
                color = getColor(javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorIndexValueIndex]);
            }
            */
            [color set];
            break;
        }
        case sun_java2d_OSXSurfaceData_kColorGradient:
        {
            qsdo->shadingInfo = (StateShadingInfo*)malloc(sizeof(StateShadingInfo));
            if (qsdo->shadingInfo == NULL)
            {
                [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory for gradient paint"];
            }

            qsdo->graphicsStateInfo.simpleStroke = NO;
            qsdo->graphicsStateInfo.simpleColor = NO;

            renderType = SD_Shade;

            qsdo->shadingInfo->start.x    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorx1Index];
            qsdo->shadingInfo->start.y    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColory1Index];
            qsdo->shadingInfo->end.x    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorx2Index];
            qsdo->shadingInfo->end.y    = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColory2Index];
            jint c1 = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValue1Index];
            qsdo->shadingInfo->colors[0] = ((c1>>16)&0xff)*kColorConversionMultiplier;
            qsdo->shadingInfo->colors[1] = ((c1>>8)&0xff)*kColorConversionMultiplier;
            qsdo->shadingInfo->colors[2] = ((c1>>0)&0xff)*kColorConversionMultiplier;
            qsdo->shadingInfo->colors[3] = ((c1>>24)&0xff)*kColorConversionMultiplier;
            jint c2 = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorRGBValue2Index];
            qsdo->shadingInfo->colors[4] = ((c2>>16)&0xff)*kColorConversionMultiplier;
            qsdo->shadingInfo->colors[5] = ((c2>>8)&0xff)*kColorConversionMultiplier;
            qsdo->shadingInfo->colors[6] = ((c2>>0)&0xff)*kColorConversionMultiplier;
            qsdo->shadingInfo->colors[7] = ((c2>>24)&0xff)*kColorConversionMultiplier;
            qsdo->shadingInfo->cyclic    = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorIsCyclicIndex] == sun_java2d_OSXSurfaceData_kColorCyclic);

            break;
        }
        case sun_java2d_OSXSurfaceData_kColorTexture:
        {
            qsdo->patternInfo = (StatePatternInfo*)malloc(sizeof(StatePatternInfo));
            if (qsdo->patternInfo == NULL)
            {
                [JNFException raise:env as:kOutOfMemoryError reason:"Failed to malloc memory for texture paint"];
            }

            qsdo->graphicsStateInfo.simpleStroke = NO;
            qsdo->graphicsStateInfo.simpleColor = NO;

            renderType = SD_Pattern;

            qsdo->patternInfo->tx        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColortxIndex];
            qsdo->patternInfo->ty        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColortyIndex];
            qsdo->patternInfo->sx        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorsxIndex];
            if (qsdo->patternInfo->sx == 0.0f)
            {
                return SD_Fill; // 0 is an invalid value, fill argb rect
            }
            qsdo->patternInfo->sy        = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kColorsyIndex];
            if (qsdo->patternInfo->sy == 0.0f)
            {
                return SD_Fill; // 0 is an invalid value, fill argb rect
            }
            qsdo->patternInfo->width    = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorWidthIndex];
            qsdo->patternInfo->height    = javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorHeightIndex];

            jobject sData = ((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kTextureImageIndex)); //deleted next time through SetUpPaint and not before ( radr://3913190 )
            if (sData != NULL)
            {
                qsdo->patternInfo->sdata = (*env)->NewGlobalRef(env, sData);
                if (qsdo->patternInfo->sdata == NULL)
                {
                    renderType = SD_Fill;
                }
            }
            else
            {
                renderType = SD_Fill;
            }

            break;
        }
    }

    return renderType;
}

#pragma mark
#pragma mark --- Shape Drawing Code ---

SDRenderType DoShapeUsingCG(CGContextRef cgRef, jint *types, jfloat *coords, jint numtypes, BOOL fill, CGFloat offsetX, CGFloat offsetY)
{
//fprintf(stderr, "DoShapeUsingCG fill=%d\n", (jint)fill);
    SDRenderType renderType = SD_Nothing;

    if (gAdjustForJavaDrawing != YES)
    {
        offsetX = 0.0f;
        offsetY = 0.0f;
    }

    if (fill == YES)
    {
        renderType = SD_Fill;
    }
    else
    {
        renderType = SD_Stroke;
    }

    if (numtypes > 0)
    {
        BOOL needNewSubpath = NO;

        CGContextBeginPath(cgRef); // create new path
//fprintf(stderr, "    CGContextBeginPath\n");

        jint index = 0;
        CGFloat mx = 0.0f, my = 0.0f, x1 = 0.0f, y1 = 0.0f, cpx1 = 0.0f, cpy1 = 0.0f, cpx2 = 0.0f, cpy2 = 0.0f;
        jint i;

        mx = (CGFloat)coords[index++] + offsetX;
        my = (CGFloat)coords[index++] + offsetY;
        CGContextMoveToPoint(cgRef, mx, my);

        for (i=1; i<numtypes; i++)
        {
            jint pathType = types[i];

            if (needNewSubpath == YES)
            {
                needNewSubpath = NO;
                switch (pathType)
                {
                    case java_awt_geom_PathIterator_SEG_LINETO:
                    case java_awt_geom_PathIterator_SEG_QUADTO:
                    case java_awt_geom_PathIterator_SEG_CUBICTO:
//fprintf(stderr, "    forced CGContextMoveToPoint (%f, %f)\n", mx, my);
                        CGContextMoveToPoint(cgRef, mx, my); // force new subpath
                        break;
                }
            }

            switch (pathType)
            {
                case java_awt_geom_PathIterator_SEG_MOVETO:
                    mx = x1 = (CGFloat)coords[index++] + offsetX;
                    my = y1 = (CGFloat)coords[index++] + offsetY;
                    CGContextMoveToPoint(cgRef, x1, y1); // start new subpath
//fprintf(stderr, "    SEG_MOVETO CGContextMoveToPoint (%f, %f)\n", x1, y1);
                    break;
                case java_awt_geom_PathIterator_SEG_LINETO:
                    x1 = (CGFloat)coords[index++] + offsetX;
                    y1 = (CGFloat)coords[index++] + offsetY;
                    CGContextAddLineToPoint(cgRef, x1, y1);
//fprintf(stderr, "    SEG_LINETO CGContextAddLineToPoint (%f, %f)\n", x1, y1);
                    break;
                case java_awt_geom_PathIterator_SEG_QUADTO:
                    cpx1 = (CGFloat)coords[index++] + offsetX;
                    cpy1 = (CGFloat)coords[index++] + offsetY;
                    x1 = (CGFloat)coords[index++] + offsetX;
                    y1 = (CGFloat)coords[index++]+ offsetY;
                    CGContextAddQuadCurveToPoint(cgRef, cpx1, cpy1, x1, y1);
//fprintf(stderr, "    SEG_QUADTO CGContextAddQuadCurveToPoint (%f, %f), (%f, %f)\n", cpx1, cpy1, x1, y1);
                    break;
                case java_awt_geom_PathIterator_SEG_CUBICTO:
                    cpx1 = (CGFloat)coords[index++] + offsetX;
                    cpy1 = (CGFloat)coords[index++] + offsetY;
                    cpx2 = (CGFloat)coords[index++] + offsetX;
                    cpy2 = (CGFloat)coords[index++] + offsetY;
                    x1 = (CGFloat)coords[index++] + offsetX;
                    y1 = (CGFloat)coords[index++] + offsetY;
                    CGContextAddCurveToPoint(cgRef, cpx1, cpy1, cpx2, cpy2, x1, y1);
//fprintf(stderr, "    SEG_CUBICTO CGContextAddCurveToPoint (%f, %f), (%f, %f), (%f, %f)\n", cpx1, cpy1, cpx2, cpy2, x1, y1);
                    break;
                case java_awt_geom_PathIterator_SEG_CLOSE:
                    CGContextClosePath(cgRef); // close subpath
                    needNewSubpath = YES;
//fprintf(stderr, "    SEG_CLOSE CGContextClosePath\n");
                    break;
            }
        }
    }

    return renderType;
}

void CompleteCGContext(JNIEnv *env, QuartzSDOps *qsdo)
{
PRINT(" CompleteCGContext")
    switch (qsdo->renderType)
    {
        case SD_Nothing:
            break;

        case SD_Stroke:
            if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
            {
                CGContextStrokePath(qsdo->cgRef);
            }
            break;

        case SD_Fill:
            if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
            {
                CGContextFillPath(qsdo->cgRef);
            }
            break;

        case SD_Shade:
            if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
            {
                contextGradientPath(qsdo);
            }
            break;

        case SD_Pattern:
            if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
            {
                //TODO:BG
                //contextTexturePath(env, qsdo);
            }
            break;

        case SD_EOFill:
            if (CGContextIsPathEmpty(qsdo->cgRef) == 0)
            {
                CGContextEOFillPath(qsdo->cgRef);
            }
            break;

        case SD_Image:
            break;

        case SD_Text:
            break;

        case SD_CopyArea:
            break;

        case SD_Queue:
            break;

        case SD_External:
            break;
    }

    if (qsdo->shadingInfo != NULL) {
        gradientPaintReleaseFunction(qsdo->shadingInfo);
        qsdo->shadingInfo = NULL;
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java QuartzSurfaceData.m 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.