| /* |
| * 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); |
| if (jdashes == NULL) { |
| CGContextSetLineDash(cgRef, 0, NULL, 0); |
| return; |
| } |
| 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; |
| } |
| } |