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