高级DirectDraw和位图图形

转至:http://blog.sina.com.cn/s/blog_7948916001015cyh.html

1.获取表面的像素格式
    函数:HRESULT IDIRECTDRAWSURFACE7::GpixelFormat(LPDDPIXELFORMAT lpDDPixelFormat)
    DDPIXELFORMAT 比较重要的域有:
    DWORD dwSize:调用函数之前必须设置为DDPIXELFORMAT结构的大小
    DWORD dwFlags:可以是一下值
      DDPF_ALPHA 像素格式描述一个只有alpha的表面
      DDPF_PALETTEINDEXED8 画面是8位色彩索引,普遍
      DDPF_RGB 像素格式中的RGB数据有效
      DDPF_ZBUFFER 像素格式描述一个Z缓冲画面
    DWORD dwRGBBitGount RGB中红绿蓝的位数
    下面就是测试的代码:
    ey:
    DDPIXELFORMAT ddpixel;
    memset(&ddpixel,0,sizeof(ddpixel));
    ddpixel.dwSize = sizeof(ddpixel);
    lpdds_primary->GetPixelFormat(&ddpixel);
    if(ddpixel.dwFlags & DDPF_RGB)
    {
      switch(ddpixel.dwRGBBitCount)
      {
        case 15:
        {} break;
        case 16:
        {} break;
        case 24:
        {} break;
        case 32:
        {} break;
        deault:
           break;
      }
    }
   else if(ddpixel.dwFlags & DDPF_PALETTEINDEXED8)
   {
    
   }
   else
   {
   }
