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@9
|
25 |
#import "Piece.h"
|
jens@0
|
26 |
|
jens@0
|
27 |
|
jens@0
|
28 |
CGColorRef kBlackColor, kWhiteColor,
|
jens@0
|
29 |
kTranslucentGrayColor, kTranslucentLightGrayColor,
|
jens@10
|
30 |
kTranslucentWhiteColor, kAlmostInvisibleWhiteColor,
|
jens@0
|
31 |
kHighlightColor;
|
jens@0
|
32 |
|
jens@0
|
33 |
|
jens@0
|
34 |
__attribute__((constructor)) // Makes this function run when the app loads
|
jens@0
|
35 |
static void InitQuartzUtils()
|
jens@0
|
36 |
{
|
jens@1
|
37 |
kBlackColor = CreateGray(0.0, 1.0);
|
jens@1
|
38 |
kWhiteColor = CreateGray(1.0, 1.0);
|
jens@1
|
39 |
kTranslucentGrayColor = CreateGray(0.0, 0.5);
|
jens@1
|
40 |
kTranslucentLightGrayColor = CreateGray(0.0, 0.25);
|
jens@10
|
41 |
kTranslucentWhiteColor = CreateGray(1, 0.25);
|
jens@1
|
42 |
kAlmostInvisibleWhiteColor = CreateGray(1, 0.05);
|
jens@1
|
43 |
kHighlightColor = CreateRGB(1, 1, 0, 0.5);
|
jens@0
|
44 |
}
|
jens@0
|
45 |
|
jens@0
|
46 |
|
jens@8
|
47 |
#if TARGET_OS_IPHONE
|
jens@1
|
48 |
CGColorRef CreateGray(CGFloat gray, CGFloat alpha)
|
jens@1
|
49 |
{
|
jens@1
|
50 |
CGColorSpaceRef graySpace = CGColorSpaceCreateDeviceGray();
|
jens@1
|
51 |
CGFloat components[2] = {gray,alpha};
|
jens@1
|
52 |
CGColorRef color = CGColorCreate(graySpace, components);
|
jens@1
|
53 |
CGColorSpaceRelease(graySpace);
|
jens@1
|
54 |
return color;
|
jens@1
|
55 |
}
|
jens@1
|
56 |
|
jens@1
|
57 |
CGColorRef CreateRGB(CGFloat red, CGFloat green, CGFloat blue, CGFloat alpha)
|
jens@1
|
58 |
{
|
jens@1
|
59 |
CGColorSpaceRef rgbSpace = CGColorSpaceCreateDeviceRGB();
|
jens@1
|
60 |
CGFloat components[4] = {red,green,blue,alpha};
|
jens@1
|
61 |
CGColorRef color = CGColorCreate(rgbSpace, components);
|
jens@1
|
62 |
CGColorSpaceRelease(rgbSpace);
|
jens@1
|
63 |
return color;
|
jens@1
|
64 |
}
|
jens@1
|
65 |
#endif
|
jens@1
|
66 |
|
jens@1
|
67 |
|
jens@0
|
68 |
CGImageRef CreateCGImageFromFile( NSString *path )
|
jens@0
|
69 |
{
|
jens@8
|
70 |
#if TARGET_OS_IPHONE
|
jens@1
|
71 |
UIImage *uiImage = [UIImage imageWithContentsOfFile: path];
|
jens@1
|
72 |
if(!uiImage) NSLog(@"Warning: UIImage imageWithContentsOfFile failed on file %@",path);
|
jens@1
|
73 |
return CGImageRetain(uiImage.CGImage);
|
jens@1
|
74 |
#else
|
jens@0
|
75 |
CGImageRef image = NULL;
|
jens@0
|
76 |
CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: path];
|
jens@0
|
77 |
CGImageSourceRef src = CGImageSourceCreateWithURL(url, NULL);
|
jens@0
|
78 |
if( src ) {
|
jens@0
|
79 |
image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
|
jens@0
|
80 |
CFRelease(src);
|
jens@0
|
81 |
if(!image) NSLog(@"Warning: CGImageSourceCreateImageAtIndex failed on file %@ (ptr size=%u)",path,sizeof(void*));
|
jens@0
|
82 |
}
|
jens@0
|
83 |
return image;
|
jens@1
|
84 |
#endif
|
jens@0
|
85 |
}
|
jens@0
|
86 |
|
jens@0
|
87 |
|
jens@0
|
88 |
CGImageRef GetCGImageNamed( NSString *name )
|
jens@0
|
89 |
{
|
jens@8
|
90 |
#if TARGET_OS_IPHONE
|
jens@1
|
91 |
name = name.lastPathComponent;
|
jens@1
|
92 |
UIImage *uiImage = [UIImage imageNamed: name];
|
jens@1
|
93 |
NSCAssert1(uiImage,@"Couldn't find bundle image resource '%@'",name);
|
jens@1
|
94 |
return uiImage.CGImage;
|
jens@1
|
95 |
#else
|
jens@0
|
96 |
// For efficiency, loaded images are cached in a dictionary by name.
|
jens@0
|
97 |
static NSMutableDictionary *sMap;
|
jens@0
|
98 |
if( ! sMap )
|
jens@0
|
99 |
sMap = [[NSMutableDictionary alloc] init];
|
jens@0
|
100 |
|
jens@0
|
101 |
CGImageRef image = (CGImageRef) [sMap objectForKey: name];
|
jens@0
|
102 |
if( ! image ) {
|
jens@0
|
103 |
// Hasn't been cached yet, so load it:
|
jens@0
|
104 |
NSString *path;
|
jens@0
|
105 |
if( [name hasPrefix: @"/"] )
|
jens@0
|
106 |
path = name;
|
jens@0
|
107 |
else {
|
jens@11
|
108 |
NSString *dir = [name stringByDeletingLastPathComponent];
|
jens@11
|
109 |
name = [name lastPathComponent];
|
jens@11
|
110 |
NSString *ext = name.pathExtension;
|
jens@11
|
111 |
name = [name stringByDeletingPathExtension];
|
jens@11
|
112 |
path = [[NSBundle mainBundle] pathForResource: name ofType: ext inDirectory: dir];
|
jens@11
|
113 |
NSCAssert3(path,@"Couldn't find bundle image resource '%@' type '%@' in '%@'",name,ext,dir);
|
jens@0
|
114 |
}
|
jens@0
|
115 |
image = CreateCGImageFromFile(path);
|
jens@0
|
116 |
NSCAssert1(image,@"Failed to load image from %@",path);
|
jens@0
|
117 |
[sMap setObject: (id)image forKey: name];
|
jens@0
|
118 |
CGImageRelease(image);
|
jens@0
|
119 |
}
|
jens@0
|
120 |
return image;
|
jens@1
|
121 |
#endif
|
jens@0
|
122 |
}
|
jens@0
|
123 |
|
jens@0
|
124 |
|
jens@0
|
125 |
CGColorRef GetCGPatternNamed( NSString *name ) // can be resource name or abs. path
|
jens@0
|
126 |
{
|
jens@0
|
127 |
// For efficiency, loaded patterns are cached in a dictionary by name.
|
jens@0
|
128 |
static NSMutableDictionary *sMap;
|
jens@0
|
129 |
if( ! sMap )
|
jens@0
|
130 |
sMap = [[NSMutableDictionary alloc] init];
|
jens@0
|
131 |
|
jens@0
|
132 |
CGColorRef pattern = (CGColorRef) [sMap objectForKey: name];
|
jens@0
|
133 |
if( ! pattern ) {
|
jens@0
|
134 |
pattern = CreatePatternColor( GetCGImageNamed(name) );
|
jens@0
|
135 |
[sMap setObject: (id)pattern forKey: name];
|
jens@0
|
136 |
CGColorRelease(pattern);
|
jens@0
|
137 |
}
|
jens@0
|
138 |
return pattern;
|
jens@0
|
139 |
}
|
jens@0
|
140 |
|
jens@0
|
141 |
|
jens@8
|
142 |
#if ! TARGET_OS_IPHONE
|
jens@11
|
143 |
|
jens@11
|
144 |
BOOL CanGetCGImageFromPasteboard( NSPasteboard *pb )
|
jens@11
|
145 |
{
|
jens@11
|
146 |
return [NSImage canInitWithPasteboard: pb]
|
jens@11
|
147 |
|| [[pb types] containsObject: @"PixadexIconPathPboardType"];
|
jens@11
|
148 |
|
jens@11
|
149 |
/*if( [[pb types] containsObject: NSFilesPromisePboardType] ) {
|
jens@11
|
150 |
NSArray *fileTypes = [pb propertyListForType: NSFilesPromisePboardType];
|
jens@11
|
151 |
NSLog(@"Got file promise! Types = %@",fileTypes);
|
jens@11
|
152 |
//FIX: Check file types
|
jens@11
|
153 |
return NSDragOperationCopy;
|
jens@11
|
154 |
}*/
|
jens@11
|
155 |
}
|
jens@11
|
156 |
|
jens@11
|
157 |
CGImageRef GetCGImageFromPasteboard( NSPasteboard *pb, id<NSDraggingInfo>dragInfo )
|
jens@0
|
158 |
{
|
jens@0
|
159 |
CGImageSourceRef src = NULL;
|
jens@0
|
160 |
NSArray *paths = [pb propertyListForType: NSFilenamesPboardType];
|
jens@0
|
161 |
if( paths.count==1 ) {
|
jens@0
|
162 |
// If a file is being dragged, read it:
|
jens@0
|
163 |
CFURLRef url = (CFURLRef) [NSURL fileURLWithPath: [paths objectAtIndex: 0]];
|
jens@0
|
164 |
src = CGImageSourceCreateWithURL(url, NULL);
|
jens@11
|
165 |
/*
|
jens@11
|
166 |
} else if( dragInfo && [[pb types] containsObject:NSFilesPromisePboardType] ) {
|
jens@11
|
167 |
NSString *dropDir = NSTemporaryDirectory();
|
jens@11
|
168 |
NSArray *filenames = [dragInfo namesOfPromisedFilesDroppedAtDestination: [NSURL fileURLWithPath: dropDir]];
|
jens@11
|
169 |
NSLog(@"promised files are %@ / %@", dropDir,filenames);
|
jens@11
|
170 |
src = nil; */
|
jens@11
|
171 |
} else if( [[pb types] containsObject: @"PixadexIconPathPboardType"] ) {
|
jens@11
|
172 |
// Candybar 3 (nee Pixadex) doesn't drag out icons in any normal image type.
|
jens@11
|
173 |
// It does support file-promises, but I couldn't get those to work using the Cocoa APIs.
|
jens@11
|
174 |
// So instead I'm using its custom type that provides the path(s) to its internal ".pxicon" files.
|
jens@11
|
175 |
// The icon is really easy to get from one of these: it's just file's custom icon.
|
jens@11
|
176 |
NSArray *files = [pb propertyListForType: @"PixadexIconPathPboardType"];
|
jens@11
|
177 |
if( files.count == 1 ) {
|
jens@11
|
178 |
NSString *path = [files objectAtIndex: 0];
|
jens@11
|
179 |
NSImage *icon = [[NSWorkspace sharedWorkspace] iconForFile: path];
|
jens@11
|
180 |
for( NSImageRep *rep in icon.representations ) {
|
jens@11
|
181 |
if( [rep isKindOfClass: [NSBitmapImageRep class]] ) {
|
jens@11
|
182 |
[rep retain]; //FIX: This leaks; but if the rep goes away, the CGImage breaks...
|
jens@11
|
183 |
return [(NSBitmapImageRep*)rep CGImage];
|
jens@11
|
184 |
}
|
jens@11
|
185 |
}
|
jens@11
|
186 |
}
|
jens@0
|
187 |
} else {
|
jens@0
|
188 |
// Else look for an image type:
|
jens@0
|
189 |
NSString *type = [pb availableTypeFromArray: [NSImage imageUnfilteredPasteboardTypes]];
|
jens@0
|
190 |
if( type ) {
|
jens@0
|
191 |
NSData *data = [pb dataForType: type];
|
jens@0
|
192 |
src = CGImageSourceCreateWithData((CFDataRef)data, NULL);
|
jens@0
|
193 |
}
|
jens@0
|
194 |
}
|
jens@0
|
195 |
if(src) {
|
jens@0
|
196 |
CGImageRef image = CGImageSourceCreateImageAtIndex(src, 0, NULL);
|
jens@0
|
197 |
CFRelease(src);
|
jens@0
|
198 |
return image;
|
jens@0
|
199 |
} else
|
jens@0
|
200 |
return NULL;
|
jens@1
|
201 |
}
|
jens@1
|
202 |
#endif
|
jens@0
|
203 |
|
jens@0
|
204 |
|
jens@9
|
205 |
CGImageRef CreateScaledImage( CGImageRef srcImage, CGFloat scale )
|
jens@9
|
206 |
{
|
jens@9
|
207 |
int width = CGImageGetWidth(srcImage), height = CGImageGetHeight(srcImage);
|
jens@9
|
208 |
if( scale > 0 ) {
|
jens@9
|
209 |
if( scale >= 4.0 )
|
jens@9
|
210 |
scale /= MAX(width,height); // interpret scale as target dimensions
|
jens@9
|
211 |
width = ceil( width * scale);
|
jens@9
|
212 |
height= ceil( height* scale);
|
jens@9
|
213 |
}
|
jens@9
|
214 |
|
jens@9
|
215 |
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
|
jens@9
|
216 |
CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, 8, 4*width, space,
|
jens@9
|
217 |
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast);
|
jens@9
|
218 |
CGColorSpaceRelease(space);
|
jens@9
|
219 |
CGContextSetInterpolationQuality(ctx,kCGInterpolationHigh);
|
jens@9
|
220 |
CGContextDrawImage(ctx, CGRectMake(0, 0, width, height), srcImage);
|
jens@9
|
221 |
CGImageRef dstImage = CGBitmapContextCreateImage(ctx);
|
jens@9
|
222 |
CGContextRelease(ctx);
|
jens@9
|
223 |
return dstImage;
|
jens@9
|
224 |
}
|
jens@9
|
225 |
|
jens@9
|
226 |
|
jens@9
|
227 |
CGImageRef GetScaledImageNamed( NSString *imageName, CGFloat scale )
|
jens@9
|
228 |
{
|
jens@9
|
229 |
// For efficiency, loaded images are cached in a dictionary by name.
|
jens@9
|
230 |
static NSMutableDictionary *sMap;
|
jens@9
|
231 |
if( ! sMap )
|
jens@9
|
232 |
sMap = [[NSMutableDictionary alloc] init];
|
jens@9
|
233 |
|
jens@9
|
234 |
NSArray *key = [NSArray arrayWithObjects: imageName, [NSNumber numberWithFloat: scale], nil];
|
jens@9
|
235 |
CGImageRef image = (CGImageRef) [sMap objectForKey: key];
|
jens@9
|
236 |
if( ! image ) {
|
jens@9
|
237 |
// Hasn't been cached yet, so load it:
|
jens@9
|
238 |
image = CreateScaledImage(GetCGImageNamed(imageName), scale);
|
jens@9
|
239 |
[sMap setObject: (id)image forKey: key];
|
jens@9
|
240 |
CGImageRelease(image);
|
jens@9
|
241 |
}
|
jens@9
|
242 |
return image;
|
jens@9
|
243 |
}
|
jens@9
|
244 |
|
jens@9
|
245 |
|
jens@0
|
246 |
float GetPixelAlpha( CGImageRef image, CGSize imageSize, CGPoint pt )
|
jens@0
|
247 |
{
|
jens@11
|
248 |
NSCParameterAssert(image);
|
jens@8
|
249 |
#if TARGET_OS_IPHONE
|
jens@1
|
250 |
// iPhone uses "flipped" (i.e. normal) coords, so images are wrong-way-up
|
jens@1
|
251 |
pt.y = imageSize.height - pt.y;
|
jens@1
|
252 |
#endif
|
jens@1
|
253 |
|
jens@0
|
254 |
// Trivial reject:
|
jens@0
|
255 |
if( pt.x<0 || pt.x>=imageSize.width || pt.y<0 || pt.y>=imageSize.height )
|
jens@0
|
256 |
return 0.0;
|
jens@0
|
257 |
|
jens@0
|
258 |
// sTinyContext is a 1x1 CGBitmapContext whose pixmap stores only alpha.
|
jens@0
|
259 |
static UInt8 sPixel[1];
|
jens@0
|
260 |
static CGContextRef sTinyContext;
|
jens@0
|
261 |
if( ! sTinyContext ) {
|
jens@0
|
262 |
sTinyContext = CGBitmapContextCreate(sPixel, 1, 1,
|
jens@0
|
263 |
8, 1, // bpp, rowBytes
|
jens@0
|
264 |
NULL,
|
jens@0
|
265 |
kCGImageAlphaOnly);
|
jens@0
|
266 |
CGContextSetBlendMode(sTinyContext, kCGBlendModeCopy);
|
jens@0
|
267 |
}
|
jens@0
|
268 |
|
jens@0
|
269 |
// Draw the image into sTinyContext, positioned so the desired point is at
|
jens@0
|
270 |
// (0,0), then examine the alpha value in the pixmap:
|
jens@0
|
271 |
CGContextDrawImage(sTinyContext,
|
jens@0
|
272 |
CGRectMake(-pt.x,-pt.y, imageSize.width,imageSize.height),
|
jens@0
|
273 |
image);
|
jens@0
|
274 |
return sPixel[0] / 255.0;
|
jens@0
|
275 |
}
|
jens@0
|
276 |
|
jens@0
|
277 |
|
jens@0
|
278 |
#pragma mark -
|
jens@0
|
279 |
#pragma mark PATTERNS:
|
jens@0
|
280 |
|
jens@0
|
281 |
|
jens@0
|
282 |
// callback for CreateImagePattern.
|
jens@0
|
283 |
static void drawPatternImage (void *info, CGContextRef ctx)
|
jens@0
|
284 |
{
|
jens@0
|
285 |
CGImageRef image = (CGImageRef) info;
|
jens@0
|
286 |
CGContextDrawImage(ctx,
|
jens@0
|
287 |
CGRectMake(0,0, CGImageGetWidth(image),CGImageGetHeight(image)),
|
jens@0
|
288 |
image);
|
jens@0
|
289 |
}
|
jens@0
|
290 |
|
jens@0
|
291 |
// callback for CreateImagePattern.
|
jens@0
|
292 |
static void releasePatternImage( void *info )
|
jens@0
|
293 |
{
|
jens@0
|
294 |
CGImageRelease( (CGImageRef)info );
|
jens@0
|
295 |
}
|
jens@0
|
296 |
|
jens@0
|
297 |
|
jens@0
|
298 |
CGPatternRef CreateImagePattern( CGImageRef image )
|
jens@0
|
299 |
{
|
jens@0
|
300 |
NSCParameterAssert(image);
|
jens@0
|
301 |
int width = CGImageGetWidth(image);
|
jens@0
|
302 |
int height = CGImageGetHeight(image);
|
jens@0
|
303 |
static const CGPatternCallbacks callbacks = {0, &drawPatternImage, &releasePatternImage};
|
jens@0
|
304 |
return CGPatternCreate (image,
|
jens@0
|
305 |
CGRectMake (0, 0, width, height),
|
jens@0
|
306 |
CGAffineTransformMake (1, 0, 0, 1, 0, 0),
|
jens@0
|
307 |
width,
|
jens@0
|
308 |
height,
|
jens@0
|
309 |
kCGPatternTilingConstantSpacing,
|
jens@0
|
310 |
true,
|
jens@0
|
311 |
&callbacks);
|
jens@0
|
312 |
}
|
jens@0
|
313 |
|
jens@0
|
314 |
|
jens@0
|
315 |
CGColorRef CreatePatternColor( CGImageRef image )
|
jens@0
|
316 |
{
|
jens@0
|
317 |
CGPatternRef pattern = CreateImagePattern(image);
|
jens@0
|
318 |
CGColorSpaceRef space = CGColorSpaceCreatePattern(NULL);
|
jens@0
|
319 |
CGFloat components[1] = {1.0};
|
jens@0
|
320 |
CGColorRef color = CGColorCreateWithPattern(space, pattern, components);
|
jens@0
|
321 |
CGColorSpaceRelease(space);
|
jens@0
|
322 |
CGPatternRelease(pattern);
|
jens@0
|
323 |
return color;
|
jens@0
|
324 |
}
|
jens@1
|
325 |
|
jens@1
|
326 |
|
jens@1
|
327 |
#pragma mark -
|
jens@1
|
328 |
#pragma mark PATHS:
|
jens@1
|
329 |
|
jens@1
|
330 |
|
jens@1
|
331 |
void AddRoundRect( CGContextRef ctx, CGRect rect, CGFloat radius )
|
jens@1
|
332 |
{
|
jens@1
|
333 |
radius = MIN(radius, floorf(rect.size.width/2));
|
jens@1
|
334 |
float x0 = CGRectGetMinX(rect), y0 = CGRectGetMinY(rect),
|
jens@1
|
335 |
x1 = CGRectGetMaxX(rect), y1 = CGRectGetMaxY(rect);
|
jens@1
|
336 |
|
jens@1
|
337 |
CGContextBeginPath(ctx);
|
jens@1
|
338 |
CGContextMoveToPoint(ctx,x0+radius,y0);
|
jens@1
|
339 |
CGContextAddArcToPoint(ctx,x1,y0, x1,y1, radius);
|
jens@1
|
340 |
CGContextAddArcToPoint(ctx,x1,y1, x0,y1, radius);
|
jens@1
|
341 |
CGContextAddArcToPoint(ctx,x0,y1, x0,y0, radius);
|
jens@1
|
342 |
CGContextAddArcToPoint(ctx,x0,y0, x1,y0, radius);
|
jens@1
|
343 |
CGContextClosePath(ctx);
|
jens@1
|
344 |
}
|
jens@1
|
345 |
|