[BMP]ビットマップの回転-補足-
前回 紹介したビットマップ回転の際の参考サイトにて、動作にでかい影響のある
バグを発見したので念のため。
(参考サイト自体、5年近く更新されていないようなので・・・)
基本アルゴリズムのさらなる最適化 においての誤記
①Yループ内増分を計算 の部分
誤:sin(R + PI/2) = cos(R)
正:sin(R + PI/2) = -cos(R)
②走査開始点の計算
誤:StX = ~~~ + (SrcCY<<10);
正:StX = ~~~ + (SrcCX<<10);
処理速度としては前回よりもこちらのほうが早い。
けど、線形補間はかかっていない。
線形補間の挑戦はまた今度。
- 忘れてしまった高校の数学を復習する本―高校数学ってこんなにやさしかった!?/柳谷 晃
- ¥1,470
- Amazon.co.jp
[本]SEの教科書
- SE の教科書 ~成功するSEの考え方、仕事の進め方 (技評SE新書001)/深沢 隆司
- ¥882
- Amazon.co.jp
現職ではSE+PG色の濃い仕事となりそうなので読みました。
「プロジェクト成功の鍵はコミュニケーションとマネジメント」
を主題とした内容。
客先常駐でSEもどきの仕事(プロジェクトマネージャサポート)をしていたのですが、
常駐先ではこの本に書かれているようなマネジメントはされていなかったと思います。
そのせいか知りませんが、結構ドツボにはまらされて徹夜残業当たり前の世界
だったわけですが。。。
HOW TO本ではないので具体的な手法や例といったものは少ないけど、
勉強になります。
特に5、6、8章はこれから待っている研修にも活用できそうです。
読書感想文は昔から苦手なのでレビューというものはありません。
文章表現の本もそのうち読んでみようと思う。
それにしても前職の2年半と同等の濃さを現職の研修3ヶ月が上回る予感。
[目次]
1章 SEの仕事は「人」が9割
2章 失敗の原因はコミュニケーション不足
3章 マネジメントが成否の鍵
4章 コミュニケーション重視の会議術【準備編】
5章 コミュニケーション重視の会議術【実践編】
6章 プロジェクト初期段階の仕事術
7章 成果物作成の仕事術
8章 顧客業務分析の仕事術
9章 設計・実装・テストの仕事術
10章 プロジェクト運営の仕事術
11章 業務システム開発は「伝言ゲーム」
[日記]約15時間
うみねこのなく頃に 第4話 Alliance of the golden witch
了。
疲れた。
途中眠かったけど、譲二サソが無敵すぎて泣いた。
死んだけど。
- うみねこのなく頃に 第四話(07th Expansion)
- ¥2,625
- D-STAGEヤフー店
明日からまた勉強しよ。
[BMP]ビットマップの回転
<※ 1/17追記>
下記ソースではちょっとしたバグがあります。
例えば180度回転したとき、元画像の最上端と最右ラインの
ピクセルが表示されません。かつ左と下のラインに空白ができます。
下記だと、例えば100*200の画像で180度回転させたとき、回転後画像の
(0, 0)に対応する座標が(100, 200)となるためです。
座標として、そして画素を持つデータとして有効な範囲は(99, 199)までです。
直した記事はこちら
成長の過程ということで、下記は残します。正月だったし。
1週間悩みに悩んでやっと動くところまで。
あけましておめでとうございます。
[作成環境]
・Visual Studi 2008 Standart Edition
・MFC-ダイアログベース
[基本的な流れ]
①元画像情報取得(幅、高さ、画像データ)
②回転角から新しい幅と高さを計算
③回転後の座標に対応する座標を元の画像からもってくる
④回転後のデータを元に、ビットマップ作成
⑤描画
[画像回転について]
座標(cx, cy)を中心に(x1, y1)をR(rad)回転させたときの回転後の座標(x2, y2)
x2 = (x1 - cx) * cos(-R) - (y1 - cy) * sin(-R) + cx
y2 = (x1 - cx) * sin(-R) + (y1 - cy) * cos(-R) + cy
となる。
ただし、元の画像から回転後の座標を求めると、計算過程により
一部回転後画像に穴があいてしまう。
これを解消するために、回転後の座標から、対応する元の画像の座標を
算出し、あてはめていくという手法を取る。
x1 = (x2 - cx) * cos(R) - (y2 - cy) * sin(R) + cx
y1 = (x2 - cx) * sin(R) + (y2 - cy) * cos(R) + cy
回転角からの座標計算は数学的な分野になるため、求め方の詳細については割愛。
というかわからないし、計算してみるのも面倒くさい。
画像中心を原点に回転させる場合、角度によっては元の画像よりも高さ、幅ともに
大きくなる。
画像が収まりきるように、回転後の幅と高さも計算する必要がある。
元の画像の幅(sw)と高さ(sh)をR(rad)回転した後の幅(dw)と高さ(dh)は
dw = | sw * cos(R) | + | sh * sin(R) | ( |~~| は絶対値)
dh = | sw * sin(R) | + | sh * cos(R) |
上記を元にしたコード。
private:
CSpinButtonCtrl m_spinctrl; // スピンコントロール
CBitmap m_srcBmp; // 元画像
int m_srcWidth; // 元画像幅
int m_srcHeight; // 元画像高さ
RGBQUAD *m_psrcData; // 元画像データポインタ
int m_dstWidth; // 回転後幅
int m_dstHeight; // 回転後高さ
RGBQUAD *m_pdstData; // 回転後データ
int m_spinDeg; // 回転任意角
BOOL CRotateBMPDlg::OnInitDialog()
{
・・・中略
// TODO: 初期化をここに追加します。
// スピンコントロール初期化
m_spinctrl.SetRange(0,360);
// 元画像の取得
m_srcBmp.Attach((HBITMAP)LoadImage(0,_T("C:\\sample.bmp"),IMAGE_BITMAP,0,0,LR_LOADFROMFILE));
// BITMAP構造体の取得
BITMAP srcBmpInfo;
m_srcBmp.GetBitmap(&srcBmpInfo);
// 元画像幅、高さの取得
m_srcWidth = srcBmpInfo.bmWidth;
m_srcHeight = srcBmpInfo.bmHeight;
// 元画像データの取得
m_psrcData = new RGBQUAD [ m_srcWidth * m_srcHeight];
m_srcBmp.GetBitmapBits(m_srcWidth * m_srcHeight * sizeof(RGBQUAD), m_psrcData);
// 初期画像は回転なし
RotateBMP(0);
return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}
void CRotateBMPDlg::OnPaint()
{
if (IsIconic())
{
・・・中略
}
else
{
CDialog::OnPaint();
// 回転後画像がなかったら即リターン
if(m_pdstData == NULL) return;
CDC *pDC = GetDC(); // デバイスコンテキスト
CDC dcMem; // メモリデバイスコンテキスト
dcMem.CreateCompatibleDC(pDC);
CBitmap rot; // 回転後ビットマップ
CBitmap *old; // 古いビットマップオブジェクト
// 回転後ビットマップ作成
rot.CreateCompatibleBitmap(pDC, m_dstWidth, m_dstHeight);
// 作成したビットマップに回転データを登録する
rot.SetBitmapBits(m_dstWidth * m_dstHeight * sizeof(RGBQUAD), m_pdstData);
old = dcMem.SelectObject(&rot);
// 転送処理
pDC->BitBlt(0,0,m_dstWidth,m_dstHeight,&dcMem,0,0,SRCCOPY);
dcMem.SelectObject(old);
// 終了処理
rot.DeleteObject();
dcMem.DeleteDC();
ReleaseDC(pDC);
}
}
// 回転処理
void CRotateBMPDlg::RotateBMP(int theta)
{
// deg → rad変換
double rad = (theta * M_PI / 180);
// 回転後の高さと幅取得
m_dstWidth = (int)((fabs(m_srcWidth * cos(rad)) + fabs(m_srcHeight * sin(rad))) + 0.5);
m_dstHeight = (int)((fabs(m_srcWidth * sin(rad)) + fabs(m_srcHeight * cos(rad))) + 0.5);
// 元画像、回転後画像の中心座標計算
int srcCX = m_srcWidth/2;
int srcCY = m_srcHeight/2;
int dstCX = m_dstWidth/2;
int dstCY = m_dstHeight/2;
// 回転後イメージ格納バッファ
if(m_pdstData != NULL) delete [] m_pdstData;
m_pdstData = new RGBQUAD [ m_dstWidth * m_dstHeight ];
memset(m_pdstData, 0x00, m_dstWidth * m_dstHeight * sizeof(RGBQUAD));
// sin、cos値を事前に計算(注1)
int int_sin = (int)(sin(rad) * 1024);
int int_cos = (int)(cos(rad) * 1024);
// 元画像、回転後座標
int srcX, srcY, dstX, dstY;
// 回転後イメージから元イメージの位置算出
for(dstY = 0; dstY < m_dstHeight; dstY++)
{
for(dstX = 0; dstX < m_dstWidth; dstX++)
{
// 回転後の座標から対応する元画像の位置を計算(注1)
srcX = (((dstX - dstCX)*int_cos - (dstY - dstCY)*int_sin) >> 10) + srcCX;
srcY = (((dstX - dstCX)*int_sin + (dstY - dstCY)*int_cos) >> 10) + srcCY;
if(srcX>=0 && srcX<m_srcWidth && srcY>=0 && srcY<m_srcHeight)
{
m_pdstData[dstX + dstY*m_dstWidth] = m_psrcData[srcX + srcY*m_srcWidth];
}
}
}
}
※RGBQUADについて
メモリ確保の際にRGBQUAD型で(幅×高さ)分取ることによって、
1ピクセル単位の色データを丸ごと回転後のピクセルへコピーしている。
※注1
処理を高速にするための処理。
→sin、cosの計算は処理時間が結構かかるのでループの外で事前に計算。
→浮動小数点よりも整数のほうが処理が早い。
詳しくは、下記の参考サイト参照。
また、この段階では画像の穴抜けはないが、エッジが汚い。
これを解消するために、線形補間というものがあるようだが、
上記コードでは未反映。これも下記サイトで説明されています。
<参考URL>TSUGU software atelier
- ひと目でわかるMicrosoft Visual C++ 2008 アプリケーション開発入門 (マイクロソフト公式解説書)/増田 智明
- ¥2,919
- Amazon.co.jp
[日記]2008年振り返り
宝くじを3枚買って300円当たりました。
よく頑張った。感動した。
岡山には牡蠣を食いに行くためだったようですが、一口しか食べませんでした。ムリポ。
今年はたくさんいろいろありました。
・結婚
・出産
・転職
・家購入
人生の大イベントを全て2008年に詰め込んだ感じです。
年越しは、今日購入してきた↓をやります。
- 【GAME】「うみねこのなく頃に」第四話 Alliance of the golden witch
- ¥2,625
- ペーパームーン Yahoo!店
みなさんも年の瀬にやるべきだと思う。
- 涼宮ハルヒちゃんの憂鬱 (2) (角川コミックス・エース 203-2)/ぷよ
- ¥588
- Amazon.co.jp
↑も一緒に買いました。