回転拡大縮小ルーチン

フルスクラッチによるグラフィックスプログラミング入門

フルスクラッチによるグラフィックスプログラミング入門

この本に回転と拡大縮小を同時に行うルーチンが書いてあったのですが、クリッピングとか中心座標とかの割り出しが省略されてたので、書き直してみました。

void BltTransrate(
    word* dst_ ,    // 転送先画像
    int dx_ ,       // (変形を加える前の)転送先座標(X)
    int dy_ ,       // (変形を加える前の)転送先座標(Y)
    int dp_ ,       // 転送先ピッチ
    word* src_ ,    // 転送元画像
    int sx_ ,       // 転送元座標(X)
    int sy_ ,       // 転送元座標(Y)
    int sp_ ,       // 転送元ピッチ
    int w_ ,        // 転送する幅
    int h_ ,        // 転送する高さ
    int dcx_ ,      // 転送先のクリッピング領域(X)
    int dcy_ ,      // 転送先のクリッピング領域(Y)
    int dcw_ ,      // 転送先のクリッピング領域(Width)
    int dch_ ,      // 転送先のクリッピング領域(Height)
    int zw_ ,       // 横の拡大縮小値(65536→等倍、65536*2→2倍、65536/2→半分)
    int zh_ ,       // 縦の拡大縮小値(65536→等倍、65536*2→2倍、65536/2→半分)
    int rotate_     // 回転角(1周の大きさは 1024)
)
{
    // BrewMath は自作の Math クラス
    BrewMath math = BrewMath::Get();
    if (math == null)
    {
        return;
    }

    // 横や縦が 0 になる場合は描画する必要無し
    if (zw_ == 0 || zh_ == 0)
    {
        return;
    }

    int sin, cos, sin256, cos256;
    sin = math.Sin(rotate_);
    cos = math.Cos(rotate_);
    sin256 = math.Sin(rotate_ + 256);
    cos256 = math.Cos(rotate_ + 256);

    int dx, dy, dw, dh;
    // 拡大縮小回転した、転送先の矩形の大きさを求める
    dw = (int)((abs((int64)zw_ * w_ * sin) + abs((int64)zw_ * w_ * cos)) >> 16);
    dh = (int)((abs((int64)zh_ * h_ * cos) + abs((int64)zh_ * h_ * sin)) >> 16);
    // それに伴って転送先もずらす
    dx = (dx_ << 16) + (w_ << 15) - (dw >> 1);
    dy = (dy_ << 16) + (h_ << 15) - (dh >> 1);
    dw >>= 16;
    dh >>= 16;
    dx >>= 16;
    dy >>= 16;

    // クリッピング
    int cl, cr, cu, cd;        // 左右上下のはみ出した量
    {
        cl = cr = cu = cd = 0;

        // 左
        if (dx < dcx_)
        {
            cl = dcx_ - dx;
        }
        // 右
        if (dx + dw > dcx_ + dcw_)
        {
            cr = (dx + dw) - (dcx_ + dcw_);
        }
        // 上
        if (dy < dcy_)
        {
            cu = dcy_ - dy;
        }
        // 下
        if (dy + dh > dcy_ + dch_)
        {
            cd = (dy + dh) - (dcy_ + dch_);
        }
    }

    dst_ = (word*)((byte*)dst_ + (dx + cl) * sizeof(word) + (dy + cu) * dp_);

    // 転送元スキャン用の矩形の大きさを求める
    int sw, sh;
    sw = abs(w_ * sin) + abs(w_ * cos);
    sh = abs(h_ * cos) + abs(h_ * sin);

    // スキャン開始位置
    int baseU = (int)(((int64)(-sw) * cos - (int64)(-sh) * sin) >> 17);
    int baseV = (int)(((int64)(-sw) * sin + (int64)(-sh) * cos) >> 17);

    // 拡大縮小値がマイナスの場合は baseU,baseV は逆になる
    if (zw_ < 0)
    {
        baseU = -baseU;
    }
    if (zh_ < 0)
    {
        baseV = -baseV;
    }

    // オフセットをずらす
    baseU += w_ << 15;
    baseV += h_ << 15;

    // 転送先 X座標 1pixel に対する、転送元の X,Y の移動量
    int DeltaXU = (int)(((int64)cos << 16) / zw_);
    int DeltaXV = (int)(((int64)sin << 16) / zw_);

    // 転送先 Y座標 1line に対する、転送元の X,Y の移動量
    int DeltaYU = (int)(((int64)cos256 << 16) / zh_);
    int DeltaYV = (int)(((int64)sin256 << 16) / zh_);

    src_ = (word*)((byte*)src_ + sx_ * sizeof(word) + sy_ * sp_);

    // クリッピングによる調整
    baseU += DeltaYU * cu;
    baseV += DeltaYV * cu;

    baseU += DeltaXU * cl;
    baseV += DeltaXV * cl;

    DeltaYU += DeltaXU * cl;
    DeltaYV += DeltaXV * cl;

    dh -= cu + cd;
    dw -= cl + cr;

    // 転送開始
    for (int j = 0 ; j < dh ; j++)
    {
        int bu = baseU;
        int bv = baseV;

        for (int i = 0 ; i < dw ; i++)
        {
            int x = bu >> 16;
            int y = bv >> 16;

            // if(0 <= x && x < w_ && 0 <= y && y < h_)
            if ((uint32)x < (uint32)w_ && (uint32)y < (uint32)h_)
            {
                dst_[i] = src_[y * sp_ + x];
            }
            bu += DeltaXU;
            bv += DeltaXV;
        }
        baseU += DeltaYU;
        baseV += DeltaYV;
        dst_ = (word*)((byte*)dst_ + dp_);
    }
}

拡大縮小・回転基準座標は画像の中心のみです。
zw_ と zh_ にはマイナスの値を指定することも可能で、例えば zw_ が -65536 の場合は左右反転します。


ARM の特性を考えると、イメージの転送部分は、

        for( int i = 0 ; i < dw ; i++ ){
            if( (uint32)bu < (uint32)(w_ << 16) && (uint32)bv < (uint32)(h_ << 16) ){
                dst_[ i ] = src_[ (bv >> 16) * sp_ + (bu >> 16) ];
            }

こっちの方が速いかも。



ところで、これってスキャンラインの移動方法をいろいろと変えてやれば、自由に変形させての転送が比較的高速に出来そうな希ガス
どっか参考になるサイトとか無いかなぁ……(;´Д`)