# # 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)