2.绘图
  1.获取了像素格式,在上面我们已经讨论过怎么做了
  2.锁定表面,使用Lock ()函数
  3.建立16bitRGB字,使用自写的宏 _RGB16BIT565
  4.写像素,这意味着采用一个USHORT指针定位主缓冲,将写入VRAM缓冲
  5.解锁主表面,调用Unlock();
  ey:
     void plot_pixel16(int x,int y int r,int g,int b,LPDIRECTDRAWSURFACE7 lpdds)
    {
      DDSURFACEDESC2 ddsd;
      memset(&ddsd,0,sizeof(ddsd));
      ddsd.dwSize(sizeof(ddsd));
      USHORT pixel = _RGB16BIT565(r,g,b);
      lpdds->Lock(NULL,&ddsd,DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR,NULL);
      USHORT *video_buffer = ddsd.lpSurface;
      videl_buffer[x+y*(ddsd.lpitch>>1)] = pixel;
      lpdds->Unlock(NULL);
    }
    但是这个函数实在太慢,因为每次函数都需要加锁解锁,这要花费很多时间,更好的办法是当所有操作之前
    加锁一个,当所有的操作都完成之后解锁一次,下面是对函数的一点点改进
    ey:
    inline void plot_pixel16_Fast16(int x,int y int r,int g,int b,USHORT*video_buffer,int lpitch)
    {
      USHORT pixel = _RGB16BIT565(r,g,b);
      videl_buffer[x+y*(lpitch>>1)] = pixel;
    } 
    更好的,你可以在lpitch移位之前就处理
    ey:
    int lpitch16 = lpitch>>1;
   inline void plot_pixel16_Fast16(int x,int y int r,int g,int b,USHORT*video_buffer,int lpitch16)
    {
      USHORT pixel = _RGB16BIT565(r,g,b);
      videl_buffer[x+y*lpitch16] = pixel;
    } 
    用16bit模式向屏幕写点是不用调色板的,下面就是一个例子
    ey:
      int main()
      {
       DDSURFACEDESC2 ddsd;
       memset(&ddsd,0,sizeof(ddsd));
       ddsd.dwSize = sizeof(ddsd);
       if(FAILED(lpddsprimary->Lock(NULL,&ddsd,DDLOCK_WIAT | DDLOCK_SURFACEMEMORYPTR,NULL))
       {
        return ;
       }
       int lpitch16 = (int)ddsd.lpitch>>1;
       USHORT *video_buffer = (USHORT*)ddsd.lpSuface;
       for(int i=0;i<1000;i++)
       {
        int r = rand()%6;
        int g = rand()%6;
        int b = rand()%6;
        int y = rand()H0;
        int x = rand()d0;
        plot_pixel16_Fast16(x,y,r,g,b,video_buffer,lpitch16);
       }
       if(FAILED(lpddsprimary->Unlock()))
       {
        return 
       }
      }
     同理,我们也可以有32位色彩的处理方式
    #define _RGB32BIT(a,r,g,b) (b+ (g<<8) + (r<<16) + (a<<24))
    UINT lpitch32 = (UINT)ddsd.lpitch>>2;
   inline void plot_pixel32(int x,int y,int a  int r,int g,int b,UINT*video_buffer,int lpitch32)
    {
      UINT pixel = _RGB32BIT(a,r,g,b);
      videl_buffer[x+y*lpitch32] = pixel;
    } 
3.双缓冲
  在离屏缓冲中绘制图像,然后将其拷贝到显示表面的处理过程称作双缓冲技术。
  首先创建640x480x8模式下的双缓冲代码
  UCHAR *double_buffer = (UCHAR*)malloc(640*480);
  或者
  UCHAR *double_buffer = new UCHAR[640*480];
  然后把double_buffer里的代码拷贝到primary_buffer,但是这里需要一点小小的措施,因为mempitch或者主表
  面的步长刚好是640的时候才行,否则需要一行一行地拷贝
  ey:
  if(mempitch == 640)
  {
     memcpy((void*)primary_buffer,(void *)double_buffer,640*480);
  }
  else
  {
    for(int y=0;y<480;y++)
    {
       memcpy((void*)primary_buffer,(void *)double_buffer,640);
       primary_buffer += mempitch;
       double_buffer += 640;
     }
  }
4.表面动态
  离屏表面--后备缓冲:指的是一些用在动画链中的表面,它们具有和主表面一样的尺寸和色深,当创建主表面
  的时候也创建它们,它们都是主表面的页面切换中的环节。
  从技术上讲,可以在切换链中创建任意个后备缓冲,但是VRAM会用完,标准是两个,不过3个性能更佳!
  创建后备缓冲,必须创建DirectDraw的复杂表面(complex-surface)
  1.将DDSD_BACKBUFFERCOUNT加到ddsd的dwFlags,后备缓冲的数目
  2.将DDSCAPS_COMPLEX | DDSCAPS_FLIP加到ddsd.ddsCaps_dwCaps上
  3.调用 HRESULT::IDIRECTDRAWSURFACE::GetAttachedSurface(LPDDSCAPS2 lpDDSCaps,LPDIRECTDRAWSURFACE7
                                                                 FAR*lplpDDAttachedSurface     )
  lpDDSCaps是DDSCAPS2结构中包含所需表面特性的标志
  ey:
  DDSURFACEDESC2 ddsd;
  LPDIRECTDRAWSURFACE7 lpddsprimary = NULL;
  LPDIRECTDRAWSURFACE7 lpddsback = NULL;
  memset(&ddsd,0,sizeof(ddsd));
  ddsd.dwSize = sizeof(ddsd);
  ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
  ddsd.dwBackBufferCount = 1;
  ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP;
  if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL))){ return ;}
  ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
  if(FAILED(lpddsprimary->GetAttachSurface(&ddsd.ddsCaps,&lpddsback))){ return ;}
  接下来你就可以想操作主表面一个操作后备缓冲,当然得先Lock,操作完后还得Unlock.
