C++基于Directx MMX实现的图像灰度转换代码

时间:2021-08-11 05:20:13

本文实例讲述了基于Directx MMX 编写的实现图像灰度处理的方法,要编译此程序需DirectX SDK5.0,代码中所需要的ddutil.h与ddutil.cpp文件,请自行下载加入工程。在WindowNT4.0+SP3环境中编译通过,代码已经过整理,包含有注释。如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
#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);
 //释放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;
}

感兴趣的朋友可以调试运行一下本文实例,相信会对大家进行C++项目开发起到一定的帮助作用。