I'm using bridging header to do use the JotUI framework, but I gots a problem.
I get the error from my title question and the culprit is within this function:
- (JotSharedBrushTexture*)brushTexture {
JotGLContext* currContext = (JotGLContext*)[JotGLContext currentContext];
if (!currContext) {
#throw [NSException exceptionWithName:#"NilGLContextException" reason:#"Cannot bind texture to nil gl context" userInfo:nil];
}
if (![currContext isKindOfClass:[JotGLContext class]]) {
#throw [NSException exceptionWithName:#"JotGLContextException" reason:#"Current GL Context must be JotGLContext" userInfo:nil];
}
JotSharedBrushTexture* texture = [currContext.contextProperties objectForKey:#"brushTexture"];
if (!texture) {
// PROBLEM
NSLog(#"Creating a texture");
texture = [[JotSharedBrushTexture alloc] initWithImage:[UIImage circleBrushTexture]];
NSLog(#"After creating texture"); // doesn't log from here.
[currContext.contextProperties setObject:texture forKey:#"brushTexture"];
}
return texture;
}
and the function being called:
+ (UIImage*)circleBrushTexture {
NSLog(#"HELLO");
if (!circleBrush) {
UIGraphicsBeginImageContext(CGSizeMake(64, 64));
CGContextRef defBrushTextureContext = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(defBrushTextureContext);
size_t num_locations = 3;
CGFloat locations[3] = {0.0, 0.2, 1.0};
CGFloat components[12] = {1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 1.0,
1.0, 1.0, 1.0, 0.0};
CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB();
CGGradientRef myGradient = CGGradientCreateWithColorComponents(myColorspace, components, locations, num_locations);
CGPoint myCentrePoint = CGPointMake(32, 32);
float myRadius = 30;
CGContextDrawRadialGradient(UIGraphicsGetCurrentContext(), myGradient, myCentrePoint,
0, myCentrePoint, myRadius,
kCGGradientDrawsAfterEndLocation);
CGGradientRelease(myGradient);
CGColorSpaceRelease(myColorspace);
UIGraphicsPopContext();
circleBrush = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return circleBrush;
}
Not sure why this is happening. I tried deleting and reputting the .m file in compile sources, clean project, and compiling the framework but nothing.
Check, if for file with circleBrushTexture method selected target is correct (for *.m file)
Related
It should be very simple to create a black to white radial gradient using Core Graphics, but even after many tries I am not able to figure where I am going wrong.
I want something like this:
This is my code:
CGFloat radius = shapeRect.size.width/2.0;
CGPoint center = CGPointMake(shapeRect.origin.x+shapeRect.size.width/2, shapeRect.origin.y+shapeRect.size.height/2);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(NULL, shapeRect.size.width, shapeRect.size.height, 8, shapeRect.size.width*4, colorSpace, kCGImageAlphaNone);
CGFloat components[] = {0.0, 1.0};
CGFloat locations[] = {0.0, 1.0};
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 2);
CGContextDrawRadialGradient(context, gradient, center, 10, center, radius, 0);
CGImageRef img = CGBitmapContextCreateImage(context);
[maskedView setImage:[UIImage imageWithCGImage:img]];
CGGradientRelease(gradient);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
ShapeRect here has self.view bounds.
I am getting a whole black screen with no gradient as output.
I am using iPhone 7 simulator.
Your suggestions can save my day
Thank you.
Please check this snippet. You can change components and locations values to get what you want and to understand how the drawing works. Refer to CGGradientCreateWithColorComponentsdescription
#import "TestView.h"
#implementation TestView {
CGGradientRef _gradient;
}
-(void)drawRect:(CGRect)rect
{
CGFloat radius = self.frame.size.width / 2.0;
CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGContextRef context = UIGraphicsGetCurrentContext();
CGGradientRef gradient = [self createGradient];
CGContextDrawRadialGradient(context, gradient, center, 10, center, radius, kCGGradientDrawsBeforeStartLocation);
}
-(CGGradientRef)createGradient
{
if (!_gradient) {
CGFloat components[] = { 0.0, 1.0, 0.5, 0.5, 1.0, 0.3 };
CGFloat locations[] = { 0.0, 0.6, 1.0 };
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
_gradient = CGGradientCreateWithColorComponents(colorSpace, components, locations, 3);
CGColorSpaceRelease(colorSpace);
}
return _gradient;
}
- (void)layoutSubviews
{
self.backgroundColor = [UIColor whiteColor];
self.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.layer.borderWidth = 1.;
}
#end
Result:
I need to draw over an image graffiti style, as below. My problem is that I need to have the capability to erase the lines I've drawn without erasing sections of the UIImage. At the moment I'm considering using one image for the background image and another image, with a transparent background, for the graffiti drawing, then combining the two once the drawing is complete. Is there a better way?
- (void)drawRect:(CGRect)rect
{
//Get drawing context
CGContextRef context = UIGraphicsGetCurrentContext();
//Create drawing layer if required
if(drawingLayer == nil)
{
drawingLayer = CGLayerCreateWithContext(context, bounds.size, NULL);
CGContextRef layerContext = CGLayerGetContext(drawingLayer);
CGContextScaleCTM(layerContext, scale, scale);
self.viewRect = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.width);
NSLog(#"%f %f",self.viewRect.size.width,self.viewRect.size.width);
}
//Draw paths from array
int arrayCount = [pathArray count];
for(int i = 0; i < arrayCount; i++)
{
path = [pathArray objectAtIndex:i];
UIBezierPath *bezierPath = path.bezierPath;
CGContextRef layerContext = CGLayerGetContext(drawingLayer);
CGContextAddPath(layerContext, bezierPath.CGPath);
CGContextSetLineWidth(layerContext, path.width);
CGContextSetStrokeColorWithColor(layerContext, path.color.CGColor);
CGContextSetLineCap(layerContext, kCGLineCapRound);
if(activeColor == 4)
{
CGContextSetBlendMode(layerContext, kCGBlendModeClear);
}
else
{
CGContextSetBlendMode(layerContext, kCGBlendModeNormal);
}
CGContextStrokePath(layerContext);
}
if (loadedImage == NO)
{
loadedImage = YES;
CGContextRef layerContext = CGLayerGetContext(drawingLayer);
CGContextSaveGState(layerContext);
UIGraphicsBeginImageContext (self.viewRect.size);
CGContextTranslateCTM(layerContext, 0, self.viewRect.size.height);
CGContextScaleCTM(layerContext, 1.0, -1.0);
CGContextDrawImage(layerContext, self.viewRect, self.image.CGImage);
UIGraphicsEndImageContext();
CGContextRestoreGState(layerContext);
}
[pathArray removeAllObjects];
CGContextDrawLayerInRect(context, self.viewRect, drawingLayer);
}
I need to draw (a bit) complex gradients programmatically. Here is my params.
#define SHADOW_OPACITY 0.35
const size_t num_locations = 4;
const CGFloat locations[num_locations] = { 0, 0.12, 0.42, 1.0 };
const CGFloat components[num_locations * 2] = { 0.0, 1.0 * SHADOW_OPACITY,
0.0, 1.0 * SHADOW_OPACITY,
0.0, 0.73 * SHADOW_OPACITY,
0.0, 0.0 };
Three rings with opacity and a dashed ring inside. All works just fine.
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawGradientInContext:context];
CGContextSetLineWidth(context, CIRCLE_STROKE_WIDTH);
CGContextBeginPath(context);
CGFloat dashLength = DASH_LENGTH(self.circleRadius);
CGFloat dashArray[] = { dashLength, dashLength };
CGContextSetLineDash(context, 0, dashArray, 2);
CGContextAddArc(context, CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds), self.circleRadius, 0, 2 * M_PI, YES);
CGContextClosePath(context);
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
CGContextDrawPath(context, kCGPathStroke);
}
- (void)drawGradientInContext:(CGContextRef)context
{
self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceGray();
CGGradientRef gradient = CGGradientCreateWithColorComponents(colorspace, components, locations, num_locations);
CGPoint gradientCenter = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
CGContextDrawRadialGradient(context,
gradient,
gradientCenter,
self.circleRadius - 1,
gradientCenter,
self.circleRadius + SHADOW_WIDTH,
kCGGradientDrawsAfterEndLocation);
CGColorSpaceRelease(colorspace);
CGGradientRelease(gradient);
}
Well, I need to apply zoom.
- (void)zoom:(UIPinchGestureRecognizer *)sender
{
effectiveScale = beginGestureScale * sender.scale;
self.circleRadius = effectiveScale * BASE_RADIUS;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) {
beginGestureScale = effectiveScale ? effectiveScale : 1.0;
}
return YES;
}
Well, since that things are screwed up: it glitches as the hell!
What could I do there?
I'm trying to make infinite drawing in custom UIView from another thread using Core Graphics. It's impossible to obtain current CGContext so I try to create a new one. But I have no idea how to apply it to the view.
Here is the code I use in my custom view:
CG_INLINE CGContextRef CGContextCreate(CGSize size)
{
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(nil, size.width, size.height, 8, size.width * (CGColorSpaceGetNumberOfComponents(space) + 1), space, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(space);
return ctx;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = CGContextCreate(self.bounds.size);
if(context)
{
[self.layer renderInContext:context];
[self drawSymbolsInContext:context];
CGContextRelease(context);
}
}
- (void)drawSymbolsInContext:(CGContextRef)context
{
for (int x = 0; x<[cellsArray count];x++)
{
for(int y=0; y<[[cellsArray objectAtIndex:x] count]; y++)
{
NSLog(#"lol %d", y);
float white[] = {1.0, 1.0, 1.0, 1.0};
CGContextSetFillColor(context, white);
CGContextFillRect(context, [self bounds]);
CGContextSetTextDrawingMode(context, kCGTextStroke);
CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSelectFont(context, "Times", 12.0, kCGEncodingMacRoman);
CGAffineTransform xform = CGAffineTransformMake(
1.0, 0.0,
0.0, -1.0,
0.0, 0.0);
CGContextSetTextMatrix(context, xform);
CGContextShowTextAtPoint(context, 100.0, 100.0 + 15*y, "test", strlen("test"));
}
}
}
Here is code I use to run thead(in ViewController):
- (void)viewDidLoad
{
[super viewDidLoad];
[NSThread detachNewThreadSelector:#selector(SomeAction:) toTarget:self withObject:self.view];
}
- (void) SomeAction:(id)anObject
{
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
DrawView *drawView = (DrawView *)anObject;
while (1)
{
COLOR col;
col.blue = 100;
col.red = 100;
col.green = 200;
int cellX = 0;
int cellY = [[rowsArray objectAtIndex:0] count];
CELL cel;
cel.x = cellX;
cel.x = cellY;
SymbolAlive *s = [[SymbolAlive alloc] initWithColor:col andFrame:cel];
[[rowsArray objectAtIndex:0] addObject:s];
[drawView drawRect:drawView.bounds];
[NSThread sleepForTimeInterval: 0.1];
}
[NSThread exit];
[autoreleasepool release];
}
Loop runs correctly. But nothing is displayed on the screen.
Any ideas?
I'm guessing you have to do your drawing on the main thread. Instead of
[drawView drawRect:drawView.bounds];
Do this
[self performSelectorOnMainThread:#selector(doDraw:) withObject:nil waitUntilDone:NO];
and then create a draw function like
- (void)doDraw:(id)notused
{
[drawView drawRect:drawView.bounds];
}
That will allow you to do the processing in the background but do the drawing on the main thread.
Also, you should be using the standard graphics context in this case.
One more comment, you can probably also move all your drawing-into-the-context into the background thread, but then call renderInContext on the main thread.
I have a for loop and within the for loop i do the following, this creates a line graph on the background of my chart, however it displays the same image of a line on each background exactly the same even though the data is different.
UIGraphicsBeginImageContext(test.size);
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextFlush(c);
CGContextSetAllowsAntialiasing(c, true);
CGContextTranslateCTM(c, 0, test.size.height);
CGContextScaleCTM(c, 1.0, -1.0);
CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CGFloat components[] = {0.0, 0.0, 1.0, 1.0};
CGColorRef color = CGColorCreate(colorspace, components);
CGContextSetStrokeColorWithColor(c, color);
CGContextMoveToPoint(c, 0, 0);
if(maxPoints > 0)
{
float xSpacing = (float)330.0f/(float)[allScorePointsSubset count];
int currentPoint;
float currentX;
float currentY;
for (int j = 0; j < [allScorePointsSubset count]; j++)
{
NSString *currentPointString = [allScorePointsSubset objectAtIndex:j];
currentPoint = (int)[currentPointString intValue];
if(currentPoint < 0)
currentPoint = 0;
currentX = (j+1)*xSpacing;
currentY = currentPoint / 100.0f * 200.0f;
CGContextAddLineToPoint(c, (int)currentX, (int)currentY);
}
}
CGContextStrokePath(c);
CGColorSpaceRelease(colorspace);
CGColorRelease(color);
UIImage *graphImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CCSprite* answerMenuNormalSprite = [CCSprite spriteWithCGImage:graphImage.CGImage key: [NSString stringWithFormat:#"answerMenuNormalSprite_%f", i]];
[answerMenuNormalSprite setPosition:ccp(176,66)];
[[background objectAtIndex:0] addChild:answerMenuNormalSprite z:0 tag:i];
Has anyone got any ideas what i am doing wrong and does this have to be in the following method
-(void)draw
{
[super draw];
}
Thanks
It turns out that my counter was not incrementing properly and the problem was simply down to not giving the image a unique key in the following line
CCSprite* answerMenuNormalSprite = [CCSprite spriteWithCGImage:graphImage.CGImage key: [NSString stringWithFormat:#"answerMenuNormalSprite_%f", i]];