ページ 11

DIB回転

Posted: 2012年7月03日(火) 22:24
by 勉強中
C言語習いたてです。質問の意図が不明でしたら大変申し訳ありません。
BitmapをDIB形式で読み込んで表示されることはできました。
以下のURLを参考にしました。
http://www.alpha-net.ne.jp/users2/uk413/vc/VCT_DIB.html
表示した画像を右に90度回転させたいのですが
・DWORD biWidth⇔DWORD biHeightを入れ替える
・DIB形式は左下から始まることに注意する
・領域外アクセスに注意する
上記は気にしてやっているつもりですが実行すると90度回転しますが
上のほうにゴミのようなものがついてしまいます。
なにか心当たりありますでしょうか?
情報不足で申し訳ありません。。

Re: DIB回転

Posted: 2012年7月03日(火) 22:32
by softya(ソフト屋)
縦と横は同じ大きさの画像でしょうか?

Re: DIB回転

Posted: 2012年7月03日(火) 22:36
by 勉強中
回答ありがとうございます。
サイズは異なります。
横と縦同じ大きさのBitmapの場合は正常に回転させることはできました。

Re: DIB回転

Posted: 2012年7月03日(火) 22:42
by softya(ソフト屋)
勉強中 さんが書きました:回答ありがとうございます。
サイズは異なります。
横と縦同じ大きさのBitmapの場合は正常に回転させることはできました。
その場合は、拡縮してやるとか隙間を埋めるとか結構面倒な事が必要となります。
あるいは、複数のブロックに分割するとかですね。これも面倒ですね。

Re: DIB回転

Posted: 2012年7月03日(火) 23:11
by 勉強中
ありがとうございます。
>拡縮してやるとか隙間を埋めるとか結構面倒な事が必要となります。
>あるいは、複数のブロックに分割するとかですね。
★了承しました。
大変お手数ですがなぜ本件の問題が発生するのかご教授いただけないでしょうか?
 もしくは上記の拡張、隙間埋めの具体例ございますでしょうか?参考URL等でも問題
 ございません。

Re: DIB回転

Posted: 2012年7月03日(火) 23:22
by softya(ソフト屋)
今の現状コードを貼り付けてもらえますか?
codeタグを忘れないようにお願いします。 http://dixq.net/board/board.html

Re: DIB回転

Posted: 2012年7月03日(火) 23:32
by 勉強中
申し訳ありません。貼り付けたいのですが今コードがある
PCが手元にないため貼り付けできません。
明日以降に連絡させて下さい。
明らかに情報が足りませんよね。失礼いたしました。

Re: DIB回転

Posted: 2012年7月04日(水) 02:13
by ISLe
もしかして、1ライン分のピクセルデータバイト数が4バイト境界に切り上げられるのを考慮していないとか?

Re: DIB回転

Posted: 2012年7月04日(水) 22:11
by 勉強中
回答ありがとうございます。確かに考慮していませんm(_ _)m
何故考慮しなければいけないのかと考えてしまう私の知識レベルです。

ちょっと長いですがコード添付します。
MFCで作成しております。変更を加えたViewと自分で追加したcppを添付します。
回転させているbitmapはペイントでサイズを96×35にして黄緑で塗り潰したものです。

コード:

 
//------------------------------------------------------------------------
//-----------------ControlDrawView.cpp-----------------------------
BITMAPINFOHEADER BI = {0};
BITMAPFILEHEADER Header = {0};

UINT8 *Bits = {0};
UINT8 *BackUp = {0};

LONG BIW = 0;
LONG BIH = 0;

// CControlDrawView

IMPLEMENT_DYNCREATE(CControlDrawView, CView)

BEGIN_MESSAGE_MAP(CControlDrawView, CView)
	// 標準印刷コマンド
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
	ON_COMMAND(ID_32771, &CControlDrawView::OnOpenFile)
	ON_COMMAND(ID_32772, &CControlDrawView::Change_RGB)
	ON_COMMAND(ID_32773, &CControlDrawView::OnRotate90)
END_MESSAGE_MAP()

// CControlDrawView コンストラクション/デストラクション

CControlDrawView::CControlDrawView()
{
	// TODO: 構築コードをここに追加します。

	
}

CControlDrawView::~CControlDrawView()
{




}

BOOL CControlDrawView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: この位置で CREATESTRUCT cs を修正して Window クラスまたはスタイルを
	//  修正してください。

	return CView::PreCreateWindow(cs);
}

