5

I am making a game in PyOpenGL and want to blit some images onto the screen as some overlay (for example, the pause button). How can I do that?

I've tried using glBitmap(), but it doesn't work.

This is what I have:

pauseimg = pygame.image.load(path + "pause.png").convert_alpha()
def blit_image(x,y,w,h,img,r,g,b):
    glColor3f(r,g,b)
    glWindowPos2f(x,y)
    glBitmap(w,h,0,0,0,0,img)
blit_image(300,300,313,115,pauseimg,1,1,1)

I expected it to blit the image, but instead it threw an exception:

Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenGL/latebind.py", line 41, in __call__
    return self._finalCall( *args, **named )
TypeError: 'NoneType' object is not callable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File ".py", line 438, in <module>
    blit_image(300,300,313,115,pauseimg,1,1,1)
  File ".py", line 135, in blit_image
    glBitmap(w,h,0,0,0,0,img)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenGL/latebind.py", line 45, in __call__
    return self._finalCall( *args, **named )
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenGL/wrapper.py", line 675, in wrapperCall
    pyArgs = tuple( calculate_pyArgs( args ))
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenGL/wrapper.py", line 436, in calculate_pyArgs
    yield converter(args[index], self, args)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenGL/converters.py", line 135, in __call__
    return self.function( incoming )
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenGL/arrays/arraydatatype.py", line 149, in asArray
    return cls.getHandler(value).asArray( value, typeCode or cls.typeConstant )
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/OpenGL/arrays/arraydatatype.py", line 53, in __call__
    typ.__module__, type.__name__, repr(value)[:50]
TypeError: ('No array-type handler for type pygame.type (value: <Surface(313x114x32 SW)>) registered', <OpenGL.converters.CallFuncPyConverter object at 0x111a720b8>)```
Noah
  • 445
  • 4
  • 19

1 Answers1

2

It is not possible to draw bitmap with the format GL_RGBA8 by glBitmap, because the buffer is treated as a buffer with format GL_COLOR_INDEX (see glTexImage2D)

See OpenGL 4.6 API Compatibility Profile Specification - 14.8 Bitmaps, page 579:

Like a polygon pattern, a bitmap is unpacked from memory according to the procedure given in section 18.1 for DrawPixels; it is as if the width and height passed to that command were equal to w and h, respectively, the type were BITMAP, and the format were COLOR_INDEX.


If you wan to draw a RGBA trexutre, then you've to create a butte array buffer. This can be achieved by NumPy:

raw_data = img.get_buffer().raw
data = numpy.fromstring(raw_data, numpy.uint8)

Create a texture object by glTexImage2D:

bitmap_tex = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, bitmap_tex)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_BGRA,GL_UNSIGNED_BYTE,data)

And draw the texture by a Quads - Primitive:

glEnable(GL_TEXTURE_2D)
glBegin(GL_QUADS)
glTexCoord2f(0, 1)
glVertex2f(x, y)
glTexCoord2f(1, 1)
glVertex2f(x+w, y)
glTexCoord2f(1, 0)
glVertex2f(x+w, y+h)
glTexCoord2f(0, 0)
glVertex2f(x, y+h)
glEnd()
glDisable(GL_TEXTURE_2D)

If the texture is transparent, then you've to activate Blending.
The function can be implemented as follows:

bitmap_tex = None
def blit_image(x,y,img,r,g,b):
    global bitmap_tex

    # get texture data
    w,h = img.get_size()
    raw_data = img.get_buffer().raw
    data = numpy.fromstring(raw_data, numpy.uint8)

    # create texture object
    if bitmap_tex == None:
      bitmap_tex = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, bitmap_tex)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_BGRA,GL_UNSIGNED_BYTE,data)

    # save and set model view and projection matrix
    glMatrixMode(GL_PROJECTION)
    glPushMatrix()
    glLoadIdentity()
    glOrtho(0, display[0], 0, display[1], -1, 1)
    glMatrixMode(GL_MODELVIEW)
    glPushMatrix()
    glLoadIdentity()

    # enable blending
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    glEnable(GL_BLEND)

    # draw textured quad
    glColor3f(r,g,b)

    glEnable(GL_TEXTURE_2D)
    glBegin(GL_QUADS)
    glTexCoord2f(0, 1)
    glVertex2f(x, y)
    glTexCoord2f(1, 1)
    glVertex2f(x+w, y)
    glTexCoord2f(1, 0)
    glVertex2f(x+w, y+h)
    glTexCoord2f(0, 0)
    glVertex2f(x, y+h)
    glEnd()
    glDisable(GL_TEXTURE_2D)

    # restore matrices
    glMatrixMode(GL_PROJECTION)
    glPopMatrix()
    glMatrixMode(GL_MODELVIEW)
    glPopMatrix()

    # disable blending
    glDisable(GL_BLEND)
pauseimg = pygame.image.load(path + "pause.png").convert_alpha()
blit_image(300,300,pauseimg,1,1,1)
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Does raw data refer to png files? How can I load them? – Noah Jun 26 '19 at 08:48
  • Then how can I blit 2d images as an overlay in PyOpenGL? – Noah Jun 26 '19 at 09:26
  • Why it doesn't blit anything? – Noah Jun 26 '19 at 11:56
  • Also, ```glOrtho(0, window_width, 0, window_height, -1, 1)``` doesn't work. It gave me a blank view. – Noah Jun 26 '19 at 11:58
  • Think I tried it. I've got ```gluPerspective()``` and ```gluLookAt()``` working. Does it matter? – Noah Jun 26 '19 at 12:14
  • The colours seemed weird. How can I fix that? – Noah Jun 26 '19 at 12:27
  • @Noah The color is multiplied by the color which is set by `glColor3f(r,g,b)`. Set `glColor3f(1,1,1)`. If it is still wired then change `GL_BGRA` to `GL_RGBA` in `glTexImage2D` (this depends on your system). – Rabbid76 Jun 26 '19 at 12:29
  • It showed light blue when I did RGBA, and yellow when BGRA. My image contains some transparent part and how can I not show the transparent part? – Noah Jun 26 '19 at 12:34
  • In pygame, I can only do a rect object. My texture is irregular and the empty part need to be transparent. When I do 1,1,1 in color3f, my transparent part got colored yellow. – Noah Jun 26 '19 at 12:47
  • Actually, ```glBlendFunc(GL_ONE_MINUS_SRC_ALPHA,GL_SRC_ALPHA)``` worked better for me and I solved the issue by changing color3f to 0,0,0. But I have meshes in front and this solution only colored the transparent part the same color as ```glClearColor()``` or black if not specified. How can I solve it? – Noah Jun 26 '19 at 12:50
  • Okay. My question is solved. I found out actually glBlend considers the current matrix, so I have to put the draw code behind the meshes. – Noah Jun 27 '19 at 11:52
  • @Noah Drawing primitives depends on the current matrix. Even if blending is enabled, the depth test is processed. If the issue is, that the sproite is covered, the you can disable the [Depth Test](https://www.khronos.org/opengl/wiki/Depth_Test) ([`glDisable(GL_DEPTH_TEST)`](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glEnable.xhtml)) in `blit_image()`. Don't forget to enable it again at the end (`glEnable (GL_DEPTH_TEST`) – Rabbid76 Jun 27 '19 at 12:10