gray.cpp

来自「我以前写的一个灰度处理源代码」· C++ 代码 · 共 541 行

CPP
541
字号
///////////////////////////////////////////////
//
//	灰度转换--HugeSoft工作室  MMX  编写
//
//		电子邮箱:hugesoft@188.net
//		个人主页:http://hugesoft.163.net
//
//	要编译此程序需DirectX SDK5.0示例中的
//		ddutil.h与ddutil.cpp文件,请自行
//		加入工程。
//	要运行此程序需DirectX 3.0以上版
//	本程序在WindowNT4.0+SP3环境中编译通过
//
///////////////////////////////////////////////

#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <ddraw.h>
#include <math.h>

#include "ddutil.h"

#define		MEAN_GRAY		0		//平均值法
#define		MAXIMUM_GRAY	1		//最大值法
#define		WEIGHT_GRAY		2		//加权平均值法

#define		TITLE		"灰度转换"	//窗口标题
#define		CLASSNAME	"Gray"	//窗口类名
#define		WIDTH		640
#define		HEIGHT		480
#define		COLORS		8
#define		STEP		1
#define		SCANLINE	16

HINSTANCE	hInst;		//应用程序实例句柄
HWND		hWndMain;	//主窗口句柄

LPDIRECTDRAW            lpDD;           // DirectDraw对象
LPDIRECTDRAWSURFACE     lpDDSPrimary;   // 主页面
LPDIRECTDRAWSURFACE     lpDDSBack;      // 后台缓冲区
LPDIRECTDRAWSURFACE     lpDDSPic1;      // 离屏页面1
LPDIRECTDRAWPALETTE     lpDDPal;        // 调色板
BOOL                    bActive;        // 应用程序是否活跃?

int Key=0;
bool Contrast=false;
bool Gray=false;

//函数声明
void FreeObjects( void );
BOOL InitDDraw(void);
BOOL InitPalette(void);
BOOL InitSurfaces(void);
void UpdateFrame(void);
void MakeRect(RECT *rect, long left, long top, long right, long bottom);

void IncreaseContrast(BYTE *pByte,const int Low,const int Hight,
					  const float Grad)
{
	if(*pByte<=Low)
		*pByte=0;
	else if((Low<*pByte)&&(*pByte<Hight))
		*pByte=(BYTE)((*pByte-Low)/Grad);
	else
		*pByte=255;
}

void ChangeContrast(int nDelta)
{
	LPPALETTEENTRY Pal = (LPPALETTEENTRY) LocalAlloc( LPTR, sizeof( PALETTEENTRY ) * 256 );
	//获取调色板
	lpDDPal->GetEntries(0,0,256,Pal);
	int Low,High;
	float Grad;

	for(int i=0; i<256; i++)
	{
		if(nDelta>=0)
		{
			Low=0+nDelta;
			High=255-nDelta;
			Grad=((float)(High-Low))/255;
			IncreaseContrast(&Pal[i].peRed ,Low,High,Grad);
			IncreaseContrast(&Pal[i].peGreen ,Low,High,Grad);
			IncreaseContrast(&Pal[i].peBlue ,Low,High,Grad);
		}
		else
		{
			Pal[i].peRed=(BYTE)((int)(Pal[i].peRed/Grad))-nDelta;
			Pal[i].peGreen=(BYTE)((int)(Pal[i].peGreen/Grad))-nDelta;
			Pal[i].peBlue=(BYTE)((int)(Pal[i].peBlue/Grad))-nDelta;
		}
	}
	//更新调色板
	lpDDPal->SetEntries(0,0,256,Pal);
}

