存档在 ‘传统像素级2D图像处理’ 分类

怎样找出精灵的边缘

2009年11月5日

在很多游戏中,将鼠标移到人物上,人物的边缘上就会出现一道光边。这也就是笔者将要和大家讨论的,如何找出这一边缘。

其实要找出它也不难,在游戏所使用的图片中,都包含有透明色和不透明色两部分,那么问题就很简单了,在透明色和不透明色相接的地方就是边缘。请看下面的程序:

程序功能:画指定区域图像的边缘在指定位置
x,y   :画边缘的坐标地址
x1,y1 :指定区域左上角的坐标
x2,y2 :指定区域右下角的坐标
TColor:透明色
var16 i,j;
uvar16 Color,TColor;
for(i=y1;i<=y2;i++)
for(j=x1;j<=x2;j++)
{
Color=GetPixel(j,i);
if(Color!=TColor) //如果这个点不是透明色
{
if(j==x1||GetPixel(j-1,i)==TColor)PutPixel(j-x1+x-1,i-y1+y  );
if(j==x2||GetPixel(j+1,i)==TColor)PutPixel(j-x1+x+1,i-y1+y  );
if(i==y1||GetPixel(j,i-1)==TColor)PutPixel(j-x1+x  ,i-y1+y-1);
if(i==y2||GetPixel(j,i+1)==TColor)PutPixel(j-x1+x  ,i-y1+y+1);
}
}

高速直线绘制程序

2009年11月5日

程序:罗健军


我特别的感谢罗健军大师,罗健军是一位水平高超的程序设计师。按我的推断,他应该有着丰富、高超的编程经验,远非在下能及。我再次向他表示感谢。

下面的算法中,var16表示有符号16位数据类型;HLine(x,y,n)是画水平线函数;VLine(x,y,n)是画垂直线函数;PutPixel(x,y)是画点函数。请看程序。

void Line(var16 x1,var16 y1,var16 x2,var16 y2)
{
 var16 p,n,x,y,tn;
 if(y1==y2)
 {
  if(x1>x2)
  {
   x=x2;x2=x1;x1=x;
  }
  HLine(x1,y1,x2-x1+1);
  return;
 }
 if(x1==x2)
 {
  if(y1>y2)
  {
   y=y2;y2=y1;y1=y;
  }
  VLine(x1,y1,y2-y1+1);
  return;
 }
 if(abs(y2-y1)<=abs(x2-x1))
 {
  if((y2<y1&&x2<x1)||(y1<=y2&&x1>x2))
  {
   x=x2;y=y2;x2=x1;y2=y1;x1=x;y1=y;
  }
  if(y2>=y1&&x2>=x1)
  {
   x=x2-x1;y=y2-y1;
   p=2*y;n=2*x-2*y;tn=x;
   while(x1<=x2)
   {
    if(tn>=0)tn-=p;
     else {tn+=n;y1++;}
    PutPixel(x1++,y1);
   }
  }
  else
  {
   x=x2-x1;y=y2-y1;
   p=-2*y;n=2*x+2*y;tn=x;
   while(x1<=x2)
   {
    if(tn>=0)tn-=p;
     else {tn+=n;y1--;}
    PutPixel(x1++,y1);
   }
  }
 }
 else
 {
  x=x1;x1=y2;y2=x;y=y1;y1=x2;x2=y;
  if((y2<y1&&x2<x1)||(y1<=y2&&x1>x2))
  {
   x=x2;y=y2;x2=x1;y2=y1;x1=x;y1=y;
  }
  if(y2>=y1&&x2>=x1)
  {
   x=x2-x1;y=y2-y1;p=2*y;n=2*x-2*y;tn=x;
   while(x1<=x2)
   {
    if(tn>=0)tn-=p;
     else {tn+=n;y1++;}
    PutPixel(y1,x1++);
   }
  }
  else
  {
   x=x2-x1;y=y2-y1;p=-2*y;n=2*x+2*y;tn=x;
   while(x1<=x2)
   {
    if(tn>=0)tn-=p;
     else {tn+=n;y1--;}
    PutPixel(y1,x1++);
   }
  }
 }
}

高速正圆绘制程序

2009年11月5日

程序:罗健军


程序中var32表示有符号32位数,var16表示有符号16位数。x0,y0为圆心坐标,r为半径。PutPixel为画点函数。

曾经长年的使用这段程序,而未向罗健军大师表示感谢,真是感到惭愧!在这里,请各位代表我向他表示感谢。

double SIN45=0.707106781186548;

