diff --git a/AppKit/CoreGraphics/CGColor.j b/AppKit/CoreGraphics/CGColor.j index 321d091b6..7b8ee6088 100644 --- a/AppKit/CoreGraphics/CGColor.j +++ b/AppKit/CoreGraphics/CGColor.j @@ -108,7 +108,7 @@ function CGColorCreateCopy(aColor) */ function CGColorCreateGenericGray(gray, alpha) { - return CGColorCreate(0, [gray, alpha]); + return CGColorCreate(CGColorSpaceCreateDeviceRGB(), [gray,gray,gray, alpha]); } /*! @@ -122,7 +122,7 @@ function CGColorCreateGenericGray(gray, alpha) */ function CGColorCreateGenericRGB(red, green, blue, alpha) { - return CGColorCreate(0, [red, green, blue, alpha]); + return CGColorCreate(CGColorSpaceCreateDeviceRGB(), [red, green, blue, alpha]); } /*! @@ -137,7 +137,8 @@ function CGColorCreateGenericRGB(red, green, blue, alpha) */ function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha) { - return CGColorCreate(0, [cyan, magenta, yellow, black, alpha]); + return CGColorCreate(CGColorSpaceCreateDeviceCMYK(), + [cyan, magenta, yellow, black, alpha]); } /*! @@ -149,19 +150,21 @@ function CGColorCreateGenericCMYK(cyan, magenta, yellow, black, alpha) */ function CGColorCreateCopyWithAlpha(aColor, anAlpha) { - var components = aColor.components; + if ( !aColor ) return aColor; // Avoid error null pointer in next line - if (!aColor || anAlpha == components[components.length - 1]) + var components = aColor.components.slice(); + + if (anAlpha == components[components.length - 1]) return aColor; + // set new alpha value now so that a potentially a new cache entry is made and + // not that an existing cache entry is mutated. + components[components.length - 1] = anAlpha; + if (aColor.pattern) - var copy = CGColorCreateWithPattern(aColor.colorspace, aColor.pattern, components); + return CGColorCreateWithPattern(aColor.colorspace, aColor.pattern, components); else - var copy = CGColorCreate(aColor.colorspace, components); - - copy.components[components.length - 1] = anAlpha; - - return copy; + return CGColorCreate(aColor.colorspace, components); } /*! diff --git a/AppKit/CoreGraphics/CGContext.j b/AppKit/CoreGraphics/CGContext.j index 88de992c1..a67d7b200 100644 --- a/AppKit/CoreGraphics/CGContext.j +++ b/AppKit/CoreGraphics/CGContext.j @@ -129,7 +129,7 @@ function CGGStateCreateCopy(aGState) return { alpha:aGState.alpha, strokeStyle:aGState.strokeStyle, fillStyle:aGState.fillStyle, lineWidth:aGState.lineWidth, lineJoin:aGState.lineJoin, lineCap:aGState.lineCap, miterLimit:aGState.miterLimit, globalAlpha:aGState.globalAlpha, blendMode:aGState.blendMode, - shadowOffset:aGState.shadowOffset, shadowBlur:aGState.shadowBlur, shadowColor:aGState.shadowColor, CTM:_CGAffineTransformMakeCopy(aGState.CTM) }; + shadowOffset:_CGSizeMakeCopy(aGState.shadowOffset), shadowBlur:aGState.shadowBlur, shadowColor:aGState.shadowColor, CTM:_CGAffineTransformMakeCopy(aGState.CTM) }; } /*! diff --git a/AppKit/CoreGraphics/CGGradient.j b/AppKit/CoreGraphics/CGGradient.j index e8396aa3d..24d0c3573 100644 --- a/AppKit/CoreGraphics/CGGradient.j +++ b/AppKit/CoreGraphics/CGGradient.j @@ -29,10 +29,15 @@ kCGGradientDrawsAfterEndLocation = 1 << 1; function CGGradientCreateWithColorComponents(aColorSpace, components, locations, count) { - if (locations === NULL) - var locations = [0.0, 1.0]; + if ( locations === undefined || locations === NULL ) + { + var num_of_colors = components.length / 4, + locations = []; + for ( var idx = 0; idx < num_of_colors; idx++ ) + locations.push( idx / (num_of_colors - 1) ); + } - if (count === NULL) + if ( count === undefined || count === NULL) var count = locations.length; var colors = []; diff --git a/Tests/AppKit/CGAffineTransformTest.j b/Tests/AppKit/CGAffineTransformTest.j new file mode 100644 index 000000000..f1d47a034 --- /dev/null +++ b/Tests/AppKit/CGAffineTransformTest.j @@ -0,0 +1,511 @@ +@import +@import "CGTestCase.j" + +@implementation CGAffineTransformTest : CGTestCase + +- (void)testAffineTransformMake +{ + [self compareTransform:CGAffineTransformMake(1, 2, 3, 4, 3.2, 5.4) + with:{ a: 1, b: 2, c:3, d:4, tx:3.2, ty:5.4 } + message:"transform make"]; +} + +- (void)testCGAffineTransformMakeIdentity +{ + [self compareTransform:CGAffineTransformMakeIdentity() + with:{ a: 1, b: 0, c:0, d:1, tx:0, ty:0 } + message:"transform make identity"]; +} + +- (void)testAffineTransformMakeCopy +{ + var transform = CGAffineTransformMake(1, 2, 3, 4, 3.2, 5.4), + t2 = CGAffineTransformMakeCopy(transform); + + transform.a = transform.b = transform.c = transform.d = + transform.tx = transform.ty = 0; + + [self compareTransform:t2 + with:CGAffineTransformMake( 1,2,3,4,3.2,5.4 ) + message:"copy correctly made"]; + + [self compareTransform:transform + with:CGAffineTransformMake( 0, 0, 0, 0, 0, 0 ) + message:"original transform was changed"] ; +} + +- (void)testAffineTransformCreateCopy +{ + // FIXME: CGAffineTransformCreateCopy seems to be working but code has a Fixme? + var transform = CGAffineTransformMake(1, 2, 3, 4, 3.2, 5.4), + t2 = CGAffineTransformCreateCopy(transform); + + transform.a = transform.b = transform.c = transform.d = + transform.tx = transform.ty = 0; + + [self compareTransform:t2 + with:CGAffineTransformMake( 1,2,3,4,3.2,5.4 ) + message:"copy correctly made"]; + + [self compareTransform:transform + with:CGAffineTransformMake( 0, 0, 0, 0, 0, 0 ) + message:"original transform was changed"] ; +} + +- (void)testAffineTransformMakeScale +{ + [self compareTransform:CGAffineTransformMakeScale(3,4) + with:CGAffineTransformMake(3, 0, 0, 4, 0, 0) + message:"make scale"]; +} + +- (void)testAffineTransformMakeTranslation +{ + [self compareTransform:CGAffineTransformMakeTranslation(3,4) + with:CGAffineTransformMake(1,0,0,1,3,4) + message:"make translation"]; +} + +- (void)testAffineTransformTranslate +{ + var transform = CGAffineTransformMakeTranslation(3,4); + + [self compareTransform:CGAffineTransformTranslate(transform,-3,-4) + with:CGAffineTransformMakeIdentity() + message:"translate to identity"]; + + [self compareTransform:CGAffineTransformTranslate(transform,0,0) + with:transform + message:"zero translate"]; +} + +- (void)testAffineTransformScale +{ + var transform = CGAffineTransformMakeScale(3,4); + + [self compareTransform:CGAffineTransformScale( transform, 1/3, 1/4) + with:CGAffineTransformMakeIdentity() + message:"scale to identity"]; + + transform = CGAffineTransformMake(2, -2, -3, 3, 3.2, 5.4); + [self compareTransform:CGAffineTransformScale(transform, 1, 1) + with:transform + message:"scale by 1"]; + + [self compareTransform:CGAffineTransformScale(transform, 2, 5) + with:CGAffineTransformMake(4,-4,-15,15, 3.2,5.4) + message:"random scale to somewhere"]; +} + +- (void)testAffineTransformConcat +{ + var testcases = { + "identity concat" : { + testdata: CGAffineTransformConcat( CGAffineTransformMakeIdentity(), + CGAffineTransformMakeIdentity() ), + expdata: CGAffineTransformMakeIdentity() + }, + + "translation" : { + testdata: CGAffineTransformConcat( CGAffineTransformMakeTranslation(3,4), + CGAffineTransformMakeTranslation(-3,-4) ), + expdata: CGAffineTransformMakeIdentity() + }, + + "translation (reversed)" : { + testdata: CGAffineTransformConcat( CGAffineTransformMakeTranslation(-3,-4), + CGAffineTransformMakeTranslation(3,4) ), + expdata: CGAffineTransformMakeIdentity() + }, + + "scale" : { + testdata: CGAffineTransformConcat(CGAffineTransformMakeScale(3,4), + CGAffineTransformMakeScale(1/3,1/4)), + expdata: CGAffineTransformMakeIdentity() + }, + + "scale (reversed)" : { + testdata: CGAffineTransformConcat(CGAffineTransformMakeScale(1/3,1/4), + CGAffineTransformMakeScale(3,4)), + expdata: CGAffineTransformMakeIdentity() + }, + + "rotation" : { + testdata: CGAffineTransformConcat(CGAffineTransformMakeRotation(Math.PI), + CGAffineTransformMakeRotation(-Math.PI)), + expdata: CGAffineTransformMakeIdentity() + }, + }; + + for ( var key in testcases ) + [self compareTransform:testcases[key].testdata with:testcases[key].expdata message:key]; +} + +- (void)testPointApplyAffineTransform +{ + var testcases = { + "translate to zero" : { + testdata: CGPointApplyAffineTransform( CGPointMake( 3, 4 ), + CGAffineTransformMakeTranslation(-3,-4)), + expdata: CGPointMakeZero() + }, + + "scale to 1,1" : { + testdata: CGPointApplyAffineTransform( CGPointMake( 3, 4 ), + CGAffineTransformMakeScale(1/3,1/4) ), + expdata: CGPointMake(1,1) + }, + + "scale and translate to zero" : { + testdata: CGPointApplyAffineTransform( CGPointMake( 3, 4 ), + CGAffineTransformConcat( + CGAffineTransformMakeScale(1/3,1/4), + CGAffineTransformMakeTranslation(-1,-1))), + expdata: CGPointMakeZero() + + }, + }; + + for ( var key in testcases ) + [self comparePoint:testcases[key].testdata with:testcases[key].expdata message:key]; +} + +- (void)testSizeApplyAffineTransform +{ + var testcases = { + "translation on size should do nothing" : { + testdata: CGSizeApplyAffineTransform( CGSizeMake(3, 12), + CGAffineTransformMakeTranslation(-3,-4)), + expdata: CGSizeMake(3, 12) + }, + + "scale to 1,1" : { + testdata: CGSizeApplyAffineTransform( CGSizeMake( 3, 4 ), + CGAffineTransformMakeScale(1/3,1/4) ), + expdata: CGSizeMake(1, 1) + }, + + "scale and translate combined" : { + testdata: CGSizeApplyAffineTransform( CGSizeMake( 3, 4 ), + CGAffineTransformConcat( + CGAffineTransformMakeScale(1/3,1/4), + CGAffineTransformMakeTranslation(-1,-1))), + expdata: CGSizeMake(1, 1) + + }, + }; + + for ( var key in testcases ) + [self compareSize:testcases[key].testdata with:testcases[key].expdata message:key]; +} + +- (void)testAffineTransformIsIdentityPositive +{ + var testcases = { + "identity is identity" : { + testdata: CGAffineTransformMakeIdentity() + }, + + "zero rotation is identity" : { + testdata: CGAffineTransformMakeRotation(0), + }, + + "zero translation is identity" : { + testdata: CGAffineTransformMakeTranslation(0,0) + }, + + "one scale is identity" : { + testdata: CGAffineTransformMakeScale(1,1) + }, + + "identity concat'ed" : { + testdata: CGAffineTransformConcat( CGAffineTransformMakeIdentity(), + CGAffineTransformMakeIdentity() ), + }, + + "translation" : { + testdata: CGAffineTransformConcat( CGAffineTransformMakeTranslation(3,4), + CGAffineTransformMakeTranslation(-3,-4) ), + }, + + "scale" : { + testdata: CGAffineTransformConcat(CGAffineTransformMakeScale(3,4), + CGAffineTransformMakeScale(1/3,1/4)), + }, + + "rotation" : { + testdata: CGAffineTransformConcat(CGAffineTransformMakeRotation(-Math.PI), + CGAffineTransformMakeRotation(Math.PI)), + }, + + }; + + for ( var key in testcases ) + [self assert:YES + equals:CGAffineTransformIsIdentity(testcases[key].testdata) + message:key]; +} + +- (void)testAffineTransformIsIdentityNegative +{ + var testcases = { + "some random transform" : { + testdata: CGAffineTransformMake(1,1,1,1,1,1) + }, + + "non-zero translation is not identity" : { + testdata: CGAffineTransformMakeTranslation(1,1) + }, + + "non-one scale is not identity" : { + testdata: CGAffineTransformMakeScale(2,2) + }, + + "rotation" : { + testdata: CGAffineTransformMakeRotation(Math.PI), + }, + + // TODO a two-pi rotation is actually identity + "2PI rotation is NOT identity?" : { + testdata: CGAffineTransformMakeRotation(Math.PI * 2), + }, + + }; + + for ( var key in testcases ) + [self assert:NO + equals:CGAffineTransformIsIdentity(testcases[key].testdata) + message:key]; +} + +- (void)testAffineTransformEqualToTransform +{ + var testcases = { + "identity" : { + lhs: CGAffineTransformMakeIdentity(), + rhs: CGAffineTransformMakeIdentity(), + expdata: YES + }, + + "translate" : { + lhs: CGAffineTransformMakeTranslation(1,1), + rhs: CGAffineTransformMakeTranslation(1,1), + expdata: YES + }, + + "scale" : { + lhs: CGAffineTransformMakeScale(1,1), + rhs: CGAffineTransformMakeScale(1,1), + expdata: YES + }, + + "rotation" : { + lhs: CGAffineTransformMakeRotation(Math.PI), + rhs: CGAffineTransformMakeRotation(Math.PI), + expdata: YES + }, + + "translate and scale" : { + lhs: CGAffineTransformMakeScale(1,1), + rhs: CGAffineTransformMakeTranslation(1,1), + expdata: NO + }, + }; + + for ( var key in testcases ) + [self assert:testcases[key].expdata + equals:CGAffineTransformEqualToTransform(testcases[key].lhs, testcases[key].rhs) + message:key]; +} + +- (void)testStringCreateWithCGAffineTransform +{ + // FIXME?: should there be a leading space on these strings + var testcases = { + "identity" : { + testdata: CGAffineTransformMakeIdentity(), + expdata: " [[ 1, 0, 0 ], [ 0, 1, 0 ], [ 0, 0, 1]]" + }, + + "scale" : { + testdata: CGAffineTransformMakeScale(3,4), + expdata: " [[ 3, 0, 0 ], [ 0, 4, 0 ], [ 0, 0, 1]]" + }, + + "translation" : { + testdata: CGAffineTransformMakeTranslation(3,4), + expdata: " [[ 1, 0, 0 ], [ 0, 1, 0 ], [ 3, 4, 1]]" + }, + + "scale and translation" : { + testdata: CGAffineTransformTranslate(CGAffineTransformMakeScale(3,4),5,6), + expdata: " [[ 3, 0, 0 ], [ 0, 4, 0 ], [ 15, 24, 1]]" + }, + }; + + for ( var key in testcases ) + [self assert:testcases[key].expdata + equals:CGStringCreateWithCGAffineTransform(testcases[key].testdata) + message:key]; +} + +- (void)testStringFromCGAffineTransform +{ + var testcases = { + "identity" : { + testdata: CGAffineTransformMakeIdentity(), + expdata: "{1, 0, 0, 1, 0, 0}" + }, + + "scale" : { + testdata: CGAffineTransformMakeScale(3,4), + expdata: "{3, 0, 0, 4, 0, 0}" + }, + + "translation" : { + testdata: CGAffineTransformMakeTranslation(3,4), + expdata: "{1, 0, 0, 1, 3, 4}" + }, + + "rotation - zero" : { + testdata: CGAffineTransformMakeRotation(0), + expdata: "{1, 0, 0, 1, 0, 0}" + }, + + "rotation - pi" : { + testdata: CGAffineTransformMakeRotation(Math.PI), + expdata: "{-1, 1.2246467991473532e-16, -1.2246467991473532e-16, -1, 0, 0}" + }, + + "rotation - 2pi" : { + testdata: CGAffineTransformMakeRotation(2 * Math.PI), + expdata: "{1, -2.4492935982947064e-16, 2.4492935982947064e-16, 1, 0, 0}" + }, + + "rotation - 3pi" : { + testdata: CGAffineTransformMakeRotation(3 * Math.PI), + expdata: "{-1, 3.6739403974420594e-16, -3.6739403974420594e-16, -1, 0, 0}" + }, + + "scale and translation and rotate" : { + testdata: CGAffineTransformRotate(CGAffineTransformTranslate(CGAffineTransformMakeScale(3,4),5,6),Math.PI), + expdata: "{-3, 4.898587196589413e-16, -3.6739403974420594e-16, -4, 15, 24}" + }, + }; + + for ( var key in testcases ) + [self assert:testcases[key].expdata + equals:CPStringFromCGAffineTransform(testcases[key].testdata) + message:key]; +} + +- (void)testAffineTransformRotate +{ + var ang = Math.PI + 0.5 * Math.PI, + transform = CGAffineTransformMake( 1,2,3,4, 5,6 ), + cos = COS(ang), + sin = SIN(ang); + + var testcases = { + "affine transform rotate failed" : { + testdata: CGAffineTransformRotate(transform, ang), + expdata: CGAffineTransformMake( transform.a * cos + transform.c * sin, + transform.b * cos + transform.d * sin, + transform.c * cos - transform.a * sin, + transform.d * cos - transform.b * sin, + transform.tx,transform.ty ) + }, + + "rotation negation" : { + testdata: CGAffineTransformRotate(CGAffineTransformRotate(CGAffineTransformMakeScale(3,4),Math.PI),-Math.PI), + expdata: CGAffineTransformMakeScale(3,4) + } + }; + + for ( var key in testcases ) + [self compareTransform:testcases[key].testdata with:testcases[key].expdata message:key]; +} + +- (void)testAffineTransformInvert +{ + var transform = CGAffineTransformRotate(CGAffineTransformTranslate(CGAffineTransformMakeScale(3,4),5,6),Math.PI), + determinant = 1 / (transform.a * transform.d - transform.b * transform.c), + invertedtransform = CGAffineTransformMake(determinant * transform.d, + -determinant * transform.b, + -determinant * transform.c, + determinant * transform.a, + determinant * (transform.c * transform.ty - + transform.d * transform.tx), + determinant * (transform.b * transform.tx - + transform.a * transform.ty)); + + var testcases = { + "test invert algorithm" : { + testdata: transform, + expdata: invertedtransform + }, + + "identity should be it self on inversion" : { + testdata: CGAffineTransformMakeIdentity(), + expdata: CGAffineTransformMakeIdentity() + }, + + "rotation" : { + testdata: CGAffineTransformMakeRotation(-Math.PI), + expdata: CGAffineTransformMakeRotation(Math.PI), + }, + + "translation" : { + testdata: CGAffineTransformMakeTranslation(4,5), + expdata: CGAffineTransformMakeTranslation(-4,-5) + }, + + "scale" : { + testdata: CGAffineTransformMakeScale(3,4), + expdata: CGAffineTransformMakeScale(1/3,1/4) + }, + }; + + for ( var key in testcases ) + [self compareTransform:CGAffineTransformInvert(testcases[key].testdata) + with:testcases[key].expdata + message:key]; +} + +- (void)testRectApplyAffineTransform +{ + var rect = CGRectMake( 3,4,5,6 ); + + var testcases = { + "identity does nothing" : { + testdata: CGRectApplyAffineTransform(rect, CGAffineTransformMakeIdentity() ), + expdata: rect + }, + + "rotation 90 degrees" : { + testdata: CGRectApplyAffineTransform(rect, CGAffineTransformMakeRotation(Math.PI/2)), + expdata: CGRectMake( -10,3.0000000000000004,6,5 ) + }, + + "translation" : { + testdata: CGRectApplyAffineTransform(rect, CGAffineTransformMakeTranslation(3,4)), + expdata: CGRectMake( 6,8,5,6 ) + }, + + "scale" : { + testdata: CGRectApplyAffineTransform(rect, CGAffineTransformMakeScale(1,4)), + expdata: CGRectMake( 3,16,5,24 ) + }, + + "rotate, translate and scale" : { + testdata: CGRectApplyAffineTransform(rect, CGAffineTransformRotate(CGAffineTransformTranslate(CGAffineTransformMakeScale(3,4),5,6),Math.PI)), + expdata: CGRectMake( -9.000000000000004, -16, + 15.000000000000002, 24.000000000000004 ) + }, + }; + + for ( var key in testcases ) + [self compareRect:testcases[key].testdata with:testcases[key].expdata message:key]; +} + +@end diff --git a/Tests/AppKit/CGColorSpaceTest.j b/Tests/AppKit/CGColorSpaceTest.j new file mode 100644 index 000000000..3df8b4c3f --- /dev/null +++ b/Tests/AppKit/CGColorSpaceTest.j @@ -0,0 +1,168 @@ +@import + +@implementation CGColorSpaceTest : OJTestCase + +- (void)testColorSpaceCreateWithName +{ + var testdata = { + "CGColorSpaceGenericGray" : { + model: kCGColorSpaceModelMonochrome, + count: 1, + base: NULL + }, + "CGColorSpaceGenericRGB" : { + model: kCGColorSpaceModelRGB, + count: 3, + base: NULL + }, + "CGColorSpaceGenericCMYK" : { + model: kCGColorSpaceModelCMYK, + count: 4, + base: NULL + }, + "CGColorSpaceGenericRGBLinear" : { + model: kCGColorSpaceModelRGB, + count: 3, + base: NULL + }, + "CGColorSpaceGenericRGBHDR" : { + model: kCGColorSpaceModelRGB, + count: 3, + base: NULL + }, + "CGColorSpaceAdobeRGB1998" : { + model: kCGColorSpaceModelRGB, + count: 3, + base: NULL + }, + "CGColorSpaceSRGB" : { + model: kCGColorSpaceModelRGB, + count: 3, + base: NULL + }, + } + + for ( var key in testdata ) { + var clrsp = CGColorSpaceCreateWithName(key), + expdata = testdata[key]; + [self assert:expdata.model equals:clrsp.model message:"model for "+key]; + [self assert:expdata.count equals:clrsp.count message:"count for "+key]; + [self assert:expdata.base equals:clrsp.base message:"base for "+key]; + } + + [self assert:NULL + equals:CGColorSpaceCreateWithName("doesnotexist") + message:"should be missing"]; +} + +- (void)testColorSpaceStandardizeComponents +{ + var testcases = { + "notsupported1" : { + testdata : { + model: kCGColorSpaceModelIndexed, + count: 5 + }, + expected: [5,4,3,2,1,NaN] // alpha value normalisation adds value at index 5 + }, + "notsupported2" : { + testdata : { + model: kCGColorSpaceModelLab, + count: 5 + }, + expected: [5,4,3,2,1,NaN] + }, + "notsupported3" : { + testdata : { + model: kCGColorSpaceModelPattern, + count: 4 + }, + expected: [5,4,3,2,1] + }, + "base override with count value" : { + testdata : { + model: kCGColorSpaceModelPattern, + base: { model: kCGColorSpaceModelMonochrome }, + count: 4 + }, + expected: [1,1,1,1,1], + }, + "base override no count value" : { + testdata : { + model: kCGColorSpaceModelPattern, + base: { model: kCGColorSpaceModelMonochrome, count: 4 }, + count: 0 + }, + expected: [1,4,3,2,1], // alpha value is assumed to be at index 0 + }, + "monochrome color space model" : { + testdata : { + model: kCGColorSpaceModelMonochrome, + count: 2 + }, + expected: [1,1,1,2,1], + }, + "rgb color space model" : { + testdata : { + model: kCGColorSpaceModelRGB, + count: 2 + }, + expected: [1,1,1,2,1], + }, + "cmyk color space model" : { + testdata : { + model: kCGColorSpaceModelCMYK, + count: 2 + }, + expected: [1,1,1,2,1], + }, + "devicen color space model" : { + testdata : { + model: kCGColorSpaceModelDeviceN, + count: 2 + }, + expected: [1,1,1,2,1], + }, + } + + for ( var key in testcases ) { + var components = [5,4,3,2,1]; + CGColorSpaceStandardizeComponents( testcases[key].testdata, components); + [self assert:testcases[key].expected equals:components message:"Failed for "+key]; + } +} + +- (void)testRgbStandardize +{ + var clrspc = CGColorSpaceCreateDeviceRGB(), + testcases = { + "negative values" : { + components: [-5,-4,-3,-2,-1,-1,-1,-1], + expected: [0,0,0,0,-1,-1,-1,-1], + }, + // TODO: this happens because first the alpha value is normalised, + // TODO: so the components array does not have length 0, rather 4. + "missing values become undefined" : { + components: [], + expected: [undefined,undefined,undefined,1], + }, + "values standarized if between 0 and 1" : { + components: [0.3, 0.4, 0.5, 0.6], + expected: [ROUND(0.3*255)/255, ROUND(0.4*255)/255, + ROUND(0.5*255)/255, ROUND(0.6*255)/255], + }, + "values 0 or 1 if zero or one" : { + components: [0, 1, 0, 1], + expected: [0,1,0,1], + } + }; + + for ( var key in testcases ) { + CGColorSpaceStandardizeComponents( clrspc, testcases[key].components); + [self assert:testcases[key].expected + equals:testcases[key].components + message:"Failed for "+key]; + } +} + +@end diff --git a/Tests/AppKit/CGColorTest.j b/Tests/AppKit/CGColorTest.j new file mode 100644 index 000000000..97416ef87 --- /dev/null +++ b/Tests/AppKit/CGColorTest.j @@ -0,0 +1,217 @@ +@import + +@implementation CGColorTest : OJTestCase + +- (void)testColorCreateReturnNullIfNull +{ + [self assert:NULL equals:CGColorCreate( NULL, [1,2,3] ) message:"colorspace null failed"]; + [self assert:NULL + equals:CGColorCreate( CGColorSpaceCreateDeviceRGB(), NULL ) + message:"components null failed"]; +} + +- (void)testColorCreateEnsureComponentsAreCopied +{ + var colorspace = CGColorSpaceCreateDeviceRGB(), + components = [2,3,4,5], + clr = CGColorCreate(colorspace, components); + + components[0] = components[1] = components[2] = components[3] = 0; + [self assert:[1,1,1,1] equals:clr.components]; + [self assert:[0,0,0,0] equals:components]; +} + +- (void)testColorCreateCachesColorsAlphaChangeIsPropagated +{ + var clr = CGColorCreate(CGColorSpaceCreateDeviceRGB(), [0.4, 0.3, 0.2, 0.2]), + newclr = CGColorCreate(CGColorSpaceCreateDeviceRGB(), [0.4, 0.3, 0.2, 0.2]); + newclr.components[newclr.components.length - 1] = 0.6; + [self assert:0.6 equals:clr.components[clr.components.length-1]]; +} + +- (void)testColorCreate +{ + var colorspace = CGColorSpaceCreateDeviceRGB(), + components = [2,3,4,5], + clruid = CFHashCode(colorspace) + components.join(""); + + // Can't access the colormap cache, but it would be nice to + // [self assert:NULL equals:_CGColorMap[clruid]]; + + var clr = CGColorCreate( colorspace, components ); + [self assert:colorspace equals:clr.colorspace message:"colorspace failed"]; + [self assert:[1,1,1,1] equals:clr.components message:"components failed"]; + [self assert:NULL equals:clr.pattern message:"pattern failed"]; + + // Can't access the colormap cache, but it would be nice to + // [self assert:clr equals:_CGColorMap[clruid]]; +} + +- (void)testColorCopy +{ + // it returns it's argument, a little artifical but will fail if something + // changes with the function. + [self assert:"banana" equals:CGColorCreateCopy( "banana" )]; + + var colorspace = CGColorSpaceCreateDeviceRGB(), + components = [2,3,4,5], + clr = CGColorCreate( colorspace, components ); + [self assert:clr equals:CGColorCreateCopy(clr)]; +} + +- (void)testColorCreateGenericGray +{ + var clr = CGColorCreateGenericGray(0.3, 0.5); + [self assert:CGColorSpaceCreateDeviceRGB() + equals:clr.colorspace message:"colorspace failed"]; + [self assert:[ROUND(0.3*255)/255,ROUND(0.3*255)/255,ROUND(0.3*255)/255,0.5] + equals:clr.components message:"components failed"]; + [self assert:NULL equals:clr.pattern message:"pattern failed"]; +} + +- (void)testColorCreateGenericRGB +{ + var clr = CGColorCreateGenericRGB(0.4,0.3,0.2,0.1); + [self assert:CGColorSpaceCreateDeviceRGB() + equals:clr.colorspace message:"colorspace failed"]; + [self assert:[ROUND(0.4*255)/255,ROUND(0.3*255)/255,ROUND(0.2*255)/255,0.1] + equals:clr.components message:"components failed"]; + [self assert:NULL equals:clr.pattern message:"pattern failed"]; +} + +- (void)testColorCreateGenericCMYK +{ + var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6) + [self assert:CGColorSpaceCreateDeviceCMYK() + equals:clr.colorspace message:"colorspace failed"]; + [self assert:[ROUND(0.2*255)/255,ROUND(0.3*255)/255,ROUND(0.4*255)/255, + ROUND(0.5*255)/255, 0.6] + equals:clr.components message:"components failed"]; + [self assert:NULL equals:clr.pattern message:"pattern failed"]; +} + +- (void)testColorCreateWithPattern +{ + var clrspc = CGColorSpaceCreateDeviceRGB(), + pattern = "a new pattern", + components = [0.2, 0.4, 0.6, 0.8], + clr = CGColorCreateWithPattern(clrspc, pattern, components); + + [self assert:clrspc equals:clr.colorspace]; + [self assert:pattern equals:clr.pattern]; + [self assert:components equals:clr.components]; + + // ensure that there was a copy made of the components + components[0] = components[1] = components[2] = components[3] = 0.0; + [self assert:[0.2, 0.4, 0.6, 0.8] equals:clr.components]; + + // ensure that null is returned + [self assert:NULL equals:CGColorCreateWithPattern(NULL, "a new pattern", [])]; + [self assert:NULL equals:CGColorCreateWithPattern(CGColorSpaceCreateDeviceRGB(), + NULL, [])]; + [self assert:NULL equals:CGColorCreateWithPattern(CGColorSpaceCreateDeviceRGB(), + "a new pattern", NULL)]; + [self assert:NULL equals:CGColorCreateWithPattern(NULL, NULL, NULL)]; +} + +- (void)testColorGetAlpha +{ + var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6); + [self assert:0.6 equals:CGColorGetAlpha(clr)]; +} + +- (void)testColorGetColorSpace +{ + var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6); + [self assert:CGColorSpaceCreateDeviceCMYK() + equals:CGColorGetColorSpace(clr)]; +} + +- (void)testColorGetComponents +{ + var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6); + [self assert:[ROUND(0.2*255)/255, ROUND(0.3*255)/255, ROUND(0.4*255)/255, + ROUND(0.5*255)/255, ROUND(0.6*255)/255] + equals:CGColorGetComponents(clr)]; +} + +- (void)testColorGetNumberOfComponents +{ + var clr = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6); + [self assert:5 equals:CGColorGetNumberOfComponents(clr)]; +} + +- (void)testColorGetPattern +{ + var clr = CGColorCreateWithPattern(CGColorSpaceCreateDeviceRGB(), "a new pattern", + [1,1,1,1]); + [self assert:"a new pattern" equals:CGColorGetPattern(clr)]; +} + +- (void)testColorCreateCopyWithAlphaWithPattern +{ + var clr = CGColorCreateWithPattern(CGColorSpaceCreateDeviceRGB(), "a new pattern", + [1,1,1,1]); + [self assert:1 equals:CGColorGetAlpha(clr) message:"initial alpha"]; + + var newclr = CGColorCreateCopyWithAlpha(clr, 0.6); + [self assert:0.6 equals:CGColorGetAlpha(newclr) message:"new color alpha value"]; + [self assert:1 equals:CGColorGetAlpha(clr) message:"alpha after copy"]; + + [self assert:clr.colorspace equals:newclr.colorspace message:"newclr colorspace failed"]; + [self assert:clr.pattern equals:newclr.pattern message:"newclr pattern failed"]; +} + +- (void)testColorCreateCopyWithAlphaWithComponents +{ + var clr = CGColorCreateGenericRGB(0.4, 0.3, 0.2, 0.3); + [self assert:0.3 equals:CGColorGetAlpha(clr) message:"initial alpha"]; + + var newclr = CGColorCreateCopyWithAlpha(clr, 0.6); + [self assert:0.6 equals:CGColorGetAlpha(newclr) message:"new color alpha value"]; + [self assert:0.3 equals:CGColorGetAlpha(clr) message:"alpha after copy"]; + + [self assert:CGColorSpaceCreateDeviceRGB() + equals:newclr.colorspace message:"newclr colorspace failed"]; + [self assert:[ROUND(0.4*255)/255,ROUND(0.3*255)/255,ROUND(0.2*255)/255,0.6] + equals:newclr.components message:"newclr components failed"]; + [self assert:NULL equals:newclr.pattern message:"newclr pattern failed"]; + + [self assert:CGColorSpaceCreateDeviceRGB() + equals:clr.colorspace message:"orig clr colorspace failed"]; + [self assert:[ROUND(0.4*255)/255,ROUND(0.3*255)/255,ROUND(0.2*255)/255,0.3] + equals:clr.components message:"orig clr components failed"]; + [self assert:NULL equals:clr.pattern message:"orig clr pattern failed"]; +} + +- (void)testColorCreateCopyWithAlphaWithComponentsNullColor +{ + [self assert:NULL + equals:CGColorCreateCopyWithAlpha(NULL,0.5) + message:"null color failed"]; + + var clr = CGColorCreateGenericRGB(0.4,0.3,0.2,0.1), + newclr = CGColorCreateCopyWithAlpha(clr, 0.1); + [self assert:clr equals:newclr]; +} + +- (void)testColorEqualToColor +{ + [self assert:true equals:CGColorEqualToColor(NULL,NULL)]; + + var clr1 = CGColorCreateGenericRGB(0.4, 0.3, 0.2, 0.3); + [self assert:false equals:CGColorEqualToColor(NULL,clr1)]; + [self assert:true equals:CGColorEqualToColor(clr1,clr1)]; + [self assert:false equals:CGColorEqualToColor(clr1,NULL)]; + + var clr2 = CGColorCreateGenericRGB(0.4, 0.3, 0.2, 0.3); + [self assert:true equals:CGColorEqualToColor(clr1,clr2)]; + [self assert:true equals:CGColorEqualToColor(clr2,clr1)]; + + clr2 = CGColorCreateGenericCMYK(0.2, 0.3, 0.4, 0.5, 0.6); + [self assert:false equals:CGColorEqualToColor(clr1,clr2)]; + [self assert:false equals:CGColorEqualToColor(clr2,clr1)]; +} + +@end + diff --git a/Tests/AppKit/CGContextTest.j b/Tests/AppKit/CGContextTest.j new file mode 100644 index 000000000..0ed6242e6 --- /dev/null +++ b/Tests/AppKit/CGContextTest.j @@ -0,0 +1,78 @@ +@import +@import "CGTestCase.j" + +@implementation CGContextTest : CGTestCase + +- (void)testEnsureTestingCanvas +{ + /* + Ensure that we have no canvas nor vml support. + */ + [self assert:YES equals:!CPFeatureIsCompatible(CPHTMLCanvasFeature)]; + [self assert:YES equals:!CPFeatureIsCompatible(CPVMLFeature)]; +} + +- (void)testGStateCreate +{ + var gstate = CGGStateCreate(), + testdata = { alpha: 1.0, + strokeStyle: "#000", + fillStyle: "#ccc", + lineWidth: 1.0, + lineJoin: kCGLineJoinMiter, + lineCap: kCGLineCapButt, + miterLimit: 10.0, + globalAlpha: 1.0, + blendMode: kCGBlendModeNormal, + shadowBlur: 0.0, + shadowColor: NULL }; + + for ( var key in testdata ) + [self assert:testdata[key] equals:gstate[key] message:"Failed for " + key]; + + [self compareSize:CGSizeMakeZero() + with:gstate.shadowOffset + message:"Failed for shadowOffset"]; + + [self compareTransform:CGAffineTransformMakeIdentity() + with:gstate.CTM + message:"Failed for CTM"]; +} + +- (void)testGStateCreateCopy +{ + var gstate = CGGStateCreate(), + gstatecopy = CGGStateCreateCopy(gstate), + testdata = { alpha: 1.0, + strokeStyle: "#000", + fillStyle: "#ccc", + lineWidth: 1.0, + lineJoin: kCGLineJoinMiter, + lineCap: kCGLineCapButt, + miterLimit: 10.0, + globalAlpha: 1.0, + blendMode: kCGBlendModeNormal, + shadowBlur: 0.0, + shadowColor: NULL }; + + for ( var key in testdata ) + [self assert:testdata[key] equals:gstatecopy[key] message:"Failed for " + key]; + + // ensure that the size is copied + var sizecopy = CGSizeMakeCopy( gstate.shadowOffset ); + gstate.shadowOffset.width += 1000; + gstate.shadowOffset.height += 2000; + [self compareSize:sizecopy with:gstatecopy.shadowOffset message:"Failed for shadowOffset"]; + + // ensure that the transform is copied + var transformcopy = CGAffineTransformMakeCopy( gstate.CTM ); + gstate.CTM.a += 1000; + gstate.CTM.b += 1000; + gstate.CTM.c += 1000; + gstate.CTM.d += 1000; + gstate.CTM.tx += 1000; + gstate.CTM.ty += 1000; + [self compareTransform:transformcopy with:gstatecopy.CTM message:"Failed with CTM"]; +} + +@end diff --git a/Tests/AppKit/CGGradientTest.j b/Tests/AppKit/CGGradientTest.j index 187dd713d..0f89d0c60 100644 --- a/Tests/AppKit/CGGradientTest.j +++ b/Tests/AppKit/CGGradientTest.j @@ -7,6 +7,22 @@ */ - (void)testCreateWithColorComponentsDefault { + + // Test one color and no location + var components = [ 0.2,0.2,0.4,0.4 ]; + + var tmp = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), + components); + [self assert:1 equals:tmp.colors.length]; + [self assert:1 equals:tmp.locations.length]; + + // TODO: This should not be supported since there is no gradient if only one color + // TODO: NaN not really helpful... + [self assert:NaN equals:tmp.locations[0]]; + + [self assert:components.slice(0,4) equals:tmp.colors[0].components]; + + // Test two colors and no location var components = [ 0.2,0.2,0.4,0.4, 0.6,0.6,0.8,0.8 ]; @@ -20,6 +36,24 @@ [self assert:components.slice(4,8) equals:tmp.colors[1].components]; [self assert:components.slice(0,4) equals:tmp.colors[0].components]; + + // Test three colors and no location + var components = [ 0.2,0.2,0.4,0.4, + 0.6,0.6,0.8,0.8, + 0.6,0.6,0.8,0.8 ]; + + var tmp = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), + components); + [self assert:3 equals:tmp.colors.length]; + [self assert:3 equals:tmp.locations.length]; + + [self assert:0 equals:tmp.locations[0]]; + [self assert:0.5 equals:tmp.locations[1]]; + [self assert:1.0 equals:tmp.locations[2]]; + + [self assert:components.slice(8,12) equals:tmp.colors[2].components]; + [self assert:components.slice(4,8) equals:tmp.colors[1].components]; + [self assert:components.slice(0,4) equals:tmp.colors[0].components]; } /* diff --git a/Tests/AppKit/CGTestCase.j b/Tests/AppKit/CGTestCase.j new file mode 100644 index 000000000..e00bb1e11 --- /dev/null +++ b/Tests/AppKit/CGTestCase.j @@ -0,0 +1,44 @@ +/* + Used for comparing geometry forms. Otherwise each unit test would repeat these + comparisons. +*/ +@implementation CGTestCase : OJTestCase + +- (void)compareRect:(CGRect)aRect + with:(id)anotherRect + message:(CPString)aMsg +{ + aMsg += " (via cmp. rect)"; + [self comparePoint:aRect.origin with:anotherRect.origin message:aMsg]; + [self compareSize:aRect.size with:anotherRect.size message:aMsg]; +} + +- (void)compareSize:(CGSize)aSize + with:(id)anotherSize + message:(CPString)aMsg +{ + [self assert:anotherSize.width equals:aSize.width message:aMsg + ": Failed for width"]; + [self assert:anotherSize.height equals:aSize.height message:aMsg + ": Failed for height"]; +} + +- (void)comparePoint:(CGPoint)aPoint + with:(id)anotherPoint + message:(CPString)aMsg +{ + [self assert:anotherPoint.x equals:aPoint.x message:aMsg + ": Failed for x"]; + [self assert:anotherPoint.y equals:aPoint.y message:aMsg + ": Failed for y"]; +} + +- (void)compareTransform:(CGAffineTransform)aTransform + with:(id)aDataSet + message:(CPString)aMsg +{ + [self assert:aDataSet.a equals:aTransform.a message:aMsg + ": Failed for a"]; + [self assert:aDataSet.b equals:aTransform.b message:aMsg + ": Failed for b"]; + [self assert:aDataSet.c equals:aTransform.c message:aMsg + ": Failed for c"]; + [self assert:aDataSet.d equals:aTransform.d message:aMsg + ": Failed for d"]; + [self assert:aDataSet.tx equals:aTransform.tx message:aMsg + ": Failed for tx"]; + [self assert:aDataSet.ty equals:aTransform.ty message:aMsg + ": Failed for ty"]; +} + +@end diff --git a/Tools/xcodecapp/XCResourceMonitor.j b/Tools/xcodecapp/XCResourceMonitor.j index d0fb64191..b6241090f 100644 --- a/Tools/xcodecapp/XCResourceMonitor.j +++ b/Tools/xcodecapp/XCResourceMonitor.j @@ -40,6 +40,8 @@ var FILE = require("file"), paths.exclude(FILE.join(FILE.dirname(aPath), "**", "*")); }); + paths.exclude(FILE.join("Build", "**", "*")); + var mtimesForFilePaths = [CPMutableDictionary new]; paths.forEach(function(aPath)