Click here to Skip to main content
15,887,746 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
Hi guys,

I've been reading articles in this site for a while, but couldn't find what I need. Basically I want to create a non-rectangular window with a shadow along its edges (not the drop shadow, but a thick shadow all around the non-rectangular shape) using Win32 API. The shadow has to be click throughable.

I used windows region and a bitmap before to draw the non-rectangular shape, but that didn't give me a shadow and, according to some articles, it is not efficient. I have been digesting the layered window stuff, but can't quite understand how to use it.

Can someone point me to an article that deals with this? Or kindly show me some code snippet on how this is done? (I am using VC2008 and pure Win32 API)

Thanks!

me4dt
Posted

A little example i use to create iconic windows from PNGs. Layered windows will not work with windows 95,98,Me. Layered windows with alpha channel (RGBA) need not a window region.
Example (the bitmap must be 32bit RGBA):
unsigned char    __alphatable[256][256] = { 42, };

static void __alphatable_init()
{
  unsigned int  x,y;
  if(42==__alphatable[0][0])
  {
    for(x=0;x<256;x++)
      for(y=0;y<256;y++)
        __alphatable[x][y] = min(255,x*y/255);
  }
}

static unsigned char __alphatable_value(unsigned char src,unsigned char dst,unsigned char alpha)
{
  unsigned short p = __alphatable[alpha][src] + __alphatable[255-alpha][dst];
  return min(p,255);
}


HBITMAP __CreateAlphaBitmap(BITMAPINFO* pbmi,const unsigned char* lpbits,const unsigned int cbbits)
{
  HDC              mdc;
  HBITMAP          hbmp;
  unsigned char*  lpc;
  int              x,y;

  mdc = CreateCompatibleDC(0);
  hbmp = CreateDIBSection(mdc,pbmi,DIB_RGB_COLORS,(void**)&lpc,0,0);
  if(lpc)
  {
    __alphatable_init();
    for(y=0;y<pbmi->bmiHeader.biHeight;y++)
    {
      for(x=0;x<pbmi->bmiHeader.biWidth;x++)
      {
        lpc[0] = __alphatable_value(lpbits[0],0,lpbits[3]);
        lpc[1] = __alphatable_value(lpbits[1],0,lpbits[3]);
        lpc[2] = __alphatable_value(lpbits[2],0,lpbits[3]);
        lpc[3] = lpbits[3];
        lpc += 4; lpbits += 4;
      }
    }
  }
  DeleteDC(mdc);
  
  return hbmp;
}

void iWndWidget::SetFace(IBitmap* pBmp,HWND hwnd)
{
  if(!hwnd) return;
  
  unsigned long    exstyle = GetWindowLong(hwnd,GWL_EXSTYLE);
  BITMAPINFO*      pbmp;
  unsigned char*  lp;
  unsigned int    cb;

  if(pBmp && (S_OK==pBmp->GetBitmapDirect(&pbmp,&lp,&cb)) && (32==pbmp->bmiHeader.biBitCount))
  {
    RECT&            rcw = GetWndPos();
    BLENDFUNCTION    blend = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
    HDC              mdc;
    HDC              hdc = 0;
    SIZE            size = { pbmp->bmiHeader.biWidth, pbmp->bmiHeader.biHeight };
    POINT            pdst = { rcw.left,rcw.top };
    POINT            psrc = { 0,0 };
    HGDIOBJ          obmp;
    HBITMAP          hbmp;

    SetWindowLong(hwnd,GWL_EXSTYLE,exstyle|WS_EX_LAYERED);

    hbmp = __CreateAlphaBitmap(pbmp,lp,cb);
    mdc = CreateCompatibleDC(hdc);
    obmp = SelectObject(mdc,hbmp);
    UpdateLayeredWindow(hwnd,hdc,&pdst,&size,mdc,&psrc,(COLORREF)0,&blend,ULW_ALPHA);

    SelectObject(mdc,obmp);
    DeleteDC(mdc);
    DeleteObject(hbmp);

    SetWindowPos(hwnd,0,pdst.x,pdst.y,size.cx,size.cy,
      SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOSENDCHANGING|SWP_NOACTIVATE);
  }
  else
  {
    SetWindowLong(hwnd,GWL_EXSTYLE,exstyle&~WS_EX_LAYERED);
    SetWindowPos(hwnd,0,0,0,0,0,
      SWP_NOSIZE|SWP_NOMOVE|SWP_NOZORDER|SWP_FRAMECHANGED|SWP_NOSENDCHANGING|SWP_NOACTIVATE);
  }
  

}

