Commits

Gabriel Jacobo committed 112fed5

Better handling of sprite adding/removal

Comments (0)

Files changed (5)

         # Do not clean the cache here, there may be useful data for other scenes -> self.dataManager.cleanup()
         gc.collect()
 
+        self.renderer.cleanup()
+
     def changeScene(self, scene_id):
         debug("Switching scene to: %s " % scene_id)
         self.resetScene()

backends/sdl/Canvas.pyx

         SDL_SetTextureBlendMode(self._surfacehw, SDL_BLENDMODE_BLEND)
 
     def __dealloc__(self):
-        debug( ">>>CANVAS DEALLOC %s<<<" % self)
+        debug( ">>>CANVAS DEALLOC %s (URL: %s) <<<" % (self, self._srcURL))
         if self._hw:
             SDL_DestroyTexture(self._surfacehw)
             self._surfacehw = NULL
             dh = sh
             
         
-        if self._isRenderTarget and self._hw and canvas._hw:
+        if self._isRenderTarget and self._hw and canvasbase._hw:
             # Both canvas are hardware based, and we can render on this canvas from another canvas
             return self.blitCanvasHW(<Canvas>canvasbase,dx,dy,dw,dh,sx,sy,sw,sh, blend)
         else:

backends/sdl/Renderer.pxd

     int z
     Uint8 r,g,b,a
 
-    bint show, dirty
+    bint show, dirty, free
     SDL_Rect _src, _dst
 
 ctypedef _Sprite* Sprite_p
     # Sprites
     cdef map[int,deque[Sprite_p]] *zmap
     cdef deque[_Sprite] *active_sprites
+    cdef deque[Sprite_p] *free_sprites
     cdef bint dirty
 
     cdef void _processSprite(self, Sprite_p sprite, SDL_Rect *screen, bint doScale) nogil
     cdef bint _spriteRot(self, _Sprite *sprite, double angle, int centerx, int centery, int flip) nogil
     cdef bint _spriteColor(self, _Sprite *sprite, Uint8 r, Uint8 g, Uint8 b, Uint8 a) nogil
 
+    cpdef cleanup(self)
 
     # Render target related stuff
     cdef int _width, _height

backends/sdl/Renderer.pyx

         # _Sprite list allocation and setup
         self.zmap = new map[int,deque[Sprite_p]]()
         self.active_sprites = new deque[_Sprite]()
+        self.free_sprites = new deque[Sprite_p]()
         self.dirty = True
 
     @cython.cdivision(True)
         #with nogil, parallel():
         for i in prange(numsprites, nogil=True):
             sprite = &self.active_sprites.at(i)
-            if all or sprite.dirty:
+            if not sprite.free and (all or sprite.dirty):
                 self._processSprite(sprite, &screen, doScale)
                 sprite.dirty = False
 #        cdef deque[Sprite].iterator iter, iter_last
         sprite.b = <Uint8>(b*255.0)
         sprite.a = <Uint8>(a*255.0)
         sprite.dirty = True
+        sprite.free = False
 
-        self.active_sprites.push_back(sprite)
-        spritep = &self.active_sprites.back()
+
+        if self.free_sprites.size() > 0:
+            spritep = self.free_sprites.back()
+            self.free_sprites.pop_back()
+            spritep[0] = sprite
+        else:
+            self.active_sprites.push_back(sprite)
+            spritep = &self.active_sprites.back()
+
         self._indexSprite(spritep)
         sprite_wrap.sprite = spritep
         return sprite_wrap
         cdef _Sprite *sprite = sprite_w.sprite
         cdef deque[_Sprite].iterator iter
         if self._unindexSprite(sprite):
-            iter = self.active_sprites.begin()
-            while iter != self.active_sprites.end():
-                if &deref(iter) == sprite:
-                    self.active_sprites.erase(iter)
-                    break
-                inc(iter)
-            return True
+            self.free_sprites.push_back(sprite)
+            sprite.free = True
         return False
 
 
         # Adjust scrolling if needed
         self.scrollBy(0,0)
 
+    cpdef cleanup(self):
+        """ Remove free sprites if they are not in use"""
+        if self.active_sprites.size() == self.free_sprites.size():
+            # All active sprites are freed, so we can modify pointers at will
+            self.active_sprites.resize(0)
+            self.free_sprites.resize(0)
+
     def __dealloc__(self):
         debug('Releasing Sprites')
 
