I am creating a Windows bitmap from a OpenGL multisampled pbuffer with a transparent background. I am using the alpha channel to control transparency in the resulting bitmap. The problem is that the alpha component in the RGBA value is inconsistent with the color components.

For example, I have a 2x FSAA pbuffer. I reset the RGBA color to (0,0,0,0). I then draw a filled white triangle. A pixel near the edge of the triangle will have these RGBA values: (188,188,188,127). I would have expected the RGB components to be the same as the alpha component. As it is, when I draw the bitmap into my device context, some of the blended pixels have color components that have overflowed (RGB component value would be greater than 255 which produces incorrect results). I am using the Win32 API AlphaBlend to draw the bitmap. It uses the following formula (red component example used) for normalized [0-1] values:

Dst.Red = Src.Red + (1 - Src.Alpha) * Dst.Red

So if I have a white destination pixel, and I substitute in the unnormalized values, I get this:

Dst.Red = 188 + (255 - 127) * 255 / 255 = 316 (Overflow!)

In my example, if the RGB components were the same as the alpha value, then there would never be an overflow. So my question is, for white fragments, why are the resulting RGB components not the same as the alpha component?

Here's my code (MFC dialog application):

Code :
class CAlphaBlendDlg : public CDialogEx
{
  ...
  HGLRC       m_hBitmapRC;
  HPBUFFERARB m_hBitmapPbuffer;
  HDC         m_hBitmapPbufferDC;
  ...
};
 
BOOL CAlphaBlendDlg::OnInitDialog()
{
  CDialogEx::OnInitDialog();
 
  CClientDC dc (this);
  CRect clientRect;
  GetClientRect(clientRect);
 
  // create an off-screen buffer for bitmap rendering
  int numMultiSamples = 2;
  const int attributes[] =
  {
    // need OpenGL supported
    WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
    // enable render to pbuffer
    WGL_DRAW_TO_PBUFFER_ARB, GL_TRUE,
    // at least 24 bits for depth
    WGL_DEPTH_BITS_ARB, 24,
    // need RGBA colors
    WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
    // at least 24 bits for color
    WGL_COLOR_BITS_ARB, 24,
    // need alpha channel for transparent background
    WGL_ALPHA_BITS_ARB, 8,
    // we don't need double buffering
    WGL_DOUBLE_BUFFER_ARB, GL_FALSE,
    // enable FSAA
    WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
    WGL_SAMPLES_ARB, numMultiSamples,
    // end with a NULL terminator
    NULL
  };
  int newPixelFormat = 0;
  UINT numFormats = 0;
  bool usePixelFormat = false;
  wglChoosePixelFormat (dc.m_hDC, attributes, NULL, 1, &newPixelFormat, &numFormats);
  // try to use OpenGL pbuffers to render to an off-screen buffer
  m_hBitmapPbuffer = wglCreatePbuffer (dc.m_hDC, newPixelFormat, clientRect.Width(), clientRect.Height(), NULL);
  m_hBitmapPbufferDC = wglGetPbufferDC (m_hBitmapPbuffer);
  /* No need to set the pixel format because the pbuffer is already
  created with the pixel format specified. */
  m_hBitmapRC = ::wglCreateContext (m_hBitmapPbufferDC);
}
 
void CAlphaBlendDlg::OnPaint()
{
  CPaintDC dc(this); // device context for painting
  CRect clientRect;
  GetClientRect (clientRect);
 
  CDC *pBitmapDC = new CDC();
  pBitmapDC->CreateCompatibleDC (&dc);
 
  BITMAPINFO bitMapInfo;
  memset (&bitMapInfo, 0, sizeof(bitMapInfo));
  bitMapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  bitMapInfo.bmiHeader.biWidth = clientRect.right;
  bitMapInfo.bmiHeader.biHeight = clientRect.bottom;
  bitMapInfo.bmiHeader.biPlanes = 1;
  bitMapInfo.bmiHeader.biBitCount = 32;
  bitMapInfo.bmiHeader.biCompression = BI_RGB;
 
  void *pBitmapBits = NULL;
  HBITMAP hBitmapDIB = ::CreateDIBSection (pBitmapDC->m_hDC, &bitMapInfo, DIB_RGB_COLORS, &pBitmapBits, NULL, 0);
  pBitmapDC->SelectObject (hBitmapDIB);
 
  VERIFY(wglMakeCurrent (m_hBitmapPbufferDC, m_hBitmapRC) != FALSE);
 
  // set up orthogonal projection so we can specify window coordinates for vertices
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D (0, clientRect.right, clientRect.bottom, 0);
  glMatrixMode (GL_MODELVIEW);
  glLoadIdentity();
  glViewport (0, 0, clientRect.Width(), clientRect.Height());
  glDisable(GL_DEPTH_TEST);
  glDrawBuffer (GL_FRONT);
  glClearColor(0,0,0,0);
  glClear(GL_COLOR_BUFFER_BIT);
 
  // draw a bunch of filled white triangles
  glColor4ub (255,255,255,255);
  static const int numTriangles = 100;
  static const double triangleSizeFactor = 0.1;
  const int triangleSize = (int) (clientRect.Width() * triangleSizeFactor);
  glBegin(GL_TRIANGLES);
  for (int index = 0; index < numTriangles; index++)
  {
    int x = rand() % (clientRect.Width() - triangleSize) + triangleSize / 2;
    int y = rand() % (clientRect.Height() - triangleSize) + triangleSize / 2;
    for (int vertexIndex = 0; vertexIndex < 3; vertexIndex++)
    {
      int vx = x + (rand() % triangleSize) - triangleSize / 2;
      int vy = y + (rand() % triangleSize) - triangleSize / 2;
      glVertex3i (vx,vy,0);
    }
  }
  glEnd();
 
  // read the pixels
  glReadBuffer(GL_FRONT);
  glReadPixels (0, 0, clientRect.Width(), clientRect.Height(), GL_BGRA_EXT, GL_UNSIGNED_BYTE, pBitmapBits);
  VERIFY(wglMakeCurrent (NULL, NULL) != FALSE);
 
  // use pBitmapBits to draw onto DC using AlphaBlend
  // ...
 
  // cleanup
  delete pBitmapDC;
  pBitmapDC = NULL;
  ::DeleteObject (hBitmapDIB);
  hBitmapDIB = NULL;
}