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