5.页面切换
  如今有了主显示表面和后备缓冲的复杂表面,就可以进行页面切换了。
  1.清楚后备缓冲
  2.将场景渲染到后备缓冲
  3.用后备缓冲表面切换掉主表面
  4.锁定在某个fps(例如30)
  5.重复第一步
  可以使用HRESULT IDIRECTDRAWSUFACE7::Flip(IDIRECTDRAWSUFACE7 lpDDSurfaceTargetoverride,
                                                               DWORD dwFlags);
  在交换链中用下一个关联的表面切换主表面
  第一个参数用来实现切换到另外一个表面,而不是同一个表面的关联表面(后备缓冲)。
  dwFlags一般设置为DDFLIP_WAIT:强迫硬件不在出现问题时立即返回,而是等待到直到页面切换为止。
  ey:
  while(FAILED(lpddsprimary->Flip(NULL,DDLIP_WAIT)));
  下面就是一个例子,使用了页面切换的技术
  ey:
 int Game_Main()
{
 memset(&ddsd,0,sizeof(ddsd));
 ddsd.dwSize = sizeof(ddsd);
 if(FAILED(lpddsback->Lock(NULL,DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR)))
 {
  return;
 }
 UCHAR *back_buffer = (UCHAR*)ddsd.lpSurface;
 if(ddsd.lpitch == 640) //后备反冲清零
 {
  memset(back_buffer,0,640*480);
 }
 else
 {
  UCHAR* dest_ptr = back_buffer;
  for(int index = 0;index<480;index++)
  {
   memset(dest_ptr,0,640);
   dest_ptr += ddsd.lpitch;
  }
 }
 for(int i = 0;i<5000;i++) //绘点
 {
  int x = rand()d0;
  int y = rand()H0;
  int col = rand()%6;
  back_buffer[x+y*ddsd.lpitch] = col;
 }
 if(FAILED(lpddsback->Unlock(NULL))
 {
  return ;
 }
 while(FAILED(lpddprimary->Flip(NULL,DDFLIP_WAIT))); //切换交换链
 return 1;
 }
 
6 使用blitter
ey:8x8x256的位图,将这个图像拷贝到视频缓冲或者离屏表面的(x,y)处,分辨率640x480
  UCHAR *bitmap[8*8];
  UCHAR *video_buffer;
  for(int index_y =0;index_y<8;index_y++)
  {
      for(int index_x =0;index_x<8;index_x++)
      {
         video[x+index_x+(y+index_y)*640] = bitmap[x+y*8];
      }
  }
 但是这个函数实在是太慢了,下面的或许更好一点
 ey:
 void bit8x8(int x,int y,UCHAR*video_buffer,UCHAR *bitmap)
{
 video_buffer += x+(y<<9)+(y<<7); //这里目标指针已经移到了(x,y)
 for(int index_y=0;index_y<8;index_y++)
 {
  for(int index_x=0;index_x<8;index_x++)
  {
   if(pixel = bitmap[index_x])
   {
    video_buffer[index_x] = pixel;
   }
  }
  video_buffer+=640;
  bitmap+=8;
 }
}
但是这个函数只能工作在640*480的分辨率下,且只能对8位位图起作用
 
7. 使用blitter进行内存填充
  DirectDraw两个用于显存移动的函数IDIRECTDRAWSURFACE7::Blt()和BltFast()
  HRESULT Blt(LPRECT lpDestRect,LPDIRECTDRAWSURFACE7 lpDDSrcSurface,
               LPRECT lpSrcRect,DWORD dwFlags,LPDDBLTFX lpDDBltFx);
  lpDestRect:定义了左上角和右下角的RECT结构,如果参数是NULL,将指向整个目标表面
  lpDDSrcSurface:源LPDIRECTDRAWSURFACE7 接口地址
  lpSrcRect:定义了左上角和右下角的RECT结构,如果参数是NULL,将指向整个源表面
  dwFlags:决定了DDBLTFX的有效成员,控制标志,可以是以下值
    DDBLT_COLORFILL:使用DDBLTFX结构中dwFillColor成员作为填充目标表面上的RGB颜色
    DDBLT_ROTATIONANGLE:使用dwRotationAngle成员作为表面旋转角
    DDBLT_KEYSRC:使用和源表面相关的色彩关键字
    DDBLT_WAIT:等待知道bit能被执行,即使忙也不返回错误信息
  lpDDBltFx:包含blitter所需要的信息结构
 
  HRESULT BltFast(DWORD dwX,DWORD dwY,LPDIRECTDRAWSURFACE7 lpDDSrcSurface,
               LPRECT lpSrcRect,DWORD dwTrans);
  dwTrans:是blitter的操作类型
    DDBLTFAST_SRCCOLORKEY 指定一次使用源色彩键的透明blit
    DDBLTFAST_NOCOLORKEY 不使用透明的标准blit
    DDBLTFAST_WAIT 直到blit能被执行
  1.将要填充的色彩索引或者RGB编码的颜色放进DDBLTFX的dwFillColor字段
  2.将RECT结构设置为目标表面上的填充区域
  3.从接口指针调用bit
  ey:
  DDBLTFX ddbltfx;
  RECT rcDest;
  memset(&ddbltfx,0,sizeof(ddbltfx));
  ddbltfx.dwSize = sizeof(ddbltfx);
  ddbltfx.dwFillColor = color;
  rcDest.left = x1;
  rcDest.top = y1;
  rcDest.right = x2;
  rcDest.bottom = y2;
  lpddsprimary->Blt(&rcDest,NULL,NULL,DDBLTFX_WAIT | DDBLTFX_COLORFILL,&ddbltfx);
8.基础剪裁知识
  剪裁:不画那些落在视口或者窗口之外的像素或图像元素
  DirectDraw提供IDirectDrawClipper接口下的DirectDraw裁剪器,需要做的就是创建一个IDirectDrawClipper
  传给他有效的剪裁区域,然后和表面关联,这样Blt()的时候,他将按剪裁器裁剪
  ey:裁剪640*480*8
  void Blit_Clipper(int x,int y,//位图坐标
    int width,int height,//位图大小
    UCHAR *bitmap,//位图数据
    UCHAR *video_buffer,//表面内存
    int mempitch//表面步长
)
{
 if((x>=640 || y>=480) || (x+wight<=0) || (y+height<=0))
 {
  return ;
 }
 int x1 = x;
 int x2 = x+weight-1;
 int y1 = y;
 int y2 = y+height-1;
 if(x1<0)
  x1 = 0;
 if(y1<0)
  y1 = 0;
 if(x2>=640)
  x2 = 640;
 if(y2>=480)
  y2 = 480;
 int x_off = x1-x;
 int y_off = y1-y;
 int dx = x2 - x1;
 int dy = y2 - y1;
 video_buffer += (x1+y1*640); //表面开始坐标
 bitmap += (x_off + y_off*width);//位图开始坐标
 for(int i=0;i<=dy;i++)
 {
  for(int j=0;j<=dx;j++)
  {
   if(pixel = bitmap[j])
    video_buffer[j] = pixel;
  }
  bitmap += width;
  video_buffer += mempitch;
 }
}
9 使用IDirectDrawClipper进行Direct裁剪
  设置DirctDraw 裁剪器对象
  1.创建DirectDraw裁剪器对象
  2.创建裁剪序列
  3.用IDIRECTDRAWCLIPPER::SetClipList()将裁剪序列发送给裁剪器
  4.用IDIRECTDRAWCLIPPER::SetClipper()将裁剪器同窗口和表面相关联
  创建IDirectDrawClipper接口的函数
    HRESULT IDIRECTDRAW7::CreateClipper(DWORD dwFlags, //总是为0
                                        LPDIRECTDRAWCLIPPER FAR*lplpDDClipper,//返回的接口地址
                                        IUnKnown FAR* pUnkOuter //高级 ,设置为0
                                        )
  ey:
  LPDIRECTDRAWCLIPPER lpddclipper = NULL;
  if(lpdd->CreateClipper(NULL,lpddclipper,NULL)) return ;
  创建裁剪序列,是RECT结构的列表,DirectDraw的blitter系统的这些区域内进行blit,要创建它,需要填充
  RGNDATA的数据结构
    typedef struct _RGNDATA
    {
      RGNDATAHEADER rhd;
      char Buffer[1];
    } RGNDATA; 这个一个可变大小的数据结构,Buffer[]的长度是任意的,实际长度放在rdh中
      typedef struct _RGNDATAHEADER
      {
         DWORD dwSize; //sizeof(RGNDATAHEADER )
         DWORD iType;  //RDH_RECTANGLES
         DWORD nCount; //RECT的数量
         DWORD nRgnSize;//sizeof(RECT)*nCount
         RECT rcBound;  //在所有的RECT周围创建一个边框并存储在rcBound中
      }RGNDATAHEADER ;
  一旦RGNDATA结构好了,就可以通过IDIRECTDRAWCLIPPER::SetClipList()发送给创建好的裁剪器
  HRESULT SetClipList(LPRGNDATA lpClipList,DWORD dwFlags)
  最后关联到表面
  HRESULT IDIRECTDRAWSURFACE7::setClipper(LPDIRECTDRAWCLIPPER lpDDClipper);
  ey:
  LPDIRECTDRAWCLIPPER DDraw_Clipper(LPDIRECTDRAWSURFACE7 lpdds,int rect_nums,LPRECT clip_list)
{
 LPDIRECTDRAWCLIPPER lpddclipper;
 LPRGNDATA rgn_data;
 if(FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL)) return ;
 rgn_data = (LPRGNDATA)malloc(sizeof(RGNDATAHEADER)+sizeof(RECT)*rect_nums);
 memcpy(rgn_data->Buffer,clip_list,sizeof(RECT)*rect_nums);
 rgn_data->rdh.dwSize = sizeof(RGNDATAHEADER);
 rgn_data->rdh.iType = RDH_RECTANGLES;
 rgn_data->rdh.nCount = rect_nums;
 rgn_data->rdh.nRgnSize = rect_nums*sizeof(RECT);
 rgn_data->rdh.rcBound.left = 64000;
 rgn_data->rdh.rcBound.top  = 64000;
 rgn_data->rdh.rcBound.right = -64000;
 rgn_data->rdh.rcBound.bottom = -64000;
 for(int index = 0;index<rect_nums;index++)
 {
  if(clip_list[index].left<rgn_data->rdh.rcBound.left)
   rgn_data->rdh.rcBound.left = clip_list[index].left;
  if(clip_list[index].right>rgn_data->rdh.rcBound.right)
   rgn_data->rdh.rcBound.right = clip_list[index].right;
  if(clip_list[index].top<rgn_data->rdh.rcBound.top)
   rgn_data->rdh.rcBound.top = clip_list[index].top;
  if(clip_list[index].bottom>rgn_data->rdh.rcBound.bottom)
   rgn_data->rdh.rcBound.bottom = clip_list[index].bottom;
 }
 if(FAILED(lpddclipper->SetClipList(rgn_data,0)))
 {
  free(rgn_data);
  return ;
 }
 if(FAILED(lpdds->SetClipper(lpddClipper)))
 {
  free(rgn_data);
  return ;
 }
 free(rgn_data);
 return lpddClipper;
}
10.使用位图
  位图.BMP格式:位图文件头 BITMAPFILEHEADER,存放图像的总体信息
                位图信息段 BITMAPINFO:包含BITMAPINFOHEADER和调色板信息部分(有的话)
                位图数据区域:数据逐行排列,有时是颠倒的,可以通过BITMAPINOFHEADER.biHeight判断
                                          +号表示颠倒,-号表示正常
 
  读取位图文件,我们先创建一个位图文件的额结构体
    typedef struct BITMAP_FILE_TAG
    {
       BITMAPFILEHEADER bitmapfileheader;
       BITMAPINFOHEADER bitmapinfoheader;
       PALETTEENTRY palette[256];
       UCHAR *buffer
     }BITMAP_FILE,*BITMAP_FILE_PTR;
  读取位图的函数:
  int Load_Bitmap_File(BITMAP_FILE_PTR bitmap,char * filename)
{
 int file_handle;// 文件句柄
 int index;  //循环变量
 UCHAR *temp_buffer = NULL;
 OFSTRUCT file_data; //用来保存文件信息
 if((file_handle=OpenFile(filename,&file_data,OF_READ))==-1)//打开文件
 {
  return 0;
 }
 _lread(file_handle,&bitmap->bitmapfileheader,sizeof(BITMAPFILEHEADER));//读取头文件
 if(bitmap->bitmapfileheader.bfType!=BITMAP_ID)//检测打开的是不是一个位图文件
 {
  _lclose(file_handle);
  return 0;
 }
 _lread(file_handle,&bitmap->bitmapinfoheader,sizeof(BITMAPINFOHEADER));//读取信息头文件
 if(bitmap->bitmapinfoheader.biBitCount == 8)//加载调色板信息
 {
  _lread(file_handle,&bitmap->palette,MAX_COLORS_PALETTE*sizeof(PALETTEENTRY));
  for(index=0;index<MAX_COLORS_PALETTE;index++)
  {
   int temp_color = bitmap->palette[index].peRed;
   bitmap->palette[index].peRed = bitmap->palette[index].peBlue;
   bitmap->palette[index].peBlue = temp_color;
   bitmap->palette[index].peFlags = PC_NOCOLLAPSE;
  }
 }
 _lseek(file_handle,-(int)(bitmap->bitmapinfoheader.biSizeImage),SEEK_END);//检测是不是自身的文件
 //读取图片文件
 if(bitmap->bitmapinfoheader.biBitCount==8 || bitmap->bitmapinfoheader.biBitCount==16 || bitmap->bitmapinfoheader.biBitCount==24)
 {
  if(bitmap->buffer)
  {
   free(bitmap->buffer);
  }
  if(!(bitmap->buffer = (UCHAR*)malloc(bitmap->bitmapinfoheader.biSizeImage)))
  {
   _lclose(file_handle);
   return 0;
  }
  _lread(file_handle,bitmap->buffer,bitmap->bitmapinfoheader.biSizeImage);
 }
 else
 {
  return 0;
 }
 #if 0
  printf("\nfilename:%s \nsize=%d \nWidth=%d \nheight=%d \nbitsperpixel=%d \ncolors=%d \nimpcolors=%d",
   filename,
   bitmap->BITMAPINFOHEADER.nSizeImage,
   bitmap->BITMAPINFOHEADER.biWidth,
   bitmap->BITMAPINFOHEADER.biHeight,
   bitmap->BITMAPINFOHEADER.biBitCount,
   bitmap->BITMAPINFOHEADER.biClrUsed,
   bitmap->BITMAPINFOHEADER.biClrImportant
   );
 #endif
  _lclose(file_handle);
 Flip_Bitmap(bitmap->buffer,bitmap->bitmapinfoheader.biWidth*(bitmap->bitmapinfoheader.biBitCount/8),bitmap->bitmapinfoheader.biHeight);
 return 1;
}


