DuiLib Window Shadow Implementation

XML Configuratino Attributes

<Attribute name="showshadow" default="false" type="BOOL" comment="Enable window shadow"/>
<Attribute name="shadowimage" default="" type="STRING" comment="Shadow image path (disables algorithmic shadow)"/>
<Attribute name="shadowcorner" default="0,0,0,0" type="RECT" comment="Nine-grid shadow corner definition"/>
<Attribute name="shadowradius" default="10" type="BYTE" comment="Blur radius (0-20)"/>
<Attribute name="shadowspread" default="0" type="BYTE" comment="Shadow expansion"/>
<Attribute name="shadowoffset" default="0,0" type="SIZE" comment="Shadow displacement"/>
<Attribute name="shadowcolor" default="0x2f000000" type="DWORD" comment="ARGB shadow color"/>

Dialog Builder Attribute Handling

if (::_tcsicmp(attrName, _T("shadowradius")) == 0) {
  pManager->GetShadow()->SetBlurRadius(::_ttoi(attrValue));
}
else if (::_tcsicmp(attrName, _T("shadowspread")) == 0) {
  pManager->GetShadow()->SetSpreadAmount(::_ttoi(attrValue));
}
else if (::_tcsicmp(attrName, _T("shadowoffset")) == 0) {
  TCHAR* parsePtr = nullptr;
  int offsetX = ::_tcstol(attrValue, &parsePtr, 10);
  int offsetY = ::_tcstol(parsePtr + 1, &parsePtr, 10);
  pManager->GetShadow()->SetDisplacement(offsetX, offsetY);
}
else if (::_tcsicmp(attrName, _T("shadowcolor")) == 0) {
  DWORD colorValue = ::_tcstoul(attrValue[0] == _T('#') ? attrValue + 1 : attrValue, nullptr, 16);
  pManager->GetShadow()->SetShadowColor(colorValue);
}
else if (::_tcsicmp(attrName, _T("shadowimage")) == 0) {
  pManager->GetShadow()->SetImageResource(attrValue);
}

Shadow Class Interface

class UILIB_API CShadowUI {
public:
  void EnableShadow(bool enable);
  void SetBlurRadius(int radius);
  void SetSpreadAmount(int spread);
  void SetDisplacement(int x, int y);
  void SetShadowColor(COLORREF color);
  void SetImageResource(LPCTSTR imagePath);
  void Create(CPaintManagerUI* manager);

private:
  void UpdateShadowAppearance();
  void GenerateBlurEffect(HDC targetDC);
  static void ApplyBlur(unsigned char* pixels, int width, int height, int radius);
  
  HWND shadowWindow_;
  LONG_PTR originalWndProc_;
  int blurRadius_;
  int spreadAmount_;
  SIZE displacement_;
  COLORREF shadowColor_;
  CDuiString imagePath_;
  RECT cornerDefinition_;
  bool useImageMode_;
};

Core Implementation Logic

void CShadowUI::Create(CPaintManagerUI* manager) {
  manager_ = manager;
  HWND parentHwnd = manager->GetPaintWindow();
  
  shadowWindow_ = ::CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, 
                                  className_, nullptr, WS_POPUP,
                                  0, 0, 0, 0, parentHwnd, nullptr, nullptr, nullptr);

  originalWndProc_ = ::SetWindowLongPtr(parentHwnd, GWLP_WNDPROC, 
                                      reinterpret_cast<long_ptr>(ParentMessageHandler));
}

void CShadowUI::UpdateShadowAppearance() {
  RECT parentRect;
  ::GetWindowRect(manager_->GetPaintWindow(), &parentRect);
  
  int width = parentRect.right - parentRect.left + (blurRadius_ + spreadAmount_) * 2;
  int height = parentRect.bottom - parentRect.top + (blurRadius_ + spreadAmount_) * 2;
  
  HDC memDC = ::CreateCompatibleDC(nullptr);
  BITMAPINFO bmi = { sizeof(BITMAPINFOHEADER), width, height, 1, 32 };
  unsigned char* pixelData = nullptr;
  HBITMAP bitmap = ::CreateDIBSection(memDC, &bmi, DIB_RGB_COLORS, (void**)&pixelData, nullptr, 0);
  
  if (useImageMode_) {
    // Render image-based shadow
  } else {
    GenerateBlurEffect(memDC);
  }
  
  POINT windowPos = { 
    parentRect.left + displacement_.cx - blurRadius_ - spreadAmount_,
    parentRect.top + displacement_.cy - blurRadius_ - spreadAmount_ 
  };
  SIZE windowSize = { width, height };
  BLENDFUNCTION blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
  ::UpdateLayeredWindow(shadowWindow_, nullptr, &windowPos, &windowSize, memDC, nullptr, 0, &blend, ULW_ALPHA);
  
  ::DeleteObject(bitmap);
  ::DeleteDC(memDC);
}

void CShadowUI::ApplyBlur(unsigned char* pixels, int width, int height, int radius) {
  if (radius < 1) return;
  
  int divisor = radius * 2 + 1;
  std::vector<unsigned char> blurMap(256 * divisor);
  for (int i = 0; i < 256 * divisor; ++i) 
    blurMap[i] = static_cast<unsigned char>(i / divisor);
  
  // Horizontal and vertical blur passes
  // Implementation of separable box blur algorithm
}</long_ptr>

Tags: DuiLib Win32API WindowShadow cpp GUIFramework

Posted on Tue, 22 Sep 2026 16:41:53 +0000 by nel