123 lines
5.0 KiB
Python
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
|