ddraw位图剪切笔记

    技术2022-05-19  19

    没有什么技术性,写这篇文章只是方便自己日后查询 DirectDraw位图的剪切很简单,当然是基于2D,3D将会复杂很多,这里讨论的是2D剪切。 2D位图剪切的基本原理: 方法1): 可以像像素剪切那样通过设置过滤器逐个剪切每一个像素点 方法2): 软件剪切,一句话来说就是只渲染位图位于显示示图之内的像素 方法3): 硬件剪切,使用IdirectDraw::IdirectDrawClipper接口进行剪切,最简单 NOTE: 方法1)剪切速度过慢,一般人不采用,-。-难道读者你是二般人 方法2) 为了思路清晰,我画了张图 黑色部份是显示视图,蓝色部份是要进行剪切的位图,红色部份是应该渲染出来的位部 份。目的很明确,我们需要的就是位图中的红色部份,要求出的就是矩形范围(dx1,dy1)(dx2,dy2) 下面是我写的一个剪切函数: inline  void  bitmap_clp(       int  x,int  y,   //位图坐标       int  width,int  height,    //位图大小       UCHAR  *bitmap,      //位图数据       UCHAR  *pVbuffer,    //缓冲(相当于图上的显示示图)       int  lpitch)   //步长 { int  dx1,dy1,dx2,dy2;      //剪切目标矩形 int  x1,y1,x2,y2;              //源位图矩形 /*判断位图是否在显示示图之外,如果在之外,当然就不需要剪切了,全部pass  */ if(x>=WIDTH  ||  y>=HEIGHT  ||  (x+width)<=0  ||  (y+height)<=0) return; /*保存源位图矩形*/ x1  =  x; y1  =  y; x2  =  x+width-1; y2  =  y+height-1; /*定位出剪切矩形*/ if(x1  <  0) x1  =  0; if(y1  <  0) y1  =  0; if(x2  >=  WIDTH) x2  =  WIDTH-1; if(y2  >=  HEIGHT) y2  =  HEIGHT-1; //求出x,y距离定位需要剪切(渲染)出来位图数据 dx1  =  x1  -  x;  dy1  =  y1  -  y; //定位位图数据 bitmap  +=  (dx1  +  dy1*width); //定位缓冲位置 pVbuffer  +=  (x1  +  y1*lpitch); //求出剪切矩形的高度和宽度 dx2  =  x2  -  x1+1; dy2  =  y2  -  y1+1; //执行剪切 UCHAR  c; for(int  i=0;i<dy2;i++) { for(int  k=0;k<dx2;k++) { c  =  bitmap[k]; if(c) pVbuffer[k]  =  c; } pVbuffer  +=  lpitch; bitmap  +=  width; } return; } 我的函数为了速度忽略大小使用了inline 短短十几行代码就可以执行剪切了,看代码就知道,需要渲染的只是红色部份 只要定位红色部份位于源位图的位置(UCHAR*  bitmap)还有定位红色部份位于显示示图的位置(UCHAR*  pVbuffer),               //请看代码上下文,花点时间搞清楚dx1和dy1指的是哪个位置               bitmap  +=  (dx1  +  dy1*width);               //定位缓冲位置               pVbuffer  +=  (x1  +  y1*lpitch);   方法3): 现在讨论的是最强大,最简单,速度最快的硬件剪切,各位看官,请仔细看清楚DirectDraw给我们提供的接口IDirectDrawClipper有多方便 需要做的只是 1)    创建DirectDrawClipper对象 2)    定义可见的矩形区域 3)    关联DirectDrawClipper对像到指定的画面(IDirectDrawSurface)上     创建DirectDrawClipper对象 要清楚的是剪切板对象是DirectDraw的接口,调用CreateClipper()就可以创建一个剪切对象了   HRESULT  CreateClipper(     DWORD  dwFlags,                                                //这个标志,总是0     LPDIRECTDRAWCLIPPER  FAR  *lplpDDClipper,//接收返回的剪切板对象     IUnknown  FAR  *pUnkOuter                              //与COM组件相关,这里用不上     );   下面代码创建一个剪切板对象(设lpdd是LPDIRECTDRAW) If(FAILED(lpdd->CreateClipper(0,&lpddclp,NULL))) {       /*    error    */ } 执行后lpddclp是创建好的剪切板对象         定义可见的矩形区域 也就是说,只有定义好的矩形区域才可见,其他的都被cut掉了 现在来看一个结构体   typedef  struct  _RGNDATA  {          RGNDATAHEADER  rdh;          char                    Buffer[1];  }  RGNDATA,  *PRGNDATA;    这个结构是可变长度的,第二个成员Buffer让我郁闷,不过这么定义也挺好玩 第一个成员rdh是RGNDATAHEADER结构体 typedef  struct  _RGNDATAHEADER  {          DWORD  dwSize;                                //结构体的大小         DWORD  iType;                                    //一定是RDH_RECTANGLES         DWORD  nCount;                                //可见矩形的个数 DWORD  nRgnSize;                            //以字体为单位的总的矩形结构大小                                                             //也就是sizeof(RECT)*NUM_RECT         RECT    rcBound;                              //矩形结构,全部矩形的一个外边框 }  RGNDATAHEADER,  *PRGNDATAHEADER;   NOTE:rcBound成员,是全部可见矩形的一个外边框,就是一个可以容纳下所有可见矩形的矩形。只要设得足够大就可以了。   第二个成员char  Buffer[1],这个Buffer定义成一个数组指针,因为这里要放的是可见矩形数组指针。   读者看到这里是不是开始郁闷了,有点吃不消看不懂的迷惑。没关系,下面代码帮助你   //定义好可见矩形位置和大小,必须是连续的矩形数据,所以应该用数组 //我只让三个矩形区域可见,下面是位置和大小 #define  NUM_RECTS  3    //可见矩形数 RECT    cut_rects[NUM_RECTS]  =  {{0,0,10,10},{100,100,130,130},{200,200,250,250}}; //定义RGNDATA LPRGNDATA  lprgndata; //申请RGNDATA的空间,必须这么做,因为要存放可变个数的可见矩形 Lprgndata  =  (LPRGNDATA)malloc(  sizeof(RGNDATAHEADER)  +  sizeof(RECT)*NUM_RECTS  ); //给lprgndata各成员赋值 Lprgndata->rdh.dwSize    =  sizeof(RGNDATAHEADER); Lprgndata->rdh.iType      =  RDH_RECTANGLES; Lprgndata->rdh.nCount    =  NUM_RECTS; Lprgndata->rdh.nRgnSize=  sizeof(RECT)*NUM_RECTS;   //只要足够大就可以了 lprgndata->rdh.rcBound.left        =  60000; lprgndata->rdh.rcBound.top          =  60000; lprgndata->rdh.rcBound.right      =  -60000; lprgndata->rdh.rcBound.bottom    =  -60000;   //这里将定义好的可见各个可见矩形数据拷贝到Buffer成员当中 //这就是为什么要定义成指针而且要申请RGNDATA的空间了 Memcpy(lprgndata->Buffer,cut_rects,sizeof(RECT)*NUM_RECS);   //这样就定义好了可见矩形的序列RGNDATA //调用IdirectDrawClipper接口的SetClipList()将定义好的数据设置在剪切板对象中 //之后如果哪个画面使用这个剪切板就是那几个定义好的矩形可见而已了 // //HRESULT  SetClipList( //    LPRGNDATA  lpClipList,    //定义好的可见矩形序列 //    DWORD  dwFlags                    //总是没有使用,0 //);   If(FAILED(lpddclp->SetClipList(lprgndata,0))) {     /*    error    */ }   //这样就完成了这个剪切板的可见矩形的定义     关联DirectDrawClipper对像到指定的画面(IDirectDrawSurface)上 应该调用IdirectDrawSurfact接口的SetClipper HRESULT  SetClipper(     LPDIRECTDRAWCLIPPER  lpDDClipper    //创建并定义好可见矩形的剪切板对象     );   //设(lpdds7是主画面指针),现在将刚才的剪切板对像关联到主画面上,那主画面就只有那几//个定义好的矩形可见了 If(FAILED(lpdds7->SetClipper(lpddclp))) {     /*    error    */ }   硬件剪切的讨论完成,下面是我给出的一个硬件剪切的函数,和上面的软件剪切一样,直接可以使用         LPDIRECTDRAWCLIPPER  dd_clp(                                                                                           LPDIRECTDRAW7                pdd7,          //DirectDraw对象                                                                                           LPDIRECTDRAWSURFACE7  pdds7,        //与剪切板关联的画面对象                                                                                           RECT*                                                    pRectBuf,  //可见矩形数组指针                                                                                            int                                                                  num_rects)//可见矩形个数 {               LPDIRECTDRAWCLIPPER  pddclp  =  NULL;               LPRGNDATA                              lprgndata;                 if(FAILED(pdd7->CreateClipper(0,&pddclp,NULL)))                             return  NULL;                 lprgndata=(LPRGNDATA)malloc(sizeof(RGNDATAHEADER)  +sizeof(RECT)*num_rects);                             memcpy(lprgndata->Buffer,pRectBuf,sizeof(RECT)*num_rects);               lprgndata->rdh.dwSize    =  sizeof(RGNDATAHEADER);               lprgndata->rdh.iType      =  RDH_RECTANGLES;               lprgndata->rdh.nCount    =  num_rects;               lprgndata->rdh.nRgnSize=  sizeof(RECT)*num_rects;                             lprgndata->rdh.rcBound.left        =  60000;               lprgndata->rdh.rcBound.top          =  60000;               lprgndata->rdh.rcBound.right      =  -60000;               lprgndata->rdh.rcBound.bottom    =  -60000;                 if(FAILED(pddclp->SetClipList(lprgndata,0)))                             return  NULL;                 if(FAILED(pdds7->SetClipper(pddclp)))                             return  NULL;                 free(lprgndata);               return  pddclp;

    最新回复(0)