#!/usr/bin/env python # Copyright (C) 2011 Sam Bull """ Base widget, all widgets inherit from this. """ from pygame.locals import Rect from OpenGL.GL import * import FTGL from ..surface import SurfaceBase class OpenGLImage(SurfaceBase): """ Class used to emulate the interface of Surface for OpenGL drawing. Functions should be used in the same manner as pygame.Surface. Differences are shown in documentation, otherwise assume it to function the same as the equivalent pygame.Surface function. """ def __init__(self, surf, parent=None, **kwargs): self._a = None self._lock = False self.display_list = [] self._children = [] if parent: self._parent = parent # Parent surface used for _abs else: self._parent = self._default_screen self._rect = Rect((0,0), (0,0)) if isinstance(surf, (tuple,list)): self._rect.size = surf elif isinstance(surf, OpenGLFont): self._rect.size = (surf.size) self.display_list.extend(surf.display_list) elif isinstance(surf, OpenGLImage): self._rect = surf._rect self._a = surf._a self.display_list = surf.display_list self._children = surf._children def blit(self, surf, pos=None): assert isinstance(surf, OpenGLImage) if surf not in self._children: if pos is not None: surf.pos = pos surf._parent = self self._children.append(surf) def draw(self): glLoadIdentity() if self.rect.w <= self._parent.rect.w and \ self.rect.h <= self._parent.rect.h: # Mask the area, so nothing is drawn outside surface area. bottom = self._default_screen.h - self.rect_abs.bottom glScissor(self.rect_abs.x, bottom, self.rect.w+1, self.rect.h+1) glTranslatef(self.pos_abs[0], self.pos_abs[1], 0) for dl,col in self.display_list: if col is not None: glColor(col[0], col[1], col[2], self.a) glCallList(dl) for child in self._children: child.draw() def fill(self, col=0, rect=None, special_flags=0): """If col == 0 and rect is None, clears image with no fill.""" if rect is None: # Clear the surface self.display_list = [] self._children = [] rect = Rect((0,0), self.size) if col != 0: col = [c/255. for c in col] dl = glGenLists(1) glNewList(dl, GL_COMPILE) glRectfv(rect.topleft, rect.bottomright) glEndList() self.display_list.append((dl, col)) def copy(self): return OpenGLImage(self) def get_size(self): return self.size def set_alpha(self, alpha): self._a = alpha/255. def set_at(self, vertex, col): col = [c/255. for c in col] if self._lock: glColor(*col) else: dl = glGenLists(1) glNewList(dl, GL_COMPILE) glBegin(GL_POINTS) glVertex(vertex) if not self._lock: glEnd() glEndList() surf.display_list.append((dl, col)) def lock(self): """ Should only be used for multiple set_at() calls. Should not be used in conjuction with the draw functions. """ self._lock = True self._lock_dl = glGenLists(1) glNewList(self._lock_dl, GL_COMPILE) glBegin(GL_POINTS) def unlock(self): glEnd() glEndList() self.display_list.append((self._lock_dl, None)) del self._lock_dl self._lock = False def replace(self, surf, **kwargs): self._rect.size = surf.size self.display_list = surf.display_list[:] self._children = surf._children[:] @property def a(self): if self._a is not None: return self._a else: return self._parent.a # --- Dummy methods. Ignore. --- def __call__(self): return self def set_colorkey(self, col): pass class OpenGLFont(): """ Wraps the FTGL.TextureFont to allow it to be added to an OpenGLImage object in the same manner as pygame.Font. """ def __init__(self, font, size): self._children = [] self.font = FTGL.TextureFont(font) self.font.FaceSize(16) self.y_offset = self.font.line_height * .75 def render(self, text, antialias, color, background=None): text = text.encode() col = [c/255. for c in color] dl = glGenLists(1) self.size = (self.font.Advance(text), self.font.line_height) glNewList(dl, GL_COMPILE) glPushMatrix() # Flip text right way up glMultMatrixf((1, 0, 0, 0, 0,-1, 0, 0, 0, 0, 1, 0, 0, self.y_offset, 0, 1)) self.font.Render(text) glPopMatrix() glEndList() self.display_list = [(dl, col)] return self class Draw(): """ Class to emulate the pygame.draw module. Functions should work in the same manner. """ def rect(self, surf, col, rect, width=0): col = [c/255. for c in col] dl = glGenLists(1) glNewList(dl, GL_COMPILE) if not width: glRectfv(rect.topleft, rect.bottomright) else: if width > 1: width -= 1 hw = width/2. glLineWidth(width) glBegin(GL_LINES) glVertex(rect.x, rect.y + hw) glVertex(rect.right - width, rect.y + hw) # (hw%1) fixes line rendering off by a pixel glVertex(rect.right - hw, rect.y + (hw%1)) glVertex(rect.right - hw, rect.bottom - width + (hw%1)) glVertex(rect.right, rect.bottom - hw) glVertex(rect.x + width, rect.bottom - hw) glVertex(rect.x + hw, rect.bottom) glVertex(rect.x + hw, rect.y + width) glEnd() glEndList() surf.display_list.append((dl, col)) def polygon(self, surf, col, pointlist, width=0): """ With width == 0, can only draw convex polygons. Be careful of the order of vertices if it draws differently than pygame. """ col = [c/255. for c in col] dl = glGenLists(1) glNewList(dl, GL_COMPILE) if not width: glBegin(GL_POLYGON) for v in pointlist: glVertex(v) glEnd() glEndList() surf.display_list.append((dl, col)) def circle(self, surf, col, pos, radius, width=0): col = [c/255. for c in col] dl = glGenLists(1) glNewList(dl, GL_COMPILE) if not width: glEnable(GL_POINT_SMOOTH) glHint(GL_POINT_SMOOTH_HINT, GL_NICEST) glPointSize(radius*2) glBegin(GL_POINTS) glVertex(pos) glEnd() glDisable(GL_POINT_SMOOTH) glPointSize() glEndList() surf.display_list.append((dl, col)) def line(self, surf, col, start_pos, end_pos, width=1): col = [c/255. for c in col] dl = glGenLists(1) glNewList(dl, GL_COMPILE) glLineWidth(width) glBegin(GL_LINES) glVertex(start_pos) glVertex(end_pos) glEnd() glEndList() surf.display_list.append((dl, col)) # Export Draw functions draw = Draw()