Sign in to follow this  
Derakon

wxGLCanvas and double-buffering

Recommended Posts

I'm working on a very-very-legacy app written in C++, with Python and WXWidgets handling the UI. One of the UI elements is a wxGLCanvas histogram of the pixels in a camera view, with some text indicating the mean/max/median values and the floor and ceiling cutoffs. The histogram itself is drawn using OpenGL calls, while the text is drawn with wxPaintDC::DrawText calls. As part of fitting more camera views onto the screen at one time, we bought a new display, with the intent of rotating it 90 degrees (so it'd be ~1200x1900 instead of the other way around). Unfortunately, we discovered that when we do that, the text in the histogram flickers like crazy, rapidly causing our users to go insane. After some debugging, I determined that the canvas object was not double-buffered; it's not clear to me why it wasn't flickering all the time because of this. I assume that rotating the display switched us to a lower display refresh rate or something. I added WX_GL_DOUBLEBUFFER to the parameters passed to the wxGLCanvas, and the flicker went away...but now the histogram colors are dark-red-on-black, instead of the black-on-white they're supposed to be. What's more, changing the colors used by the OpenGL calls has no effect. Note that the color of the drawn text is unchanged; it's just the background of the canvas, and the histogram itself, that have gone nuts. It doesn't look like an RGB-BGA type of issue either (i.e. color rotation) because the saturation and brightness of the histogram have changed. I've also determined that using GLUT's glutBitmapCharacter instead of the DrawText calls will result in zero flicker, but it also causes irregular crashes; somehow the OnPaint method of the canvas object is getting called on an invalid object, so the first time we try to access a member field, we get a segfault. I've included the relevant code below. If you have any ideas what might be wrong, I'd love to hear them. I apologize for the styling; one of my many long-term projects is to clean all this up.
// Code where we create the GLHistogram instances:
int glParams[] = {WX_GL_DOUBLEBUFFER};
for(int camID=0;camID<NCAMS;camID++)
{
  m_histPlotWin[camID] = new GLHistogram(this, ID_WIN_HIST+camID, (1<<BITS_PER_PIXEL), NULL, glParams);
}

// GLHistogram code
GLHistogram::GLHistogram(wxWindow *parent, wxWindowID id, int histSize, GLViewer *vie, int* glParams) :
wxGLCanvas(parent, id, wxDefaultPosition, wxSize(100,100), 0, _T("GLHisto"), glParams),
  maxRight(histSize-1),
  viewer(vie),
  m_histogramData( new wxUint16[histSize] )
{
  m_init   = FALSE;
  m_gllist = 0;

  dragging = 0;
  m_log = false;
  left =   0;
  right = 678;

  leftMap = 350;
  rightMap = 450;
  histogram_displayed = NULL;

  ResetStats();

  m_pMenuPopup = new wxMenu;
  m_pMenuPopup->Append(Menu1_Recalc, _T("&Recalc"));
  m_pMenuPopup->Append(Menu1_ZoomContrast, _T("zoom to braces"));
  m_pMenuPopup->Append(Menu1_ZoomFull, _T("&zoom full"));
  m_pMenuPopup->Append(Menu1_ZoomMinMax, _T("zoom &fit"));
  m_pMenuPopup->Append(Menu1_Log, _T("&logarithm"));

  SetCursor(*wxCROSS_CURSOR);
}

bool GLHistogram::InitGL()
{
  GetSize(&w,&h);
  wxCHECK2(w>0, return false);//Linux
  wxCHECK2(h>0, return false);//Linux
  
  range = right - left;
  rangePerPixel = double(range)/w;
  leftMap_displayed  = int( (leftMap - left)  / rangePerPixel );
  rightMap_displayed = int( (rightMap - left) / rangePerPixel );

  histogram_displayed = new int[w];

  glViewport(0, 0, (GLint) w, (GLint) h);
  
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(  0, w-1, 0, h-1, -1,1);
  glMatrixMode(GL_MODELVIEW);
  
  glEnable(GL_TEXTURE_2D);
  glClearColor(1.0, 1.0, 1.0, 0.0);
  return m_init = TRUE;
}

// Function stolen from the GLUT example at
// http://www.opengl.org/resources/features/fontsurvey
void GLHistogram::print_bitmap_string(char* str, double x, double y) {
  glRasterPos2f(x, y);
  if (str && strlen(str)) {
    while (*str) {
      glutBitmapCharacter(GLUT_BITMAP_8_BY_13, *str);
      str++;
    }
  }
}