void Circle(var16 x0,var16 y0,uvar16 r)
{
 var32 tn;
 var16 x,y;
 var16 xmax;
 y=r;x=0;
 xmax=var16(r*SIN45);
 tn=(1-r*2);
 while(x<=xmax)
 {
  if(tn>=0)
  {
   tn+=(6+((x-y)<<2));
   y--;
  }
  else
   tn+=((x<<2)+2);
  PutPixel(x0+y,y0+x);
  PutPixel(x0+x,y0+y);
  PutPixel(x0-x,y0+y);
  PutPixel(x0-y,y0+x);
  PutPixel(x0-y,y0-x);
  PutPixel(x0-x,y0-y);
  PutPixel(x0+x,y0-y);
  PutPixel(x0+y,y0-x);
  x++;
 }
 PutPixel(x0+y,y0+x);
 PutPixel(x0+x,y0+y);
 PutPixel(x0-x,y0+y);
 PutPixel(x0-y,y0+x);
 PutPixel(x0-y,y0-x);
 PutPixel(x0-x,y0-y);
 PutPixel(x0+x,y0-y);
 PutPixel(x0+y,y0-x);
}

高速椭圆绘制程序

2009年11月5日

程序:罗健军


程序中var16为有符号16位数字型,uvar1616位无符号数字型,uvar32为无符号32位数字型,var32为有符号32位数字型。Ellipse参数中的x0,y0为圆心坐标,r1r2分别为横半径和纵半径。PutPixel为画点函数。

void Ellipse(var16 x0,var16 y0,uvar16 r1,uvar16 r2)
{
 uvar32 r,r12,r22;
 var16 x,y,xmax;
 var32 tn;
 x=0;y=r2;
 r12=r1*r1;r22=r2*r2;
 xmax=var16(r12/sqrt(r12+r22));
 tn=r12-2*r2*r12; 
 while(x<=xmax)
 {
  if(tn<0||y==0)tn+=(4*x+2)*r22;
   else
   {
    tn+=(4*x+2)*r22+(1-y)*4*r12;
    y--;
   }   
  PutPixel(x0+x,y0+y);
  PutPixel(x0-x,y0+y);
  PutPixel(x0+x,y0-y);
  PutPixel(x0-x,y0-y);
  x++;
 }
 PutPixel(x0+x,y0+y);
 PutPixel(x0-x,y0+y);
 PutPixel(x0+x,y0-y);
 PutPixel(x0-x,y0-y);
 r=r1;r1=r2;
 r2=(uvar16)r;
 x=0;y=r2;
 r12=r1*r1;r22=r2*r2;
 xmax=var16(r12/sqrt(r12+r22));
 tn=r12-2*r2*r12; 
 while(x<=xmax)
 {
  if(tn<0||y==0)tn+=(4*x+2)*r22;
   else
   {
    tn+=(4*x+2)*r22+(1-y)*4*r12;
    y--;
   }   
  PutPixel(x0+y,y0+x);
  PutPixel(x0+y,y0-x);
  PutPixel(x0-y,y0+x);
  PutPixel(x0-y,y0-x);
  x++;
 } 
 PutPixel(x0+y,y0+x);
 PutPixel(x0+y,y0-x);
 PutPixel(x0-y,y0+x);
 PutPixel(x0-y,y0-x);
}

高速扇形绘制程序

2009年11月5日

程序:罗健军

程序中var16为有符号16位数字型,uvar16为无符号16位数字型。var32为有符号32位数字型。PutPixel为画点函数。

函数Sector入口参数x0,y0为圆心坐标,r为半径。stangle为起始角度,endangle为结束角度。

