Bodypaint Layer Bitmap [SOLVED]

On 04/12/2014 at 02:16, xxxxxxxx wrote:

Hello,

I hope someone can help me with this!
I can´t find an easy way to get a Bitmap out of a Bodypaint layer.
I want to show those Layers on an Userarea, how can I do this?
Additionally it seems to me that some bodypaint features are not available in Python?
(at the bottom of the thread)

What I´ve found out so far:

if there is an active BB texture;

  
  tex = bodypaint.PaintTexture.GetSelectedTexture()  
  texturename = tex.GetFilename()  
  actlay = tex.GetActive()  

if one have to load a new BB texture;

  
  #loaddialog  
  texturename =  c4d.storage.LoadDialog(type= c4d.FILESELECTTYPE_IMAGES, title="Load Hdr", flags=c4d.FILESELECT_LOAD)  
    
  #pass it to BB and make itself and the first layer active  
  settings = c4d.BaseContainer()  
  settings.SetFilename(c4d.LOADTEXTURE_FILENAME, texturename)  
  tex = bodypaint.SendPainterCommand(c4d.PAINTER_LOADTEXTURE, doc=doc, tex=None, bc=settings)  
  if not tex:return  
  lay = tex.GetFirstLayer()  
  tex.SetActiveLayer(lay, activatetexture=True, show=True)  
    
  #get the active layer  
  actlay = tex.GetActive()  
  print actlay, "layer"  
    
  #read layerdimensions  
  res =  actlay.GetBoundingBox()  
  xres = res['x2']   
  yres = res['y2']  
    
    
  #check attributes  
  print actlay[c4d.ID_BASELIST_NAME]  
  print actlay[c4d.ID_PAINTBITMAP_PREVIEW]  
  print actlay[c4d.ID_PAINTLAYER_BLEND]  
  print actlay[c4d.ID_PAINTLAYER_OPACITY]  
  print actlay[c4d.ID_PAINTLAYER_SHOW]  
    
  #add a new layer  
  print tex.AddLayerBmp(insertafter=ActLay, useundo=True, activate=True)  
    
  ######################################################################  
  #save the whole texture and pass it to the userarea is not what I want  
  ######################################################################  
  
  #orig = bitmaps.BaseBitmap()  
  #orig.InitWith(texturename)  
  #bitmaps.ShowBitmap(orig)  
  ######################################################################  

-How to add a new layer on top of the last one? Why only insertafter?
-[c4d.ID_PAINTBITMAP_PREVIEW]is always None?

Thanks in advance
Martin

On 04/12/2014 at 06:26, xxxxxxxx wrote:

Hello,

to get the raw content of a bitmap layer use GetPixelCnt(). Then you can copy this data into a BaseBitmap with SetPixelCnt(). You find examples how to use this functions in the painting examples in the C++ and Python SDK like SculptPaintBrush.

  
  if actlay.IsInstanceOf(c4d.OBJECT_PAINTLAYERBMP) == True:  
        
      bmp = actlay.ToPaintLayerBmp()  
            
      xres = bmp.GetBw()  
      yres = bmp.GetBh()  
      size = xres * yres  
        
      if size == 0:  
          return  
           
      colorMode = bmp.GetColorMode()     
           
      # prepare storage  
      sq = storage.ByteSeq(None, size*c4d.COLORBYTES_RGB)  
        
      inc = 3  
        
      # read data  
      for row in xrange(yres) :  
          offset = sq.GetOffset(row*(xres*inc))  
          bmp.GetPixelCnt(0, row, xres, offset, colorMode, c4d.PIXELCNT_0)   
    
      # prepare bitmap  
      baseBitmap = bitmaps.BaseBitmap()  
      baseBitmap.Init(xres,yres,)  
    
      # write data  
      for row in xrange(yres) :  
          offset = sq.GetOffset(row*(xres*inc))   
          baseBitmap.SetPixelCnt(0, row, xres, offset, inc,colorMode, c4d.PIXELCNT_0)   
        
      # save  
      filename = c4d.storage.SaveDialog(title="Save preview")  
      baseBitmap.Save(filename,c4d.FILTER_TIF)  

AddLayerBmp() should accept None as an argument for "insertafter". This is a bug. ID_PAINTBITMAP_PREVIEW returns a CUSTOMDATATYPE_BITMAPBUTTON which is currently not supported in Python.

Best wishes,
Sebastian

On 04/12/2014 at 07:00, xxxxxxxx wrote:

Hello Sebastian,

Thank you very much! Tremendous help as always!
Just two hours ago I started with storage and bytesequence, but could´nt get it to work.
I tried it with the new R16 function
PaintLayerBmp.GetPixelCnt

  
  sq = storage.ByteSeq(None, xres*yres*inc)  
  ActLay.GetPixelCnt(0, 0, cnt, sq , dstmode= c4d.COLORMODE_RGB, flags= c4d.PIXELCNT_0)  

But no luck! Do you have a working example ?

AddLayerBmp() does not accept None.
the resulting errormessage:
TypeError: argument 1 must be c4d.modules.bodypaint.PaintLayer, not None
Could you please give it a try?

Thanks again
Martin

On 04/12/2014 at 07:43, xxxxxxxx wrote:

Hi Martin,

Here a script that read a bitmap file, converts it to spherical and writes it back.

  
import c4d, math
from c4d import bitmaps, gui, storage
from c4d import plugins
  
#
# spherical projection
# y=y, x=x*cos(y)
#
  
BUTTON_ID_SELECT = 1001 
MY_PATH = 1003
MY_FILEDIMENSIONS = 1007
MY_GOBUTTON = 100014
MY_LINKBOX = 100015
  
#global variables
width = 0
height = 0
orig = 0   
path = None
  
