GeUserArea.DrawBitmap

On 07/12/2017 at 12:06, xxxxxxxx wrote:

User Information:
Cinema 4D Version:   R19 
Platform:      Mac OSX  ; 
Language(s) :       PYTHON  ;

---------
Hi,

there seems to be an issue with GeUserArea.DrawBitmap().

Not only that  BMP_DiMIMAGE is listed twice in the docs, but also the attribute of the function "mode" ,except BMP_NORMAL, can´t be assigned.
Especially BMP_ALLOWALPHA is really needed.

cheers
Martin

On 08/12/2017 at 08:09, xxxxxxxx wrote:

Hi Martin,

instead of drawing into the GeUserArea directly, we recommend to do all drawing (especially with alpha channels) into a GeClipMap. And then in the end only use DrawBitmap() with BMP_NORMAL and BMP_ALLOWALPHA to draw the GeClipMap (or rather the BaseBitrmap retrieved from the GeClipMap) into the GeUserArea. In this way BMP_ALLOWALPHA works for me as expected.

I borrowed some of Niklas' code from this thread:

import c4d
import c4d.gui     as gui
import c4d.bitmaps as bitmaps
  
class Area(gui.GeUserArea) :
    
    def __init__(self, w = 20, h = 20, r = 1, g = 1, b = 1) :
        self.bmp = bitmaps.GeClipMap()
        self.bmp.Init(w, h)
        self.bmp.BeginDraw()
        self.w   = w
        self.h   = h
        
        r = int(r * 255)
        g = int(g * 255)
        b = int(b * 255)
  
        for x in xrange(w) :
            for y in xrange(h) :
                a = (x * 4 / float(w))**(y / float(h))
                try:
                    a = 1 / (x**2 / float(w)**2) * a
                except:
                    a = 0
                a = int(a * 255)
                self.bmp.SetPixelRGBA(x, y, r, g, b, a)
                
        self.bmp.EndDraw()
                
    def DrawMsg(self, x1, y1, x2, y2, msg) :
        #self.DrawSetPen(c4d.Vector(0, 0, 0))  # changes bg color seen through alpha
  
        w, h = self.w, self.h
        self.DrawBitmap(self.bmp.GetBitmap(),
            0, 0, w, h, 0, 0, w, h, c4d.BMP_NORMAL | c4d.BMP_ALLOWALPHA)
            
    def GetMinSize(self) :
        return self.w, self.h
        
class Dialog(gui.GeDialog) :
    
    def __init__(self, area) :
        self.area = area
    
    def CreateLayout(self) :
        self.AddUserArea(1000, c4d.BFH_SCALEFIT | c4d.BFV_SCALEFIT)
        self.AttachUserArea(self.area, 1000)
        return True
    
def main() :
    area = Area(200, 200, 1.0, 0.66, 0.24)
    dlg  = Dialog(area)
    dlg.Open(c4d.DLG_TYPE_MODAL)
    
if __name__ == "__main__":
    main()

The double entry in the Python documentation will be fixed.

For now, I have moved this thread into the Python sub-forum. If you still feel BMP_ALLOWALPHA is buggy, please provide us with some more detail.

On 08/12/2017 at 11:39, xxxxxxxx wrote:

Hi Andreas,

I used GeClipMap() before, but no luck.
Confident through Niklas example I investigated further and the error occurred because a negative value was set to ww and wh attribute of the function.
In this special case BMP_NORMAL is valid, while the other modes are not, confusing...
An abs() function or an exception raise would be nice in GeUserArea.DrawBitmap().

thanks,
Martin

On 11/12/2017 at 01:08, xxxxxxxx wrote:

Hi Andreas,

thanks for your help,
some additional notes and a working example:
- drawing everything into one clipmap and at the end draw this clipmap to the userarea,
 seems to be the only working way of layering several alpha bitmaps on top of each other.

- it is important to set to c4d.GE_CM_DRAWMODE_BLEND in GeClipMap.SetDrawMode(),
 otherwise the alpha channel is not being used.

- it is important to overwrite GeUserArea.Sized() ,
 otherwise the clipmap won't show up

If you feel like it´s ok, you can mark this thread as solved.

import c4d  
from c4d import bitmaps  
  
OK = 1003  
SHOW = 1005  
USERAREA = 1004  
  
