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