//处理上下颠倒的图片
int Flip_Bitmap(UCHAR *image,int bytes_per_line,int height)
{
 UCHAR*buffer;
 int index;
 if(!(buffer=(UCHAR*)malloc(bytes_per_line*height)))
 {
  return 0;
 }
 memcpy(buffer,image,bytes_per_line*height);
 for(index=0;index<height;index++)
 {
  memcpy(&image[((height-1)-index)*bytes_per_line],&buffer[index*bytes_per_line],bytes_per_line);
 }
 free(buffer);
 return 1;
}
int Unload_Bitmap_File(BITMAP_FILE_PTR bitmap)
{
 if(bitmap->buffer)
 {
  free(bitmap->buffer);
  bitmap->buffer = NULL;
 }
 return 1;
}
11.离屏表面
  创建离屏表面
  1.DDSURFACEDESC2.dwFlags = (DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT)
  2.DDSURFACEDESE2.dwWidth和DDSURFACEDESC2.ddwHeight设置为所请求的大小
  3.DDSURFACEDESC2.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | memory_flags,memory_flags可以是创建在
    VRAM中VIDEOMEMORY,可以是系统内存中SYSTEMMEMORY;
  ey:创建任何类型的表面
  LPDIRECTDRAWSURFACE7 DDraw_Create_Surface(int width,int height,int mem_flags)
  {
     DDSURFACEDESC2 ddsd;
     LPDIRECTDRAWSURFACE7 lpdds;
     memset(&ddsd,0,sizeof(ddsd));
     ddsd.dwSize = sizeof(ddsd);
     ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
     ddsd.dwWidth = width;
     ddsd.dwHeight = height;
     ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | mem_flags;
     if(FAILED(lpdd->CreateSurface(&ddsd,&lpdds,NULL)))
     {
        return ;
     }
     DDCOLORKEY color_key;
     color_key.dwColorSpaceLowValue = 0;
     color_key.dwColorSpaceHeightValues = 0;
     lpdds->SetColorKey(DDCKEY_SRCBLT,&color_key);
     return lpdds;
  }
