ofImage.grabScreen not giving expected results when using the programmable renderer (OpenGL 3.3)

Hello, I’m very new to openFrameworks and OpenGL and I have the following 2 issues using oF version 0.10.1 on Windows 10.

1- I draw a simple 1px wide line with ofDrawLine() and use ofImage.grabScreen() followed by ofImage.save() to save the result in a png file. If I use the old renderer (using OpenGL 2.1 or 2.0), then the result is the same as what I see on the screen with anti-aliasing. But if I switch to the programmable renderer (using OpenGL 3.3), then the saved image does not look good and is not anti-aliased.

1-Aalpha%20blending%20OK%20in%20GL_21

2-Missing%20alpha%20blending%20in%20GL_33

2- When using the programmalbe rendrer (OpenGL 3.3), ofBackground(0) clear the screen as expected, but the saved image has a transparent background and we can see artefacts of the previous draw (using the imageSaverExample, but extending the image to grab the whole screen and clear the background to black for example). For this issue, I can solve it by drawing a rectangle with the clear color (black in my case), but I don’t know if this is the right way to do it.

Any help would be greatly appreciated as I have searched the internet for hours but couldn’t find anything helpful.

Thanks

Update on point #2: Note that the issue seems to only happen when using a pure black color no matter which method is used (either ofSetBackgroundColor(0) using the automatic clear or any other methods such as ofClear(0) or ofBackground(0) inside the draw method). Using any other colors seems to work.

Should issues on github be created for both problems?

(I could not save the edit on the original post because there are 2 images so I added this reply instead)

can you post a small example that shows the problem?

For some reason, I cannot upload files (new user) even though I was able to add the pictures in the original post.

The easiest way is to start with the imageSaver example and copy the following code:

main.cpp:

#include "ofMain.h"
#include "ofApp.h"

//========================================================================
int main()
{
    // Old Renderer
	// NOTE(Eric): The issue does not happen using the old renderer
    // ofSetupOpenGL(1280, 720, OF_WINDOW);
    
    // Programmable Renderer if the version is higher than 3.0
    ofGLFWWindowSettings windowSettings;
    windowSettings.setSize(1280, 720);
    //windowSettings.setGLVersion(2, 1);
    windowSettings.setGLVersion(3, 3);
    ofCreateWindow(windowSettings);

    // this kicks off the running of my app
    // can be OF_WINDOW or OF_FULLSCREEN
    // pass in width and height too:
    ofRunApp(new ofApp());
}

ofApp.h:

#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp
{
public:
    void setup();
    void update();
    void draw();

    void keyPressed(int key);

    int 				snapCounter;
    ofImage 			img;
    ofTrueTypeFont		cooper;
    bool 				bSnapshot;
    float 				phase;

    bool fixedDrawing;
};

ofApp.cpp:

#include "ofApp.h"

//
// NOTE(Eric): After multiple experimentations, it seems that clearing to pure black
// is problematic, but using another color works for the image background.  This does
// not solve the issue of the anti-aliasing though (lines are still jagged).  The
// image displayed on the screen is always fine no matter what color is used.
//

//--------------------------------------------------------------
void ofApp::setup()
{
    ofSetWindowTitle("Save image tests | x - f");
    ofSetCircleResolution(48);
    //ofSetBackgroundAuto(false);

    snapCounter = 0;
    bSnapshot = false;
    cooper.load("cooperBlack.ttf", 50);
    phase = 0;
}


//--------------------------------------------------------------
void ofApp::update()
{
    // IMPORTANT(Eric): According to the doc on openFrameworks, this should be in 
    // the update if the colors are changing, since the background is cleared before
    // the draw call.
    // NOTE(Eric): Color used for the automatic clear function (see setup above)
    // NOTE(Eric): When using the default color, the saved image will have the proper
    // background color and no artefarct of past images, although the anti-aliasing
    // issue still remains.
    //ofSetBackgroundColor(1);
    //ofSetBackgroundColor(0, 255);
    //ofSetBackgroundColor(0, 0, 0, 255);
}


