概要
-
SDL_CreateShapedWindow
とSDL_SetWindowShape
で非矩形のウィンドウを作成する- 作成はできたが、背景の透過が一部うまくいかなかった (要調査)
- D言語+SDL2
- dmd2.068.0(32bit)+Dub0.9.23
- SDL2-2.0.3
- Windows10(64bit)でのみ確認
経緯
SDL2では非矩形(キャラクターや図形の形)のウィンドウを作成できると聞いたので、調べてみました。
最初「SDL 非矩形」でググったのですが、「SDL2.0の新しい機能」の所で紹介されているぐらいで、
情報を見つけることができませんでした。(なんでや・・)
仕方なく日本語の資料をあきらめて、英語の記事やMercurialにあるソースコードを漁ってみた結果、
SDL_CreateShapedWindow
とSDL_SetWindowShape
という関数がそれらしいことがわかりました。
https://wiki.libsdl.org/SDL-gsoc2010_shaped_windows
https://hg.libsdl.org/SDL/file/704a0bfecf75/src/video/SDL_shape.c
https://hg.libsdl.org/SDL/file/704a0bfecf75/test/testshape.c
ちなみにSDLはC言語のライブラリですが、DerelictSDL2というバインディングでD言語からそのまま利用できます。
サンプル
プロジェクトの配置
- shapedwindow/
- image/
- background.bmp
- library/
- SDL.dll
- souce/
- app.d
- dub.json
- image/
ランタイム
SDL公式からアーカイブを取得・展開後、上記の通りに配置する
ソースコード
こちらを参考にさせて頂きました。
「プログラムでシダを描画する」をD言語+SDL2+dubで描画する
ありがとうございました。
import std.exception;
import std.string;
import std.c.string;
import derelict.sdl2.sdl;
/// SDLのロード
static this()
{
DerelictSDL2.load("./library/SDL2.dll");
}
/// メイン関数
void main()
{
// SDL初期化
enforceSdl(SDL_Init(SDL_INIT_EVERYTHING) == 0);
scope(exit) SDL_Quit();
// ウィンドウの生成
auto window = enforceSdl(SDL_CreateShapedWindow(
toStringz("SDL_CreateShapedWindow test"),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_BORDERLESS));
scope(exit) SDL_DestroyWindow(window);
// レンダラーの生成
auto renderer = enforceSdl(SDL_CreateRenderer(window, -1, 0));
scope(exit) SDL_DestroyRenderer(renderer);
// 背景画像の読み込み
auto surface = enforceSdl(SDL_LoadBMP(toStringz("./image/background.bmp"))); //<- 背景が緑色の画像
scope(exit) SDL_FreeSurface(surface);
// 透過色の指定
SDL_WindowShapeMode mode;
mode.mode = ShapeModeColorKey;
mode.parameters.colorKey = SDL_Color(0, Uint8.max, 0, Uint8.max);
auto texture = enforceSdl(SDL_CreateTextureFromSurface(renderer, surface));
scope(exit) SDL_DestroyTexture(texture);
// 非矩形ウィンドウに設定
SDL_Rect srcRect;
enforceSdl(SDL_QueryTexture(texture, null, null, &srcRect.w, &srcRect.h) == 0);
SDL_SetWindowSize(window, srcRect.w, srcRect.h);
SDL_SetWindowShape(window, surface, &mode);
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
// 描画実行
enforceSdl(SDL_RenderCopy(renderer, texture, null, null) == 0);
SDL_RenderPresent(renderer);
// イベントループ
mainLoop: for (SDL_Event event; SDL_WaitEvent(&event);)
{
switch (event.type)
{
// ウィンドウクローズで終了
case SDL_QUIT:
break mainLoop;
// ESCキー押し下げで終了
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE)
break mainLoop;
break;
default:
break;
}
}
}
/**
* SDLのエラーメッセージの取得
*
* Returns:
* SDLのエラーメッセージ。エラーが無ければnull。
*/
string getSdlMessage() @trusted
{
if (auto msg = SDL_GetError())
{
SDL_ClearError();
return msg[0 .. strlen(msg)].idup;
}
else
{
return null;
}
}
/**
* SDL関数のエラーチェック
*
* Params:
* value = SDL関数の戻り値か、エラーチェック結果。
* Returns:
* エラーが発生していなければvalue。
* Throws:
* Exception エラーが発生していた場合にスローされる。
*/
T enforceSdl(T)(T value, string file = __FILE__, size_t line = __LINE__)
{
return enforce(value, getSdlMessage(), file, line);
}
{
"name": "shapedwindow",
"targetType": "executable",
"description": "A non-rectunglar window test written by D language.",
"dependencies": {
"derelict-sdl2": "1.2.8"
}
}
ビルド
dub
結果
非矩形のウィンドウをは作成できました__が、__背景(緑色)の一部が透過されずに見えてしまっています。
textureからrendererにコピーする際に拡大縮小が起きている(?)のかもしれませんが、わかりませんでした。
また、現在(2015.8.25)SDL公式で配布されているSDL2.0.3では、SDL_SetWindowHitTest
が未実装のため、
タイトルバーのない非矩形ウィンドウは__マウスドラッグで動かせません。__
(マウスイベントとSetWindowPosition
で自前で頑張れば出来るのかもしれませんが)
次のSDL2.0.4でSDL_SetWindowHitTest
が実装されるのを待つのが無難な気がしました。
(DerelictSDL2の対応的な意味で)
とりあえず当初知りたかったことは確認できたので、良しとします。。。
感想
- DerelictやDubのおかげで、D言語からSDLを扱うのはとても簡単。
- 昔のSDLはゲーム専用という印象だったが、最近は色々できることが増えている様子。
- ファイルを扱うCUIなアプリに、簡単なGUIフロントエンドを被せて配布・・みたいなこともやってみたい。
- 個人的には、SDLが__D言語の標準ライブラリに欲しい。__