Board index » cppbuilder » Creating / mimicing the XP shutdown grey fade...

Creating / mimicing the XP shutdown grey fade...


2006-03-30 07:53:22 AM
cppbuilder87
Couldn't think of a better way to describe my task so sorry about the
ambiguous subject line...
I have a task that I need to complete and I'm not 100% sure how to do it.
Basically I need to recreate the visual effect that Windows XP/2003 have
when you option to shut down or restart. When this happens, it appear as if
the screen fades out so a semi-transparent grey colour.
So I need to mimic the same behaviour in my application. I have one form
(MainForm) that is set as fsStayOnTop, then I need to display another form
(Dialog) on top of that but fade the original one. Now I have a few
thoughts on how to do this but I just wanted to see if anyone else had done
this or could point me in the right direction.
So my thoughts are:
1) Create a hidden panel on the MainForm (assuming that the Mainform has
it's title bar removed...).
2) When fade effect is needed, create a TBitmap and then capture the
'desktop' using Bmp->Canvas->Handle=GetDC(0);
3) Assign the Bmp to the Hidden Panel, then show the panel and maximise to
the full screen.
4) Create a thread or use a timer to increment the colour of this desktop
image.
5) Create the secondary dialog and leave the thread / timer running to
increment the colour of each pixel.
- I'm not sure if that makes sense to anyone else but it makes some sense to
me. My main problem is that I'm not sure of the best way to increment the
colour of the pixels and what sort of algorithm to use to convert an RGB
value to a grey scale colour. Also is it best to alter the Bmp's canvas and
then re-assign or modify the panel directly.
I know this is a bit vague but any advice would be greatly appreciated so as
to steer me down the right path...
Many thanks in advance,
Mike C
 
 

Re:Creating / mimicing the XP shutdown grey fade...

"Mike Collins" < XXXX@XXXXX.COM >wrote:
Quote

[...] When this happens, it appear as if the screen fades
out so a semi-transparent grey colour.

So I need to mimic the same behaviour in my application.
The final presentation of the screen is a simple gray-scale
which is quite easy to mimic:
//-------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Graphics::TBitmap *Bmp = new Graphics::TBitmap;
Bmp->Width = Width;
Bmp->Height = Height;
Bmp->PixelFormat = pf24bit;
HDC hDC = ::GetWindowDC( Handle );
::BitBlt( Bmp->Canvas->Handle, 0, 0, Width, Height, hDC, 0, 0, SRCCOPY );
for( int y = 0; y < Height; ++y )
{
BYTE* ScanLine = static_cast<BYTE*>( Bmp->ScanLine[y] );
for( int x = 0; x < Width; ++x )
{
BYTE R = ScanLine[ x * 3 + 0 ];
BYTE G = ScanLine[ x * 3 + 1 ];
BYTE B = ScanLine[ x * 3 + 2 ];
BYTE L = (BYTE)((R * 0.299) + (G * 0.587) + (B * 0.114));
ScanLine[ x * 3 + 0 ] = L;
ScanLine[ x * 3 + 1 ] = L;
ScanLine[ x * 3 + 2 ] = L;
}
}
::BitBlt( hDC, 0, 0, Width, Height, Bmp->Canvas->Handle, 0, 0, SRCCOPY );
::ReleaseDC( Handle, hDC );
delete Bmp;
}
//-------------------------------------------------------------
However, XP also scales the colors gradually in a loop that's
executes (IIRC) 7 times until it gets to the gray-scale so
it's approach is quite different than the above. I'm not the
graphic's guru but I think that we used a method known as
Gamma Correction to animate the fading.
Of course, the above is an incomplete solution because it
doesn't account for when Windows causes a repaint like
when the window is disabled or looses focus or it's size or
position changes or when part of it is uncovered by another
window.
These conditions can be accounted for by BitBlt(ing) the image
when you detect WM_PAINT or WM_NCPAINT but only if the dialog
is opened.
~ JD
 

Re:Creating / mimicing the XP shutdown grey fade...

"JD" < XXXX@XXXXX.COM >wrote:
Quote

