Lightningbeam/PyGUI-2.5.3/GUI/Generic/GGLTextures.py

123 lines
5.0 KiB
Python

#
# PyGUI - OpenGL Textures - Generic
#
from weakref import WeakKeyDictionary
from OpenGL.GL import glGenTextures, glBindTexture, glDeleteTextures, \
glTexImage2D, GL_TEXTURE_2D, GL_RGBA
from OpenGL.GLU import gluBuild2DMipmaps
from GUI.GGLContexts import current_share_group
from GUI.GLDisplayLists import call_when_not_compiling_display_list
#----------------------------------------------------------------------
class TextureIdMap(WeakKeyDictionary):
def __del__(self):
#print "GL.TextureIdMap.__del__:", self ###
def free_texture():
glDeleteTextures([gl_id])
for share_group, gl_id in self.items():
context = share_group._some_member()
if context:
#print "...freeing texture id", gl_id, "for", share_group, "using", context ###
context.with_context(free_texture)
#----------------------------------------------------------------------
class Texture(object):
"""This class encapsulates an OpenGL texture and maintains a
representation of it for each OpenGL context with which it is used.
Allocation and maintentance of texture numbers is handled automatically.
Constructor:
Texture(texture_type)
where texture_type is the appropriate GL constant for the type
of texture (GL_TEXTURE_2D etc.)
"""
#
# _gl_type int GL_TEXTURE_2D, etc.
# _gl_id ShareGroup -> int Mapping from OpenGL share group to texture number
def __init__(self, texture_type):
self._gl_type = texture_type
self._gl_id = TextureIdMap()
def deallocate(self):
"""Deallocate any OpenGL resources that have been allocated for this
texture in any context."""
self._gl_id.__del__()
def bind(self):
"""Makes this texture the current texture for the current context
by calling glBindTexture. If this texture has not previously been
used with the current context, do_setup() is called to allocate
and initialise a representation of the texture."""
gl_id = self.gl_id()
glBindTexture(self._gl_type, gl_id)
def gl_id(self):
"""Returns the OpenGL texture number corresponding to this texture
in the current context. May trigger allocation of a new texture and
a call to do_setup(). Does not bind the texture, unless a new texture
is allocated, in which case the current texture binding may be changed
as a side effect."""
share_group = current_share_group()
gl_id = self._gl_id.get(share_group)
if gl_id is None:
gl_id = glGenTextures(1)
#print "GLTexture: assigned id %d for %s in share group %s" % (
# gl_id, self, share_group) ###
self._gl_id[share_group] = gl_id
call_when_not_compiling_display_list(lambda: self._setup(gl_id))
return gl_id
def _setup(self, gl_id):
glBindTexture(self._gl_type, gl_id)
self.do_setup()
def do_setup(self):
"""Subclasses should override this to make the necessary OpenGL
calls to initialise the texture, assuming that glBindTexture has
already been called."""
raise NotImplementedError
def gl_tex_image_2d(self, image, target = GL_TEXTURE_2D, internal_format = GL_RGBA,
border = False, with_mipmaps = False):
"""Load the currently bound texture with data from an image, with automatic
scaling to power-of-2 size and optional mipmap generation."""
border = bool(border)
if border and with_mipmaps:
raise ValueError("Bordered texture cannot have mipmaps")
b2 = 2 * border
width, height = image.size
twidth = pow2up(width - b2) + b2
theight = pow2up(height - b2) + b2
#print "GUI.GGLTextures.Texture.gl_tex_image_2d: before scaling: size =", (width, height) ###
if width <> twidth or height <> theight:
#print "GUI.GGLTextures.Texture.gl_tex_image_2d: scaling image to size", (twidth, theight) ###
from Pixmaps import Pixmap
image2 = Pixmap(twidth, theight)
def scale(canvas):
image.draw(canvas, (0, 0, width, height), (0, 0, twidth, theight))
image2.with_canvas(scale)
image = image2
format, type, data = self._gl_get_texture_data(image)
if with_mipmaps:
#print "GUI.GGLTextures.Texture.gl_tex_image_2d: loading mipmaps" ###
gluBuild2DMipmaps(target, internal_format, twidth, theight,
format, type, data)
else:
#print "GUI.GGLTextures.Texture.gl_tex_image_2d: loading texture" ###
glTexImage2D(target, 0, internal_format, twidth, theight, border,
format, type, data)
#----------------------------------------------------------------------
def pow2up(size):
# Round size up to a power of 2
psize = 1
while psize < size:
psize <<= 1
return psize