Click here to Skip to main content
15,894,343 members
Articles / Desktop Programming / MFC

Smoov Code Project Screen Saver

Rate me:
Please Sign up or sign in to vote.
4.74/5 (25 votes)
24 May 200212 min read 236.3K   4.1K   77  
Asynchronous XML Web Client Animated Screen Saver in Win32/MFC
// Animation.cpp: implementation of the CAnimation class.
//
// Smoov Code Project Screen Saver Release 1.1 by Ben Bryant
// Copyright (C) 2002 First Objective Software, Inc. All rights reserved
// Retain this notice and disclaimer
// This software class is provided as is with no warranty
// You may redistribute and modify this software, but the
// uncompiled source code cannot be sold without the authors written consent!
// You may use this class freely in compiled commercial applications, enjoy!
// Go to www.firstobject.com for the latest version and information
//
// The Code Project name, web service and some graphics are the
// property of The Code Project www.codeproject.com

#include "stdafx.h"
#include "CPSmoovScr.h"
#include "Animation.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// CAnimationItem

CAnimationItem::CAnimationItem()
{
	SetDefaults();
	m_pAE = NULL;
}

CAnimationItem::CAnimationItem( CAnimationEngine* pAE )
{
	SetDefaults();
	m_pAE = pAE;
}

CAnimationItem::~CAnimationItem()
{
}

CAnimationItem& CAnimationItem::SetDefaults()
{
	m_nFrames = 20;
	m_nEffects = 0;
	m_pFont = NULL;
	m_pBitmap = NULL;
	int nFirstPic = -1;
	int nLastPic = -1;
	SetTextColor( RGB(191,191,191) );
	SetPoint( CPoint(0,0) );
	m_bDrawnYet = FALSE;
	m_bWait = FALSE;
	m_posKickOff = NULL;
	m_posNext = NULL;
	return *this;
}

CAnimationItem& CAnimationItem::SetFont( CString csFont )
{
	m_pFont = m_pAE->GetFont( csFont );
	return *this;
}

CAnimationItem& CAnimationItem::SetBitmap( UINT nBitmapID, int nFirst, int nLast )
{
	m_pBitmap = m_pAE->GetBmp( nBitmapID );
	m_nFirstPic = nFirst;
	m_nLastPic = nLast;
	return *this;
}

CAnimationItem& CAnimationItem::SetTextColor( COLORREF rgb )
{
	m_rgbTextColor = rgb;
	m_rgbTextColorCurrent = rgb;
	return *this;
}

CAnimationItem& CAnimationItem::SetText( CString csText )
{
	m_csText = csText;
	return *this;
}

CAnimationItem& CAnimationItem::SetStart( CPoint pt )
{
	m_ptStart = pt;
	m_pt = m_ptStart;
	m_dX = pt.x;
	m_dY = pt.y;
	return *this;
}

CAnimationItem& CAnimationItem::SetFinish( CPoint pt )
{
	m_ptFinish = pt;
	return *this;
}

CAnimationItem& CAnimationItem::SetPoint( CPoint pt )
{
	SetStart( pt );
	return SetFinish( pt );
}

CAnimationItem& CAnimationItem::SetFrames( int nFrames )
{
	m_nFrames = nFrames;
	return *this;
}

CAnimationItem& CAnimationItem::GoAfter( POSITION pos )
{
	if ( ! pos )
		return *this;
	CAnimationItem* pItem = &m_pAE->m_listItems.GetAt( pos );
	if ( pItem->m_posKickOff )
		m_posNext = pItem->m_posKickOff;
	pItem->m_posKickOff = m_pos;
	m_bWait = TRUE;
	return *this;
}

CAnimationItem& CAnimationItem::GoWith( POSITION pos )
{
	if ( ! pos )
		return *this;
	CAnimationItem* pItem = &m_pAE->m_listItems.GetAt( pos );
	m_posNext = pItem->m_posNext;
	m_bWait = pItem->m_bWait;
	pItem->m_posNext = m_pos;
	return *this;
}

//////////////////////////////////////////////////////////////////////
// CAnimationEngine

CAnimationEngine::CAnimationEngine()
{
	m_rect.SetRectEmpty();
	m_rgbBackground = RGB(0,0,0);
	m_brushBackground.CreateStockObject(BLACK_BRUSH);
	m_pDefaultFont = GetFont( "15,MS Sans Serif" );
	srand( (unsigned)time( NULL ) );
}

CAnimationEngine::~CAnimationEngine()
{
	DestroyGDIs();
}

