Gabriel Jacobo avatar Gabriel Jacobo committed 2c341a8

MASSIVE speed improvements, more coming up!

Comments (0)

Files changed (48)

-1df8027101c527a38eab69c041a5c21ab7cb3648 external/SDL
+6570fe9f165eeac4c9b51546c888fb19911c8132 external/SDL
 270ba9f56e7bc5301e4744d8d97141af9afc6f23 external/SDL_image
 2fc381f45d059a86c4958a47f952c20f9a9ec149 external/SDL_ttf
 from ignifuga.Gilbert import Event, Gilbert, Signal
 from ignifuga.Log import error
 from ignifuga.components.Component import Component
+from Task import *
 
 import weakref,traceback
 
         # Initialize internal fields
         self.id = None
         self._released = False
+        self._initialized = False
         self._components = {}
         self._componentsByTag = {}
         self._componentsBySignal = {}
         self.tags = []
         self.signalQueue = []
         self._initFailCount = 0
+        self._initialComponents = []
 
         """
         Preprocess kwargs
         if self._released:
             error("Node %s released more than once" % self.id)
 
+        for component in self._components.itervalues():
+            component.free()
+
         self._components = {}
         self._componentsByTag = {}
         self._componentsBySignal = {}
         self._properties = {}
         self._released = True
+        self._initialComponents = []
 
     def __str__(self):
         return "Entity with ID %s" % (self.id,)
 
-    def init(self,data):
+    def init(self,**data):
         """ Initialize the required external data """
-        components = self._components.keys()
+        components = self._initialComponents
         failcount = {}
         while components:
             component = components.pop(0)
             try:
-                self._components[component].init(**data)
+                component.init(**data)
             except Exception, ex:
                 # Something failed, try it again later
                 if component not in failcount:
                     components.append(component)
                 else:
                     error('Temporarily failed initializing Entity %s because of component %s' % (self.id, component))
+                    error(traceback.format_exc())
                     self._initFailCount+=1
                     if self._initFailCount > 10:
                         error('Ignoring Entity %s, could not initialize it because of component %s' % (self.id, component))
                         error(traceback.format_exc())
-                        return self
-                    return None
-        return self
+                        return DONE()
+                    ERROR()
+                    return
+
+        self._initialized = True
+
 
 #    def register(self):
 #        """ Register Entity with the Overlord """
 
     def unregister(self):
         """ Unregister Entity with the Overlord """
-        Gilbert().stopEntity(self)
+        Gilbert().gameLoop.stopEntity(self)
         # Break dependency cycles
         if not self._released:
             self.__free__()
                 for c_id, c_data in data['components'].iteritems():
                     c_data['id'] = c_id
                     c_data['entity'] = self
-                    Component.create(**c_data)
+                    self._initialComponents.append(Component.create(**c_data))
             elif isinstance(data['components'], list):
                 for c_data in data['components']:
                     c_data['entity'] = self
-                    Component.create(**c_data)
+                    self._initialComponents.append(Component.create(**c_data))
 
     def __getstate__(self):
         odict = self.__dict__.copy()
 
         self.refreshTags()
 
+
     def addProperties(self, component):
         """ Adopt the component public properties"""
         for property in component.properties:
+            if hasattr(self, property):
+                setattr(component, property, getattr(self, property))
+                delattr(self, property)
             self._properties[property] = component
 
     def removeProperties(self, component):
     ###########################################################################
     # Update functions
     ###########################################################################
-    def update(self, data):
+    def update(self, now, **data):
         """ Public customizable update function """
-        pass
 
-    def _update(self, data):
+        # By default we don't need an entity update loop
+        STOP()
+
+    def _update(self, now, **data):
         """ Internal update function, updates components, etc, runs IN PARALLEL with update """
         # Dispatch signals
         for signal in self.signalQueue:
 
         self.signalQueue = []
 
-        # Run the active components update loop
-        for component in self._components.itervalues():
-            if component.active:
-                component.update(**data)
+#        # Run the active components update loop
+#        for component in self._components.itervalues():
+#            if component.active:
+#                component.update(now, **data)
 
     # Events from the overlord
     def event(self, event):
 # Main Singleton
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
-from ignifuga.Rect import Rect
+#from ignifuga.Rect import *
 from ignifuga.Singleton import Singleton
 from ignifuga.Log import *
 import sys, pickle, os, weakref, gc, platform, copy, base64
 
 class BACKENDS:
     sdl = 'sdl'
-    
-class REQUESTS:
-    loadImage = 'loadImage'
-#    loadSprite = 'loadSprite'
-    #dirtyRects = 'dirtyRects'
-    done = 'done'
-    skip = 'skip'
-    stop = 'stop'
-    nativeResolution = 'nativeResolution'
-    iterate = 'iterate'
-    sceneSize = 'sceneSize'
 
-from Task import Task
+# This needs to match the values defined in GameLoopBase.pxd
+
+REQUEST_NONE = 0x00000000
+REQUEST_DONE = 0x00000001
+REQUEST_SKIP = 0x00000002
+REQUEST_STOP = 0x00000004
+REQUEST_LOADIMAGE = 0x0000008
+REQUEST_ERROR = 0x80000000
+
+#from Task import Task
     
 class Event:
     class TYPE:
     zoom = 'zoom'
     scroll = 'scroll'
 
-def Renderer():
+def getRenderer():
     return Gilbert().renderer
 
 def Canvas():
 GameLoop = None
 DataManager = None
 _Canvas = None
-Target = None
+Renderer = None
 
 class GilbertPickler(pickle.Pickler):
     def memoize(self, obj):
     def __init__(self):
         pass
     
-    def init(self, backend, scenesFile, firstScene):
+    def init(self, backend, firstScene, scenesFile=None):
+        """
+        backend: 'sdl'
+        firstScene: The first scene ID (a string) in which case it'll be loaded from scenesFile or a Scene object
+        scenesFile: A File where to load scenes from
+        """
         self.backend = backend
         debug ('Initializing Gilbert Overlord')
 
         global GameLoop
         global DataManager
         global _Canvas
-        global Target
+        global Renderer
 
         self.platform = sys.platform
         if self.platform.startswith('linux'):
             from backends.sdl.GameLoop import GameLoop as gameloop
             from backends.sdl.DataManager import DataManager as datamanager
             from backends.sdl.Canvas import Canvas as canvas
-            from backends.sdl.Target import Target as target
+            from backends.sdl.Renderer import Renderer as renderer
             initializeBackend()
             GameLoop = gameloop
             DataManager = datamanager
             _Canvas = canvas
-            Target = target
+            Renderer = renderer
             debug('Backend %s initialized' % (backend,))
         else:
             error('Unknown backend %s. Aborting' % (backend,))
         self.entities = {}
         # These dictionaries keep weakrefs via WeakSet, contain the current scene entities
         self.entitiesByTag = {}
-        self.entitiesByZ = {}
+        self.entitiesByZ = []
 
         # These keep weakrefs in the key and via Task
-        self.loading = {}
-        self.running = {}
+        #self.loading = {}
+        #self.running = {}
 
         self._touches = {}
         self._touchCaptured = False
 
         self._freezeRenderer = False
 
-        
-        self.renderer = _Renderer(Target(width=options.width, height=options.height, fullscreen= not options.windowed, display=options.display))
+        self.renderer = Renderer(width=options.width, height=options.height, fullscreen= not options.windowed, display=options.display)
         self.dataManager = DataManager()
+        self.gameLoop = GameLoop()
 
         if not self.loadState():
             debug('Failed loading previous state')
-            scenes = self.dataManager.loadJsonFile(scenesFile)
-            self.loadScenes(scenes)
+            if scenesFile is not None:
+                self.loadScenesFromFile(scenesFile)
+
+            if isinstance(firstScene, Scene):
+                self.scenes[firstScene.id] = firstScene
+                self._firstScene = firstScene.id
+            else:
+                self._firstScene = firstScene
 ########################################################################################################################
 # SPLASH SCENE CODE
 # ANY MODIFICATION OF THE SCENE DEFINITION AND RELATED CODE AND ARTWORK RENDERS THE LICENSE TO USE THIS ENGINE VOID.
 ########################################################################################################################
-            self._firstScene = firstScene
             self.loadScene('splash', copy.deepcopy(SPLASH_SCENE))
+            #self.startFirstScene()
             if not self.startScene('splash'):
                 error('Could not load splash scene')
                 return
 
     def startLoop(self):
         """Set up the game loop"""
-        self.gameLoop = GameLoop()
         self.gameLoop.run()
 
         # Engine is exiting from here onwards
         """ End the game loop, free stuff """
         self.gameLoop.quit = True
 
-    def update(self, now=0, wrapup=False):
-        """ Update everything, then render the scene
-        now is the current time, specified in seconds
-        wrapup = True forces the update loop to be broken, all running entities eventually stop running
-        """
-        # Call the pre update so we can tally how long the whole frame processing took (logic+render)
-        if not wrapup:
-            self.renderer.preUpdate(now)
 
-        # Initialize objects
-        remove_entities = []
-        for entity_ref in self.loading.iterkeys():
-            task, req, data = self.loading[entity_ref]
-            if task != None:
-                task, req, data = self._processTask(task, req, data, now, wrapup, True)
-
-            if task != None:
-                self.loading[entity_ref] = (task, req, data)
-            else:
-                remove_entities.append(entity_ref)
-
-        for entity_ref in remove_entities:
-            del self.loading[entity_ref]
-            if not self.loading:
-                self._freezeRenderer  = False
-
-
-        # Update objects
-        remove_entities = []
-        for entity_ref in self.running.keys():
-            # This check may seem overkill, but there's situations where the ref may have been removed from self.running!
-            if entity_ref in self.running:
-                task1, req1, data1,task2,req2,data2 = self.running[entity_ref]
-                if task1 != None:
-                    task1, req1, data1 = self._processTask(task1, req1, data1, now, wrapup)
-
-                if task2 != None:
-                    task2, req2, data2 = self._processTask(task2, req2, data2, now, wrapup)
-
-                if task1 != None or task2 != None:
-                    self.running[entity_ref] = (task1, req1, data1,task2, req2, data2)
-                else:
-                    remove_entities.append(entity_ref)
-
-        for entity_ref in remove_entities:
-            del self.running[entity_ref]
-
-    def _processTask(self, task, req, data, now, wrapup, init=False):
-        if req == None:
-            req, data = task.wakeup({'now': now})
-            if init and req == REQUESTS.done and data is None:
-                # There was a problem with initialization, let's try again
-                task = Task(task.entity, task.entity().init, parent=Task.getcurrent())
-                return task, None, None
-
-        if req == REQUESTS.done:
-            if init:
-                # Entity is ready, start the update loop for it
-                if not wrapup:
-                    # Fire up the task to update the entity and the entity components
-                    task1 = Task(task.entity, task.entity().update, parent=Task.getcurrent())
-                    req1, data1 = task1.wakeup({'now': now})
-                    task2 = Task(task.entity, task.entity()._update, parent=Task.getcurrent())
-                    req2, data2 = task2.wakeup({'now': now})
-                    self.running[task.entity] = (task1, req, data1,task2, req2, data2)
-                    return (None,None,None)
-            else:
-                if wrapup:
-                    return None,None,None
-                else:
-                    # Restart the update loop
-                    task = Task(task.entity, task.runnable, parent=Task.getcurrent())
-                    req, data = task.wakeup({'now': now})
-                    return (task, req, data)
-        elif req == REQUESTS.skip:
-            # Normal operation continues
-            return (task, None, None)
-        elif req == REQUESTS.stop:
-            # Stop entity from updating
-            return None,None,None
-        elif req == REQUESTS.loadImage:
-            # Load an image
-            if data.has_key('url') and data['url'] != None:
-                # Try to load an image
-                img = self.dataManager.getImage(data['url'])
-                if img == None:
-                    return (task, req, data)
-                else:
-                    req, data = task.wakeup(img)
-                    return (task, req, data)
-            else:
-                # URL is invalid, just keep going
-                return (task, None, None)
-#            elif req == REQUESTS.loadSprite:
-#                # Load a sprite definition
-#                if data.has_key('url') and data['url'] != None:
-#                    # Try to load a sprite
-#                    sprite = self.dataManager.getSprite(data['url'])
-#                    if sprite == None:
-#                        self.loading[entity_ref] = (task, req, data)
-#                    else:
-#                        req, data = task.wakeup(sprite)
-#                        self.loading[entity_ref] = (task, req, data)
-#                else:
-#                    # URL is invalid, just keep going
-#                    self.loading[entity_ref] = (task, None, None)
-#            elif req == REQUESTS.nativeResolution:
-#                # Set the native resolution of the scene for scaling purpouses
-#                w,h, ar = data
-#                self.renderer.setNativeResolution(w,h, ar)
-#                self.loading[entity_ref] = (task, None, None)
-#            elif req == REQUESTS.sceneSize:
-#                # Set the size of the scene for scrolling purpouses
-#                w,h = data
-#                self.renderer.setSceneSize(w,h)
-#                self.loading[entity_ref] = (task, None, None)
-
-        else:
-            # Unrecognized request
-            return (task, None, None)
 
     def renderScene(self):
         # Render a new scene
         if not self._freezeRenderer:
             self.renderer.update()
 
-    def refreshEntityZ(self, entity):
-        """ Change the entity's z index ordering """
-        # Remove from the old z-index
-        if entity.id in self.loading:
-            return
-
-        self.hideEntity(entity)
-        # Add to the new z-index
-        new_z = entity.z
-        if new_z != None and 'viewable' in entity.tags:
-            if not new_z in self.entitiesByZ:
-                self.entitiesByZ[new_z] = weakref.WeakSet()
-            self.entitiesByZ[new_z].add(entity)
-
-    def hideEntity(self, entity):
-        """ Check all the Z layers, remove the entity from it"""
-        for z in self.entitiesByZ.keys():
-            entities = self.entitiesByZ[z]
-            if entity in entities:
-                entities.remove(entity)
-                if not entities:
-                    del self.entitiesByZ[z]
-                break
+#    def refreshEntityZ(self, entity):
+#        """ Change the entity's z index ordering """
+#        # Remove from the old z-index
+#        if entity.id in self.loading:
+#            return
+#
+#        self.hideEntity(entity)
+#        # Add to the new z-index
+#        if entity not in self.entitiesByZ:
+#            self.entitiesByZ.append(entity)
+#
+#        self.entitiesByZ.sort(key = lambda x: x.z)
+#
+#    def hideEntity(self, entity):
+#        """ Check all the Z layers, remove the entity from it"""
+#        if entity in self.entitiesByZ:
+#            self.entitiesByZ.remove(entity)
 
     def refreshEntityTags(self, entity, added_tags=[], removed_tags=[]):
         for tag in added_tags:
             if tag not in self.entitiesByTag:
-                self.entitiesByTag[tag] = weakref.WeakSet()
+                self.entitiesByTag[tag] = [] #set() #weakref.WeakSet()
             if entity not in self.entitiesByTag[tag]:
-                self.entitiesByTag[tag].add(entity)
+                self.entitiesByTag[tag].append(entity)
 
         for tag in removed_tags:
             if tag in self.entitiesByTag:
             self._lastEvent = event
 
             if not self._touchCaptured:
-                zindexs = self.entitiesByZ.keys()
-                if len(zindexs) >0:
-                    zindexs.sort(reverse=True)
-                for z in zindexs:
+                for entity in self.entitiesByZ:
+                    continuePropagation, captureEvent = entity.event(event)
+                    if captureEvent:
+                        self._touchCaptor = entity
+                        self._touchCaptured = True
                     if not continuePropagation:
                         break
-                    for entity in self.entitiesByZ[z]:
-                        continuePropagation, captureEvent = entity.event(event)
-                        if captureEvent:
-                            self._touchCaptor = entity
-                            self._touchCaptured = True
-                        if not continuePropagation:
-                            break
             elif self._touchCaptor is not None:
                 continuePropagation, captureEvent = self._touchCaptor.event(event)
                 if not captureEvent:
             
         elif event.ethereal:
             # Send the event to all entity until something stops it
-            zindexs = self.entitiesByZ.keys()
-            if len(zindexs) >0:
-                zindexs.sort(reverse=True)
-            for z in zindexs:
+            for entity in self.entitiesByZ:
+                continuePropagation, captureEvent = entity.event(event)
+                if captureEvent:
+                    self._touchCaptor = entity
+                    self._touchCaptured = True
                 if not continuePropagation:
                     break
-                for entity in self.entitiesByZ[z]:
-                    continuePropagation, captureEvent = entity.event(event)
-                    if captureEvent:
-                        self._touchCaptor = entity
-                        self._touchCaptured = True
-                    if not continuePropagation:
-                        break
 
             if continuePropagation:
                 if self.scene and self.scene.userCanZoom:
         """ Serialize the current status of the engine """
         debug('Waiting for entities to finish loading before saving state')
         tries = 0
-        while self.loading:
-            self.update()
-            tries += 1
-            if tries > 100:
-                debug('Still waiting for loading entities: %s' % self.loading)
-                tries = 0
+#        while self.loading:
+#            self.update()
+#            tries += 1
+#            if tries > 100:
+#                debug('Still waiting for loading entities: %s' % self.loading)
+#                tries = 0
 
 
         #f = open('ignifuga.state', 'w')
         for scene_id, scene_data in copy.deepcopy(data).iteritems():
             self.loadScene(scene_id, scene_data)
 
+    def loadScenesFromFile(self, scenesFile):
+        scenes = self.dataManager.loadJsonFile(scenesFile)
+        self.loadScenes(scenes)
+
     def loadScene(self, scene_id, scene_data):
         scene = Scene(id=scene_id, **scene_data)
         self.scenes[scene_id] = scene
             self.scene = self.scenes[scene_id]
             self.entities = self.scene.entities
             self.scene.sceneInit()
+            self.startEntity(self.scene)
             for entity in self.entities.itervalues():
                 self.startEntity(entity)
 
         debug('Waiting for entities to finish loading/running')
         tries = 0
         self._freezeRenderer = True
-        while self.loading or self.running:
-            self.update(wrapup=True)
-            tries += 1
-            if tries > 100:
-                debug('Still waiting for loading entities: %s' % self.loading)
-                debug('Still waiting for running entities: %s' % self.running)
-                tries = 0
+#        while self.loading or self.running:
+#            self.update(wrapup=True)
+#            tries += 1
+#            if tries > 100:
+#                debug('Still waiting for loading entities: %s' % self.loading)
+#                debug('Still waiting for running entities: %s' % self.running)
+#                tries = 0
 
 
         for entity in self.entities.values():
         del self.scene
         del self.entitiesByZ
         del self.entitiesByTag
-        del self.loading
-        del self.running
+        #del self.loading
+        #del self.running
 
         # Really remove nodes and data
         gc.collect()
 
         self.scene = None
         self.entities = {}
-        self.entitiesByZ = {}
+        self.entitiesByZ = []
         self.entitiesByTag = {}
-        self.loading = {}
-        self.running = {}
+        #self.loading = {}
+        #self.running = {}
 
         # Clean up cache
         # Do not clean the cache here, there may be useful data for other scenes -> self.dataManager.cleanup()
 
     def startEntity(self, entity):
         # Add it to the loading queue
-        wr = weakref.ref(entity)
-        task = Task(wr, entity.init, parent=Task.getcurrent())
-        self.loading[wr] = (task, None, None)
-        entity.loading = True
+        self.gameLoop.startEntity(entity)
 
     def stopEntity(self, entity):
         """ Remove node, release its data """
