分享
 
 
 

乱弹运用direct技术进行图像裁减的实现之三(完结篇)

王朝vc·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

又是n(n>7)天没有来维护自己的blog了,还是因为没有时间,今天才想起在我得blog上好像还有没有完成的文章(真是没有责任心的一个人),所以,无论如何今天都要抽点时间来完成自己的东东,总不能背一个半途而废的名声吧。

今天是最后一话了,而且废话了这么久都没有切入主题,偶对不住大家啦,今天我们就正儿八经的的进入这次我们这个冗长的故事的核心的部分了,同时也是最后一个部分。

书归正传,在direct编程里面有两个比较重要的函数,也是使用频率相当高的两个函数,在进入主题前我们必须先介绍这两个函数,我们这个程序里面也是用了他们。

第一个很有用的函数是BltFast(),他的原型是

HRESULT BltFast(

DWORD dwX,

DWORD dwY,

LPDIRECTDRAWSURFACE lpDDSrcSurface,

LPRECT lpSrcRect,

DWORD dwTrans

);

下面将逐一介绍这几个参数:

(1)dwX和dwY:图像将被传送到目标页面何处。

(2)lpDDSrcSurface:图像传送操作的源页面。目标页面就是调用此方法的页面。

(3)lpSrcRect:一个 RECT (Rectangle,即矩形)结构的地址,指明源页面上将被传送的区域。如果该参数是NULL,整个源页面将被使用。RECT结构在DirectDraw中非常常用,最好在程序中定义一个RECT类型的全局变量,如rect,再象这样写一个函数:void MakeRect ()(关于这个函数就是我说的第二个非常有用的函数,我们在后面在专门对他进行介绍和说明)。

(4)dwTrans:指定传送类型。有如下几种:

DDBLTFAST_NOCOLORKEY

指定进行一次普通的复制,不带透明成分。

DDBLTFAST_SRCCOLORKEY

指定进行一次带透明色的图像传送,使用源页面的透明色。

DDBLTFAST_WAIT

如果图像传送器正忙,不断重试直到图像传送器准备好并传送好时才返回。一般都使用这个参数。由于第四个参数比较长,而且有时候是两个搭配起来用,所以大多数时候都是定义两个全局变量,后面使用起来就比较方便。

DWORD SrcKey = DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT

DWORD NoKey = DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT

第二个很有用的函数就是我们前面提到的void MakeRect (),这一般是一个自定义函数,我们可以这样定义他:

void MakeRect (int left, int top, int right, int bottom)

{

rect.bottom = bottom;

rect.left = left;

rect.right = right;

rect.top = top;

}

主要是确定了这个r结构的四个边际。

所以要裁减一副图片到一个页面中就很简单了,是不是^_^,举个例子:我们要贴第一回的哪个背景到SPrimary这个主页面里,就可以这样

MakeRect(0,0,640,480);

lpDDSBuffer->BltFast(0,0,lpDDSPrimary,&r,NoKey);

核心部分

因为哪个背景的大小是640*480,所以我们的右边际是640,下边际是480,最后在利用BltFast()函数贴出来就是了,而且没有透明,这样我们就可以贴图了。

可是怎么样裁减呢?其实也就没什么难度了,你想在MakeRect()这个函数里面我们改变要贴图的大小不就可以贴同一幅图里任意位置的图部分了吗,这就是裁减了塞,然后我们设成变量,用鼠标自定义贴的大小,就必须和windows的消息循环机制联合起来使用了塞,我们在mousedown的时候得到他的一个坐标,在mouseup的时候得到他的另一个坐标,将两个坐标之间的图片贴出来就可以了塞。其实这个裁减程序的主要的核心技术就在这里了,实现思想也就是这么回事,简单吧:),其他的都是一些旁支末节的东西了。不过我们还是要一一介绍完。

ok,到这里我们就可以单独写一个类出来,完成我们的图片裁减和贴图功能了,我们写一个类叫draw.cpp,这里我们还把他的头文件draw.h写出来。

draw.h:

#if !defined (draw_h)

#define draw_h

class draw

{

public:

int x1;

int y1;

int x2;

int y2;

void Rectanglep(int x1, int y1, int width, int height);//做成一个鼠标在屏幕移动矩形的函数

void blt(int x1,int y1,int x2,int y2);//完成我们的图片裁减功能

};

#endif

draw.cpp:

#include "main.h"

void draw::blt(int x1,int y1,int x2,int y2)

