Source/QuartzUtils.m
author Jens Alfke <jens@mooseyard.com>
Fri Mar 07 11:43:02 2008 -0800 (2008-03-07)
changeset 0 e9f7ba4718e1
child 1 3eb7be1dd7b6
permissions -rw-r--r--
Initial check-in into Mercurial. Branched from 1.0 release of Apple's sample code. No longer requires garbage collection. Fixed some memory leaks of CG objects. Fixed a bug when advancing to the 8th row in the Checkers game.
     1 /*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
     2     http://developer.apple.com/samplecode/GeekGameBoard/
     3     Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
     4 
     5     Redistribution and use in source and binary forms, with or without modification, are permitted
     6     provided that the following conditions are met:
     7 
     8     * Redistributions of source code must retain the above copyright notice, this list of conditions
     9       and the following disclaimer.
    10     * Redistributions in binary form must reproduce the above copyright notice, this list of
    11       conditions and the following disclaimer in the documentation and/or other materials provided
    12       with the distribution.
    13 
    14     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    15     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
    16     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
    17     BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    18     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
    19     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
    20     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
    21     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    22 */
    23 #import "QuartzUtils.h"
    24 
    25 
    26 CGColorRef kBlackColor, kWhiteColor, 
    27            kTranslucentGrayColor, kTranslucentLightGrayColor,
    28            kAlmostInvisibleWhiteColor,
    29            kHighlightColor;
    30 
    31 
    32 __attribute__((constructor))        // Makes this function run when the app loads
    33 static void InitQuartzUtils()
    34 {
    35     kBlackColor = CGColorCreateGenericGray(0.0, 1.0);
    36     kWhiteColor = CGColorCreateGenericGray(1.0, 1.0);
    37     kTranslucentGrayColor = CGColorCreateGenericGray(0.0, 0.5);
    38     kTranslucentLightGrayColor = CGColorCreateGenericGray(0.0, 0.25);
    39     kAlmostInvisibleWhiteColor = CGColorCreateGenericGray(1, 0.05);
    40     kHighlightColor = CGColorCreateGenericRGB(1, 1, 0, 0.5);
    41 }
    42 
    43 
    44 void ChangeSuperlayer( CALayer *layer, CALayer *newSuperlayer, int index )
    45 {
    46     // Disable actions, else the layer will move to the wrong place and then back!
    47     [CATransaction flush];
    48     [CATransaction begin];
    49     [CATransaction setValue:(id)kCFBooleanTrue
    50                      forKey:kCATransactionDisableActions];
    51 
    52     CGPoint pos = [newSuperlayer convertPoint: layer.position 
    53                       fromLayer: layer.superlayer];
    54     [layer retain];
    55     [layer removeFromSuperlayer];
    56     if( index >= 0 )
    57         [newSuperlayer insertSublayer: layer atIndex: index];
    58     else
    59         [newSuperlayer addSublayer: layer];
    60     layer.position = pos;
    61     [layer release];
    62 
    63     [CATransaction commit];
    64 }
    65 
    66 
    67 void RemoveImmediately( CALayer *layer )
    68 {
    69     [CATransaction flush];
    70     [CATransaction begin];
    71     [CATransaction setValue:(id)kCFBooleanTrue
    72                      forKey:kCATransactionDisableActions];
    73     [layer removeFromSuperlayer];
    74     [CATransaction commit];
    75 }    
    76 
    77 
    78 CATextLayer* AddTextLayer( CALayer *superlayer,
    79                            NSString *text, NSFont* font,
    80                            enum CAAutoresizingMask align )
    81 {
    82     CATextLayer *label = [[CATextLayer alloc] init];
    83     label.string = text;
    84     label.font = font;
    85     label.fontSize = font.pointSize;
    86     label.foregroundColor = kBlackColor;
    87     
    88     NSString *mode;
    89     if( align & kCALayerWidthSizable )
    90         mode = @"center";
    91     else if( align & kCALayerMinXMargin )
    92         mode = @"right";
    93     else
    94         mode = @"left";
    95     align |= kCALayerWidthSizable;
    96     label.alignmentMode = mode;
    97     
    98     CGFloat inset = superlayer.borderWidth + 3;
    99     CGRect bounds = CGRectInset(superlayer.bounds, inset, inset);
   100     CGFloat height = font.ascender;
   101     CGFloat y = bounds.origin.y;
   102     if( align & kCALayerHeightSizable )
   103         y += (bounds.size.height-height)/2.0;
   104     else if( align & kCALayerMinYMargin )
   105         y += bounds.size.height - height;
   106     align &= ~kCALayerHeightSizable;
   107     label.bounds = CGRectMake(0, font.descender,
   108                               bounds.size.width, height - font.descender);
   109     label.position = CGPointMake(bounds.origin.x,y+font.descender);
   110     label.anchorPoint = CGPointMake(0,0);
   111     
   112     label.autoresizingMask = align;
   113     [superlayer addSublayer: label];
   114     [label release];
   115     return label;
   116 }
   117 
   118 
   119 CGImageRef CreateCGImageFromFile( NSString *path )
   120 {
   121     CGImageRef image = NULL;
   122     CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: path];
   123     CGImageSourceRef src = CGImageSourceCreateWithURL(url, NULL);
   124     if( src ) {
   125         image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
   126         CFRelease(src);
   127         if(!image) NSLog(@"Warning: CGImageSourceCreateImageAtIndex failed on file %@ (ptr size=%u)",path,sizeof(void*));
   128     }
   129     return image;
   130 }
   131 
   132 
   133 CGImageRef GetCGImageNamed( NSString *name )
   134 {
   135     // For efficiency, loaded images are cached in a dictionary by name.
   136     static NSMutableDictionary *sMap;
   137     if( ! sMap )
   138         sMap = [[NSMutableDictionary alloc] init];
   139     
   140     CGImageRef image = (CGImageRef) [sMap objectForKey: name];
   141     if( ! image ) {
   142         // Hasn't been cached yet, so load it:
   143         NSString *path;
   144         if( [name hasPrefix: @"/"] )
   145             path = name;
   146         else {
   147             path = [[NSBundle mainBundle] pathForResource: name ofType: nil];
   148             NSCAssert1(path,@"Couldn't find bundle image resource '%@'",name);
   149         }
   150         image = CreateCGImageFromFile(path);
   151         NSCAssert1(image,@"Failed to load image from %@",path);
   152         [sMap setObject: (id)image forKey: name];
   153         CGImageRelease(image);
   154     }
   155     return image;
   156 }
   157 
   158 
   159 CGColorRef GetCGPatternNamed( NSString *name )         // can be resource name or abs. path
   160 {
   161     // For efficiency, loaded patterns are cached in a dictionary by name.
   162     static NSMutableDictionary *sMap;
   163     if( ! sMap )
   164         sMap = [[NSMutableDictionary alloc] init];
   165     
   166     CGColorRef pattern = (CGColorRef) [sMap objectForKey: name];
   167     if( ! pattern ) {
   168         pattern = CreatePatternColor( GetCGImageNamed(name) );
   169         [sMap setObject: (id)pattern forKey: name];
   170         CGColorRelease(pattern);
   171     }
   172     return pattern;
   173 }
   174 
   175 
   176 CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb )
   177 {
   178     CGImageSourceRef src = NULL;
   179     NSArray *paths = [pb propertyListForType: NSFilenamesPboardType];
   180     if( paths.count==1 ) {
   181         // If a file is being dragged, read it:
   182         CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: [paths objectAtIndex: 0]];
   183         src = CGImageSourceCreateWithURL(url, NULL);
   184     } else {
   185         // Else look for an image type:
   186         NSString *type = [pb availableTypeFromArray: [NSImage imageUnfilteredPasteboardTypes]];
   187         if( type ) {
   188             NSData *data = [pb dataForType: type];
   189             src = CGImageSourceCreateWithData((CFDataRef)data, NULL);
   190         }
   191     }
   192     if(src) {
   193         CGImageRef image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
   194         CFRelease(src);
   195         return image;
   196     } else
   197         return NULL;
   198 }    
   199 
   200 
   201 float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt )
   202 {
   203     // Trivial reject:
   204     if( pt.x<0 || pt.x>=imageSize.width || pt.y<0 || pt.y>=imageSize.height )
   205         return 0.0;
   206     
   207     // sTinyContext is a 1x1 CGBitmapContext whose pixmap stores only alpha.
   208     static UInt8 sPixel[1];
   209     static CGContextRef sTinyContext;
   210     if( ! sTinyContext ) {
   211         sTinyContext = CGBitmapContextCreate(sPixel, 1, 1, 
   212                                              8, 1,     // bpp, rowBytes
   213                                              NULL,
   214                                              kCGImageAlphaOnly);
   215         CGContextSetBlendMode(sTinyContext, kCGBlendModeCopy);
   216     }
   217     
   218     // Draw the image into sTinyContext, positioned so the desired point is at
   219     // (0,0), then examine the alpha value in the pixmap:
   220     CGContextDrawImage(sTinyContext, 
   221                        CGRectMake(-pt.x,-pt.y, imageSize.width,imageSize.height),
   222                        image);
   223     return sPixel[0] / 255.0;
   224 }
   225 
   226 
   227 #pragma mark -
   228 #pragma mark PATTERNS:
   229 
   230 
   231 // callback for CreateImagePattern.
   232 static void drawPatternImage (void *info, CGContextRef ctx)
   233 {
   234     CGImageRef image = (CGImageRef) info;
   235     CGContextDrawImage(ctx, 
   236                        CGRectMake(0,0, CGImageGetWidth(image),CGImageGetHeight(image)),
   237                        image);
   238 }
   239 
   240 // callback for CreateImagePattern.
   241 static void releasePatternImage( void *info )
   242 {
   243     CGImageRelease( (CGImageRef)info );
   244 }
   245 
   246 
   247 CGPatternRef CreateImagePattern( CGImageRef image )
   248 {
   249     NSCParameterAssert(image);
   250     int width = CGImageGetWidth(image);
   251     int height = CGImageGetHeight(image);
   252     static const CGPatternCallbacks callbacks = {0, &drawPatternImage, &releasePatternImage};
   253     return CGPatternCreate (image,
   254                             CGRectMake (0, 0, width, height),
   255                             CGAffineTransformMake (1, 0, 0, 1, 0, 0),
   256                             width,
   257                             height,
   258                             kCGPatternTilingConstantSpacing,
   259                             true,
   260                             &callbacks);
   261 }
   262 
   263 
   264 CGColorRef CreatePatternColor( CGImageRef image )
   265 {
   266     CGPatternRef pattern = CreateImagePattern(image);
   267     CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL);
   268     CGFloat components[1] = {1.0};
   269     CGColorRef color = CGColorCreateWithPattern(space, pattern, components);
   270     CGColorSpaceRelease(space);
   271     CGPatternRelease(pattern);
   272     return color;
   273 }