はじめに
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処理を追加)