{

x1=drawp.x1;

y2=drawp.y1;

x2=drawp.x2;

y2=drawp.y2;

MakeRect(x1,y1,x2,y2);

lpDDSBuffer->BltFast(0,0,lpDDSMap,&r,SrcKey);

}

void draw::Rectanglep(int x1,int y1,int x2,int y2)

{

HDC hdc;

lpDDSPrimary->GetDC(&hdc);//主页面得到句柄

HPEN Red_Pen=CreatePen(PS_SOLID,0,RGB(0,255,0));//填充画笔的颜色

SelectObject(hdc,Red_Pen);//设置画笔的类型

Line(x1,y1,x1,y2,hdc);//画线

Line(x1,y1,x2,y1,hdc);

Line(x2,y1,x2,y2,hdc);

Line(x1,y2,x2,y2,hdc);

lpDDSPrimary->ReleaseDC(hdc);//用完后释放句柄

}

这里就是对这两自定义函数的具体化了。

第一个函数就是我们前面介绍的实现图形的裁减的部分,而且定义了四个变量作为鼠标取点的坐标。

第二个函数就是鼠标在屏幕上运动画出的轨迹,是由四条线组成的一个矩形,这里用到了API函数作图,也就是微软给我们的函数(微软什么都给我们封装好了,用起来还真是方便呀)。

然后就是公共使用的函数的一个类,这里面自定义的函数可以全部被自由的使用

我们定义为

publicfuction.cpp

//direct的初始化,前面介绍过,可以参看前面的章节

void init()

{

DDSURFACEDESC2 ddsd;

DirectDrawCreateEx (NULL, (void **)&lpDD,IID_IDirectDraw7, NULL);

lpDD->SetCooperativeLevel(hwnd,DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN);

lpDD->SetDisplayMode( 640, 480, 32, 0, DDSDM_STANDARDVGAMODE );

memset(&ddsd, 0, sizeof(DDSURFACEDESC2));

ddsd.dwSize = sizeof( ddsd );

ddsd.dwFlags = DDSD_CAPS|DDSD_BACKBUFFERCOUNT;

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE|DDSCAPS_FLIP|DDSCAPS_COMPLEX;

ddsd.dwBackBufferCount = 1;

lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL );

ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;

lpDDSPrimary->GetAttachedSurface( &ddsd.ddsCaps, &lpDDSBuffer );

ddsd.dwSize = sizeof( ddsd );

ddsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT;

ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

ddsd.dwHeight = 480;

ddsd.dwWidth = 640;

lpDD->CreateSurface( &ddsd, &lpDDSMap, NULL );

ddsd.dwHeight=26;

ddsd.dwWidth=32;

lpDD->CreateSurface( &ddsd, &lpDDSMouse, NULL);

ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

DDBLTFX ddBltFx;

ddBltFx.dwSize=sizeof(DDBLTFX);

ddBltFx.dwFillColor=0;

lpDDSPrimary ->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&ddBltFx);

lpDDSBuffer->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&ddBltFx);

lpDDSMap->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&ddBltFx);

lpDDSMouse->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&ddBltFx);

DDReLoadBitmap(lpDDSMap,"inn.BMP");

DDReLoadBitmap(lpDDSMouse,"mouse.BMP");

MakeRect(0,0,640,480);

lpDDSPrimary->BltFast(0,0,lpDDSMap,&r,NoKey);

DDSetColorKey(lpDDSMap,RGB(0,255,0));

DDSetColorKey(lpDDSMouse,RGB(0,255,0));

}

//取矩形举例的函数,前面也介绍过

void MakeRect(int left,int top,int right,int bottom)

{

r.bottom=bottom;

r.left=left;

r.right=right;

r.top=top;

}

//画直线的函数

void Line(int x1,int y1,int x2,int y2,HDC dc)

{

MoveToEx (dc, x1, y1, 0);//得到画直线的起始坐标

LineTo (dc, x2, y2);//得到画直线的终点坐标

}

//在屏幕上输出文字的函数

void print(char text[255],int x,int y)

{

HDC hdc;

lpDDSBuffer->GetDC(&hdc);//后台页面得到句柄

SetBkMode(hdc, TRANSPARENT);//设置显示模式

SetTextColor(hdc, RGB(0,255,255));//输出文字颜色

TextOut(hdc, x, y , text, strlen(text));//输出文字

lpDDSBuffer->ReleaseDC(hdc);//释放句柄

}

//前台和后台页面不断交换的函数,这样才能交替显示两个页面

void Flip()

{

HRESULT ddrval;

ddrval=lpDDSPrimary->Flip(NULL,DDFLIP_WAIT);

if (ddrval==DDERR_SURFACELOST)

Restore();

}

//指针释放的函数,指针用完以后得释放

HRESULT Restore(void)

{

HRESULT ddrval;

ddrval = lpDDSPrimary->Restore();

ddrval = lpDDSBuffer->Restore();

ddrval = lpDDSMouse->Restore();

ddrval = lpDDSMap->Restore();

init();

return ddrval;

}

在公共类里面还有一个最关键得函数就是refresh(),

void refresh()

{

//将背景图贴到buffer页面了,这样刷新的时候一直都在显示一幅图

MakeRect(0,0,640,480);

lpDDSBuffer->BltFast(0,0,lpDDSMap,&r,NoKey);

//鼠标的移动,同样写在刷新里面是为了不会有鼠标移动过的痕迹留下

POINT curpos;

GetCursorPos(&curpos);

for (int i=0;i<2;i++)

{

CursorX[i]=CursorX[i+1];

CursorY[i]=CursorY[i+1];

CursorX[2]=curpos.x;

CursorY[2]=curpos.y;

lpDDSBuffer->BltFast(CursorX[1],CursorY[1],lpDDSMouse,NULL,SrcKey);

}

//ok,调用了裁减的方法,有一个标志,在这个标志为1的时候才调用

if(enable==1)

drawp.blt(drawp.x1,drawp.y1,drawp.x2,drawp.y2);

//调用了鼠标移动画矩形的方法,也有一个标志,当标志为2的时候才调用

if(enable==2)

drawp.Rectanglep(x,y,curpos.x,curpos.y);

//为字符串赋值,以便调用这些字符串

char *text="LEFT MOUSEDOWN:点击鼠标左键可以确定裁剪的一个顶点坐标";

char *text1="LEFT MOUSEMOVE:拖动鼠标可以选取裁剪的区域";

char *text2="LEFT MOUSEUP:放掉鼠标左键可以取定另外一个顶点坐标";

char *text3="SPACE:按空格键可以贴出裁剪区域";

char *text4="世间本无事,庸人自扰自!";

//调用显示字的自定义函数,在屏幕上显示文字,放在刷新页面里是因为可以一直都显示

print(text,10,10);

print(text1,10,30);

print(text2,10,50);

print(text3,10,70);

print(text4,400,450);

//翻页,主页和buffer页交替显示

Flip();

}

就这样,这就是全部功能的实现和原理了,再回到消息循环机制里面看看。

main.h:

#if !defined(Course_Design)

#define Course_Design

#include <windows.h>

#include <math.h>

#include <ddraw.h>

#include "ddutil.h"

#include "draw.h"

extern HDC hdc;

extern RECT r;

extern HWND hwnd;

extern DWORD SrcKey;

extern DWORD NoKey;

extern POINT curpos;

extern int enable;

extern int x;

extern int y;

extern int x3;

extern int y3;

extern LPDIRECTDRAW7 lpDD;

extern LPDIRECTDRAWSURFACE7 lpDDSPrimary;

extern LPDIRECTDRAWSURFACE7 lpDDSBuffer;

extern LPDIRECTDRAWSURFACE7 lpDDSMap;

extern LPDIRECTDRAWSURFACE7 lpDDSMouse;

extern draw drawp;

void init();

void MakeRect(int left,int top,int right,int bottom);

void refresh();

void Flip();

void print(char text[255],int x,int y);

void Line(int x1,int y1,int x2,int y2,HDC dc);

HRESULT Restore(void);

#endif

main.cpp

#include "main.h"

main.cpp

BOOL InitWindow( HINSTANCE hInstance, int nCmdShow );

LRESULT CALLBACK WinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );

HWND hwnd;

RECT r;

HDC hdc;

POINT curpos;

int enable;

int x;

int y;

int x3;

int y3;

LPDIRECTDRAW7 lpDD;

LPDIRECTDRAWSURFACE7 lpDDSPrimary;

LPDIRECTDRAWSURFACE7 lpDDSBuffer;

LPDIRECTDRAWSURFACE7 lpDDSMap;

LPDIRECTDRAWSURFACE7 lpDDSMouse;

DWORD SrcKey = DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT;