// CControlDrawView 描画

int flag = 0;

void CControlDrawView::OnDraw(CDC* pDC)
{
	CControlDrawDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: この場所にネイティブ データ用の描画コードを追加します。

	int pointx = 100;
	int pointy = 100;


	::StretchDIBits(pDC->GetSafeHdc(),pointx, pointy, BI.biWidth,BI.biHeight, \
		0,0,BI.biWidth,BI.biHeight,Bits,(BITMAPINFO*)&BI,DIB_RGB_COLORS,SRCCOPY);

	if (flag){

		::StretchDIBits(pDC->GetSafeHdc(),pointx * 2, pointy * 2, BI.biWidth,BI.biHeight, \
		0,0,BI.biWidth,BI.biHeight,Bits,(BITMAPINFO*)&BI,DIB_RGB_COLORS,SRCCOPY);

	}


#if 0  //回転可能だがあとあと困るかも
	if (flag) { 
		for(int i = 0; i < BIW; i++){
			for(int j = 0; j < BIH; j++){
				pDC->SetPixel(pointy + j, pointx + BIW -1-i,pDC->GetPixel(i,j));
			}
		}
	}
#endif


}


// CControlDrawView 印刷

BOOL CControlDrawView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// 既定の印刷準備
	return DoPreparePrinting(pInfo);
}

void CControlDrawView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 印刷前の特別な初期化処理を追加してください。
}

void CControlDrawView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 印刷後の後処理を追加してください。
}


// CControlDrawView 診断

#ifdef _DEBUG
void CControlDrawView::AssertValid() const
{
	CView::AssertValid();
}

void CControlDrawView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CControlDrawDoc* CControlDrawView::GetDocument() const // デバッグ以外のバージョンはインラインです。
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CControlDrawDoc)));
	return (CControlDrawDoc*)m_pDocument;
}
#endif //_DEBUG


// CControlDrawView メッセージ ハンドラー

void CControlDrawView::OnOpenFile()
{
	// TODO: ここにコマンド ハンドラー コードを追加します。
	CFile Srcfile;
	CFileDialog FileDialog(TRUE);
	CString BmpFileName;
	
	if(FileDialog.DoModal() == IDOK){
		BmpFileName = FileDialog.GetPathName();
		Srcfile.Open(BmpFileName, CFile::modeRead | CFile::shareDenyNone);

		Srcfile.Read(&Header, sizeof(BITMAPFILEHEADER));
		Srcfile.Read(&BI, sizeof(BITMAPINFOHEADER));

		//ファイルサイズ分の領域を確保
		Bits = (UINT8 *)malloc(BI.biSizeImage);
		BackUp = (UINT8 *)malloc(BI.biSizeImage);

		BIW = BI.biWidth;
		BIH = BI.biHeight;

		Srcfile.Read(Bits, BIW * BIH * 3);



		Srcfile.Close();

		
		this->RedrawWindow();
		free(Bits);
		free(BackUp);

	}


}


void CControlDrawView::Change_RGB()
{
	// TODO: ここにコマンド ハンドラー コードを追加します。

	UINT8 ColorTemp;

	for (int i = 0; i < BIW * BIH * 3; i+=3)
	{
		ColorTemp = Bits[i];
		Bits[i] = Bits[i + 1];
		Bits[i + 1] = Bits[i + 2];
		Bits[i + 2] = ColorTemp;

	}

	this->RedrawWindow();
}


void CControlDrawView::OnRotate90()
{
	// TODO: ここにコマンド ハンドラー コードを追加します。
	int w, h;
	unsigned char r = 0, g = 0, b = 0;
	LONG tempsize = 0;
	CopyBits(BackUp, Bits, BIW, BIH);


#if 1

	tempsize = BI.biWidth;
	BI.biWidth = BI.biHeight;
	BI.biHeight = tempsize;

	flag = 1;

	for(w = 0; w < BI.biHeight; w++){
		for(h = 0; h < BI.biWidth; h++){
			GetRotateBits(w, h, BI.biHeight,  BI.biWidth, &r, &g, &b);
			SetBitmapBits(h, w, BI.biHeight, BI.biWidth, r, g, b);
			//Bits[BIH * 3 * i + j * 3]	= Bits[BIW * 3 * i + j * 3];
			r = 0;
			g = 0;
			b = 0;
		}
	}
#endif

	this->RedrawWindow();

}


