If CCRenderTexture is not full window size, glDrawArrays output is smaller and at a strange angle. In my test code, the diagonal line should run from corner to corner at a 45 degree angle. How can I draw this smooth line correctly? I'm new to cocos2d and any help is much appreciated.
//compute vertex points for smooth line triangle strip
CGPoint start = CGPointMake(0., 0.);
CGPoint end = CGPointMake(200., 200.);
float lineWidth = 10.0;
float deltaX = end.x - start.x;
float deltaY = end.y - start.y;
float length = sqrtf(deltaX*deltaX+deltaY*deltaY);
if (length < 0.25) return; //line too small to show on display
float offsetX = -lineWidth*deltaY/length;
float offsetY = lineWidth*deltaX/length;
GLfloat lineVertices[12]; //6 vertices x,y values
lineVertices[0] = start.x + offsetX;
lineVertices[1] = start.y + offsetY;
lineVertices[2] = end.x + offsetX;
lineVertices[3] = end.y + offsetY;
lineVertices[4] = start.x;
lineVertices[5] = start.y;
lineVertices[6] = end.x;
lineVertices[7] = end.y;
lineVertices[8] = start.x - offsetX;
lineVertices[9] = start.y - offsetY;
lineVertices[10] = end.x - offsetX;
lineVertices[11] = end.y - offsetY;
ccColor4F colorVertices[6];
ccColor4F color1 = {1., 0., 0., 0.};
ccColor4F color2 = {1., 0., 0., 1.};
colorVertices[0] = color1;
colorVertices[1] = color1;
colorVertices[2] = color2;
colorVertices[3] = color2;
colorVertices[4] = color1;
colorVertices[5] = color1;
CCRenderTexture *rtx = [CCRenderTexture renderTextureWithWidth:200 height:200];
[rtx beginWithClear:1. g:1. b:1. a:1.];
[shaderProgram_ use];
ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position | kCCVertexAttribFlag_Color);
glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, lineVertices);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_FLOAT, GL_FALSE, 0, colorVertices);
glViewport(0,0, screenWidth, screenHeight); //dimensions of main screen
glDrawArrays(GL_TRIANGLE_STRIP, 0, 6);
[rtx end];
[rtx saveToFile:#"lineDrawTest" format:kCCImageFormatPNG];
I added a glViewport call to set the view to the original full screen size. glDrawArrays now draws the smooth line with the right size and angle.
Related
I have been trying to draw single glyph with core text, but the x position of letter is little bit different. The red rectangle show the correct position.
CGContextRef main = [[NSGraphicsContext currentContext] graphicsPort];
CGContextSetAllowsAntialiasing(main, false);
CGContextSetFont(main, font);
CGContextSetFontSize(main, 200);
CGContextSetTextPosition(main, 0, 0);
glyphs[0] = CGFontGetGlyphWithGlyphName(font, CFSTR("E"));
points[0] = CGPointMake(100, 100);
CGContextSetRGBFillColor(main, 0, 1, 0, 1);
CGContextShowGlyphsAtPositions(main, glyphs, points, 1);
CGRect *r = malloc(sizeof(CGRect)*1);
CGFontGetGlyphBBoxes(font, glyphs, 1, r);
float t = roundf(r[0].size.width/CGFontGetUnitsPerEm(font)*200);
float t2 = roundf(r[0].size.height/CGFontGetUnitsPerEm(font)*200);
CGRect r2 = CGRectMake(points[0].x, points[0].y-1, t, t2+2);
CGContextSetRGBStrokeColor(main, 1, 0, 0, 1);
CGContextStrokeRect(main, r2);
Here is screenshot:
You're assuming the bounding box's origin is at zero. It isn't. You need to add in its offset. Something like (following your patterns):
float cornerX = roundf(r[0].origin.x/CGFontGetUnitsPerEm(font)*200);
float cornerY = roundf(r[0].origin.y/CGFontGetUnitsPerEm(font)*200);
CGRect r2 = CGRectMake(points[0].x+cornerX, points[0].y-1+cornerY, t, t2+2);
I Am trying to implement picking in a NSOpenGLView , but not works, this is the code.
I render only the objects that I need pick with no lights and I render the scene as same in normal render.
- (void)drawSeleccion
{
NSSize size = [self bounds].size;
GLuint selectBuf[16 * 4] = {0};
GLint hits;
glClearColor (0.0, 0.0, 0.0, 0.0);
glColor4f(1.0, 1.0, 1.0, 1.0);
glSelectBuffer (16 * 4, selectBuf);
glRenderMode(GL_SELECT);
/// *** Start ***
glInitNames();
glPushName(0);
// view port.
glViewport(0, 0, size.width, size.height);
// projection matrix.
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float dist = 534;
aspectRatio = size.width / size.height;
nearDist = MAX(10, dist - 360.0);
farDist = dist + 360.0;
GLKMatrix4 m4 = GLKMatrix4MakePerspective(zoom, aspectRatio, nearDist, farDist);
glMultMatrixf(m4.m);
// Model view.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// I look at.
GLKMatrix4 glm = GLKMatrix4MakeLookAt(0, dist, 0, 0, 0, 0, 0, 0, 1);
glMultMatrixf(glm.m);
// rotate viewPort.
glRotated(rotate_x, 1, 0, 0);
glRotated(rotate_z, 0, 0, 1);
glTranslated(translate_x - frente * 0.5, fondo * -0.5, translate_z - alto * 0.5);
/// render model....
glPushMatrix();
for (int volNo = 0; volNo < [self.modeloOptimo.arr_volumenes count]; volNo++) {
VolumenOptimo *volOp = self.modeloOptimo.arr_volumenes[volNo];
glLoadName(volNo);
volOp->verProblemas = false;
[volOp drawVolumen];
}
glPopName();
glPopMatrix();
// Flush view.
glFlush();
hits = glRenderMode(GL_RENDER);
processHits (hits, selectBuf);
} // Fin de drawRect.
Always hits = 0, and selectBuf is empty.
Any idea. thanks
I am creating a program that allows me to plot points in 3 space, connects them using a Catmull-Rom Spline, and then draws a cylinder around the Spline. I am using GL_TRIANGLES_STRIP to connect circles of points drawn around the Spline at short intervals, to hopefully connect them all together into a Cylinder around the Spline.
I have managed to draw complete circles of points at these intervals, using GL_POINTS, and orientate them correctly to the line with regards to a Frenet Frame. Unfortunately, to use GL_TRIANGLE_STRIP, I believe I need to plot the points one at a time between a set of two circles of points.
The problem I am having, is that the glMultMatrix doesn't seem to work when inside a glBegin. The code below will draw a circle of points, but at the origin, and the glMultMatrix, which I use to translate and orientate the circle of points, doesn't seem to apply when inside the glbegin. Is there a solution to this?
//The matrixes that are applied to the circle of points
GLfloat M1[16]={
N1.x(),N1.y(),N1.z(),0,
B1.x(),B1.y(),B1.z(),0,
T1.x(),T1.y(),T1.z(),0,
fromPoint->x,fromPoint->y,fromPoint->z,1
};
GLfloat M2[16]={
N2.x(),N2.y(),N2.z(),0,
B2.x(),B2.y(),B2.z(),0,
T2.x(),T2.y(),T2.z(),0,
toPoint->x,toPoint->y,toPoint->z,1
};
glBegin(GL_TRIANGLE_STRIP);
GLfloat x, y;
GLfloat radius = 0.4f;
GLint pointCount = 180;
for (GLfloat theta = 0; theta < 2*M_PI; theta += (2*M_PI)/pointCount) {
x = radius * cos(theta);
y = radius * sin(theta);
// Now push a matrix, multiply it, draw a point and pop the matrix
glPushMatrix();
glMultMatrixf(& M1[0]);
// Draw the point here
glVertex3f(x, y, 0);
glPopMatrix();
// Do the same again for the second section
glPushMatrix();
glMultMatrixf(& M2[0]);
glVertex3f(x, y, 0);
glPopMatrix();
}
glEnd();
The problem I am having, is that the glMultMatrix doesn't seem to work when inside a glBegin
Unsurprising:
Only a subset of GL commands can be used between glBegin and glEnd.
The commands are
glVertex,
glColor,
glSecondaryColor,
glIndex,
glNormal,
glFogCoord,
glTexCoord,
glMultiTexCoord,
glVertexAttrib,
glEvalCoord,
glEvalPoint,
glArrayElement,
glMaterial, and
glEdgeFlag.
Also,
it is acceptable to use
glCallList or
glCallLists to execute
display lists that include only the preceding commands.
If any other GL command is executed between glBegin and glEnd,
the error flag is set and the command is ignored.
glMultMatrix() before glBegin():
//The matrixes that are applied to the circle of points
GLfloat M1[16]=
{
N1.x(),N1.y(),N1.z(),0,
B1.x(),B1.y(),B1.z(),0,
T1.x(),T1.y(),T1.z(),0,
fromPoint->x,fromPoint->y,fromPoint->z,1
};
GLfloat M2[16]=
{
N2.x(),N2.y(),N2.z(),0,
B2.x(),B2.y(),B2.z(),0,
T2.x(),T2.y(),T2.z(),0,
toPoint->x,toPoint->y,toPoint->z,1
};
GLfloat x, y;
GLfloat radius = 0.4f;
GLint pointCount = 180;
for (GLfloat theta = 0; theta < 2*M_PI; theta += (2*M_PI)/pointCount)
{
x = radius * cos(theta);
y = radius * sin(theta);
// Now push a matrix, multiply it, draw a point and pop the matrix
glPushMatrix();
glMultMatrixf(& M1[0]);
// Draw the point here
glBegin(GL_POINTS);
glVertex3f(x, y, 0);
glEnd();
glPopMatrix();
// Do the same again for the second section
glPushMatrix();
glMultMatrixf(& M2[0]);
glBegin(GL_POINTS);
glVertex3f(x, y, 0);
glEnd();
glPopMatrix();
}
Or apply the transforms client-side and hand OpenGL a big block 'o vertices to render in one go.
EDIT: Or pull those matrix multiplies outside the loop entirely:
GLfloat x, y;
GLfloat radius = 0.4f;
GLint pointCount = 180;
glPushMatrix();
glMultMatrixf(& M1[0]);
glBegin(GL_POINTS);
for (GLfloat theta = 0; theta < 2*M_PI; theta += (2*M_PI)/pointCount)
{
x = radius * cos(theta);
y = radius * sin(theta);
// Draw the point here
glVertex3f(x, y, 0);
}
glEnd();
glPopMatrix();
glPushMatrix();
glMultMatrixf(& M2[0]);
glBegin(GL_POINTS);
for (GLfloat theta = 0; theta < 2*M_PI; theta += (2*M_PI)/pointCount)
{
x = radius * cos(theta);
y = radius * sin(theta);
// Draw the point here
glVertex3f(x, y, 0);
}
glEnd();
glPopMatrix();
you can also use a vertex array to hold all info and then push the data to openGL:
GLFloat[] points = new GLFloat[3*pointCount];
for (GLfloat theta = 0; theta < 2*M_PI; theta += (2*M_PI)/pointCount)
{
points[0+off] = radius * cos(theta);
points[1+off] = radius * sin(theta);
points[2+off] = 0;
off+=3;
}
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, points);
glPushMatrix();
glMultMatrixf(& M1[0]);
glDrawArrays(GL_POINTS, 0, pointCount);
glPopMatrix();
glPushMatrix();
glMultMatrixf(& M2[0]);
glDrawArrays(GL_POINTS, 0, pointCount);
glPopMatrix();
glDisableClientState(GL_VERTEX_ARRAY);
delete[] points;
I am working on an application that allows the user to select an image (camera or gallery), then draw on that image with their finger. The area that they draw becomes the transparent portion of a mask. A second image is then drawn below the first image.
I've been working on improving performance, especially on the iPad 3 and I seem to have hit a wall. I am new to iOS and Objective-C.
So my question is: What can I do to improve the drawing performance of my application?
I used this tutorial as a starting point for my code.
First I draw to a cache context:
- (void) drawToCache {
CGRect dirtyPoint1;
CGRect dirtyPoint2;
UIColor *color;
if (point1.x > -1){
hasDrawn = YES;
maskContext = CGLayerGetContext(maskLayer);
blackBackgroundContext = CGLayerGetContext(blackBackgroundLayer);
if (!doUndo){
color = [UIColor whiteColor];
CGContextSetBlendMode(maskContext, kCGBlendModeColor);
}
else{
color = [UIColor clearColor];
CGContextSetBlendMode(maskContext, kCGBlendModeClear);
}
CGContextSetShouldAntialias(maskContext, YES);
CGContextSetRGBFillColor(blackBackgroundContext, 0.0, 0.0, 0.0, 1.0);
CGContextFillRect(blackBackgroundContext, self.bounds);
CGContextSetStrokeColorWithColor(maskContext, [color CGColor]);
CGContextSetLineCap(maskContext, kCGLineCapRound);
CGContextSetLineWidth(maskContext, brushSize);
double x0 = (point0.x > -1) ? point0.x : point1.x; //after 4 touches we should have a back anchor point, if not, use the current anchor point
double y0 = (point0.y > -1) ? point0.y : point1.y; //after 4 touches we should have a back anchor point, if not, use the current anchor point
double x1 = point1.x;
double y1 = point1.y;
double x2 = point2.x;
double y2 = point2.y;
double x3 = point3.x;
double y3 = point3.y;
double xc1 = (x0 + x1) / 2.0;
double yc1 = (y0 + y1) / 2.0;
double xc2 = (x1 + x2) / 2.0;
double yc2 = (y1 + y2) / 2.0;
double xc3 = (x2 + x3) / 2.0;
double yc3 = (y2 + y3) / 2.0;
double len1 = sqrt((x1-x0) * (x1-x0) + (y1-y0) * (y1-y0));
double len2 = sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1));
double len3 = sqrt((x3-x2) * (x3-x2) + (y3-y2) * (y3-y2));
double k1 = len1 / (len1 + len2);
double k2 = len2 / (len2 + len3);
double xm1 = xc1 + (xc2 - xc1) * k1;
double ym1 = yc1 + (yc2 - yc1) * k1;
double xm2 = xc2 + (xc3 - xc2) * k2;
double ym2 = yc2 + (yc3 - yc2) * k2;
double smooth_value = 0.5;
float ctrl1_x = xm1 + (xc2 - xm1) * smooth_value + x1 - xm1;
float ctrl1_y = ym1 + (yc2 - ym1) * smooth_value + y1 - ym1;
float ctrl2_x = xm2 + (xc2 - xm2) * smooth_value + x2 - xm2;
float ctrl2_y = ym2 + (yc2 - ym2) * smooth_value + y2 - ym2;
CGContextMoveToPoint(maskContext, point1.x, point1.y);
CGContextAddCurveToPoint(maskContext, ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y, point2.x, point2.y);
CGContextStrokePath(maskContext);
dirtyPoint1 = CGRectMake(point1.x-(brushSize/2), point1.y-(brushSize/2), brushSize, brushSize);
dirtyPoint2 = CGRectMake(point2.x-(brushSize/2), point2.y-(brushSize/2), brushSize, brushSize);
[self setNeedsDisplayInRect:CGRectUnion(dirtyPoint1, dirtyPoint2)];
}
}
And my drawRect:
- (void)drawRect:(CGRect)rect {
CGRect imageRect = CGRectMake(0, 0, 1024, 668);
CGSize size = CGSizeMake(1024, 668);
//Draw the user touches to the context, this creates a black and white image to convert into a mask
UIGraphicsBeginImageContext(size);
CGContextSetShouldAntialias(UIGraphicsGetCurrentContext(), YES);
CGContextDrawLayerInRect(UIGraphicsGetCurrentContext(), imageRect, blackBackgroundLayer);
CGContextDrawLayerInRect(UIGraphicsGetCurrentContext(), imageRect, maskLayer);
CGImageRef maskRef = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());
UIGraphicsEndImageContext();
//Make the mask
CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef), CGImageGetHeight(maskRef), CGImageGetBitsPerComponent(maskRef), CGImageGetBitsPerPixel(maskRef), CGImageGetBytesPerRow(maskRef), CGImageGetDataProvider(maskRef), NULL, false);
//Mask the user image
CGImageRef masked = CGImageCreateWithMask([self.original CGImage], mask);
UIGraphicsBeginImageContext(size);
CGContextDrawLayerInRect(UIGraphicsGetCurrentContext(), imageRect, backgroundTextureLayer);
CGImageRef background = CGBitmapContextCreateImage(UIGraphicsGetCurrentContext());
UIGraphicsEndImageContext();
//Flip the context so everything is right side up
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, 668);
CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
//Draw the background on the context
CGContextDrawImage(UIGraphicsGetCurrentContext(), imageRect, background);
//Draws the mask on the context
CGContextDrawImage(UIGraphicsGetCurrentContext(), imageRect, masked);
//Release everything to prevent memory leaks
CGImageRelease(background);
CGImageRelease(maskRef);
CGImageRelease(mask);
CGImageRelease(masked);
}
I spent yesterday researching these questions:
How can I improve CGContextFillRect and CGContextDrawImage performance
CGContextDrawImage very slow on iPhone 4
Drawing in CATiledLayer with CoreGraphics CGContextDrawImage
iPhone: How do I add layers to UIView's root layer to display an image with the content property?
iPhone CGContextDrawImage and UIImageJPEGRepresentation drastically slowing down application
Any help is appreciated!
Thanks,
Kevin
I can give you some tips but you're going to need to experiment to see where the problems are (using Instruments):
do not assume you need to draw the whole view - when you need to dirty some area, use setNeedsDisplayInRect. Then in the drawRect only update the dirty area.
the above means your maskRef is much smaller and easier to construct
instead of '[self.original CGImage]', create a smaller subregion image using CGImageCreateWithImageInRect(), which lets you select a subsection of an image, then use that smaller image in conjunction with the mask.
Dipping my feet into some more Core Graphics drawing, I'm attempting to recreate a wicked looking metallic knob, and I've landed on what is probably a show-stopping issue.
There doesn't seem to be any way to draw angle gradients in Core Graphics. I see there's CGContextDrawRadialGradient() and CGContextDrawLinearGradient(), but there's nothing that I see that would allow me to draw an angle gradient. Does anyone know of a workaround, or a bit of framework hidden away somewhere to accomplish this without pre-rendering the knob into an image file?
AngleGradientKnob http://dl.dropbox.com/u/3009808/AngleGradient.png.
This is kind of thrown together, but it's the approach I'd probably take. This is creating an angle gradient by drawing it directly into a bitmap using some simple trig, then clipping it to a circle. I create a grid of memory using a grayscale colorspace, calculate the angle from a given point to the center, and then color that based on a periodic function, running between 0 to 255. You could of course expand this to do RGBA color as well.
Of course you'd cache this and play with the math to get the colors you want. This currently runs all the way from black to white, which doesn't look as good as you'd like.
- (void)drawRect:(CGRect)rect {
CGImageAlphaInfo alphaInfo = kCGImageAlphaNone;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
size_t components = CGColorSpaceGetNumberOfComponents( colorSpace );
size_t width = 100;
size_t height = 100;
size_t bitsPerComponent = 8;
size_t bytesPerComponent = bitsPerComponent / 8;
size_t bytesPerRow = width * bytesPerComponent * components;
size_t dataLength = bytesPerRow * height;
uint8_t data[dataLength];
CGContextRef imageCtx = CGBitmapContextCreate( &data, width, height, bitsPerComponent,
bytesPerRow, colorSpace, alphaInfo );
NSUInteger offset = 0;
for (NSUInteger y = 0; y < height; ++y) {
for (NSUInteger x = 0; x < bytesPerRow; x += components) {
CGFloat opposite = y - height/2.;
CGFloat adjacent = x - width/2.;
if (adjacent == 0) adjacent = 0.001;
CGFloat angle = atan(opposite/adjacent);
data[offset] = abs((cos(angle * 2) * 255));
offset += components * bytesPerComponent;
}
}
CGImageRef image = CGBitmapContextCreateImage(imageCtx);
CGContextRelease(imageCtx);
CGColorSpaceRelease(colorSpace);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect buttonRect = CGRectMake(100, 100, width, width);
CGContextAddEllipseInRect(ctx, buttonRect);
CGContextClip(ctx);
CGContextDrawImage(ctx, buttonRect, image);
CGImageRelease(image);
}
To expand on what's in the comments to the accepted answer, here's the code for generating an angle gradient using Core Image. This should work in iOS 8 or later.
// generate a dummy image of the required size
UIGraphicsBeginImageContextWithOptions(CGSizeMake(256.0, 256.0), NO, [[UIScreen mainScreen] scale]);
CIImage *dummyImage = [CIImage imageWithCGImage:UIGraphicsGetImageFromCurrentImageContext().CGImage];
// define the kernel algorithm
NSString *kernelString = #"kernel vec4 circularGradientKernel(__color startColor, __color endColor, vec2 center, float radius) { \n"
" vec2 point = destCoord() - center;"
" float rsq = point.x * point.x + point.y * point.y;"
" float theta = mod(atan(point.y, point.x), radians(360.0));"
" return (rsq < radius*radius) ? mix(startColor, endColor, 0.5+0.5*cos(4.0*theta)) : vec4(0.0, 0.0, 0.0, 1.0);"
"}";
// initialize a Core Image context and the filter kernel
CIContext *context = [CIContext contextWithOptions:nil];
CIColorKernel *kernel = [CIColorKernel kernelWithString:kernelString];
// argument array, corresponding to the first line of the kernel string
NSArray *args = #[ [CIColor colorWithRed:0.5 green:0.5 blue:0.5],
[CIColor colorWithRed:1.0 green:1.0 blue:1.0],
[CIVector vectorWithCGPoint:CGPointMake(CGRectGetMidX(dummyImage.extent),CGRectGetMidY(dummyImage.extent))],
[NSNumber numberWithFloat:200.0]];
// apply the kernel to our dummy image, and convert the result to a UIImage
CIImage *ciOutputImage = [kernel applyWithExtent:dummyImage.extent arguments:args];
CGImageRef cgOutput = [context createCGImage:ciOutputImage fromRect:ciOutputImage.extent];
UIImage *gradientImage = [UIImage imageWithCGImage:cgOutput];
CGImageRelease(cgOutput);
This generates the following image: