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