Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Overlaying icons (like Alembic)

    Cinema 4D SDK
    r20 r21 s22 c++ classic api
    2
    9
    1.1k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • fwilleke80F
      fwilleke80
      last edited by

      Hi,

      here's the next icon-related question from me 😉

      Is there any ready-to-use function in the API to overlay icons, like the Alembic import does?

      Screenshot 2020-09-02 at 10.49.43.png

      I need to do exactly the same, so I better ask if there's an easy way, before I start fiddling around with bitmaps and their alpha channels.

      Thanks in advance!

      Greetings,
      Frank

      www.frankwilleke.de
      Only asking personal code questions here.

      1 Reply Last reply Reply Quote 0
      • M
        m_adam
        last edited by

        Hi Frank, this is done via the MSG_GETCUSTOMICON and the GetCustomIcon with the callback.

        //----------------------------------------------------------------------------------------
        /// Writes an overlay icon bitmap into another bitmap at given coordinates and dimensions.
        /// @param[in] overlay Source icon bitmap.
        /// @param[in] dest    Destination icon bitmap.
        /// @param[in] x       Starting horizontal coordinate.
        /// @param[in] y       Starting vertical coordinate.
        /// @param[in] bw      Width of source bitmap region to write.
        /// @param[in] bh      Height of source bitmap region to write.
        //----------------------------------------------------------------------------------------
        inline void BlitOverlayIcon(IconData* overlay, IconData* dest, Int32 x, Int32 y, Int32 bw, Int32 bh, Int32 xOffset = 0, Int32 yOffset = 32)
        {
        	Int32	 px, py;
        	UInt16 aa, a, rr, r, gg, g, bb, b;
        
        	if (!overlay || !overlay->bmp || !dest || !dest->bmp)
        		return;
        
        	BaseBitmap* channel = overlay->bmp->GetInternalChannel();
        	BaseBitmap* destAlphaChannel = dest->bmp->GetInternalChannel();
        
        	if (!channel)
        		return;
        
        	Int32 destW = dest->w;
        	Int32 destH = dest->h;
        	if (bw > destW)
        		bw = destW;
        	if (bh + yOffset > destH)
        		bh = destH - yOffset;
        	if (bw + xOffset > destW)
        		bw = destW - xOffset;
        
        	aa = a = rr = r = gg = g = bb = b = 0xff;
        	for (py = 0; py < bh; py++)
        	{
        		for (px = 0; px < bw; px++)
        		{
        			// get color and alpha from overlay icon
        			overlay->bmp->GetPixel(x + px, y + py, &r, &g, &b);
        			overlay->bmp->GetAlphaPixel(channel, x + px, y + py, &a);
        
        			// get color and alpha from background icon if available
        			dest->bmp->GetPixel(px + xOffset, py + yOffset, &rr, &gg, &bb);
        			if (destAlphaChannel)
        			{
        				dest->bmp->GetAlphaPixel(destAlphaChannel, px + xOffset, py + yOffset, &aa);
        			}
        
        			// blend overlay color against existing icon background color
        			Float blend = a / 255.0;
        			dest->bmp->SetPixel(px + xOffset, py + yOffset, (Int32)Blend(rr, r, blend), (Int32)Blend(gg, g, blend), (Int32)Blend(bb, b, blend));
        
        			// use only the overlay alpha if the opacity is higher to keep the original icon complete
        			// and only in case the background has an alpha at all
        			if (destAlphaChannel && aa < a)
        			{
        				dest->bmp->SetAlphaPixel(destAlphaChannel, px + xOffset, py + yOffset, a);
        			}
        		}
        	}
        }
        
        
        // In the message
        case MSG_GETCUSTOMICON:
        {
            GetCustomIconData* cid = (GetCustomIconData*)data;
            if (!cid)
                return false;
        
            BaseContainer* bc = ((BaseObject*)node)->GetDataInstance();
        
            CustomIconDrawDelegate drawCallback = [this, bc](IconData& dat) -> void
            {
                IconData overlayIcon;
                Bool overlayIconloaded = false;
        
                // According to a value in the BaseContainer change the loaded overlay
                if (bc->GetBool(10000))
                    overlayIconloaded = GetIcon(RESOURCEIMAGE_OK, &overlayIcon);
                else
                    overlayIconloaded = GetIcon(RESOURCEIMAGE_CANCEL, &overlayIcon);
        
        
                if (overlayIconloaded)
                {
                    BlitOverlayIcon(&overlayIcon, &dat, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h);
                }
            };
        
            // Get the default custom icons and settings
            CustomIconSettings customIconSettings;
            ((BaseObject*)node)->Message(MSG_GETCUSTOMICON_SETTINGS, &customIconSettings);
            customIconSettings._defaultIconId = bc->GetInt32(Ocube, Onull);
            customIconSettings._fillDefault = true;
        
            // Set cid with the passed customIconSettings and callback
            GetCustomIcon(*cid, customIconSettings, false, nullptr, &drawCallback);
        
            break;
        }
        

        Cheers,
        Maxime.

        MAXON SDK Specialist

        Development Blog, MAXON Registered Developer

        1 Reply Last reply Reply Quote 1
        • fwilleke80F
          fwilleke80
          last edited by

          Nice, thank you! 🙂

          www.frankwilleke.de
          Only asking personal code questions here.

          1 Reply Last reply Reply Quote 0
          • fwilleke80F
            fwilleke80
            last edited by fwilleke80

            Hmm, I'm not sure how to use this properly.

            My plugin objects' icons are loaded from one big TIFF. When I use your BlitOverlayIcon() to draw an overlay over an icon, it basically works, but seems to change the bitmap that all the objects use. Drawing the overlay in one of the objects changes the icon for all object of a kind. Also, the bitmap is not reset, so everything I ever overlay remains there. With every call of MSG_GETCUSTOMICON I get more and more stuff drawn on top of it.

            I guess, I need to use CustomIconSettingsand GetCustomIcon() to avoid changing the common bitmap for all objects, but those are not explained in the SDK. Do I have to use all that color-related stuff? What's in that BaseContainer in your code that it returns something from the ID Ocube?

            I have several objects, and each object should be able to change its own icon (not the icon of all object of the same type) by overlaying a second icon on it. Could you elaborate on your code a little?

            Thanks in advance!

            Cheers,
            Frank

            www.frankwilleke.de
            Only asking personal code questions here.

            1 Reply Last reply Reply Quote 0
            • M
              m_adam
              last edited by

              Unfortunately, I'm not able to reproduce your issue, here the atom example with the Message method adapted like so

              inline void BlitOverlayIcon(IconData* overlay, IconData* dest, Int32 x, Int32 y, Int32 bw, Int32 bh, Int32 xOffset = 0, Int32 yOffset = 32)
              {
              	Int32	 px, py;
              	UInt16 aa, a, rr, r, gg, g, bb, b;
              
              	if (!overlay || !overlay->bmp || !dest || !dest->bmp)
              		return;
              
              	BaseBitmap* channel = overlay->bmp->GetInternalChannel();
              	BaseBitmap* destAlphaChannel = dest->bmp->GetInternalChannel();
              
              	if (!channel)
              		return;
              
              	Int32 destW = dest->w;
              	Int32 destH = dest->h;
              	if (bw > destW)
              		bw = destW;
              	if (bh + yOffset > destH)
              		bh = destH - yOffset;
              	if (bw + xOffset > destW)
              		bw = destW - xOffset;
              
              	aa = a = rr = r = gg = g = bb = b = 0xff;
              	for (py = 0; py < bh; py++)
              	{
              		for (px = 0; px < bw; px++)
              		{
              			// get color and alpha from overlay icon
              			overlay->bmp->GetPixel(x + px, y + py, &r, &g, &b);
              			overlay->bmp->GetAlphaPixel(channel, x + px, y + py, &a);
              
              			// get color and alpha from background icon if available
              			dest->bmp->GetPixel(px + xOffset, py + yOffset, &rr, &gg, &bb);
              			if (destAlphaChannel)
              			{
              				dest->bmp->GetAlphaPixel(destAlphaChannel, px + xOffset, py + yOffset, &aa);
              			}
              
              			// blend overlay color against existing icon background color
              			Float blend = a / 255.0;
              			dest->bmp->SetPixel(px + xOffset, py + yOffset, (Int32)Blend(rr, r, blend), (Int32)Blend(gg, g, blend), (Int32)Blend(bb, b, blend));
              
              			// use only the overlay alpha if the opacity is higher to keep the original icon complete
              			// and only in case the background has an alpha at all
              			if (destAlphaChannel && aa < a)
              			{
              				dest->bmp->SetAlphaPixel(destAlphaChannel, px + xOffset, py + yOffset, a);
              			}
              		}
              	}
              }
              
              Bool AtomObject::Message(GeListNode* node, Int32 type, void* t_data)
              {
              	if (type == MSG_DESCRIPTION_VALIDATE)
              	{
              		BaseContainer* data = ((BaseObject*)node)->GetDataInstance();
              		CutReal(*data, ATOMOBJECT_CRAD, 0.0, data->GetFloat(ATOMOBJECT_SRAD));
              	}
              	if (type == MSG_GETCUSTOMICON)
              	{
              		GetCustomIconData* cid = (GetCustomIconData*)t_data;
              		if (!cid)
              			return false;
              
              		BaseContainer* bc = ((BaseObject*)node)->GetDataInstance();
              
              		CustomIconDrawDelegate drawCallback = [this, bc](IconData& dat) -> void
              		{
              			IconData overlayIcon;
              			Bool overlayIconloaded = false;
              
              			// According to a value in the BaseContainer change the loaded overlay
              			if (bc->GetBool(10000))
              				overlayIconloaded = GetIcon(RESOURCEIMAGE_OK, &overlayIcon);
              			else
              				overlayIconloaded = GetIcon(RESOURCEIMAGE_CANCEL, &overlayIcon);
              
              
              			if (overlayIconloaded)
              			{
              				BlitOverlayIcon(&overlayIcon, &dat, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h);
              			}
              		};
              
              		// Get the default custom icons and settings
              		CustomIconSettings customIconSettings;
              		((BaseObject*)node)->Message(MSG_GETCUSTOMICON_SETTINGS, &customIconSettings);
              		customIconSettings._defaultIconId = bc->GetInt32(Ocube, Onull);
              		customIconSettings._fillDefault = true;
              
              		// Set cid with the passed customIconSettings and callback
              		GetCustomIcon(*cid, customIconSettings, false, nullptr, &drawCallback);
              	}
              
              	return true;
              }
              

              Then if you call in python op[10000] = True, only this instance will be enabled.
              f860b2fd-e602-475e-85ae-a0718bea5386-image.png
              Maybe you can share your code with us, or try to adapt BlitOverlayIcon to fit your need, but here I can't reproduce your issue.

              Cheers,
              Maxime.

              MAXON SDK Specialist

              Development Blog, MAXON Registered Developer

              1 Reply Last reply Reply Quote 0
              • fwilleke80F
                fwilleke80
                last edited by

                Oh right, that really works. Hmmm, no idea what I did before to make it behave so strange.
                I'll build upon this code and come back if there are any more questions.

                Thanks a lot! I'll mark this solved for now.

                Cheers,
                Frank

                www.frankwilleke.de
                Only asking personal code questions here.

                1 Reply Last reply Reply Quote 0
                • fwilleke80F
                  fwilleke80
                  last edited by

                  Here's another question about this...

                  The solution offered in this thread works in R21, S22, and R23. But it does not compile in R20, because there's no customgui_iconchooser.h and no struct CustomIconSettings.

                  How would one do this in R20?

                  Thanks in advance!
                  Cheers,
                  Frank

                  www.frankwilleke.de
                  Only asking personal code questions here.

                  1 Reply Last reply Reply Quote 0
                  • fwilleke80F
                    fwilleke80
                    last edited by fwilleke80

                    My first try was this:
                    (R20 code)

                    BaseContainer* bc = ((BaseObject*)node)->GetDataInstance();
                    
                    IconData overlayIcon;
                    
                    // According to a value in the BaseContainer change the loaded overlay
                    const Int32 iconId = MyFunctionToGetTheIconId();
                    const Bool overlayIconloaded = GetIcon(iconId, &overlayIcon);
                    if (overlayIconloaded && iconId != NOTOK)
                    {
                    	BlitOverlayIcon(&overlayIcon, cid->dat, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h, 16, 16);
                    	cid->filled = true;
                    }
                    

                    This did draw my overlay icon, but the underlying icon is just black. It seems that cid->dat->bmp does not contain anything.

                    Next try:
                    (R20 code)

                    BaseContainer* bc = ((BaseObject*)node)->GetDataInstance();
                    
                    // Load node's icon
                    if (!GetIcon(nodeType, cid->dat))
                    	return false;
                    
                    IconData overlayIcon;
                    
                    // According to a value in the BaseContainer change the loaded overlay
                    const Int32 iconId = MyFunctionToGetTheIconId();
                    const Bool overlayIconloaded = GetIcon(iconId, &overlayIcon);
                    if (overlayIconloaded && iconId != NOTOK)
                    {
                    	BlitOverlayIcon(&overlayIcon, cid->dat, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h, 16, 16);
                    	cid->filled = true;
                    }
                    

                    This didn't change anything. Still a black icon with my overlay on top.

                    Maybe GetIcon() doesn't work for some reason? Let's try AutoBitmap().

                    This crashes:
                    (R20 code)

                    BaseContainer* bc = ((BaseObject*)node)->GetDataInstance();
                    
                    const Int32 nodeType = node->GetType();
                    BaseBitmap *bmp = AutoBitmap(nodeType);
                    
                    bmp->CopyTo(cid->dat->bmp); // <-- crashes here!
                    
                    IconData overlayIcon;
                    
                    // According to a value in the BaseContainer change the loaded overlay
                    const Int32 iconId = MyFunctionToGetTheIconId();
                    const Bool overlayIconloaded = GetIcon(iconId, &overlayIcon);
                    if (overlayIconloaded && iconId != NOTOK)
                    {
                    	BlitOverlayIcon(&overlayIcon, cid->dat, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h, 16, 16);
                    	cid->filled = true;
                    }
                    

                    OK, so that crashes on CopyTo().

                    Last try, use AutoBitmap() to load the icon directly into cid->dat->bmp.
                    (R20 code)

                    BaseContainer* bc = ((BaseObject*)node)->GetDataInstance();
                    				
                    const Int32 nodeType = node->GetType();
                    cid->dat->bmp = AutoBitmap(nodeType);
                    if (!GetIcon(nodeType, cid->dat))
                    	return false;
                    				
                    IconData overlayIcon;
                    				
                    // According to a value in the BaseContainer change the loaded overlay
                    const Int32 iconId = MyFunctionToGetTheIconId();
                    const Bool overlayIconloaded = GetIcon(iconId, &overlayIcon);
                    if (overlayIconloaded && iconId != NOTOK)
                    {
                    	BlitOverlayIcon(&overlayIcon, cid->dat, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h, 16, 16);
                    	cid->filled = true;
                    }
                    

                    This does not crash, but while cid->dat has the correct x, y, b, and h values, cid->dat->bmp has size 0,0. For some reason, the normal icon shows up in the Object Manager.

                    How can I solve this in R20?

                    Thanks for any tips!!

                    Cheers,
                    Frank

                    www.frankwilleke.de
                    Only asking personal code questions here.

                    1 Reply Last reply Reply Quote 0
                    • fwilleke80F
                      fwilleke80
                      last edited by fwilleke80

                      OK, I got it. Once it's all figured out, the solution seems laughably simple.

                      In case anybody else needs to maintain R20 compatibility and wants to use custom icon overlays, here's a solution:

                      In MyObject class declaration:
                      (R20 code)

                      class MyObject : public ObjectData
                      {
                      	INSTANCEOF(MyObject, ObjectData);
                      
                      	// ...
                      
                      private:
                      #if API_VERSION < 21000
                      	BaseBitmap *_customIcon; ///< Used to store a node's custom icon in R20
                      #endif
                      };
                      
                      public:
                      #if API_VERSION < 21000
                      	MyObject() : _customIcon(nullptr)
                      	{
                      	}
                      
                      	~ MyObject()
                      	{
                      		if (_customIcon)
                      			BaseBitmap::Free(_customIcon);
                      	}
                      #endif
                      

                      In MyObject::Message():
                      (R20 code)

                      const BaseContainer &dataRef = op->GetDataInstanceRef();
                      
                      //
                      // 1. Copy default icon into customIcon
                      //
                      
                      // Load default icon
                      IconData defaultIconData;
                      if (!GetIcon(node->GetType(), &defaultIconData))
                      	return false;
                      
                      // Free _customIcon if it has been allocated before, because
                      // it will now receive the pointer to a newly allocated BaseBitmap.
                      if (_customIcon)
                      	BaseBitmap::Free(_customIcon);
                      
                      // Get the actual bitmap that is our icon
                      _customIcon = defaultIconData.GetClonePart();
                      if (!_customIcon)
                      	return false;
                      
                      //
                      // 2. Blit overlay into customIcon
                      //
                      
                      // Load overlay icon
                      IconData overlayIcon;
                      const Int32 overlayIconId = MyFunctionToGetTheIconId();
                      if (overlayIconId == NOTOK)
                      	return false;
                      const Bool overlayIconloaded = GetIcon(overlayIconId, &overlayIcon);
                      if (overlayIconloaded)
                      {
                      	BlitOverlayBitmap(overlayIcon.bmp, _customIcon, overlayIcon.x, overlayIcon.y, overlayIcon.w, overlayIcon.h, 32, 32);
                      
                      	//
                      	// 3. Set cid->dat to use customIcon
                      	//
                      	cid->dat->bmp = _customIcon;
                      	cid->dat->x = 0;
                      	cid->dat->y = 0;
                      	cid->dat->w = _customIcon->GetBw();
                      	cid->dat->h = _customIcon->GetBh();
                      	cid->filled = true;
                      }
                      

                      And BlitOverlayBitmap(), a variation of your BlitOverlayIcon():

                      void BlitOverlayBitmap(BaseBitmap *overlay, BaseBitmap *dest, Int32 x, Int32 y, Int32 bw, Int32 bh, Int32 xOffset, Int32 yOffset)
                      {
                      	if (!overlay || !dest)
                      		return;
                      
                      	BaseBitmap *overlayAlpha = overlay->GetInternalChannel();
                      	BaseBitmap *destAlpha = dest->GetInternalChannel();
                      
                      	const Int32 destW = dest->GetBw();
                      	const Int32 destH = dest->GetBh();
                      	if (bw > destW)
                      		bw = destW;
                      	if (bh + yOffset > destH)
                      		bh = destH - yOffset;
                      	if (bw + xOffset > destW)
                      		bw = destW - xOffset;
                      
                      	UInt16 aa, a, rr, r, gg, g, bb, b;
                      	aa = a = rr = r = gg = g = bb = b = 0xff;
                      
                      	for (Int32 py = 0; py < bh; py++)
                      	{
                      		for (Int32 px = 0; px < bw; px++)
                      		{
                      			// get color and alpha from overlay icon
                      			overlay->GetPixel(x + px, y + py, &r, &g, &b);
                      			overlay->GetAlphaPixel(overlayAlpha, x + px, y + py, &a);
                      
                      			// get color and alpha from background icon if available
                      			dest->GetPixel(px + xOffset, py + yOffset, &rr, &gg, &bb);
                      			if (destAlpha)
                      			{
                      				dest->GetAlphaPixel(destAlpha, px + xOffset, py + yOffset, &aa);
                      			}
                      
                      			// blend overlay color against existing icon background color
                      			Float blend = a / 255.0;
                      			dest->SetPixel(px + xOffset, py + yOffset, (Int32)Blend(rr, r, blend), (Int32)Blend(gg, g, blend), (Int32)Blend(bb, b, blend));
                      
                      			// use only the overlay alpha if the opacity is higher to keep the original icon complete
                      			// and only in case the background has an alpha at all
                      			if (destAlpha && aa < a)
                      			{
                      				dest->SetAlphaPixel(destAlpha, px + xOffset, py + yOffset, a);
                      			}
                      		}
                      	}
                      }
                      

                      Thanks for patience & support!

                      Cheers,
                      Frank

                      www.frankwilleke.de
                      Only asking personal code questions here.

                      1 Reply Last reply Reply Quote 1
                      • First post
                        Last post