ReferenceError when trying to Cache a GeClipMap



  • Hello,
    I'm trying to create a crude example of caching the Bitmaps/GeClipMaps and I'm running into an error I can't seem to solve. In the code below, self.UpdateBackgroundCache() works, but self.UpdateBoxCache() does not. In this function, I'm creating a GeClipMap, initializing it, drawing a rectangle, and storing the clipmap's BaseBitmap as a property of the GeUserArea. I keep getting the error:

    ReferenceError: the object 'c4d.bitmaps.BaseBitmap' is not alive
    

    Can anyone help me understand what is going wrong? Thank you!

    import c4d
    from c4d import gui,bitmaps
    
    GADGET_ID_GEUSERAREA = 10000
    
    def drawColorBitmap(w,h,r,g,b):
        r = int(r * 255)
        g = int(g * 255)
        b = int(b * 255)
        bmp = bitmaps.BaseBitmap()
        bmp.Init(w, h, 24)
        for wPixel in range(w):
            for hPixel in range(h):
                bmp.SetPixel(wPixel, hPixel, r, g, b)
        return bmp
    
    class MyUserArea(c4d.gui.GeUserArea):
        def __init__(self):
            self.boxX = 50
            self.boxY = 50
            self.boxWidth = 50
            self.boxHeight = 50
            self.boxAngle = 0
            self.boxOpacity = 1.0
            self.bgCache = None
            self.boxCache = None
    
        def UpdateBackgroundCache(self,x2,y2):
            self.bgCache = drawColorBitmap(x2,y2,0.35,0.35,0.35)
    
        def UpdateBoxCache(self):
            rMap = c4d.bitmaps.GeClipMap()
            rMap.Init(self.boxWidth, self.boxHeight)
            rMap.BeginDraw()
            rMap.SetDrawMode(c4d.GE_CM_DRAWMODE_BLEND,
                c4d.GE_CM_SRC_MAX_OPACITY)
            rMap.SetColor(255,0,0,255)
            rMap.FillRect(0,0,50,self.boxHeight)
            rMap.EndDraw()
            self.boxCache = rMap.GetBitmap()        
    
        def GetMinSize(self):
            return 300,150
    
        def UpdateCache(self):
            #Cache Background
            if self.bgCache == None:
                self.UpdateBackgroundCache(self.GetWidth(),self.GetHeight())
    
            #Cache Red Box
            if self.boxCache == None:
                self.UpdateBoxCache()
    
            #Blit Red Box with Background
            clipMap = bitmaps.GeClipMap()
            clipMap.InitWithBitmap(self.bgCache, None)
            clipMap.BeginDraw()
            clipMap.SetDrawMode(c4d.GE_CM_DRAWMODE_BLEND,
                c4d.GE_CM_SRC_MAX_OPACITY)
    
            rectMap = bitmaps.GeClipMap()
            rectMap.InitWithBitmap(self.boxCache, None)
    
            clipMap.Blit(self.boxX, self.boxY,
                rectMap, 0, 0, self.boxWidth, self.boxHeight,
                rop = c4d.GE_CM_BLIT_COL)
    
            clipMap.EndDraw()
            bmp = clipMap.GetBitmap()
            return bmp
    
        def DrawMsg(self, x1, y1, x2, y2, msg):
            cache = self.UpdateCache()
            self.DrawBitmap(cache, x1, y1, x2, y2, 0, 0, 300, 150, c4d.BMP_NORMAL | c4d.BMP_ALLOWALPHA)
    
    class ExampleDialog(c4d.gui.GeDialog):
        geUserArea = MyUserArea()
    
        def CreateLayout(self):
            self.SetTitle("ClipMap")
            self.AddUserArea(GADGET_ID_GEUSERAREA, c4d.BFH_CENTER | c4d.BFH_SCALE | c4d.BFV_CENTER | c4d.BFV_SCALE, 300, 150)
            self.AttachUserArea(self.geUserArea, GADGET_ID_GEUSERAREA)
            return True
    
    def main():
        global dlg
        dlg = ExampleDialog()
        dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=400, defaulth=250, xpos=-2, ypos=-2)
    
    if __name__=='__main__':
        main()
    


  • Hi @blastframe,

    thank you for reaching out to us. The error ReferenceError: the object 'xyz' is not alive means that something has deallocated the object you are trying to reference. This happens because you have two competing memory management systems, one provided by Python, its gc, and one provided by Cinema's C++ API. In this case the method GeClipMap.GetBitmap() (Link to C++ Doc) only does return a pointer, i.e. an object that is owned by the GeClipMap. To avoid the error, you have either to avoid the GeClipMap being garbage collected or clone the bitmap so that you have full ownership. I "fixed" your code by just plastering in two .GetClone().

    Cheers,
    Ferdinand

    import c4d
    from c4d import gui,bitmaps
    
    GADGET_ID_GEUSERAREA = 10000
    FOO = None
    def drawColorBitmap(w,h,r,g,b):
        r = int(r * 255)
        g = int(g * 255)
        b = int(b * 255)
        bmp = bitmaps.BaseBitmap()
        bmp.Init(w, h, 24)
        for wPixel in range(w):
            for hPixel in range(h):
                bmp.SetPixel(wPixel, hPixel, r, g, b)
        return bmp
    
    class MyUserArea(c4d.gui.GeUserArea):
        def __init__(self):
            self.boxX = 50
            self.boxY = 50
            self.boxWidth = 50
            self.boxHeight = 50
            self.boxAngle = 0
            self.boxOpacity = 1.0
            self.bgCache = None
            self.boxCache = None
    
        def UpdateBackgroundCache(self,x2,y2):
            self.bgCache = drawColorBitmap(x2,y2,0.35,0.35,0.35)
    
        def UpdateBoxCache(self):
            rMap = c4d.bitmaps.GeClipMap()
            rMap.Init(self.boxWidth, self.boxHeight)
            rMap.BeginDraw()
            rMap.SetDrawMode(c4d.GE_CM_DRAWMODE_BLEND,
                c4d.GE_CM_SRC_MAX_OPACITY)
            rMap.SetColor(255,0,0,255)
            rMap.FillRect(0,0,50,self.boxHeight)
            rMap.EndDraw()
            # The bitmap returned by GeClipMap.GetBitmap() is owned by the 
            # GeClipMap. Leaving the scope of this function will free the
            # GeClipMap and with it the attached BaseBitmap.
            bitmap = rMap.GetBitmap()
            # Either clone the bitmap or also reference the GeClipMap so that
            # it won't be deallocated on exit.
            self.boxCache = bitmap.GetClone()
    
        def GetMinSize(self):
            return 300,150
    
        def UpdateCache(self):
            #Cache Background
            if self.bgCache == None:
                self.UpdateBackgroundCache(self.GetWidth(),self.GetHeight())
    
            #Cache Red Box
            if self.boxCache == None:
                self.UpdateBoxCache()
    
            #Blit Red Box with Background
            clipMap = bitmaps.GeClipMap()
            clipMap.InitWithBitmap(self.bgCache, None)
            clipMap.BeginDraw()
            clipMap.SetDrawMode(c4d.GE_CM_DRAWMODE_BLEND,
                c4d.GE_CM_SRC_MAX_OPACITY)
    
            rectMap = bitmaps.GeClipMap()
            rectMap.InitWithBitmap(self.boxCache, None)
    
            clipMap.Blit(self.boxX, self.boxY,
                rectMap, 0, 0, self.boxWidth, self.boxHeight,
                rop = c4d.GE_CM_BLIT_COL)
    
            clipMap.EndDraw()
            # The same thing applies here.
            bmp = clipMap.GetBitmap()
            return bmp.GetClone()
    
        def DrawMsg(self, x1, y1, x2, y2, msg):
            cache = self.UpdateCache()
            self.DrawBitmap(cache, x1, y1, x2, y2, 0, 0, 300, 150, c4d.BMP_NORMAL | c4d.BMP_ALLOWALPHA)
    
    class ExampleDialog(c4d.gui.GeDialog):
        geUserArea = MyUserArea()
    
        def CreateLayout(self):
            self.SetTitle("ClipMap")
            self.AddUserArea(GADGET_ID_GEUSERAREA, c4d.BFH_CENTER | c4d.BFH_SCALE | c4d.BFV_CENTER | c4d.BFV_SCALE, 300, 150)
            self.AttachUserArea(self.geUserArea, GADGET_ID_GEUSERAREA)
            return True
    
    def main():
        global dlg
        dlg = ExampleDialog()
        dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=400, defaulth=250, xpos=-2, ypos=-2)
    
    if __name__=='__main__':
        main()
    


  • Hi @blastframe,

    thank you for reaching out to us. The error ReferenceError: the object 'xyz' is not alive means that something has deallocated the object you are trying to reference. This happens because you have two competing memory management systems, one provided by Python, its gc, and one provided by Cinema's C++ API. In this case the method GeClipMap.GetBitmap() (Link to C++ Doc) only does return a pointer, i.e. an object that is owned by the GeClipMap. To avoid the error, you have either to avoid the GeClipMap being garbage collected or clone the bitmap so that you have full ownership. I "fixed" your code by just plastering in two .GetClone().

    Cheers,
    Ferdinand

    import c4d
    from c4d import gui,bitmaps
    
    GADGET_ID_GEUSERAREA = 10000
    FOO = None
    def drawColorBitmap(w,h,r,g,b):
        r = int(r * 255)
        g = int(g * 255)
        b = int(b * 255)
        bmp = bitmaps.BaseBitmap()
        bmp.Init(w, h, 24)
        for wPixel in range(w):
            for hPixel in range(h):
                bmp.SetPixel(wPixel, hPixel, r, g, b)
        return bmp
    
    class MyUserArea(c4d.gui.GeUserArea):
        def __init__(self):
            self.boxX = 50
            self.boxY = 50
            self.boxWidth = 50
            self.boxHeight = 50
            self.boxAngle = 0
            self.boxOpacity = 1.0
            self.bgCache = None
            self.boxCache = None
    
        def UpdateBackgroundCache(self,x2,y2):
            self.bgCache = drawColorBitmap(x2,y2,0.35,0.35,0.35)
    
        def UpdateBoxCache(self):
            rMap = c4d.bitmaps.GeClipMap()
            rMap.Init(self.boxWidth, self.boxHeight)
            rMap.BeginDraw()
            rMap.SetDrawMode(c4d.GE_CM_DRAWMODE_BLEND,
                c4d.GE_CM_SRC_MAX_OPACITY)
            rMap.SetColor(255,0,0,255)
            rMap.FillRect(0,0,50,self.boxHeight)
            rMap.EndDraw()
            # The bitmap returned by GeClipMap.GetBitmap() is owned by the 
            # GeClipMap. Leaving the scope of this function will free the
            # GeClipMap and with it the attached BaseBitmap.
            bitmap = rMap.GetBitmap()
            # Either clone the bitmap or also reference the GeClipMap so that
            # it won't be deallocated on exit.
            self.boxCache = bitmap.GetClone()
    
        def GetMinSize(self):
            return 300,150
    
        def UpdateCache(self):
            #Cache Background
            if self.bgCache == None:
                self.UpdateBackgroundCache(self.GetWidth(),self.GetHeight())
    
            #Cache Red Box
            if self.boxCache == None:
                self.UpdateBoxCache()
    
            #Blit Red Box with Background
            clipMap = bitmaps.GeClipMap()
            clipMap.InitWithBitmap(self.bgCache, None)
            clipMap.BeginDraw()
            clipMap.SetDrawMode(c4d.GE_CM_DRAWMODE_BLEND,
                c4d.GE_CM_SRC_MAX_OPACITY)
    
            rectMap = bitmaps.GeClipMap()
            rectMap.InitWithBitmap(self.boxCache, None)
    
            clipMap.Blit(self.boxX, self.boxY,
                rectMap, 0, 0, self.boxWidth, self.boxHeight,
                rop = c4d.GE_CM_BLIT_COL)
    
            clipMap.EndDraw()
            # The same thing applies here.
            bmp = clipMap.GetBitmap()
            return bmp.GetClone()
    
        def DrawMsg(self, x1, y1, x2, y2, msg):
            cache = self.UpdateCache()
            self.DrawBitmap(cache, x1, y1, x2, y2, 0, 0, 300, 150, c4d.BMP_NORMAL | c4d.BMP_ALLOWALPHA)
    
    class ExampleDialog(c4d.gui.GeDialog):
        geUserArea = MyUserArea()
    
        def CreateLayout(self):
            self.SetTitle("ClipMap")
            self.AddUserArea(GADGET_ID_GEUSERAREA, c4d.BFH_CENTER | c4d.BFH_SCALE | c4d.BFV_CENTER | c4d.BFV_SCALE, 300, 150)
            self.AttachUserArea(self.geUserArea, GADGET_ID_GEUSERAREA)
            return True
    
    def main():
        global dlg
        dlg = ExampleDialog()
        dlg.Open(c4d.DLG_TYPE_ASYNC, defaultw=400, defaulth=250, xpos=-2, ypos=-2)
    
    if __name__=='__main__':
        main()
    


  • @zipit Thank you very much, Ferdinand. This helped me a lot!