CAnimationItem& CAnimationEngine::CreateItem( CString csText )
{
	CAnimationItem item( this );
	item.m_csText = csText;
	POSITION pos = m_listItems.AddTail( item );
	CAnimationItem& itemRef = m_listItems.GetAt( pos );
	itemRef.m_pos = pos;
	return itemRef;
}

CAnimationItem& CAnimationEngine::CreateItemAfter( POSITION pos )
{
	CAnimationItem& itemFrom = m_listItems.GetAt( pos );
	CAnimationItem& itemRef = CreateItem( itemFrom.m_csText );
	itemRef.m_csText = itemFrom.m_csText;
	itemRef.SetPoint( itemFrom.m_ptFinish );
	itemRef.SetTextColor( itemFrom.m_rgbTextColor );
	itemRef.m_pFont = itemFrom.m_pFont;
	itemRef.m_nEffects |= (itemFrom.m_nEffects &
		(CAnimationItem::AF_AlignCenter|CAnimationItem::AF_AlignRight));
	itemRef.GoAfter( pos );
	itemRef.m_pBitmap = itemFrom.m_pBitmap;
	itemRef.m_nFirstPic = itemFrom.m_nFirstPic;
	itemRef.m_nLastPic = itemFrom.m_nLastPic;
	return itemRef;
}

int CAnimationEngine::RunFrame( CDC& dc )
{
	// Setup DC
	CFont* pOldFont = dc.SelectObject( m_pDefaultFont );
	dc.SetBkColor( RGB(0,0,0) );
	dc.SetBkMode( TRANSPARENT );
	CRect rect;

	// Loop through items
	int nItems = 0, nItemsAlive = 0;
	POSITION pos = m_listItems.GetHeadPosition();
	while ( pos )
	{
		POSITION posCurrent = pos;
		CAnimationItem& item = m_listItems.GetNext( pos );

		++nItems;
		if ( item.m_bWait )
			continue;
		++nItemsAlive;

		// Font
		if ( item.m_pFont )
			dc.SelectObject( item.m_pFont );
		else
			dc.SelectObject( m_pDefaultFont );

		// Alignment
		DWORD nDrawFlags = DT_WORDBREAK;
		if ( item.m_nEffects & item.AF_AlignRight )
			nDrawFlags |= DT_RIGHT;
		else if ( item.m_nEffects & item.AF_AlignCenter )
			nDrawFlags |= DT_CENTER;

		// Set flag if erase is necessary (always erase on last step)
		BOOL bErase = ( ! item.m_nFrames );

		// Calculate new position
		CPoint ptNew = item.m_pt;
		if ( item.m_bDrawnYet && item.m_ptStart != item.m_ptFinish )
		{
			if ( ! item.m_nFrames )
				ptNew = item.m_ptFinish;
			else
			{
				item.m_dX += ( ((double)item.m_ptFinish.x) - item.m_dX ) / ((double)item.m_nFrames);
				item.m_dY += ( ((double)item.m_ptFinish.y) - item.m_dY ) / ((double)item.m_nFrames);
				ptNew = CPoint( (int)item.m_dX, (int)item.m_dY );
			}
			if ( item.m_pt != ptNew )
				bErase = TRUE;
		}

		// Calculate fade
		BOOL bFade = item.m_nEffects & (item.AF_FadeIn|item.AF_FadeOut);
		if ( bFade )
		{
			BOOL bIn = item.m_nEffects & item.AF_FadeIn;
			if ( ! item.m_nFrames )
				item.m_rgbTextColorCurrent = bIn?item.m_rgbTextColor:RGB(0,0,0);
			else
			{
				int nMask = 0xff;
				for ( int nC = 0; nC < 3; ++nC )
				{
					int nColor = (item.m_rgbTextColor & nMask) >> (8*nC);
					int nCurColor = (item.m_rgbTextColorCurrent & nMask) >> (8*nC);
					if ( bIn )
						nCurColor += ( (nColor-nCurColor) / item.m_nFrames );
					else
						nCurColor -= ( nCurColor / item.m_nFrames );
					item.m_rgbTextColorCurrent = (item.m_rgbTextColorCurrent&(~nMask)) | (nCurColor << (8*nC));
					nMask = nMask << 8;
				}
			}
		}

		// Remove if drawn previously
		if ( item.m_pBitmap )
			DrawBitmapItem( dc, item, ptNew, item.m_nFrames != 0 );
		else if ( item.m_bDrawnYet && bErase )
			DrawTextItem( dc, item, nDrawFlags, FALSE );
		item.m_bDrawnYet = FALSE;

		// See if any more frames remain for this item
		if ( ! item.m_nFrames )
		{
			POSITION posKickOff = item.m_posKickOff;
			while ( posKickOff )
			{
				CAnimationItem* pNext = &m_listItems.GetAt( posKickOff );
				pNext->m_bWait = FALSE;
				posKickOff = pNext->m_posNext;
				pNext->m_posNext = NULL;
			}
			m_listItems.RemoveAt( posCurrent );
			continue;
		}
		--item.m_nFrames;

		// Draw at new position
		item.m_pt = ptNew;
		if ( ! item.m_pBitmap )
			DrawTextItem( dc, item, nDrawFlags, TRUE );
		item.m_bDrawnYet = TRUE;
	}

	if ( nItems && ! nItemsAlive )
		ASSERT( 0 );

	dc.SelectObject( pOldFont );
	return nItems;
}

