0
votes

I'm dealing with the undo feature of a basic iOS painting app. There's no problem with normal undoing when I only paint the strokes in one colour, but when I use different colours always render all the strokes with the last stroke color.

To accomplish this, I'm storing in the undo array my custom objects that containts the point array and the stroke colour, and at last painting it again after remove the last object and clear the screen.

Here is the code

- (void)undo { 
static GLfloat*     vertexBuffer = NULL;
static NSUInteger   vertexMax = 64;
NSUInteger          vertexCount = 0, count, i; 
CGFloat oldRed = self.red;
CGFloat oldGreen = self.green;
CGFloat oldBlue = self.blue;

if (self.undoBuffer.count > 0)
{
    TEUndoStroke *undoStroke = [self.undoBuffer lastObject];
    if (!self.redoBuffer)
        self.redoBuffer = [NSMutableArray array];
    [self.redoBuffer addObject:undoStroke];

    [self.undoBuffer removeLastObject];

    [EAGLContext setCurrentContext:context];
    glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
    glClearColor(1.0, 1.0, 1.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    for (TEUndoStroke *_stroke in self.undoBuffer)
    {
        //Aplicamos el color que tenia
        NSLog(@"R: %f G: %f B: %f", _stroke.red, _stroke.green, _stroke.blue);
        [self setBrushColorWithRed:_stroke.red green:_stroke.green blue:_stroke.blue];

        for (int k = 1; k < _stroke.arrayStrokes.count; k++)
        {
            CGPoint start = [[_stroke.arrayStrokes objectAtIndex:k-1] CGPointValue];
            CGPoint end = [[_stroke.arrayStrokes objectAtIndex:k] CGPointValue];

            glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);

            // Convert locations from Points to Pixels
            CGFloat scale = self.contentScaleFactor;
            start.x *= scale;
            start.y *= scale;
            end.x *= scale;
            end.y *= scale;

            // Allocate vertex array buffer
            if(vertexBuffer == NULL)
                vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat));

            // Add points to the buffer so there are drawing points every X pixels
            count = MAX(ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / kBrushPixelStep), 1);
            for(i = 0; i < count; ++i) {
                if(vertexCount == vertexMax) {
                    vertexMax = 2 * vertexMax;
                    vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat));
                }

                vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((GLfloat)i / (GLfloat)count);
                vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((GLfloat)i / (GLfloat)count);
                vertexCount += 1;
            }

        }

    }

    //Render everithing at last to avoid blinking.

    glEnableVertexAttribArray(ATTRIB_VERTEX);
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, 0);

    // Draw
    glUseProgram(program[PROGRAM_POINT].id);
    glDrawArrays(GL_POINTS, 0, vertexCount);

    // Load data to the Vertex Buffer Object
    glBindBuffer(GL_ARRAY_BUFFER, vboId);
    glBufferData(GL_ARRAY_BUFFER, vertexCount*2*sizeof(GLfloat), vertexBuffer, GL_DYNAMIC_DRAW);

    // Display the buffer
    glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER];

}

[self setBrushColorWithRed:oldRed green:oldGreen blue:oldBlue];
}

This is the setBrushColorMethod

- (void)setBrushColorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue
{
    self.red = red;
    self.green = green;
    self.blue = blue;

    // Update the brush color
    brushColor[0] = red * kBrushOpacity;
    brushColor[1] = green * kBrushOpacity;
    brushColor[2] = blue * kBrushOpacity;
    brushColor[3] = kBrushOpacity;

    if (initialized) {
        glUseProgram(program[PROGRAM_POINT].id);
        glUniform4fv(program[PROGRAM_POINT].uniform[UNIFORM_VERTEX_COLOR], 1, brushColor);
    }
}

For example, if I draw three letters:

R in red color G in green color B in blue color

I press the undo button, letter "B" dissapears but R and G are both colored in green.

1
Your question is confusing. Do you wish to re-paint everything in one colour? Or is the fact that everything is the same colour your problem? Please clarify your question and ask something in specific. - insys
The problem is that everything is re-painted with the same color, the color of the last stroke painted. - Rotten

1 Answers

0
votes

At last I found the problem. It seems that variable declarations should be inside the draw line point by point bucle.

That's the code.

- (void)undo
{
    if (self.undoBuffer.count > 0)
    {
    TEUndoStroke *undoStroke = [self.undoBuffer lastObject];
    if (!self.redoBuffer)
        self.redoBuffer = [NSMutableArray array];
    [self.redoBuffer addObject:undoStroke];

    [self.undoBuffer removeLastObject];

    [EAGLContext setCurrentContext:context];
    glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
    glClearColor(1.0, 1.0, 1.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);

    for (TEUndoStroke *_stroke in self.undoBuffer)
    {
        for (int k = 1; k < _stroke.arrayStrokes.count; k++)
        {
            CGPoint start = [[_stroke.arrayStrokes objectAtIndex:k-1] CGPointValue];
            CGPoint end = [[_stroke.arrayStrokes objectAtIndex:k] CGPointValue];

            static GLfloat*     vertexBuffer = NULL;
            static NSUInteger   vertexMax = 64;
            NSUInteger          vertexCount = 0,
            count,
            i;

            [EAGLContext setCurrentContext:context];
            glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);

            // Convert locations from Points to Pixels
            CGFloat scale = self.contentScaleFactor;
            start.x *= scale;
            start.y *= scale;
            end.x *= scale;
            end.y *= scale;

            // Allocate vertex array buffer
            if(vertexBuffer == NULL)
                vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat));

            // Add points to the buffer so there are drawing points every X pixels
            count = MAX(ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / kBrushPixelStep), 1);
            for(i = 0; i < count; ++i) {
                if(vertexCount == vertexMax) {
                    vertexMax = 2 * vertexMax;
                    vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat));
                }

                vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((GLfloat)i / (GLfloat)count);
                vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((GLfloat)i / (GLfloat)count);
                vertexCount += 1;
            }

            CGFloat _strokeColor[4];

            _strokeColor[0] = _stroke.red * kBrushOpacity;
            _strokeColor[1] = _stroke.green * kBrushOpacity;
            _strokeColor[2] = _stroke.blue * kBrushOpacity;
            _strokeColor[3] = kBrushOpacity;

            if (initialized) {
                glUseProgram(program[PROGRAM_POINT].id);
                glUniform4fv(program[PROGRAM_POINT].uniform[UNIFORM_VERTEX_COLOR], 1, _strokeColor);
            }

            // Load data to the Vertex Buffer Object
            glBindBuffer(GL_ARRAY_BUFFER, vboId);
            glBufferData(GL_ARRAY_BUFFER, vertexCount*2*sizeof(GLfloat), vertexBuffer, GL_DYNAMIC_DRAW);

            glEnableVertexAttribArray(ATTRIB_VERTEX);
            glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, 0);

            // Draw
            glUseProgram(program[PROGRAM_POINT].id);
            glDrawArrays(GL_POINTS, 0, vertexCount);

        }

    }

    // Display the buffer
    glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER];

}

}