186 lines
7.4 KiB
Python
186 lines
7.4 KiB
Python
#
|
|
# PyGUI - OpenGL View - Cocoa
|
|
#
|
|
|
|
__all__ = ['GLConfig', 'GLView', 'GLPixmap']
|
|
|
|
import AppKit
|
|
from Foundation import NSSize
|
|
from AppKit import NSOpenGLPixelFormat, NSOpenGLView, \
|
|
NSBitmapImageRep, NSCachedImageRep, NSImage, NSAlphaFirstBitmapFormat, \
|
|
NSFloatingPointSamplesBitmapFormat
|
|
from Foundation import NSMakeRect
|
|
from OpenGL.GL import glViewport, glFlush, glFinish, glReadPixels, \
|
|
GL_RGB, GL_RGBA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, \
|
|
GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT, GL_FLOAT, \
|
|
glReadPixelsub, glTexImage2D, glPixelStorei, GL_UNPACK_ALIGNMENT
|
|
from OpenGL.GLU import gluBuild2DMipmaps
|
|
from GUI.GGLViews import GLView as GGLView
|
|
from GUI.GGLPixmaps import GLPixmap as GGLPixmap
|
|
from GUI.GGLConfig import GLConfig as GGLConfig
|
|
from GUI.GLContexts import GLContext
|
|
from GUI.GLTextures import Texture
|
|
from GUI.GLDisplayLists import DisplayList
|
|
from GUI.Utils import NSMultiClass, PyGUI_NS_ViewBase
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
class GLConfig(GGLConfig):
|
|
# _ns_pixel_format NSOpenGLPixelFormat
|
|
|
|
def _ns_get_pixel_format(self, offscreen = False):
|
|
attrs = [AppKit.NSOpenGLPFAColorSize, self._color_size]
|
|
if self._double_buffer:
|
|
attrs += [AppKit.NSOpenGLPFADoubleBuffer]
|
|
if self._alpha:
|
|
attrs += [AppKit.NSOpenGLPFAAlphaSize, self._alpha_size]
|
|
if self._stereo:
|
|
attrs += [AppKit.NSOpenGLPFAStereo]
|
|
if self._aux_buffers:
|
|
attrs += [AppKit.NSOpenGLPFAAuxBuffers, self._aux_buffers]
|
|
if self._depth_buffer:
|
|
attrs += [AppKit.NSOpenGLPFADepthSize, self._depth_size]
|
|
if self._stencil_buffer:
|
|
attrs += [AppKit.NSOpenGLPFAStencilSize, self._stencil_size]
|
|
if self._accum_buffer:
|
|
attrs += [AppKit.NSOpenGLPFAAccumSize, self._accum_size]
|
|
if self._multisample:
|
|
attrs += [AppKit.NSOpenGLPFASampleBuffers, 1]
|
|
attrs += [AppKit.NSOpenGLPFASamples, self._samples_per_pixel]
|
|
if offscreen:
|
|
attrs += [AppKit.NSOpenGLPFAOffScreen]
|
|
attrs.append(0)
|
|
ns_pf = NSOpenGLPixelFormat.alloc().initWithAttributes_(attrs)
|
|
if not ns_pf and self._double_buffer:
|
|
attrs.remove(AppKit.NSOpenGLPFADoubleBuffer)
|
|
ns_pf = NSOpenGLPixelFormat.alloc().initWithAttributes_(attrs)
|
|
if not ns_pf:
|
|
raise GLConfigError
|
|
return ns_pf
|
|
|
|
def _ns_set_pixel_format(self, ns_pf):
|
|
def ns_attr(attr):
|
|
return ns_pf.getValues_forAttribute_forVirtualScreen_(attr, 0)[0]
|
|
self._ns_pixel_format = ns_pf
|
|
self._double_buffer = ns_attr(AppKit.NSOpenGLPFADoubleBuffer)
|
|
self._color_size = ns_attr(AppKit.NSOpenGLPFAColorSize)
|
|
self._alpha_size = ns_attr(AppKit.NSOpenGLPFAAlphaSize)
|
|
self._alpha = self._alpha_size > 0
|
|
self._stereo = ns_attr(AppKit.NSOpenGLPFAStereo)
|
|
self._aux_buffers = ns_attr(AppKit.NSOpenGLPFAAuxBuffers)
|
|
self._depth_size = ns_attr(AppKit.NSOpenGLPFADepthSize)
|
|
self._depth_buffer = self._depth_size > 0
|
|
self._stencil_size = ns_attr(AppKit.NSOpenGLPFAStencilSize)
|
|
self._stencil_buffer = self._stencil_size > 0
|
|
self._accum_size = ns_attr(AppKit.NSOpenGLPFAAccumSize)
|
|
self._accum_buffer = self._accum_size > 0
|
|
self._multisample = ns_attr(AppKit.NSOpenGLPFASampleBuffers) > 0
|
|
self._samples_per_pixel = ns_attr(AppKit.NSOpenGLPFASamples)
|
|
|
|
def supported(self, mode = 'both'):
|
|
try:
|
|
ns_pf = self._ns_get_pixel_format()
|
|
pf = GLConfig.__new__()
|
|
pf._ns_set_pixel_format(ns_pf)
|
|
return pf
|
|
except GLConfigError:
|
|
return None
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
class GLView(GGLView):
|
|
# _ns_view NSOpenGLView
|
|
# _ns_context NSOpenGLContext
|
|
# _ns_flush function for flushing/swapping buffers
|
|
|
|
def __init__(self, config = None, share_group = None, **kwds):
|
|
pf = GLConfig._from_args(config, kwds)
|
|
ns_pf = pf._ns_get_pixel_format()
|
|
width, height = GGLView._default_size
|
|
ns_rect = NSMakeRect(0, 0, width, height)
|
|
ns_view = _PyGUI_NSOpenGLView.alloc().initWithFrame_pixelFormat_(
|
|
ns_rect, ns_pf)
|
|
ns_view.pygui_component = self
|
|
GGLView.__init__(self, _ns_view = ns_view)
|
|
GLContext.__init__(self, share_group = share_group, _ns_pixel_format = ns_pf)
|
|
ns_context = self._ns_context
|
|
ns_view.setOpenGLContext_(ns_context)
|
|
#ns_context.setView_(ns_view) # Docs say this is needed, but
|
|
# prints warning and seems to work without.
|
|
if pf.double_buffer:
|
|
self._ns_flush = ns_context.flushBuffer
|
|
else:
|
|
self._ns_flush = glFlush
|
|
self.set(**kwds)
|
|
self.with_context(self._init_context)
|
|
|
|
def destroy(self):
|
|
#print "GLView.destroy:", self ###
|
|
ns_view = self._ns_view
|
|
GGLView.destroy(self)
|
|
#print "GLView.destroy: breaking back link from", ns_view ###
|
|
ns_view.pygui_component = None
|
|
|
|
def invalidate(self):
|
|
self._ns_view.setNeedsDisplay_(True)
|
|
|
|
def update(self):
|
|
self._ns_view.displayIfNeeded()
|
|
|
|
def track_mouse(self):
|
|
return self._ns_track_mouse(self._ns_view)
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
class GLPixmap(GGLPixmap):
|
|
|
|
def __init__(self, width, height, config = None, share_group = None, **kwds):
|
|
pf = GLConfig._from_args(config, kwds)
|
|
ns_pf = pf._ns_get_pixel_format()
|
|
ns_size = NSSize(width, height)
|
|
ns_cache = NSCachedImageRep.alloc().initWithSize_depth_separate_alpha_(
|
|
ns_size, 0, True, True)
|
|
ns_image = NSImage.alloc().initWithSize_(ns_size)
|
|
GLContext.__init__(self, share_group = share_group, _ns_pixel_format = ns_pf)
|
|
self._ns_context.setView_(ns_cache.window().contentView())
|
|
self._init_with_ns_image(ns_image, flipped = False)
|
|
self._ns_cache = ns_cache
|
|
self.with_context(self._init_context)
|
|
|
|
def _ns_flush(self):
|
|
glFlush()
|
|
width, height = self.size
|
|
pixels = glReadPixels(0, 0, int(width), int(height), GL_RGBA, GL_UNSIGNED_BYTE)
|
|
bytes_per_row = int(width) * 4
|
|
ns_new_bitmap = NSBitmapImageRep.alloc().\
|
|
initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(
|
|
(pixels, "", "", "", ""), int(width), int(height), 8, 4, True, False, AppKit.NSDeviceRGBColorSpace, bytes_per_row, 0)
|
|
ns_image = NSImage.alloc().initWithSize_(NSSize(width, height))
|
|
ns_image.addRepresentation_(ns_new_bitmap)
|
|
ns_image.lockFocus()
|
|
ns_image.unlockFocus()
|
|
self._ns_image = ns_image
|
|
self._ns_bitmap_image_rep = ns_new_bitmap
|
|
|
|
#------------------------------------------------------------------------------
|
|
|
|
class _PyGUI_NSOpenGLView(NSOpenGLView, PyGUI_NS_ViewBase):
|
|
__metaclass__ = NSMultiClass
|
|
#
|
|
# pygui_component GLView
|
|
|
|
__slots__ = ['pygui_component']
|
|
|
|
def isFlipped(self):
|
|
return True
|
|
|
|
def reshape(self):
|
|
comp = self.pygui_component
|
|
if comp.window:
|
|
comp.with_context(comp._update_viewport)
|
|
|
|
def drawRect_(self, rect):
|
|
comp = self.pygui_component
|
|
comp.with_context(comp._render, flush = True)
|