duilib支持了窗体阴影,有算法阴影和图片阴影两种,但是算法阴影的效果不尽人意,参数设置也不能理解
之前写过一篇关于阴影的duilib窗体阴影偏移情况 纠正了图片的一些偏移情况,今天偶然看到css里设置box-shadow的阴影启示,何不把duilib也弄成了css里的? 所以开始了这篇文章,原理很简单,算法模式更换成先画小矩形的阴影,在模糊化,图片添加到CPaintManagerUI中,后面的事儿就和图片一样了。 效果如图:
Paste_Image.png
参数的设置和css一样了,radius、spread、ofset、color 颜色支持透明度。 属性列表:
参数修改了当然要修改UIDlgBuilder,参数关键词修改如下,把带shadow的标签词替换成下面的。
else if( _tcsicmp(pstrName, _T("shadowradius")) == 0 ) {
pManager->GetShadow()->SetRadius(_ttoi(pstrValue));
}
else if( _tcsicmp(pstrName, _T("shadowspread")) == 0 ) {
pManager->GetShadow()->SetSpread(_ttoi(pstrValue));
}
else if( _tcsicmp(pstrName, _T("shadowoffset")) == 0 ) {
LPTSTR pstr = NULL;
int cx = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr);
int cy = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
pManager->GetShadow()->SetOffset(cx, cy);
}
else if( _tcsicmp(pstrName, _T("shadowcolor")) == 0 ) {
if( *pstrValue == _T('#')) pstrValue = ::CharNext(pstrValue);
LPTSTR pstr = NULL;
DWORD clrColor = _tcstoul(pstrValue, &pstr, 16);
pManager->GetShadow()->SetColor(clrColor);
}
else if( _tcsicmp(pstrName, _T("shadowcorner")) == 0 ) {
RECT rcCorner = { 0 };
LPTSTR pstr = NULL;
rcCorner.left = _tcstol(pstrValue, &pstr, 10); ASSERT(pstr);
rcCorner.top = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
rcCorner.right = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
rcCorner.bottom = _tcstol(pstr + 1, &pstr, 10); ASSERT(pstr);
pManager->GetShadow()->SetShadowCorner(rcCorner);
}
else if( _tcsicmp(pstrName, _T("shadowimage")) == 0 ) {
pManager->GetShadow()->SetImage(pstrValue);
}
else if( _tcsicmp(pstrName, _T("showshadow")) == 0 ) {
pManager->GetShadow()->ShowShadow(_tcsicmp(pstrValue, _T("true")) == 0);
}
UIShadow.h和UIShadow.cpp文件
//UIShadow.h
// WndShadow.h : header file
//
// Version 0.1
//
// Copyright (c) 2006 Perry Zhu, All Rights Reserved.
//
// mailto:perry@live.com
//
//
// This source file may be redistributed unmodified by any means PROVIDING
// it is NOT sold for profit without the authors expressed written
// consent, and providing that this notice and the author's name and all
// copyright notices remain intact. This software is by no means to be
// included as part of any third party components library, or as part any
// development solution that offers MFC extensions that are sold for profit.
//
// If the source code is used in any commercial applications then a statement
// along the lines of:
//
// "Portions Copyright (c) 2006 Perry Zhu" must be included in the "Startup
// Banner", "About Box" or "Printed Documentation". This software is provided
// "as is" without express or implied warranty. Use it at your own risk! The
// author accepts no liability for any damage/loss of business that this
// product may cause.
//
/
//****************************************************************************
#ifndef __UISHADOW_H__
#define __UISHADOW_H__
#pragma once
#include "map"
namespace DuiLib
{
class UILIB_API CShadowUI
{
public:
friend class CPaintManagerUI;
CShadowUI(void);
virtual ~CShadowUI(void);
public:
// bShow为真时才会创建阴影
void ShowShadow(bool bShow);
bool IsShowShadow() const;
void DisableShadow(bool bDisable);
bool IsDisableShadow() const;
// 算法阴影的函数
bool SetRadius(int NewSize = 0);
DWORD GetRadius() const;
bool SetSpread(int NewSharpness);
DWORD GetSpread() const;
bool SetOffset(int NewXOffset, int NewYOffset);
SIZE GetOffset() const;
bool SetColor(COLORREF NewColor);
DWORD GetCorlor() const;
// 图片阴影的函数
bool SetImage(LPCTSTR szImage);
LPCTSTR GetImage() const;
bool SetShadowCorner(RECT rcCorner); // 九宫格方式描述阴影
RECT GetShadowCorner() const;
// 把自己的阴影样式复制到传入参数
bool CopyShadow(CShadowUI* pShadow);
// 创建阴影窗体,由CPaintManagerUI自动调用,除非自己要单独创建阴影
void Create(CPaintManagerUI* pPaintManager);
protected:
// 初始化并注册阴影类
static bool Initialize(HINSTANCE hInstance);
// 保存已经附加的窗体句柄和与其关联的阴影类,方便在ParentProc()函数中通过句柄得到阴影类
static std::map& GetShadowMap();
// 子类化父窗体
static LRESULT CALLBACK ParentProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
// 父窗体改变大小,移动,或者主动重绘阴影时调用
void Update(HWND hParent);
//快速模糊化
static void superFastBlur(unsigned char *pix, int w, int h, int radius);
// 通过算法计算阴影
void MakeShadowImage(HDC hDc);
protected:
enum ShadowStatus
{
SS_ENABLED = 1, // Shadow is enabled, if not, the following one is always false
SS_VISABLE = 1 m_bIsDisableShadow) {
#pragma warning(disable: 4312) // temporrarily disable the type_cast warning in Win32
// Call the default(original) window procedure for other messages or messages processed but not returned
return ((WNDPROC)pThis->m_OriParentProc)(hwnd, uMsg, wParam, lParam);
#pragma warning(default: 4312)
}
switch(uMsg)
{
case WM_WINDOWPOSCHANGED:
RECT WndRect;
GetWindowRect(hwnd, &WndRect);
if (pThis->m_bIsImageMode) {
SetWindowPos(pThis->m_hWnd, 0, WndRect.left - pThis->m_rcShadowCorner.left, WndRect.top - pThis->m_rcShadowCorner.top, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
}
else {
SetWindowPos(pThis->m_hWnd, 0, WndRect.left + pThis->m_szOffset.cx - pThis->m_Radius - pThis->m_Spread, WndRect.top + pThis->m_szOffset.cy - pThis->m_Radius - pThis->m_Spread, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
}
break;
case WM_MOVE:
if(pThis->m_Status & SS_VISABLE)
{
RECT WndRect;
GetWindowRect(hwnd, &WndRect);
if (pThis->m_bIsImageMode) {
SetWindowPos(pThis->m_hWnd, 0, WndRect.left - pThis->m_rcShadowCorner.left, WndRect.top - pThis->m_rcShadowCorner.top, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
}
else {
SetWindowPos(pThis->m_hWnd, 0, WndRect.left + pThis->m_szOffset.cx - pThis->m_Radius - pThis->m_Spread, WndRect.top + pThis->m_szOffset.cy - pThis->m_Radius - pThis->m_Spread, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE);
}
}
break;
case WM_SIZE:
if(pThis->m_Status & SS_ENABLED)
{
if(SIZE_MAXIMIZED == wParam || SIZE_MINIMIZED == wParam)
{
::ShowWindow(pThis->m_hWnd, SW_HIDE);
pThis->m_Status &= ~SS_VISABLE;
}
else if(pThis->m_Status & SS_PARENTVISIBLE) // Parent maybe resized even if invisible
{
// Awful! It seems that if the window size was not decreased
// the window region would never be updated until WM_PAINT was sent.
// So do not Update() until next WM_PAINT is received in this case
if(LOWORD(lParam) > LOWORD(pThis->m_WndSize) || HIWORD(lParam) > HIWORD(pThis->m_WndSize))
pThis->m_bUpdate = true;
else
pThis->Update(hwnd);
if(!(pThis->m_Status & SS_VISABLE))
{
::ShowWindow(pThis->m_hWnd, SW_SHOWNOACTIVATE);
pThis->m_Status |= SS_VISABLE;
}
}
pThis->m_WndSize = lParam;
}
break;
case WM_PAINT:
{
if(pThis->m_bUpdate)
{
pThis->Update(hwnd);
pThis->m_bUpdate = false;
}
//return hr;
break;
}
// In some cases of sizing, the up-right corner of the parent window region would not be properly updated
// Update() again when sizing is finished
case WM_EXITSIZEMOVE:
if(pThis->m_Status & SS_VISABLE)
{
pThis->Update(hwnd);
}
break;
case WM_SHOWWINDOW:
if(pThis->m_Status & SS_ENABLED)
{
if(!wParam) // the window is being hidden
{
::ShowWindow(pThis->m_hWnd, SW_HIDE);
pThis->m_Status &= ~(SS_VISABLE | SS_PARENTVISIBLE);
}
else if(!(pThis->m_Status & SS_PARENTVISIBLE))
{
//pThis->Update(hwnd);
pThis->m_bUpdate = true;
::ShowWindow(pThis->m_hWnd, SW_SHOWNOACTIVATE);
pThis->m_Status |= SS_VISABLE | SS_PARENTVISIBLE;
}
}
break;
case WM_DESTROY:
DestroyWindow(pThis->m_hWnd); // Destroy the shadow
break;
case WM_NCDESTROY:
GetShadowMap().erase(hwnd); // Remove this window and shadow from the map
break;
}
#pragma warning(disable: 4312) // temporrarily disable the type_cast warning in Win32
// Call the default(original) window procedure for other messages or messages processed but not returned
return ((WNDPROC)pThis->m_OriParentProc)(hwnd, uMsg, wParam, lParam);
#pragma warning(default: 4312)
}
void GetLastErrorMessage() { //Formats GetLastError() value.
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf, 0, NULL
);
// Display the string.
//MessageBox(NULL, (const wchar_t*)lpMsgBuf, L"GetLastError", MB_OK | MB_ICONINFORMATION);
// Free the buffer.
LocalFree(lpMsgBuf);
}
void CShadowUI::Update(HWND hParent)
{
if(!m_bIsShowShadow || !(m_Status & SS_VISABLE)) return;
RECT WndRect;
GetWindowRect(hParent, &WndRect);
int nShadWndWid;
int nShadWndHei;
if (m_bIsImageMode) {
if(m_sShadowImage.IsEmpty()) return;
nShadWndWid = WndRect.right - WndRect.left + m_rcShadowCorner.left + m_rcShadowCorner.right;
nShadWndHei = WndRect.bottom - WndRect.top + m_rcShadowCorner.top + m_rcShadowCorner.bottom;
}
else {
if (m_Radius == 0) return;
nShadWndWid = WndRect.right - WndRect.left + (m_Radius + m_Spread) * 2;
nShadWndHei = WndRect.bottom - WndRect.top + (m_Radius + m_Spread) * 2;
}
// Create the alpha blending bitmap
BITMAPINFO bmi; // bitmap header
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = nShadWndWid;
bmi.bmiHeader.biHeight = nShadWndHei;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32; // four 8-bit components
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = nShadWndWid * nShadWndHei * 4;
BYTE *pvBits; // pointer to DIB section
HBITMAP hbitmap = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&pvBits, NULL, 0);
if (hbitmap == NULL) {
GetLastErrorMessage();
}
HDC hMemDC = CreateCompatibleDC(NULL);
if (hMemDC == NULL) {
GetLastErrorMessage();
}
HBITMAP hOriBmp = (HBITMAP)SelectObject(hMemDC, hbitmap);
if (GetLastError()!=0) {
GetLastErrorMessage();
}
if (m_bIsImageMode)
{
RECT rcPaint = {0, 0, nShadWndWid, nShadWndHei};
const TImageInfo* data = m_pManager->GetImageEx((LPCTSTR)m_sShadowImage, NULL, 0);
if( !data ) return;
RECT rcBmpPart = {0};
rcBmpPart.right = data->nX;
rcBmpPart.bottom = data->nY;
RECT corner = m_rcShadowCorner;
CRenderEngine::DrawImage(hMemDC, data->hBitmap, rcPaint, rcPaint, rcBmpPart, corner, data->bAlpha, 0xFF, true, false, false);
}
else
{
MakeShadowImage(hMemDC);
RECT rcPaint = { 0, 0, nShadWndWid, nShadWndHei };
const TImageInfo* data = m_pManager->GetImage(m_sMadeShadowImage);
if (!data) return;
RECT rcBmpPart = { 0 };
rcBmpPart.right = data->nX;
rcBmpPart.bottom = data->nY;
RECT corner = { m_Radius + m_Spread - m_szOffset.cx, m_Radius + m_Spread - m_szOffset.cy, m_Radius + m_Spread + m_szOffset.cx, m_Radius + m_Spread + m_szOffset.cy };
CRenderEngine::DrawImage(hMemDC, data->hBitmap, rcPaint, rcPaint, rcBmpPart, corner, data->bAlpha, 0xFF, true, false, false);
}
POINT ptDst;
if (m_bIsImageMode)
{
ptDst.x = WndRect.left - m_rcShadowCorner.left;
ptDst.y = WndRect.top - m_rcShadowCorner.top;
}
else
{
ptDst.x = WndRect.left + m_szOffset.cx - m_Radius - m_Spread;
ptDst.y = WndRect.top + m_szOffset.cy - m_Radius - m_Spread;
}
POINT ptSrc = {0, 0};
SIZE WndSize = {nShadWndWid, nShadWndHei};
BLENDFUNCTION blendPixelFunction= { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
MoveWindow(m_hWnd, ptDst.x, ptDst.y, nShadWndWid, nShadWndHei, FALSE);
BOOL bRet= ::UpdateLayeredWindow(m_hWnd, NULL, &ptDst, &WndSize, hMemDC, &ptSrc, 0, &blendPixelFunction, ULW_ALPHA);
_ASSERT(bRet); // something was wrong....
// Delete used resources
SelectObject(hMemDC, hOriBmp);
DeleteObject(hbitmap);
DeleteDC(hMemDC);
}
/*
* Super Fast Blur v1.1
* Original author: Mario Klingemann (C++ version)
* Original address: http://incubator.quasimondo.com/processing/superfastblur.pde
* C version updated by Lellansin (http://www.lellansin.com)
*/
//32位图
void CShadowUI::superFastBlur(unsigned char *pix, int w, int h, int radius)
{
int div;
int wm, hm, wh;
int *vMIN, *vMAX;
unsigned char *r, *g, *b,*a, *dv;
int rsum, gsum, bsum,asum, x, y, i, p, p1, p2, yp, yi, yw;
if (radius < 1) return;
wm = w - 1;
hm = h - 1;
wh = w * h;
div = radius + radius + 1;
vMIN = (int *)malloc(sizeof(int)* max(w, h));
vMAX = (int *)malloc(sizeof(int)* max(w, h));
r = (unsigned char *)malloc(sizeof(unsigned char)* wh);
g = (unsigned char *)malloc(sizeof(unsigned char)* wh);
b = (unsigned char *)malloc(sizeof(unsigned char)* wh);
a = (unsigned char *)malloc(sizeof(unsigned char)* wh);
dv = (unsigned char *)malloc(sizeof(unsigned char)* 256 * div);
for (i = 0; i < 256 * div; i++)
dv[i] = (i / div);
yw = yi = 0;
for (y = 0; y < h; y++)
{
rsum = gsum = bsum = asum=0;
for (i = -radius; i AddImage(m_sMadeShadowImage, hbitmap, srcWidth, srcHeigh, true, false);
}
void CShadowUI::ShowShadow(bool bShow)
{
m_bIsShowShadow = bShow;
}
bool CShadowUI::IsShowShadow() const
{
return m_bIsShowShadow;
}
void CShadowUI::DisableShadow(bool bDisable) {
m_bIsDisableShadow = bDisable;
if (m_hWnd != NULL) {
if (m_bIsDisableShadow) {
::ShowWindow(m_hWnd, SW_HIDE);
}
else {
// Determine the initial show state of shadow according to parent window's state
LONG lParentStyle = GetWindowLongPtr(GetParent(m_hWnd), GWL_STYLE);
if (!(WS_VISIBLE & lParentStyle)) // Parent invisible
m_Status = SS_ENABLED;
else if ((WS_MAXIMIZE | WS_MINIMIZE) & lParentStyle) // Parent visible but does not need shadow
m_Status = SS_ENABLED | SS_PARENTVISIBLE;
else // Show the shadow
{
m_Status = SS_ENABLED | SS_VISABLE | SS_PARENTVISIBLE;
}
if ((WS_VISIBLE & lParentStyle) && !((WS_MAXIMIZE | WS_MINIMIZE) & lParentStyle))// Parent visible && no maxsize or min size
{
::ShowWindow(m_hWnd, SW_SHOWNOACTIVATE);
Update(GetParent(m_hWnd));
}
}
}
}
TODO shadow disnable fix
bool CShadowUI::IsDisableShadow() const {
return m_bIsDisableShadow;
}
bool CShadowUI::SetRadius(int NewRadius)
{
if(NewRadius > 20 || NewRadius < -20)
return false;
m_Radius = (signed char)NewRadius;
if(m_hWnd != NULL && (SS_VISABLE & m_Status))
Update(GetParent(m_hWnd));
return true;
}
DWORD CShadowUI::GetRadius() const
{
return m_Radius;
}
bool CShadowUI::SetSpread(int NewSpread)
{
if(NewSpread > 20)
return false;
m_Spread = (unsigned char)NewSpread;
if(m_hWnd != NULL && (SS_VISABLE & m_Status))
Update(GetParent(m_hWnd));
return true;
}
DWORD CShadowUI::GetSpread() const
{
return m_Spread;
}
bool CShadowUI::SetOffset(int NewXOffset, int NewYOffset)
{
if(NewXOffset > 20 || NewXOffset < -20 ||
NewYOffset > 20 || NewYOffset < -20)
return false;
m_szOffset.cx = NewXOffset;
m_szOffset.cy = NewYOffset;
if(m_hWnd != NULL && (SS_VISABLE & m_Status))
Update(GetParent(m_hWnd));
return true;
}
SIZE CShadowUI::GetOffset() const
{
return m_szOffset;
}
bool CShadowUI::SetColor(COLORREF NewColor)
{
m_ShadowColor = NewColor;
if(m_hWnd != NULL && (SS_VISABLE & m_Status))
Update(GetParent(m_hWnd));
return true;
}
DWORD CShadowUI::GetCorlor() const
{
return (DWORD)m_ShadowColor;
}
bool CShadowUI::SetImage(LPCTSTR szImage)
{
if (szImage == NULL)
return false;
m_bIsImageMode = true;
m_sShadowImage = szImage;
if(m_hWnd != NULL && (SS_VISABLE & m_Status))
Update(GetParent(m_hWnd));
return true;
}
LPCTSTR CShadowUI::GetImage() const
{
return m_sShadowImage.GetData();
}
bool CShadowUI::SetShadowCorner(RECT rcCorner)
{
if (rcCorner.left < 0 || rcCorner.top < 0 || rcCorner.right < 0 || rcCorner.bottom < 0) return false;
m_rcShadowCorner = rcCorner;
if(m_hWnd != NULL && (SS_VISABLE & m_Status)) {
Update(GetParent(m_hWnd));
}
return true;
}
RECT CShadowUI::GetShadowCorner() const
{
return m_rcShadowCorner;
}
bool CShadowUI::CopyShadow(CShadowUI* pShadow)
{
if (m_bIsImageMode) {
pShadow->SetImage(m_sShadowImage);
pShadow->SetShadowCorner(m_rcShadowCorner);
}
else {
pShadow->SetRadius(m_Radius);
pShadow->SetSpread(m_Spread);
pShadow->SetColor(m_ShadowColor);
pShadow->SetOffset(m_szOffset.cx, m_szOffset.cy);
}
pShadow->ShowShadow(m_bIsShowShadow);
return true;
}
} //namespace DuiLib