-        cdef _Sprite *sprite
-        cdef zmap_iterator ziter = self.zmap.begin()
-        cdef deque[Sprite_p] *ds
-        cdef deque_Sprite_iterator iter
-
-        # Release unused sprites
+        # Release sprites
+        del self.zmap
         del self.active_sprites
-
-        # Release sprites in use
-        while ziter != self.zmap.end():
-            ds = &deref(ziter).second
-            iter = ds.begin()
-            while iter != ds.end():
-                sprite = deref(iter)
-                free(sprite)
-            ds.resize(0)
-
-        del self.zmap
+        del self.free_sprites
 
         debug('Releasing SDL renderer')
         if self.renderer != NULL:

components/Sprite.py

             '_blur': 0.0,
             '_lastBlurAmount': None,
             '_overlays': {},
-            '_rendererSpriteId': None
+            '_rendererSpriteId': None,
+            '_static': False,
+            '_parent': None
             })
 
         super(Sprite, self).__init__(id, entity, active, frequency, **data)
             else:
                 self.sprite = None
                 self._canvas = self._atlas
+                self._static = True
         self._updateSize()
 
         self.show()
 
     def free(self, **kwargs):
         self.hide()
+        self._tmpcanvas = None
         super(Sprite, self).free(**kwargs)
 
     def update(self, now, **data):
         """ Initialize the required external data """
         super(Sprite, self).update(now, **data)
 
-        if self._canvas == self._atlas:
+        if self._static:
             # This is a single image non animated sprite, we return with the STOP instruction so the update loop is not called
             STOP()
             return
         self.updateRenderer()
 
     def _updateRenderer(self):
