/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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 <AppKit/AppKit.h>
#import "ThreadUtilities.h"
//#define DEBUG
#else
// 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
{
};
// 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,
{
if (colorspace == 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.
index = index & (gColorCacheSize - 1); // The bits are scrambled, we just need to make sure it fits inside our table
{
//fprintf(stderr, "+");fflush(stderr);//hit
}
else
{
{
//fprintf(stderr, "!");fflush(stderr);//miss and replace - double ouch
}
//fprintf(stderr, "-");fflush(stderr);// miss
}
}
// this function MUST NOT be inlined!
{
jint k;
//fprintf(stderr, "range=%f\n", range);
for (k=0; k<4; k++)
{
//fprintf(stderr, " c1=%f", c1);
//fprintf(stderr, ", c2=%f", c2);
{
//fprintf(stderr, ", %f", *(out-1));
}
{
//fprintf(stderr, ", %f", *(out-1));
}
else// if (c1 < c2)
{
//fprintf(stderr, ", %f", *(out-1));
}
//fprintf(stderr, "\n");
}
}
// this function MUST NOT be inlined!
{
jint k;
// put the range within the period
if (range < periodLeft)
{
while (range < periodLeft)
{
count++;
}
}
else if (range > periodRight)
{
count = 1;
while (range > periodRight)
{
count++;
}
}
else
{
}
// cycle up or down
{
for (k=0; k<4; k++)
{
{
}
{
}
else// if (c1 < c2)
{
}
}
}
else
{
for (k=0; k<4; k++)
{
{
}
{
}
else// if (c1 < c2)
{
}
}
}
}
// this function MUST NOT be inlined!
void gradientPaintReleaseFunction(void *info)
{
PRINT(" gradientPaintReleaseFunction")
}
{
PRINT(" ContextGradientPath")
{
static const CGFunctionCallbacks callbacks = {0, &gradientLinearPaintEvaluateFunction, &gradientPaintReleaseFunction};
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
//fprintf(stderr, "GIVEN x1=%f, y1=%f x2=%f, y2=%f\n", x1, y1, x2, y2);
{
}
{
}
else
{
// find the original line function y = mx + c
//fprintf(stderr, " m1=%f, c1=%f\n", m1, c1);
// a line perpendicular to the original one will have the slope
//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
//fprintf(stderr, " x1A=%f, y1A=%f, c1A=%f\n", x1A, y1A, c1A);
//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
//fprintf(stderr, "NEW x1Anew=%f, y1Anew=%f x1Bnew=%f, y1Bnew=%f\n", x1Anew, y1Anew, x1Bnew, y1Bnew);
// select the new starting point
{
}
else
{
}
//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
//fprintf(stderr, " x2A=%f, y2A=%f, c2A=%f\n", x2A, y2A, c2A);
//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
//fprintf(stderr, "NEW x2Anew=%f, y2Anew=%f x2Bnew=%f, y2Bnew=%f\n", x2Anew, y2Anew, x2Bnew, y2Bnew);
// select the new ending point
{
}
else
{
}
//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));
{
// compute segment lengths that we will need for the gradient function
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);
static const CGFunctionCallbacks callbacks = {0, &gradientCyclicPaintEvaluateFunction, &gradientPaintReleaseFunction};
}
}
if (shadingFunc != NULL)
{
// rdar://problem/5214320
// Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
if (qsdo->isEvenOddFill) {
} else {
}
}
}
// this function MUST NOT be inlined!
{
CGContextDrawImage(cgRef, CGRectMake(0.0f, 0.0f, patternInfo->width, patternInfo->height), isdo->imgRef);
}
// this function MUST NOT be inlined!
void texturePaintReleaseFunction(void *info)
{
PRINT(" texturePaintReleaseFunction")
}
{
PRINT(" ContextTexturePath")
CGAffineTransform ptm = {patternInfo->sx, 0.0f, 0.0f, -patternInfo->sy, patternInfo->tx, patternInfo->ty};
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);
// rdar://problem/5214320
// Gradient fills of Java GeneralPath don't respect the even odd winding rule (quartz pipeline).
if (qsdo->isEvenOddFill) {
} else {
}
}
{
if (colorspace == NULL)
{
}
}
{
PRINT(" SetUpCGContext")
//fprintf(stderr, "%p ", cgRef);
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 (javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipStateIndex] == sun_java2d_OSXSurfaceData_kClipRect)
{
}
else
{
BOOL eoFill = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kClipWindingRuleIndex] == java_awt_geom_PathIterator_WIND_EVEN_ODD);
jobject coordsarray = (jobject)((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kClipCoordinatesIndex));
jobject typesarray = (jobject)((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kClipTypesIndex));
DoShapeUsingCG(cgRef, types, coords, numtypes, NO, qsdo->graphicsStateInfo.offsetX, qsdo->graphicsStateInfo.offsetY);
{
if (eoFill)
{
}
else
{
}
}
else
{
}
}
}
// for debugging
//CGContextResetClip(cgRef);
{
if (gAdjustForJavaDrawing == YES)
{
// find the offsets in the device corrdinate system
{
// 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.
}
}
else
{
}
}
// for debugging
//CGContextResetCTM(cgRef);
{
CGFloat alphaCompositeValue = javaFloatGraphicsStates[sun_java2d_OSXSurfaceData_kCompositeValueIndex];
switch (alphaCompositeRule)
{
break;
break;
break;
break;
break;
break;
break;
break;
// Alpha must be set to 0 because we're using the kCGCompositeSover rule
alphaCompositeValue = 0.0f;
break;
break;
break;
op = NSCompositeXOR;
break;
default:
alphaCompositeValue = 1.0f;
break;
}
//CGContextSetCompositeOperation(cgRef, op);
}
{
// jint textAntialiasHint = javaGraphicsStates[sun_java2d_OSXSurfaceData_kHintsTextAntialiasIndex];
// 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)
// First check if the interpolation hint is suggesting to turn off interpolation:
{
}
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:
}
{
}
else if (renderingHint == sun_awt_SunHints_INTVAL_RENDER_SPEED)
{
}
// else interpolationHint == -1 || renderingHint == sun_awt_SunHints_INTVAL_CSURFACE_DEFAULT --> kCGInterpolationDefault
// antialiasing
}
{
jobject dasharray = ((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kStrokeDashArrayIndex));
if (linewidth == 0.0f)
{
}
switch (linecap)
{
break;
break;
default:
break;
}
switch (linejoin)
{
break;
break;
default:
break;
}
{
{
jint i;
for (i=0; i<length; i++)
{
}
}
else
{
dashphase = 0;
length = 0;
}
{
}
}
else
{
}
}
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) {
}
}
}
{
switch (colorState)
{
{
{
}
// sets the color on the CGContextRef (CGContextSetStrokeColorWithColor/CGContextSetFillColorWithColor)
break;
}
{
// All our custom Colors are NSPatternColorSpace so we are complex colors!
/* TODO:BG
{
color = getColor(javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorIndexValueIndex]);
}
*/
break;
}
{
{
}
qsdo->shadingInfo->cyclic = (javaGraphicsStates[sun_java2d_OSXSurfaceData_kColorIsCyclicIndex] == sun_java2d_OSXSurfaceData_kColorCyclic);
break;
}
{
{
}
{
return SD_Fill; // 0 is an invalid value, fill argb rect
}
{
return SD_Fill; // 0 is an invalid value, fill argb rect
}
jobject sData = ((*env)->GetObjectArrayElement(env, qsdo->javaGraphicsStatesObjects, sun_java2d_OSXSurfaceData_kTextureImageIndex)); //deleted next time through SetUpPaint and not before ( radr://3913190 )
{
{
}
}
else
{
}
break;
}
}
return renderType;
}
SDRenderType DoShapeUsingCG(CGContextRef cgRef, jint *types, jfloat *coords, jint numtypes, BOOL fill, CGFloat offsetX, CGFloat offsetY)
{
//fprintf(stderr, "DoShapeUsingCG fill=%d\n", (jint)fill);
if (gAdjustForJavaDrawing != YES)
{
offsetX = 0.0f;
offsetY = 0.0f;
}
{
}
else
{
}
if (numtypes > 0)
{
//fprintf(stderr, " CGContextBeginPath\n");
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;
for (i=1; i<numtypes; i++)
{
if (needNewSubpath == YES)
{
needNewSubpath = NO;
switch (pathType)
{
//fprintf(stderr, " forced CGContextMoveToPoint (%f, %f)\n", mx, my);
break;
}
}
switch (pathType)
{
//fprintf(stderr, " SEG_MOVETO CGContextMoveToPoint (%f, %f)\n", x1, y1);
break;
//fprintf(stderr, " SEG_LINETO CGContextAddLineToPoint (%f, %f)\n", x1, y1);
break;
//fprintf(stderr, " SEG_QUADTO CGContextAddQuadCurveToPoint (%f, %f), (%f, %f)\n", cpx1, cpy1, x1, y1);
break;
//fprintf(stderr, " SEG_CUBICTO CGContextAddCurveToPoint (%f, %f), (%f, %f), (%f, %f)\n", cpx1, cpy1, cpx2, cpy2, x1, y1);
break;
//fprintf(stderr, " SEG_CLOSE CGContextClosePath\n");
break;
}
}
}
return renderType;
}
{
PRINT(" CompleteCGContext")
switch (qsdo->renderType)
{
case SD_Nothing:
break;
case SD_Stroke:
{
}
break;
case SD_Fill:
{
}
break;
case SD_Shade:
{
}
break;
case SD_Pattern:
{
//TODO:BG
//contextTexturePath(env, qsdo);
}
break;
case SD_EOFill:
{
}
break;
case SD_Image:
break;
case SD_Text:
break;
case SD_CopyArea:
break;
case SD_Queue:
break;
case SD_External:
break;
}
}
}