SDLで画像のロード

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
アバター
Hiragi(GKUTH)
記事: 167
登録日時: 14年前
住所: 大阪府
連絡を取る:

SDLで画像のロード

#1

投稿記事 by Hiragi(GKUTH) » 12年前

OS:Ubuntu 12.04.2 LTS
コンパイラ gcc + ライブラリ SDL , SDL_image

最近SDLを始めて、とりあえず画像のロードと適当に移動させてみようかと思って、一度メイン関数に全部書いて動いたので
関数化してみようかと思い、とりあえず画像のロードとか座標の初期化だけを分けてみようと試みたんですが、いざやってみると画像がロードされない・・・
ヒントやアドバイスなどを教えてくれると幸いです。
関数に渡してる型が問題なのかな〜とか思いながら試行錯誤したのですが、どうにも解決しないのでここで質問することにしました。

目的:画像のロードを関数化した状態ですること

以下ソース ※たまにコメントアウトしてるのは気にしない方針で

コード:

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <stdio.h>

int CheckEvent(SDL_Event event);

//int move(SDL_Rect *scr_rect , SDL_Event event);


void Init(SDL_Surface *image,SDL_Rect *rect,SDL_Rect *scr_rect)
{
	//画像読み込み 
	image = IMG_Load("test.png");
	if(image == NULL)
	{
		printf("画像がロードできていないようです");
	} else {
		printf("ロード完了");
	}

	// 画像の矩形情報設定 
	rect->x = 0;
	rect->y = 0;
	rect->w = image->w;
	rect->h = image->h;

	// 画像配置位置情報の設定 
	scr_rect->x = 0;
	scr_rect->y = 0;
}



int main(int argc, char* argv[])
{	
	/************* 変数の宣言 ************/
	
	SDL_Surface *image,*scr;      //画像ハンドルとウィンドウ作成の変数
	SDL_Rect rect, scr_rect, fsc = {0 ,0 ,640 ,480}; //画像位置情報
	SDL_Event event;//eventk;         //イベントの監視
	int quit;                //イベント結果の返り値
	
	
	/***************ここまで**************/
	
	Init(image,&rect,&scr_rect); //初期化に画像ハンドルと位置情報を渡す
	
	SDL_Init(SDL_INIT_EVERYTHING); //SDL初期化処理
	
	scr = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); //ウィンドウ作成
	
	SDL_WM_SetCaption("ゆっくりがゆっくりうごくよ!", NULL); // タイトルを設定する
/*	
	image = IMG_Load("test.png");
	
		// 画像の矩形情報設定 
	rect.x = 0;
	rect.y = 0;
	rect.w = image->w;
	rect.h = image->h;

		// 画像配置位置情報の設定 
	scr_rect.x = 0;
	scr_rect.y = 0;
*/
	

	while(1) //とりあえず無限ループ 
	{	
//		move(&scr_rect ,eventk);
		if(scr_rect.x == 640) //いろいろ移動させる、
		{
			scr_rect.y = scr_rect.y + 1;
			scr_rect.x = 0;
		} else {
			scr_rect.x = scr_rect.x + 2;
		}
		
		if(scr_rect.y > 480) scr_rect.y = 0; //画面したの端まで行ったら一番上に戻る
		
		SDL_BlitSurface(image, &rect, scr, &scr_rect); //画像描画
		
		SDL_Delay(16.66666f); //だいたい60FPSぶんぐらい待つ
		SDL_Flip(scr); //表に出す
		SDL_FillRect(scr, &fsc, SDL_MapRGB(scr->format, 0,0,0)); //画面のクリア
		
		quit = CheckEvent(event);
		if(quit) return 0;
	}
	SDL_FreeSurface(image); //画像の開放

	SDL_Quit();    //終了処理

	return 0;
}


int CheckEvent(SDL_Event event)
{
	// すべてのイベントを処理する
	while (SDL_PollEvent(&event)) 
	{
		// QUIT イベントが発生するか、ESC キーが押されたら終了する
		if ((event.type == SDL_QUIT) ||
			(event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE))
			return 1;
	}
	return 0;
}					 
よろしくお願いします。

:追記:
エラーの表記を忘れていました、しかしエラーは出ていません、警告としてimageが初期化されていないと出ますが(てかこいつが原因だと思う

SDL.cpp:53:33: 警告: ‘image’ はこの関数内で初期化されずに使用されています [-Wuninitialized]
だいがくせい!

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: SDLで画像のロード

#2

投稿記事 by softya(ソフト屋) » 12年前

image = IMG_Load("test.png");のIMG_Loadで得た構造体ポインタを仮引数に代入してますが、単なるポインタなので元のSDL_Surface *imageには反映されません。
つまり、SDL_Surfaceのポインタを書き換えるためには、SDL_Surfaceのポインタのポインタが必要なんです。

コード:

イメージ図
SDL_Surface*を指すポインタ	SDL_Surfaceを指すポインタ	 IMG_Loadが確保した構造体
+-------------------+		+-------------------+		+-------------------+
+	SDL_Surface**	+	→	+	SDL_Surface*	+	→	+	SDL_Surface		+
+-------------------+		+-------------------+		+-------------------+
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
Hiragi(GKUTH)
記事: 167
登録日時: 14年前
住所: 大阪府
連絡を取る:

Re: SDLで画像のロード

#3

投稿記事 by Hiragi(GKUTH) » 12年前

ポインタのポインタというのは初耳ですがハンドルの変数imageのポインタのポインタとして関数Initに渡せばいいという事でしょうか、
(その記述方法がわかりません。)

ちょっと思いつきました、そのSDL_Surfaceという構造体の中にはメンバ変数があると思うのですが、そのポインタという事でしょうか?、つまり
*imageは構造体SDL_Surfaceのポインタであり、ポインタのポインタとはそのSDL_Surface構造体のメンバ変数のポインタを指すという解釈で合ってるのでしょうか?

編集:誤字修正
だいがくせい!

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: SDLで画像のロード

#4

投稿記事 by h2so5 » 12年前

ポインタのポインタを渡すコードです。

コード:

void Init(SDL_Surface **image,SDL_Rect *rect,SDL_Rect *scr_rect)
{
    //画像読み込み 
    *image = IMG_Load("test.png");
    if(*image == NULL)
    {
        printf("画像がロードできていないようです");
        return;
    } else {
        printf("ロード完了");
    }
 
    // 画像の矩形情報設定 
    rect->x = 0;
    rect->y = 0;
    rect->w = (*image)->w;
    rect->h = (*image)->h;
 
    // 画像配置位置情報の設定 
    scr_rect->x = 0;
    scr_rect->y = 0;
}

コード:

Init(&image,&rect,&scr_rect); //初期化に画像ハンドルと位置情報を渡す

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: SDLで画像のロード

#5

投稿記事 by softya(ソフト屋) » 12年前

Hiragi(GKUTH) さんが書きました:ポインタのポインタというのは初耳ですがハンドルの変数imageのポインタのポインタとして関数Initに渡せばいいという事でしょうか、
(その記述方法がわかりません。)

ちょっと思いつきました、そのSDL_Surfaceという構造体の中にはメンバ変数があると思うのですが、そのポインタという事でしょうか?、つまり
*imageは構造体SDL_Surfaceのポインタであり、ポインタのポインタとはそのSDL_Surface構造体のメンバ変数のポインタを指すという解釈で合ってるのでしょうか?
ここ最近よく話題に成っていますが、ポインタというのは変数です。構造体も変数です。
そこをよく分かっていないと、訳が分からなくなります、

IMG_Load("test.png");ではSDL_Surfaceのポインタが戻り値となります。
これは、IMG_Loadの中でメモリがmallocされているであろう事を暗示させますが、つまりSDL_Surface構造体はmallocしたメモリ空間上にあってそこに"test.png"の情報が格納されていると言うことです。IMG_Loadは、メモリ情報であるSDL_Surface構造体のポインタを返すという仕組みなっていると言うことです。

image = IMG_Load("test.png");
だとSDL_Surface *の型を持つimageポインタ変数にポインタ値が格納されます。
このimageポインタ変数が何処に属するか分かりますか?
それは、Init関数の仮引数です。
仮引数は実引数のコピーであり、関数呼び出し時にコピーされて渡されると関数の時に勉強すると思います(これを大勢の人が忘れます)。
そして寿命は、Init関数が終了するまでとなります。
なので、imageポインタ変数は仮変数なので値を何処にも残さずにInit関数が終わると消滅してします。

以上問題点の説明ですが理解出来ますか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
Hiragi(GKUTH)
記事: 167
登録日時: 14年前
住所: 大阪府
連絡を取る:

Re: SDLで画像のロード

#6

投稿記事 by Hiragi(GKUTH) » 12年前

ちょっと用事があったので返信できませんでした、明日部活もあるので今日は寝ることにします、
だいがくせい!

アバター
Hiragi(GKUTH)
記事: 167
登録日時: 14年前
住所: 大阪府
連絡を取る:

Re: SDLで画像のロード

#7

投稿記事 by Hiragi(GKUTH) » 12年前

ここ最近よく話題に成っていますが、ポインタというのは変数です。構造体も変数です。
そこをよく分かっていないと、訳が分からなくなります、

IMG_Load("test.png");ではSDL_Surfaceのポインタが戻り値となります。
これは、IMG_Loadの中でメモリがmallocされているであろう事を暗示させますが、つまりSDL_Surface構造体はmallocしたメモリ空間上にあってそこに"test.png"の情報が格納されていると言うことです。IMG_Loadは、メモリ情報であるSDL_Surface構造体のポインタを返すという仕組みなっていると言うことです。

image = IMG_Load("test.png");
だとSDL_Surface *の型を持つimageポインタ変数にポインタ値が格納されます。
このimageポインタ変数が何処に属するか分かりますか?
それは、Init関数の仮引数です。
仮引数は実引数のコピーであり、関数呼び出し時にコピーされて渡されると関数の時に勉強すると思います(これを大勢の人が忘れます)。
そして寿命は、Init関数が終了するまでとなります。
なので、imageポインタ変数は仮変数なので値を何処にも残さずにInit関数が終わると消滅してします。

以上問題点の説明ですが理解出来ますか?
ポインタは変数であることは知っています。ポインタの使い道として、
とある関数Aにポインタ変数を渡したとして、関数Aがそのポインタ変数を通常モードにして値を書きこむことで、
そのアドレスの値が直接書き換わるので
「実引数と仮引数のコピーに関係なく別の関数で宣言された変数であっても直接書き換えられる」
というふうに習いました、

で、今回のポインタ変数imageはメイン関数からコピーとして渡されたので、その変数imageにIMG_LoadがSDL_Surface構造体のポインタ値として書きこんだので、
その仮引数imageが関数Initで寿命を終えるとそのポインタ値も消えてしまう、ということでしょうか?
だいがくせい!

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: SDLで画像のロード

#8

投稿記事 by softya(ソフト屋) » 12年前

Hiragi(GKUTH) さんが書きました:「実引数と仮引数のコピーに関係なく別の関数で宣言された変数であっても直接書き換えられる」
というふうに習いました、
Hiragi(GKUTH) さんが書きました:
ポインタ値から見れる範囲と限定しないとダメです。
Hiragi(GKUTH) さんが書きました: で、今回のポインタ変数imageはメイン関数からコピーとして渡されたので、その変数imageにIMG_LoadがSDL_Surface構造体のポインタ値として書きこんだので、
その仮引数imageが関数Initで寿命を終えるとそのポインタ値も消えてしまう、ということでしょうか?
だいたいそんな感じですが、少し気になるので補足します。

実引数から仮引数にポインタ変数が渡されたのでは無く、ポインタ変数のポインタ値がコピーされたのです(ここ重要)。
これを値渡しと呼びます。基本的にC言語には値渡しの機能しかありません。

元々のmainのSDL_Surface *imageの値は未初期化で不定ですよね。
これがInit()関数の仮引数にコピーされてきています。この状態だとポインタ値は不定です。
それを上書きしてるのがimage = IMG_Load("test.png");で仮引数のSDL_Surface *imageに代入されます。
これでimageにポインタ値は代入されましたが、代入されたのは仮引数のimageであってmainのimageとは別の変数です。
2つの関係は関数呼び出し時に値のコピーがされたと言うだけです。

それで、Init()が終了するときに仮引数のimageは消滅します。元のmainのimageとは別の変数ですから消滅で影響は与えませんが、仮引数のimageの値も反映されません。
なので、元のmainのimageのポインタ値は不定なままです。

【コラム】 C言語における参照渡し。
C言語でポインタを使うと参照渡しが出来ると書いているWEBページは多いかと思います。
これは、ポインタが指している変数を書き換えできるという意味であってポインタ変数そのものを書き換えできるという意味ではありません。
ポインタは変数でありポインタ値を保存しているものです。仮引数と実引数で受け渡されるのは値だけでありポインタ変数も例外ではありません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: SDLで画像のロード

#9

投稿記事 by h2so5 » 12年前

苦Cのポインタ変数モード・通常変数モードという用語はちょっと気になりますね。
ポインタがアドレス値以外の状態を持っているかのような言い回しなので。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: SDLで画像のロード

#10

投稿記事 by softya(ソフト屋) » 12年前

h2so5 さんが書きました:苦Cのポインタ変数モード・通常変数モードという用語はちょっと気になりますね。
ポインタがアドレス値以外の状態を持っているかのような言い回しなので。
確かにはじめて見ました。
苦C以外も見て自分で情報を補完して欲しいところですね。

カーニハンのプログラミング言語Cでは「*は間接演算子すなわち逆参照演算子である。これをポインタに適用するとポインタの指すオブジェクトにアクセスできる」とは書いてありますが特別な名称はないですね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: SDLで画像のロード

#11

投稿記事 by ISLe » 12年前

ポインタ変数自体に、『ポインタ変数モード』『通常変数モード』なんてものはありませんね。
苦Cの解説は滅茶苦茶なところがたくさんありますが、これは最悪にひどい。

int a, *p;
という宣言があって

pがポインタ変数モードでしょうか。
*pが通常変数モードですかね。

だとしたら
&aはポインタ変数モード(但し左辺値にはならない)
aは通常変数モード
ということになりませんかね。

けっきょくポインタ型の変数を別のものと刷り込むパターンなのですが。
softyaさんのおっしゃるとおりの演算子の機能を、変数の機能とすり替えてもいるし。

アバター
Hiragi(GKUTH)
記事: 167
登録日時: 14年前
住所: 大阪府
連絡を取る:

Re: SDLで画像のロード

#12

投稿記事 by Hiragi(GKUTH) » 12年前

ようはmain関数のimageとInit関数のimageは別物になっている、という事ですね、(変数じゃなくて変数の値を仮引数(別の変数)にコピーしている
一応目的が達成できた上、仕組みもだいたい理解できたので一旦ここで解決とさせて頂きます。

softyaさん、他にも記述方法を教えてくれたカロさん、苦Cの解説が滅茶苦茶というのを教えてくれたlSLeさん(苦Cって結構正確なサイトだと思ってた
ありがとうございました、苦C以外にもいろんな本籍やサイトで勉強してみようと思います。

追記:
解決後のソース

コード:

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <stdio.h>

int CheckEvent(SDL_Event event , SDL_Rect *scr_rect , int* push);

void Init(SDL_Surface **scr,SDL_Surface **image,SDL_Rect *rect,SDL_Rect *scr_rect)
{
	SDL_Init(SDL_INIT_EVERYTHING); //SDL初期化処理
	
	SDL_WM_SetCaption("ゆっくりがゆっくりうごくよ!", NULL); // タイトルを設定する
	
	*scr = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); //ウィンドウ作成
	
	//画像読み込み 
	*image = IMG_Load("test.png");
	if(*image == NULL)
	{
		printf("画像がロードできていないようです");
	} else {
		printf("ロード完了");
	}

	// 画像の矩形情報設定 /
	rect->x = 0;
	rect->y = 0;
	rect->w = (*image)->w;
	rect->h = (*image)->h;

	// 画像配置位置情報の設定 /
	scr_rect->x = 0;
	scr_rect->y = 0;
}



int main(int argc, char* argv[])
{	
	/************* 変数の宣言 ************/
	
	SDL_Surface *image,*scr;      //画像ハンドル
	SDL_Rect rect, scr_rect, fsc = {0 ,0 ,640 ,480}; //画像位置情報
	SDL_Event event;//eventk;         //イベントの監視
	int quit;                //イベント結果の返り値
	int push = 0;
	
	
	/***************ここまで**************/
	
	Init(&scr,&image,&rect,&scr_rect); //初期化に画像ハンドルと位置情報を渡す
	

	while(1) //とりあえず無限ループ 
	{	

		SDL_BlitSurface(image, &rect, scr, &scr_rect); //画像描画
		
		
		SDL_Delay(16.66666f); //だいたい60FPSぶんぐらい待つ
		SDL_Flip(scr); //表に出す
		SDL_FillRect(scr, &fsc, SDL_MapRGB(scr->format, 0,0,0)); //画面のクリア
		
		quit = CheckEvent(event,&scr_rect,&push);
		if(quit) return 0;
	}
	SDL_FreeSurface(image); //画像の開放

	SDL_Quit();    //終了処理

	return 0;
}


int CheckEvent(SDL_Event event,SDL_Rect *scr_rect,int *push)
{
	*push = 1;
	// すべてのイベントを処理する
	while (SDL_PollEvent(&event)) 
	{
		// QUIT イベントが発生するか、ESC キーが押されたら終了する
		if ((event.type == SDL_QUIT) ||
			(event.type == SDL_KEYUP && event.key.keysym.sym == SDLK_ESCAPE))
			return 1;
			
		if(event.type == SDL_KEYDOWN) *push = 1;
		
		if(event.type == SDL_KEYUP) *push = 0;
		
		if(*push == 1)
		{
			switch(event.key.keysym.sym)
			{
				case SDLK_UP:
					scr_rect->y--;
					break;
				case SDLK_DOWN:
					scr_rect->y++;
					break;
				case SDLK_RIGHT:
					scr_rect->x++;
					break;
				case SDLK_LEFT:
					scr_rect->x--;
					break;
			}
		}
		printf("now X = %d ,Y = %d \n",scr_rect->x,scr_rect->y);
			
		
	}
	return 0;
}

だいがくせい!

アバター
h2so5
副管理人
記事: 2212
登録日時: 14年前
住所: 東京
連絡を取る:

Re: SDLで画像のロード

#13

投稿記事 by h2so5 » 12年前

画像のロードに失敗した場合に27, 28行目でNULLポインタに対して間接参照してしまうので対策したほうが良いです。
NULLポインタにアロー演算子を使用した場合の動作は未定義です。

アバター
Hiragi(GKUTH)
記事: 167
登録日時: 14年前
住所: 大阪府
連絡を取る:

Re: SDLで画像のロード

#14

投稿記事 by Hiragi(GKUTH) » 12年前

h2so5 さんが書きました:画像のロードに失敗した場合に27, 28行目でNULLポインタに対して間接参照してしまうので対策したほうが良いです。
NULLポインタにアロー演算子を使用した場合の動作は未定義です。
imageがNULLだったら代入をしない、という事なら

コード:

	//画像読み込み 
	*image = IMG_Load("test.png");
	if(*image == NULL)
	{
		printf("画像がロードできていないようです。");
	} else {
		printf("ロード完了");
		rect->x = 0;
		rect->y = 0;
		rect->w = (*image)->w;
		rect->h = (*image)->h;
		
		scr_rect->x = 0;
		scr_rect->y = 0;
	}
こういうことでいいでしょうか? 画像がロードされていない場合座標を決定する必要が無いと思うので全てifの中にしましたが。
だいがくせい!

閉鎖

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