class Canvas(c4d.gui.GeUserArea) :  
  
  def __init__(self) :  
      self.w = self.GetWidth()  
      self.h = self.GetHeight()  
      self.rectangle=[-1,-1,-1,-1]  
      self.drawMap = bitmaps.GeClipMap()  
        
  ###########################          
  #UserArea Functions          
  def DrawMsg(self, x1, y1, x2, y2, msg) :    
        
      self.OffScreenOn()  
  
      #draw everything in one clip map  
      self.drawMap.BeginDraw()  
      #draw background  
      self.drawMap.SetColor(51, 51, 51)  
      self.drawMap.FillRect(x1, y1, x2, y2)  
        
      #ensure that there is an object to draw the icon from  
      obj=doc.GetFirstObject()  
      if not obj:  
          print "no item"  
          self.drawMap.SetColor(255,170, 24)    
          self.drawMap.TextAt(3,3,"Please, insert an object into the scene for testing!")  
          self.drawMap.EndDraw()  
          self.DrawBitmap(self.drawMap.GetBitmap(), 0, 0, self.GetWidth(), self.GetHeight(),  
                          0, 0, self.GetWidth(), self.GetHeight(),  
                          c4d.BMP_NORMAL)  
          return  
         
      #draw text   
      self.drawMap.SetColor(255,170, 24)    
      self.drawMap.TextAt(3,3,str(50000000))  
      #draw a rectangle  
      self.drawMap.SetColor(100, 100, 100, 255)  
      self.drawMap.FillRect(150, 0, 250, 50)  
        
      #needs to be set to draw alpha maps--------------------#  
      self.drawMap.SetDrawMode(c4d.GE_CM_DRAWMODE_BLEND, 255)  
      #------------------------------------------------------#  
        
      #draw icon with alpha  
      objicon = obj.GetIcon()  
      bmp = objicon['bmp']  
      wbmp = objicon['w']  
      hbmp = objicon['h']  
      xbmp = objicon['x']  
      ybmp = objicon['y']  
      icon = bitmaps.BaseBitmap()  
      icon.Init(wbmp,hbmp,depth=24)  
      bmp.CopyPartTo(icon, xbmp, ybmp, wbmp, hbmp)  
      alphaicon =icon.GetInternalChannel()  
  
      iconclip = bitmaps.GeClipMap()  
      iconclip.InitWithBitmap(icon,alphaicon)  
        
      self.drawMap.Blit( 50,50, iconclip, 0, 0,  wbmp, hbmp, rop = c4d.GE_CM_BLIT_COL)    
        
      #draw drag    
      self.drawMap.SetColor(200, 200, 255, 100)    
      xdr,ydr,x2dr,y2dr = self.toolDragSortEx()  
      self.drawMap.FillRect(xdr,ydr,x2dr,y2dr)   
        
      self.drawMap.SetColor(255, 255, 255, 255)   
      self.drawMap.Rect(xdr,ydr,x2dr,y2dr)  
                                      
      self.drawMap.EndDraw()  
      self.DrawBitmap(self.drawMap.GetBitmap(), 0, 0, self.GetWidth(), self.GetHeight(),  
                          0, 0, self.GetWidth(), self.GetHeight(),  
                          c4d.BMP_NORMAL)  
      return      
  
  def Sized(self, w, h) :  
      self.w=w  
      self.h=h  
      #needs to be set---------------------------------------#   
      self.drawMap.Destroy()  
      self.drawMap.Init(self.w, self.h)  
      #------------------------------------------------------#  
      return  
    
  def GetMinSize(self) :  
      return self.w, self.h  
  
  def InputEvent(self, msg) :  
      dev = msg.GetLong(c4d.BFM_INPUT_DEVICE)  
      if dev == c4d.BFM_INPUT_MOUSE:  
          return self.HandleMouseEvents(msg)  
      return False   
            
  def HandleMouseEvents(self, msg) :  
      #init values  
      mousex = msg.GetLong(c4d.BFM_INPUT_X)  
      mousey = msg.GetLong(c4d.BFM_INPUT_Y)   
      start_x = mx = mousex - self.Local2Global()['x']  
      start_y = my = mousey - self.Local2Global()['y']  
   
      #drag interaction    
      state = c4d.BaseContainer()  
      self.MouseDragStart(c4d.KEY_MLEFT,start_x, start_y, c4d.MOUSEDRAGFLAGS_DONTHIDEMOUSE| c4d.MOUSEDRAGFLAGS_NOMOVE )  
      while True:  
          result, dx, dy, channels = self.MouseDrag()  
            
          #end of Drag  
          if result == c4d.MOUSEDRAGRESULT_ESCAPE:  
              break  
          if not self.GetInputState(c4d.BFM_INPUT_MOUSE, c4d.BFM_INPUT_MOUSELEFT, state) :  
              print "mouse right etc"  
              break  
          if state[c4d.BFM_INPUT_VALUE] == 0:  
              #mouse release  
              self.rectangle = [-1,-1,-1,-1]  
              self.Redraw()  
              break  
  
          #not moving, continue  
          if dx == 0 and dy == 0:  
              continue  
  
          #draging  
          mx -= dx  
          my -= dy  
  
          #start drag with rectangle  
          self.rectangle = [start_x,start_y,mx,my]  
          self.Redraw()       
      return True  
    
  def toolDragSortEx(self) :    
      if self.rectangle[0]<self.rectangle[2]:  
          x1,x2 = self.rectangle[0],self.rectangle[2]  
      else:  
          x1,x2 = self.rectangle[2],self.rectangle[0]  
      if self.rectangle[1]<self.rectangle[3]:  
          y1,y2 = self.rectangle[1],self.rectangle[3]  
      else:  
          y1,y2 = self.rectangle[3],self.rectangle[1]  
      return x1,y1,x2,y2  
  
