Source/QuartzUtils.m
changeset 0 e9f7ba4718e1
child 1 3eb7be1dd7b6
     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 +}