CGContext donesn't work correctly in thread - ios

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.

Related

[UIImage circleBrushTexture]: unrecognized selector sent to class 0x10a426a50

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)

iOS drawing over UIImage

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);
}

UIView With Multiple DrawRect Methods

I'm currently trying to create a Grid/Cinematic Overlay with a UIView.
I created a few methods; drawVerticalLine and Horizontal Lines and stuff...
I have a UIViewController that inits the UIGridView. I can put all my methods in the draw rect and draw them all at once.
However, I want to be able to call them individually from the ViewController. When I try to doenter code here that. I get an ": CGContextDrawPath: invalid context 0x0" Code Below.
From my ViewController I want to be able to call "drawGrid :withColor :andLines;" Or something
-
(void)drawRect:(CGRect)rect
{
if (self.verticalLinesON == YES) {
[self drawVerticalLinesForGrid:100 :[UIColor redColor] :[UIColor greenColor]];
}
[self show16NineOverLay:[UIColor orangeColor]];
[self show4ThreeOverLay:[UIColor orangeColor]];
[self drawHorizontalLinesForGrid:100 :[UIColor blueColor] :[UIColor yellowColor]];
}
-(void)drawVerticalLinesForGrid:(float)sectionsVertically :(UIColor *)lineColor1 :(UIColor *)lineColor2
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2);
int i = 0;
float amountOfSectionsVertically = sectionsVertically;
for (i = 1; i < amountOfSectionsVertically; i++)
{//Horizontal Lines first.
float xCoord = self.frame.size.width * ((i+0.0f)/amountOfSectionsVertically);
CGContextMoveToPoint(context, xCoord, 0);
CGContextAddLineToPoint(context, xCoord, self.frame.size.height);
if (i%2 == 1)
{//if Odd
CGContextSetStrokeColorWithColor(context, lineColor1.CGColor);
}
else if(i%2 == 0)
{//if Even
CGContextSetStrokeColorWithColor(context, lineColor2.CGColor);
}
CGContextStrokePath(context);
}
}
-(void)drawHorizontalLinesForGrid :(float)sectionsHorizontally :(UIColor *)lineColor1 :(UIColor *)lineColor2
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2);
int i = 0;
float amountOfSectionsHorizontally = sectionsHorizontally;
for (i = 1; i < amountOfSectionsHorizontally; i++)
{//Vertical Lines first.
float yCoord = self.frame.size.height * ((i+0.0f)/amountOfSectionsHorizontally);
CGContextMoveToPoint(context, 0, yCoord);
CGContextAddLineToPoint(context, self.frame.size.width, yCoord);
if (i%2 == 1)
{//if Odd
CGContextSetStrokeColorWithColor(context, lineColor1.CGColor);
}
else if(i%2 == 0)
{//if Even
CGContextSetStrokeColorWithColor(context, lineColor2.CGColor);
}
CGContextStrokePath(context);
}
}
-(void)show16NineOverLay:(UIColor *)lineColor
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 10);
//x/y
float yCoord = (0.5) * (self.frame.size.height * (1.778)
What you should do is set some state on your grid view class that specifies what should be drawn (just vertical, just horizontal, both, etc) and then call setNeedsDisplay on the view.
This will trigger a call to drawRect:. Then your drawRect: method should look at its current state and call just the appropriate methods to draw the desired parts.
You must never directly call drawRect: on a view.

iOS CGContext drawing a line graph in a for loop cocos2d

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]];

CATiledLayer; high resolution bitmap scaling

I'am trying to render waveform (high resolution bitmap) into UIView with different frames. I want to scale it and decided to use CATiledLayer for better performance. But in a result I got what I didn't expected.
source bitmap (5000 x 80 pixels):
result ( ? x 80 pixels):
Tracks are not the same; and when I continue scaling/scrolling - they differ.
Code itself:
+ (Class)layerClass {
return [CATiledLayer class];
}
- (id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.loading = YES;
self.recording = NO;
CATiledLayer *tiledLayer = (CATiledLayer *)[self layer];
tiledLayer.levelsOfDetail = 7;
tiledLayer.levelsOfDetailBias = 3;
tiledLayer.tileSize = CGSizeMake(128.0, 128.0);
[self addSubview:activityView];+ (Class)layerClass {
return [CATiledLayer class];
}
-(void)drawRect:(CGRect)rect {
UIImage* image = [UIImage imageWithContentsOfFile:self.imagePath];
CGFloat scale = parent.frame.size.width/image.size.width;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, rect);
CGContextSaveGState(context);
CGContextScaleCTM(context, scale, 1.0);
CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
CGContextRestoreGState(context);
}
-What I am doing wrong here?
-Could CATiledLayer be scaled only by X-Axis?
Thanks in advance!

Resources