Surfaceflinger(3):变换

struct mat33 {
        vec3 v[3];
        inline const vec3& operator [] (int i) const { return v[i]; }
        inline vec3& operator [] (int i) { return v[i]; }
    };

    mat33               mMatrix;
    mutable uint32_t    mType;

废话不多说直接看代码,表示变换的类就包含上面两个数据,一个矩阵用一个3*3的数组表示。另一个变量mType则表示变换的类型。

看完数据结构后我们该看一下它所包含的方法
要弄清楚变换操作首先要看下Transform的无参构造函数

Transform::Transform() {
    reset();
}
void Transform::reset() {
    mType = IDENTITY;
    for(int i=0 ; i<3 ; i++) {
        vec3& v(mMatrix[i]);
        for (int j=0 ; j<3 ; j++)
            v[j] = ((i==j) ? 1.0f : 0.0f);
    }
}

无参构造函数构造了一个主对角线为1的矩阵,数学上叫做单位矩阵,单位矩阵的特性就是点乘一个向量,不会使这个向量产生变换,如下公式

100010001xyz=xyz

1 平移

void Transform::set(float tx, float ty)
{
    mMatrix[2][0] = tx;
    mMatrix[2][1] = ty;
    mMatrix[2][2] = 1.0f;

    if (isZero(tx) && isZero(ty)) {
        mType &= ~TRANSLATE;
    } else {
        mType |= TRANSLATE;
    }
}

这个函数是用于设置偏移,后面判断如果tx,ty不是0,则设置mType |= TRANSLATE,表示这个变换类包含位移的变换。我们来看下如何使用矩阵进行变换

100010TxTy1xy1=x+Txy+Ty1

对于二维变换,考虑到点成(0,0,0)坐标的情况,要进行齐次坐标变换,所以要变换二维向量,要使用3*3矩阵。 变换完成再进行投影转变为二维就可以了。

根据理论反推我们可以知道Transform的mMatrix所保存的矩阵是用来变换二维向量的,另外坐标mMatrix[2][0],mMatrix[2][1],mMatrix[2][2] 表示的是矩阵的第三列。由此可见矩阵Matrix[i][j] 中i表示的为列,j表示为行。
看完平移变换,它的type标志为TRANSLATE,我们再看下有没有其他的变换类型。

 enum type_mask {
                IDENTITY            = 0,
                TRANSLATE           = 0x1,
                ROTATE              = 0x2,
                SCALE               = 0x4,
                UNKNOWN             = 0x8
            };

在看其他变换之前我们先看一下Transform的乘法操作
,这里我们看一下如何重载乘法操作

Transform Transform::operator * (const Transform& rhs) const
{
    if (CC_LIKELY(mType == IDENTITY))
        return rhs;

    Transform r(*this);
    if (rhs.mType == IDENTITY)
        return r;

    // TODO: we could use mType to optimize the matrix multiply
    const mat33& A(mMatrix);
    const mat33& B(rhs.mMatrix);
          mat33& D(r.mMatrix);
    for (int i=0 ; i<3 ; i++) {
        const float v0 = A[0][i];
        const float v1 = A[1][i];
        const float v2 = A[2][i];
        D[0][i] = v0*B[0][0] + v1*B[0][1] + v2*B[0][2];
        D[1][i] = v0*B[1][0] + v1*B[1][1] + v2*B[1][2];
        D[2][i] = v0*B[2][0] + v1*B[2][1] + v2*B[2][2];
    }
    r.mType |= rhs.c;

    // TODO: we could recompute this value from r and rhs
    r.mType &= 0xFF;
    r.mType |= UNKNOWN_TYPE;
    return r;
}

首先对于 if (rhs.mType == IDENTITY)请情况,不会对坐标产生任何影响,所以直接返回自身。剩下的部分实现了简单的矩阵点乘,之后对mType进行设置,并清除高位,然后返回计算后的Transform。