-        if self._rendererSpriteId:
+        if self._parent is not None:
+            print "PARENT _DOCOMPOSITING"
+            self._parent._doCompositing()
+        elif self._rendererSpriteId:
             self.renderer.spriteDst(self._rendererSpriteId, self._x, self._y, self._width_pre, self._height_pre)
 
             self.renderer.spriteRot(self._rendererSpriteId,
             self._dirty = False
 
     def updateRenderer(self):
-        if self._canvas == self._atlas:
-            #print "_updateRenderer", self, self._rendererSpriteId
+        if self._static:
             # No animation loop, directly update renderer
             self._updateRenderer()
         else:
             # Mark as dirty so we update on the next update
-            #print "_updateRenderer", self, "mark dirty"
             self._dirty = True
 
     # The current composed full image frame (not the animation atlas, but the consolidated final viewable image)
     @property
     def canvas(self):
-#        if self._tmpcanvas != None:
-#            return self._tmpcanvas
-#        elif self.sprite != None:
-#            return self.sprite.canvas
-#        elif self._atlas != None:
-#            return self._atlas
-#        else:
-#            return None
         return self._canvas
 
     @Viewable.red.setter
         """ Set the blur amount, this is actually an integer value that equals the pixel displacement used to simulate bluring"""
         if value <= 0:
             # Remove the temporal canvas
-            self._tmpcanvas = None
             self._lastBlurAmount = None
             value = 0
         else:
             value = int(value)
         self._blur = value
 
+        if self._static:
+            self._doCompositing()
+
     @Viewable.x.setter
     def x(self, new_x):
         Viewable.x.fset(self, new_x)
                 if self._overlays:
                     use_tmpcanvas = True
                     source.mod(1.0,1.0,1.0,1.0)
-                    self._tmpcanvas.blitCanvas(source, 0, 0, self._tmpcanvas.width, self._tmpcanvas.height, 0,0,self._tmpcanvas.width,self._tmpcanvas.height, self._tmpcanvas.BLENDMODE_NONE)
+                    self._tmpcanvas.blitCanvas(source, 0, 0, self._tmpcanvas.width, self._tmpcanvas.height, 0,0,source.width,source.height, self._tmpcanvas.BLENDMODE_NONE)
                     for z in self._overlays.iterkeys():
                         (id,x,y,op,_r,_g,_b,_a,sprite) = self._overlays[z]
+
                         w = sprite.width
                         h = sprite.height
                         canvas = sprite.canvas
                         if canvas is not None:
-                            r = canvas.r
-                            g = canvas.g
-                            b = canvas.b
-                            a = canvas.a
+                            r = sprite._red
+                            g = sprite._green
+                            b = sprite._blue
+                            a = sprite._alpha
                             canvas.mod(_r if _r is not None else r, _g if _g is not None else g, _b if _b is not None else b, _a if _a is not None else a)
                             self._tmpcanvas.blitCanvas(canvas,0,0,w,h,x,y,w,h,op)
                             canvas.mod(r,g,b,a)
 
-                if self._blur > 0 and self._blur != self._lastBlurAmount:
-                    use_tmpcanvas = self._doBluring(source, not use_tmpcanvas) or use_tmpcanvas
+                if self._blur > 0:
+                    if self._blur != self._lastBlurAmount:
+                        self._doBluring(source, not use_tmpcanvas)
+                    use_tmpcanvas = True
 
                 source.mod(self._red, self._green, self._blue, self._alpha)
 
         if use_tmpcanvas:
-            self._canvas = self._tmpcanvas
+            if self._canvas != self._tmpcanvas:
+                self._canvas = self._tmpcanvas
+                self.hide()
+                self.show()
         else:
-            self._tmpcanvas = None
+            #
             if self.sprite is not None:
-                self._canvas = self.sprite.canvas
+                if self._canvas != self.sprite.canvas:
+                    self._canvas = self.sprite.canvas
+                    self.hide()
+                    self.show()
             elif self._atlas is not None:
-                self._canvas = self._atlas
+                if self._canvas != self._atlas:
+                    self._canvas = self._atlas
+                    self.hide()
+                    self.show()
             else:
-                self._canvas = None
+                if self._canvas is not None:
+                    self._canvas = None
+                    self.hide()
+
+        self.updateRenderer()
 
     def _doBluring(self, source, start_clean = False):
         """ Take the current canvas and "blur it" by doing a few blits out of center"""
-        if self._blur > 0:
+        if self._blur > 0 and self._tmpcanvas is not None:
             if start_clean:
                 source.mod(1.0,1.0,1.0,1.0)
-                self._tmpcanvas.blitCanvas(source, 0, 0, self._tmpcanvas.width, self._tmpcanvas.height, 0,0,self._tmpcanvas.width,self._tmpcanvas.height, self._tmpcanvas.BLENDMODE_NONE)
+                self._tmpcanvas.blitCanvas(source, 0, 0, self._tmpcanvas.width, self._tmpcanvas.height, 0,0,source.width,source.height, self._tmpcanvas.BLENDMODE_NONE)
             source.mod(1.0,1.0,1.0,0.2)
             b = int (self._blur / 2)
             w = self._tmpcanvas.width-b
         if self.entity is not None:
             sprite = self.entity.getComponent(id)
             if sprite is not None and isinstance(sprite, Sprite):
+                if z in self._overlays:
+                    self.removeOverlay(z, update=False)
                 self._overlays[z] = (id,x,y,op,r,b,g,a,sprite)
+                sprite.parent = self
                 if update:
                     self._doCompositing()
                 return True
     def removeOverlay(self, zindex, update=False):
         """ Remove an overlay by index"""
         try:
+            id,x,y,op,r,b,g,a,sprite = self._overlays[z]
+            sprite.parent = None
             del self._overlays[zindex]
             if update:
                 self._doCompositing()
             return None
 
     def clearOverlays(self, update=False):
+        for z in self._overlays.keys():
+            self.removeOverlay(z, update=False)
+
         self._overlays = {}
         if update:
             self._doCompositing()
         return odict
 
     def show(self):
-        if self._rendererSpriteId is None:
+        if self._rendererSpriteId is None and self._active and self._canvas != None:
+            self._updateSize()
             self._rendererSpriteId = self.renderer.addSprite(self._canvas,
                 self._z,
                 0, 0, self._width_src, self._height_src,
                 self.show()
             else:
                 self.hide()
+    @property
+    def parent(self):
+        return self._parent
+
+    @parent.setter
+    def parent(self, value):
+        self._parent = value
+
 
 
 class _Sprite(object):