はじめに
D言語では、標準ライブラリPhobosの他に、DUBパッケージで提供されるライブラリを利用可能です。
活用することで、開発できるアプリケーションの幅が広がります。
今回は、DUB
パッケージで提供されているSDL
、FreeImage
を使った実装例を示したいと思います。
SDLとは
SDLは、クロスプラットフォームのマルチメディアライブラリです。グラフィックの描画やサウンドの再生などのAPIを提供しています。Windows、macOS、Linux、iOS、Androidを公式にサポートします。 (Wikipediaより)
インタフェース部はPerl、Python、Ruby、Javaなどのプログラミング言語にも移植されているそうですが、D言語でもbindbc-sdlとして、利用可能です。
FreeImageとは
FreeImageは、一般的なグラフィック画像形式をサポートしたい開発者向けのオープンソースライブラリプロジェクトです。
PNG
、JPEG
、WebP
、GIF
、BMP
、JPEG 2000
、TIFF
など、様々な画像形式に対応しています。
BMP files [reading, writing]
Dr. Halo CUT files [reading] *
DDS files [reading]
EXR files [reading, writing]
Raw Fax G3 files [reading]
GIF files [reading, writing]
HDR files [reading, writing]
ICO files [reading, writing]
IFF files [reading]
JBIG files [reading, writing] **
JNG files [reading, writing]
JPEG/JIF files [reading, writing]
JPEG-2000 File Format [reading, writing]
JPEG-2000 codestream [reading, writing]
JPEG-XR files [reading, writing]
KOALA files [reading]
Kodak PhotoCD files [reading]
MNG files [reading]
PCX files [reading]
PBM/PGM/PPM files [reading, writing]
PFM files [reading, writing]
PNG files [reading, writing]
Macintosh PICT files [reading]
Photoshop PSD files [reading]
RAW camera files [reading]
Sun RAS files [reading]
SGI files [reading]
TARGA files [reading, writing]
TIFF files [reading, writing]
WBMP files [reading, writing]
WebP files [reading, writing]
XBM files [reading]
XPM files [reading, writing]
- only grayscale
** only via external plugin, might require a commercial license
D言語では、bindbc-freeimageとして、利用可能です。
ソースコード
私の場合、開発用フォルダD:\Dev
にFIViewer
というフォルダ、さらにその下にsource
フォルダを作成します。
FIViewer
フォルダにdub.json
、FIViewer\source
フォルダにapp.d
を配置します。
app.d
は、空行、コメント行を除くと、98行で実装できました。
import std.conv;
import std.stdio;
import std.string;
import std.windows.charset;
import bindbc.freeimage;
import bindbc.sdl;
int main(string[] args)
{
// Argument check
if ( args.length < 2 ){
writeln("Argument required : filename");
return ( 1 );
}
string filename = to!string(toMBSz(args[1]));
// Load SDL
SDLSupport ret = loadSDL();
if ( ret != sdlSupport ){
if ( ret == SDLSupport.noLibrary ){
writeln("SDLSupport.noLibrary");
} else if ( SDLSupport.badLibrary ){
writeln("SDLSupport.badLibrary");
}
return ( 1 );
}
scope(exit) unloadSDL();
// Initialize SDL
if ( SDL_Init(SDL_INIT_VIDEO) < 0 ){
writefln("SDL could not initialize! SDL_Error: %s", SDL_GetError());
return ( 1 );
}
scope(exit) SDL_Quit();
// Load FreeImage
FISupport fis = loadFreeImage();
if ( fis != fiSupport ){
if ( fis == FISupport.noLibrary ){
writeln("FISupport.noLibrary");
} else if ( FISupport.badLibrary ){
writeln("FISupport.badLibrary");
}
return ( 1 );
}
// Load Image
FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(filename.toStringz, 0);
FIBITMAP *dib = FreeImage_Load(fif, filename.toStringz, 0);
scope(exit) FreeImage_Unload(dib);
FIMEMORY* hmem = FreeImage_OpenMemory();
FreeImage_SaveToMemory(FIF_BMP, dib, hmem, 0);
scope(exit) FreeImage_CloseMemory(hmem);
ubyte* mem;
uint bufsize;
FreeImage_AcquireMemory(hmem, &mem, &bufsize);
int orgw = FreeImage_GetWidth(dib);
int orgh = FreeImage_GetHeight(dib);
// Create window
SDL_DisplayMode dm;
SDL_GetCurrentDisplayMode(0, &dm);
int dispw = (orgw < dm.w - 50) ? orgw : dm.w - 50;
int disph = (orgh < dm.h - 50) ? orgh : dm.h - 50;
if ( (cast(double)dispw) / orgw > (cast(double)disph) / orgh ){
dispw = orgw * disph / orgh;
} else {
disph = orgh * dispw / orgw;
}
SDL_Window* window = SDL_CreateWindow("FreeImage Viewer",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dispw, disph,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if ( window == null ){
writefln("Window could not be created! SDL_Error: %s", SDL_GetError());
return ( 1 );
}
scope(exit) SDL_DestroyWindow(window);
SDL_Surface* image0 = SDL_LoadBMP_RW(SDL_RWFromMem(mem, bufsize), 1);
if ( !image0 ){
writefln("SDL_LoadBMP_RW: %s", IMG_GetError());
return ( 1 );
}
SDL_Rect imageRect = SDL_Rect(0, 0, orgw, orgh);
SDL_Rect drawRect = SDL_Rect(0, 0, dispw, disph);
SDL_Surface* image = SDL_CreateRGBSurface(0, orgw, orgh, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
scope(exit) SDL_FreeSurface(image);
SDL_BlitSurface(image0, &imageRect, image, &imageRect);
SDL_FreeSurface(image0);
// Show Image
eventloop : while ( true ){
SDL_Event e;
while ( SDL_PollEvent(&e) ){
if ( e.type == SDL_QUIT ){
break eventloop;
}
SDL_GetWindowSize(window, &dispw, &disph);
if ( (cast(double)dispw) / orgw > (cast(double)disph) / orgh ){
drawRect = SDL_Rect(0, 0, orgw * disph / orgh, disph);
} else {
drawRect = SDL_Rect(0, 0, dispw, orgh * dispw / orgw);
}
SDL_Surface* screen = SDL_GetWindowSurface(window);
SDL_FillRect(screen, null, SDL_MapRGB(screen.format, 200, 200, 200));
SDL_LowerBlitScaled(image, &imageRect, screen, &drawRect);
SDL_UpdateWindowSurface(window);
}
SDL_Delay(100);
}
return ( 0 );
}
{
"name": "fiviewer",
"description": "FreeImage Viewer",
"license": "BSL-1.0",
"dependencies": {
"bindbc-freeimage": ">=0.3.0",
"bindbc-sdl": ">=0.11.0",
},
"versions": ["FI_318","BindSDL_Image"]
}
ソースコード補足説明
SDL_
から始まる関数はSDL(SDL2.dll)
が提供する関数です。詳細は、SDL 2.0日本語リファレンスマニュアルを参照するとよいかと思います。
FreeImage_
から始まる関数はFreeImage(FreeImage.dll)
が提供する関数です。詳細は、Documentationを参照するとよいかと思います。
大まかな処理の流れとして、
- 引数があるかをチェック。引数が表示したい画像ファイル名となります。
-
SDL(SDL2.dll)
を使うためにloadSDL
、FreeImage(FreeImage.dll)
を使うためにloadFreeImage
を先に呼び出す必要があります。 -
FreeImage_
の関数を使って、ファイルの画像情報をメモリ上に保存。 - 画像ファイルと画面サイズを比較。画像が大きい場合、画面に収まるように表示サイズを計算します。(
dispw
、disph
) -
SDL_
の関数でウィンドウを作成し、画像を画面に表示。 - ウィンドウを閉じるまで、
eventloop
でループします。ウィンドウサイズを変更すると、画像表示サイズを再計算します。
コンパイル、実行
D:\Dev> cd D:\Dev\FIViewer
D:\Dev\FIViewer> dmd --version
DMD32 D Compiler v2.094.0-dirty
Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved written by Walter Bright
D:\Dev\FIViewer> dub build --build=release --arch=x86_64
Performing "release" build using D:\dmd2\windows\bin\dmd.exe for x86_64.
bindbc-loader 0.3.2: target for configuration "noBC" is up to date.
bindbc-freeimage 0.5.0: target for configuration "dynamic" is up to date.
bindbc-sdl 0.19.1: target for configuration "dynamic" is up to date.
fiviewer ~master: building configuration "application"...
Linking...
To force a rebuild of up-to-date targets, run again with --force.
D:\Dev\FIViewer> tree
フォルダー パスの一覧: ボリューム ****
ボリューム シリアル番号は ****-**** です
D:.
├─.dub
│ └─build
│ └─application-release-windows-x86_64-dmd_2094-6074A52ED719B4D88AA30E8597869525
└─source
コンパイルの結果、FIViewer
フォルダに .dub
フォルダとdub.selections.json
、fiviewer.exe
ファイルが生成されます。
FIViewer
フォルダに、SDL2.dll
、FreeImage.dll
を配置します。
SDL2.dll
は、ダウンロードページにあるSDL2-2.0.12-win32-x64.zip
から入手します。
FreeImage.dll
は、ダウンロードページにあるDownload FreeImage 3.18.0 [WIN32/WIN64]
から入手します。
D:\Dev\FIViewer> dir
ドライブ D のボリューム ラベルは **** です
ボリューム シリアル番号は ****-**** です
D:\Dev\FIViewer のディレクトリ
2020/10/24 17:49 <DIR> .
2020/10/24 17:49 <DIR> ..
2020/10/24 16:51 <DIR> .dub
2020/10/24 16:51 208 dub.json
2020/10/24 16:51 125 dub.selections.json
2020/10/24 17:49 662,528 fiviewer.exe
2020/10/24 17:18 6,942,208 FreeImage.dll
2020/10/24 17:11 1,471,488 SDL2.dll
2020/10/24 13:58 <DIR> source
D:\Dev\FIViewer> fiviewer.exe D:\Dev\dlangman.webp
こちらから、D言語くんのwebp形式の画像を入手させていただきました。
fiviewer.exe "ファイル名"
で、指定したファイルを画像表示できます。
参考情報
SDL
FreeImage
bindbc-sdl
bindbc-freeimage
D言語でビルドツールDUBを用いて便利なライブラリをより簡単に利用する
更新履歴
- 2020.10.24 初回投稿
- 2021.3.8
app.d
修正 -
SDL_LowerBlitScaled
が8ビットカラーで動作しないことへの対応(SDL_CreateRGBSurface
処理を追加)