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@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@0: kBlackColor = CGColorCreateGenericGray(0.0, 1.0); jens@0: kWhiteColor = CGColorCreateGenericGray(1.0, 1.0); jens@0: kTranslucentGrayColor = CGColorCreateGenericGray(0.0, 0.5); jens@0: kTranslucentLightGrayColor = CGColorCreateGenericGray(0.0, 0.25); jens@0: kAlmostInvisibleWhiteColor = CGColorCreateGenericGray(1, 0.05); jens@0: kHighlightColor = CGColorCreateGenericRGB(1, 1, 0, 0.5); jens@0: } jens@0: jens@0: 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@0: CGPoint pos = [newSuperlayer convertPoint: layer.position jens@0: fromLayer: layer.superlayer]; jens@0: [layer retain]; jens@0: [layer removeFromSuperlayer]; jens@0: if( index >= 0 ) jens@0: [newSuperlayer insertSublayer: layer atIndex: index]; jens@0: else jens@0: [newSuperlayer addSublayer: layer]; jens@0: layer.position = pos; 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: CATextLayer* AddTextLayer( CALayer *superlayer, jens@0: NSString *text, NSFont* font, jens@0: enum CAAutoresizingMask align ) jens@0: { jens@0: CATextLayer *label = [[CATextLayer alloc] init]; jens@0: label.string = text; jens@0: label.font = font; jens@0: label.fontSize = font.pointSize; jens@0: label.foregroundColor = kBlackColor; jens@0: jens@0: NSString *mode; jens@0: if( align & kCALayerWidthSizable ) jens@0: mode = @"center"; jens@0: else if( align & kCALayerMinXMargin ) jens@0: mode = @"right"; jens@0: else jens@0: mode = @"left"; jens@0: align |= kCALayerWidthSizable; jens@0: label.alignmentMode = mode; jens@0: jens@0: CGFloat inset = superlayer.borderWidth + 3; jens@0: CGRect bounds = CGRectInset(superlayer.bounds, inset, inset); jens@0: CGFloat height = font.ascender; jens@0: CGFloat y = bounds.origin.y; jens@0: if( align & kCALayerHeightSizable ) jens@0: y += (bounds.size.height-height)/2.0; jens@0: else if( align & kCALayerMinYMargin ) jens@0: y += bounds.size.height - height; jens@0: align &= ~kCALayerHeightSizable; jens@0: label.bounds = CGRectMake(0, font.descender, jens@0: bounds.size.width, height - font.descender); jens@0: label.position = CGPointMake(bounds.origin.x,y+font.descender); jens@0: label.anchorPoint = CGPointMake(0,0); jens@0: jens@0: label.autoresizingMask = align; jens@0: [superlayer addSublayer: label]; jens@0: [label release]; jens@0: return label; jens@0: } jens@0: jens@0: jens@0: CGImageRef CreateCGImageFromFile( NSString *path ) jens@0: { 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@0: } jens@0: jens@0: jens@0: CGImageRef GetCGImageNamed( NSString *name ) jens@0: { 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@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@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@0: } jens@0: jens@0: jens@0: float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt ) jens@0: { 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: }