1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
|
# PyEPL: hardware/graphics/__init__.py
#
# Copyright (C) 2003-2005 Michael J. Kahana
# Authors: Ian Schleifer, Per Sederberg, Aaron Geller, Josh Jacobs
# URL: http://memory.psych.upenn.edu/programming/pyepl
#
# Distributed under the terms of the GNU Lesser General Public License
# (LGPL). See the license.txt that came with this file.
"""
This module provides 2D graphics functionality.
"""
import pygame
from pygame.locals import *
from OpenGL.GL import *
import os
import time
import pyepl
import pyepl.hardware.eventpoll
import pyepl.hardware.timing
from image import LowImage
from font import LowFont
import screensync
pyepldir = os.path.abspath(os.path.dirname(pyepl.__file__))
def initialize(**options):
"""
Do anything that needs to happen before 2D graphics output can
happen.
"""
global init_options
init_options = options
# see if set the linux screen sync
platform = os.uname()[0]
if platform =='Linux':
screensync.linuxSetRefreshBlock(init_options["sync_to_vbl"])
# init the pygame display
pygame.display.init()
pygame.font.init()
def finalize():
"""
Clean up.
"""
pygame.display.quit()
pygame.font.quit()
def toggleFullscreen():
"""
"""
global fullscreen
fullscreen = not fullscreen
pygame.display.toggle_fullscreen()
def getFullscreen():
"""
"""
global fullscreen
return fullscreen
def getShowFPS():
"""
"""
global init_options
return init_options["show_fps"]
def getResolution():
"""
Return a 2-tuple of the X, Y resolution of the window.
"""
return pygame.display.get_surface().get_size()
def setVideoCaption(caption):
"""
Set the window caption.
"""
pygame.display.set_caption(caption)
lastclearcolor = None
lastclearimage = None
def clearScreen(color):
"""
Set the whole window to RGB 3-tuple color.
"""
global lastclearcolor
global lastclearimage
try:
alpha = color[3]
except IndexError:
alpha = 255
if alpha == 255:
glClearColor(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, alpha / 255.0)
glClear(GL_COLOR_BUFFER_BIT)
else:
if lastclearcolor != color:
x, y = pygame.display.get_surface().get_size()
lastclearimage = LowImage(x + 1, y + 1)
lastclearimage.fill(color[0], color[1], color[2], alpha)
lastclearcolor = color
lastclearimage.show(0, 0)
# thresh is set to 250 microseconds because tests have shown that a
# non-blocking flip takes less than that. It will be necessary to
# test this more. Keep in mind that if it does block, but takes less
# than 250us, then we will essentially miss a frame, reporting that
# the image came on the screen one frame after it actually did.
blockThresh = .000250
# max number of flips to prevent loop. If we don't block after 5,
# then something is wrong.
maxFlips = 5
def doMultiFlips(vTrack):
"""
In order to block correctly, we must call a second flip if the
first flip did not block. The only issue is that we have to copy
the front buffer to the back buffer before we call flip again.
It passes in the instance of the video track so that it can redraw
the pending showables on the back buffer to enable multiple flips.
"""
# get thresh for if blocking
global blockThresh
global maxFlips
# time the first flip
starttime = time.time() #pyepl.hardware.timing.universal_time()
pygame.display.flip()
endtime = time.time() #pyepl.hardware.timing.universal_time()
# see if the flip took no time
if endtime - starttime < blockThresh:
# Must call the flip again
# copy front buffer to back
# get the screen size
#w, h = pygame.display.get_surface().get_size()
# draw everything to back buffer
# iterate through the onscreen pending list...
for shown, showable, x, y in vTrack.pending:
# ...showing everything in order
showable.show(x, y)
# flip until blocked
starttime = time.time()
pygame.display.flip()
endtime = time.time()
count = 0
while (endtime-starttime)<blockThresh and count < maxFlips:
starttime = time.time()
pygame.display.flip()
endtime = time.time()
count = count + 1
# # uncomment the following few lines for debugging
# print count+1, (endtime-starttime)*1000.
# else:
# print 0, (endtime-starttime)*1000.
#############
# the old slow way
# perform the opengl buffer copy
#glReadBuffer (GL_FRONT)
#glDrawBuffer (GL_BACK)
#glCopyPixels (0,0,w,h,GL_COLOR)
# call flip
#pygame.display.flip()
#############
def doBlockingFlip():
"""
Perform a flip and then write a pixel to the back buffer to pause
the pipeline. Inspired by PsychToolbox code.
"""
# do the flip
pygame.display.flip()
# The following is taken from the PsychToolbox
# Draw a single pixel in left-top area of back-buffer. This will wait/stall the rendering pipeline
# until the buffer flip has happened, aka immediately after the VBL has started.
# We need the pixel as "synchronization token", so the following glFinish() really
# waits for VBL instead of just "falling through" due to the asynchronous nature of
# OpenGL:
glDrawBuffer(GL_BACK)
# We draw our single pixel with an alpha-value of zero - so effectively it doesn't
# change the color buffer - just the z-buffer if z-writes are enabled...
glColor4f(0,0,0,0)
glBegin(GL_POINTS)
glVertex2i(10,10)
glEnd()
# This glFinish() will wait until point drawing is finished, ergo backbuffer was ready
# for drawing, ergo buffer swap in sync with start of VBL has happened.
glFinish()
# vTrack has to be optional because the clock is currently optional.
#def makeVideoChanges(t = None, vTrack=None):
def makeVideoChanges(t = None):
"""
At time t, update the screen to reflect all prior changes.
Returns a timestamp indicating when the update actually happened.
"""
# See if syncing or not
if init_options["sync_to_vbl"]:
# Call the multiple flip saving the timestamp
#tempstamp = pyepl.hardware.timing.timedCall(t, doMultiFlips, vTrack)[0]
tempstamp = pyepl.hardware.timing.timedCall(t, doBlockingFlip)[0]
# return adjusted time based on the blocking
timestamp = (tempstamp[0]+tempstamp[1]-1,1)
# timestamp = tempstamp # for debugging to see latencies
else:
# Just call single flip
timestamp = pyepl.hardware.timing.timedCall(t, pygame.display.flip)[0]
return timestamp
def startVideo():
"""
"""
global init_options
global fullscreen
flags = DOUBLEBUF
flags |= OPENGL
# check fullscreen setting:
fullscreen = init_options["fullscreen"]
if fullscreen:
flags |= FULLSCREEN
platform = os.uname()[0]
if platform == 'Darwin':
pygame.display.set_mode(init_options["resolution"], flags, 24)
# on the mac, we set up VBL syncing after opengl init...
# check VBL-sync setting:
if init_options["sync_to_vbl"]:
# sync to the VBL
screensync.setRefreshBlock()
if platform == 'Linux':
pygame.display.set_mode(init_options["resolution"], flags)
pygame.display.set_caption("PyEPL VideoTrack")
# hide the mouse pointer
pygame.mouse.set_visible(False)
# set the icon and the splash image
pygame.display.set_icon(pygame.image.load(os.path.join(pyepldir, "resources", "icon.png")))
x, y = init_options["resolution"]
LowImage(os.path.join(pyepldir, "resources", "splash.png")).scale(x + 1, y + 1).show(0, 0)
pygame.display.flip()
def stopVideo():
"""
"""
pass
def setGammaRamp(red,green,blue):
"""
"""
# Call the set_gamma_ramp function
return pygame.display.set_gamma_ramp(red,green,blue)
|