12.色彩键
  源色彩键:选择单一的色彩索引或者一定范围的RGB色彩作为源图像的透明色,然后,将源blit向目标时,不
  拷贝有透明色的像素
  IDIRECTDRAWSURFACE7::SetColorKey();
  typedef struct _DDCOLORKEY
  {
    DWORD dwColorSpaceLowValue;
    DWORD dwColorSpaceHeightValue;
  }DDCOLORKEY,FAR *LPDDCOLORKEY;
 13.色彩效果
  色彩动画是指在运行中对调色板进行操作,如更改或轮转,例如:发光物质,闪烁的灯光等许多效果。
  写一个函数,实现功能:
  1.创建最多256盏的闪光灯,
  2.每盏灯有亮灭的状态,之间有一段延时
  3.通过ID能打开或者关闭任何一盏灯
  4.终止一盏灯并收回资源
  存储每一盏灯的数据结构:
  typedef struct BLINKER_TYPE
  {
    int color_index;
    PALETTEENTRY on_color;
    PALETTEENTRY off_color;
    int on_time;
    int off_time;
    int counter;  //记录状态转变的次数
    int state;
  }BLINKER,*BLINKER_PTR;
  下面是代码:
  int Blink_Colors(int command,BLINKER_PTR new_light,int id)
{
 static BLINKER light[256];
 static int initialized = 0;
 if(!initialized)
 {
  initialized = 1;
  memset((void *)light,0,sizeof(light));
 }
 switch (command)
 {
  case BLINKER_ADD:
   {
    for(int index_light=0;index_light<256;index_light++)
    {
     if(light[index_light].state == 0)
     {
      light[index_light] = *new_light;
      light[index_light].counter = 0;
      light[index_light].state = -1;
      lpddpal->SetEntries(0,light[index_light].color_index,1,&light[index_light].off_color);
     }
    }
   }
   break;
  case BLINKER_DELETE:
    if(light[id].state!=0)
    {
     memset((void*)&light[id],0,sizeof(BLINKER));
     return id;
    }
    else
    {
     return -1;
    }
   break;
  case BLINKER_UPDATE:
   {
    if(light[id].state!=0)
    {
     light[id].on_color = new_light->on_color;
     light[id].off_color = new_light->off_color;
     light[id].on_time = new_light->on_time;
     light[id].off_time = new_light->off_time;
     
     if(light[id].state == -1)
     {
      lpddpal->SetEntries(0,light[id].color_index,1,&light[id].off_color);
     }
     else
     {
      lpddpal->SetEntries(0,light[id].color_index,1,&light[id].on_color);
     }
     return id;
    }
    else
    {
     return -1;
    }
   }
   break;
  case BLINKER_RUN:
   {
    for(int index=0;index<256;index++)
    {
     if(light[index].state == -1)
     {
      if(++light[index].counter >= light[index].off_time)
      {
       light[index].counter = 0;
       light[index].state = -light[index].state;
       lpddpal->SetEntries(0,light[index].color_index,1,&light[index].on_color);
      }
      
     }
     else if(light[index].state == 1)
     {
      if(++light[index].counter >= light[index].on_time)
      {
       light[index].counter = 0;
       light[index].state = -light[index].state;
       lpddpal->SetEntries(0,light[index].color_index,1,&light[index].off_color);
      }
     }
    }
   }
   break;
  default: break;
 }
 return 1;
}
 
   //色彩效果
 BLINKER temp;
 PALETTEENTRY red = {235,0,0,PC_NOCOLLAPSE};
 PALETTEENTRY green = {0,255,0,PC_NOCOLLAPSE};
 PALETTEENTRY black = {0,0,0,PC_NOCOLLAPSE};
 temp.color_index = 250;
 temp.on_color = red;
 temp.off_color = black;
 temp.on_time = 300;
 temp.off_time = 300;
 
 int red_id = Blink_Colors(BLINKER_ADD,&temp,0);
 temp.color_index = 251;
 temp.on_color = green;
 temp.off_color = black;
 temp.on_time = 600;
 temp.off_time = 600;
 
 int green_id = Blink_Colors(BLINKER_ADD,&temp,0);
然后在Game_Main()中使用 Blink_Colors(BLINKER_RUN, NULL, 0);
 

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页