www.gusucode.com > VC++生成逼真流动的云彩特效-源码程序 > VC++生成逼真流动的云彩特效-源码程序/code/Clouds.cpp

    /****************************************************************/
/*																*/
/*  CloudsCtrl.cpp												*/
/*																*/
/*  Implementation of the CCloudsCtrl class.					*/
/*	Attempt to simulate clouds using MFC 						*/
/*																*/
/*  DO NOT REMOVE THE ORGINAL CREDITS/COPYRIGHTS!!!				*/
/*																*/
/*  Programmed by Pablo van der Meer							*/
/*  Copyright Pablo Software Solutions 2003						*/
/*  This code is stolen from: http://www.pablovandermeer.nl		*/
/*																*/
/*  Last updated: December 15, 2003								*/
/*																*/
/****************************************************************/


#include "stdafx.h"
#include "Clouds.h"
#include <math.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// Download by http://www.NewXing.com

CCloudsCtrl::CCloudsCtrl()
{
	// protected bitmaps to restore the memory DC's
	m_pOldBitmap = NULL;

	m_pCloudPlasma1 = NULL;
	m_pCloudPlasma2 = NULL;
	m_pCloudPlasma3 = NULL;

	m_nDensity = 8;

	m_bInterActive = FALSE;
	m_nSpeed = 2;
	
	m_ptDirection = m_ptMove = CPoint(m_nSpeed, 1);
	m_ptOffset1 = m_ptOffset2 = m_ptOffset3 = CPoint(0,0);

	m_pPixels = NULL;
	m_nSizeOfBits = 0;
	m_hBitmap = NULL;

	m_strCredits = "\tCCloudsCtrl\n\n"
				   "\rProgrammed by:\n"
				   "Pablo van der Meer\n\n"
				   "Copyright ?2003 Pablo Software Solutions\n"
				   "All right reserved.\n\n"
				   "\rhttp:\\www.pablovandermeer.nl\n"; 
}


CCloudsCtrl::~CCloudsCtrl()
{
	if (m_pOldBitmap != NULL)
		m_MainDC.SelectObject(m_pOldBitmap);  

	if (m_pCloudPlasma1)
		delete[] m_pCloudPlasma1;
	if (m_pCloudPlasma2)
		delete[] m_pCloudPlasma2;
	if (m_pCloudPlasma3)
		delete[] m_pCloudPlasma3;

	if(m_hBitmap)
		::DeleteObject(m_hBitmap);
}


BEGIN_MESSAGE_MAP(CCloudsCtrl, CStatic)
	//{{AFX_MSG_MAP(CCloudsCtrl)
	ON_WM_PAINT()
	ON_WM_TIMER()
	ON_WM_ERASEBKGND()
	ON_WM_MOUSEMOVE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()