The final presentation of the screen is a simple gray-scale
which is quite easy to mimic:
I peeked at the source today and it's only slightly more
complicated but it's also not mine to give you.
Quote
::BitBlt( Bmp->Canvas->Handle, 0, 0, Width, Height, hDC, 0, 0, SRCCOPY );
After you grab the form's image (we did *not* use TForm's
GetFormImage method) and before you change the image to gray-
scale, you need to itterate the form's Components property
and paint those to the bitmap. For example:
for( int x = 0; x < ComponentCount; ++x )
{
TWinControl *wControl = dynamic_cast<TWinControl*>( Components[x] );
if( wControl )
{
HDC hDC = ::GetWindowDC( wControl->Handle );
::BitBlt( Bmp->Canvas->Handle,
wControl->Left + ::GetSystemMetrics(SM_CXFRAME),
wControl->Top + ::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYFRAME),
wControl->Width + ::GetSystemMetrics(SM_CXFRAME),
wControl->Height + ::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYFRAME),
hDC, 0, 0, SRCCOPY );
::ReleaseDC( wControl->Handle, hDC );
}
}
If the TWinControl has Components as well, the above block of
code needs to be executed for that control as well. Then, if
it has components, it needs to be executed again and so on.
For this reason, the above block needs to be placed into a
recursive function call.
There's also another couple lines of code missing from the
above block that has to do with painting the controls in grey-
scale. As you find a TWinControl, you need to save a pointer
to it along with it's WindowProc method (so that you can restore it later) and then set it's WindowProc to a dummy
method that swallows all of the messages sent to it.
Quote
Of course, the above is an incomplete solution [...]
You will need to add message handling for when the form
receives WM_PAINT and WM_NCPAINT. I don't know (exactly)
how we're doing it but this worked for me:
private:
MESSAGE void __fastcall WMPaint ( TWMPaint &Message );
MESSAGE void __fastcall WMNCPaint( TMessage &Message );
public:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER( WM_PAINT, TWMPaint, WMPaint )
VCL_MESSAGE_HANDLER( WM_NCPAINT, TMessage, WMNCPaint )
END_MESSAGE_MAP( TForm )
//-------------------------------------------------------------
MESSAGE void __fastcall TForm1::WMNCPaint(TMessage &Message)
{
if( Bmp )
{
TRect R = Rect( 0, 0, Width, ::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYFRAME) );
::BitBlt( hWindowDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
R = Rect( 0, ::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYFRAME), ::GetSystemMetrics(SM_CYFRAME), Height );
::BitBlt( hWindowDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
R = Rect( ::GetSystemMetrics(SM_CYFRAME), Height - ::GetSystemMetrics(SM_CYFRAME), Width, Height );
::BitBlt( hWindowDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
R = Rect( Width - ::GetSystemMetrics(SM_CYFRAME), ::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYFRAME), Width, Height );
::BitBlt( hWindowDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
}
else
{
inherited::Dispatch(&Message);
}
}
//-------------------------------------------------------------
MESSAGE void __fastcall TForm1::WMPaint(TWMPaint &Message)
{
if( Bmp )
{
PAINTSTRUCT ps = { 0 };
::BeginPaint( Handle, &ps );
::ValidateRect( Handle, &ps.rcPaint );
TRect R = ps.rcPaint;
R.Left += ::GetSystemMetrics(SM_CYFRAME);
R.Top += ::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYFRAME);
R.Right += ::GetSystemMetrics(SM_CYFRAME);
R.Bottom += ::GetSystemMetrics(SM_CYCAPTION) + ::GetSystemMetrics(SM_CYFRAME);
::BitBlt( hWindowDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
::EndPaint( Handle, &ps );
}
else
{
inherited::Dispatch(&Message);
}
}
//-------------------------------------------------------------
Then, for dialogs, add an OnShow event and for TForm's that
are modal, add an OnActivate event to get the gray-scaled
image painted:
//-------------------------------------------------------------
void __fastcall TForm1::OpenDialog1Show(TObject *Sender)
{
SendMessage( Handle, WM_NCPAINT, 0, 0 );
TRect R = ClientRect;
::InvalidateRect( Handle, &R, FALSE );
}
//-------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
CaptureFormImage();
bool Executed = OpenDialog1->Execute();
ReleaseFormImage();
if( Executed )
{
//
}
}
//-------------------------------------------------------------
void __fastcall TForm2::FormActivate(TObject *Sender)
{
SendMessage( Application->MainForm->Handle, WM_NCPAINT, 0, 0 );
TRect R = Application->MainForm->ClientRect;
::InvalidateRect( Application->MainForm->Handle, &R, FALSE );
}
//-------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
CaptureFormImage();
Form2->ShowModal();
ReleaseFormImage();
}
//-------------------------------------------------------------
~ JD
 

{smallsort}

Re:Creating / mimicing the XP shutdown grey fade...

"JD" < XXXX@XXXXX.COM >wrote:
Quote

I peeked at the source today and it's only slightly more
complicated but it's also not mine to give you.
I looked at it again and it's not doing anything like I
thought it was. If you're interested, I do have a class that
will handle everything for you.
~ JD
 

Re:Creating / mimicing the XP shutdown grey fade...

Hay JD, thanks for post, was so busy that I forgot to get back to you.
Basically I combined what you had posted with some code that I saw from
CodeProject.
Firstly I captured the destop with hDC = ::GetDC(0); I then tried to
gradually fade this to the gray slate final colour but it ended up looking
{*word*99}, stagged and jurkey so when straight to the gray - it looks ok but if
you have any code that achieves that I want, I'd love to have a look.
Thanks again,
Mike C
"JD" < XXXX@XXXXX.COM >wrote in message
Quote

"JD" < XXXX@XXXXX.COM >wrote:
>
>I peeked at the source today and it's only slightly more
>complicated but it's also not mine to give you.

I looked at it again and it's not doing anything like I
thought it was. If you're interested, I do have a class that
will handle everything for you.

~ JD

 

Re:Creating / mimicing the XP shutdown grey fade...

Mike Collins wrote:
Quote
Hay JD, thanks for post, was so busy that I forgot to get back to you.
Basically I combined what you had posted with some code that I saw from
CodeProject.

Firstly I captured the destop with hDC = ::GetDC(0); I then tried to
gradually fade this to the gray slate final colour but it ended up looking
{*word*99}, stagged and jurkey so when straight to the gray - it looks ok but if
you have any code that achieves that I want, I'd love to have a look.
One method is to pixelate-to-grayish.
turn every eighth pixel to gray or black (stagger each line)
turn every 4th, end with every 2nd.
Other method is as JD posted, trend each pixel to r=g=b=128.
One that comes to mind;
#define AdjustColor( c ) (( 128-(c) )/4)
pixel.red += AdjustColor(pixel.red); // move 1/4 towards gray
I think there will be some sign issues with that
There might also be color issues that I think JD's code handled with
the varying rates depending on R,G,B.
 

Re:Creating / mimicing the XP shutdown grey fade...

"Mike Collins" < XXXX@XXXXX.COM >wrote:
Quote