double SIN45=0.707106781186548;
uvar32 SINV[91]={0,
     17452406,   34899496,   52335956,   69756473,   87155742,  104528463,
    121869343,  139173100,  156434465,  173648177,  190808995,  207911690,
    224951054,  241921895,  258819045,  275637355,  292371704,  309016994,
    325568154,  342020143,  358367949,  374606593,  390731128,  406736643,
    422618261,  438371146,  453990499,  469471562,  484809620,  500000000,
    515038074,  529919264,  544639035,  559192903,  573576436,  587785252,
    601815023,  615661475,  629320391,  642787609,  656059028,  669130606,
    681998360,  694658370,  707106781,  719339800,  731353701,  743144825,
    754709580,  766044443,  777145961,  788010753,  798635510,  809016994,
    819152044,  829037572,  838670567,  848048096,  857167300,  866025403,
    874619707,  882947592,  891006524,  898794046,  906307787,  913545457,
    920504853,  927183854,  933580426,  939692620,  945518575,  951056516,
    956304755,  961261695,  965925826,  970295726,  974370064,  978147600,
    981627183,  984807753,  987688340,  990268068,  992546151,  994521895,
    996194698,  997564050,  998629534,  999390827,  999847695,  1000000000
};
/////////////////////////////////////////////////////////////////////////////////
// 查找表式sin函数
/////////////////////////////////////////////////////////////////////////////////
double Lsin(var16 angle)
{
 double v,f;
 if(angle>360||angle<-360)angle=angle-(angle/360)*360;
 if(angle<0)angle=360+angle;
 if(angle>180)f=-1;else f=1;
 if(angle>90&&angle<=180)angle=180-angle;
  else if(angle>180&&angle<=270)angle=angle-180;
   else if(angle>270)angle=360-angle;
 v=f*SINV[angle]/10.0e8;
 return(v);
}
/////////////////////////////////////////////////////////////////////////////////
// 查找表式cos函数
/////////////////////////////////////////////////////////////////////////////////
double Lcos(var16 angle)
{
 double v,f;
 if(angle>360||angle<-360)angle=angle-(angle/360)*360;
 if(angle<0)angle=360+angle;
 if(angle<270&&angle>90)f=-1;else f=1;
 if(angle>90&&angle<=180)angle=180-angle;
 else if(angle>180&&angle<=270)angle=angle-180;
  else if(angle>270)angle=360-angle;
 angle=90-angle;
 v=f*SINV[angle]/10.0e8;
 return(v);
}
/////////////////////////////////////////////////////////////////////////////////
// 扇形绘制函数
////////////////////////////////////////////////////////////////////////////////
void Sector(var16 x0,var16 y0,uvar16 r,uvar16 stangle,uvar16 endangle)
{
 var16 i,j;
 var16 *xy;
 var16 bx,ex,bxd,exd,bxf,exf,ben;
 var32 tn,x,y;
 var32 xmax;
 y=r; x=0;
 xmax=(var32)(r*SIN45);
 tn=(1-r*2);
 xy=(var16 *)calloc(20,sizeof(var16));
 xy[ 0]=x0+r;xy[1]=y0;
 xy[ 2]=x0;  xy[3]=y0-r;
 xy[ 4]=x0;  xy[5]=y0-r;
 xy[ 6]=x0-r;xy[7]=y0;
 xy[ 8]=x0-r;xy[9]=y0;
 xy[10]=x0;  xy[11]=y0+r;
 xy[12]=x0;  xy[13]=y0+r;
 xy[14]=x0+r;xy[15]=y0;
 bx=stangle/45;
 ex=endangle/45;
 ben=ex-bx-1;
 xy[16]=(var16)(r*Lcos(stangle));
 xy[17]=(var16)(r*Lsin(stangle));
 xy[18]=(var16)(r*Lcos(endangle));
 xy[19]=(var16)(r*Lsin(endangle));
 Line(x0+xy[16],y0-xy[17],x0,y0);
 Line(x0+xy[18],y0-xy[19],x0,y0);
 if(bx==1||bx==2||bx==5||bx==6)bxd=abs(xy[16]);else bxd=abs(xy[17]);
 if(ex==1||ex==2||ex==5||ex==6)exd=abs(xy[18]);else exd=abs(xy[19]);
 if(bx==0||bx==2||bx==4||bx==6)bxf=0;          else bxf=1;
 if(ex==0||ex==2||ex==4||ex==6)exf=1;          else exf=0;
 while(x<=xmax)
 {
  if(tn>=0)
  {
   tn+=(6+((x-y)*4));
   y--;
   xy[0]--;
   xy[3]++;
   xy[5]++;
   xy[6]++;
   xy[8]++;
   xy[11]--;
   xy[13]--;
   xy[14]--;
  }
  else tn+=((x*4)+2);
  if(stangle<endangle)
  {
   j=(bx+1)*2;
   for(i=0;i<ben;i++)
   {
    PutPixel(xy[j],xy[j+1]);
    j+=2;
   }
  }
  else if(stangle>endangle)
  {
   j=(bx+1)*2;
   for(i=bx+1;i<8;i++)
   {
    PutPixel(xy[j],xy[j+1]);
    j+=2;
   }
   j=0;
   for(i=0;i<ex;i++)
   {
    PutPixel(xy[j],xy[j+1]);
    j+=2;
   }
  }
  i=bx*2;
  if( (x>bxd)^bxf )PutPixel(xy[i],xy[i+1]);i=ex*2;
  if( (x>exd)^exf )PutPixel(xy[i],xy[i+1]);x++;
  xy[ 1]--;
  xy[ 2]++;
  xy[ 4]--;
  xy[ 7]--;
  xy[ 9]++;
  xy[10]--;
  xy[12]++;
  xy[15]++;
 }
 free(xy);
}