Regards.
 
Share this answer
 
v2
Comments
Sergey Alexandrovich Kryukov 1-Apr-11 22:55pm    
There is a little problem with this. Is you click on fully transparent region of such window inside window bounds, it will not activate underlying window but will be processed by a window of this application; in other words, it will look as non-rectangular, but when you touch it with mouse, it well behave like rectangular, am I right?
--SA
mbue 2-Apr-11 5:43am    
Layered windows working like windows with region. The part of the window that is fully transparant is not processed by any window message (paint, mouse events and so on). If you make the whole window alpha plane full transparent this works like ShowWindow(SW_HIDE) - you only can kill the app to quit.
Sergey Alexandrovich Kryukov 2-Apr-11 16:34pm    
Very interesting. I want to play a bit with this techniques to understand it better. Thank you very much for this sample. Event is there any problems, it certainly deserves my 5.
--SA
mbue 3-Apr-11 5:08am    
see my last comment. regards.
me4dt 2-Apr-11 18:27pm    
Thanks mbue for your kind reply. I'll try your code and see what it can accomplish. Seems you did not use the TRANSPARENT bit, so areas with alpha value zero will be transparent. Am I correct?

Can you explain your code a little more so I may have a better understanding? Cause I thought when alpha value is zero, the pixel is invisible. Then how the shadow is shown? Also, I thought TRANSPARENT bit does not mean the pixel is really transparent (invisible), it only means to ignore messages. Correct me if I am wrong. I am really fuzzy on this.

Best regards.

me4dt
It looks like it is impossible.
The real problem is that the window region should be a sharp, not fuzzy subset of the windows rectangular area. This is the limitation of the area. The shadow is by definition is a part of the other window, not part of the windows of your application. When you click at the shadow, you supposed to activate the window underneath. But you cannot affect other windows (unless you're injecting some code into them... well, does not look realistic.

Well, suppose you can sacrifice this part of functionality — you agree to make the shadow actually a part of your windows at the edges. But in this case you need to show different picture at the shadow area, depending on the colors on the windows underneath. It means that you need modulation of transparency of the window: semi-transparent at the edge and not-transparent in the main area. To best of my knowledge, this effect it not available in the Windows API. Only uniform transparency factor is available (and rendering is somewhat problematic, it's recommended to avoid windows transparency).

So far, I was talking about Windows API before Windows 7 (XP and older). For this system, you can adjust color and transparency of the borders — see http://www.microsoft.com/enable/training/windowsvista/borders.aspx[^]. This might allow for some effects resembling shadow cast on underlying windows. The problem is: I never heard the border can be of custom shape. Perhaps I'm not well familiar with Windows 7 specific API.

It looks like we're close but not there yet. You can do any thinkable semi-transparent trick inside a window (using alpha channel of colors and brushes used for rendering), but at to the window it self— Anyway, I will be amazed if somebody points out solution. I think it does not exist.

—SA
 
Share this answer
 
Comments
me4dt 2-Apr-11 18:09pm    
At first I thought the transparent bit and alpha feature of layered window could help me on creating a click-through shadow. Then I couldn't figure out a way to do this. I was wondering if there is a trick to get it done. Oh well ...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900