It doesn't fade the image but I think it could with what Bob
posted and a timer:
//-------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TGrayedForm *Dummy = new TGrayedForm( this );
Form2->ShowModal();
delete Dummy;
}
//-------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
TGrayedForm *Dummy = new TGrayedForm( this );
bool Result = OpenDialog1->Execute();
Form2->ShowModal();
delete Dummy;
if( Result )
{
//
}
}
//-------------------------------------------------------------
//-------------------------------------------------------------
#ifndef GrayedFormH
#define GrayedFormH
//-------------------------------------------------------------
#include <Classes.hpp>
#include <ExtCtrls.hpp>
//-------------------------------------------------------------
#define UWM_INITIALIZE (WM_USER + 100)
//-------------------------------------------------------------
class TGrayedForm : public TPanel
{
protected:
virtual void __fastcall PaintWindow(HDC DC);
virtual void __fastcall WndProc( TMessage &Message );
virtual void __fastcall NewFormWndProc( TMessage &Message );
private:
typedef TPanel inherited;
TForm *pForm;
Graphics::TBitmap* Bmp;
TList* NCControlList;
TWndMethod SaveFormWndProc;
void __fastcall NCPaint();
public:
__fastcall TGrayedForm( TComponent *Owner );
__fastcall ~TGrayedForm();
};
//-------------------------------------------------------------
#endif
//-------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "GrayedForm.h"
#pragma package(smart_init)
//-------------------------------------------------------------
__fastcall TGrayedForm::TGrayedForm( TComponent *Owner ) : TPanel( Owner )
{
pForm = dynamic_cast<TForm*>( Owner );
if( !pForm ) throw Exception("TGrayedForm : Owner is not a TForm." );
Parent = pForm;
Visible = false;
BevelOuter = bvNone;
BevelInner = bvNone;
Left = 0;
Top = 0;
Align = alClient;
Bmp = NULL;
NCControlList = NULL;
Application->ProcessMessages();
Bmp = new Graphics::TBitmap;
Bmp->Width = pForm->Width;
Bmp->Height = pForm->Height;
Bmp->PixelFormat = pf24bit;
HDC hDC = ::GetWindowDC( pForm->Handle );
::BitBlt( Bmp->Canvas->Handle, 0, 0, pForm->Width, pForm->Height, hDC, 0, 0, SRCCOPY );
::ReleaseDC( pForm->Handle, hDC );
BYTE R,G,B,L,*ScanLine;
for( int y = 0; y < Bmp->Height; ++y )
{
ScanLine = static_cast<BYTE*>( Bmp->ScanLine[y] );
for( int x = 0; x < Bmp->Width; ++x )
{
R = ScanLine[ x * 3 + 0 ];
G = ScanLine[ x * 3 + 1 ];
B = ScanLine[ x * 3 + 2 ];
L = (BYTE)((R * 0.299) + (G * 0.587) + (B * 0.114));
ScanLine[ x * 3 + 0 ] = L;
ScanLine[ x * 3 + 1 ] = L;
ScanLine[ x * 3 + 2 ] = L;
}
}
SaveFormWndProc = pForm->WindowProc;
pForm->WindowProc = NewFormWndProc;
pForm->Enabled = false;
::PostMessage( Handle, UWM_INITIALIZE, 0, 0 );
}
//-------------------------------------------------------------
__fastcall TGrayedForm::~TGrayedForm()
{
delete Bmp;
if( NCControlList )
{
for( int x = 0; x < NCControlList->Count; ++x )
{
TWinControl *pControl = static_cast<TWinControl*>( NCControlList->Items[x] );
pControl->Visible = true;
}
delete NCControlList;
}
pForm->WindowProc = SaveFormWndProc;
pForm->Enabled = true;
}
//-------------------------------------------------------------
void __fastcall TGrayedForm::NewFormWndProc( TMessage &Message )
{
if( Message.Msg == WM_NCPAINT ) NCPaint();
else SaveFormWndProc( Message );
}
//-------------------------------------------------------------
void __fastcall TGrayedForm::PaintWindow(HDC DC)
{
TRect R;
::GetWindowRect( Handle, &R );
::OffsetRect( &R, -pForm->Left, -pForm->Top );
Canvas->CopyRect( Rect(0, 0, Width, Height), Bmp->Canvas, R );
}
//-------------------------------------------------------------
void __fastcall TGrayedForm::NCPaint()
{
TRect R;
::GetWindowRect( Handle, &R );
HDC hDC = ::GetWindowDC( pForm->Handle );
R = Rect( 0, 0, pForm->Width, R.Top - pForm->Top );
::BitBlt( hDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
R = Rect( 0, R.Bottom, ::GetSystemMetrics(SM_CXFRAME), pForm->Height );
::BitBlt( hDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
R = Rect( pForm->Width - ::GetSystemMetrics(SM_CXFRAME), R.Top, pForm->Width, R.Bottom );
::BitBlt( hDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
R = Rect( ::GetSystemMetrics(SM_CXFRAME), pForm->Height - ::GetSystemMetrics(SM_CYFRAME), pForm->Width - ::GetSystemMetrics(SM_CXFRAME), R.Bottom );
::BitBlt( hDC, R.Left, R.Top, R.Right, R.Bottom, Bmp->Canvas->Handle, R.Left, R.Top, SRCCOPY );
::ReleaseDC( pForm->Handle, hDC );
}
//-------------------------------------------------------------
void __fastcall TGrayedForm::WndProc( TMessage &Message )
{
if( Message.Msg == UWM_INITIALIZE )
{
// need to handle TWinControls that steal from client area
// to simulate that they're part of the non-client area
NCControlList = new TList();
for( int x = 0; x < pForm->ComponentCount; ++x )
{
TWinControl *pControl = dynamic_cast<TWinControl*>( pForm->Components[x] );
if( pControl )
{
TRect R;
::GetWindowRect( pControl->Handle, &R );
TPoint P = Point( 0, 0 );
P = ClientToScreen( P );
if( P.y>= R.Top )
{
pControl->Visible = false;
NCControlList->Add( pControl );
}
}
}
Visible = true;
NCPaint();
}
inherited::WndProc( Message );
}
//-------------------------------------------------------------
~ JD