Overlaying icons (like Alembic)
-
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?
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 -
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. -
Nice, thank you!
-
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
CustomIconSettings
andGetCustomIcon()
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 IDOcube
?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 -
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.
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. -
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 -
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 nostruct CustomIconSettings
.How would one do this in R20?
Thanks in advance!
Cheers,
Frank -
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 tryAutoBitmap()
.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 intocid->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 correctx
,y
,b
, andh
values,cid->dat->bmp
has size0,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 -
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 yourBlitOverlayIcon()
: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