jens@0: /* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. jens@0: http://developer.apple.com/samplecode/GeekGameBoard/ jens@0: Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. jens@0: jens@0: Redistribution and use in source and binary forms, with or without modification, are permitted jens@0: provided that the following conditions are met: jens@0: jens@0: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@0: and the following disclaimer. jens@0: * Redistributions in binary form must reproduce the above copyright notice, this list of jens@0: conditions and the following disclaimer in the documentation and/or other materials provided jens@0: with the distribution. jens@0: jens@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@0: */ jens@0: #import "QuartzUtils.h" jens@1: #import jens@9: #import "Piece.h" jens@0: jens@0: jens@0: CGColorRef kBlackColor, kWhiteColor, jens@0: kTranslucentGrayColor, kTranslucentLightGrayColor, jens@10: kTranslucentWhiteColor, kAlmostInvisibleWhiteColor, jens@0: kHighlightColor; jens@0: jens@0: jens@0: __attribute__((constructor)) // Makes this function run when the app loads jens@0: static void InitQuartzUtils() jens@0: { jens@1: kBlackColor = CreateGray(0.0, 1.0); jens@1: kWhiteColor = CreateGray(1.0, 1.0); jens@1: kTranslucentGrayColor = CreateGray(0.0, 0.5); jens@1: kTranslucentLightGrayColor = CreateGray(0.0, 0.25); jens@10: kTranslucentWhiteColor = CreateGray(1, 0.25); jens@1: kAlmostInvisibleWhiteColor = CreateGray(1, 0.05); jens@1: kHighlightColor = CreateRGB(1, 1, 0, 0.5); jens@0: } jens@0: jens@0: jens@8: #if TARGET_OS_IPHONE jens@1: CGColorRef CreateGray(CGFloat gray, CGFloat alpha) jens@1: { jens@1: CGColorSpaceRef graySpace = CGColorSpaceCreateDeviceGray(); jens@1: CGFloat components[2] = {gray,alpha}; jens@1: CGColorRef color = CGColorCreate(graySpace, components); jens@1: CGColorSpaceRelease(graySpace); jens@1: return color; jens@1: } jens@1: jens@1: CGColorRef CreateRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha) jens@1: { jens@1: CGColorSpaceRef rgbSpace = CGColorSpaceCreateDeviceRGB(); jens@1: CGFloat components[4] = {red,green,blue,alpha}; jens@1: CGColorRef color = CGColorCreate(rgbSpace, components); jens@1: CGColorSpaceRelease(rgbSpace); jens@1: return color; jens@1: } jens@1: #endif jens@1: jens@1: jens@0: CGImageRef CreateCGImageFromFile( NSString *path ) jens@0: { jens@8: #if TARGET_OS_IPHONE jens@1: UIImage *uiImage = [UIImage imageWithContentsOfFile: path]; jens@21: if(!uiImage) Warn(@"UIImage imageWithContentsOfFile failed on file %@",path); jens@1: return CGImageRetain(uiImage.CGImage); jens@1: #else jens@0: CGImageRef image = NULL; jens@0: CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: path]; jens@0: CGImageSourceRef src = CGImageSourceCreateWithURL(url, NULL); jens@0: if( src ) { jens@0: image = CGImageSourceCreateImageAtIndex(src, 0, NULL); jens@0: CFRelease(src); jens@21: if(!image) Warn(@"CGImageSourceCreateImageAtIndex failed on file %@ (ptr size=%u)",path,sizeof(void*)); jens@0: } jens@0: return image; jens@1: #endif jens@0: } jens@0: jens@0: jens@0: CGImageRef GetCGImageNamed( NSString *name ) jens@0: { jens@8: #if TARGET_OS_IPHONE jens@1: name = name.lastPathComponent; jens@1: UIImage *uiImage = [UIImage imageNamed: name]; jens@1: NSCAssert1(uiImage,@"Couldn't find bundle image resource '%@'",name); jens@1: return uiImage.CGImage; jens@1: #else jens@0: // For efficiency, loaded images are cached in a dictionary by name. jens@0: static NSMutableDictionary *sMap; jens@0: if( ! sMap ) jens@0: sMap = [[NSMutableDictionary alloc] init]; jens@0: jens@0: CGImageRef image = (CGImageRef) [sMap objectForKey: name]; jens@0: if( ! image ) { jens@0: // Hasn't been cached yet, so load it: jens@0: NSString *path; jens@0: if( [name hasPrefix: @"/"] ) jens@0: path = name; jens@0: else { jens@11: NSString *dir = [name stringByDeletingLastPathComponent]; jens@11: name = [name lastPathComponent]; jens@11: NSString *ext = name.pathExtension; jens@11: name = [name stringByDeletingPathExtension]; jens@11: path = [[NSBundle mainBundle] pathForResource: name ofType: ext inDirectory: dir]; jens@11: NSCAssert3(path,@"Couldn't find bundle image resource '%@' type '%@' in '%@'",name,ext,dir); jens@0: } jens@0: image = CreateCGImageFromFile(path); jens@0: NSCAssert1(image,@"Failed to load image from %@",path); jens@0: [sMap setObject: (id)image forKey: name]; jens@0: CGImageRelease(image); jens@0: } jens@0: return image; jens@1: #endif jens@0: } jens@0: jens@0: jens@0: CGColorRef GetCGPatternNamed( NSString *name ) // can be resource name or abs. path jens@0: { jens@0: // For efficiency, loaded patterns are cached in a dictionary by name. jens@0: static NSMutableDictionary *sMap; jens@0: if( ! sMap ) jens@0: sMap = [[NSMutableDictionary alloc] init]; jens@0: jens@0: CGColorRef pattern = (CGColorRef) [sMap objectForKey: name]; jens@0: if( ! pattern ) { jens@0: pattern = CreatePatternColor( GetCGImageNamed(name) ); jens@0: [sMap setObject: (id)pattern forKey: name]; jens@0: CGColorRelease(pattern); jens@0: } jens@0: return pattern; jens@0: } jens@0: jens@0: jens@8: #if ! TARGET_OS_IPHONE jens@11: jens@11: BOOL CanGetCGImageFromPasteboard( NSPasteboard *pb ) jens@11: { jens@11: return [NSImage canInitWithPasteboard: pb] jens@11: || [[pb types] containsObject: @"PixadexIconPathPboardType"]; jens@11: jens@11: /*if( [[pb types] containsObject: NSFilesPromisePboardType] ) { jens@11: NSArray *fileTypes = [pb propertyListForType: NSFilesPromisePboardType]; jens@11: NSLog(@"Got file promise! Types = %@",fileTypes); jens@11: //FIX: Check file types jens@11: return NSDragOperationCopy; jens@11: }*/ jens@11: } jens@11: jens@11: CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb, iddragInfo ) jens@0: { jens@0: CGImageSourceRef src = NULL; jens@0: NSArray *paths = [pb propertyListForType: NSFilenamesPboardType]; jens@0: if( paths.count==1 ) { jens@0: // If a file is being dragged, read it: jens@0: CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: [paths objectAtIndex: 0]]; jens@0: src = CGImageSourceCreateWithURL(url, NULL); jens@11: /* jens@11: } else if( dragInfo && [[pb types] containsObject:NSFilesPromisePboardType] ) { jens@11: NSString *dropDir = NSTemporaryDirectory(); jens@11: NSArray *filenames = [dragInfo namesOfPromisedFilesDroppedAtDestination: [NSURL fileURLWithPath: dropDir]]; jens@11: NSLog(@"promised files are %@ / %@", dropDir,filenames); jens@11: src = nil; */ jens@11: } else if( [[pb types] containsObject: @"PixadexIconPathPboardType"] ) { jens@11: // Candybar 3 (nee Pixadex) doesn't drag out icons in any normal image type. jens@11: // It does support file-promises, but I couldn't get those to work using the Cocoa APIs. jens@11: // So instead I'm using its custom type that provides the path(s) to its internal ".pxicon" files. jens@11: // The icon is really easy to get from one of these: it's just file's custom icon. jens@11: NSArray *files = [pb propertyListForType: @"PixadexIconPathPboardType"]; jens@11: if( files.count == 1 ) { jens@11: NSString *path = [files objectAtIndex: 0]; jens@11: NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile: path]; jens@11: for( NSImageRep *rep in icon.representations ) { jens@11: if( [rep isKindOfClass: [NSBitmapImageRep class]] ) { jens@11: [rep retain]; //FIX: This leaks; but if the rep goes away, the CGImage breaks... jens@11: return [(NSBitmapImageRep*)rep CGImage]; jens@11: } jens@11: } jens@11: } jens@0: } else { jens@0: // Else look for an image type: jens@0: NSString *type = [pb availableTypeFromArray: [NSImage imageUnfilteredPasteboardTypes]]; jens@0: if( type ) { jens@0: NSData *data = [pb dataForType: type]; jens@0: src = CGImageSourceCreateWithData((CFDataRef)data, NULL); jens@0: } jens@0: } jens@0: if(src) { jens@0: CGImageRef image = CGImageSourceCreateImageAtIndex(src, 0, NULL); jens@0: CFRelease(src); jens@0: return image; jens@0: } else jens@0: return NULL; jens@1: } jens@1: #endif jens@0: jens@0: jens@9: CGImageRef CreateScaledImage( CGImageRef srcImage, CGFloat scale ) jens@9: { jens@9: int width = CGImageGetWidth(srcImage), height = CGImageGetHeight(srcImage); jens@9: if( scale > 0 ) { jens@9: if( scale >= 4.0 ) jens@9: scale /= MAX(width,height); // interpret scale as target dimensions jens@9: width = ceil( width * scale); jens@9: height= ceil( height* scale); jens@9: } jens@9: jens@9: CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); jens@9: CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, 8, 4*width, space, jens@9: kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast); jens@9: CGColorSpaceRelease(space); jens@9: CGContextSetInterpolationQuality(ctx,kCGInterpolationHigh); jens@9: CGContextDrawImage(ctx, CGRectMake(0, 0, width, height), srcImage); jens@9: CGImageRef dstImage = CGBitmapContextCreateImage(ctx); jens@9: CGContextRelease(ctx); jens@9: return dstImage; jens@9: } jens@9: jens@9: jens@9: CGImageRef GetScaledImageNamed( NSString *imageName, CGFloat scale ) jens@9: { jens@9: // For efficiency, loaded images are cached in a dictionary by name. jens@9: static NSMutableDictionary *sMap; jens@9: if( ! sMap ) jens@9: sMap = [[NSMutableDictionary alloc] init]; jens@9: jens@9: NSArray *key = [NSArray arrayWithObjects: imageName, [NSNumber numberWithFloat: scale], nil]; jens@9: CGImageRef image = (CGImageRef) [sMap objectForKey: key]; jens@9: if( ! image ) { jens@9: // Hasn't been cached yet, so load it: jens@9: image = CreateScaledImage(GetCGImageNamed(imageName), scale); jens@9: [sMap setObject: (id)image forKey: key]; jens@9: CGImageRelease(image); jens@9: } jens@9: return image; jens@9: } jens@9: jens@9: jens@0: float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt ) jens@0: { jens@11: NSCParameterAssert(image); jens@8: #if TARGET_OS_IPHONE jens@1: // iPhone uses "flipped" (i.e. normal) coords, so images are wrong-way-up jens@1: pt.y = imageSize.height - pt.y; jens@1: #endif jens@1: jens@0: // Trivial reject: jens@0: if( pt.x<0 || pt.x>=imageSize.width || pt.y<0 || pt.y>=imageSize.height ) jens@0: return 0.0; jens@0: jens@0: // sTinyContext is a 1x1 CGBitmapContext whose pixmap stores only alpha. jens@0: static UInt8 sPixel[1]; jens@0: static CGContextRef sTinyContext; jens@0: if( ! sTinyContext ) { jens@0: sTinyContext = CGBitmapContextCreate(sPixel, 1, 1, jens@0: 8, 1, // bpp, rowBytes jens@0: NULL, jens@0: kCGImageAlphaOnly); jens@0: CGContextSetBlendMode(sTinyContext, kCGBlendModeCopy); jens@0: } jens@0: jens@0: // Draw the image into sTinyContext, positioned so the desired point is at jens@0: // (0,0), then examine the alpha value in the pixmap: jens@0: CGContextDrawImage(sTinyContext, jens@0: CGRectMake(-pt.x,-pt.y, imageSize.width,imageSize.height), jens@0: image); jens@0: return sPixel[0] / 255.0; jens@0: } jens@0: jens@0: jens@0: #pragma mark - jens@0: #pragma mark PATTERNS: jens@0: jens@0: jens@0: // callback for CreateImagePattern. jens@0: static void drawPatternImage (void *info, CGContextRef ctx) jens@0: { jens@0: CGImageRef image = (CGImageRef) info; jens@0: CGContextDrawImage(ctx, jens@0: CGRectMake(0,0, CGImageGetWidth(image),CGImageGetHeight(image)), jens@0: image); jens@0: } jens@0: jens@0: // callback for CreateImagePattern. jens@0: static void releasePatternImage( void *info ) jens@0: { jens@0: CGImageRelease( (CGImageRef)info ); jens@0: } jens@0: jens@0: jens@0: CGPatternRef CreateImagePattern( CGImageRef image ) jens@0: { jens@0: NSCParameterAssert(image); jens@0: int width = CGImageGetWidth(image); jens@0: int height = CGImageGetHeight(image); jens@0: static const CGPatternCallbacks callbacks = {0, &drawPatternImage, &releasePatternImage}; jens@0: return CGPatternCreate (image, jens@0: CGRectMake (0, 0, width, height), jens@0: CGAffineTransformMake (1, 0, 0, 1, 0, 0), jens@0: width, jens@0: height, jens@0: kCGPatternTilingConstantSpacing, jens@0: true, jens@0: &callbacks); jens@0: } jens@0: jens@0: jens@0: CGColorRef CreatePatternColor( CGImageRef image ) jens@0: { jens@0: CGPatternRef pattern = CreateImagePattern(image); jens@0: CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL); jens@0: CGFloat components[1] = {1.0}; jens@0: CGColorRef color = CGColorCreateWithPattern(space, pattern, components); jens@0: CGColorSpaceRelease(space); jens@0: CGPatternRelease(pattern); jens@0: return color; jens@0: } jens@1: jens@1: jens@1: #pragma mark - jens@1: #pragma mark PATHS: jens@1: jens@1: jens@1: void AddRoundRect( CGContextRef ctx, CGRect rect, CGFloat radius ) jens@1: { jens@1: radius = MIN(radius, floorf(rect.size.width/2)); jens@1: float x0 = CGRectGetMinX(rect), y0 = CGRectGetMinY(rect), jens@1: x1 = CGRectGetMaxX(rect), y1 = CGRectGetMaxY(rect); jens@1: jens@1: CGContextBeginPath(ctx); jens@1: CGContextMoveToPoint(ctx,x0+radius,y0); jens@1: CGContextAddArcToPoint(ctx,x1,y0, x1,y1, radius); jens@1: CGContextAddArcToPoint(ctx,x1,y1, x0,y1, radius); jens@1: CGContextAddArcToPoint(ctx,x0,y1, x0,y0, radius); jens@1: CGContextAddArcToPoint(ctx,x0,y0, x1,y0, radius); jens@1: CGContextClosePath(ctx); jens@1: } jens@1: