<Error>: doClip: empty path. written to your console, with no obvious place to put a breakpoint. To find out where you're doing something wrong, put a breakpoint on CGPostError. float domain[2] = { 0.0, 1.0 }; // 1-in function
float range[8] = { 0.0, 1.0, // N-out, RGBA
0.0, 1.0,
0.0, 1.0,
0.0, 1.0 };
CGFunctionCallbacks callbacks = { 0, evaluate, NULL };
CGFunctionRef shaderFunction;
shaderFunction = CGFunctionCreate (self, // info / rock / context
1, // # of inputs for domain
domain, // domain
4, // # of inputs for range
range, // range
&callbacks);
CGColorSpaceRef deviceRGB;
deviceRGB = CGColorSpaceCreateDeviceRGB ();
CGShadingRef shader;
shader = CGShadingCreateAxial (deviceRGB, // colorspace
cgpoint(start), // start of axis
cgpoint(end), // end of axis
shaderFunction, // shader, 1-n, n-out
NO, // extend start
NO); // extend end
CGContextSaveGState (context); {
NSRect bounds = [self bounds];
CGContextClipToRect (context, cgrect(bounds));
CGContextDrawShading (context, shader);
} CGContextRestoreGState (context);
[self drawSpotAt: start size: 4];
[self drawSpotAt: end size: 4];
CGFunctionRelease (shaderFunction);
CGColorSpaceRelease (deviceRGB);
CGShadingRelease (shader);
And the evaluator function is given an array of inputs and outputs. Use the in value(s) (which run from your domain's start-to-finish values) to generate the out values (which should be in the range's start-to-finish values):
static void evaluate (void *info, const float *in, float *out)
{
float thing;
thing = in[0];
out[0] = thing;
out[1] = thing;
out[2] = thing;
out[3] = 1.0;
} // evaluate CGPathRef border = ... get a path from somewhere;
CGContextRef context = UIGraphicsGetCurrentContext ();
CGContextSaveGState (context); {
CGContextAddPath (context, border);
CGContextClip (context);
// draw draw draw
} CGContextRestoreGState (context);
// Based on Foley and van Dam algorithm.
void ConvertHSLToRGB (const CGFloat *hslComponents, CGFloat *rgbComponents) {
CGFloat hue = hslComponents[0];
CGFloat saturation = hslComponents[1];
CGFloat lightness = hslComponents[2];
CGFloat temp1, temp2;
CGFloat rgb[3]; // "temp3"
if (saturation == 0) {
// Like totally gray man.
rgb[0] = rgb[1] = rgb[2] = lightness;
} else {
if (lightness < 0.5) temp2 = lightness * (1.0 + saturation);
else temp2 = (lightness + saturation) - (lightness * saturation);
temp1 = (lightness * 2.0) - temp2;
// Convert hue to 0..1
hue /= 360.0;
// Use the rgb array as workspace for our "temp3"s
rgb[0] = hue + (1.0 / 3.0);
rgb[1] = hue;
rgb[2] = hue - (1.0 / 3.0);
// Magic
for (int i = 0; i < 3; i++) {
if (rgb[i] < 0.0) rgb[i] += 1.0;
else if (rgb[i] > 1.0) rgb[i] -= 1.0;
if (6.0 * rgb[i] < 1.0) rgb[i] = temp1 + ((temp2 - temp1)
* 6.0 * rgb[i]);
else if (2.0 * rgb[i] < 1.0) rgb[i] = temp2;
else if (3.0 * rgb[i] < 2.0) rgb[i] = temp1 + ((temp2 - temp1)
* ((2.0 / 3.0) - rgb[i]) * 6.0);
else rgb[i] = temp1;
}
}
// Clamp to 0..1 and put into the return pile.
for (int i = 0; i < 3; i++) {
rgbComponents[i] = MAX (0.0, MIN (1.0, rgb[i]));
}
} // ConvertHSLToRGB
// Based on Foley and van Dam algorithm.
void ConvertRGBToHSL (const CGFloat *rgbComponents, CGFloat *hslComponents) {
CGFloat red = rgbComponents[0];
CGFloat green = rgbComponents[1];
CGFloat blue = rgbComponents[2];
CGFloat maxColor = MAX (red, MAX (green, blue));
CGFloat minColor = MIN (red, MIN (green, blue));
CGFloat deltaColor = maxColor - minColor;
CGFloat hue, saturation;
CGFloat lightness = (maxColor + minColor) / 2.0;
// All the same means a gray color.
if (maxColor == minColor) {
saturation = 0.0;
hue = 0.0; // officially undefined with gray, but go ahead and zero out.
} else {
if (lightness < 0.5) saturation = deltaColor / (maxColor + minColor);
else saturation = deltaColor / (2.0 - deltaColor);
if (red == maxColor) hue = (green - blue) / deltaColor;
else if (green == maxColor) hue = 2.0 + (blue - red) / deltaColor;
else hue = 4.0 + (red - green) / deltaColor;
// H will be in the range of 0..6, convert to degrees.
hue *= 60.0;
// Convert to positive angle.
if (hue < 0) hue += 360.0;
}
// Clamp to legal values.
hslComponents[0] = MAX (0.0, MIN (360.0, hue));
hslComponents[1] = MAX (0.0, MIN (1.0, saturation));
hslComponents[2] = MAX (0.0, MIN (1.0, lightness));
} // ConvertRGBToHSLCGAffineTransformRotate. I personally think better in degrees (360 degrees in a circle).
Convert degrees to radians by multiplying by 180 / pi.
Convert radians to degrees by multiplying by pi / 180
I have a couple of #defines I stick into a common header for projects that need it;
#define BWDegToRad(d) ((d) * M_PI / 180.0) #define BWRadToDeg(r) ((r) * 180 / M_PI)
- (CGImageRef) newMaskFromImage: (UIImage *) image {
CGImageRef maskRef = image.CGImage;
CGImageRef mask2 = CGImageMaskCreate (CGImageGetWidth (maskRef),
CGImageGetHeight (maskRef),
CGImageGetBitsPerComponent (maskRef),
CGImageGetBitsPerPixel (maskRef),
CGImageGetBytesPerRow (maskRef),
CGImageGetDataProvider (maskRef),
NULL, NO); // provider, shouldInterpolate
return mask2;
} // newMaskFromImage
NSString *blah = @"Placeholder Pane";
NSSize size = [blah sizeWithAttributes: nil];
NSPoint startPoint;
startPoint.x = bounds.origin.x + bounds.size.width / 2 - size.width / 2;
startPoint.y = bounds.origin.y + bounds.size.height / 2 - size.height / 2;
[blah drawAtPoint: startPoint
withAttributes: nil];
- (void) drawGradientInRect: (CGRect) rect
colorSpace: (CGColorSpaceRef) colorSpcae
context: (CGContextRef) context {
CGFloat startComponents[4] = { 254.0 / 255.0, 254.0 / 255.0, 254.0 / 255.0, 1.0 };
CGFloat endComponents[4] = { 206.0 / 255.0, 206.0 / 255.0, 206.0 / 255.0, 1.0 };
CGColorRef startColor = CGColorCreate (colorSpace, startComponents);
CGColorRef endColor = CGColorCreate (colorSpace, endComponents);
NSArray *array = [NSArray arrayWithObjects: (id)startColor, (id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors (colorSpace,
(CFArrayRef)array, NULL);
CGPoint endPoint = rect.origin;
endPoint.y += rect.size.height;
// Don't let the gradient bleed all over everything
CGContextSaveGState (context); {
CGContextClipToRect (context, rect);
CGContextDrawLinearGradient (context, gradient, rect.origin, endPoint, 0);
} CGContextRestoreGState (context);
CGGradientRelease (gradient);
CGColorRelease (startColor);
CGColorRelease (endColor);
} // drawGradientInRect
- (void) drawText: (NSString *) text
centeredInRect: (CGRect) rect
font: (UIFont *) font {
CGSize textSize = [text sizeWithFont: font
constrainedToSize: rect.size
lineBreakMode: NSLineBreakByWordWrapping];
// Center text rect inside of |rect|.
CGRect textRect = CGRectMake (CGRectGetMidX(rect) - textSize.width / 2.0,
CGRectGetMidY(rect) - textSize.height / 2.0,
textSize.width, textSize.height);
[text drawInRect: textRect
withFont: font
lineBreakMode: NSLineBreakByWordWrapping
alignment: NSTextAlignmentCenter];
} // drawText
- (void) drawRect: (CGRect) rect {
NSString *string = @"132";
CGContextRef ctx = UIGraphicsGetCurrentContext ();
CGContextTranslateCTM (ctx, 0.0, self.bounds.size.height);
CGContextScaleCTM (ctx, 1.0, -1.0);
CGContextSelectFont (ctx, "Helvetica-Bold", 48.0, kCGEncodingMacRoman);
CGContextSetTextDrawingMode (ctx, kCGTextFillStroke);
// Draw a background to see the text on.
CGContextSetFillColorWithColor (ctx, [[UIColor yellowColor] CGColor]);
CGContextFillRect (ctx, self.bounds);
CGContextSetFillColorWithColor (ctx, [[UIColor blackColor] CGColor]);
CGContextSetStrokeColorWithColor (ctx, [[UIColor whiteColor] CGColor]);
CGContextSetLineWidth (ctx, 2);
CGContextSetShadow (ctx, CGSizeMake (3.0, 3.0), 2.5);
CGContextShowTextAtPoint (ctx, 50, 50, [string UTF8String], string.length);
} // drawRect
(Thanks to Chris Liscio for handy pointers)#define cgrect(nsrect) (*(CGRect *)&(nsrect))
- (void) drawRect: (NSRect) rect
{
NSRect bounds = [self bounds];
NSGraphicsContext *cocoaContext = [NSGraphicsContext currentContext];
CGContextRef context = (CGContextRef)[cocoaContext graphicsPort];
CGContextSetLineWidth (context, 5.0);
CGContextBeginPath(context); {
CGContextAddRect (context, cgrect(bounds));
CGContextSetRGBFillColor (context, 1.0, 0.9, 0.8, 1.0);
} CGContextFillPath(context);
} // drawRect- (NSImage *) captureScreenImageWithFrame: (NSRect) frame
{
// Fetch a graphics port of the screen
CGrafPtr screenPort = CreateNewPort ();
Rect screenRect;
GetPortBounds (screenPort, &screenRect);
// Make a temporary window as a receptacle
NSWindow *grabWindow = [[NSWindow alloc] initWithContentRect: frame
styleMask: NSBorderlessWindowMask
backing: NSBackingStoreRetained
defer: NO
screen: nil];
CGrafPtr windowPort = GetWindowPort ([grabWindow windowRef]);
Rect windowRect;
GetPortBounds (windowPort, &windowRect);
SetPort (windowPort);
// Copy the screen to the temporary window
CopyBits (GetPortBitMapForCopyBits(screenPort),
GetPortBitMapForCopyBits(windowPort),
&screenRect,
&windowRect,
srcCopy,
NULL);
// Get the contents of the temporary window into an NSImage
NSView *grabContentView = [grabWindow contentView];
[grabContentView lockFocus];
NSBitmapImageRep *screenRep;
screenRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: frame];
[grabContentView unlockFocus];
NSImage *screenImage = [[NSImage alloc] initWithSize: frame.size];
[screenImage addRepresentation: screenRep];
// Clean up
[grabWindow close];
DisposePort(screenPort);
return (screenImage);
} // captureScreenImageWithFrame
- (void) fillMask: (CGImageRef) mask withColor: (UIColor *) color
atOffset: (CGPoint) offset {
if (color == nil) color = [UIColor purpleColor]; // everybody loves purple
CGContextRef context = UIGraphicsGetCurrentContext ();
CGSize size = self.bounds.size;
CGContextSaveGState (context); {
CGContextTranslateCTM (context, offset.x, offset.y);
// Account for retina-sized graphics.
CGContextScaleCTM (context, size.width / CGImageGetWidth(mask),
size.height / CGImageGetHeight(mask));
CGRect rect = { {0.0, 0.0}, { CGImageGetWidth(mask), CGImageGetHeight(mask) } };
CGContextClipToMask (context, rect, mask);
[color set];
UIRectFill (rect);
} CGContextRestoreGState (context);
} // fillMask
CGContextRef context = ...;
...
CGContextSaveGState (context); {
[drawable drawWithContext: context
inRect: bounds
withMetrics: self.metrics]; // or whatever drawing you're doing
} CGContextRestoreGState (context);
Edit: Gus Meuller of Flying Meat fame has a block-based utility that does this:
void FMCGContextHoldGState (CGContextRef context, void (^block)()) {
CGContextSaveGState(context); {
block ();
} CGContextRestoreGState(context);
}
and if you're wanting to do something similar with NSGraphicsContexts:
void FMNSContextHoldGState (void (^block)()) {
[NSGraphicsContext saveGraphicsState]; {
block ();
} [NSGraphicsContext restoreGraphicsState];
}
CFRelease when done.
- (CGPathRef) newPathForRoundRect: (CGRect) rect
radius: (CGFloat) radius
strokeWidth: (CGFloat) strokeWidth {
// Fit the stroked path inside the rectangle.
rect.size.height -= strokeWidth;
rect.size.width -= strokeWidth;
rect.origin.x += strokeWidth / 2.0;
rect.origin.y += strokeWidth / 2.0;
CGMutablePathRef path = CGPathCreateMutable();
// The inner rect size gives us X/Y/W/H values for the parts of the rect
// that aren't on the curve.
CGRect innerRect = CGRectInset(rect, radius, radius);
CGFloat insideRight = innerRect.origin.x + innerRect.size.width;
CGFloat outsideRight = rect.origin.x + rect.size.width;
CGFloat insideBottom = innerRect.origin.y + innerRect.size.height;
CGFloat outsideBottom = rect.origin.y + rect.size.height;
CGFloat insideTop = innerRect.origin.y;
CGFloat outsideTop = rect.origin.y;
CGFloat outsideLeft = rect.origin.x;
CGPathMoveToPoint (path, NULL, innerRect.origin.x, outsideTop);
CGPathAddLineToPoint (path, NULL, insideRight, outsideTop);
CGPathAddArcToPoint (path, NULL, outsideRight, outsideTop,
outsideRight, insideTop, radius);
CGPathAddLineToPoint (path, NULL, outsideRight, insideBottom);
CGPathAddArcToPoint (path, NULL, outsideRight, outsideBottom,
insideRight, outsideBottom, radius);
CGPathAddLineToPoint (path, NULL, innerRect.origin.x, outsideBottom);
CGPathAddArcToPoint (path, NULL, outsideLeft, outsideBottom,
outsideLeft, insideBottom, radius);
CGPathAddLineToPoint (path, NULL, outsideLeft, insideTop);
CGPathAddArcToPoint (path, NULL, outsideLeft, outsideTop,
innerRect.origin.x, outsideTop, radius);
CGPathCloseSubpath (path);
return path;
} // newPathForRoundRect
If you're not in CGLand (or not needing the same code to work on the desktop and device), there's also UIBezierPath's -bezierPathWithRoundedRect:cornerRadius: (for all corners), and -bezierPathWithRoundedRect:byRoundingCorners:cornerRadii: (for some arbitrary subset of corners), and NSBezierPath's -bezierPathWithRoundedRect:xRadius:yRadius:. Muchos Thankos to Paul Collins for the reminder.
- (void) makeLayerInContext: (CGContextRef) enclosingContext
{
layer = CGLayerCreateWithContext (enclosingContext, CGSizeMake(100, 100),
NULL); // options - unused in Tiger
CGContextRef context;
context = CGLayerGetContext (layer);
// .. and do your drawing
} // makeLayer
And then to draw the layer at a particular point (like replicating it a bunch of different points as done here):
for (i = 0; i < pointCount; i++) {
CGContextDrawLayerAtPoint (context, locations[i], layer);
}
-init):
_timeIndicatorImage = [[[UIImage imageNamed: @"ride-profile-time-indicator"]
stretchableImageWithLeftCapWidth: 0.0
topCapHeight: 1.0] retain];
Then draw it. In this case it's a vertical indicator.
- (void) drawTimeIndicatorInRect: (CGRect) rect {
CGFloat timeFraction = _time / _totalDuration;
CGFloat x = [self horizontalPositionForTimeFraction: timeFraction];
CGRect indicatorRect = CGRectMake (x, 0, 3.0, rect.size.height);
[_timeIndicatorImage drawInRect: indicatorRect];
} // drawTimeIndicatorInRect
Sizes are square. Assuming a universal app 512 iTunesArtwork iTunes. No .png extension 57 Icon.png AppStore, iPhone/Touch home screen. Required 114 Icon@2x.png iPhone4 72 Icon-72.png App Store, iPad. Required. 29 Icon-Small.png Settings on iPad and iPhone, Spotlight iPhone 50 Icon-Small-50.png Spotlight iPad 58 Icon-Small@2x.png Settings and Spotlight iPhone4Then in your info.plist, make an "Icon files" entry (an array), and list all the Icon*.png files.
- (NSBezierPath *) makePathFromString: (NSString *) string
forFont: (NSFont *) font
{
NSTextView *textview;
textview = [[NSTextView alloc] init];
[textview setString: string];
[textview setFont: font];
NSLayoutManager *layoutManager;
layoutManager = [textview layoutManager];
NSRange range;
range = [layoutManager glyphRangeForCharacterRange:
NSMakeRange (0, [string length])
actualCharacterRange: NULL];
NSGlyph *glyphs;
glyphs = (NSGlyph *) malloc (sizeof(NSGlyph)
* (range.length * 2));
[layoutManager getGlyphs: glyphs range: range];
NSBezierPath *path;
path = [NSBezierPath bezierPath];
[path moveToPoint: NSMakePoint (20.0, 20.0)];
[path appendBezierPathWithGlyphs: glyphs
count: range.length inFont: font];
free (glyphs);
[textview release];
return (path);
} // makePathFromString