coding Shift_JIS; // 文字コードの明示(文字化け予防) // ライブラリの読み込み import GUI; // ウィンドウやボタンなどの画面部品を扱うライブラリ import Graphics; // 画像データや描画用リソースを扱うライブラリ import Graphics2D; // 2D描画を扱うライブラリ import File; // ファイルやフォルダの情報を扱うライブラリ import system.Int; // 整数に関する補助機能を提供するライブラリ // GUI関連の定数設定値 const int DEFAULT_WINDOW_WIDTH = 800; // 起動時のウィンドウ幅 const int DEFAULT_WINDOW_HEIGHT = 700; // 起動時のウィンドウ高さ const int CONTROL_AREA_HEIGHT = 60; // ウィンドウ下段の操作部分の高さ const int PLAY_BUTTON_WIDTH = 140; // 再生/停止ボタンの幅 const string PLAY_TEXT = "PLAY"; // 再生を表す、再生ボタンのラベル文字 const string STOP_TEXT = "STOP"; // 停止を表す、再生ボタンのラベル文字 const int BACKGROUND_COLOR[] = { 50, 50, 50, 255 }; // 背景色(R,G,B,α) // ファイルパス関連の定数設定値 const string DEFAULT_DIRECTORY_PATH = "./sample"; // デフォルトのフォルダ場所 const string DEFAULT_FILE_NAME = "image"; // デフォルトの画像ファイル名(番号や拡張子は除く) const string FILE_EXTENTION_SELECT_MESSAGE = "- 拡張子を選択 -"; // 拡張子の選択メッセージ const string FILE_EXTENTIONS[] = { FILE_EXTENTION_SELECT_MESSAGE, ".png", ".jpg" }; // 拡張子選択肢 // その他の定数設定値 const int DEFAULT_WAIT = 30; // 画像更新間隔のデフォルト値(ミリ秒) const int FILE_NUMBER_SEARCH_LIMIT = 1000; // 連番の開始番号を探す上限 // ファイル名・パス関連の変数(値はconfiure 関数内で設定) string directoryPath; // 画像ファイルの読み込みフォルダのパス string fileNameHead; // ファイル名(番号部分や拡張子は除いた部分) string extention; // 拡張子(ドットを含む) int fileNumberBegin; // 連番の始点番号 int fileNumberEnd; // 連番の終点番号 // GUIのレイアウト関連の変数 int windowWidth = DEFAULT_WINDOW_WIDTH; // 画像表示ウィンドウの幅(可変) int windowHeight = DEFAULT_WINDOW_HEIGHT; // 画像表示ウィンドウの高さ(可変) int screenWidth; // 画像表示スクリーンの幅(configure関数内で設定) int screenHeight; // 画像表示スクリーンの高さ(configure関数内で設定) // GUIの部品関連の変数 int screenWindow = NULL; // 画像表示ウィンドウのIDを格納 int screenImageLabel = NULL; // 画像表示ラベルのIDを格納 int screenGraphics = NULL; // スクリーン描画用グラフィックスリソースのIDを格納 int screenRenderer = NULL; // スクリーンのレンダラー(描画エンジン)のIDを格納 int playButton = NULL; // 再生/停止ボタンのIDを格納 int seekSlider = NULL; // シークバーのIDを格納 int waitField = NULL; // 画像間隔入力欄のIDを格納 int seekLabel = NULL; // シークバー左の情報表示ラベルのIDを格納 int waitLabel = NULL; // 画像間隔入力欄の左の説明ラベルのIDを格納 // 画像関連の変数 int totalNumberOfImages; // 画像ファイルの総数 string imageFilePaths[]; // 全画像ファイルのパスを格納する配列 int imageGraphicsBuffers[]; // 画像内容をバッファする配列(各画像のグラフィックスリソースのIDを格納) // 制御関連の変数 bool continuesMainLoop = true; // メインループの継続/脱出を制御(falseにすると脱出して終了) bool windowSizeChanged = false; // ウィンドウがリサイズされたら true にし、再レイアウト処理を要求する bool playing = true; // 再生中は true、停止中は false になる bool bufferingEnabled; // 画像バッファリングの有効/無効を制御(trueで有効、configure関数内で設定) // ================================================== // 全体的な処理 - この関数は起動時に自動で実行されます // ================================================== void main() { // ユーザー入力による設定処理を実行 configure(); // 描画関連の初期化処理 initializeGraphicsResources(); // 画面(GUI)関連の初期化処理 initializeGUI(); // 画面のレイアウト処理を実行 updateLayout(); // 画像表示ウィンドウが立ち上がったら、コンソールは邪魔なので非表示にする hide(); // 同じ画像のくり返し描画をスキップするため、前回描画した画像を控えておく変数 int lastImageIndex = -1; // メインループ: 1周ごとに画像更新と描画を行う while (continuesMainLoop) { // 描画する画像のインデックスを、シークバーの位置から取得 int imageIndex = getComponentInt(seekSlider); // スクリーンに画像を描画(アニメーション停止時等、描画対象画像が変わらない場合はスキップ) if (imageIndex != lastImageIndex) { drawScreen(imageIndex); } // ウィンドウがリサイズされた場合の処理 if (windowSizeChanged) { // この変数はリサイズ通知のため onWindowResize 関数内でtrueになる updateLayout(); // GUIの再レイアウト drawScreen(imageIndex); // スクリーンに画像を描画 windowSizeChanged = false; // リサイズ通知変数をリセット } // 描画した画像のインデックスを控える lastImageIndex = imageIndex; // 更新間隔に指定されている時間だけ、処理を止めて待つ(アニメーションウェイト) int wait = getWaitValue(); sleep(wait); // アニメーション再生時に、時間を進める処理 if (playing) { // 画像インデックスを1つ進め、終端に達したら始点に戻す imageIndex++; if (imageIndex == totalNumberOfImages) { imageIndex = 0; } // シークバーの位置を、進めた画像インデックスに合わせる setComponentInt(seekSlider, imageIndex); } } // メインループを脱出したらプログラム終了 exit(); } // ================================================== // ユーザー入力による設定処理を行う関数 // ================================================== void configure() { // 画像の読み込みフォルダの選択(スキップすると sample フォルダを使用) directoryPath = DEFAULT_DIRECTORY_PATH; if (confirm("画像の読み込みフォルダを指定しますか?", "(「 いいえ 」を選択すると 「 " + DEFAULT_DIRECTORY_PATH + " 」 フォルダを使用)")) { directoryPath = choose("画像の読み込みフォルダを選択", "."); } println("画像読み込みフォルダ = " + directoryPath); // 画像ファイル名(番号部分や拡張子は除く)の入力 fileNameHead = input("画像ファイル名(番号部分や拡張子は除く)を入力", DEFAULT_FILE_NAME); println("画像読ファイル名 = " + fileNameHead); // 拡張子の選択 extention = select(FILE_EXTENTIONS); while (extention == FILE_EXTENTION_SELECT_MESSAGE) { extention = select(FILE_EXTENTIONS); } println("拡張子 = " + extention); // フォルダ内の画像ファイルを検索し、連番の始点を fileNumberBegin、終点を fileNumberEnd に設定 scanFileIndex(); // 画像バッファリングの有効/無効を選択 bufferingEnabled = confirm( "画像をバッファリングしますか?", "バッファリングすると、アニメーションがスムーズになります。", "古いPCなどで、画像読み込みのくり返しによるHDDの負担を抑えたい場合にも有効です。", "半面、それなりのメモリー容量を必要とするため、環境によってはメモリーが不足します。" ); } // ================================================== // 画像ファイルを検索、連番の始点と終点を判定する関数 // ================================================== void scanFileIndex() { println("ファイル検索中..."); // 始点番号を検索し、fileNumberBegin に設定 fileNumberBegin = 0; while (true) { // 以下、fileNumberBegin を0から1ずつ増やしながら、 // その値を連番部分とする名前のファイルが存在するか検査し、 // 最初に存在したものを開始番号としてループ脱出 string fileName = fileNameHead + fileNumberBegin + extention; string filePath = getFilePath(fileName, directoryPath); if (exists(filePath)) { println("開始ファイル: " + filePath); break; } if(fileNumberBegin >= FILE_NUMBER_SEARCH_LIMIT) { pop("連番ファイルを" + FILE_NUMBER_SEARCH_LIMIT + "番まで探しましたが、見つかりませんでした。"); exit(); } fileNumberBegin++; } println("開始番号 = " + fileNumberBegin); // 終点番号の検索し、fileNumberEnd に設定 fileNumberEnd = fileNumberBegin; while (true) { // 以下、fileNumberEnd を fileNumberBegin から1ずつ増やしながら、 // その値を連番とする名前のファイルが存在するか検査し、 // 「最初に存在しなくなった番号-1」を終了番号としてループ脱出 string fileName = fileNameHead + fileNumberEnd + extention; string filePath = getFilePath(fileName, directoryPath); if (!exists(filePath)) { fileNumberEnd--; break; } fileNumberEnd++; } println("終了番号 = " + fileNumberEnd); } // ================================================== // 描画関連の初期化処理 // ================================================== void initializeGraphicsResources() { // 画像ファイル総数を求めて設定 totalNumberOfImages = fileNumberEnd - fileNumberBegin + 1; // その総数を要素数として、ファイルパスやバッファ配列を動的確保 alloc[totalNumberOfImages] imageFilePaths; alloc[totalNumberOfImages] imageGraphicsBuffers; // ファイルパスは配列の各要素に、パスを求めて格納 for (int i=0; i= 10000 && wait != lastWait) { if( !confirm("更新間隔に大きい値(10秒以上)が指定されています。本当にこの値にしますか?") ) { waitText = (string)DEFAULT_WAIT; setComponentText(waitField, waitText); wait = DEFAULT_WAIT; } } // 今回警告した際の値を控え、次回この値と同じなら警告表示はスキップする lastWait = wait; return wait; } // ================================================== // 画面上のGUI部品等のレイアウト処理を行う関数 // ================================================== void updateLayout() { // ウィンドウ内側のサイズを取得し、スクリーンのサイズを求めて設定 int windowInnnerSize[] = getComponentSize(screenWindow, INNER); screenWidth = windowInnnerSize[0]; screenHeight = windowInnnerSize[1] - CONTROL_AREA_HEIGHT; // 更新したスクリーンサイズを、レンダラーと画像表示ラベルに再設定 setGraphics2DSize(screenRenderer, screenWidth, screenHeight); setComponentSize(screenImageLabel, screenWidth, screenHeight); // 以下、ウィンドウ下段の操作部のレイアウト // 再生/実行ボタン setComponentSize(playButton, PLAY_BUTTON_WIDTH, CONTROL_AREA_HEIGHT); setComponentLocation(playButton, 0, screenHeight); // シークバー setComponentSize(seekSlider, screenWidth - PLAY_BUTTON_WIDTH - 180, 20); setComponentLocation(seekSlider, PLAY_BUTTON_WIDTH + 160, screenHeight + 10); // 更新間隔入力欄 setComponentSize(waitField, 100, 20); setComponentLocation(waitField, PLAY_BUTTON_WIDTH + 160, screenHeight + 30); // シークバーの左の情報表示ラベル setComponentSize(seekLabel, 140, 20); setComponentLocation(seekLabel, PLAY_BUTTON_WIDTH + 20, screenHeight + 10); // 更新間隔入力欄の左の説明ラベル setComponentSize(waitLabel, 140, 20); setComponentLocation(waitLabel, PLAY_BUTTON_WIDTH + 20, screenHeight + 30); // ラベルの再描画 paintComponent(seekLabel); paintComponent(waitLabel); } // ================================================== // スクリーンに画像を描画する関数 // ================================================== void drawScreen(int i) { // 画像ファイルの内容を保持する、グラフィックスリソースのIDを格納する変数を宣言 int imageFileGraphics; // バッファリングが有効の場合は、バッファされているグラフィックスリソースをそのまま使う if (bufferingEnabled) { imageFileGraphics = imageGraphicsBuffers[i]; // バッファリングが無効の場合は、画像ファイルをパスから読み込み、グラフィックスリソースを生成 } else { imageFileGraphics = newGraphics(imageFilePaths[i]); } // 画像の幅と高さを取得 int imageWidth = getGraphicsWidth(imageFileGraphics); int imageHeight = getGraphicsHeight(imageFileGraphics); // 画像の縦横比(アスペクト比)を求める float imageAspectRatio = (float)imageWidth / (float)imageHeight; // 縦横比を用いて、画像を画面一杯に表示するための描画幅と高さを求める int drawWidth = screenHeight * imageAspectRatio; int drawHeight = screenHeight; if (drawWidth > screenWidth) { drawWidth = screenWidth; drawHeight = screenWidth / imageAspectRatio; } // 画像の中心が画面中心になるように、画像を描画する左上頂点位置の座標を求める int drawX = screenWidth/2 - drawWidth/2; int drawY = screenHeight/2 - drawHeight/2; // 背景色で画面を塗りつぶす setDrawColor(screenRenderer, BACKGROUND_COLOR); drawRectangle(screenRenderer, 0, 0, screenWidth, screenHeight, true); // その上に画像を描画する drawImage(screenRenderer, drawX, drawY, drawWidth, drawHeight, imageFileGraphics); setComponentString(seekLabel, "画像番号=" + getComponentInt(seekSlider)); // 画像表示ラベルを再描画 paintComponent(screenImageLabel); // バッファリングが無効の場合は、この関数内でグラフィックスリソースを毎回生成するので、毎回破棄する if (!bufferingEnabled) { deleteGraphics(imageFileGraphics); } } // ================================================== // ボタンが押された際に呼ばれる関数(イベントハンドラ) // ================================================== void onButtonClick(int id) { // 再生/停止ボタン if (id == playButton) { // 再生中なら停止に、停止中なら再生にする playing = !playing; // それに合わせてボタンのラベルも変更 if (playing) { setComponentString(playButton, STOP_TEXT); } else { setComponentString(playButton, PLAY_TEXT); } } } // ================================================== // ウィンドウがリサイズされた際に呼ばれる関数(イベントハンドラ) // ================================================== void onWindowResize(int id, int width, int height) { // 画像表示ウィンドウがリサイズされた場合 if (id == screenWindow) { // リサイズ通知変数を true にして、メインループ内で再レイアウト処理を行わせる // (リサイズイベントは数ピクセルおきに連発するのでここで重い処理は行わない) windowSizeChanged = true; } } // ================================================== // ウィンドウが閉じられた際に呼ばれる関数(イベントハンドラ) // ================================================== void onWindowClose (int id) { // 画像表示ウィンドウを閉じた場合 if (id == screenWindow) { // メインループを脱出して実行を終了させる continuesMainLoop = false; } }