-
-        # Add it to the loading queue
-        wr = weakref.ref(entity)
-        if wr in self.loading:
-            del self.loading[wr]
-
-        self.hideEntity(entity)
-        self.refreshEntityTags(entity, [], entity.tags)
-
-        if wr in self.running:
-            del self.running[wr]
+        self.gameLoop.stopEntity(entity)
 
 
     def getEmbedded(self, url):
 
 
 # Gilbert imports
-from Renderer import Renderer as _Renderer
-from Log import Log
 from Entity import Entity
 from components import Component
 from Scene import Scene
 # Created 20101105
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
+# xcython: profile=True
+
 from ignifuga.Singleton import Singleton
 import sys
 
     cpdef bool fits(self, Rect other)
     cpdef scale(self, double scale_x=*, double scale_y=*)
     cpdef list cutout(self, Rect other)
+
+
+cpdef Rect rectFromRect(Rect r)
+cpdef Rect rectFromXYWH(double x, double y, double w, double h)
+cpdef Rect rectFromWH( double w, double h)
 #Permission to use this file is granted under the conditions of the Ignifuga Game Engine License
 #whose terms are available in the LICENSE file or at http://www.ignifuga.org/license
 
+
+# xcython: profile=True
+
 from cpython cimport bool
 
+
 cdef class Rect(object):
-    def __init__(self, xywh):
-        """
-        xywh must be a 2 or 4 tuple or a Rect instance.
-        """
-        if isinstance(xywh, Rect):
-            self.left = xywh.left
-            self.top = xywh.top
-            self.width = xywh.width
-            self.height = xywh.height
-        else:
-            if len(xywh) == 4:
-                self.width = float(xywh[2])
-                self.height = float(xywh[3])
-                self.left = float(xywh[0])
-                self.top = float(xywh[1])
-            elif len(xywh) == 2:
-                self.width = float(xywh[0])
-                self.height = float(xywh[1])
-                self.left = 0.0
-                self.top = 0.0
+#    def __init__(self, xywh):
+#        """
+#        xywh must be a 2 or 4 tuple or a Rect instance.
+#        """
+#        if isinstance(xywh, Rect):
+#            self.left = xywh.left
+#            self.top = xywh.top
+#            self.width = xywh.width
+#            self.height = xywh.height
+#        else:
+#            if len(xywh) == 4:
+#                self.width = float(xywh[2])
+#                self.height = float(xywh[3])
+#                self.left = float(xywh[0])
+#                self.top = float(xywh[1])
+#            elif len(xywh) == 2:
+#                self.width = float(xywh[0])
+#                self.height = float(xywh[1])
+#                self.left = 0.0
+#                self.top = 0.0
 
     def __repr__(self):
         return "%s((%s,%s,%s,%s))" % (self.__class__.__name__, self.left, self.top, self.width, self.height)
         
     cpdef Rect copy (self):
         """ Return a new copy of the rectangle """
-        return Rect(self)
-        
+        return rectFromRect(self)
+
     cdef bool intersects(self, other):
         """
         Test if a rect intersects with this rect.
         if self.left > other.left+other.width: return False
         if self.top > other.top + other.height: return False
         if self.left + self.width < other.left: return False
-        if self.top + self.width  < other.bottom: return False
+        if self.top + self.width  < (other.top + other.height - 1.0): return False
         return True 
 
     cpdef Rect intersection(self, Rect other):
         Return the intersection of this rect and other rect.
         Return None if no intersection.
         """
-        left = max((self.left, other.left))
-        top = max((self.top, other.top))
-        right = min((self.right, other.right))
-        bottom = min((self.bottom, other.bottom))
+        cdef double left, top, right, bottom, oright, obottom
+#        left = max((self.left, other.left))
+#        top = max((self.top, other.top))
+#        right = min((self.left + self.width, other.left + other.width)) - 1.0
+#        bottom = min((self.top + self.height , other.top + other.height)) - 1.0
+
+        left = self.left if self.left>other.left else other.left
+        top = self.top if self.top > other.top else other.top
+        right = self.left + self.width - 1.0
+        oright = other.left + other.width - 1.0
+        right = right if right < oright else oright
+        bottom = self.top + self.height - 1.0
+        obottom = other.top + other.height - 1.0
+        bottom = bottom if bottom < obottom else obottom
+
         if left > right or top > bottom: return None
-        return Rect((left,top,right-left+1,bottom-top+1))
+        return rectFromXYWH(left,top,right-left+1,bottom-top+1)
 
     cpdef bool contains(self, Rect other):
         """
         Return True if self contains other
         """
-        if other.left >= self.left and other.right <= self.right:
-            if other.top >= self.top and other.bottom <= self.bottom:
+        if other.left >= self.left and other.left + other.width <= self.left + self.width:
+            if other.top >= self.top and other.top + other.height <= self.top + self.height:
                 return True
         return False
 
             
         if self.intersects(other):
             # R1
-            r = Rect((self.left, self.top, other.left-self.left,self.bottom-self.top+1))
+            r = rectFromXYWH(self.left, self.top, other.left-self.left,self.bottom-self.top+1)
             if r.width > 0 and r.height > 0:
                 rects.append(r)
                 
             # R2
-            r = Rect((other.left, self.top, other.right-other.left+1,other.top-self.top))
+            r = rectFromXYWH(other.left, self.top, (other.left + other.width - 1.0)-other.left+1,other.top-self.top)
             if r.width > 0 and r.height > 0:
                 rects.append(r)
             
             # R3
-            r = Rect((other.right+1, self.top, self.right-other.right,self.bottom-self.top+1))
+            r = rectFromXYWH(other.left + other.width , self.top, (self.left + self.width - 1.0)-(other.left + other.width - 1.0),(self.top + self.height - 1.0)-self.top+1)
             if r.width > 0 and r.height > 0:
                 rects.append(r)
             
             # R4
-            r = Rect((other.left, other.bottom+1, other.right-other.left+1,self.bottom-other.bottom))
+            r = rectFromXYWH(other.left, other.top + other.height, (other.left + other.width - 1.0)-other.left+1,(self.top + self.height - 1.0)-(other.top + other.height - 1.0))
             if r.width > 0 and r.height > 0:
                 rects.append(r)
 
             return rects
         else:
-            return [Rect(self),]
+            return [rectFromRect(self)]
+
+
+
+cpdef Rect rectFromRect(Rect r):
+    newr = Rect()
+    newr.left = r.left
+    newr.top = r.top
+    newr.width = r.width
+    newr.height = r.height
+    return newr
+
+cpdef Rect rectFromXYWH(double x, double y, double w, double h):
+    newr = Rect()
+    newr.left = x
+    newr.top = y
+    newr.width = w
+    newr.height = h
+    return newr
+
+cpdef Rect rectFromWH( double w, double h):
+    newr = Rect()
+    newr.left = 0
+    newr.top = 0
+    newr.width = w
+    newr.height = h
+    return newr

Renderer.pxd

-#Copyright (c) 2010-2012, Gabriel Jacobo
-#All rights reserved.
-#Permission to use this file is granted under the conditions of the Ignifuga Game Engine License
-#whose terms are available in the LICENSE file or at http://www.ignifuga.org/license
-
-
-# Ignifuga Game Engine
-# Main Renderer
-# Backends available: SDL
-# Author: Gabriel Jacobo <gabriel@mdqinc.com>
-
-from ignifuga.backends.TargetBase cimport TargetBase
-from ignifuga.backends.CanvasBase cimport CanvasBase
-from cpython cimport bool
-
-cdef class Renderer:
-    cdef double frameTimestamp
-    cdef double frameLapse
-    cdef tuple nativeResolution
-    # Scale factor = screen/scene
-    cdef double _scale_x, _scale_y
-    cdef TargetBase _target
-    #cdef list dirtyRects
-    # Native scene resolution
-    cdef double _native_res_w, _native_res_h
-    # Native scene size
-    cdef double _native_size_w, _native_size_h
-    cdef bool _keep_aspect
-    # Scroll displacement in screen coordinates
-    cdef int _scroll_x, _scroll_y
-
-    cpdef update(self)
-    #cpdef dirty(self, int x, int y, int w, int h)
-    cpdef setNativeResolution(self, double w=*, double h=*, bool keep_aspect=*, bool autoscale=*)
-    cpdef setSceneSize(self, int w, int h)
-    cpdef _calculateScale(self, double scene_w, double scene_h, int screen_w, int screen_h, bool keep_aspect=*)
-    cpdef windowResized(self)
-    cpdef scrollBy(self, int deltax, int deltay)
-    cpdef scrollTo(self, int x, int y)
-    cpdef scaleBy(self, int delta)
-    cpdef scaleByFactor(self, double factor)
-    cpdef centerScene(self)
-    cpdef centerOnScenePoint(self, double sx, double sy)
-    cpdef centerOnScreenPoint(self, int sx, int sy)
-    cpdef tuple screenToScene(self, int sx, int sy)
-    cpdef tuple sceneToScreen(self, double sx, double sy)

Renderer.pyx

