DriverPygame: output to character buffer, draw whole screen at once.
authorRadek Brich <radek.brich@devl.cz>
Wed, 12 Oct 2011 10:11:27 +0200
changeset 28 feee783d4fc5
parent 27 139d1241b4c5
child 29 c0cdef06fd16
DriverPygame: output to character buffer, draw whole screen at once.
demo_window.py
tuikit/driver_pygame.py
--- a/demo_window.py	Wed Oct 12 00:58:46 2011 +0200
+++ b/demo_window.py	Wed Oct 12 10:11:27 2011 +0200
@@ -1,11 +1,11 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
 
+import cProfile, pstats
 import locale
 import os
 
 from tuikit.application import Application
-from tuikit.editfield import EditField
 from tuikit.window import Window
 from tuikit.button import Button
 
@@ -46,5 +46,9 @@
     locale.setlocale(locale.LC_ALL, '')
     os.environ['ESCDELAY'] = '25' # do not wait 1 second after pressing Escape key
     app = MyApplication()
-    app.start()
+    #app.start()
+    
+    cProfile.run('app.start()', 'appstats')
+    p = pstats.Stats('appstats')
+    p.sort_stats('time', 'cumulative').print_stats(20)
 
--- a/tuikit/driver_pygame.py	Wed Oct 12 00:58:46 2011 +0200
+++ b/tuikit/driver_pygame.py	Wed Oct 12 10:11:27 2011 +0200
@@ -8,11 +8,11 @@
 from tuikit.common import Coords, Size, MouseEvent
 
 
-class CharCache:
+class TerminalScreen:
     
-    '''CharCache should implement character cache, as the name suggests.
+    '''Provide character-level output to screen SDL surface.
     
-    Currently it does not, characters ale rendered directly from TTF.
+    This is performance bottleneck and should be optimized as much as possible.
     
     '''
     
@@ -32,7 +32,7 @@
         else:
             self.render = self.render_noglyph
 
-    def render_glyph(self, screen, x, y, c, fgcolor, bgcolor, attr):
+    def render_glyph(self, screen, x, y, c, fgcolor, bgcolor, defcolor, attr):
         '''Render using render_glyph and metrics.
         
         This is the correct way, but the output seems same as of render_noglyph
@@ -40,12 +40,21 @@
         
         This implements render() method. See render_noglyph for other implementation.
         
-        '''
+        '''                
+        # draw background
+        dest = Coords(x * self.charsize.w, y * self.charsize.h)
+        if bgcolor != defcolor:
+            screen.fill(bgcolor, pygame.Rect(dest.x, dest.y, self.charsize.w, self.charsize.h))
+        
+        if not c:
+            return
+
+        # choose font
         if attr == 'bold':
             font = self.font_bold
         else:
             font = self.font
-        
+
         # render character, get metrics
         surface = font.render_glyph(c, True, fgcolor, bgcolor)
         metrics = font.metrics(c)[0]
@@ -65,10 +74,6 @@
         if maxx > advance:
             maxx = advance
         
-        # draw background
-        dest = Coords(x * self.charsize.w, y * self.charsize.h)
-        screen.fill(bgcolor, pygame.Rect(dest.x, dest.y, self.charsize.w, self.charsize.h))
-        
         # draw character
         dest.x += minx
         dest.y += ascent - maxy
@@ -164,7 +169,7 @@
         self.log = logging.getLogger('tuikit')
         self.screen = None
         self.size.w, self.size.h = 120, 40  # screen size in characters
-        self.charcache = None
+        self.term = None
         self.charsize = Size(16, 8)  # character size in pixels
         self.last_keypress = None
         self.last_key = None
@@ -175,12 +180,17 @@
 
     def start(self, mainfunc):
         pygame.init()
-        self.charcache = CharCache()
-        self.charsize = self.charcache.charsize
+        self.term = TerminalScreen()
+        self.charsize = self.term.charsize
+        self.resize_screen()
+        mainfunc()
+
+    def resize_screen(self):
         mode = self.size.w * self.charsize.w, self.size.h * self.charsize.h
         self.screen = pygame.display.set_mode(mode, pygame.RESIZABLE)
-        mainfunc()
-
+        numchars = self.size.w * self.size.h
+        self.screenchars = [' '] * numchars
+        self.screencolors = [self.default_color] * numchars
 
     ## input ##
     
@@ -218,8 +228,7 @@
                 neww, newh = ev.w // self.charsize.w, ev.h // self.charsize.h
                 if neww != self.size.w or newh != self.size.h:
                     self.size.w, self.size.h = neww, newh
-                    mode = self.size.w * self.charsize.w, self.size.h * self.charsize.h
-                    self.screen = pygame.display.set_mode(mode, pygame.RESIZABLE)
+                    self.resize_screen()
                     events.append(('resize',))
             elif ev.type == pygame.QUIT:
                 events.append(('quit',))
@@ -242,16 +251,31 @@
     
     def erase(self):
         '''Clear screen.'''
-        self.screen.fill(self.colormap['black'])
+        numchars = self.size.w * self.size.h
+        for pos in range(numchars):
+            self.screenchars[pos] = ' '
+            self.screencolors[pos] = self.default_color
+        self.screen.fill(self.default_color[1])
     
     def putch(self, x, y, c):
         if not self.clipstack.test(x, y):
             return
-        fgcolor, bgcolor, attr = self.current_color
-        self.charcache.render(self.screen, x, y, c, fgcolor, bgcolor, attr)
+        pos = y*self.size.w+x
+        self.screencolors[pos] = self.current_color
+        self.screenchars[pos] = c
 
     def commit(self):
         '''Commit changes to the screen.'''
+        pos = 0
+        for y in range(self.size.h):
+            for x in range(self.size.w):
+                fgcolor, bgcolor, attr = self.screencolors[pos]
+                c = self.screenchars[pos]
+                if c == ' ':
+                    c = None
+                self.term.render(self.screen, x, y, c,
+                    fgcolor, bgcolor, self.default_color[1], attr)
+                pos += 1
         pygame.display.flip()