看了上面的两个函数就可以看其他的变换了
除了平移还有旋转和缩放,这三种操作都可以通过下面函数完成

status_t Transform::set(uint32_t flags, float w, float h)
{
    if (flags & ROT_INVALID) {
        // that's not allowed!
        reset();
        return BAD_VALUE;
    }

    Transform H, V, R;
    if (flags & ROT_90) {
        // w & h are inverted when rotating by 90 degrees
        swap(w, h);
    }

    if (flags & FLIP_H) {
        H.mType = (FLIP_H << 8) | SCALE;
        H.mType |= isZero(w) ? IDENTITY : TRANSLATE;
        mat33& M(H.mMatrix);
        M[0][0] = -1;
        M[2][0] = w;
    }

    if (flags & FLIP_V) {
        V.mType = (FLIP_V << 8) | SCALE;
        V.mType |= isZero(h) ? IDENTITY : TRANSLATE;
        mat33& M(V.mMatrix);
        M[1][1] = -1;
        M[2][1] = h;
    }

    if (flags & ROT_90) {
        const float original_w = h;
        R.mType = (ROT_90 << 8) | ROTATE;
        R.mType |= isZero(original_w) ? IDENTITY : TRANSLATE;
        mat33& M(R.mMatrix);
        M[0][0] = 0;    M[1][0] =-1;    M[2][0] = original_w;
        M[0][1] = 1;    M[1][1] = 0;
    }

    *this = (R*(H*V));
    return NO_ERROR;
}

2 旋转

旋转主要通过flags表示(不需要该表位置和大小),在Android 中,支持的屏幕旋转角度有4个(注意这里是指沿坐标原点旋转):0(360),90,180,270。为了实现这四个角度的旋转,又引入了两个操作FLIP_H横向翻转和FLIP_V纵向翻转,这二者的含义都不难理解,就是沿y轴旋转180度和沿着x旋转180度。所以我们可以通过沿坐标轴旋转实现沿着原点旋转。沿着坐标轴旋转被表示成如下四种模式:

ROT_90
R0T_0
ROT_180 = FLIP_H|FLIP_V,  
ROT_270 = ROT_180|ROT_90,

回到前面的set函数,如果设置了旋转90度,可能是真的旋转90度也可能是旋转270度,因为ROT_270 = ROT_180|ROT_90,所以先将hw对调(这里的w,h是用于调整屏幕不要跑到坐标为负值的地方)

(1)然后如果包含FLIP_H操作,则设置变换矩阵为

100010w01

变换过程如下

100010w01xy1=x+wy1

(2)FLIP_V操作,则设置变换矩阵为

1000100h1

变换过程如下

1000100h1xy1=xy+h1

(3)旋转90度

变换过程如下

010000w01xy1=y+wx1

上拉w防止跑到负轴。

这样就可以完成变换了,另外读者可能比较疑惑的地方,横轴或者纵轴翻转的时候为什么设置了V.mType = (FLIP_V << 8) | SCALE,仔细想一下,翻转的时候相当于x,y都发生的变化,类似是一种缩放操作

3 对Region进行变换操作

Region Transform::transform(const Region& reg) const
{
Region out;
if (CC_UNLIKELY(type() > TRANSLATE)) {
if (CC_LIKELY(preserveRects())) {
Region::const_iterator it = reg.begin();
Region::const_iterator const end = reg.end();
while (it != end) {
out.orSelf(transform(*it++));
}
} else {
out.set(transform(reg.bounds()));
}
} else {
int xpos = floorf(tx() + 0.5f);
int ypos = floorf(ty() + 0.5f);
out = reg.translate(xpos, ypos);
}
return out;
}

type()函数是用于根据矩阵推算出变换的,由于太晚了,这里就不分析了,上面的函数比较简单,发现如果只有TRANSLATE操作的死后直接位移x,y。 如果有其他变换操作则利用上述矩阵,对每个Region中的Rect的四个点进行矩阵点乘。

Transform函数就分析到这里

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值