-#Copyright (c) 2010-2012, Gabriel Jacobo
-#All rights reserved.
-#Permission to use this file is granted under the conditions of the Ignifuga Game Engine License
-#whose terms are available in the LICENSE file or at http://www.ignifuga.org/license
-
-
-# Ignifuga Game Engine
-# Main Renderer
-# Backends available: SDL
-# Author: Gabriel Jacobo <gabriel@mdqinc.com>
-
-from ignifuga.Gilbert import BACKENDS, Gilbert, Event
-from ignifuga.Log import Log, debug
-
-from ignifuga.Singleton import Singleton
-from ignifuga.Rect cimport Rect
-
-from time import time as getTime
-import sys
-
-cdef class Renderer:
-    def __init__(self, target):
-        self.frameTimestamp = 0.0 # seconds since epoch
-        self.frameLapse = 0.0 # Time it takes to render a frame
-        self.nativeResolution = (None, None)
-        self._scale_x = 1.0
-        self._scale_y = 1.0
-        self._target = None
-        self._scroll_x = 0
-        self._scroll_y = 0
-        self._target = target
-
-
-
-    cpdef update(self):
-        """ Renders the whole screen in every frame, ignores dirty rectangle markings completely (easier for handling rotations, etc) """
-        cdef Rect nr, ir, screen
-        cdef int z,
-        cdef double extra, sx,sy,sw,sh,dx,dy,dw,dh
-        cdef CanvasBase canvas
-
-        if self.frameTimestamp == 0.0:
-            raise Exception ('You have to call preUpdate before calling update')
-
-        # In the following, screen coordinates refers to a set of coordinates that start in 0,0 and go to (screen width-1, screen height-1)
-        # Scene coordinates are the logical entity coordinates, which relate to the screen via scale and scroll modifiers.
-        # What we do here is basically put everything in scene coordinates first, see what we have to render, then move those rectangles back to screen coordinates to render them.
-
-        # Let's start building a rectangle that holds the part of the scene we want to show
-        # screen is the rectangle that holds the piece of scene that we will show. We still have to apply scaling to it.
-        screen_w = self.target.width
-        screen_h = self.target.height
-        screen = Rect((self._scroll_x, self._scroll_y, screen_w, screen_h))
-
-        # Apply the overall scale setting if needed.
-        if self._scale_x != 1.0 or self._scale_y != 1.0:
-            doScale = True
-            # Convert screen coordinates to unscaled absolute coordinates
-            screen.scale(1.0/self._scale_x, 1.0/self._scale_y)
-        else:
-            doScale = False
-
-        gilbert = Gilbert()
-        # Get a list of the z index of the entities in the scenes, we will traverse it in increasing order
-        zindexs = gilbert.entitiesByZ.keys()
-        #print zindexs
-        if len(zindexs) >0:
-            zindexs.sort()
-
-        for z in zindexs:
-            for entity in gilbert.entitiesByZ[z]:
-                #print "Rendering", entity.id
-
-                # Intersect the entity rectangle with the screen rectangle
-                # nr is in scene coordinates
-                nr = Rect(entity.getRect())
-                # ir is the intersection, in scene coordinates
-                if entity.angle != 0:
-                    # Expand the entity rect with some generous dimensions (to avoid having to calculate exactly how bigger it is)
-                    extra = nr.width if nr.width > nr.height else nr.height
-                    nr.left -= extra
-                    nr.top -= extra
-                    nr.height += extra
-                    nr.width += extra
-
-                ir = screen.intersection(nr)
-                if ir != None:
-                    # There's an intersection, go over the entity areas, and see what parts of those fall inside the intersected rect.
-                    # This areas is what we end up rendering.
-                    #nx, ny = entity.position
-
-
-                    # NO LONGER USED, KEPT HERE FOR A WHILE AS REFERENCE
-                    # THIS BIT OF CODE WAS CREATED WHEN WE SUPPORTED "deltak" bitmaps, now its all deltap or whole keyframe
-                    # =======================================================
-                    #for a in entity.getFrameAreas():
-                    # a is a frame area, it's format is [sx, sy, dx, dy, w, h]
-                    # sx,sy -> coordinates in the atlas
-                    # dx,dy -> entity coordinates where to put this
-                    # w,h -> size of the rectangle to blit
-                    # =======================================================
-
-                    sx,sy,sw,sh,dx,dy,dw,dh = entity.getRenderArea()
-                    #print entity.id, sx,sy,sw,sh,dx,dy,dw,dh
-                    # sx,sy  -> are the source coordinates in the sprite (0,0 as the sprite will always handle its own compositing)
-                    # sw,sh  -> are the dimensions of the source material/sprite
-                    # dx,dy  -> entity coordinates where to render
-                    # dw,dh  -> entity dimensions in scene coordinates
-
-
-                    # Create nr, a rectangle with the destination location in scene coordinates  (scene coords = entity coords+entity position)
-                    nr = Rect((dx, dy, dw, dh))
-
-                    if entity.angle == 0 and entity.flipv == False and entity.fliph == False:
-                        ir = screen.intersection(nr)
-                    else:
-                        ir = nr
-
-                    if ir != None:
-                        # ir is now the intersection of the frame area (moved to the proper location in the scene) with the screen rectangle, in scene coordinates
-                        src_r = ir.copy()
-                        dst_r = ir.copy()
-
-                        # src_r is in scene coordinates, created by intersecting the destination rectangle with the screen rectangle in scene coordinates
-                        # We need to move it to the proper position on the source canvas
-                        # We adjust it to entity coordinates by substracting the entity position
-                        # Then, we substract the dx,dy coordinates (as they were used to construct nr and we don't need those)
-                        # Finally we add sx,sy to put the rectangle in the correct position in the canvas
-                        # This operations as completed in one step, and we end up with a source rectangle properly intersected with r, in source canvas coordinates
-                        src_r.move(sx-dx, sy-dy)
-
-                        # Apply reverse scaling to the source rectangle
-                        #print 'pre', src_r, dst_r
-                        if dw != sw or dh != sh:
-                            src_r.scale(sw/dw, sh/dh)
-
-                        # dst_r is in scene coordinates, we will adjust it to screen coordinates
-                        # Now we apply the scale factor
-                        if doScale:
-                            #Scale the dst_r values
-                            dst_r.scale(self._scale_x, self._scale_y)
-
-                        # Apply scrolling
-                        dst_r.move(-self._scroll_x, -self._scroll_y)
-
-                        #print src_r, dst_r
-
-                        # Perform the blitting if the src and dst rectangles have w,h > 0
-                        if src_r.width > 0 and src_r.height >0 and dst_r.width>0 and dst_r.height > 0:
-                            if entity.center == None:
-                                self.target.blitCanvas(entity.canvas, dst_r.left, dst_r.top, dst_r.width, dst_r.height, src_r.left, src_r.top, src_r.width, src_r.height, entity.angle, False, 0, 0, (1 if entity.fliph else 0) + (2 if entity.flipv else 0) )
-                            else:
-                                self.target.blitCanvas(entity.canvas, dst_r.left, dst_r.top, dst_r.width, dst_r.height, src_r.left, src_r.top, src_r.width, src_r.height, entity.angle, True, entity.center[0], entity.center[1], (1 if entity.fliph else 0) + (2 if entity.flipv else 0))
-                            #raw_input("Press Enter to continue...")
-        self._target.flip()
-        # Calculate how long it's been since the pre update until now.
-        self.frameLapse = getTime() - self.frameTimestamp
-        self.frameTimestamp = 0.0
-    
-    def preUpdate(self, now):
-        """Mark the initiation time of the frame and save it"""
-        self.frameTimestamp = now
-        
-    property target:
-        def __get__(self):
-            """ Return the rendering target """
-            return self._target
-        def __set__(self, TargetBase new_target):
-            """ Set the rendering target """
-            self._target = new_target
-
-    property screenSize:
-        def __get__(self):
-            """ Return the width,height of the screen """
-            if self._target != None:
-                return self._target.width, self._target.height
-            else:
-                return 0,0
-            
-    property sceneSize:
-        def __get__(self):
-            """ Return the width,height of the scene """
-            return self._native_size_w, self._native_size_h
-        def __set__(self, value):
-            self.setSceneSize(value[0], value[1])
-
-    property scroll:
-        def __get__(self):
-            """ Return the scrolling of the scene in scene coordinates"""
-            return self._scroll_x, self._scroll_y
-        def __set__(self, value):
-            self.scrollTo(value[0], value[1])
-
-    property scale:
-        def __get__(self):
-            """ Return the scaling factor of the scene """
-            return self._scale_x, self._scale_y
-
-    def getTimestamp(self):
-        """ Return the current frame timestamp in ms """
-        return self.frameTimestamp
-    
-    def checkRate(self, lastTime, rate):
-        """ Check if for a given frame rate enough time has elapsed  """
-        return self.frameTimestamp - lastTime > 60.0/rate
-    
-    def checkLapse(self, lastTime, lapse):
-        """ Check that a given time lapse has passed """
-        return self.frameTimestamp - lastTime > lapse
-    
-    cpdef setNativeResolution(self, double w=-1.0, double h=-1.0, bool keep_aspect=True, bool autoscale=True):
-        """ This function receives the scene native resolution. Based on it, it sets the scaling factor to the screen to fit the scene """
-        self._native_res_w = w
-        self._native_res_h = h
-        self._keep_aspect = keep_aspect
-        if autoscale:
-            self._calculateScale(w,h,self.target.width, self.target.height, keep_aspect)
-        else:
-            self._calculateScale(self.target.width,self.target.height,self.target.width, self.target.height, keep_aspect)
-
-    cpdef centerScene(self):
-        """ Scroll the scene so it's centered on the screen"""
-        self.centerOnScenePoint(self._native_size_w/2.0, self._native_size_h/2.0)
-
-    cpdef centerOnScenePoint(self, double sx, double sy):
-        """ Center scene around the given scene point"""
-        cdef int ssx, ssy
-        ssx,ssy = self.sceneToScreen(sx,sy)
-        self.centerOnScreenPoint(ssx,ssy)
-
-    cpdef centerOnScreenPoint(self, int sx, int sy):
-        """ Center scene around the given screen point"""
-        self.scrollTo(sx-self.target.width/2,sy-self.target.height/2)
-
-    cpdef tuple screenToScene(self, int sx, int sy):
-        """ Scale a point in screen coordinates to scene coordinates """
-        return <double>(sx+self._scroll_x)/self._scale_x,<double>(sy+self._scroll_y)/self._scale_y
-
-    cpdef tuple sceneToScreen(self, double sx, double sy):
-        """ Scale a point in scene coordinates to screen coordinates """
-        return (sx*self._scale_x)-self._scroll_x,(sy*self._scale_y)-self._scroll_y
-
-    cpdef setSceneSize(self, int w, int h):
-        """ This function receives the scene size. Based on it, it controls the scrolling allowed """
-        self._native_size_w = w
-        self._native_size_h = h
-        
-    cpdef _calculateScale(self, double scene_w, double scene_h, int screen_w, int screen_h, bool keep_aspect=True):
-        cdef double sx, sy
-        if scene_w!=-1.0 and scene_h != -1.0:
-            sx = <double>screen_w/scene_w
-            sy = <double>screen_h/scene_h
-            if keep_aspect:
-                # Choose the higher scaling
-                if sx > sy:
-                    sy = sx
-                else:
-                    sx = sy
-            self._scale_x = sx
-            self._scale_y = sy
-        else:
-            self._scale_x = 1.0
-            self._scale_y = 1.0
-
-    cpdef windowResized(self):
-        """ Tell the target to update it's size """
-        screen_w = self.target.width
-        screen_h = self.target.height
-        self.target.updateSize()
-        #debug("windowResized: from %dx%d to %dx%d" %(screen_w, screen_h, self.target.width, self.target.height))
-
-        if screen_w != self.target.width or screen_h != self.target.height:
-            new_sx = <int>(self._scroll_x * self.target.width/screen_w if screen_w != 0 else 0)
-            new_sy = <int>(self._scroll_y * self.target.height/screen_h if screen_h != 0 else 0)
-            # Adjust scaling
-            self._calculateScale(self._native_res_w, self._native_res_h, self.target.width, self.target.height, self._keep_aspect)
-            # Always scroll after adjusting scale!
-            #debug("windowResized: requesting new scroll point %dx%d" %(new_sx,new_sy))
-            self.scrollTo(new_sx, new_sy)
-
-    cpdef scrollBy(self, int deltax, int deltay):
-        """ Scroll the screen by deltax,deltay. deltax/y are in screen coordinates"""
-        cdef int sx = self._scroll_x - deltax
-        cdef int sy = self._scroll_y - deltay
-
-        self.scrollTo(sx,sy)
-
-    cpdef scrollTo(self, int sx, int sy):
-        """ sx,sy are screen coordinates
-        They can range from 0 to the screen scaled max size of the scene minus the screen size
-        """
-        cdef int max_w, max_h
-        max_w = self._native_size_w*self._scale_x - self.target.width
-        max_h = self._native_size_h*self._scale_y - self.target.height
-        #debug("GOT A REQUEST TO SCROLL TO %dx%d" % (sx,sy))
-
-        if sx < 0:
-            sx = 0
-        elif sx > max_w:
-            sx = max_w
-
-        if sy < 0:
-            sy = 0
-        elif sy > max_h:
-            sy = max_h
-
-        if self._scroll_x != sx or self._scroll_y != sy:
-            #debug("SCROLLING TO %dx%d" % (sx,sy))
-            self._scroll_x = sx
-            self._scroll_y = sy
-            Gilbert().reportEvent(Event(Event.TYPE.scroll, self._scroll_x, self._scroll_y))
-
-    cpdef scaleBy(self, int delta):
-        """ delta is a value in pixel area (width*height)"""
-        cdef double factor = <double> delta / <double> (self.target.width*self.target.height)
-        self.scaleByFactor(1.0+factor)
-
-    cpdef scaleByFactor(self, double factor):
-        """ Apply a scaling factor"""
-        cdef double scale_x = self._scale_x * factor
-        cdef double scale_y = self._scale_y * factor
-
-        if self._native_size_w*scale_x < self.target.width:
-            scale_x = self.target.width / self._native_size_w
-
-        if self._native_size_h*scale_y < self.target.height:
-            scale_y = self.target.height / self._native_size_h
-
-        if self._keep_aspect:
-            if scale_x > scale_y:
-                scale_y = scale_x
-            else:
-                scale_x = scale_y
-
-        self._scale_x = scale_x
-        self._scale_y = scale_y
-
-        # Adjust scrolling if needed
-        self.scrollBy(0,0)
-
-
-
-# OLD UPDATE ROUTINE THAT's DIRTY RECT BASED. KEPT HERE FOR FUTURE GENERATIONS ENJOYMENT ¿?
-
-    #cpdef update(self):
-    #""" Update the screen by rendering the entities that intersect the dirty rectangles """
-    #cdef Rect nr, ir, r
-    #cdef int z
-
-    #if self.frameTimestamp == 0.0:
-    #raise Exception ('You have to call preUpdate before calling update')
-
-    #if self._target.isDoubleBuffered:
-    ## Double buffered systems force us to draw all the screen in every frame as there's no delta updating possible.
-    #self.dirtyAll()
-
-    ## In the following, screen coordinates refers to a set of coordinates that start in 0,0 and go to (screen width-1, screen height-1)
-    ## Scene coordinates are the logical entity coordinates, which relate to the screen via scale and scroll modifiers.
-    ## What we do here is basically put everything in scene coordinates first, see what we have to render, then move those rectangles back to screen coordinates to render them.
-
-    ## Let's start building a rectangle that holds the part of the scene we want to show
-    ## screen is the rectangle that holds the piece of scene that we will show. We still have to apply scaling to it.
-    #screen_w = self.target.width
-    #screen_h = self.target.height
-    #screen = Rect((self._scroll_x, self._scroll_y, screen_w, screen_h))
-
-    #rects = []
-
-    ## Apply the overall scale setting if needed.
-    #if self._scale_x != 1.0 or self._scale_y != 1.0:
-    #doScale = True
-    ## Convert screen coordinates to unscaled absolute coordinates
-    #screen.scale(1.0/self._scale_x, 1.0/self._scale_y)
-    #else:
-    #doScale = False
-
-    ## At this point, screen contains the rectangle in scene coordinates that we will show, everything that falls inside it gets on the screen.
-    ## Now we get all the dirty rectangles reported by the entities, and we determine which ones intersect with the screen, discarding everything else.
-    #if self.dirtyRects != None:
-    #for dr in self.dirtyRects:
-    ## dr is in scene coordinates
-    ## Intersect the rect with the screen rectangle in scene coordinates
-    #ir = screen.intersection(Rect(dr))
-    #if ir != None:
-    ## There's some intersection, append it to the list of rectangles to be rendered.
-    #rects.append(ir)
-    #else:
-    ## Set all the screen as dirty
-    #rects.append(screen)
-
-    #gilbert = Gilbert()
-    ## Get a list of the z index of the entities in the scenes, we will traverse it in increasing order
-    #zindexs = gilbert.entitiesByZ.keys()
-    #if len(zindexs) >0:
-    #zindexs.sort()
-
-    ## Iterate over the dirty rects that fall on the viewable area and draw them
-    #for r in rects:
-    ##print "DIRTY RECTANGLE:", r
-    ## r is in scene coordinates, already intersected with the scaled & scrolled screen rectangle
-    #for z in zindexs:
-    #for entity in gilbert.entitiesByZ[z]:
-    ## Intersect the entity rectangle with the dirty rectangle
-    ## nr is in scene coordinates
-    #nr = Rect(entity.getRect())
-    ##print entity.id, 'nr: ', nr, 'r:', r
-    ## ir is the intersection, in scene coordinates
-    #ir = r.intersection(nr)
-    ##print "Intersect r ", r, " with nr ", nr, " results in ", ir
-    #if ir != None:
-    ## There's an intersection, go over the entity areas, and see what parts of those fall inside the intersected rect.
-    ## This areas is what we end up rendering.
-    #nx, ny = entity.position
-    #for a in entity.getFrameAreas():
-    ## a is a frame area, it's format is [sx, sy, dx, dy, w, h]
-    ## sx,sy -> coordinates in the atlas
-    ## dx,dy -> entity coordinates where to put this
-    ## w,h -> size of the rectangle to blit
-    #sx,sy,dx,dy,w,h = a
-
-
-    ## Create nr, a rectangle with the destination location in scene coordinates  (scene coords = entity coords+entity position)
-    #nr = Rect((dx+nx, dy+ny, w, h))
-
-    ##print entity.id, ' r:', r, ' nr :', nr, 'Frame Area:', sx,sy,dx,dy,w,h
-
-    #ir = r.intersection(nr)
-    #if ir != None:
-    ## ir is now the intersection of the frame area (moved to the proper location in the scene) with the dirty rectangle, in scene coordinates
-    #src_r = ir.copy()
-    #dst_r = ir.copy()
-
-    ## src_r is in scene coordinates, created by intersecting the destination rectangle with the dirty rectangle in scene coordinates
-    ## We need to move it to the proper position on the source canvas
-    ## We adjust it to entity coordinates by substracting the entity position
-    ## Then, we substract the dx,dy coordinates (as they were used to construct nr and we don't need those)
-    ## Finally we add sx,sy to put the rectangle in the correct position in the canvas
-    ## This operations as completed in one step, and we end up with a source rectangle properly intersected with r, in source canvas coordinates
-    #src_r.move(sx-nx-dx, sy-ny-dy)
-
-    ## dst_r is in scene coordinates, we will adjust it to screen coordinates
-    ## Now we apply the scale factor
-    #if doScale:
-    ##Scale the dst_r values
-    #dst_r.scale(self._scale_x, self._scale_y)
-
-    ## Apply scrolling
-    #dst_r.move(-self._scroll_x, -self._scroll_y)
-
-    ## Perform the blitting if the src and dst rectangles have w,h > 0
-    #if src_r.width > 0 and src_r.height >0 and dst_r.width>0 and dst_r.height > 0:
-    #if entity.center == None:
-    #self.target.blitCanvas(entity.canvas, dst_r.left, dst_r.top, dst_r.width, dst_r.height, src_r.left, src_r.top, src_r.width, src_r.height, entity.angle, False, 0, 0, (1 if entity.fliph else 0) + (2 if entity.flipv else 0) )
-    #else:
-    #self.target.blitCanvas(entity.canvas, dst_r.left, dst_r.top, dst_r.width, dst_r.height, src_r.left, src_r.top, src_r.width, src_r.height, entity.angle, True, entity.center[0], entity.center[1], (1 if entity.fliph else 0) + (2 if entity.flipv else 0))
-    ##raw_input("Press Enter to continue...")
-    #self._target.flip()
-    #self.dirtyNone()
-    ## Calculate how long it's been since the pre update until now.
-    #self.frameLapse = getTime() - self.frameTimestamp
-    #self.frameTimestamp = 0.0
-
-#   def reset(self):
-#            """ Reset the renderer, mark everything as clean """
-#        self.dirtyNone()
-#
-#   cpdef dirty(self, int x, int y, int w, int h):
-#        """ Mark the area that starts at x,y with size w,h as dirty """
-#        if self.dirtyRects != None:
-#            self.dirtyRects.append((x,y,w,h))
-#
-#   def dirtyAll(self):
-#        """ Dirty up all the screen """
-#        self.dirtyRects = None
-#
-#   def dirtyNone(self):
-#        """ Remove all dirty rectangles """
-#        self.dirtyRects = []
-#    property needsRects:
-#        def __get__(self):
-#            """ Answer whether dirty rectangles are needed or discarded (due to double buffering for example) """
-#            return not self._target.isDoubleBuffered
             '_size': {'width': None, 'height': None}
         })
         # Add the Scene entity
