[BMP]ビットマップの回転-修正版- | Assertion Failed!

[BMP]ビットマップの回転-修正版-

前回 の回転処理にはちょっとした不具合があったので、アルゴリズムから見直し。

(不具合内容は↑のリンク)


方針の変更として、画像中心を回転の中心とするのではなく、

回転したあとに平行移動して表示する方法に変更。

また、速度よりも精度を重視したアルゴリズムに変更。


回転のイメージ

わかりやすくするため、外周1ピクセルを赤線引っ張ってます。


Assertion Failed!-回転修正版0度 0度

Assertion Failed!-回転修正版180度 180度。外周も正しく表示

Assertion Failed!-回転修正版135度 135度。

外周が太くなっている部分は最近傍法というものが使われているため。
線形補間(バイリニア法)などを使えばもっと滑らかになるはず。まだ試せてない。


[回転部のみ修正]

void CSampleDlg::RotateBmp(int theta)
{
  // deg→rad変換
  double rad = (theta * M_PI / 180);
  
  // 元画像の情報(何らかの方法で取得してください)
  // バイト幅に関しては、「ビットマップのフォーマット 」参照
  int nSrcWidth = ***;  // ピクセル幅
  int nSrcHeight = ***;  // ピクセル高さ
  int nSrcLength = ***;  // バイト幅
  BYTE *pSrcData = ***;  // 元画像のカラーデータ


  // sin、cos値の計算(関数内では定数として扱う)
  double dsin = sin(rad);
  double dcos = cos(rad);


  // 各頂点の位置を格納
  CPoint ptTop[4] = {CPoint(0, 0),              // 左下
              CPoint(nSrcWidth - 1, 0),       // 右下
              CPoint(0, nSrcHeight - 1),     // 左上
              CPoint(nSrcWidth - 1, nSrcHeight - 1)}; // 右上


  // 回転後の各頂点の位置を計算し、回転後の頂点の中から
  // 最小となる点を求める

  double xMin = 0;
  double xMax = 0;
  double yMin = 0;
  double yMax = 0;
  for(int i = 1; i < 4; i++)
  {
    // 回転元→回転先への変換
    double topX = ptTop[i].x * dcos + ptTop[i].y * dsin;
    double topY = -ptTop[i].x * dsin + ptTop[i].y * dcos;


    xMin = min(xMin, topX);
    xMax = max(xMax, topX);
    yMin = min(yMin, topY);
    yMax = max(yMax, topY);
  }


  // 平行移動する移動量を求める
  double dtX = -xMin;
  double dtY = -yMin;


  // 回転後の幅と高さを求める。
  // (幅としての変換のため+1、四捨五入のため+0.5)

  int nDstWidth = static_cast<int>(xMax - xMin + 1.5);
  int nDtHeight = static_cast<int>(yMax - yMin + 1.5);


  // バイト幅を求める
  int nDstLength = 0;
  if((nDstWidth*3)%4 == 0) nDstLength = nDstWidth*3;
  else nDstLength = nDstWidth*3 + (4-(nDstWidth*3)%4);


  // 回転後画像のカラーデータ確保

  // 何もデータがない箇所は黒で表示するために0x00で初期化
  BYTE *pDstData = new BYTE [nDstLength * nDtHeight];
  memset(pDstData, 0x00, nDstLength * nDtHeight);
  
  // 回転処理
  for(int dstY = 0; dstY < nDtHeight; dstY++)
  {
    for(int dstX = 0; dstX < nDstWidth; dstX++)
    {
      // 回転先→回転元
      int srcX = static_cast<int>((dstX - dtX) * dcos - (dstY - dtY) * dsin + 0.5);
      int srcY = static_cast<int>((dstX - dtX) * dsin + (dstY - dtY) * dcos + 0.5);

      if((0 <= srcX) && (srcX < nSrcWidth) && (0 <= srcY) && (srcY < nSrcHeight))
      {
        memcpy(&pDstData[dstX * 3 + dstY * nDstLength],
              &pSrcData[srcX * 3 + srcY * nSrcLength],
              3);
      }
    }
  }

  // 回転後データを表示するためにメンバ変数などへ
  // コピーなどしてください。

  delete [] pDstData;
}