Source/QuartzUtils.m
author Jens Alfke <jens@mooseyard.com>
Thu May 29 15:04:06 2008 -0700 (2008-05-29)
changeset 9 a59acc683080
parent 8 45c82a071aca
child 10 6c78cc6bd7a6
permissions -rw-r--r--
Finally fixed the slow animation performance of board games; all it took was changing the board's z index from 1 to 0, somehow. Games working well now.
     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 #import <QuartzCore/QuartzCore.h>
    25 #import "Piece.h"
    26 
    27 
    28 CGColorRef kBlackColor, kWhiteColor, 
    29            kTranslucentGrayColor, kTranslucentLightGrayColor,
    30            kAlmostInvisibleWhiteColor,
    31            kHighlightColor;
    32 
    33 
    34 __attribute__((constructor))        // Makes this function run when the app loads
    35 static void InitQuartzUtils()
    36 {
    37     kBlackColor = CreateGray(0.0, 1.0);
    38     kWhiteColor = CreateGray(1.0, 1.0);
    39     kTranslucentGrayColor = CreateGray(0.0, 0.5);
    40     kTranslucentLightGrayColor = CreateGray(0.0, 0.25);
    41     kAlmostInvisibleWhiteColor = CreateGray(1, 0.05);
    42     kHighlightColor = CreateRGB(1, 1, 0, 0.5);
    43 }
    44 
    45 
    46 #if TARGET_OS_IPHONE
    47 CGColorRef CreateGray(CGFloat gray, CGFloat alpha)
    48 {
    49     CGColorSpaceRef graySpace = CGColorSpaceCreateDeviceGray();
    50     CGFloat components[2] = {gray,alpha};
    51     CGColorRef color = CGColorCreate(graySpace, components);
    52     CGColorSpaceRelease(graySpace);
    53     return color;
    54 }
    55 
    56 CGColorRef CreateRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)
    57 {
    58     CGColorSpaceRef rgbSpace = CGColorSpaceCreateDeviceRGB();
    59     CGFloat components[4] = {red,green,blue,alpha};
    60     CGColorRef color = CGColorCreate(rgbSpace, components);
    61     CGColorSpaceRelease(rgbSpace);
    62     return color;
    63 }
    64 #endif
    65 
    66 
    67 CGImageRef CreateCGImageFromFile( NSString *path )
    68 {
    69 #if TARGET_OS_IPHONE
    70     UIImage *uiImage = [UIImage imageWithContentsOfFile: path];
    71     if(!uiImage) NSLog(@"Warning: UIImage imageWithContentsOfFile failed on file %@",path);
    72     return CGImageRetain(uiImage.CGImage);
    73 #else
    74     CGImageRef image = NULL;
    75     CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: path];
    76     CGImageSourceRef src = CGImageSourceCreateWithURL(url, NULL);
    77     if( src ) {
    78         image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
    79         CFRelease(src);
    80         if(!image) NSLog(@"Warning: CGImageSourceCreateImageAtIndex failed on file %@ (ptr size=%u)",path,sizeof(void*));
    81     }
    82     return image;
    83 #endif
    84 }
    85 
    86 
    87 CGImageRef GetCGImageNamed( NSString *name )
    88 {
    89 #if TARGET_OS_IPHONE
    90     name = name.lastPathComponent;
    91     UIImage *uiImage = [UIImage imageNamed: name];
    92     NSCAssert1(uiImage,@"Couldn't find bundle image resource '%@'",name);
    93     return uiImage.CGImage;
    94 #else
    95     // For efficiency, loaded images are cached in a dictionary by name.
    96     static NSMutableDictionary *sMap;
    97     if( ! sMap )
    98         sMap = [[NSMutableDictionary alloc] init];
    99     
   100     CGImageRef image = (CGImageRef) [sMap objectForKey: name];
   101     if( ! image ) {
   102         // Hasn't been cached yet, so load it:
   103         NSString *path;
   104         if( [name hasPrefix: @"/"] )
   105             path = name;
   106         else {
   107             path = [[NSBundle mainBundle] pathForResource: name ofType: nil];
   108             NSCAssert1(path,@"Couldn't find bundle image resource '%@'",name);
   109         }
   110         image = CreateCGImageFromFile(path);
   111         NSCAssert1(image,@"Failed to load image from %@",path);
   112         [sMap setObject: (id)image forKey: name];
   113         CGImageRelease(image);
   114     }
   115     return image;
   116 #endif
   117 }
   118 
   119 
   120 CGColorRef GetCGPatternNamed( NSString *name )         // can be resource name or abs. path
   121 {
   122     // For efficiency, loaded patterns are cached in a dictionary by name.
   123     static NSMutableDictionary *sMap;
   124     if( ! sMap )
   125         sMap = [[NSMutableDictionary alloc] init];
   126     
   127     CGColorRef pattern = (CGColorRef) [sMap objectForKey: name];
   128     if( ! pattern ) {
   129         pattern = CreatePatternColor( GetCGImageNamed(name) );
   130         [sMap setObject: (id)pattern forKey: name];
   131         CGColorRelease(pattern);
   132     }
   133     return pattern;
   134 }
   135 
   136 
   137 #if ! TARGET_OS_IPHONE
   138 CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb )
   139 {
   140     CGImageSourceRef src = NULL;
   141     NSArray *paths = [pb propertyListForType: NSFilenamesPboardType];
   142     if( paths.count==1 ) {
   143         // If a file is being dragged, read it:
   144         CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: [paths objectAtIndex: 0]];
   145         src = CGImageSourceCreateWithURL(url, NULL);
   146     } else {
   147         // Else look for an image type:
   148         NSString *type = [pb availableTypeFromArray: [NSImage imageUnfilteredPasteboardTypes]];
   149         if( type ) {
   150             NSData *data = [pb dataForType: type];
   151             src = CGImageSourceCreateWithData((CFDataRef)data, NULL);
   152         }
   153     }
   154     if(src) {
   155         CGImageRef image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
   156         CFRelease(src);
   157         return image;
   158     } else
   159         return NULL;
   160 }
   161 #endif
   162 
   163 
   164 CGImageRef CreateScaledImage( CGImageRef srcImage, CGFloat scale )
   165 {
   166     int width = CGImageGetWidth(srcImage), height = CGImageGetHeight(srcImage);
   167     if( scale > 0 ) {
   168         if( scale >= 4.0 )
   169             scale /= MAX(width,height);             // interpret scale as target dimensions
   170         width = ceil( width * scale);
   171         height= ceil( height* scale);
   172     }
   173 
   174     CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
   175     CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, 8, 4*width, space,
   176                                              kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast);
   177     CGColorSpaceRelease(space);
   178     CGContextSetInterpolationQuality(ctx,kCGInterpolationHigh);
   179     CGContextDrawImage(ctx, CGRectMake(0, 0, width, height), srcImage);
   180     CGImageRef dstImage = CGBitmapContextCreateImage(ctx);
   181     CGContextRelease(ctx);
   182     return dstImage;
   183 }
   184 
   185 
   186 CGImageRef GetScaledImageNamed( NSString *imageName, CGFloat scale )
   187 {
   188     // For efficiency, loaded images are cached in a dictionary by name.
   189     static NSMutableDictionary *sMap;
   190     if( ! sMap )
   191         sMap = [[NSMutableDictionary alloc] init];
   192     
   193     NSArray *key = [NSArray arrayWithObjects: imageName, [NSNumber numberWithFloat: scale], nil];
   194     CGImageRef image = (CGImageRef) [sMap objectForKey: key];
   195     if( ! image ) {
   196         // Hasn't been cached yet, so load it:
   197         image = CreateScaledImage(GetCGImageNamed(imageName), scale);
   198         [sMap setObject: (id)image forKey: key];
   199         CGImageRelease(image);
   200     }
   201     return image;
   202 }
   203 
   204 
   205 float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt )
   206 {
   207 #if TARGET_OS_IPHONE
   208     // iPhone uses "flipped" (i.e. normal) coords, so images are wrong-way-up
   209     pt.y = imageSize.height - pt.y;
   210 #endif
   211     
   212     // Trivial reject:
   213     if( pt.x<0 || pt.x>=imageSize.width || pt.y<0 || pt.y>=imageSize.height )
   214         return 0.0;
   215     
   216     // sTinyContext is a 1x1 CGBitmapContext whose pixmap stores only alpha.
   217     static UInt8 sPixel[1];
   218     static CGContextRef sTinyContext;
   219     if( ! sTinyContext ) {
   220         sTinyContext = CGBitmapContextCreate(sPixel, 1, 1, 
   221                                              8, 1,     // bpp, rowBytes
   222                                              NULL,
   223                                              kCGImageAlphaOnly);
   224         CGContextSetBlendMode(sTinyContext, kCGBlendModeCopy);
   225     }
   226     
   227     // Draw the image into sTinyContext, positioned so the desired point is at
   228     // (0,0), then examine the alpha value in the pixmap:
   229     CGContextDrawImage(sTinyContext, 
   230                        CGRectMake(-pt.x,-pt.y, imageSize.width,imageSize.height),
   231                        image);
   232     return sPixel[0] / 255.0;
   233 }
   234 
   235 
   236 #pragma mark -
   237 #pragma mark PATTERNS:
   238 
   239 
   240 // callback for CreateImagePattern.
   241 static void drawPatternImage (void *info, CGContextRef ctx)
   242 {
   243     CGImageRef image = (CGImageRef) info;
   244     CGContextDrawImage(ctx, 
   245                        CGRectMake(0,0, CGImageGetWidth(image),CGImageGetHeight(image)),
   246                        image);
   247 }
   248 
   249 // callback for CreateImagePattern.
   250 static void releasePatternImage( void *info )
   251 {
   252     CGImageRelease( (CGImageRef)info );
   253 }
   254 
   255 
   256 CGPatternRef CreateImagePattern( CGImageRef image )
   257 {
   258     NSCParameterAssert(image);
   259     int width = CGImageGetWidth(image);
   260     int height = CGImageGetHeight(image);
   261     static const CGPatternCallbacks callbacks = {0, &drawPatternImage, &releasePatternImage};
   262     return CGPatternCreate (image,
   263                             CGRectMake (0, 0, width, height),
   264                             CGAffineTransformMake (1, 0, 0, 1, 0, 0),
   265                             width,
   266                             height,
   267                             kCGPatternTilingConstantSpacing,
   268                             true,
   269                             &callbacks);
   270 }
   271 
   272 
   273 CGColorRef CreatePatternColor( CGImageRef image )
   274 {
   275     CGPatternRef pattern = CreateImagePattern(image);
   276     CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL);
   277     CGFloat components[1] = {1.0};
   278     CGColorRef color = CGColorCreateWithPattern(space, pattern, components);
   279     CGColorSpaceRelease(space);
   280     CGPatternRelease(pattern);
   281     return color;
   282 }
   283 
   284 
   285 #pragma mark -
   286 #pragma mark PATHS:
   287 
   288 
   289 void AddRoundRect( CGContextRef ctx, CGRect rect, CGFloat radius )
   290 {
   291     radius = MIN(radius, floorf(rect.size.width/2));
   292     float x0 = CGRectGetMinX(rect), y0 = CGRectGetMinY(rect),
   293     x1 = CGRectGetMaxX(rect), y1 = CGRectGetMaxY(rect);
   294     
   295     CGContextBeginPath(ctx);
   296     CGContextMoveToPoint(ctx,x0+radius,y0);
   297     CGContextAddArcToPoint(ctx,x1,y0, x1,y1, radius);
   298     CGContextAddArcToPoint(ctx,x1,y1, x0,y1, radius);
   299     CGContextAddArcToPoint(ctx,x0,y1, x0,y0, radius);
   300     CGContextAddArcToPoint(ctx,x0,y0, x1,y0, radius);
   301     CGContextClosePath(ctx);
   302 }
   303