jens@0: /* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. jens@0: http://developer.apple.com/samplecode/GeekGameBoard/ jens@0: Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. jens@0: jens@0: Redistribution and use in source and binary forms, with or without modification, are permitted jens@0: provided that the following conditions are met: jens@0: jens@0: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@0: and the following disclaimer. jens@0: * Redistributions in binary form must reproduce the above copyright notice, this list of jens@0: conditions and the following disclaimer in the documentation and/or other materials provided jens@0: with the distribution. jens@0: jens@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@0: */ jens@0: #import "QuartzUtils.h" jens@1: #import jens@0: jens@0: jens@0: CGColorRef kBlackColor, kWhiteColor, jens@0: kTranslucentGrayColor, kTranslucentLightGrayColor, jens@0: kAlmostInvisibleWhiteColor, jens@0: kHighlightColor; jens@0: jens@0: jens@0: __attribute__((constructor)) // Makes this function run when the app loads jens@0: static void InitQuartzUtils() jens@0: { jens@1: kBlackColor = CreateGray(0.0, 1.0); jens@1: kWhiteColor = CreateGray(1.0, 1.0); jens@1: kTranslucentGrayColor = CreateGray(0.0, 0.5); jens@1: kTranslucentLightGrayColor = CreateGray(0.0, 0.25); jens@1: kAlmostInvisibleWhiteColor = CreateGray(1, 0.05); jens@1: kHighlightColor = CreateRGB(1, 1, 0, 0.5); jens@0: } jens@0: jens@0: jens@1: #if TARGET_OS_ASPEN jens@1: CGColorRef CreateGray(CGFloat gray, CGFloat alpha) jens@1: { jens@1: CGColorSpaceRef graySpace = CGColorSpaceCreateDeviceGray(); jens@1: CGFloat components[2] = {gray,alpha}; jens@1: CGColorRef color = CGColorCreate(graySpace, components); jens@1: CGColorSpaceRelease(graySpace); jens@1: return color; jens@1: } jens@1: jens@1: CGColorRef CreateRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) jens@1: { jens@1: CGColorSpaceRef rgbSpace = CGColorSpaceCreateDeviceRGB(); jens@1: CGFloat components[4] = {red,green,blue,alpha}; jens@1: CGColorRef color = CGColorCreate(rgbSpace, components); jens@1: CGColorSpaceRelease(rgbSpace); jens@1: return color; jens@1: } jens@1: #endif jens@1: jens@1: jens@0: void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index ) jens@0: { jens@0: // Disable actions, else the layer will move to the wrong place and then back! jens@0: [CATransaction flush]; jens@0: [CATransaction begin]; jens@0: [CATransaction setValue:(id)kCFBooleanTrue jens@0: forKey:kCATransactionDisableActions]; jens@0: jens@3: CGPoint pos = layer.position; jens@3: if( layer.superlayer ) jens@3: pos = [newSuperlayer convertPoint: pos fromLayer: layer.superlayer]; jens@0: [layer retain]; jens@0: [layer removeFromSuperlayer]; jens@7: layer.position = pos; jens@0: if( index >= 0 ) jens@0: [newSuperlayer insertSublayer: layer atIndex: index]; jens@0: else jens@0: [newSuperlayer addSublayer: layer]; jens@0: [layer release]; jens@0: jens@0: [CATransaction commit]; jens@0: } jens@0: jens@0: jens@0: void RemoveImmediately( CALayer *layer ) jens@0: { jens@0: [CATransaction flush]; jens@0: [CATransaction begin]; jens@0: [CATransaction setValue:(id)kCFBooleanTrue jens@0: forKey:kCATransactionDisableActions]; jens@0: [layer removeFromSuperlayer]; jens@0: [CATransaction commit]; jens@0: } jens@0: jens@0: jens@0: CGImageRef CreateCGImageFromFile( NSString *path ) jens@0: { jens@1: #if TARGET_OS_ASPEN jens@1: UIImage *uiImage = [UIImage imageWithContentsOfFile: path]; jens@1: if(!uiImage) NSLog(@"Warning: UIImage imageWithContentsOfFile failed on file %@",path); jens@1: return CGImageRetain(uiImage.CGImage); jens@1: #else jens@0: CGImageRef image = NULL; jens@0: CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: path]; jens@0: CGImageSourceRef src = CGImageSourceCreateWithURL(url, NULL); jens@0: if( src ) { jens@0: image = CGImageSourceCreateImageAtIndex(src, 0, NULL); jens@0: CFRelease(src); jens@0: if(!image) NSLog(@"Warning: CGImageSourceCreateImageAtIndex failed on file %@ (ptr size=%u)",path,sizeof(void*)); jens@0: } jens@0: return image; jens@1: #endif jens@0: } jens@0: jens@0: jens@0: CGImageRef GetCGImageNamed( NSString *name ) jens@0: { jens@1: #if TARGET_OS_ASPEN jens@1: name = name.lastPathComponent; jens@1: UIImage *uiImage = [UIImage imageNamed: name]; jens@1: NSCAssert1(uiImage,@"Couldn't find bundle image resource '%@'",name); jens@1: return uiImage.CGImage; jens@1: #else jens@0: // For efficiency, loaded images are cached in a dictionary by name. jens@0: static NSMutableDictionary *sMap; jens@0: if( ! sMap ) jens@0: sMap = [[NSMutableDictionary alloc] init]; jens@0: jens@0: CGImageRef image = (CGImageRef) [sMap objectForKey: name]; jens@0: if( ! image ) { jens@0: // Hasn't been cached yet, so load it: jens@0: NSString *path; jens@0: if( [name hasPrefix: @"/"] ) jens@0: path = name; jens@0: else { jens@0: path = [[NSBundle mainBundle] pathForResource: name ofType: nil]; jens@0: NSCAssert1(path,@"Couldn't find bundle image resource '%@'",name); jens@0: } jens@0: image = CreateCGImageFromFile(path); jens@0: NSCAssert1(image,@"Failed to load image from %@",path); jens@0: [sMap setObject: (id)image forKey: name]; jens@0: CGImageRelease(image); jens@0: } jens@0: return image; jens@1: #endif jens@0: } jens@0: jens@0: jens@0: CGColorRef GetCGPatternNamed( NSString *name ) // can be resource name or abs. path jens@0: { jens@0: // For efficiency, loaded patterns are cached in a dictionary by name. jens@0: static NSMutableDictionary *sMap; jens@0: if( ! sMap ) jens@0: sMap = [[NSMutableDictionary alloc] init]; jens@0: jens@0: CGColorRef pattern = (CGColorRef) [sMap objectForKey: name]; jens@0: if( ! pattern ) { jens@0: pattern = CreatePatternColor( GetCGImageNamed(name) ); jens@0: [sMap setObject: (id)pattern forKey: name]; jens@0: CGColorRelease(pattern); jens@0: } jens@0: return pattern; jens@0: } jens@0: jens@0: jens@1: #if ! TARGET_OS_ASPEN jens@0: CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb ) jens@0: { jens@0: CGImageSourceRef src = NULL; jens@0: NSArray *paths = [pb propertyListForType: NSFilenamesPboardType]; jens@0: if( paths.count==1 ) { jens@0: // If a file is being dragged, read it: jens@0: CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: [paths objectAtIndex: 0]]; jens@0: src = CGImageSourceCreateWithURL(url, NULL); jens@0: } else { jens@0: // Else look for an image type: jens@0: NSString *type = [pb availableTypeFromArray: [NSImage imageUnfilteredPasteboardTypes]]; jens@0: if( type ) { jens@0: NSData *data = [pb dataForType: type]; jens@0: src = CGImageSourceCreateWithData((CFDataRef)data, NULL); jens@0: } jens@0: } jens@0: if(src) { jens@0: CGImageRef image = CGImageSourceCreateImageAtIndex(src, 0, NULL); jens@0: CFRelease(src); jens@0: return image; jens@0: } else jens@0: return NULL; jens@1: } jens@1: #endif jens@0: jens@0: jens@0: float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt ) jens@0: { jens@1: #if TARGET_OS_ASPEN jens@1: // iPhone uses "flipped" (i.e. normal) coords, so images are wrong-way-up jens@1: pt.y = imageSize.height - pt.y; jens@1: #endif jens@1: jens@0: // Trivial reject: jens@0: if( pt.x<0 || pt.x>=imageSize.width || pt.y<0 || pt.y>=imageSize.height ) jens@0: return 0.0; jens@0: jens@0: // sTinyContext is a 1x1 CGBitmapContext whose pixmap stores only alpha. jens@0: static UInt8 sPixel[1]; jens@0: static CGContextRef sTinyContext; jens@0: if( ! sTinyContext ) { jens@0: sTinyContext = CGBitmapContextCreate(sPixel, 1, 1, jens@0: 8, 1, // bpp, rowBytes jens@0: NULL, jens@0: kCGImageAlphaOnly); jens@0: CGContextSetBlendMode(sTinyContext, kCGBlendModeCopy); jens@0: } jens@0: jens@0: // Draw the image into sTinyContext, positioned so the desired point is at jens@0: // (0,0), then examine the alpha value in the pixmap: jens@0: CGContextDrawImage(sTinyContext, jens@0: CGRectMake(-pt.x,-pt.y, imageSize.width,imageSize.height), jens@0: image); jens@0: return sPixel[0] / 255.0; jens@0: } jens@0: jens@0: jens@0: #pragma mark - jens@0: #pragma mark PATTERNS: jens@0: jens@0: jens@0: // callback for CreateImagePattern. jens@0: static void drawPatternImage (void *info, CGContextRef ctx) jens@0: { jens@0: CGImageRef image = (CGImageRef) info; jens@0: CGContextDrawImage(ctx, jens@0: CGRectMake(0,0, CGImageGetWidth(image),CGImageGetHeight(image)), jens@0: image); jens@0: } jens@0: jens@0: // callback for CreateImagePattern. jens@0: static void releasePatternImage( void *info ) jens@0: { jens@0: CGImageRelease( (CGImageRef)info ); jens@0: } jens@0: jens@0: jens@0: CGPatternRef CreateImagePattern( CGImageRef image ) jens@0: { jens@0: NSCParameterAssert(image); jens@0: int width = CGImageGetWidth(image); jens@0: int height = CGImageGetHeight(image); jens@0: static const CGPatternCallbacks callbacks = {0, &drawPatternImage, &releasePatternImage}; jens@0: return CGPatternCreate (image, jens@0: CGRectMake (0, 0, width, height), jens@0: CGAffineTransformMake (1, 0, 0, 1, 0, 0), jens@0: width, jens@0: height, jens@0: kCGPatternTilingConstantSpacing, jens@0: true, jens@0: &callbacks); jens@0: } jens@0: jens@0: jens@0: CGColorRef CreatePatternColor( CGImageRef image ) jens@0: { jens@0: CGPatternRef pattern = CreateImagePattern(image); jens@0: CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL); jens@0: CGFloat components[1] = {1.0}; jens@0: CGColorRef color = CGColorCreateWithPattern(space, pattern, components); jens@0: CGColorSpaceRelease(space); jens@0: CGPatternRelease(pattern); jens@0: return color; jens@0: } jens@1: jens@1: jens@1: #pragma mark - jens@1: #pragma mark PATHS: jens@1: jens@1: jens@1: void AddRoundRect( CGContextRef ctx, CGRect rect, CGFloat radius ) jens@1: { jens@1: radius = MIN(radius, floorf(rect.size.width/2)); jens@1: float x0 = CGRectGetMinX(rect), y0 = CGRectGetMinY(rect), jens@1: x1 = CGRectGetMaxX(rect), y1 = CGRectGetMaxY(rect); jens@1: jens@1: CGContextBeginPath(ctx); jens@1: CGContextMoveToPoint(ctx,x0+radius,y0); jens@1: CGContextAddArcToPoint(ctx,x1,y0, x1,y1, radius); jens@1: CGContextAddArcToPoint(ctx,x1,y1, x0,y1, radius); jens@1: CGContextAddArcToPoint(ctx,x0,y1, x0,y0, radius); jens@1: CGContextAddArcToPoint(ctx,x0,y0, x1,y0, radius); jens@1: CGContextClosePath(ctx); jens@1: } jens@1: