home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: WPS_PM
/
WPS_PM.zip
/
xfld085s.zip
/
helpers
/
animate.c
next >
Wrap
C/C++ Source or Header
|
1999-02-23
|
27KB
|
708 lines
/*
*@@sourcefile animate.c:
* contains helper functions to animate a static icon control.
* These functions do not rely on the XFolder code, but could
* be used in any program.
*
* This is a new file with V0.81. Most of this code used to reside
* in common.c with previous versions.
*
* Function prefixes (new with V0.81):
* -- anm* Animation helper functions
*
* To animate a static icon control, you can simply create it
* with the dialog editor. Then do the following in your code:
*
* 1) load the dlg box template (using WinLoadDlg);
*
* 2) load all the icons for the animation in an array of
* HPOINTERs (check xsdLoadAnimation in xshutdwn.c for an
* example);
*
* 3) call anmPrepareAnimation, e.g.:
+ anmPrepareAnimation(WinWindowFromID(hwndDlg, ID_ICON), // get icon hwnd
+ 8, // no. of icons for the anim
+ &ahptr[0], // ptr to first icon in the array
+ 150, // delay
+ TRUE); // start animation now
*
* 4) call WinProcessDlg(hwndDlg);
*
* 5) stop the animation, e.g.:
+ anmStopAnimation(WinWindowFromID(hwndDlg, ID_ICON));
*
* 6) destroy the dlg window;
*
* 7) free all the HPOINTERS loaded above (check xsdFreeAnimation in
* xshutdwn.c for an example).
*
*@@include #define INCL_WINWINDOWMGR
*@@include #define INCL_WINPOINTERS
*@@include #include <os2.h>
*@@include #include "animate.h"
*/
/*
* Copyright (C) 1997-99 Ulrich Möller.
* This file is part of the XFolder source package.
* XFolder is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, in version 2 as it comes in the
* "COPYING" file of the XFolder main distribution.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define INCL_DOS
#define INCL_DOSERRORS
#define INCL_DOSDEVICES
#define INCL_DOSDEVIOCTL
#define INCL_WIN
#define INCL_WINSYS
#define INCL_GPILOGCOLORTABLE
#define INCL_GPIPRIMITIVES
#include <os2.h>
#include "animate.h"
// #define _PMPRINTF_
#include "pmprintf.h"
#include "dosh.h"
#include "winh.h"
#include "gpih.h"
#include <stdlib.h>
#include <string.h>
/*
*@@ fnwpAnimatedIcon:
* subclassed wnd proc for static icons subclassed
* with anmPrepareStaticIcon (below). This func
* intercepts a few messages to improve icon painting
* and also handles the timer for animations, if this
* has been enabled.
*/
MRESULT EXPENTRY fnwpAnimatedIcon(HWND hwndStatic, ULONG msg, MPARAM mp1, MPARAM mp2)
{
PANIMATIONDATA pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER);
// animation data which was stored in window words
PFNWP OldStaticProc = NULL;
MRESULT mrc = NULL;
if (pa) {
OldStaticProc = pa->OldStaticProc;
switch(msg) {
/*
* WM_TIMER:
* this timer is started by the anm* funcs
* below. Proceed to the next animation step.
*/
case WM_TIMER: {
pa->usAniCurrent++;
if (pa->usAniCurrent >= pa->usAniCount)
pa->usAniCurrent = 0;
WinSendMsg(hwndStatic,
SM_SETHANDLE,
(MPARAM)pa->ahptrAniIcons[pa->usAniCurrent],
(MPARAM)NULL);
break; }
/*
* SM_SETHANDLE:
* this is the normal message sent to a static
* icon control to change its icon. Unfortunately,
* the standard static control paints garbage if
* the icons contain transparent areas, and the
* display often flickers too.
* We improve this by creating one bitmap from
* the icon that we were given which we can then
* simply copy to the screen in one step in
* WM_PAINT.
*/
case SM_SETHANDLE: {
HDC hdcMem;
PSZ pszData[4] = { "Display", NULL, NULL, NULL };
HPS hpsMem;
SIZEL sizlPage = {0, 0};
BITMAPINFOHEADER2 bmp;
PBITMAPINFO2 pbmi = NULL;
// HBITMAP hbm = NULLHANDLE;
POINTL aptl[3];
LONG alData[2];
LONG lBkgndColor = winhQueryPresColor(
WinQueryWindow(hwndStatic, QW_PARENT),
PP_BACKGROUNDCOLOR,
"DialogBackground",
"255 255 255");
HAB hab = WinQueryAnchorBlock(hwndStatic);
HPS hps = WinGetPS(hwndStatic);
// store new icon in our own structure
pa->hptr = (HPOINTER)mp1;
// switch to RGB mode
GpiCreateLogColorTable(hps, 0,
LCOLF_RGB,
0, 0, NULL);
// if we have created a bitmap previously,
// delete it
if (pa->hbm)
GpiDeleteBitmap(pa->hbm);
// create a memory PS
if (gpihCreateMemPS(hab, &hdcMem, &hpsMem))
{
// switch to RGB mode
GpiCreateLogColorTable(hpsMem, 0,
LCOLF_RGB,
0, 0, NULL);
// create a suitable bitmap w/ the size of the
// static control
if (pa->hbm = gpihCreateBitmap(hpsMem, &(pa->rclIcon)))
{
// associate the bit map with the memory PS
if (GpiSetBitmap(hpsMem, pa->hbm) != HBM_ERROR)
{
POINTL ptl = {0, 0};
POINTERINFO pi;
// fill the bitmap with the current static
// background color
GpiMove(hpsMem, &ptl);
ptl.x = pa->rclIcon.xRight;
ptl.y = pa->rclIcon.yTop;
GpiSetColor(hpsMem,
lBkgndColor);
GpiBox(hpsMem,
DRO_FILL, // interior only
&ptl,
0, 0); // no corner rounding
// now get the bitmaps for the new icon
// that we were given in mp1.
// Each icon consists of two (really three)
// bitmaps, which are stored in the POINTERINFO
// structure:
// pi.hbmColor is the actual bitmap to be
// drawn. The parts that are
// to be transparent or inverted
// are black in this image.
// pi.hbmPointer has twice the height of
// hbmColor. The upper bitmap
// contains an XOR mask (for
// inverting parts), the lower
// bitmap an AND mask (for
// transparent parts).
if (WinQueryPointerInfo(pa->hptr, &pi))
{
POINTL aptl[4];
memset(aptl, 0, sizeof(POINTL) * 4);
aptl[1].x = pa->ulSysIconSize;
aptl[1].y = pa->ulSysIconSize;
aptl[3].x = pa->ulSysIconSize + 1;
aptl[3].y = pa->ulSysIconSize + 1;
GpiSetColor(hpsMem, CLR_WHITE);
GpiSetBackColor(hpsMem, CLR_BLACK);
// GpiErase(hpsMem);
// work on the AND image
GpiWCBitBlt(hpsMem,
pi.hbmPointer,
4L, // point count, always 4
&aptl[0], // point array
ROP_SRCAND,
BBO_OR);
// paint the real image
if (pi.hbmColor)
GpiWCBitBlt(hpsMem,
pi.hbmColor,
4L,
&aptl[0], // point array
ROP_SRCPAINT,
BBO_OR);
GpiSetColor(hpsMem, lBkgndColor);
// work on the XOR image
aptl[2].y = pa->ulSysIconSize;
aptl[3].y = (pa->ulSysIconSize * 2) + 1;
GpiWCBitBlt(hpsMem,
pi.hbmPointer,
4L, // point count, always 4
&aptl[0], // point array
ROP_SRCINVERT,
BBO_OR);
}
}
}
GpiDestroyPS(hpsMem);
DevCloseDC(hdcMem);
} // end if (hdcMem = DevOpenDC(
// enforce WM_PAINT
WinInvalidateRect(hwndStatic, NULL, FALSE);
/* WinFillRect(hps, &(pa->rclIcon),
pa->lBkgndColor);
if (pa->hptr) {
WinDrawPointer(hps, 0, 0, pa->hptr, DP_NORMAL);
} */
WinReleasePS(hps);
break; }
/*
* WM_PAINT:
* "normal" paint; this only arrives here if the
* icon needs to be repainted for reasons other
* than changing the icon.
*/
case WM_PAINT: {
RECTL rcl;
POINTL ptl = {0, 0};
POINTERINFO pi;
HPS hps = WinBeginPaint(hwndStatic, NULLHANDLE, &rcl);
// draw the bitmap that've previously created
WinDrawBitmap(hps,
pa->hbm,
NULL, // whole bmp
&ptl,
0, 0, // no colors
DBM_NORMAL);
WinEndPaint(hps);
break; }
/*
* WM_DESTROY:
* clean up.
*/
case WM_DESTROY: {
// undo subclassing in case more WM_TIMERs come in
WinSubclassWindow(hwndStatic, OldStaticProc);
if (pa->hbm)
GpiDeleteBitmap(pa->hbm);
// clean up ANIMATIONDATA struct
WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)NULL);
free(pa);
mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
break; }
default:
mrc = OldStaticProc(hwndStatic, msg, mp1, mp2);
}
}
return (mrc);
}
/*
*@@ anmPrepareStaticIcon:
* turns a static control into one which properly
* displays icons when given a SM_SETHANDLE msg.
* This is done by subclassing the static control
* with fnwpAnimatedIcon.
* This function gets called by anmPrepareAnimation,
* so you need not call it independently.
* This func does _not_ start an animation yet.
* Returns a PANIMATIONDATA if subclassing succeeded or
* NULL upon errors. If you only call this function, you
* do not need this structure; it is needed by
* anmPrepareAnimation though.
* The subclassed static icon func above automatically
* cleans up resources, so you don't have to worry about
* that either.
*/
PANIMATIONDATA anmPrepareStaticIcon(HWND hwndStatic,
USHORT usAnimCount) // needed for allocating extra memory;
// this must be at least 1
{
PANIMATIONDATA pa = NULL;
PFNWP OldStaticProc = WinSubclassWindow(hwndStatic, fnwpAnimatedIcon);
if (OldStaticProc) {
pa = malloc(
sizeof(ANIMATIONDATA)
+ ((usAnimCount-1) * sizeof(HPOINTER))
);
pa->OldStaticProc = OldStaticProc;
WinQueryWindowRect(hwndStatic, &(pa->rclIcon));
pa->hbm = NULLHANDLE;
pa->ulSysIconSize = WinQuerySysValue(HWND_DESKTOP, SV_CXICON);
WinSetWindowULong(hwndStatic, QWL_USER, (ULONG)pa);
}
return (pa);
}
/*
*@@ anmPrepareAnimation:
* this function makes a regular static icon
* control an animated one. This is the one-shot
* function for animating static icon controls.
* It calls anmPrepareStaticIcon first, then uses
* the passed parameters to prepare an animation:
* pahptr must point to an array of HPOINTERs
* which must contain usAnimCount icon handles.
* If (fStartAnimation == TRUE), the animation
* is started already. Otherwise you'll have
* to call anmStartAnimation yourself.
* When the icon control is destroyed, the
* subclassed window proc (fnwpAnimatedIcon)
* automatically cleans up resources. However,
* the icons in *pahptr are not freed.
*/
BOOL anmPrepareAnimation(HWND hwndStatic, // icon hwnd
USHORT usAnimCount, // no. of anim steps
HPOINTER *pahptr, // array of HPOINTERs
ULONG ulDelay, // delay per anim step (in ms)
BOOL fStartAnimation) // TRUE: start animation now
{
PANIMATIONDATA paNew = anmPrepareStaticIcon(hwndStatic, usAnimCount);
if (paNew) {
paNew->ulDelay = ulDelay;
// paNew->OldStaticProc already set
paNew->hptr = NULLHANDLE;
// paNew->rclIcon already set
paNew->usAniCurrent = 0;
paNew->usAniCount = usAnimCount;
memcpy(&(paNew->ahptrAniIcons), pahptr,
(usAnimCount * sizeof(HPOINTER)));
if (fStartAnimation) {
WinStartTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic,
1, ulDelay);
WinPostMsg(hwndStatic, WM_TIMER, NULL, NULL);
}
}
return (paNew != NULL);
}
/*
*@@ anmStartAnimation:
* starts an animation that not currently running. You
* must prepare the animation by calling anmPrepareAnimation
* first.
*/
BOOL anmStartAnimation(HWND hwndStatic)
{
BOOL brc = FALSE;
PANIMATIONDATA pa = (PANIMATIONDATA)WinQueryWindowULong(hwndStatic, QWL_USER);
if (pa)
if (WinStartTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic,
1, pa->ulDelay))
{
brc = TRUE;
WinPostMsg(hwndStatic, WM_TIMER, NULL, NULL);
}
return (brc);
}
/*
*@@ anmStopAnimation:
* stops an animation that is currently running.
* This does not free any resources.
*/
BOOL anmStopAnimation(HWND hwndStatic)
{
return (WinStopTimer(WinQueryAnchorBlock(hwndStatic), hwndStatic, 1));
}
/*
*@@ anmBlowUpBitmap:
* this displays an animation based on a given bitmap.
* The bitmap is "blown up" in that it is continually
* increased in size until the original size is reached.
* The animation is calculated so that it lasts exactly
* ulAnimation milliseconds, no matter how fast the
* system is.
* You should run this routine in a thread with higher-
* than-normal priority, because otherwise the kernel
* might interrupt the display.
* Returns the count of animation steps that were drawn.
* This is dependent on the speed of the system.
*/
BOOL anmBlowUpBitmap(HPS hps, // in: from WinGetPS(HWND_DESKTOP)
HBITMAP hbm, // in: bitmap to be displayed
ULONG ulAnimationTime) // in: total animation time (ms)
{
ULONG ulrc = 0;
ULONG ulInitialTime,
ulNowTime;
// ulCurrentSize = 10;
if (hps) {
POINTL ptl = {0, 0};
RECTL rtlStretch;
ULONG ul,
ulSteps = 20;
BITMAPINFOHEADER bih;
GpiQueryBitmapParameters(hbm, &bih);
/* ptl.y = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN)
- BMPSPACING
- bih.cy; */
ptl.x = (WinQuerySysValue(HWND_DESKTOP, SV_CXSCREEN)
- bih.cx) / 2;
ptl.y = (WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN)
- bih.cy) / 2;
// we now use ul for the current animation step,
// which is a pointer on a scale from 1 to ulAnimationTime;
// ul will be recalculated after each animation
// step according to how much time the animation
// has cost on this display so far. This has the
// following advantages:
// 1) no matter how fast the system is, the animation
// will always last ulAnimationTime milliseconds
// 2) since large bitmaps take more time to calculate,
// the animation won't appear to slow down then
ulInitialTime = doshGetULongTime();
ul = 1;
ulSteps = 1000;
do {
LONG cx = (((bih.cx-20) * ul) / ulSteps) + 20;
LONG cy = (((bih.cy-20) * ul) / ulSteps) + 20;
rtlStretch.xLeft = ptl.x + ((bih.cx - cx) / 2);
rtlStretch.yBottom = ptl.y + ((bih.cy - cy) / 2);
rtlStretch.xRight = rtlStretch.xLeft + cx;
rtlStretch.yTop = rtlStretch.yBottom + cy;
WinDrawBitmap(hps, hbm, NULL, (PPOINTL)&rtlStretch,
0, 0, // we don't need colors
DBM_STRETCH);
ulNowTime = doshGetULongTime();
// recalculate ul: rule of three based on the
// time we've spent on animation so far
ul = (ulSteps
* (ulNowTime - ulInitialTime)) // time spent so far
/ ulAnimationTime; // time to spend altogether
ulrc++; // return count
} while (ul < ulSteps);
// finally, draw the 1:1 version
WinDrawBitmap(hps, hbm, NULL, &ptl,
0, 0, // we don't need colors
DBM_NORMAL);
} // end if (hps)
return (ulrc);
}
#define LAST_WIDTH 2
#define LAST_STEPS 50
#define WAIT_TIME 10
/*
*@@ anmPowerOff:
* displays an animation that looks like a
* monitor being turned off; hps must have
* been acquired using WinGetScreenPS,
* ulSteps should be around 40-50.
*/
VOID anmPowerOff(HPS hps, ULONG ulSteps)
{
RECTL rclScreen, rclNow, rclLast, rclDraw;
ULONG ul = ulSteps;
ULONG ulPhase = 1;
WinQueryWindowRect(HWND_DESKTOP, &rclScreen);
WinShowPointer(HWND_DESKTOP, FALSE);
memcpy(&rclLast, &rclScreen, sizeof(RECTL));
// In order to draw the animation, we tell apart three
// "phases", signified by the ulPhase variable. While
// ulPhase != 99, we stay in a do-while loop.
ul = 0;
ulPhase = 1;
do {
ULONG ulFromTime = doshGetULongTime();
if (ulPhase == 1)
{
// Phase 1: "shrink" the screen by drawing black
// rectangles from the edges towards the center
// of the screen. With every loop, we draw the
// rectangles a bit closer to the center, until
// the center is black too. Sort of like this:
// ***************************
// * black *
// * *
// * ............. *
// * . rclNow: . *
// * -> . untouched . <- *
// * ............. *
// * ^ *
// * ! *
// ***************************
// rclNow contains the rectangle _around_ which
// the black rectangles are to be drawn. With
// every iteration, rclNow is reduced in size.
rclNow.xLeft = ((rclScreen.yTop / 2) * ul / ulSteps );
rclNow.xRight = (rclScreen.xRight) - rclNow.xLeft;
rclNow.yBottom = ((rclScreen.yTop / 2) * ul / ulSteps );
rclNow.yTop = (rclScreen.yTop) - rclNow.yBottom;
if (rclNow.yBottom > (rclNow.yTop - LAST_WIDTH) ) {
rclNow.yBottom = (rclScreen.yTop / 2) - LAST_WIDTH;
rclNow.yTop = (rclScreen.yTop / 2) + LAST_WIDTH;
}
// draw black rectangle on top of rclNow
rclDraw.xLeft = rclLast.xLeft;
rclDraw.xRight = rclLast.xRight;
rclDraw.yBottom = rclNow.yTop;
rclDraw.yTop = rclLast.yTop;
WinFillRect(hps, &rclDraw, CLR_BLACK);
// draw black rectangle left of rclNow
rclDraw.xLeft = rclLast.xLeft;
rclDraw.xRight = rclNow.xLeft;
rclDraw.yBottom = rclLast.yBottom;
rclDraw.yTop = rclLast.yTop;
WinFillRect(hps, &rclDraw, CLR_BLACK);
// draw black rectangle right of rclNow
rclDraw.xLeft = rclNow.xRight;
rclDraw.xRight = rclLast.xRight;
rclDraw.yBottom = rclLast.yBottom;
rclDraw.yTop = rclLast.yTop;
WinFillRect(hps, &rclDraw, CLR_BLACK);
// draw black rectangle at the bottom of rclNow
rclDraw.xLeft = rclLast.xLeft;
rclDraw.xRight = rclLast.xRight;
rclDraw.yBottom = rclLast.yBottom;
rclDraw.yTop = rclNow.yBottom;
WinFillRect(hps, &rclDraw, CLR_BLACK);
// remember rclNow for next iteration
memcpy(&rclLast, &rclNow, sizeof(RECTL));
// done with "shrinking"?
if ( rclNow.xRight < ((rclScreen.xRight / 2) + LAST_WIDTH) ) {
ulPhase = 2; // exit
}
} else if (ulPhase == 2) {
// Phase 2: draw a horizontal white line about
// where the last rclNow was. This is only
// executed once.
// ***************************
// * black *
// * *
// * *
// * ----------- *
// * *
// * *
// * *
// ***************************
rclDraw.xLeft = (rclScreen.xRight / 2) - LAST_WIDTH;
rclDraw.xRight = (rclScreen.xRight / 2) + LAST_WIDTH;
rclDraw.yBottom = (rclScreen.yTop * 1 / 4);
rclDraw.yTop = (rclScreen.yTop * 3 / 4);
WinFillRect(hps, &rclDraw, CLR_WHITE);
ulPhase = 3;
ul = 0;
} else if (ulPhase == 3) {
// Phase 3: make the white line shorter with
// every iteration by drawing black rectangles
// above it. These are drawn closer to the
// center with each iteration.
// ***************************
// * black *
// * *
// * *
// * -> ---- <- *
// * *
// * *
// * *
// ***************************
rclDraw.xLeft = (rclScreen.xRight / 2) - LAST_WIDTH;
rclDraw.xRight = (rclScreen.xRight / 2) + LAST_WIDTH;
rclDraw.yTop = (rclScreen.yTop * 3 / 4);
rclDraw.yBottom = (rclScreen.yTop * 3 / 4) - ((rclScreen.yTop * 1 / 4) * ul / LAST_STEPS);
if (rclDraw.yBottom < ((rclScreen.yTop / 2) + LAST_WIDTH))
rclDraw.yBottom = ((rclScreen.yTop / 2) + LAST_WIDTH);
WinFillRect(hps, &rclDraw, CLR_BLACK);
rclDraw.xLeft = (rclScreen.xRight / 2) - LAST_WIDTH;
rclDraw.xRight = (rclScreen.xRight / 2) + LAST_WIDTH;
rclDraw.yBottom = (rclScreen.yTop * 1 / 4);
rclDraw.yTop = (rclScreen.yTop * 1 / 4) + ((rclScreen.yTop * 1 / 4) * ul / LAST_STEPS);
if (rclDraw.yTop > ((rclScreen.yTop / 2) - LAST_WIDTH))
rclDraw.yBottom = ((rclScreen.yTop / 2) - LAST_WIDTH);
WinFillRect(hps, &rclDraw, CLR_BLACK);
ul++;
if (ul > LAST_STEPS) {
ulPhase = 99;
}
}
ul++;
while (doshGetULongTime() < ulFromTime + WAIT_TIME) {
// PSZ p = NULL; // keep compiler happy
}
} while (ulPhase != 99);
// sleep a while
DosSleep(500);
WinFillRect(hps, &rclScreen, CLR_BLACK);
DosSleep(500);
WinShowPointer(HWND_DESKTOP, TRUE);
}