DWORD NoKey = DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT;

draw drawp;

int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,

int nCmdShow)

{

MSG msg;

InitWindow(hInstance,nCmdShow);

init();//初始化

while(1)

{

if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))

{

if (msg.message==WM_QUIT)

break;

TranslateMessage(&msg);

DispatchMessage(&msg);

}

else

{

refresh();//消息循环,完成刷新功能,具体功能在前面介绍这个函数时说过了

}

}

return msg.wParam;

}

static BOOL InitWindow( HINSTANCE hInstance, int nCmdShow )

{

WNDCLASS wc;

wc.style = NULL;

wc.lpfnWndProc = (WNDPROC)WinProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hInstance;

wc.hIcon = NULL;

wc.hCursor = NULL;

wc.hbrBackground = NULL;

wc.lpszMenuName = NULL;

wc.lpszClassName = "Course Design";

RegisterClass(&wc);

hwnd = CreateWindow("Course Design","My Course

Design",WS_POPUP|WS_MAXIMIZE,0,0,GetSystemMetrics( SM_CXSCREEN ),GetSystemMetrics( SM_CYSCREEN ), NULL,NULL,hInstance,NULL);

if( !hwnd ) return FALSE;

ShowWindow(hwnd,nCmdShow);

UpdateWindow(hwnd);

return TRUE;

}

LRESULT CALLBACK WinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )

{

switch( message )

{

case WM_SETCURSOR:

SetCursor(NULL);

return 0;

case WM_KEYDOWN:

switch( wParam )

{

case VK_ESCAPE://按ESC键退出程序

MessageBox(hWnd,"quit!","Keyboard",MB_OK);

PostQuitMessage( 0 );

break;

case VK_SPACE://按空格键得到两个坐标,并将标志设为1

drawp.x1=x;

drawp.y1=y;

drawp.x2=x3;

drawp.y2=y3;

enable=1;

return 0;

}

return 0;

case WM_LBUTTONDOWN://按下鼠标左键得到一个点的坐标

x=(int)LOWORD(lParam);

y=(int)HIWORD(lParam);

break;

case WM_LBUTTONUP://放开鼠标左键得到一个点的坐标。并画出这个矩形

x3=(int)LOWORD(lParam);

y3=(int)HIWORD(lParam);

drawp.Rectanglep(x,y,x3,y3);

break;

case WM_MOUSEMOVE://鼠标移动时设置标志为2

enable=2;

break;

case WM_DESTROY:

PostQuitMessage( 0 );

return 0;

}

return DefWindowProc(hWnd, message, wParam, lParam);

}

这样全部代码我就放出来,我想只要比照着注释看就能明白个大概了。

总结之,洋洋洒洒的写了这么多东西,自己都感觉很凌乱,用一句口头禅:“清不到方向”,所以有必要总结一下,总体来说,我们做了一个图片裁减的小程序,然后这是一个很简单的程序,主要用到了Windows消息循环和Direct编程这两个方面的知识,而要实现程序的核心就是裁减也就只用到了Direct编程的贴图(BltFast())函数,也就是贴图的大小和范围的变化不同就实现了裁减了。(程序-->裁减-->direct-->BltFast),这就是核心完成的任务了,其他的无非是使用起来方便或者其他的。希望我把自己要说的说清楚了的哈。希望大家能看懂我说的是什么,也就不枉费我莫名其妙的说了一大串了哈。

说实话这是我第一次写这么多字的技术性文章,没有什么经验,自己都觉得写的比较凌乱而且似乎没有重点,我只能做到尽量做到把关键的原理和知识说清楚,把源代码尽量多的做上注释,让大家看明白,如果有什么看不懂和希望问的就给我留言到邮箱,有时间的话,我会尽量给大家回复清楚的。其实Dierct编程是很有意思的一个部分,这里面包含了很多的东西,甚至还可以做游戏,不过我也只是一知半解,还有很多东西至今没有琢磨透,同时也非常希望在这方面编程有自己的心得和体会的朋友多和我交流,共同进步啦。

总算大功告成,辛苦啊!!

一直以为写东西是件多么简单的事,到今天自己亲自尝试了后才知道,原本世界上根本没有什么简单的东西哈,这样一来更加佩服侯捷老师和李维老师的智慧了,写出来的东西丝丝入扣,不但能够简单明了的说清楚事物的本质,而且语言幽默,看了以后真是过目不忘哈。还应该多多学习哈。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有