franky99
franky99

Reputation: 73

Visualize a single character in an OpenGL window (python)

With the following code I'd like to show a single character in an opengl window, but all I get if the character printed multiple time orizontally and vertically in small sizer into a space that is, I presume, as large as the character should be. Thank you.

The code (almost all):

import numpy
from freetype import *
import OpenGL.GL as gl
import OpenGL.GLUT as glut

text  = 'H'

def makefont(filename, size):
    face = Face(filename)
    face.set_char_size( size*64 )
    face.load_char(text, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
    bitmap = face.glyph.bitmap

    data = bitmap.buffer
    rows = bitmap.rows
    width = bitmap.width
    ascender  = face.glyph.bitmap_top
    descender = bitmap.rows-face.glyph.bitmap_top
    height = ascender+descender
    imageData = numpy.array(bitmap.buffer, numpy.uint8).reshape(rows,width)
    imageData = imageData[::-1,:]
    #########

    textureID = gl.glGenTextures(1)
    gl.glBindTexture( gl.GL_TEXTURE_2D, textureID )
    gl.glTexParameterf( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR )
    gl.glTexParameterf( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR )
    gl.glTexImage2D( gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0,
                     gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, imageData )
    #
    gl.glBindTexture(gl.GL_TEXTURE_2D, textureID)
    gl.glBegin(gl.GL_QUADS)
    gl.glTexCoord2f(0, 0)
    x = 0
    y = 0
    w = width
    h = height
    gl.glVertex2f(x, y)
    gl.glTexCoord2f(1, 0)
    gl.glVertex2f(x + w, y)
    gl.glTexCoord2f(1, 1)
    gl.glVertex2f(x + w, y + h)
    gl.glTexCoord2f(0, 1)
    gl.glVertex2f( x, y + h) 
    gl.glEnd()


def on_display( ):
    gl.glClearColor(1,1,1,1)
    gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
    gl.glColor(0,0,0,1)
    makefont( './VeraMono.ttf', 64 )
    glut.glutSwapBuffers( )

def on_reshape( width, height ):
    #

if __name__ == '__main__':
    import sys
    glut.glutInitDisplayMode( glut.GLUT_DOUBLE | glut.GLUT_RGB | glut.GLUT_DEPTH )

    gl.glTexEnvf( gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, gl.GL_MODULATE )
    gl.glEnable( gl.GL_DEPTH_TEST )
    gl.glEnable( gl.GL_BLEND )
    gl.glEnable( gl.GL_COLOR_MATERIAL )
    gl.glColorMaterial( gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT_AND_DIFFUSE )
    gl.glBlendFunc( gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA )
    gl.glEnable( gl.GL_TEXTURE_2D )
    glut.glutMainLoop( )

Upvotes: 1

Views: 373

Answers (1)

Rabbid76
Rabbid76

Reputation: 210996

The glyph image which is rendered by the freetype library has just 1 color channel. Use GL_ALPHA for the format and internal format of the texture. See glTexImage2D.
Further more you have to change the GL_UNPACK_ALIGNMENT parameter. The GL_UNPACK_ALIGNMENT parameter defines the alignment of the first pixel in each row (line) of an image when the image is read form the buffer. By default the this parameter is 4.
Each pixel of the glyph image is encoded in one byte and the image is tightly packed. So the the alignment of the glyph image is 1 and the parameter has to be changed before the image is read and the texture is specified:

gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_ALPHA, width, height, 0,
                gl.GL_ALPHA, gl.GL_UNSIGNED_BYTE, imageData)

Since you do not set any projection matrix, you have to draw the geometry in normalized device space. In normalized device space all the coordinates are in rage [-1.0, 1.0]:

x, y, w, h = -1, -1, 2, 2
gl.glBegin(gl.GL_QUADS)
gl.glTexCoord2f(0, 0)
gl.glVertex2f(x, y)
gl.glTexCoord2f(1, 0)
gl.glVertex2f(x + w, y)
gl.glTexCoord2f(1, 1)
gl.glVertex2f(x + w, y + h)
gl.glTexCoord2f(0, 1)
gl.glVertex2f( x, y + h) 
gl.glEnd()

If you want to use window coordinates, the you have to set an orthographic projection by glOrtho:

gl.glMatrixMode(gl.GL_PROJECTION)
gl.glLoadIdentity()
gl.glOrtho(0, width, height, 0, -1, 1)
gl.glMatrixMode(gl.GL_MODELVIEW)
x, y, w, h = 0, 0, width, height
gl.glBegin(gl.GL_QUADS)
gl.glTexCoord2f(0, 0)
gl.glVertex2f(x, y)
gl.glTexCoord2f(1, 0)
gl.glVertex2f(x + w, y)
gl.glTexCoord2f(1, 1)
gl.glVertex2f(x + w, y + h)
gl.glTexCoord2f(0, 1)
gl.glVertex2f( x, y + h) 
gl.glEnd()

Complete function makefont and on_reshape:

def makefont(filename, size):
    face = Face(filename)
    face.set_char_size( size*64 )
    face.load_char(text, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT)
    bitmap = face.glyph.bitmap

    data = bitmap.buffer
    rows = bitmap.rows
    width = bitmap.width
    ascender  = face.glyph.bitmap_top
    descender = bitmap.rows-face.glyph.bitmap_top
    height = ascender+descender
    imageData = numpy.array(bitmap.buffer, numpy.uint8).reshape(rows,width)
    imageData = imageData[::-1,:]

    textureID = gl.glGenTextures(1)
    gl.glBindTexture( gl.GL_TEXTURE_2D, textureID )
    gl.glTexParameterf( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR )
    gl.glTexParameterf( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR )
    gl.glPixelStorei(gl.GL_UNPACK_ALIGNMENT, 1)
    gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_ALPHA, width, height, 0,
                gl.GL_ALPHA, gl.GL_UNSIGNED_BYTE, imageData)

    x, y, w, h = 0, 0, width, height
    gl.glBindTexture(gl.GL_TEXTURE_2D, textureID)
    gl.glBegin(gl.GL_QUADS)
    gl.glTexCoord2f(0, 0)
    gl.glVertex2f(x, y)
    gl.glTexCoord2f(1, 0)
    gl.glVertex2f(x + w, y)
    gl.glTexCoord2f(1, 1)
    gl.glVertex2f(x + w, y + h)
    gl.glTexCoord2f(0, 1)
    gl.glVertex2f( x, y + h) 
    gl.glEnd()
def on_reshape( width, height ):
    gl.glViewport(0, 0, width, height)
    gl.glMatrixMode(gl.GL_PROJECTION)
    gl.glLoadIdentity()
    gl.glOrtho(0, width, height, 0, -1, 1)
    gl.glMatrixMode(gl.GL_MODELVIEW)

See also Immediate mode and legacy OpenGL

Upvotes: 1

Related Questions