Hi, I'm developing painting app. I've tried to do it with CoreGraphics/Quartz 2D and drawing curves algorithm is pretty slow. So we've decided to switch to the OpenGL ES. I've never had any OpenGL experience, so I found glPaint example from apple and started play with it.
I've changed erase
method do make white background.
How I stuck with brushes and blending. In the example Apple uses "white on black" texture for the brush (first on the pic below). But it didn't work for me (I played with different blending modes). So I've decided to use different brushes, but I didn't find the proper way.
I found few questions on the stackoverflow, but all of them were unanswered. Here is a picture (from another question, thanks to Kevin Beimers).
So the question is how to implement stroke like "desired" in the picture. And how to blend 2 strokes closer to real life experience (blue over yellow = dark green).
Thanks.
There is current code (bit modified from glPaint) for the brush (from initWithFrame
method:
// Make sure the image exists
if(brushImage) {
// Allocate memory needed for the bitmap context
brushData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
// Use the bitmatp creation function provided by the Core Graphics framework.
brushContext = CGBitmapContextCreate(brushData, width, width, 8, width * 4, CGImageGetColorSpace(brushImage), kCGImageAlphaPremultipliedLast);
// After you create the context, you can draw the image to the context.
CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), brushImage);
// You don't need the context at this point, so you need to release it to avoid memory leaks.
CGContextRelease(brushContext);
// Use OpenGL ES to generate a name for the texture.
glGenTextures(1, &brushTexture);
// Bind the texture name.
glBindTexture(GL_TEXTURE_2D, brushTexture);
// Set the texture parameters to use a minifying filter and a linear filer (weighted average)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Specify a 2D texture image, providing the a pointer to the image data in memory
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData);
// Release the image data; it's no longer needed
free(brushData);
// Make the current material colour track the current color
glEnable( GL_COLOR_MATERIAL );
// Enable use of the texture
glEnable(GL_TEXTURE_2D);
// Set a blending function to use
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// Enable blending
glEnable(GL_BLEND);
// Multiply the texture colour by the material colour.
glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
}
//Set up OpenGL states
glMatrixMode(GL_PROJECTION);
CGRect frame = self.bounds;
glOrthof(0, frame.size.width, 0, frame.size.height, -1, 1);
glViewport(0, 0, frame.size.width, frame.size.height);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_DITHER);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_BLEND);
// Alpha blend each "dab" of paint onto background
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
//glBlendFunc(GL_SRC_COLOR, GL_ONE);
glEnable(GL_POINT_SPRITE_OES);
glTexEnvf(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE);
self.brushScale = 3;
self.brushStep = 3;
self.brushOpacity = (1.0 / 1.5);
glPointSize(width / brushScale);
//Make sure to start with a cleared buffer
needsErase = YES;
[self erase];