回転拡大縮小ルーチン
- 作者: 須崎亮太郎,内村創,荻野友隆
- 出版社/メーカー: 秀和システム
- 発売日: 2004/12/21
- メディア: 単行本
- 購入: 2人 クリック: 201回
- この商品を含むブログ (83件) を見る
この本に回転と拡大縮小を同時に行うルーチンが書いてあったのですが、クリッピングとか中心座標とかの割り出しが省略されてたので、書き直してみました。
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) ]; }
こっちの方が速いかも。
ところで、これってスキャンラインの移動方法をいろいろと変えてやれば、自由に変形させての転送が比較的高速に出来そうな希ガス。
どっか参考になるサイトとか無いかなぁ……(;´Д`)
2009/06/27追記
ここのプログラムよりは、
- 2次元アフィン変換の高速転送(1) - melpon日記 - HaskellもC++もまともに扱えないへたれのページ
- 2次元アフィン変換の高速転送(2) - melpon日記 - HaskellもC++もまともに扱えないへたれのページ
- 2次元アフィン変換の高速転送(3) - melpon日記 - HaskellもC++もまともに扱えないへたれのページ
ここら辺のプログラムの方がより汎用的なので見ておくと良いかも。