-        self.entities[self.id] = self
+        #self.entities[self.id] = self
 
         super(Scene, self).__init__(**data)
 
         if self._autoCenter:
             Gilbert().renderer.centerScene()
 
-        #print "SCENEINIT ", self.id, self.entities
-
 #    def init(self,data={}):
 #        """ Initialize the required external data """
 #        super(Scene, self).init(data)
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
 import greenlet
-from ignifuga.Gilbert import REQUESTS
+from ignifuga.Gilbert import REQUEST_DONE, REQUEST_LOADIMAGE, REQUEST_ERROR, REQUEST_STOP
 
-class Task(greenlet.greenlet):
-    def __init__(self, entity_wr, run=None, parent=None):
-        # A ref or weakref to the associated entity
-        self.entity = entity_wr
-        self.runnable = run
-        super(Task, self).__init__(run,parent)
+#class Task(greenlet.greenlet):
+#    def __init__(self, entity_wr, run=None, parent=None):
+#        # A ref or weakref to the associated entity
+#        self.entity = entity_wr
+#        self.runnable = run
+#        super(Task, self).__init__(run,parent)
+#
+#    def wakeup(self, data=None):
+#        value = self.switch(data)
+#        if self.dead:
+#            req = REQUESTS.done
+#            data = value
+#        else:
+#            req, data = value
+#
+#        return req,data
+#
+#
+def DONE(data=None):
+    g = greenlet.getcurrent()
+    return g.parent.switch((REQUEST_DONE, data))
 
