2
votes

I tried to make gdiplus and opengl works together in one offscreen hdc. Current code is something like this (no Internet access)

  1. create a gdiplus bitmap A
  2. create a gdiplus graphics from A as B
  3. get B's HDC
  4. choose a proper pixelformat for HDC
  5. create a gdiplus graphics from HDC as C
  6. create an opengl context from HDC as D
  7. drawing with C and D
  8. release HDC to B
  9. draw A to other graphics

C is needed because I found that after I release the HDC to B, it is not possible for opengl to change the bitmap. To check if things work, I added a bitmap E by reading gl pixels before (9)

There are still a problem in current program After releasing the hdc, the bitmap A losses its alpha information. The bitmap E works fine with correct alpha, but the GL_BGRA cannot read out alpha so I have to read GL_RGBA data and do a per pixel convertion into gdiplus color format.

Should I just use E or is there any other attempt?

Example of releasehdc alpha lossing:

Bitmap bitmap(100,100);
Graphics graphics(&bitmap);
HDC hdc=graphics.GetHDC();
Graphics graphics2(hdc);
graphics2.Clear(Color(128,255,0,0));
graphics2.Flush();
graphics.ReleaseHDC(hdc);
//draw bitmap to other graphics

Note: I just figure out that gdiplus didn't actually use the alpha channel in the HDC, only the rgb channels are shared, so I wonder how it works with a BGRA target bitmap

1
I do not use GDI+ but GDI does not know alpha instead transparency is achieved by using transparent color. So no alpha but more like Sprite mask) If you need to use alpha do it on OpenGL side .... you can fetch GDI stuff as texture ...Spektre

1 Answers

0
votes

I think the best way to use OpenGL and GDI+ together is to generate a texture in GDI+, then load that into OpenGL.

The gist of this is:

void MakeTexture(GLuint& texId)
{
    Bitmap offscreen(512, 512, PixelFormat32bppARGB);
    Graphics gr(&offscreen);

    gr.Clear(Color(128, 255, 0, 0));
    Gdiplus::SolidBrush brush(Color(255, 0, 0, 255));
    Pen pen(Color(128, 255, 0, 0), 16.f);
    Font font(L"Arial", 48.f);
    Rect r(25, 25, 100, 100);
    gr.DrawRectangle(&pen, r);
    gr.DrawString(TEXT("TEST STRING"), -1, &font, PointF(50, 50), &brush);

    vector<DWORD> argb;
    GetBitsLockBits(offscreen, argb, 1);
    genTexture(texId, offscreen.GetWidth(), offscreen.GetHeight(), argb);
}

void GetBitsLockBits(Bitmap& bmp, vector<DWORD>& argb, bool invert = 0)
{
    BitmapData bmpData;
    RectF rectf;
    Unit unit;
    bmp.GetBounds(&rectf, &unit);
    Rect rect(rectf.X, rectf.Y, rectf.Width, rectf.Height);
    printf("Got rect %d %d %d %d\n", rect.X, rect.Y, rect.Width, rect.Height);
    bmp.LockBits(&rect, ImageLockModeRead, PixelFormat32bppARGB, &bmpData);
    printf("BMP has w=%d h=%d stride=%d\n", bmpData.Width, bmpData.Height, bmpData.Stride);
    argb.resize(bmpData.Width * bmpData.Height);

    if (invert)
        for (int i = 0; i < bmpData.Height; i++)
            memcpy(&argb[i * bmpData.Width], (GLuint*)bmpData.Scan0 + (bmpData.Height - 1 - i) * bmpData.Width, bmpData.Width * 4);
    else if (bmpData.Stride == bmpData.Width * 4)
        memcpy(&argb[0], bmpData.Scan0, bmpData.Width * bmpData.Height * 4); // If the bmp is padded then
    // this won't read the image correctly (it will read it with pad bits between)
    else
        for (int i = 0; i < bmpData.Height; i++)
            memcpy(&argb[i * bmpData.Width], (GLuint*)bmpData.Scan0 + i * bmpData.Width, bmpData.Width * 4);
    bmp.UnlockBits(&bmpData);
}

void genTexture(GLuint& texId, int w, int h, const vector<DWORD>& argb)
{
    glGenTextures(1, &texId);  CHECK_GL;
    glBindTexture(GL_TEXTURE_2D, texId);  CHECK_GL;
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);  CHECK_GL;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  CHECK_GL;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  CHECK_GL;
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &argb[0]);  CHECK_GL;
}

Trying to have OpenGL and GDI+ cooperate by drawing into the same window directly will probably get you very flickery results.