LoginSignup
2
0

More than 3 years have passed since last update.

D言語で作る画像ビューア

Last updated at Posted at 2020-10-24

はじめに

D言語では、標準ライブラリPhobosの他に、DUBパッケージで提供されるライブラリを利用可能です。
活用することで、開発できるアプリケーションの幅が広がります。

今回は、DUBパッケージで提供されているSDLFreeImageを使った実装例を示したいと思います。

SDLとは

SDLは、クロスプラットフォームのマルチメディアライブラリです。グラフィックの描画やサウンドの再生などのAPIを提供しています。Windows、macOS、Linux、iOS、Androidを公式にサポートします。 (Wikipediaより)
インタフェース部はPerl、Python、Ruby、Javaなどのプログラミング言語にも移植されているそうですが、D言語でもbindbc-sdlとして、利用可能です。

FreeImageとは

FreeImageは、一般的なグラフィック画像形式をサポートしたい開発者向けのオープンソースライブラリプロジェクトです。
PNGJPEGWebPGIFBMPJPEG 2000TIFFなど、様々な画像形式に対応しています。

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:\DevFIViewerというフォルダ、さらにその下にsourceフォルダを作成します。
FIViewerフォルダにdub.jsonFIViewer\sourceフォルダにapp.dを配置します。
app.dは、空行、コメント行を除くと、98行で実装できました。

app.d
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 );
}
dub.json
{
    "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)を使うためにloadSDLFreeImage(FreeImage.dll)を使うためにloadFreeImageを先に呼び出す必要があります。
  • FreeImage_の関数を使って、ファイルの画像情報をメモリ上に保存。
  • 画像ファイルと画面サイズを比較。画像が大きい場合、画面に収まるように表示サイズを計算します。(dispwdisph
  • 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.jsonfiviewer.exeファイルが生成されます。

FIViewerフォルダに、SDL2.dllFreeImage.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 "ファイル名"で、指定したファイルを画像表示できます。

無題.png

参考情報

SDL
FreeImage
bindbc-sdl
bindbc-freeimage
D言語でビルドツールDUBを用いて便利なライブラリをより簡単に利用する

更新履歴

  • 2020.10.24 初回投稿
  • 2021.3.8 app.d 修正
    • SDL_LowerBlitScaledが8ビットカラーで動作しないことへの対応(SDL_CreateRGBSurface処理を追加)
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0