-    def wakeup(self, data=None):
-        value = self.switch(data)
-        if self.dead:
-            req = REQUESTS.done
-            data = value
-        else:
-            req, data = value
-            
-        return req,data
-        
+def ERROR(data=None):
+    g = greenlet.getcurrent()
+    return g.parent.switch((REQUEST_ERROR, data))
 
-def DONE(entity):
+def STOP(data=None):
     g = greenlet.getcurrent()
-    return g.parent.switch((REQUESTS.done, None))
-    
+    return g.parent.switch((REQUEST_STOP, data))
+
 def LOAD_IMAGE(url):
     g = greenlet.getcurrent()
-    return g.parent.switch((REQUESTS.loadImage, {'url': url}))
-    
+    return g.parent.switch((REQUEST_LOADIMAGE, {'url': url}))
+
 #def LOAD_SPRITE(url):
 #    g = greenlet.getcurrent()
 #    return g.parent.switch((REQUESTS.loadSprite, {'url': url}))
-    
-    
+
+
 #def NATIVE_RESOLUTION(w, h, keep_aspect):
 #    g = greenlet.getcurrent()
 #    return g.parent.switch((REQUESTS.nativeResolution, (w, h, keep_aspect)))
 #    g = greenlet.getcurrent()
 #    return g.parent.switch((REQUESTS.sceneSize, (w, h)))
 
-    
+
 #def DIRTY_RECTS (rects):
 #    g = greenlet.getcurrent()
 #    return g.parent.switch((REQUESTS.dirtyRects, rects))

backends/CanvasBase.pyx

 # Canvas Base
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
+# xcython: profile=True
+
 from cpython cimport bool
 
 cdef class CanvasBase (object):

backends/FontBase.pyx

 # Data Manager Base
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
+# xcython: profile=True
+
 cdef class FontBase (object):
     pass

backends/GameLoopBase.pxd

 # Game Loop
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
-from cpython cimport bool
+from cpython cimport *
+from libc.stdlib cimport *
+from libc.string cimport *
+from libcpp.map cimport *
+from libcpp.deque cimport *
+from libcpp.pair cimport *
+
+cdef extern from "Python.h":
+    struct _frame
+
+cdef extern from "Modules/greenlet.h":
+    cdef struct _greenlet
+    cdef struct _greenlet:
+        PyObject head
+        char* stack_start
+        char* stack_stop
+        char* stack_copy
+        long stack_saved
+        _greenlet* stack_prev
+        _greenlet* parent
+        PyObject* run_info
+        _frame* top_frame
+        int recursion_depth
+        PyObject* weakreflist
+
+    ctypedef _greenlet PyGreenlet
+    PyGreenlet * PyGreenlet_New(PyObject *run, PyGreenlet *parent)
+    PyGreenlet * PyGreenlet_GetCurrent()
+    PyObject * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args, PyObject *kwargs)
+    void PyGreenlet_Import()
+
+ctypedef enum REQUESTS:
+    REQUEST_NONE = 0x00000000
+    REQUEST_DONE = 0x00000001
+    REQUEST_SKIP = 0x00000002
+    REQUEST_STOP = 0x00000004
+    REQUEST_LOADIMAGE = 0x0000008
+    REQUEST_ERROR = 0x80000000
+
+cdef struct _Task:
+    PyGreenlet *greenlet
+    REQUESTS req
+    PyObject *entity
+    PyObject *runnable
+    PyObject *data
+    bint release
+
+ctypedef _Task*  _Task_p
+
+cdef struct _EntityTasks:
+    _Task *loading
+    _Task *running
+
+ctypedef PyObject* PyObject_p
+
+ctypedef map[PyObject_p, _EntityTasks].iterator entities_iterator
+ctypedef deque[_Task_p].iterator task_iterator
 
 cdef class GameLoopBase(object):
-    cdef public bool quit, paused
+    cdef public bint quit, paused, freezeRenderer
     cdef public double _fps, _interval
     cdef str platform
+    cdef deque[_Task_p] *loading
+    cdef deque[_Task_p] *running
+    cdef map[PyObject_p, _EntityTasks] *entities
+    cdef deque[_Task_p] *free_tasks
+    cdef readonly int frame_time
+
+    cdef bint _allocTasks(self, int num=*)
+    cpdef startEntity(self, entity)
+    cpdef startComponent(self, component)
+    cpdef bint stopEntity(self, entity)
+    cpdef bint stopComponent(self, component)
+    cpdef update(self, int now=*, bint wrapup=*)
+    cdef bint _doSwitch(self, _Task *task, PyObject *args, PyObject *kwargs)
+    cdef bint _processTask(self, _Task *task, int now=*, bint wrapup=*, bint init=*)

backends/GameLoopBase.pyx

 # Game Loop
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
+# xcython: profile=True
+from cython.operator cimport dereference as deref, preincrement as inc #dereference and increment operators
+from ignifuga.Gilbert import Gilbert
+
+
+cdef bint isdead(PyGreenlet* greenlet):
+    ##define PyGreenlet_STARTED(op)    (((PyGreenlet*)(op))->stack_stop != NULL)
+    ##define PyGreenlet_ACTIVE(op)     (((PyGreenlet*)(op))->stack_start != NULL)
+
+    #if (PyGreenlet_ACTIVE(self) || !PyGreenlet_STARTED(self)):
+    if greenlet.stack_start != NULL or greenlet.stack_stop == NULL:
+        return False
+
+    return True
+
+
 cdef class GameLoopBase(object):
     def __init__(self, fps = 30.0):
-        # SDL should be initialized at this point when Target was instantiated
+        # SDL should be initialized at this point when Renderer was instantiated
         self.quit = False
         self.fps = fps
+        self.frame_time = 0
+
+        self.loading = new deque[_Task_p]()
+        self.running = new deque[_Task_p]()
+        self.entities = new map[PyObject_p, _EntityTasks]()
+        self.free_tasks = new deque[_Task_p]()
+
+        PyGreenlet_Import()
+
+    def __dealloc__(self):
+        print "Releasing Game Loop data"
+
+        cdef _Task *task
+        cdef entities_iterator iter
+        cdef _EntityTasks *et
+
+        # Release unused sprites
+        while not self.free_tasks.empty():
+            task = self.free_tasks.back()
+            free(task)
+            self.free_tasks.pop_back()
+
+        # Release sprites in use
+        iter = self.entities.begin()
+        while iter != self.entities.end():
+            et = &deref(iter).second
+            if et.loading != NULL:
+                Py_XDECREF(et.loading.data)
+                Py_XDECREF(<PyObject*>et.loading.greenlet)
+                Py_XDECREF(et.loading.runnable)
+                Py_XDECREF(et.loading.entity)
+                free(et.loading)
+            if et.running != NULL:
+                Py_XDECREF(et.running.data)
+                Py_XDECREF(<PyObject*>et.running.greenlet)
+                Py_XDECREF(et.running.runnable)
+                Py_XDECREF(et.running.entity)
+                free(et.running)
+
+        del self.free_tasks
+        del self.loading
+        del self.running
+        del self.entities
+
+
+    cdef bint _allocTasks(self, int num=100):
+        cdef size_t nbytes = sizeof(_Task)*num
+        cdef _Task *tasks = <_Task*>malloc(nbytes)
+        cdef int i
+
+        if tasks != NULL:
+            memset(<void *>tasks, 0, nbytes)
+            for i in range(0,num):
+                self.free_tasks.push_back(&tasks[i])
+
+            return True
+
+        return False
 
     def run(self):
         raise Exception('not implemented')
         def __set__(self, fps):
             self._fps = float(fps)
             self._interval = 1000.0 / fps