void CAnimationEngine::DrawBitmapItem( CDC& dc, CAnimationItem& item, CPoint ptNew, BOOL bShow )
{
	CPoint pt = item.m_pt;
	CRect rectBmp( 0, 0, item.m_pBitmap->GetWidth(), item.m_pBitmap->GetHeight() );
	if ( item.m_nFirstPic != -1 )
	{
		// Bitmap can be divided into square pictures
		int nWidth = rectBmp.Height();
		rectBmp.right = nWidth;
		rectBmp.OffsetRect( item.m_nFirstPic*nWidth, 0 );
	}
	CRect rect( 0,0,rectBmp.Width(),rectBmp.Height() );
	rect.OffsetRect( pt );
	if ( item.m_nEffects & item.AF_AlignCenter )
		rect.OffsetRect( - rectBmp.Width()/2, 0 );
	if ( ! bShow )
	{
		dc.FillRect( &rect, &m_brushBackground );
		return;
	}

	if ( pt != ptNew )
	{
		// Determine rects that need to be cleared
		CRect rectLeftOrRight( rect ), rectTopOrBottom( rect );
		int nXDiff = ptNew.x - pt.x;
		int nYDiff = ptNew.y - pt.y;
		if ( nXDiff >= 0 ) // moving right
			rectLeftOrRight.right = rectTopOrBottom.left = rectLeftOrRight.left + nXDiff;
		else // moving left
			rectLeftOrRight.left = rectTopOrBottom.right = rectLeftOrRight.left + nXDiff;
		if ( nYDiff >= 0 ) // moving down
			rectTopOrBottom.bottom = rectTopOrBottom.top + nYDiff;
		else // moving up
			rectTopOrBottom.top = rectTopOrBottom.top + nYDiff;
		dc.FillRect( &rectLeftOrRight, &m_brushBackground );
		dc.FillRect( &rectTopOrBottom, &m_brushBackground );
	}

	rect.SetRect( 0,0,rectBmp.Width(),rectBmp.Height() );
	rect.OffsetRect( ptNew );
	if ( item.m_nEffects & item.AF_AlignCenter )
		rect.OffsetRect( - rectBmp.Width()/2, 0 );
	item.m_pBitmap->DrawDIB( &dc, rect, rectBmp );
}

void CAnimationEngine::DrawTextItem( CDC& dc, CAnimationItem& item, int nDrawFlags, BOOL bShow )
{
	CRect rect;
	CPoint pt = item.m_pt;

	if ( bShow )
		dc.SetTextColor( item.m_rgbTextColorCurrent );
	else
		dc.SetTextColor( m_rgbBackground );

	CSize size = dc.GetTextExtent( item.m_csText );
	int nBottom = pt.y + size.cy * 2 + 1;
	if ( item.m_nEffects & item.AF_AlignCenter )
		rect.SetRect( pt.x-size.cx/2, pt.y, pt.x+size.cx/2+2, nBottom );
	else if ( item.m_nEffects & item.AF_AlignRight )
		rect.SetRect( pt.x-size.cx-1, pt.y, pt.x, nBottom );
	else
		rect.SetRect( pt.x, pt.y, pt.x+size.cx+1, nBottom );
	dc.DrawText( item.m_csText, &rect, nDrawFlags );
}