//------------------------------------------------------------------------
//-----------------common.cpp----------------------------------------
#include "stdafx.h"
#include "math.h"

void CopyBits(UINT8 *src, UINT8 *dst, LONG Width, LONG Height)
{
	int i, j;
	for(i = 0; i < Width; i++){
		for(j = 0; j < Height; j++){
			src[Width * j * 3 + i * 3] = dst[Height * j * 3 + i * 3];
			src[Width * j * 3 + i * 3 + 1] = dst[Height * j * 3 + i * 3 + 1];
			src[Width * j * 3 + i * 3 + 2] = dst[Height * j * 3 + i * 3 + 2];
		}
	}
	double x = cos(1.00);
}


void SetBitmapBits(int x, int y, LONG Width, LONG Height, UINT8 r,UINT8 g,UINT8 b)
{
	Bits[(Height ) * y * 3 + (x  ) * 3] = b;
	Bits[(Height ) * y * 3 + (x  ) * 3 + 1] = g;
	Bits[(Height ) * y * 3 + (x  ) * 3 + 2] = r;
}

// CDrawStudyView メッセージ ハンドラ

void GetRotateBits(int x, int y, LONG Width, LONG Height, UINT8* r,UINT8* g,UINT8* b)
{

	*b = BackUp[(Width -1 - x) * 3 + y * Width  * 3];
	*g = BackUp[(Width -1 - x) * 3 + 1 + y * Width  * 3];
	*r = BackUp[(Width -1 - x) * 3 + 2 + y * Width  * 3];

}

//------------------------------------------------------------------------
//-----------------common.h------------------------------------------

#ifndef _COMMMON_H
#define _COMMMON_H

typedef unsigned char UINT8;

extern BITMAPINFOHEADER BI;
extern BITMAPFILEHEADER Header;

extern UINT8 *Bits;
extern UINT8 *BackUp;

extern void CopyBits(UINT8 *src, UINT8 *dst, LONG Width, LONG Height);
extern void SetBitmapBits(int x, int y, LONG Width, LONG Height, UINT8 r,UINT8 g,UINT8 b);
extern void GetRotateBits(int x, int y, LONG Width, LONG Height, UINT8* r,UINT8* g,UINT8* b);

#endif //_COMMMON_H


Re: DIB回転

Posted: 2012年7月04日(水) 23:17
by 勉強中
すいません165行目からのfree二つはコメントアウトでした↓↓
試しにいろんなタイミングでFreeしてたのが残ってました。
このままだと画像がおかしなことになりますm(_ _)m

Re: DIB回転

Posted: 2012年7月05日(木) 00:32
by softya(ソフト屋)
すいません。明日また検討の上で回答しますね。

Re: DIB回転

Posted: 2012年7月05日(木) 03:22
by ISLe
1ピクセルがBGRの3バイトで構成されているビットマップを前提とします。
#読み込み時にチェックしたほうが良いのでは。

サイズが96x35の場合、1ラインは96*3バイトで288バイトです。
288はちょうど4で割り切れるので、詰め物はありません。
ビットマップのイメージのサイズは288x35で10080バイトです。

サイズが35x96の場合、1ラインは35x3バイトで105バイトです。
4で割り切れないので切り上げて108バイトになります。
1ラインごとに3バイトの詰め物が入ります。
ビットマップのイメージのサイズは108x96で10368バイトになります。

縦横のサイズを入れ替えるときイメージ本体のサイズも変えなければなりません。
とうぜんピクセルデータの配置も変わります。

対応策はいくつかあります。
いずれもBITMAPINFOHEADERとイメージ本体を加工します。
  • 回転の度に作り直す
  • ビットマップファイル読み込み時に、詰め物バイトの影響を受けないサイズに調整する
    ※回転時に余白を考慮する必要あり
  • ビットマップファイル読み込み時に、1ピクセル4バイト(の倍数)のフォーマットに変換する
    ※回転時には何も考慮する必要なし

Re: DIB回転

Posted: 2012年7月05日(木) 23:15
by 勉強中
ISLe [URL] 様
ご丁寧な回答ありがとうございますm(_ _)m

35×96と96×35はイコールという意識が強かったです。
今回のケースはイコールとはなりませんね、丁寧な回答から
理解することができました。

単純な回転でもプログラムの奥深さを実感いたしました。