-        
+
+    cpdef startEntity(self, entity):
+        cdef _Task *task
+        cdef entities_iterator iter
+        cdef PyObject *obj = <PyObject*> entity
+        cdef _EntityTasks *et = NULL, newtask
+
+        if self.free_tasks.empty():
+            self._allocTasks()
+
+        if not self.free_tasks.empty():
+            task = self.free_tasks.back()
+            self.free_tasks.pop_back()
+            task.req = REQUEST_NONE
+            task.entity = obj
+            Py_XINCREF(task.entity)
+            # Note: Got to do this assignment with an intermediary object, otherwise Cython just can't take it!
+            runnable = entity.init
+            task.runnable = <PyObject*>runnable
+            Py_XINCREF(task.runnable)
+            task.greenlet = PyGreenlet_New(task.runnable, NULL )
+            Py_XINCREF(<PyObject*>task.greenlet)
+            task.data = NULL
+            self.loading.push_back(task)
+
+            iter = self.entities.find(obj)
+            if iter == self.entities.end():
+                self.entities.insert(pair[PyObject_p,_EntityTasks](obj,newtask))
+                iter = self.entities.find(obj)
+
+            et = &(deref(iter).second)
+            et.loading = task
+            et.running = NULL
+
+    cpdef startComponent(self, component):
+        """ Components hit the ground running, their initialization was handled by their entity"""
+        cdef _Task *task
+        cdef entities_iterator iter
+        cdef PyObject *obj = <PyObject*> component
+        cdef _EntityTasks *et = NULL, newtask
+
+        if self.free_tasks.empty():
+            self._allocTasks()
+
+        if not self.free_tasks.empty():
+            task = self.free_tasks.back()
+            self.free_tasks.pop_back()
+            task.req = REQUEST_NONE
+            task.entity = obj
+            Py_XINCREF(task.entity)
+            # Note: Got to do this assignment with an intermediary object, otherwise Cython just can't take it!
+            runnable = component.update
+            task.runnable = <PyObject*>runnable
+            Py_XINCREF(task.runnable)
+            task.greenlet = PyGreenlet_New(task.runnable, NULL )
+            Py_XINCREF(<PyObject*>task.greenlet)
+            task.data = NULL
+            self.running.push_back(task)
+
+            iter = self.entities.find(obj)
+            if iter == self.entities.end():
+                self.entities.insert(pair[PyObject_p,_EntityTasks](obj,newtask))
+                iter = self.entities.find(obj)
+
+            et = &(deref(iter).second)
+            et.loading = NULL
+            et.running = task
+
+    cpdef bint stopEntity(self, entity):
+        cdef _Task *task
+        cdef entities_iterator iter
+        cdef task_iterator titer
+        cdef _EntityTasks *et
+        cdef PyObject *obj = <PyObject*> entity
+        cdef bint eraseEntity = True
+
+        # Release tasks in use
+        iter = self.entities.find(obj)
+        if iter != self.entities.end():
+            et = &deref(iter).second
+            # The tasks may be in use, if that's the case we mark them for release and wait for the update loop to release them and call us back
+            if et.loading != NULL:
+                et.loading.release = True
+                eraseEntity = False
+            if et.running != NULL:
+                et.running.release = True
+                eraseEntity = False
+
+            if eraseEntity:
+                self.entities.erase(iter)
+            return True
+
+        return False
+
+    cpdef bint stopComponent(self, component):
+        return self.stopEntity(component)
+
+
+    cpdef update(self, int now=0, bint wrapup=False):
+        """ Update everything, then render the scene
+        now is the current time, specified in milliseconds
+        wrapup = True forces the update loop to be broken, all running entities eventually stop running
+        """
+        #cdef deque[_Task_p] remove_entities = new deque[_Task_p]()
+        cdef _Task *task, *task_aux
+        cdef _EntityTasks *et
+        cdef entities_iterator eiter
+        cdef task_iterator iter
+        cdef PyObject *entity
+
+        # Initialize objects
+        iter = self.loading.begin()
+        while iter != self.loading.end():
+            task = deref(iter)
+
+            if task.release or not self._processTask(task, now, wrapup, True):
+                # Remove the task from the loading deque, start it in the running deque
+                iter = self.loading.erase(iter)
+
+                # No need to double check here, task.entity was added in self.entities when startEntity was called
+                eiter = self.entities.find(task.entity)
+                et = &(deref(eiter).second)
+                et.loading = NULL
+                if et.running == NULL and et.loading == NULL:
+                    obj = <object>task.entity
+                    self.stopEntity(obj)
+
+                # Release the reference we held to data
+                entity = task.entity
+                Py_XDECREF(task.data)
+                Py_XDECREF(<PyObject*>task.greenlet)
+                Py_XDECREF(task.runnable)
+                Py_XDECREF(task.entity)
+                task.data = NULL
+                task.greenlet = NULL
+                task.runnable = NULL
+                task.entity = NULL
+                task.release = False
+
+                if wrapup:
+                    self.free_tasks.push_back(task)
+                else:
+                    # Reuse the task pointer for the first running task of the entity
+                    task.entity = entity
+                    Py_XINCREF(task.entity)
+                    # Note: Got to do this assignment with an intermediary object, otherwise Cython just can't take it!
+                    tentity = <object>task.entity
+                    runnable = tentity.update
+                    task.runnable = <PyObject*>runnable
+                    Py_XINCREF(task.runnable)
+                    task.greenlet = PyGreenlet_New(task.runnable, NULL)
+                    Py_XINCREF(<PyObject*>task.greenlet)
+                    task.req = REQUEST_NONE
+                    task.data = NULL
+                    self.running.push_back(task)
+                    et.running = task
+
+                    if self.free_tasks.empty():
+                        self._allocTasks()
+            else:
+                # Someone may have deleted a loading task in the middle of this!
+                if iter == self.loading.end():
+                    break
+                inc(iter)
+
+        if self.loading.empty() and self.freezeRenderer:
+            self.freezeRenderer = False
+
+        # Update objects
+        iter = self.running.begin()
+        while iter != self.running.end():
+            task = deref(iter)
+            if task.release or not self._processTask(task, now, wrapup, False):
+                # Remove the task from the indexes
+
+                # No need to double check here, task.entity was added in self.entities when startEntity was called
+                eiter = self.entities.find(task.entity)
+                et = &(deref(eiter).second)
+                et.running = NULL
+                if et.running == NULL and et.loading == NULL:
+                    obj = <object>task.entity
+                    self.stopEntity(obj)
+
+                # Release the reference we held to data
+                Py_XDECREF(task.data)
+                Py_XDECREF(<PyObject*>task.greenlet)
+                Py_XDECREF(task.runnable)
+                Py_XDECREF(task.entity)
+                task.data = NULL
+                task.greenlet = NULL
+                task.runnable = NULL
+                task.entity = NULL
+
+                iter = self.running.erase(iter)
+                task.release = False
+                self.free_tasks.push_back(task)
+            else:
+                # Someone may have deleted a loading task in the middle of this!
+                if iter == self.running.end():
+                    break
+                inc(iter)
+
+
+    cdef bint _doSwitch(self, _Task *task, PyObject *args, PyObject *kwargs):
+        cdef PyObject *retp = NULL
+
+        # Switch to the greenlet
+        retp = PyGreenlet_Switch(task.greenlet, args, kwargs)
+        if task.release:
+            # The task was marked for release at some point during the switch, don't use it further
+            return False
+        if isdead(task.greenlet):
+            # The greenlet is dead, assume it was done
+            task.req = REQUEST_DONE
+            Py_XDECREF(task.data)
+            task.data = NULL
+            return True
+
+        if retp != NULL:
+            Py_XINCREF(retp)
+            ret = <object>retp
+            task.req = <REQUESTS> ret[0]
+            Py_XDECREF(task.data)
+            task.data = <PyObject*> ret[1]
+            Py_XINCREF(task.data)
+            Py_XDECREF(retp)
+            return True
+
+        return False
+
+
+    cdef bint _processTask(self, _Task *task, int now=0, bint wrapup=False, bint init=False):
+        cdef PyObject *args, *kwargs
+        if init:
+            # Init functions have the format self.init(**data), so we pass now in the kwargs
+            kw_data = {'now':now}
+            kwargs = <PyObject*> kw_data
+            args = NULL
+        else:
+            # Update functions have the format self.update(now, **data), so we pass now as a arg
+            kw_data = {}
+            kwargs = <PyObject*>kw_data
+            data = (now,)
+            args = <PyObject*>data
+
+        cdef PyObject *retp
+        if task.req == REQUEST_NONE:
+            if self._doSwitch(task, args, kwargs):
+                if init and task.req == REQUEST_ERROR:
+                    # There was a problem with initialization, let's try again
+                    Py_XDECREF(<PyObject*>task.greenlet)
+                    task.req = REQUEST_NONE
+                    task.greenlet = PyGreenlet_New(task.runnable, NULL)
+                    Py_XINCREF(<PyObject*>task.greenlet)
+                    return True
+            else:
+                return False
+
+        if task.req == REQUEST_DONE:
+            if init:
+                # Entity is ready, start the update loop for it
+                return False
+            else:
+                if wrapup:
+                    return False
+                else:
+                    # Restart the update loop
+                    Py_XDECREF(<PyObject*>task.greenlet)
+                    task.greenlet = PyGreenlet_New(task.runnable, NULL)
+                    Py_XINCREF(<PyObject*>task.greenlet)
+                    return self._doSwitch(task, args, kwargs)
+        elif task.req == REQUEST_SKIP:
+            # Normal operation continues
+            task.req = REQUEST_NONE
+            return True
+        elif task.req == REQUEST_STOP:
+            # Stop entity from updating
+            return False
+        elif task.req == REQUEST_LOADIMAGE:
+            # Load an image
+            data = <object>task.data
+            if data.has_key('url') and data['url'] != None:
+                # Try to load an image
+                img = (Gilbert().dataManager.getImage(data['url']),)
+                if img is not None:
+                    return self._doSwitch(task, <PyObject*>img, NULL)
+                return True
+            else:
+                # URL is invalid, just keep going
+                task.req = REQUEST_NONE
+                return True
+        else:
+            # Unrecognized request
+            return self._doSwitch(task, args, kwargs)
+
+