//////////////////////////////////////////////////////
//灰度转换函数--本程序的关键
//函数:ConvertToGrayScale
//参数:Method为转换灰度的方式,值可为:
//		MAXIMUM_GRAY=最大值法,MEAN_GRAY=平均值法,
//		WEIGHT_GRAY=加权平均值法
//		Color目前只能为8即只能处理8位调色板方式
//返值:无
//////////////////////////////////////////////////////
void ConvertToGrayScale(unsigned short Method,unsigned short Color)
{
	BYTE Convert; 
	int i;

	switch(Color)
	{
	case 8:
		LPPALETTEENTRY Pal = (LPPALETTEENTRY) LocalAlloc( LPTR, sizeof( PALETTEENTRY ) * 256 );

		//获取调色板
		lpDDPal->GetEntries(0,0,256,Pal);

		switch(Method)
		{
		case MAXIMUM_GRAY:
			//最大值法转换
			for(i=0; i<256; i++)
			{
				//计算的公式为:R=G=B=max(R,G,B)
				Convert=( max(max(Pal[i].peRed ,Pal[i].peGreen),Pal[i].peBlue));
				Pal[i].peRed=Pal[i].peGreen=Pal[i].peBlue=Convert;
			}	
			break;

		case MEAN_GRAY:
			//平均值法转换
			for(i=0; i<256; i++)
			{
				//计算的公式为:R=G=B=(R+G+B)/3
				Convert=((Pal[i].peRed +Pal[i].peGreen+Pal[i].peBlue)/3);
				Pal[i].peRed=Pal[i].peGreen=Pal[i].peBlue=Convert;
			}	
			break;

		case WEIGHT_GRAY:
			//加权平均值法转换
			for(i=0; i<256; i++)
			{
				//计算的公式为:R=G=B=(R*0.3+G*0.59+B*0.11)
				//为不使用浮点乘法,现公式为:
				//R=G=B=(R*3+G*6+B)/10
				//些许的误差可以不计
				Convert=( Pal[i].peRed * 3 + Pal[i].peGreen *6 + Pal[i].peBlue ) / 10; 
				Pal[i].peRed=Pal[i].peGreen=Pal[i].peBlue=Convert;
			}	
			break;			
		}
			
		//更新调色板
		lpDDPal->SetEntries(0,0,256,Pal);
		break;
	}
}

//*******************************************************************
//函数:FreeObject
//功能:释放所有DirectDraw对象
//*******************************************************************
void FreeObjects( void )
{
    if( lpDD != NULL )//释放DirectDraw对象
    {
        if( lpDDSPrimary != NULL )//释放主页面
        {
            lpDDSPrimary->Release();
            lpDDSPrimary = NULL;
        }
        if( lpDDSPic1 != NULL )//释放离屏页面1
        {
            lpDDSPic1->Release();
            lpDDSPic1 = NULL;
        }
        if( lpDDPal != NULL )//释放调色板
        {
            lpDDPal->Release();
            lpDDPal = NULL;
        }
        lpDD->Release();
        lpDD = NULL;
    }
} 

//*******************************************************************
//函数:RestoreAll
//功能:页面丢失后,恢复页面内存
//*******************************************************************
HRESULT RestoreAll( void )
{
    HRESULT     ddrval;

	//恢复主页面,这也将恢复换页链中的所有页面
    ddrval = lpDDSPrimary->Restore();

	//恢复离屏页面
    ddrval = lpDDSPic1->Restore();

	//重新绘制页面图象
	InitSurfaces();

    return ddrval;
}

//*******************************************************************
//函数:WindowProc
//功能:主窗口的消息处理过程
//*******************************************************************
LRESULT CALLBACK WinProc( HWND hWnd, UINT message, 
                            WPARAM wParam, LPARAM lParam )
{
    switch( message )
    {
    case WM_SETCURSOR:
		SetCursor(NULL);
		return TRUE;

    case WM_ACTIVATEAPP://应用程序激活消息
        bActive = wParam;
        break;

    case WM_KEYDOWN://击键消息
        switch( wParam )
        {
        case VK_ESCAPE:
            PostMessage(hWnd, WM_CLOSE, 0, 0);
            break;
		case VK_F1:
			Gray=true;
			Key=MEAN_GRAY;
			InitPalette();
			break;
		case VK_F2:
			Gray=true;
			Key=MAXIMUM_GRAY;
			InitPalette();
			break;
		case VK_F3:
			Gray=true;
			Key=WEIGHT_GRAY;
			InitPalette();
			break;
		case VK_F4:
			Gray=false;
			Key=0;
			InitPalette();
			break;
		case VK_F5:
			Contrast=!Contrast;
			if(false==Contrast)
			{
				InitPalette();
			}
			break;
		case VK_F6:
			break;
		}
        break;

    case WM_DESTROY://销毁窗口消息
        FreeObjects();
        PostQuitMessage(0);
        break;
    }

	//调用缺省的过程处理过程
    return DefWindowProc(hWnd, message, wParam, lParam);
}