class AreaDialog(c4d.gui.GeDialog) :  
  
  def __init__(self,userarea) :  
      self.userarea = userarea  
  
  def CreateLayout(self) :  
      self.SetTitle("USERAREATEST")  
      self.AddUserArea(USERAREA, c4d.BFH_SCALEFIT|c4d.BFV_SCALEFIT)  
      self.AttachUserArea(self.userarea, USERAREA)                      
      self.AddButton(OK, c4d.BFH_LEFT, name="OK")  
      self.AddButton(SHOW, c4d.BFH_LEFT, name="Show bitmap")  
      return True  
  
  def Command(self, id, msg) :  
      if id==OK:  
          self.Close()  
          return True  
        
      if id==SHOW:  
          bitmaps.ShowBitmap(self.userarea.drawMap.GetBitmap())  
      return True  
  
def main() :   
  userarea = Canvas()  
  dialog = None  
  dialog = AreaDialog(userarea)  
  dialog.Open(dlgtype=c4d.DLG_TYPE_MODAL_RESIZEABLE, xpos=100, ypos=100, defaultw=350, defaulth=500)  
  
if __name__=='__main__':  
  main()  

cheers,
Martin

On 11/12/2017 at 09:52, xxxxxxxx wrote:

Hi Martin,

thanks for your detailed final conclusion.

Just a note on GeUserArea.Sized() : This function should actually always be implemented for a GeUserArea to properly react to resizing requests (well, if you have elements that need adaption like your bitmap for example).

On 12/12/2017 at 04:33, xxxxxxxx wrote:

Hi Andreas,

thanks for the advice.
A last aesthetical kind of question on this subject.
How should this clipmap draw solution draw sharp alphas and typo into the userarea?
The clipmap is always blown up to screen pixels after DrawBitmap .
Is there any solution?

cheers,
Martin

On 13/12/2017 at 10:16, xxxxxxxx wrote:

Hi Martin,

I'm not sure I understand. Are you saying the GeClipmap gets modified/scaled by DrawBitmap()?
Looking at your DrawBitmap() call in the code above, you are passing the same GeUserArea width and height into "both" parameters of DrawBitmap(), while actually the second width and height should be the size of your bitmap. Could that be part of the problem?

On 13/12/2017 at 12:42, xxxxxxxx wrote:

Hi Andreas,

the clipmap was initialized with the width and height of the userarea. That´s why it makes no difference if  I use .Getwidth() from the userarea or .GetBw() from the clipmap.
The example code shows the loss of quality/sharpness comparing drawing text with GeUserArea.DrawText and drawing text on the clipmap and afterwards draw the bitmap from the clipmap.

You can simply replace the following DrawMsg function in my code above, to give it a try.

Thanks in advance!
Martin

    def DrawMsg(self, x1, y1, x2, y2, msg) :  
  
      self.OffScreenOn()  
  
      #draw everything in one clip map  
      self.drawMap.BeginDraw()  
      #draw background  
      self.drawMap.SetColor(51, 51, 51)  
      self.drawMap.FillRect(x1, y1, x2, y2)  
        
      #draw text   
      self.drawMap.SetColor(255,170, 24)    
      self.drawMap.TextAt(3,10,"Motion-Tracker-Tracks können jetzt auch runde Suchmuster verwenden. ")  
        
      self.drawMap.EndDraw()  
      self.DrawBitmap(self.drawMap.GetBitmap(), 0, 0, self.w, self.h,  
                          0, 0, self.drawMap.GetBw(),self.drawMap.GetBh(),  
                          c4d.BMP_NORMAL)  
                                    
      #draw with usual textdraw    
      self.DrawSetTextCol(c4d.COLOR_TEXT_SELECTED_DARK,  
                                  c4d.COLOR_TRANS)   
      self.DrawText("Motion-Tracker-Tracks können jetzt auch runde Suchmuster verwenden.", 3, 30)  
      return  

On 15/12/2017 at 02:53, xxxxxxxx wrote:

Hi Martin,

GeClipmap.TextAt() and GeUserArea.DrawText() doesn't give the same result because they use different means to draw text from different contexts.
GeClipmap.TextAt() draws into a bitmap whereas GeUserArea.DrawText() directly draws into the GUI.

(I answered as Andreas left for vacation.)

On 19/12/2017 at 01:06, xxxxxxxx wrote:

Hi Yannick,

I see, could you please confirm than that there is either a sup-pixel sampling in drawBitmap or a working alpha draw method missing while drawing several alphas over elements in the user area draw function?
I can´t reach the quality/result  I´m aiming for…

cheers martin

On 22/12/2017 at 06:41, xxxxxxxx wrote:

Hi Martin,

I'm afraid I can't disclose internal implementation details.