Animated Transparent Windows v.1.2
Since I have published smallGoblin in spring 2000, there were many letters with questions - how to create such software? So, here is a short overview, to help those, who spend time, creating useless & crazy programs.
The most important problem for Screen Mate is - how to make a window transparent?
There seems to be 4 ways to do this in MS Windows:
See also the Conclusions
1. Redrawing background manually
This strategy was used by almost all old Screen Mates, including famous Scam Poo. It is still popular among some developers.
The trick is to receive a part of a desktop, which is covered by Screen Mate's window, as an image. And then draw it in the Screen Mate's window. Then to draw in the same window the image of the Screen Mate, without changing pixels, which are transparent. And it'll look like the image is transparent.
I have also heard, that if standard WM_ERASEBKGND (or smth. like this ;-)) event handler for window is overrided with the empty function (returning proper value), we'll have the same effect. But I failed to do this (frankly speaking I haven't tried much).
The advantage of this technology is that it works on any Windows - from 3.1 till 2000 (I guess so :)). It is also quite fast.
The disadvantage is big - background is redrawing not very good. Especially when user opens, closes, moves windows behind the Screen Mate - the Mate does not redrawing background quickly enough, the image twinkles, etc.
Essentially, there is not much known for me about this technique, 'cause we selected Win32 regions as an engine for smallGoblin.
2.1 Transparent window
This is a legal way to make transparent window in MS Windows (since Windows 95). Here is the code:
--------------- [cut] -----------
// Defining variables
HRGN rgn1, rgn2; // regions
HWND hwnd1; //
window handle
// Create any regions with the standard Win32 function
(CreateRectRgn, CreateEllipticRgn, CreatePolygonRgn, etc.):
rgn1 =
CreateEllipticRgn(0,0,10,10); // create circle 1
rgn2 = CreateEllipticRgn(5,5,20,20); // create circle 2
// Combine a complex region from simple regions
CombineRgn(rgn1,rgn1,rgn2,RGN_OR); // combine two circles
// Delete useless object
DeleteObject(rgn2);
// delete useless region of circle 1
hwnd1 = ... ; // hwnd1 must point to the form window, which
will be transparent
// Set the window region of a window Handle
if ( rgn1 != 0 ) SetWindowRgn( hwnd1 , rgn1, true ); // applying region
to the window
--------------- [cut] -----------
2.2 Transparent image
Now we can make transparent windows. Ok, but how
to create a region for the specific image? And to make it transparent for
a special color in the image? Well, I don't know the excellent solution. It's
possible to create an array of points of polygon which describes
the image, and use CreatePolygonRgn() Win32 function to create a polygonal region.
Or to use the algorithm of smallGobiln. It is divided into 2
parts (for quick dynamic animation. see the next section). First part creates a
temporary array of rectangles (rects) on program start. The second part creates
a region from this array with CreateRectRgn() / CombineRgn() Win32
functions (dynamically on every window repaint).
The main idea is
simple. Function scans the image, pixel by pixel, creates a small
rectangular regions for every not-transparent pixel & combine them into 1 main region.
The smallGoblin algorithm is just the improved version of this idea. We are creating regions not for
pixels, but for vertical lines. We also can reduce the number of lines by
combining them into rectangles (PackRectListV()) when it is possible.
Here are corresponding functions from smallGoblin code (C++Builder 3):
--------------- [cut] -----------
#define iSprites 36 // we have 36
sprites
unsigned char RegionRects[iSprites][4][76]; // 36
sprites,
// 4 coordinates (left,top,right,bottom), 76 maximum rects
unsigned char NumberOfRects[iSprites];
// number of rects in every array
....
// Create an array of rects from an image in Can; image rect= R; store
info in a global array with
// Id = iSprite
void CreateRectListV( TCanvas* Can, TRect* R, int iSprite)
{
int X,Y,Ye ; //
picture - scanning variables
int clTransparent; // transparent color
int iRects =
0; // rects counter
X =
R->Left; //
start scanning from left-top cornen
Y = R->Top;
clTransparent =
Can->Pixels[X][Y]; // First top-left point is considered to be
// a transparent color
do
{
// take first point
while (Can->Pixels[X][Y] ==
clTransparent )
{
Y++ ;
if ( Y >R->Bottom )
{
Y = R->Top; X++;
if ( X >R->Right ) break;
}
}
if ( X >R->Right ) break;
// take end point
Ye = Y;
while (Can->Pixels[X][Ye] != clTransparent )
{
Ye++;
if ( Ye >R->Bottom ) break;
}
// now storing new rect in a global array
frmGoblin->RegionRects[iSprite][0][iRects] = X -R->Left;
frmGoblin->RegionRects[iSprite][1][iRects] = Y -R->Top;
frmGoblin->RegionRects[iSprite][2][iRects] = X -R->Left;
frmGoblin->RegionRects[iSprite][3][iRects] = Ye - 1 -R->Top;
iRects++;
if (iRects>75)
{
ShowMessage("Internal Error - Too many rects " + IntToStr(iSprite));
break;
}
if (Ye >R->Bottom ) { Ye = R->Top; X++; }
Y =
Ye;
}
while ( X <=
R->Right );
// storing the number of rects, in the global array
frmGoblin->NumberOfRects[iSprite] = iRects;
}
//-------------------------------------------------------------------------
// This function pack all rects (to near rects are converted to 1 rect),
// so that they'll take less place
// returns how many rectangles saved, in %
int PackRectListV( )
{
-- skipped --
}
//-------------------------------------------------------------------------
// This function dynamically creates a region from rects array
// iSprite - ID of the rect array to use
HRGN CreateRgnFormRects( int iSprite)
{
HRGN h1,h2;
int iRects =
frmGoblin->NumberOfRects[iSprite];
if ( iRects < 1 ) return 0;
h1 =
CreateRectRgn(frmGoblin->RegionRects[iSprite][0][0],
frmGoblin->RegionRects[iSprite][1][0],
frmGoblin->RegionRects[iSprite][2][0]+1,
frmGoblin->RegionRects[iSprite][3][0]+1);
if ( iRects == 1 ) return h1;
for ( int i = 1; i < iRects; i++ )
{
h2 =
CreateRectRgn(frmGoblin->RegionRects[iSprite][0][i],
frmGoblin->RegionRects[iSprite][1][i],
frmGoblin->RegionRects[iSprite][2][i]+1,
frmGoblin->RegionRects[iSprite][3][i]+1);
CombineRgn(h1,h1,h2,RGN_OR);
DeleteObject(h2);
}
return h1;
}
--------------- [cut] -----------
2.3 Animated transparent image
That's the most difficult part, 'cause I haven't found how to store pre-generated regions somewhere. Of course, you can say - it is possible to create many windows with different images & different regions, and to show only 1 at a time. Hmmm... but how much resources this will take, if we have, f.e. 36 images? And if we try to run 10 Goblins? We'll have 360 windows.... %) No! That's not our way. The Goblin do this: 1. Create a temporary array of rects for every sprite at program start. And then creates new region from this array on EVERY WINDOW REPAINT... This also sounds horrible, but it works more or less well. Here is the code (C++Builder 3):
--------------- [cut] -----------
On Goblin start-up we generate arrays of rects for all images:
...........
// Generating rects arrays for all sprites.
// Sprites are stored in a 1 big image (iGoblins) - 9 pictures width, 4 pictures
height
// isGetTop(), isGetLeft() - are just functions, which returns correct
coordinates in the big image
for ( int i = 0; i < iSprites; i++ )
{
CreateRectListV(iGoblins->Canvas,
&Rect( isGetLeft(i),
// GWidth * ( i % 10 ) + GAddX,
isGetTop(i),
// GHeight * ( i / 10 ) + GAddY,
isGetLeft(i) + GWidth-2,
//GWidth * ( ( i % 10 ) + 1 ) - 1 + GAddX,
isGetTop(i) + GHeight-2
//GHeight * ( ( i / 10 ) + 1 ) + GAddY
),i );
}
PackRectListV(); // making big rects from many small rects where
possible.
...........
Then, we can use this code to draw current Goblin sprite:
rgn = CreateRgnFormRects( iSprite ); // Now!!! We are generating
a Region!
if ( rgn != 0 ) SetWindowRgn( Handle, rgn, true ); // Handle is a Goblin
// main window handle
// Drawing the current sprite image to the window
frmGoblin->Canvas->CopyRect( Rect(0, 0, GWidth, GHeight),
iGoblins->Canvas,
Rect( isGetLeft(Sprite), // X * GWidth + GAddX,
isGetTop(Sprite),//Y * GHeight + GAddY,
isGetLeft(Sprite)+GWidth,//( X + 1 ) * GWidth + GAddX,
isGetTop(Sprite)+GHeight//( Y + 1 ) * GHeight + GAddY
));
--------------- [cut] -----------
Seems that's all. Hope I
described this, hmmm..., not so bad...
Anyway - things are not so bad! :))) I wrote a a simple example, based on smallGoblin
drawing engine. Take it here (exe+src) and enjoy!
Try the fortune on http://msdn.microsoft.com - here may be some explanations of regions conception from the creators. :)
By the way! There is a nice Screen Mate Wind-Up Bird with Visual Basic src at http://www.cwinapp.com (look in Fun Stuff section). This Mate also uses SetWindowRgn()...
There is an example from DirectX SDK (I saw
it in DirectX 7.0 SDK):
Mosquito DirectDraw Sample
This seems to be very good solution for Screen Mate development. The only
limitation is DirectX, that must be installed on the system. As far as I
understood, it uses features of DirectX 5. So - this will not
work on pure Windows95 and Windows NT 4 (without DirectX manually installed). But
this may work without any problems on Windows 98 & Windows 2000.
We - those, who are making screen mates, are not alone in the galaxy. Bill Gates & his crew made something in this sphere too. They called the creature "Microsoft Agent". We can see it, for example, in MS Office2000 as "Office Assistants". So - what is it?
Microsoft Agent is an ActiveX component... What does it means? Hmmm...
It's necessary to install this component. (take executables from Microsoft & run them). Then we may have: 1) Microsoft Agent ActiveX engine 2) TrueVoice speak engine 3) Tool for editing *.asc files (sprites for the character). Now we can take out favorite programming language, import "Microsoft Agent" ActiveX library & enjoy using it.
Here is an sample HTML page, which controlls MS Agent with VBScript (it's possible to write MS Agent controllers as HTML of WSH (Windows Scripting Host) files, using JScript or VBScript). The following page is from MS INet SDK. If you have MS Agent installed, you may see and hear smth.:
Technology | Speed | Program Size | Run at | Examples | Mouse Hit Transparency |
Redrawing background manually | normal | small | Win 3.x, 9x, NT, 2000 | Scam Poo, Message Mates | No |
Win32 Regions | slow-normal | small | Win 9x, NT, 2000 | :) smallGoblin, Wind-Up Bird | Yes |
DirectDraw Overlays | fast | hmmm... seems small | Win98, 2000 or other, if DirectX installed | Mosquito | No |
Microsoft Agent | fast | big (asc file is big; code may be small) | Win9x, NT, 2000 + MS Agent ActiveX component installed | MS Office Assistants | Yes |
You can contact me at: smallgoblin@yahoo.com
Homepage is: http://smallgoblin.narod.ru
12.12.2000 (c) Kane+
14.12.2000
v.1.2