Tolerance = 0.0000001
masterRadius = 1
derivedRadius = math.pi/2
    
class YourDialog(gui.GeDialog) :
  
    def CreateLayout(self) : 
       
        self.SetTitle("Spherical Projection.")
  
        self.GroupBegin(id=8000, flags=c4d.BFH_LEFT, cols=2)     #, rows=1)
        self.GroupBorderSpace(10, 10, 10, 20)
        
        self.AddButton(BUTTON_ID_SELECT, flags=c4d.BFH_LEFT, initw=145, inith=30, name="Select Input file")
        self.element = self.AddStaticText(id=MY_PATH, flags=c4d.BFH_MASK, initw=600, name="   ", borderstyle=c4d.BORDER_NONE)
        self.element = self.AddStaticText(id=11004, flags=c4d.BFH_MASK, name="File Dimensions   ", borderstyle=c4d.BORDER_NONE)
        self.element = self.AddStaticText(id=MY_FILEDIMENSIONS, flags=c4d.BFH_MASK, initw=600, name="   ", borderstyle=c4d.BORDER_NONE)
        
        self.AddSeparatorH(inith=80)
        self.AddSeparatorH(inith=80)
  
        self.AddButton(MY_GOBUTTON, c4d.BFV_MASK, initw=145, name="Go!")
  
        self.GroupEnd()    
            
  
        return True
  
    def Command(self, id, msg) :
    
        global width, height, orig, path, copy
        
        if (id == BUTTON_ID_SELECT) : 
            path = storage.LoadDialog(type=c4d.FILESELECTTYPE_IMAGES, title="Please Choose a 32-bit Image:")
            print "Path: ", path
            
            if (path != None) :
  
                # Create and initialize selected image
                orig = bitmaps.BaseBitmap()
                if orig.InitWith(path)[0] != c4d.IMAGERESULT_OK:
                    gui.MessageDialog("Cannot load image \"" + path + "\".")
                    return
                
                # Get selected image infos
                width, height = orig.GetSize()
                #print "w+h: ", width, " - ", height
                
                bits = orig.GetBt()
                #print "bt: ", bits
            
                self.SetString(MY_PATH, path)
                self.SetString(MY_FILEDIMENSIONS, str(width)+"X"+str(height)+ "  Bits: "+str(bits))
 
            # Create the copy and initialize it
            copy = bitmaps.BaseBitmap()
            copy.Init(width, height, bits)
  
        #return True
            
        if (id == MY_GOBUTTON) : 
            #c4d.EventAdd()
            #self.Close()
  
            #print "Gobutton path: ", path
            if (path == None) :
                gui.MessageDialog ("No Input File selected!")
                return -1   
            
            ret = SphericalProjection(self.GetString(MY_PATH))
  
            #print "Showbitmaps orig + copy"
            bitmaps.ShowBitmap(orig)
            bitmaps.ShowBitmap(copy)    
            
        return True
  
    def DestroyWindow(self) :        #Use this method to toggle the switch back to it's original off state
        #obj[c4d.ID_USERDATA, 1]=0
        c4d.EventAdd() 
  
def SphericalProjection(path) :
  
    global width,height,orig
    
    c4d.gui.SetMousePointer(c4d.MOUSE_BUSY)
  
    if not path: return
     
    height2 = height / 2
    width2 = width / 2
  
    
    for ix in range(width) :  
        for iy in range(height) :
            ycos = math.sin(math.radians(90.0/height2)*iy)
            x = width2 + (width2-ix)*ycos
            r,g,b = orig.GetPixel(int(x), iy)
            copy.SetPixel(width-ix, iy, r, g, b) 
  
    return True
 
if __name__=='__main__':
    
    dlg = YourDialog()
    dlg.Open(dlgtype= c4d.DLG_TYPE_ASYNC, defaultw=400)       #, xpos=600, ypos=500, defaultw=200, defaulth=200)
  

On 04/12/2014 at 08:53, xxxxxxxx wrote:

Hello Martin,

do you have any questions about the above example or the linked examples?

As stated above, it is a bug that AddLayerBmp() does not accept None.

Best wishes,
Sebastian

On 04/12/2014 at 09:27, xxxxxxxx wrote:

@Sebastian
ahh,
I see, I assigned the bug statement to the bitmapbutton for whatever reason.Thanks.

The example is pretty clear.
You just used the new GetPixelCnt method?
Thanks!

For the Limit list on support site, should I write an email that it´s not possible in Python, or will this restrictions registered automatically?

@Pim
Thanks!

Best wishes
Martin

On 05/12/2014 at 01:50, xxxxxxxx wrote:

Hello,

just in case someone needs to know.
To avoid the layer insertion point bug, one can use GeListNode class commands like:

  
  #add a new layer  
  newlayer = tex.AddLayerBmp(insertafter=actlay, useundo=True, activate=True)  
  # as (insertafter= None) does not work a workaround to paste the new layer on top of the list  
  newlayer.InsertBefore(tex.GetFirstLayer())  
  #to avoid problems with previous loaded textures, set the first layer activ  
  first = tex.GetFirstLayer()  
  tex.SetActiveLayer(first, activatetexture=True, show=True)  

@Sebastian
could you please explain what´s the difference between GetPixelCnt with a Bitmap and a BodyPaint layer?
I didn´t get it.

Thanks in advance
Martin

On 05/12/2014 at 05:42, xxxxxxxx wrote:

Hello,

PaintLayerBmp.GetPixelCnt() and BaseBitmap.GetPixelCnt() are two different methods of two different classes. They are used pretty much the same way; the only difference is that BaseBitmap.GetPixelCnt() allows you to define a byte increment per pixel for the output buffer.

best wishes,
Sebastian