//******************************************************************
//函数:InitWindow()
//功能:创建主窗口。
//******************************************************************
BOOL InitWindow( HINSTANCE hInstance, int nCmdShow )
{
    WNDCLASS			wc;		//窗口类结构

	//填充窗口类结构
    wc.style = 0;
    wc.lpfnWndProc = WinProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon( hInstance, IDI_APPLICATION );
    wc.hCursor = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);//选择黑色刷做为窗口背景
    wc.lpszMenuName = NULL;
	wc.lpszClassName = CLASSNAME;

	//注册窗口类
    RegisterClass( &wc );
    
	//创建主窗口
    hWndMain= CreateWindowEx(
		0,
		CLASSNAME,	//窗口的类名称,必须与上面的wc.lpszClassName一致
		TITLE,		//窗口的标题名
		WS_POPUP,
		0,
		0,
		GetSystemMetrics( SM_CXSCREEN ),
		GetSystemMetrics( SM_CYSCREEN ),
		NULL,
		NULL,
		hInstance,
		NULL );

    if( !hWndMain )	
		return FALSE;

	//显示并更新窗口
    ShowWindow( hWndMain, nCmdShow );

	return TRUE;
}

//******************************************************************
//函数:InitDDraw()
//功能:初始化DirectDraw环境,创建换页链(主页面,一个后台缓冲区)
//		以及创建一个定时器。
//******************************************************************
BOOL InitDDraw(void)
{
    DDSURFACEDESC       ddsd;
    DDSCAPS             ddscaps;
    HRESULT             ddrval;

    //创建DirectDraw对象
	ddrval = DirectDrawCreate( NULL, &lpDD, NULL );
    if( ddrval != DD_OK )
        return FALSE;

    //取得全屏独占模式
    ddrval = lpDD->SetCooperativeLevel( hWndMain, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN );
    if( ddrval != DD_OK )
        return FALSE;

    //设置显示器显示模式为640x480x8
    ddrval = lpDD->SetDisplayMode( WIDTH, HEIGHT, COLORS);
    if( ddrval != DD_OK )
        return FALSE;

    //填充换页链结构
    ddsd.dwSize = sizeof( ddsd );
    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
                          DDSCAPS_FLIP |
                          DDSCAPS_COMPLEX;
	//后台缓冲区数量为1
    ddsd.dwBackBufferCount = 1;

	//创建换页链,包括了主页面及其后台缓冲区
    ddrval = lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL );
    if( ddrval != DD_OK )
        return FALSE;

    //获得指向后台缓冲区的页面指针
	ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
    ddrval = lpDDSPrimary->GetAttachedSurface(&ddscaps, &lpDDSBack);
    if( ddrval != DD_OK )
        return FALSE;

	//创建离屏页面
	ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT |DDSD_WIDTH;
    ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
    ddsd.dwWidth = WIDTH;
    ddsd.dwHeight = HEIGHT;

    if (lpDD->CreateSurface(&ddsd, &lpDDSPic1, NULL) != DD_OK)
		return FALSE;

	//调用页面初始化函数
	if( !InitSurfaces() )
        return FALSE;

    return TRUE;
}