backends/TargetBase.pxd

-#Copyright (c) 2010-2012, Gabriel Jacobo
-#All rights reserved.
-#Permission to use this file is granted under the conditions of the Ignifuga Game Engine License
-#whose terms are available in the LICENSE file or at http://www.ignifuga.org/license
-
-# Ignifuga Game Engine
-# SDL Render Target
-# Author: Gabriel Jacobo <gabriel@mdqinc.com>
-from cpython cimport bool
-from ignifuga.backends.CanvasBase cimport CanvasBase
-
-cdef class TargetBase (object):    
-    cdef int _width, _height
-    cdef bool _fullscreen
-    cdef str platform
-
-    cpdef clear(self, x, y, w, h)
-    cpdef clearRect(self, rect)
-    cpdef blitCanvas(self, CanvasBase canvas, int dx=*, int dy=*, int dw=*, int dh=*, int sx=*, int sy=*, int sw=*, int sh=*, double angle=*, bool offCenter=*, int centerx=*, int centery=*, int flip=*)
-    cpdef flip(self)
-    cpdef isVisible(self)

backends/TargetBase.pyx

-#Copyright (c) 2010-2012, Gabriel Jacobo
-#All rights reserved.
-#Permission to use this file is granted under the conditions of the Ignifuga Game Engine License
-#whose terms are available in the LICENSE file or at http://www.ignifuga.org/license
-
-# Ignifuga Game Engine
-# SDL Render Target
-# Author: Gabriel Jacobo <gabriel@mdqinc.com>
-
-cdef class TargetBase (object):
-    def __init__ (self, width=1280, height=800, **kwargs):
-        raise Exception('method not implemented')
-
-    @property
-    def width(self):
-        return self._width
-    
-    @property
-    def height(self):
-        return self._height
-
-    @property
-    def isDoubleBuffered(self):
-        return False
-
-    cpdef clear(self, x, y, w, h):
-        raise Exception('method not implemented')
-
-    cpdef clearRect(self, rect):
-        raise Exception('method not implemented')
-
-    cpdef blitCanvas(self, CanvasBase canvas, int dx=0, int dy=0, int dw=-1, int dh=-1, int sx=0, int sy=0, int sw=-1, int sh=-1, double angle=0, bool offCenter=False, int centerx=0, int centery=0, int flip=0 ):
-        raise Exception('method not implemented')
-
-    cpdef flip(self):
-        raise Exception('method not implemented')
-
-    cpdef isVisible(self):
-        raise Exception('method not implemented')
-
-    property visible:
-        def __get__(self):
-            return self.isVisible()

backends/sdl/Canvas.pyx

 # SDL 2D Canvas
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
+# xcython: profile=True
+
 from ignifuga.Log import debug, info, error
-from ignifuga.Gilbert import Gilbert, Event, Renderer as getRenderer
-from ignifuga.Renderer cimport Renderer
-from ignifuga.backends.sdl.Target cimport Target
+from ignifuga.Gilbert import Gilbert, Event, getRenderer
+from ignifuga.backends.sdl.Renderer cimport Renderer
 from ignifuga.Log import *
 from sys import exit
 from SDL cimport *
         cdef SDL_Surface *ss = NULL
         cdef char *bindata
         cdef bytes embedded_data
-        self._sdlRenderer = (<Target>getRenderer().target).renderer
+        renderer = getRenderer()
+        self._sdlRenderer = (<Renderer>renderer).renderer
         self._srcURL = srcURL
         self._isRenderTarget = isRenderTarget
         self._fontURL = None
             self._hw = hw
             
             if width is None:
-                width = Renderer().target.width
+                width = Renderer()._width
             if height is None:
-                height = Renderer().target.height
+                height = Renderer()._height
                 
             self._width = width
             self._height = height
             SDL_FreeSurface(self._surfacesw)
 
     cpdef blitCanvas(self, CanvasBase canvasbase, int dx=0, int dy=0, int dw=-1, int dh=-1, int sx=0, int sy=0, int sw=-1, int sh=-1, int blend=-1):
-        cdef Canvas canvas
+        #cdef Canvas canvas
 
-        canvas = <Canvas>canvasbase
+        #canvas = <Canvas>canvasbase
 
         if sw == -1:
-            sw = canvas._width
+            sw = canvasbase._width
         if sh == -1:
-            sh = canvas._height
+            sh = canvasbase._height
         
         if dw == -1:
             dw = sw
         
         if self._isRenderTarget and self._hw and canvas._hw:
             # Both canvas are hardware based, and we can render on this canvas from another canvas
-            return self.blitCanvasHW(canvas,dx,dy,dw,dh,sx,sy,sw,sh, blend)
+            return self.blitCanvasHW(<Canvas>canvasbase,dx,dy,dw,dh,sx,sy,sw,sh, blend)
         else:
             # At least one of the canvas is software based
-            return self.blitCanvasSW(canvas,dx,dy,dw,dh,sx,sy,sw,sh, blend)
+            return self.blitCanvasSW(<Canvas>canvasbase,dx,dy,dw,dh,sx,sy,sw,sh, blend)
             
             
     cdef blitCanvasSW(self, Canvas canvas, int dx, int dy, int dw, int dh, int sx, int sy, int sw, int sh, int blend):

backends/sdl/Font.pyx

 # Ignifuga Game Engine
 # SDL Font wrapper
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
+
+# xcython: profile=True
+
 from ignifuga.Log import *
 
 cdef class Font(FontBase):

backends/sdl/GameLoop.pxd

 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
 from ignifuga.backends.sdl.SDL cimport *
-from ignifuga.backends.sdl.Target cimport Target
 from ignifuga.Gilbert import Gilbert, Event
 from ignifuga.backends.GameLoopBase cimport GameLoopBase
+from ignifuga.backends.sdl.Renderer cimport Renderer
 
 cdef class GameLoop(GameLoopBase):
     cdef int _screen_w, _screen_h
+    cdef Renderer renderer
     cpdef run(self)
     cdef handleSDLEvent(self, SDL_Event *sdlev)
     cdef normalizeFingerEvent(self, SDL_TouchFingerEvent *fev)
+
+

backends/sdl/GameLoop.pyx

 # Game Loop
 # Author: Gabriel Jacobo <gabriel@mdqinc.com>
 
+# xcython: profile=True
+# cython: boundscheck=False
+# cython: wraparound=False
+
 from ignifuga.Gilbert import Gilbert, Event
 from ignifuga.Log import debug
 import time, sys
 cdef class GameLoop(GameLoopBase):
     def __init__(self, fps = 30.0):
         super(GameLoop, self).__init__(fps)
-        self._screen_w, self._screen_h = Gilbert().renderer.screenSize
+        self.renderer = <Renderer>Gilbert().renderer
+        self._screen_w, self._screen_h = self.renderer.screenSize
         self.paused = False
 
+
     cpdef run(self):
         cdef SDL_Event ev
         cdef Uint32 now
+        cdef double remainingTime
 
         overlord = Gilbert()
-        target = overlord.renderer.target
         if overlord.platform in ['iphone',]:
             # Special loop for single app mobile platforms that slows down when not active
             # TODO: Is this really required for iPhone?
             # Note: Android does not need this anymore, I've enabled a blocking option in Android_PumpEvents
             while not self.quit:
                 now = SDL_GetTicks()
-                overlord.update(now/1000.0)
+                self.update(now)
                 if not self.paused:
-                    overlord.renderScene()
+                    self.renderer.update(now)
 
                 while SDL_PollEvent(&ev):
                     self.handleSDLEvent(&ev)
             # Regular loop, draws all the time independently of shown/hidden status
             while not self.quit:
                 now = SDL_GetTicks()
-                overlord.update(now/1000.0)
-                overlord.renderScene()
+                self.update(now)
+#                print "UPDATE: ", SDL_GetTicks() - now
+#                now = SDL_GetTicks()
+                if not self.freezeRenderer:
+                    self.renderer.update(now)
+#                print "RENDESCENE: ", SDL_GetTicks() - now
 
                 while SDL_PollEvent(&ev):
                     self.handleSDLEvent(&ev)
 
                 # Sleep for the remainder of the alloted frame time, if there's time left
-                remainingTime = self._interval  - (SDL_GetTicks()-now)
+                self.frame_time = SDL_GetTicks()-now
+                remainingTime = self._interval  - self.frame_time
                 if remainingTime > 0:
                     SDL_Delay( <Uint32>(remainingTime+0.5) )
 
         cdef SDL_MouseWheelEvent *mwev      
         cdef SDL_WindowEvent *winev
         cdef SDL_TouchFingerEvent *fev
-        cdef Target target
-        
+
         if sdlev.type == SDL_QUIT: