Help us understand the problem. What is going on with this article?

D言語+SDL2で非矩形ウィンドウを作成する

More than 3 years have passed since last update.

概要

  • SDL_CreateShapedWindowSDL_SetWindowShapeで非矩形のウィンドウを作成する
    • 作成はできたが、背景の透過が一部うまくいかなかった (要調査)
  • D言語+SDL2
    • dmd2.068.0(32bit)+Dub0.9.23
    • SDL2-2.0.3
  • Windows10(64bit)でのみ確認

経緯

SDL2では非矩形(キャラクターや図形の形)のウィンドウを作成できると聞いたので、調べてみました。

最初「SDL 非矩形」でググったのですが、「SDL2.0の新しい機能」の所で紹介されているぐらいで、
情報を見つけることができませんでした。(なんでや・・)

仕方なく日本語の資料をあきらめて、英語の記事やMercurialにあるソースコードを漁ってみた結果、
SDL_CreateShapedWindowSDL_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

ランタイム

SDL公式からアーカイブを取得・展開後、上記の通りに配置する

ソースコード

こちらを参考にさせて頂きました。
「プログラムでシダを描画する」をD言語+SDL2+dubで描画する
ありがとうございました。

souce/app.d
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);
}
dub.json
{
    "name": "shapedwindow",
    "targetType": "executable",
    "description": "A non-rectunglar window test written by D language.",
    "dependencies": {
        "derelict-sdl2": "1.2.8"
    }
}

ビルド

Console
dub

結果

[before]
ss0.png

[after]
ss.png

非矩形のウィンドウをは作成できましたが、背景(緑色)の一部が透過されずに見えてしまっています。
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言語の標準ライブラリに欲しい。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした