用于EagleEye3.0 规则集漏报和误报测试的示例项目,项目收集于github和gitee
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

362 lines
9.6 KiB

#include "stdafx.h"
#include "3dtransform.h"
#include "PerspectiveTransform.h"
#include "3dlib.h"
#include <float.h>
#include <malloc.h>
bool g_bInitSinCosTable = false;
#define STRIDELEN(WID,BITPIXEL) ((WID*BITPIXEL+31)/32*4)
namespace IMAGE3D
{
C3DTransform::~C3DTransform()
{
if(m_pSrcBits) free(m_pSrcBits);
}
void C3DTransform::Initialize()
{
if (false == g_bInitSinCosTable)
{
_control87(_MCW_RC, _RC_DOWN); //设置FPU的舍入模式,在Bilinear函数中需要使用汇编加快float->int
Build_Sin_Cos_Tables();
g_bInitSinCosTable = true;
}
}
// 使用3D图形算法来获取图片旋转后的四个顶点位置
void C3DTransform::GetQuadByAnimateValue(int nDegreeX, int nDegreeY, int nDegreeZ, int nZOffset, Quad* pOutQuad)
{
// .局部坐标(以矩形的中心作为局部坐标的原点)
float x = (float)m_nSrcWndWidth/2;
float y = (float)m_nSrcWndHeight/2;
POINT3D pt3dModel[4] = {
{-x, y, 0}, {x, y, 0}, {x, -y, 0}, {-x, -y, 0}
};
POINT3D pt3DWorld[4] = {0};
POINT3D pt3DCamera[4] = {0};
#pragma region // .局部坐标->世界坐标
{
// .以当前值作为角度进行旋转
float fDegreeX = (float)nDegreeX;
float fDegreeY = (float)nDegreeY;
float fDegreeZ = (float)nDegreeZ;
MATRIX_4_4 matTemp1, matTemp2;
MATRIX_4_4 matRotateY, matRotateX, matRotateZ;
MATRIX_4_4_PTR pLeftArg = NULL;
if (0 != fDegreeY)
{
MAT_IDENTITY_4_4(&matRotateY);
matRotateY.M00 = Fast_Cos(fDegreeY);
matRotateY.M02 = -Fast_Sin(fDegreeY);
matRotateY.M20 = Fast_Sin(fDegreeY);
matRotateY.M22 = Fast_Cos(fDegreeY);
pLeftArg = &matRotateY;
}
if (0 != fDegreeX)
{
MAT_IDENTITY_4_4(&matRotateX);
matRotateX.M11 = Fast_Cos(fDegreeX);
matRotateX.M12 = Fast_Sin(fDegreeX);
matRotateX.M21 = -Fast_Sin(fDegreeX);
matRotateX.M22 = Fast_Cos(fDegreeX);
if (NULL == pLeftArg)
{
pLeftArg = &matRotateX;
}
else
{
Mat_Mul_4X4(pLeftArg, &matRotateX, &matTemp1);
pLeftArg = &matTemp1;
}
}
if (0 != fDegreeZ)
{
MAT_IDENTITY_4_4(&matRotateZ);
matRotateZ.M00 = Fast_Cos(fDegreeZ);
matRotateZ.M01 = Fast_Sin(fDegreeZ);
matRotateZ.M10 = -Fast_Sin(fDegreeZ);
matRotateZ.M11 = Fast_Cos(fDegreeZ);
if (NULL == pLeftArg)
{
pLeftArg = &matRotateZ;
}
else
{
Mat_Mul_4X4(pLeftArg, &matRotateZ, &matTemp2);
pLeftArg = &matTemp2;
}
}
if (pLeftArg)
{
for (int i = 0; i < 4; i++)
Mat_Mul_VECTOR3D_4X4(&pt3dModel[i], pLeftArg, &pt3DWorld[i]);
}
else
{
for (int i = 0; i < 4; i++)
{
pt3DWorld[i].x = pt3dModel[i].x;
pt3DWorld[i].y = pt3dModel[i].y;
}
}
// .由于仍然是平移到世界坐标的 (0,0,0)位置,因此不用计算平移
for (int i = 0; i < 4; i++)
{
pt3DWorld[i].z += nZOffset;
}
}
#pragma endregion
// 相机位置(这里默认将视平面放在与矩形所在面的同一位置,这样透视出来的坐标直接就可以
// 当成屏幕坐标来用了,省了一步操作。
// 但是需要注意的是,如果nCameraPos过小的话看到的图像就会缩小,
CAMERA camerpos = {0};
float fCameraPos = 2000.0f; // 相机位置
float d = fCameraPos; // 相机与视平面的距离。将两值设成一样,避免了一次到屏幕坐标的转换
VECTOR4D_INITXYZ(&camerpos.WorldPos, 0,0, -fCameraPos);
#pragma region // 世界坐标转换为相机坐标
{
// 平移矩阵
MATRIX_4_4 matCameraTrans = {0};
MAT_IDENTITY_4_4(&matCameraTrans);
matCameraTrans.M30 = -camerpos.WorldPos.x;
matCameraTrans.M31 = -camerpos.WorldPos.y;
matCameraTrans.M32 = -camerpos.WorldPos.z;
// 相机角度为0,不旋转
for (int i = 0; i < 4; i++)
{
Mat_Mul_VECTOR3D_4X4(&pt3DWorld[i], &matCameraTrans, &pt3DCamera[i]);
}
}
#pragma endregion
#pragma region // 相机坐标转换为透视坐标
POINT3D pt3DPerspectivePos[4];
for (int i = 0; i < 4; i++)
{
float z = pt3DCamera[i].z; // 这里的z是用于和d相比的距离,不是坐标. 当d值取的比较小时,会导致z为负
float i_z = 1/z;
if (pt3DCamera[i].z != 0)
{
pt3DPerspectivePos[i].x = d * pt3DCamera[i].x * i_z; // nCameraPos相当于d
pt3DPerspectivePos[i].y = d * pt3DCamera[i].y * i_z; //
}
}
#pragma endregion
// 转换到屏幕坐标上
for (int i = 0; i < 4; ++i)
{
pt3DPerspectivePos[i].x += (m_nSrcWndWidth>>1);
pt3DPerspectivePos[i].y = -pt3DPerspectivePos[i].y;
pt3DPerspectivePos[i].y += (m_nSrcWndHeight>>1);
}
// 赋值给返回值
for (int i = 0; i < 4; i++)
{
pOutQuad->pos[2*i] = (int)pt3DPerspectivePos[i].x;
pOutQuad->pos[2*i+1] = (int)pt3DPerspectivePos[i].y;
}
}
// 将函数调用改成宏定义,因为这个函数调用太频繁了
#define GetLine(pBits, nPitch,y) \
(pBits + nPitch*(y))
void C3DTransform::Render(const PARAM3DTRANSFORM & param3d)
{
RECT rc = {0, 0, m_nSrcWndWidth, m_nSrcWndHeight};
// 计算当前的动画窗口屏幕坐标
Quad quad;
GetQuadByAnimateValue(param3d.nRotateX,param3d.nRotateY, param3d.nRotateZ, param3d.nOffsetZ, &quad);
// 用于计算矩形->任意四边形的对应关系
PerspectiveTransform perspective;
perspective.SetQuad(&quad, &rc);
perspective.ChangeToFixedPoint();
int nWidthDst = m_nSrcWndWidth-1; // (300->299),数据范围是它的大小减一
int nHeightDst = m_nSrcWndHeight-1; //
int nDstPitch = nWidthDst*4;
float fxSrc = 0;
float fySrc = 0;
float *pfxSrc = &fxSrc; // 为了在perspective_transform中使用指针
float *pfySrc = &fySrc;
LPBYTE pDstBits=m_pDstBits;
int nPixByte=m_nBitsPixel/8;
#if 1 // 在大循环之前剔除掉一些空白区域
//将目标图片清空
memset(pDstBits, 0, nDstPitch * nHeightDst);
int nMinX = max(0, min(min(min(quad.Ax,quad.Bx),quad.Cx),quad.Dx));
int nMinY = max(0, min(min(min(quad.Ay,quad.By),quad.Cy),quad.Dy));
int nMaxX = min(nWidthDst, max(max(max(quad.Ax,quad.Bx),quad.Cx),quad.Dx));
int nMaxY = min(nHeightDst, max(max(max(quad.Ay,quad.By),quad.Cy),quad.Dy));
pDstBits += (nMinY*nDstPitch);
for (int Y = nMinY; Y < nMaxY; Y++)
{
LPBYTE pDest=pDstBits;
for (int X = nMinX; X < nMaxX; X++)
{
#else
for (int X = 0, Y = 0; Y < m_nSrcWndHeight; Y++)
{
for (X = 0; X < m_nSrcWndWidth; X++)
{
#endif
perspective_transform_fp(perspective, X, Y, pfxSrc, pfySrc);
int nx = 0; //fx; // 注: 直接赋值 int i = floatvalue; 会产生一个 _ftol()调用
int ny = 0; //fy; // 导致效率降低。这里使用内嵌汇编指令。(3D游戏编程大师技巧 P918)
// 浮点数转整数。 注意:默认的fistp是四舍五入模式。需要通过调用_control87(_MCW_RC, _RC_DOWN);进行调整
#ifndef _WIN64
__asm fld fxSrc;
__asm fistp nx;
__asm fld fySrc;
__asm fistp ny;
#else
nx = int(fxSrc);
ny = int(fySrc);
#endif//WIN32
// 1. ceil的效率非常非常低!千万别在这用
// 但是有一个问题如果height为300,ySrc=299.99999999时,转成(int)得到的结果是300,
// 但在这里判断的话是成功的,导致最后崩溃,因此当ySrc>299时即退出
if (nx < 0 || nx >= nWidthDst || ny < 0 || ny >= nHeightDst)
{
continue;
}
// 目标与源图像素对应
#if 0
//////////////////////////////////////////////////////////////////////////
// 邻近取样, 速度快,但有锯齿
((DWORD*)pDstBits)[X] = ((DWORD*)(m_pSrcBits + m_nSrcPitch*(int)ySrc))[(int)xSrc];
#else
//////////////////////////////////////////////////////////////////////////
// 二次线性插值,能有效去掉锯齿,但速度非常慢
//((DWORD*)pDstBits)[X] = Bilinear(m_pSrcBits, m_nSrcPitch, nx, ny, (float)fxSrc, (float)fySrc);
//
// 二次线性插值公式推导
// http://blog.csdn.net/dakistudio/article/details/1767100 二次线性插值公式推导
//
// x为fx的向下取整,y为fy的向下取整
//
//inline DWORD& Bilinear(BYTE* pBits, const int& nPitch, const int& x, const int& y, const float& fx, const float& fy)
{
// 已将原始图片的right/bottom扩大1px,这样在获取 x+1, y+1时达到数组边缘也不会崩溃
// 取附近的四个像素的颜色值(x,y) (x+1, y) (x, y+1) (x+1, y+1)
LPBYTE pValue=GetLine(m_pSrcBits, m_nSrcPitch, ny);
pValue += nx*nPixByte;
LPBYTE p0=pValue;//(x,y);
LPBYTE p2=pValue+nPixByte;//(x+1,y)
pValue+=m_nSrcPitch;//y+1
LPBYTE p1=pValue;//(x,y+1)
LPBYTE p3=pValue+nPixByte;//(x+1,y+1)
// 将浮点数乘法转为定点数乘法。同时优化浮点数转成整数。
float u = (float)fxSrc - nx;
float v = (float)fySrc - ny;
#ifndef _WIN64
float fpm3 = FLOAT_TO_FIXP16(u*v);
float fpm2 = FLOAT_TO_FIXP16(u*(1.0f-v));
float fpm1 = FLOAT_TO_FIXP16(v*(1.0f-u));
float fpm0 = FLOAT_TO_FIXP16((1.0f-u)*(1.0f-v));
int pm3_16 = 0;
int pm2_16 = 0;
int pm1_16 = 0;
int pm0_16 = 0;
__asm
{
fld fpm3;
fistp pm3_16;
fld fpm2;
fistp pm2_16;
fld fpm1;
fistp pm1_16;
fld fpm0;
fistp pm0_16;
}
#else
int pm3_16 = int(FLOAT_TO_FIXP16(u*v));
int pm2_16 = int(FLOAT_TO_FIXP16(u*(1.0f-v)));
int pm1_16 = int(FLOAT_TO_FIXP16(v*(1.0f-u)));
int pm0_16 = int(FLOAT_TO_FIXP16((1.0f-u)*(1.0f-v)));
#endif//WIN32
pDest = pDstBits + X * nPixByte;
*pDest++=(BYTE)((pm0_16*(*p0++) + pm1_16*(*p1++) + pm2_16*(*p2++) + pm3_16*(*p3++)) >> FIXP16_SHIFT);
*pDest++=(BYTE)((pm0_16*(*p0++) + pm1_16*(*p1++) + pm2_16*(*p2++) + pm3_16*(*p3++)) >> FIXP16_SHIFT);
*pDest++=(BYTE)((pm0_16*(*p0++) + pm1_16*(*p1++) + pm2_16*(*p2++) + pm3_16*(*p3++)) >> FIXP16_SHIFT);
if(nPixByte==4) *pDest++=(BYTE)((pm0_16*(*p0++) + pm1_16*(*p1++) + pm2_16*(*p2++) + pm3_16*(*p3++)) >> FIXP16_SHIFT);
}
#endif
}
pDstBits += nDstPitch;
}
}
BOOL C3DTransform::SetImage( LPBYTE pSour,LPBYTE pDest,int nWid,int nHei,int nBitsPixel )
{
if(nBitsPixel!=24 && nBitsPixel!=32) //只支持24和32位
return FALSE;
m_nSrcWndWidth=nWid+1,m_nSrcWndHeight=nHei+1;
m_nSrcPitch=STRIDELEN(m_nSrcWndWidth,nBitsPixel);
m_pSrcBits=(LPBYTE)calloc(1,m_nSrcPitch*m_nSrcWndHeight);
m_nBitsPixel=nBitsPixel;
m_pDstBits=pDest;
LPBYTE p1=m_pSrcBits+m_nSrcPitch;//bottom-up,跳过最下面一行
LPBYTE p2=pSour;
int nLen=STRIDELEN(nWid,nBitsPixel);
for(int i=0;i<nHei;i++)
{
memcpy(p1,p2,nLen);
p1+=m_nSrcPitch;
p2+=nLen;
}
return TRUE;
}
}//end of namespace