/********************************************************************/
/*																	*/
/* Function name : PreSubclassWindow								*/
/* Description   : Initialize some stuff							*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::PreSubclassWindow() 
{
	CStatic::PreSubclassWindow();

	CRect rect;
	GetClientRect(&rect);

	m_nWidth = rect.Width();
	m_nHeight = rect.Height();

	m_pCloudPlasma1 = new int[m_nWidth * m_nHeight];
	m_pCloudPlasma2 = new int[m_nWidth * m_nHeight];
	m_pCloudPlasma3 = new int[m_nWidth * m_nHeight];

	int nBits = (((m_nWidth * 32 + 31) & ~31) >> 3) * m_nHeight;

	memset(&m_bmpInfo, 0, sizeof(m_bmpInfo));
	m_bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	m_bmpInfo.bmiHeader.biWidth = m_nWidth;
	m_bmpInfo.bmiHeader.biHeight = m_nHeight;
	m_bmpInfo.bmiHeader.biSizeImage = nBits;
	m_bmpInfo.bmiHeader.biPlanes = 1;
	m_bmpInfo.bmiHeader.biBitCount = 32;

	CDC *pDC = GetDC();
	m_hBitmap = CreateDIBSection(pDC->m_hDC, &m_bmpInfo, DIB_RGB_COLORS, (void**)&m_pPixels, NULL, 0);

	// create credits dc
	LoadCredits(pDC);

	ReleaseDC(pDC);

	m_nSizeOfBits = (nBits - (m_nWidth * 2))/sizeof(RGBQUAD);

	// initialize randomizer
	srand((unsigned)time(NULL));

	// initialize random noise
	GeneratePlasma(m_pCloudPlasma1);
	GeneratePlasma(m_pCloudPlasma2);
	GeneratePlasma(m_pCloudPlasma3);

	// initialize blue color map
	SetExponentialClouds(FALSE);

	// set scroll counter
	m_nCounter = m_nHeight + 25;

	DrawCtrl();

	SetTimer(1, 50, NULL);
}


/********************************************************************/
/*																	*/
/* Function name : OnPaint											*/
/* Description   : Called when the application makes a request to	*/
/*				   repaint a portion of the window.					*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::OnPaint() 
{
	CPaintDC dc(this); 
	
	CRect rect;
	GetClientRect(&rect);

	CDC memDC;
	CBitmap memBitmap;
	CBitmap* oldBitmap;

	memDC.CreateCompatibleDC(&dc);
	memBitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
	oldBitmap = (CBitmap *)memDC.SelectObject(&memBitmap);

	if (memDC.GetSafeHdc() != NULL)
	{
		// copy image to memory dc
		memDC.BitBlt(0, 0, rect.Width(), rect.Height(), &m_MainDC, 0, 0, SRCCOPY);
		// send result to the display
		dc.BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY);
	}
	memDC.SelectObject(oldBitmap);
}


/********************************************************************/
/*																	*/
/* Function name : DrawCtrl											*/
/* Description   : Render the clouds to a bitmap.					*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::DrawCtrl()
{
	CRect rect;
	GetClientRect(&rect);

	// in case we haven't established the memory dc's
	CClientDC dc(this);

	// if we don't have one yet, set up a memory dc for the control 
	if (m_MainDC.GetSafeHdc() == NULL)
	{
		m_MainDC.CreateCompatibleDC(&dc);
		m_MainBitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
		m_pOldBitmap = m_MainDC.SelectObject(&m_MainBitmap);
	}

	// update cloud image
	UpdateCloudImage();

	// copy cloud image to decive context
	SetDIBitsToDevice(m_MainDC.m_hDC,
					  0,
					  0,
					  m_nWidth,
					  m_nHeight,
					  0,
					  0,
					  0,
					  m_nHeight,
					  (LPVOID)m_pPixels,
					  &m_bmpInfo,
					  DIB_RGB_COLORS);

	// copy credits in memory dc
	m_MainDC.BitBlt(0, m_nCounter--, m_nWidth, m_nHeight, &m_CreditsDC, 0, 0, MERGEPAINT);

	if (m_nCounter < -m_nHeight)
	{
		m_nCounter = m_nHeight;
	}

	// finally, force redraw
	Invalidate();
} 


/********************************************************************/
/*																	*/
/* Function name : GeneratePlasma									*/
/* Description   : Fill the noise array with random integers		*/
/*																	*/
/* Credits:		   Partially based on code by Andrea Griffini.		*/ 
/*																	*/
/********************************************************************/
void CCloudsCtrl::GeneratePlasma(int byteArray[])
{
	int noiseGrid[256][256];
	
    // initialize to zero
    for (int i=0; i < 256; i++) 
	{
		for (int j=0; j < 256; j++) 
		{
			noiseGrid[i][j] = 0;
		}
    }

	// create plasma
	for(int z = 256; z >= 2; z /= 2)
	{
		int nDensity = z * m_nDensity;

		for(int x = 0; x < 256; x += z)
		{
			int x1 = (z + x) % 256;
			int x2 = z/2 + x;

			for(int y = 0; y < 256; y += z)
			{
				int y1 = (z + y) % 256;
				int y2 = z/2 + y;

				// top-left
				int nValue1 = noiseGrid[x][y];
				// top-right
				int nValue2 = noiseGrid[x1][y];
				// bottom-left
				int nValue3 = noiseGrid[x][y1];
				// bottom-right
				int nValue4 = noiseGrid[x1][y1];
				
				// central point
				noiseGrid[x2][y2] = GetRandomValue((nValue1 + nValue2 + nValue3 + nValue4) /4, nDensity);

				// top point (of top row) 
				if (x == 0)
					noiseGrid[x2][y] = GetRandomValue((nValue1 + nValue2) /2, nDensity);

				// left point (of left column) 
				if (y == 0)
					noiseGrid[x][y2] = GetRandomValue((nValue1 + nValue3) /2, nDensity);

				// bottom point
				noiseGrid[x2][y1] = GetRandomValue((nValue3 + nValue4) / 2, nDensity);

				// right point
				noiseGrid[x1][y2] = GetRandomValue((nValue2 + nValue4) / 2, nDensity);
			}
		}
	}

	// tile generated noise grid into noise array
	double xStep = (double)256 / (double)m_nWidth;
    double yStep = (double)256 / (double)m_nHeight;

	int nCount = 0;

    for(int row = 0; row < m_nHeight; row++)
    {
		int y = (int)((double)row * yStep);

        for(int col = 0; col < m_nWidth; col++)
        {
			int x = (int)((double)col * xStep);
            byteArray[nCount++] = noiseGrid[x][y];
		}
	}
}