void GLHistogram::OnPaint( wxPaintEvent& event )
{
    // This is a dummy, to avoid an endless succession of paint messages.
    // OnPaint handlers must always create a wxPaintDC.
    wxPaintDC dc(this);

#ifndef __WXMOTIF__
  if (!GetContext()) return;
#endif
    if (GetParent()->IsShown())
      SetCurrent();
  
    if (!m_init)
    {
    if( !InitGL() ) return;
    }

  // Sets up the 'p' array of histogram values.
  // Doesn't actually draw anything.
  generateDisplayedHistogram();

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // actual histogram:
  glColor3f(0.0, 0.0, 0.0);
  //NOFILL glBegin(GL_LINE_STRIP);
  glBegin(GL_LINES);

  int *p = histogram_displayed;
  double fac = histogram_displayed_max>1 
    ? ( double(h-20) / (m_log ? log(histogram_displayed_max+1.0) : histogram_displayed_max ) )  //2003Jun06: +1.0 for '1 count to show > 0' !!!!
    : 1;
  for(int x=0;x<w;x++)
    {
    const int y = *p++;
    glVertex2f(x, 0);
    glVertex2f(x, (m_log? log(y+1.0)*fac :y*fac)); //2003Jun06: +1.0 for '1 count to show > 0' !!!!
    }
  glEnd();


  // leftMap - rightMap - brackets
  glColor3f(1.0, 0.0, 0.0);
  glBegin(GL_LINE_STRIP);
     // Specify verticies in quad
    glVertex2i(leftMap_displayed+5, 7);
    glVertex2i(leftMap_displayed,   7);
    glVertex2i(leftMap_displayed,   h-7);
    glVertex2i(leftMap_displayed+5, h-7);
  glEnd();
  glBegin(GL_LINE_STRIP);
    // Specify verticies in quad
    glVertex2i(rightMap_displayed-5, 7);
    glVertex2i(rightMap_displayed,   7);
    glVertex2i(rightMap_displayed,   h-7);
    glVertex2i(rightMap_displayed-5, h-7);
  glEnd();

  
  // mean idicator
  int mean_displayed = int( (m_histavg - left) / rangePerPixel );
  int yy = 7+ h/4;
  //printf("%d %f %d %f\n", mean_displayed,m_histavg, left, rangePerPixel );
  glColor3f(0.0, 1.0, 0.0);
  glBegin(GL_LINES);
    glVertex2i(mean_displayed-1, 7);    glVertex2i(mean_displayed-1, yy);
    glVertex2i(mean_displayed  , 4);    glVertex2i(mean_displayed  , yy+3);
    glVertex2i(mean_displayed+1, 7);    glVertex2i(mean_displayed+1, yy);
  glEnd();
  

  //const int baseLineOffset = wxGetApp().getImgBaseLineOffset();

  // TODO: Better string handling

  //Non-flickering text, but crashes after running for awhile.
  //const int leftSpacer = 10;
  //const int betweenSpacer = 10;
  //char str1[20], str2[10], str3[30];
  //sprintf(str1, "%5d [ %5d", leftMap - baseLineOffset, m_histmin - baseLineOffset);
  //glColor3f(0.0, 0.0, 1.0);
  //print_bitmap_string(str1, leftSpacer, 45);
  //sprintf(str2, "%5d", m_histpeak - baseLineOffset);
  //glColor3f(1.0, 0.0, 0.0);
  //print_bitmap_string(str2, leftSpacer + betweenSpacer + strlen(str1) * 8, 45);
  //sprintf(str3, "%5d ] %5d %s", m_histmax - baseLineOffset, rightMap - baseLineOffset, m_log ? "(log)" : "");
  //glColor3f(0.0, 0.0, 1.0);
  //print_bitmap_string(str3, leftSpacer + betweenSpacer * 2 + (strlen(str1) + strlen(str2)) * 8, 45);

  glFlush();
  SwapBuffers();
  glFinish(); //20060417 http://glprogramming.com/red/appendixc.html - Synchronizing Execution

	const int baseLineOffset= wxGetApp().getImgBaseLineOffset(); //20060322 
	dc.SetTextForeground(*wxBLUE);

        // Non-crashing, flickers-unless-double-buffered text
	wxString s1 = wxString::Format(_T("%d [ %d"),
  								 leftMap-baseLineOffset, m_histmin-baseLineOffset);
	wxString s2 = wxString::Format(_T("%d"), m_histpeak-baseLineOffset);
	wxString s3 = wxString::Format(_T("%d ] %d %s"), 
  								 m_histmax-baseLineOffset,
								 rightMap-baseLineOffset, 
                                   m_log?_T("(log)"):_T(""));


  	dc.DrawText(s1, 10, 5);
	dc.SetTextForeground(*wxRED);
  	dc.DrawText(s2, 10-10 + s1.length()*8, 5); // guessed offsets for windows
	dc.SetTextForeground(*wxBLUE);
  	dc.DrawText(s3, 10+0 + (s1.length()+s2.length())*8, 5);
}



Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this