二维世界中的旋转

2009年11月5日

首先,在解析几何中我们可以找到以下公式:

x’=x*cos(ang)-y*sin(ang)
y’=x*sin(ang)+y*cos(ang)

这个公式是以原点(0,0)为中心,将点(x,y)旋转ang度,旋转后的坐标为(x’,y’)。不过这只是以原点为中心进行旋转的,如果我们想以任意点为中心旋转呢!公式变形如下:

x’=zx+(x-zx)*cos(ang)-(y-zy)*sin(ang)
y’=zy+(x-zx)*sin(ang)+(y-zy)*cos(ang)

好,这也就是我们想要的结果了。以(zx,zy)为中心,将点(x,y)旋转ang度,旋转后的坐标为(x’,y’)

由于在标准C/C++库中,sincos所要求的参数为弧度,而并非角度,所以我们需要一个转换。

弧度=角度*圆周率/180

按以上,最后的标准C/C++程序如下:

float x,y;           //原始点坐标
float rx,ry;         //旋转中心点坐标
float nx,ny;         //旋转后的点坐标
float ang;           //旋转角度(0-360)
float as,ac;
……

as=sin(ang*M_PI/180.0);
ac=cos(ang*M_PI/180.0);

nx=rx+((x-rx)*ac-(y-ry)*as);
ny=ry+((x-rx)*as+(y-ry)*ac);


DirectDraw7初始化

2009年11月5日

//—————————————————————————
#include <windows.h>
#include <ddraw.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#pragma hdrstop

//—————————————————————————

#pragma argsused

#define GAMENAME    “DirectDraw 7 窗口示例”
#define ScreenWidth 640
#define ScreenHigh  480
#define ScreenColor 32
#define WINDOWSTYLE WS_VISIBLE|WS_THICKFRAME|WS_SYSMENU|WS_MINIMIZEBOX

IDirectDraw         *_DirectDraw=NULL;
IDirectDraw7        *DirectDraw;
IDirectDrawSurface7 *PrimarySurface;
DDSURFACEDESC2      ddsd;

bool InitDirectDraw(HWND hwnd)
{
if(DirectDrawCreate(NULL,&_DirectDraw,NULL)!=DD_OK)return(false);
if(_DirectDraw->QueryInterface(IID_IDirectDraw7,(void **)&DirectDraw)!=DD_OK)
{
_DirectDraw->Release();
return(false);
}

if(DirectDraw->SetCooperativeLevel(hwnd,DDSCL_EXCLUSIVE|DDSCL_FULLSCREEN)!=DD_OK)
{
DirectDraw->Release();
_DirectDraw->Release();
return(false);
}

if(DirectDraw->SetDisplayMode(ScreenWidth,ScreenHigh,ScreenColor,0,0)!=DD_OK)
{
DirectDraw->Release();
_DirectDraw->Release();
return(false);
}

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

ddsd.dwSize             =sizeof(DDSURFACEDESC2);
ddsd.dwFlags            =DDSD_CAPS;
ddsd.ddsCaps.dwCaps     =DDSCAPS_PRIMARYSURFACE;

if(DirectDraw->CreateSurface(&ddsd,&PrimarySurface,NULL)!=DD_OK)
{
DirectDraw->Release();
_DirectDraw->Release();
return(false);
}

return(true);
}

void CloseDirectDraw()
{
PrimarySurface->Release();
DirectDraw->Release();
_DirectDraw->Release();
}

HWND InitWindow(HINSTANCE hinstance)
{
HWND hwnd;
WNDCLASSEX wc;

wc.cbSize=sizeof(WNDCLASSEX);
wc.style=0;
wc.lpfnWndProc=(WNDPROC)DefWindowProc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hInstance=hinstance;
wc.hIcon=LoadIcon(NULL,NULL);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_BACKGROUND;
wc.lpszMenuName=0;
wc.lpszClassName=”绝情创作群”;
wc.hIconSm=LoadIcon(hinstance,IDI_APPLICATION);

RegisterClassEx(&wc);
hwnd=CreateWindowEx(NULL,”绝情创作群”,GAMENAME,WINDOWSTYLE,0,0,ScreenWidth,ScreenHigh,0,0,hinstance,0);

ShowWindow(hwnd,SW_SHOWNORMAL);
UpdateWindow(hwnd);

return(hwnd);
}

WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;

hwnd=InitWindow(hInstance);
InitDirectDraw(hwnd);

//………….

CloseDirectDraw();
return(0);
}
//—————————————————————————

鄂ICP备09027626号