/********************************************************************/
/*																	*/
/* Function name : GetRandomValue									*/
/* Description   : Get random value (within the range 0 .. 255)		*/
/*																	*/
/********************************************************************/
int CCloudsCtrl::GetRandomValue(int nBase, int nDelta)
{
	double random = (double)(rand()%1000)/(double)1000;

	int nValue = nBase + (int)(random * (double)nDelta) - nDelta/2;
    nValue = nValue < 0 ? 0 : nValue > 255 ? 255 : nValue;
	return nValue;
}


/********************************************************************/
/*																	*/
/* Function name : CloudExpCurve									*/
/* Description   : Used to let the plasma more look like a cloud	*/
/*																	*/
/********************************************************************/
double CCloudsCtrl::CloudExpCurve(double value)
{
	double nSharpness = 0.575;
	double nCoverRate = 0.475;
	double nSkyRate = 1 - nCoverRate;

	double c1 = __max(0, value - nSkyRate);
	double c2 = 1.0/nSharpness - 1.0;
	return pow(sin(c1/(1-nSkyRate)*3.14159265359/2.0), c2);
}


/********************************************************************/
/*																	*/
/* Function name : UpdateCloudImage									*/
/* Description   : Update cloud image								*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::UpdateCloudImage()
{
	if (!m_bInterActive)
    {
		m_ptMove = m_ptDirection;
	}

	// calculate offsets
	m_ptOffset1.x = (m_ptOffset1.x + m_nWidth + m_ptMove.x) % m_nWidth;
	m_ptOffset1.y = (m_ptOffset1.y + m_nHeight + m_ptMove.y) % m_nHeight;

	m_ptOffset2.x = (m_ptOffset2.x + m_nWidth + m_ptMove.x/2) % m_nWidth;
	m_ptOffset2.y = (m_ptOffset2.y + m_nHeight + m_ptMove.y/2) % m_nHeight;

	m_ptOffset3.x = (m_ptOffset2.x + m_nWidth + m_ptMove.x/3) % m_nWidth;
	m_ptOffset3.y = (m_ptOffset2.y + m_nHeight + m_ptMove.y/3) % m_nHeight;

	// walk through rows
	for(int row = 0; row < m_nHeight; row++)
	{
		int nCount = row * m_nWidth;
		
		int nIndex1 = ((row + m_ptOffset1.y) % m_nHeight) * m_nWidth;
		int nIndex2 = ((row + m_ptOffset2.y) % m_nHeight) * m_nWidth;
		int nIndex3 = ((row + m_ptOffset3.y) % m_nHeight) * m_nWidth;
				
		// walk through columns
		for(int col = 0; col < m_nWidth; col++)
		{
			// calculate average intensity of pixel
			int nValue1 = m_pCloudPlasma1[(col + m_ptOffset1.x) % m_nWidth + nIndex1];
			int nValue2 = m_pCloudPlasma2[(col + m_ptOffset2.x) % m_nWidth + nIndex2];
			int nValue3 = m_pCloudPlasma3[(col + m_ptOffset3.x) % m_nWidth + nIndex3];

			// blend colors
			int nBlueValue = (nValue1 + nValue2 + nValue3) / 3;

			// set blue value for this pixel
			m_pPixels[nCount + col] = m_BlueColorMap[nBlueValue];
		}
	}

}


/********************************************************************/
/*																	*/
/* Function name : OnTimer											*/
/* Description   : Handle timer events								*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::OnTimer(UINT nIDEvent) 
{
	DrawCtrl();		
	CStatic::OnTimer(nIDEvent);
}


/********************************************************************/
/*																	*/
/* Function name : OnMouseMove										*/
/* Description   : Change movement direction						*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
	if (m_ptCursor.x < point.x)
		m_ptMove.x = -m_nSpeed;
	else
	if (m_ptCursor.x > point.x)
		m_ptMove.x = m_nSpeed;
	else
		m_ptMove.x = 0;

	if (m_ptCursor.y < point.y)
		m_ptMove.y = m_nSpeed;
	else
	if (m_ptCursor.y > point.y)
		m_ptMove.y = -m_nSpeed;
	else
		m_ptMove.y = 0;

	m_ptCursor = point;

	CStatic::OnMouseMove(nFlags, point);
}


BOOL CCloudsCtrl::OnEraseBkgnd(CDC* pDC) 
{
	return TRUE;//CStatic::OnEraseBkgnd(pDC);
}


/********************************************************************/
/*																	*/
/* Function name : SetDensity										*/
/* Description   : Set cloud density								*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::SetDensity(int nValue)
{
	m_nDensity = nValue;
}


/********************************************************************/
/*																	*/
/* Function name : SetInteractive									*/
/* Description   : Set interactive mode								*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::SetInteractive(BOOL bFlag)
{
	m_bInterActive = bFlag;
}


/********************************************************************/
/*																	*/
/* Function name : SetSpeed											*/
/* Description   : Set speed of the clouds							*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::SetSpeed(int nValue)
{
	m_nSpeed = nValue;
}


/********************************************************************/
/*																	*/
/* Function name : LoadCredits										*/
/* Description   : Create credits picture into device context		*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::LoadCredits(CDC *pDC)
{
	CBitmap bitmap;
	CBitmap *pOldBitmap;

	// Create the DC
	m_CreditsDC.CreateCompatibleDC(pDC);

	bitmap.CreateCompatibleBitmap(pDC, m_nWidth, m_nHeight);		

	pOldBitmap = (CBitmap *)m_CreditsDC.SelectObject(&bitmap);

	CRect rect(0, 0, m_nWidth, m_nHeight);

	CFont fontTitle, fontBold, fontNormal;

	m_CreditsDC.FillSolidRect(&rect, RGB(255,255,255));

	// create a bunch of fonts
	fontTitle.CreateFont(24, 0, 0, 0, 
 							FW_BOLD, FALSE, FALSE, 0, 
   							ANSI_CHARSET,
                OUT_DEFAULT_PRECIS,
                CLIP_DEFAULT_PRECIS,
                PROOF_QUALITY,
                VARIABLE_PITCH | 0x04 | FF_DONTCARE,
                (LPSTR)"Arial");

	fontBold.CreateFont(16, 0, 0, 0, 
 							FW_BOLD, FALSE, FALSE, 0, 
   							ANSI_CHARSET,
                OUT_DEFAULT_PRECIS,
                CLIP_DEFAULT_PRECIS,
                PROOF_QUALITY,
                VARIABLE_PITCH | 0x04 | FF_DONTCARE,
                (LPSTR)"Arial");

	fontNormal.CreateFont(14, 0, 0, 0, 
 							FALSE, FALSE, FALSE, 0, 
   							ANSI_CHARSET,
                OUT_DEFAULT_PRECIS,
                CLIP_DEFAULT_PRECIS,
                PROOF_QUALITY,
                VARIABLE_PITCH | 0x04 | FF_DONTCARE,
                (LPSTR)"Arial");
	
	int nSavedDC = m_CreditsDC.SaveDC();

	CString strSub;
	int nCount=0;
	// draw each line, based on specified type
	while(AfxExtractSubString(strSub, m_strCredits, nCount++, '\n'))
	{
		TCHAR nType = 0;

		if (!strSub.IsEmpty())
			nType = strSub.GetAt(0);

		switch(nType)
		{
			case '\t':	// title
				m_CreditsDC.SetTextColor(RGB(0,0,0));
				m_CreditsDC.SelectObject(fontTitle);
				strSub.TrimLeft('\t');
				m_CreditsDC.DrawText(strSub, strSub.GetLength(), &rect, DT_TOP | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE);
				break;
			case '\r':	// bold
				m_CreditsDC.SetTextColor(RGB(0,0,0));
				m_CreditsDC.SelectObject(fontBold);
				strSub.TrimLeft('\r');
				m_CreditsDC.DrawText(strSub, strSub.GetLength(), &rect, DT_TOP | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE);
				break;
			default:	// normal
				m_CreditsDC.SetTextColor(RGB(0,0,0));
				m_CreditsDC.SelectObject(fontNormal);
				m_CreditsDC.DrawText(strSub, strSub.GetLength(), &rect, DT_TOP | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE);
				break;
		}
		// next line
		TEXTMETRIC tm;  
		m_CreditsDC.GetTextMetrics(&tm);
		rect.top += tm.tmHeight;
	}

	m_CreditsDC.SelectObject(pOldBitmap);

	m_CreditsDC.RestoreDC(nSavedDC);
}


/********************************************************************/
/*																	*/
/* Function name : SetCredits										*/
/* Description   : Member function to set credits text.				*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::SetCredits(LPCTSTR lpszCredits)
{
	m_strCredits = lpszCredits;
}


/********************************************************************/
/*																	*/
/* Function name : SetExponentialClouds								*/
/* Description   : Use exponential function to generate clouds		*/
/*																	*/
/********************************************************************/
void CCloudsCtrl::SetExponentialClouds(BOOL bFlag)
{
	// initialize blue color map
    for(int i = 0; i < 256; i++)
	{
		int nValue = bFlag ? (int)(255*CloudExpCurve(i/256.0)) : i;

		m_BlueColorMap[i] = nValue << 16 | nValue << 8 | 255;
	}
}