CDIBitmap* CAnimationEngine::GetBmp( UINT nBitmapID )
{
	CDIBitmap* pBmp = NULL;
	if ( ! m_mapBmps.Lookup( nBitmapID, pBmp ) )
	{
		pBmp = new CDIBitmap;
		if ( ! pBmp->LoadResource(nBitmapID) )
		{
			delete pBmp;
			return NULL;
		}

		// Create bitmap and add it to the pool
		m_mapBmps.SetAt( nBitmapID, pBmp );
	}
	return pBmp;
}

CFont* CAnimationEngine::GetFont( CString csFont )
{
	CFont* pFont;
	if ( ! m_mapFonts.Lookup( csFont, pFont ) )
	{
		// Create font and add it to the pool
		LOGFONT logfont;
		logfont.lfHeight = -12;
		logfont.lfWidth = 0;
		logfont.lfEscapement = 0;
		logfont.lfOrientation = 0;
		logfont.lfWeight = FW_NORMAL;
		logfont.lfItalic = 0;
		logfont.lfUnderline = 0;
		logfont.lfStrikeOut = 0;
		logfont.lfCharSet = ANSI_CHARSET;
		logfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
		logfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
		logfont.lfQuality = NONANTIALIASED_QUALITY;
		logfont.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
		strcpy(logfont.lfFaceName, "Arial");

		// Break out font string into comma-delimited font attributes
		CString csAttrib;
		int nComma = 0, nStart = 0;
		while ( (nComma = csFont.Find(",",nStart)) != -1 )
		{
			csAttrib = csFont.Mid( nStart, nComma - nStart );
			if ( csAttrib.GetLength() )
			{
				switch ( csAttrib[0] )
				{
				case 'B': // Bold
					if ( csAttrib.GetLength() > 1 )
						logfont.lfWeight = atoi(csAttrib.Mid(1));
					else
						logfont.lfWeight = FW_BOLD;
					break;
				case 'I': // Italic
					logfont.lfItalic = TRUE;
					break;
				case 'U': // Underline
					logfont.lfUnderline = TRUE;
					break;
				case 'S': // StrikeOut
					logfont.lfStrikeOut = TRUE;
					break;
				case 'C': // CS is CharSet
					if ( csAttrib.GetLength() > 2 )
						logfont.lfCharSet = atoi(csAttrib.Mid(2));
					break;
				default:
					if ( isdigit(csAttrib[0]) || csAttrib[0] == '-' )
						logfont.lfHeight = - atoi( csAttrib );
					break;
				}
			}
			nStart = nComma + 1;
		}
		if ( csFont.GetLength() > nStart )
		{
			strncpy(logfont.lfFaceName, csFont.Mid(nStart), LF_FACESIZE);
			logfont.lfFaceName[LF_FACESIZE-1] = '\0';
		}
		pFont = new CFont;
		pFont->CreateFontIndirect( &logfont );
		m_mapFonts.SetAt( csFont, pFont );
	}
	return pFont;
}

int CAnimationEngine::Rand( int n )
{
	int nRand = rand() * n / RAND_MAX;
	return max(0,min(nRand,n-1)); // enforce bounds
}

CPoint CAnimationEngine::RandPt( int nMargin, int nMin )
{
	int nHorizMargin = max( 0, min(m_rect.Width()/2-nMin,nMargin) );
	int nX = Rand(m_rect.Width()-nHorizMargin*2) + m_rect.left + nHorizMargin;
	int nVertMargin = max( 0, min(m_rect.Height()/2-nMin,nMargin) );
	int nY = Rand(m_rect.Height()-nVertMargin*2) + m_rect.left + nVertMargin;
	return CPoint( nX, nY );
}

void CAnimationEngine::DestroyGDIs()
{
	POSITION pos = m_mapFonts.GetStartPosition();
	while ( pos )
	{
		CString csKey;
		CFont* pFont;
		m_mapFonts.GetNextAssoc( pos, csKey, pFont );
		pFont->DeleteObject();
		delete pFont;
	}
	m_mapFonts.RemoveAll();

	pos = m_mapBmps.GetStartPosition();
	while ( pos )
	{
		UINT nID;
		CDIBitmap* pBmp;
		m_mapBmps.GetNextAssoc( pos, nID, pBmp );
		delete pBmp;
	}
	m_mapBmps.RemoveAll();

	m_brushBackground.DeleteObject();
}

void CAnimationEngine::SetScreen( CRect rect )
{
	m_rect = rect;
}



By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Raised in Southern Ontario Canada. Bachelor of Science from the University of Toronto in Computer Science and Anthropology. Living near Washington D.C. in Virginia, USA.

Comments and Discussions