DXライブラリで選択範囲を囲む4隅の座標を知りたい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Tangeθ

DXライブラリで選択範囲を囲む4隅の座標を知りたい

#1

投稿記事 by Tangeθ » 11年前

DXライブラリでペイントソフトを作っています。ペイントソフトでよくある自動選択機能は出来たのですが、今度はそれを変形するために、その選択範囲を囲む4隅の座標を取得するプログラムが書けずに悩んでいます。

文章で書くと分かりづらいので、図で解説します。
http://s1.gazo.cc/up/95035.png
画像
白が、透過しています。黒い円が選択範囲です。知りたいのはpnt1~4の座標です。なお、pnt1~pnt4を直線でつなぐと、長方形になります。

ピクセルシェーダにプログラム内の変数を渡して座標を取得しようとしたのですが、SetPSConstF関数で設定する変数は代入することはできないという趣旨のエラーメッセージが出ました。(後述)
何か、座標を取得する良いアイデアはありますでしょうか?

環境
Windows7 64bit
VisualStdio C++2008

以下、失敗したfxファイルです(cfPointOneとcfPointTwoは、pnt1~4の座標です。現在のテクスチャ座標が透過していないなら、pnt1~4の座標を変更しています)

コード:

// ピクセルシェーダーの入力
struct PS_INPUT
{
    float4 DiffuseColor       : COLOR0 ;
    float4 SpecularColor      : COLOR1 ;
    float2 TextureCoord0      : TEXCOORD0 ;//現在参照しているテクスチャの座標が入っています(xとyそれぞれ0.0f~1.0fの値)
    float2 TextureCoord1      : TEXCOORD1 ;
} ;

// ピクセルシェーダーの出力
struct PS_OUTPUT
{
    float4 Output             : COLOR0 ;
} ;

// 描画するテクスチャ
sampler Texture : register( s0 ) ;
// 選択点4隅の座標
float4 cfPointOne : register( c0 ) ;
float4 cfPointTwo : register( c1 ) ;

PS_OUTPUT main( PS_INPUT PSInput )  //入力値
{
    PS_OUTPUT PSOutput ;
    float4 TextureColor;
    TextureColor = tex2D( Texture , PSInput.TextureCoord0 );

    if(TextureColor.a>0.0f )
    {
         if(cfPointOne.x > PSInput.TextureCoord0.x)
	 {
		cfPointOne.x=PSInput.TextureCoord0.x;
		cfPointTwo.x=PSInput.TextureCoord0.x;
	 }
	 if(cfPointOne.y > PSInput.TextureCoord0.y)
	 {
		cfPointOne.y=PSInput.TextureCoord0.y;
		cfPointOne.w=PSInput.TextureCoord0.y;
	 }
	 if(cfPointOne.z < PSInput.TextureCoord0.x)
	 {
		cfPointOne.z=PSInput.TextureCoord0.x;
		cfPointTwo.z=PSInput.TextureCoord0.x;
	 }
	 if(cfPointTwo.y < PSInput.TextureCoord0.y)
	 {
		cfPointTwo.y=PSInput.TextureCoord0.y;
		cfPointTwo.w=PSInput.TextureCoord0.y;
	 }
	cfPointOne.x=p4One.x;
    }

    PSOutput.Output = TextureColor;
    return PSOutput;
}

シェーダコンパイル時のエラーメッセージ

コード:

C:\Users\ユーザー名\Desktop\setFourPnt.fx(32,15): error X3025: global variables are implicitly constant, enable compatibility mode to allow modification
(Excite翻訳。。。グローバル変数は暗黙に一定で、互換モードが修正を許可することを可能にする。)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#2

投稿記事 by みけCAT » 11年前

入力と計算の変数を分けてはいかがですか?

コード:

// 前略

// 選択点4隅の座標
float4 cfPointOne_input : register( c0 ) ;
float4 cfPointTwo_input : register( c1 ) ;

PS_OUTPUT main( PS_INPUT PSInput )  //入力値
{
    PS_OUTPUT PSOutput ;
    float4 cfPointOne = cfPointOne_input;
    float4 cfPointTwo = cfPointTwo_input;
    float4 TextureColor;
    TextureColor = tex2D( Texture , PSInput.TextureCoord0 );
 
    if(TextureColor.a>0.0f )
    {
        if(cfPointOne.x > PSInput.TextureCoord0.x)
        {
            cfPointOne.x=PSInput.TextureCoord0.x;
            cfPointTwo.x=PSInput.TextureCoord0.x;
        }

// 後略
コンパイルしていないので、もしかしたら間違っているかもしれません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Tangeθ

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#3

投稿記事 by Tangeθ » 11年前

>>みけCATさん
返信ありがとうございます。
たしかに、計算することは出来ますが、実際のcppファイル内にこの計算結果を持ってくることは可能でしょうか?

また、この計算の後に、
cfPointOne_input=cfPointOne;
と記述してコンパイルしてもやはり同じエラーが出てしまいました。

そして、コンパイル時のbatファイル内の記述も
ShaderCompiler.exe /Tps_2_0 setFourPnt.fx
から
ShaderCompiler.exe /Tps_3_0 setFourPnt.fx
に直してもやはりエラーは直りませんでした。

追伸
cfPointOne.x=p4One.x;
とプログラムに記載してありますが、これは削除し忘れのミスでした。ないものとして扱って下さい。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#4

投稿記事 by みけCAT » 11年前

ピクセルシェーダを使うこと自体間違っているかもしれません。
Tangeθ さんが書きました:ペイントソフトでよくある自動選択機能は出来たのですが
これはどのように実装しましたか?
自動選択を行うときに、各座標の最大値/最小値を保存しておき、選択範囲のデータとともに持っておくといいかもしれません。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Tangeθ

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#5

投稿記事 by Tangeθ » 11年前

>>みけCATさん
返信ありがとうございます。
仰るとおり、ピクセルシェーダ以外のアプローチも必要だと感じています。

さて、自動選択の実装方法なのですが、DXライブラリのPaint関数を使いました。自動選択するところを白に。それ以外を黒に。その後、黒をピクセルシェーダで透過度MAXにしました。Paint関数が自動で塗りづぶしているので、各座標ごとの情報は分かりません。
Paint関数の中身がどうなっているか分かれば内部変数を取得できるのかもしれないですが・・・(たしか2,3年前に、DXライブラリの掲示板にDXLibのソースを管理人様がアップしていたような気はしますが)

Tangeθ

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#6

投稿記事 by Tangeθ » 11年前

↓のページの一番下からDXライブラリのソースをダウンロードしました。

http://homepage2.nifty.com/natupaji/DxLib/dxdload.html

どうやら、これを元に改良すれば良いようです。
Paint関数に関係している所は、

コード:

//DxLib.h
extern	int			Paint(			int x, int y, int FillColor, int BoundaryColor = -1 ) ;													// 指定点から境界色があるところまで塗りつぶす(境界色を -1 にすると指定点の色の領域を塗りつぶす)

//DxGateway.cpp
extern int Paint( int x, int y, int FillColor, int BoundaryColor )
~略~

//DxStatic.h
extern	int NS_Paint(			int x, int y, int FillColor, int BoundaryColor = -1 ) ;
と
#define NS_Paint										Paint

//DxGraphics.cpp
extern int NS_Paint( int x, int y, int FillColor, int BoundaryColor )
~略~

//DxMemImg.h
extern	void	PaintMemImg( MEMIMG *DestImg, int x, int y, int FillColor, int BoundaryColor ) ;

//DxMemImgDrawFunction3.cpp
extern void PaintMemImg( MEMIMG *DestImg, int x, int y, int FillColor, int BoundaryColor )
{
	return ;
}
と
extern void PaintMemImg( MEMIMG *DestImg, int x, int y, int FillColor, int BoundaryColor )
~略~

のようです。
このうち、改良する必要がある場所はPaint関数≒NS_Paint関数内で呼ばれているPaintMemImg関数及び、その他もろもろのようです(座標のアドレスを引数に追加することなど)。

一応、私が改造やっている内に力尽きる可能性大+この質問が未解決になる可能性も大きいため、中間の進捗としてここに書いておきます。
何か、解決法、アイデア、アドバイスする点などありましたら、引き続きお願いいたします。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#7

投稿記事 by みけCAT » 11年前

ピクセルシェーダーを使いたいくらい巨大な画像なのですか?
画像(画面)が4096x4096程度までの大きさなら、
1. 画面サイズを取得
2. そのサイズのソフトイメージを作成
3. GetDrawScreenSoftImage関数(DxLib.h参照)で画面のデータをソフトイメージに転送
4. ソフトウェアで1ピクセルずつソフトイメージの色を確認し、選択範囲ならその座標が最大値/最小値か確認し、更新する
という手順で、長くて1秒程度で処理できるかもしれません。(未テストです)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#8

投稿記事 by みけCAT » 11年前

Tangeθ さんが書きました:白が、透過しています。黒い円が選択範囲です。
Tangeθ さんが書きました:

コード:

    if(TextureColor.a>0.0f )
Tangeθ さんが書きました:自動選択するところを白に。それ以外を黒に。その後、黒をピクセルシェーダで透過度MAXにしました。
仕様が錯綜しているようなので確認したいのですが、
・選択する部分は白ですか?黒ですか?その他ですか?
・選択する部分の透明度は0ですか?0より大きいですか?


【追記】よく見たら大丈夫でした。
・白黒→最初の図の色は説明用であり、実装とは関係ないと解釈した
・透明度→「透過度」がMAXだとaは0であり、矛盾していない
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#9

投稿記事 by みけCAT » 11年前

テストを行いました。結果はLog.txtに出力されます。

コード:

#include "DxLib.h"

struct minmax_t {
	int minx,miny,maxx,maxy;
};

minmax_t getSelectionMinMax(int graph) {
	int prevDrawScreen=GetDrawScreen();
	int gx,gy;
	int softImage;
	minmax_t res;
	// 画像データを取得する
	SetDrawScreen(graph);
	GetGraphSize(graph,&gx,&gy);
	softImage=MakeARGB8ColorSoftImage(gx,gy);
	GetDrawScreenSoftImage(0,0,gx,gy,softImage);
	// 不透明な部分の座標の最大値/最小値を取得する
	res.minx=gx;
	res.miny=gy;
	res.maxx=-1;
	res.maxy=-1;
#if 0
	// APIを使ったアクセス(比較的安全そう)
	for(int y=0;y<gy;y++) {
		for(int x=0;x<gx;x++) {
			int a,r,g,b;
			GetPixelSoftImage_Unsafe_ARGB8(softImage,x,y,&r,&g,&b,&a);
			if(a>0) {
				if(x<res.minx)res.minx=x;
				if(x>res.maxx)res.maxx=x;
				if(y<res.miny)res.miny=y;
				if(y>res.maxy)res.maxy=y;
			}
		}
	}
#else
	// メモリへの直接アクセス(比較的速そう)
	unsigned char* imageData=(unsigned char*)GetImageAddressSoftImage(softImage);
	int imagePitch=GetPitchSoftImage(softImage);
	for(int y=0;y<gy;y++) {
		for(int x=0;x<gx;x++) {
			if(imageData[x*4+3]>0) {
				if(x<res.minx)res.minx=x;
				if(x>res.maxx)res.maxx=x;
				if(y<res.miny)res.miny=y;
				if(y>res.maxy)res.maxy=y;
			}
		}
		imageData+=imagePitch;
	}
#endif
	// 画像データを開放する
	DeleteSoftImage(softImage);
	SetDrawScreen(prevDrawScreen);
	return res;
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
	if( ChangeWindowMode(TRUE) != DX_CHANGESCREEN_OK || DxLib_Init() == -1 ) return -1; //初期化処理

	// テスト用画像の生成
	int graph=MakeScreen(4096,4096,TRUE);
	SetDrawScreen(graph);
	DrawCircle(1564,2043,1051,GetColor(255,255,255),TRUE);

	SetDrawScreen(DX_SCREEN_FRONT);
	ErrorLogAdd("-----------test information-----------\n");
	const int iterationNum=10;
	LONGLONG totalElapsedTime=0;
	for(int i=0;i<iterationNum;i++) {
		LONGLONG startTime,elapsedTime;
		minmax_t minmax;
		// 最大値・最小値を取得する
		startTime=GetNowHiPerformanceCount();
		minmax=getSelectionMinMax(graph);
		elapsedTime=GetNowHiPerformanceCount()-startTime;
		ErrorLogFmtAdd("try %3d : %.6fs (%d,%d)-(%d,%d)",i,(double)elapsedTime/1e6,
			minmax.minx,minmax.miny,minmax.maxx,minmax.maxy);
		totalElapsedTime+=elapsedTime;
	}
	ErrorLogFmtAdd("average elapsed time: %.8fs",(double)totalElapsedTime/iterationNum/1e6);
	ErrorLogAdd("-----------test information end-----------\n");

	DxLib_End();
	return 0;
}
自分の環境で4096x4096の画像を入力したとき、API版が約0.76秒、メモリアクセス版が約0.60秒でした。

Windows Vista Home Premium SP2 32ビット
Intel(R) Core(TM)2Duo T8100 @2.10GHz 2.10GHz
RAM 4.00GB
DXライブラリ 3.11f
gcc 4.8.1 最適化: O2

厳密には、最初の1回の実行が他の実行より遅いという結果になりました。
API版

コード:

2817:try   0 : 1.102175s (513,992)-(2616,3094)
3576:try   1 : 0.736897s (513,992)-(2616,3094)
4315:try   2 : 0.735543s (513,992)-(2616,3094)
5036:try   3 : 0.716730s (513,992)-(2616,3094)
5753:try   4 : 0.713080s (513,992)-(2616,3094)
6485:try   5 : 0.728473s (513,992)-(2616,3094)
7216:try   6 : 0.726424s (513,992)-(2616,3094)
7931:try   7 : 0.710477s (513,992)-(2616,3094)
8663:try   8 : 0.728976s (513,992)-(2616,3094)
9395:try   9 : 0.727994s (513,992)-(2616,3094)
9400:average elapsed time: 0.76267689s
メモリアクセス版

コード:

2648:try   0 : 0.874176s (513,992)-(2616,3094)
3218:try   1 : 0.566053s (513,992)-(2616,3094)
3803:try   2 : 0.581408s (513,992)-(2616,3094)
4370:try   3 : 0.562619s (513,992)-(2616,3094)
4947:try   4 : 0.573127s (513,992)-(2616,3094)
5512:try   5 : 0.560519s (513,992)-(2616,3094)
6091:try   6 : 0.575705s (513,992)-(2616,3094)
6668:try   7 : 0.572978s (513,992)-(2616,3094)
7235:try   8 : 0.563046s (513,992)-(2616,3094)
7812:try   9 : 0.572819s (513,992)-(2616,3094)
7816:average elapsed time: 0.60024500s
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

Tangeθ

Re: DXライブラリで選択範囲を囲む4隅の座標を知りたい

#10

投稿記事 by Tangeθ » 11年前

>>みけCATさん
返信ありがとうございます。お陰で無事解決できました。

>>ピクセルシェーダーを使いたいくらい巨大な画像なのですか?
いいえ。各々のパソコンに使われているスクリーンサイズ*2を最大のサイズとする予定です。
よって今の所は、2500*2000くらいが最高値です。

ちなみに私のコードで(↓)、640*480のサイズの実行にかかる時間をGetNowCount()関数で取得したところ、10ミリ秒。
4000*4000のサイズですと500~600ミリ秒、4096*4096ですと、約1000ミリ秒でした。
急に高くなりましたが、4095*4095までは、↑と同じ500~600ミリ秒前後でした。

DirectXなために、テクスチャサイズは2をかけるごとのサイズ(128,256など)以上になると処理が倍倍に増えていくのでしょう。

スペックは、
DXライブラリ 3.12a
プロセッサ:Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz 2.30GHz
メモリ:4.00GB
です。

一応、コードも載せておきますが、2回目以降にかかる時間も変わらなかったため、みけCATさんのプログラムの方が優れています。

コード:

		int px[4],py[4];
		// 想定される最低値-1を代入して初期化していきます
		px[0]=SCREEN_W+1;py[0]=SCREEN_H+1;
		px[1]=-1;py[1]=SCREEN_H+1;
		px[2]=SCREEN_W+1;py[2]=-1;
		px[3]=-1;py[3]=-1;
		int softImage=MakeARGB8ColorSoftImage( SCREEN_W,SCREEN_H );
// 計ったのは、ここから
		GetDrawScreenSoftImage(0,0,SCREEN_W,SCREEN_H,softImage);
		int r,g,b,a;
		for(int i=0;i<SCREEN_W;i++)
		{
			for(int j=0;j<SCREEN_H;j++)
			{
				GetPixelSoftImage( softImage, i, j, &r, &g, &b, &a ) ;
				if(a==0)continue;
				else
				{
					if(px[0]>i)
					{
						px[0]=i;
						px[2]=i;
					}
					if(py[0]>j)
					{
						py[0]=j;
						py[1]=j;
					}
					if(px[1]<i)
					{
						px[1]=i;
						px[3]=i;
					}
					if(py[2]<j)
					{
						py[2]=j;
						py[3]=j;
					}
				}
			}
		}
// ここまで計りました
		DrawQuadrangle(px[0],py[0],px[1],py[1],px[3],py[3],px[2],py[2],GetColor(255,255,255),FALSE);
とても早い!ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る