//******************************************************************
//函数:WinMain()
//功能:应用程序入口
//******************************************************************
int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        LPSTR lpCmdLine, int nCmdShow)
{
    MSG         msg;
	hInst=hInstance;

	//初始化主窗口
	if (!InitWindow( hInstance, nCmdShow))
			return FALSE;
	
	//初始化DirectDraw环境
	if (!InitDDraw())
	{
		MessageBox(hWndMain, "初始化DirectDraw过程中出错!", "Error", MB_OK);
		FreeObjects();
		DestroyWindow(hWndMain);
		return FALSE;
	}

	//进入消息循环
	while(1)
	{
		if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
		{
			if(!GetMessage(&msg, NULL, 0, 0 ))
				return msg.wParam;
			TranslateMessage(&msg); 
			DispatchMessage(&msg);
		}
		else if(bActive)
		{
			UpdateFrame();
		}
		else WaitMessage();
	}

    return msg.wParam;
} 

BOOL InitPalette(void)
{
    //从磁盘文件中载入调色板
	lpDDPal = DDLoadPalette(lpDD, "back.bmp");
	//将调色板设置给主页面
    if (lpDDPal)
	{
        lpDDSPrimary->SetPalette( lpDDPal );
		return TRUE;
	}
	return FALSE;
}

//******************************************************************
//函数:InitSurfaces()
//功能:初始化页面图象
//******************************************************************
BOOL InitSurfaces( void )
{
    HBITMAP hbm;

	InitPalette();

    //从磁盘文件中将我们所需要的图象载入到一个hbm位图对象中
    hbm = (HBITMAP)LoadImage(hInst, "back.bmp", IMAGE_BITMAP, 
		0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE);
    if (hbm == NULL)
		return FALSE;

	DDCopyBitmap(lpDDSPic1, hbm, 0, 0, WIDTH, HEIGHT);

//	DDSetColorKey(lpDDSPic1, CLR_INVALID);

	//释放hbm位图对象
    DeleteObject(hbm);

    return TRUE;
}

//更新屏幕
void UpdateFrame( void )
{
	HRESULT		ddrval;
	//计算刷新率
	static int fps=0, frame=0, nt=0, ot=0;
	frame++;
	nt=timeGetTime(); 
	if (nt > ot+1000)
	{
		ot=nt;
		fps=frame;
		frame=0;
	}

	//清屏后台缓冲区
	DDBLTFX	ddBltFx;
	ddBltFx.dwSize = sizeof(DDBLTFX);
	ddBltFx.dwFillColor = DDColorMatch(lpDDSBack, RGB(0,0,0));
	lpDDSBack->Blt(NULL, NULL, NULL, DDBLT_WAIT | DDBLT_COLORFILL, &ddBltFx);
	
	//调用灰度的实现函数
	if(true==Gray)
	{
		ConvertToGrayScale(Key,COLORS);
		Gray=false;
	}
	//增加对比度
	if(true==Contrast)
	{
		ChangeContrast(2);
	}

	RECT srect, drect;
	MakeRect(&srect, 0, 0, WIDTH, HEIGHT);
	MakeRect(&drect, 0, 0, WIDTH, HEIGHT);
	//将背景图象Blit到后台缓冲区
	lpDDSBack->Blt(&drect, lpDDSPic1, &srect, DDBLT_WAIT, NULL);

	//打印刷新率fps
	HDC hdc;
	char temp[50];
	lpDDSBack->GetDC(&hdc);
		SetBkMode(hdc, TRANSPARENT);
		SetTextColor(hdc, RGB(0,255,255)); 
		sprintf(temp, "fps=%d", fps);
		TextOut(hdc, 0, 0, temp, strlen(temp));

		sprintf(temp,"图象特效----灰度转换");
		TextOut(hdc, 30, 400, temp, strlen(temp));
	lpDDSBack->ReleaseDC(hdc);

    // 换页
    while( 1 )
    {
        ddrval = lpDDSPrimary->Flip( NULL, DDFLIP_WAIT );//调用换页函数
        if( ddrval == DD_OK )//成功则退出while循环
            break;
        else if( ddrval == DDERR_SURFACELOST )//如果页面丢失,则恢复页面,再继续while循环
            RestoreAll();
        else
			break;
	}
}

void MakeRect(RECT *rect, long left, long top, long right, long bottom)
{
	rect->left=left;
	rect->top=top;
	rect->right=right;
	rect->bottom=bottom;
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?