1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Source/QuartzUtils.m Fri Mar 07 11:43:02 2008 -0800
1.3 @@ -0,0 +1,273 @@
1.4 +/* This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
1.5 + http://developer.apple.com/samplecode/GeekGameBoard/
1.6 + Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
1.7 +
1.8 + Redistribution and use in source and binary forms, with or without modification, are permitted
1.9 + provided that the following conditions are met:
1.10 +
1.11 + * Redistributions of source code must retain the above copyright notice, this list of conditions
1.12 + and the following disclaimer.
1.13 + * Redistributions in binary form must reproduce the above copyright notice, this list of
1.14 + conditions and the following disclaimer in the documentation and/or other materials provided
1.15 + with the distribution.
1.16 +
1.17 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
1.18 + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
1.19 + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
1.20 + BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
1.21 + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1.22 + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1.23 + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
1.24 + THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.25 +*/
1.26 +#import "QuartzUtils.h"
1.27 +
1.28 +
1.29 +CGColorRef kBlackColor, kWhiteColor,
1.30 + kTranslucentGrayColor, kTranslucentLightGrayColor,
1.31 + kAlmostInvisibleWhiteColor,
1.32 + kHighlightColor;
1.33 +
1.34 +
1.35 +__attribute__((constructor)) // Makes this function run when the app loads
1.36 +static void InitQuartzUtils()
1.37 +{
1.38 + kBlackColor = CGColorCreateGenericGray(0.0, 1.0);
1.39 + kWhiteColor = CGColorCreateGenericGray(1.0, 1.0);
1.40 + kTranslucentGrayColor = CGColorCreateGenericGray(0.0, 0.5);
1.41 + kTranslucentLightGrayColor = CGColorCreateGenericGray(0.0, 0.25);
1.42 + kAlmostInvisibleWhiteColor = CGColorCreateGenericGray(1, 0.05);
1.43 + kHighlightColor = CGColorCreateGenericRGB(1, 1, 0, 0.5);
1.44 +}
1.45 +
1.46 +
1.47 +void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index )
1.48 +{
1.49 + // Disable actions, else the layer will move to the wrong place and then back!
1.50 + [CATransaction flush];
1.51 + [CATransaction begin];
1.52 + [CATransaction setValue:(id)kCFBooleanTrue
1.53 + forKey:kCATransactionDisableActions];
1.54 +
1.55 + CGPoint pos = [newSuperlayer convertPoint: layer.position
1.56 + fromLayer: layer.superlayer];
1.57 + [layer retain];
1.58 + [layer removeFromSuperlayer];
1.59 + if( index >= 0 )
1.60 + [newSuperlayer insertSublayer: layer atIndex: index];
1.61 + else
1.62 + [newSuperlayer addSublayer: layer];
1.63 + layer.position = pos;
1.64 + [layer release];
1.65 +
1.66 + [CATransaction commit];
1.67 +}
1.68 +
1.69 +
1.70 +void RemoveImmediately( CALayer *layer )
1.71 +{
1.72 + [CATransaction flush];
1.73 + [CATransaction begin];
1.74 + [CATransaction setValue:(id)kCFBooleanTrue
1.75 + forKey:kCATransactionDisableActions];
1.76 + [layer removeFromSuperlayer];
1.77 + [CATransaction commit];
1.78 +}
1.79 +
1.80 +
1.81 +CATextLayer* AddTextLayer( CALayer *superlayer,
1.82 + NSString *text, NSFont* font,
1.83 + enum CAAutoresizingMask align )
1.84 +{
1.85 + CATextLayer *label = [[CATextLayer alloc] init];
1.86 + label.string = text;
1.87 + label.font = font;
1.88 + label.fontSize = font.pointSize;
1.89 + label.foregroundColor = kBlackColor;
1.90 +
1.91 + NSString *mode;
1.92 + if( align & kCALayerWidthSizable )
1.93 + mode = @"center";
1.94 + else if( align & kCALayerMinXMargin )
1.95 + mode = @"right";
1.96 + else
1.97 + mode = @"left";
1.98 + align |= kCALayerWidthSizable;
1.99 + label.alignmentMode = mode;
1.100 +
1.101 + CGFloat inset = superlayer.borderWidth + 3;
1.102 + CGRect bounds = CGRectInset(superlayer.bounds, inset, inset);
1.103 + CGFloat height = font.ascender;
1.104 + CGFloat y = bounds.origin.y;
1.105 + if( align & kCALayerHeightSizable )
1.106 + y += (bounds.size.height-height)/2.0;
1.107 + else if( align & kCALayerMinYMargin )
1.108 + y += bounds.size.height - height;
1.109 + align &= ~kCALayerHeightSizable;
1.110 + label.bounds = CGRectMake(0, font.descender,
1.111 + bounds.size.width, height - font.descender);
1.112 + label.position = CGPointMake(bounds.origin.x,y+font.descender);
1.113 + label.anchorPoint = CGPointMake(0,0);
1.114 +
1.115 + label.autoresizingMask = align;
1.116 + [superlayer addSublayer: label];
1.117 + [label release];
1.118 + return label;
1.119 +}
1.120 +
1.121 +
1.122 +CGImageRef CreateCGImageFromFile( NSString *path )
1.123 +{
1.124 + CGImageRef image = NULL;
1.125 + CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: path];
1.126 + CGImageSourceRef src = CGImageSourceCreateWithURL(url, NULL);
1.127 + if( src ) {
1.128 + image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
1.129 + CFRelease(src);
1.130 + if(!image) NSLog(@"Warning: CGImageSourceCreateImageAtIndex failed on file %@ (ptr size=%u)",path,sizeof(void*));
1.131 + }
1.132 + return image;
1.133 +}
1.134 +
1.135 +
1.136 +CGImageRef GetCGImageNamed( NSString *name )
1.137 +{
1.138 + // For efficiency, loaded images are cached in a dictionary by name.
1.139 + static NSMutableDictionary *sMap;
1.140 + if( ! sMap )
1.141 + sMap = [[NSMutableDictionary alloc] init];
1.142 +
1.143 + CGImageRef image = (CGImageRef) [sMap objectForKey: name];
1.144 + if( ! image ) {
1.145 + // Hasn't been cached yet, so load it:
1.146 + NSString *path;
1.147 + if( [name hasPrefix: @"/"] )
1.148 + path = name;
1.149 + else {
1.150 + path = [[NSBundle mainBundle] pathForResource: name ofType: nil];
1.151 + NSCAssert1(path,@"Couldn't find bundle image resource '%@'",name);
1.152 + }
1.153 + image = CreateCGImageFromFile(path);
1.154 + NSCAssert1(image,@"Failed to load image from %@",path);
1.155 + [sMap setObject: (id)image forKey: name];
1.156 + CGImageRelease(image);
1.157 + }
1.158 + return image;
1.159 +}
1.160 +
1.161 +
1.162 +CGColorRef GetCGPatternNamed( NSString *name ) // can be resource name or abs. path
1.163 +{
1.164 + // For efficiency, loaded patterns are cached in a dictionary by name.
1.165 + static NSMutableDictionary *sMap;
1.166 + if( ! sMap )
1.167 + sMap = [[NSMutableDictionary alloc] init];
1.168 +
1.169 + CGColorRef pattern = (CGColorRef) [sMap objectForKey: name];
1.170 + if( ! pattern ) {
1.171 + pattern = CreatePatternColor( GetCGImageNamed(name) );
1.172 + [sMap setObject: (id)pattern forKey: name];
1.173 + CGColorRelease(pattern);
1.174 + }
1.175 + return pattern;
1.176 +}
1.177 +
1.178 +
1.179 +CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb )
1.180 +{
1.181 + CGImageSourceRef src = NULL;
1.182 + NSArray *paths = [pb propertyListForType: NSFilenamesPboardType];
1.183 + if( paths.count==1 ) {
1.184 + // If a file is being dragged, read it:
1.185 + CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: [paths objectAtIndex: 0]];
1.186 + src = CGImageSourceCreateWithURL(url, NULL);
1.187 + } else {
1.188 + // Else look for an image type:
1.189 + NSString *type = [pb availableTypeFromArray: [NSImage imageUnfilteredPasteboardTypes]];
1.190 + if( type ) {
1.191 + NSData *data = [pb dataForType: type];
1.192 + src = CGImageSourceCreateWithData((CFDataRef)data, NULL);
1.193 + }
1.194 + }
1.195 + if(src) {
1.196 + CGImageRef image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
1.197 + CFRelease(src);
1.198 + return image;
1.199 + } else
1.200 + return NULL;
1.201 +}
1.202 +
1.203 +
1.204 +float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt )
1.205 +{
1.206 + // Trivial reject:
1.207 + if( pt.x<0 || pt.x>=imageSize.width || pt.y<0 || pt.y>=imageSize.height )
1.208 + return 0.0;
1.209 +
1.210 + // sTinyContext is a 1x1 CGBitmapContext whose pixmap stores only alpha.
1.211 + static UInt8 sPixel[1];
1.212 + static CGContextRef sTinyContext;
1.213 + if( ! sTinyContext ) {
1.214 + sTinyContext = CGBitmapContextCreate(sPixel, 1, 1,
1.215 + 8, 1, // bpp, rowBytes
1.216 + NULL,
1.217 + kCGImageAlphaOnly);
1.218 + CGContextSetBlendMode(sTinyContext, kCGBlendModeCopy);
1.219 + }
1.220 +
1.221 + // Draw the image into sTinyContext, positioned so the desired point is at
1.222 + // (0,0), then examine the alpha value in the pixmap:
1.223 + CGContextDrawImage(sTinyContext,
1.224 + CGRectMake(-pt.x,-pt.y, imageSize.width,imageSize.height),
1.225 + image);
1.226 + return sPixel[0] / 255.0;
1.227 +}
1.228 +
1.229 +
1.230 +#pragma mark -
1.231 +#pragma mark PATTERNS:
1.232 +
1.233 +
1.234 +// callback for CreateImagePattern.
1.235 +static void drawPatternImage (void *info, CGContextRef ctx)
1.236 +{
1.237 + CGImageRef image = (CGImageRef) info;
1.238 + CGContextDrawImage(ctx,
1.239 + CGRectMake(0,0, CGImageGetWidth(image),CGImageGetHeight(image)),
1.240 + image);
1.241 +}
1.242 +
1.243 +// callback for CreateImagePattern.
1.244 +static void releasePatternImage( void *info )
1.245 +{
1.246 + CGImageRelease( (CGImageRef)info );
1.247 +}
1.248 +
1.249 +
1.250 +CGPatternRef CreateImagePattern( CGImageRef image )
1.251 +{
1.252 + NSCParameterAssert(image);
1.253 + int width = CGImageGetWidth(image);
1.254 + int height = CGImageGetHeight(image);
1.255 + static const CGPatternCallbacks callbacks = {0, &drawPatternImage, &releasePatternImage};
1.256 + return CGPatternCreate (image,
1.257 + CGRectMake (0, 0, width, height),
1.258 + CGAffineTransformMake (1, 0, 0, 1, 0, 0),
1.259 + width,
1.260 + height,
1.261 + kCGPatternTilingConstantSpacing,
1.262 + true,
1.263 + &callbacks);
1.264 +}
1.265 +
1.266 +
1.267 +CGColorRef CreatePatternColor( CGImageRef image )
1.268 +{
1.269 + CGPatternRef pattern = CreateImagePattern(image);
1.270 + CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL);
1.271 + CGFloat components[1] = {1.0};
1.272 + CGColorRef color = CGColorCreateWithPattern(space, pattern, components);
1.273 + CGColorSpaceRelease(space);
1.274 + CGPatternRelease(pattern);
1.275 + return color;
1.276 +}