//--------------------------------------------------------------
void ofApp::draw()
{
    // NOTE(Eric): Color used for the automatic clear function (see setup above)
    // NOTE(Eric): When using the default color, the saved image will have the proper
    // background color and no artefarct of past images, although the anti-aliasing
    // issue still remains.
    //ofSetBackgroundColor(0);
    //ofSetBackgroundColor(0, 255);
    //ofSetBackgroundColor(0, 0, 0, 255);

    // NOTE(Eric): Not using any clear at all does not produce artefacts in the saved
    // image, but the anti-aliasing is still missing.
    //ofBackground(0);
    //ofBackground(0, 255);
    //ofBackground(0, 0, 0, 255);
    //ofClear(0);
    //ofClear(0, 255);
    //ofClear(0, 0, 0, 255);
    //glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    // NOTE(Eric): This fixes the issue where the saved image is not cleared to black
    // and shows all past frames.
    //ofSetColor(0);
    //ofDrawRectangle(0, 0, ofGetWindowWidth(), ofGetWindowHeight());

    ofSetColor(255);
    ofDrawBitmapString("press 'x' to capture the whole screen\n", 100, 500);
    ofDrawBitmapString("press 'f' to toggle between rotating image and fixed drawing (used for anti-aliasing issue)\n", 100, 520);

    if (fixedDrawing)
    {
        ofSetColor(255, 203, 84);
        ofDrawLine(10, 10, 100, 100);
        ofDrawLine(10, 30, 77, 100);
        ofDrawEllipse(200, 200, 100, 200);
    }
    else
    {
        phase += 0.35f;

        ofSetHexColor(0xDDDDFF);
        ofDrawRectangle(200, 200, 300, 180);

        ofSetColor(255, 255, 255, 128);
        ofPushMatrix();
        float width = cooper.stringWidth("catch me\nif you can!");
        ofTranslate(350, 290, 0);
        ofRotateDeg(phase * 3, 0, 0, 1);
        ofScale(1 + 0.5f * sin(phase / 10.0f), 1 + 0.5f * sin(phase / 10.0f), 1);
        cooper.drawString("catch me\nif you can!", -width / 2, 20);
        ofPopMatrix();

        ofSetColor(255, 150, 140, 128);
        ofPushMatrix();
        ofTranslate(330, 280, 0);
        ofRotateDeg(phase * 5, 0, 0, 1);
        ofDrawRectangle(-25, -25, 50, 50);
        ofPopMatrix();
    }

    if (bSnapshot == true)
    {
			
        // NOTE(Eric): Needed if using the Programmable Renderer with
        // ofSetBackgroundAuto(false) since it does not use double buffering and
        // defaults to reading from the back buffer
        //glReadBuffer(GL_FRONT);
        img.grabScreen(0, 0, ofGetWindowWidth(), ofGetWindowHeight());

        string fileName = "snapshot_" + ofToString(snapCounter, 5, '0') + ".png";
        img.save(fileName);
		// NOTE(Eric): These methods produce the same result as above.
        //ofSaveScreen(fileName);
        //ofSaveViewport(fileName);
        snapCounter++;
        bSnapshot = false;
    }
}


//--------------------------------------------------------------
void ofApp::keyPressed(int key)
{
    if (key == 'x')
    {
        bSnapshot = true;
    }

    if (key == 'f')
    {
        fixedDrawing = !fixedDrawing;
    }
}

Running the example as is will use the automatic clear with the default color. Simply uncomment the desired line to set the color to black or try clearing the screen to black manually. In main.cpp, setting the OpenGL version to 2.1 or using the other method without specifying a version will work as expected, but using a version higher than 3.0 will produce the issues as described in the original post.

I hope this is clear enough.

Thanks.