...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

DFL: Timerのサンプルコード

Timerクラスのサンプルコードです。

Startボタンでカウントアップ開始、Stopボタンで停止します。

import dfl;
import std.conv;

version(Have_dfl) // For DUB.
{
}
else
{
    pragma(lib, "dfl.lib");
}

class MainForm : Form
{
    private Label _label;
    private Timer _timer;
    private Button _start;
    private Button _stop;
    private uint _count;

    public this()
    {
        this.text = "Timer example";
        this.size = Size(300, 200);

        _label = new Label;
        _label.location = Point(100, 0);
        _label.font = new Font("Verdana", 50f);
        _label.autoSize = true;
        _label.text = to!string(_count);
        _label.parent = this;

        _timer = new Timer;
        _timer.interval = 1000; // 1秒周期に設定
        _timer.tick ~= (Timer t, EventArgs e) {
            _label.text = to!string(_count);
            _count++;
        };

        _start = new Button;
        _start.text = "Start";
        _start.location = Point(10, 10);
        _start.click ~= (Control c, EventArgs e) {
            _timer.start();
        };
        _start.parent = this;

        _stop = new Button;
        _stop.text = "Stop";
        _stop.location = Point(10, 50);
        _stop.click ~= (Control c, EventArgs e) {
            _timer.stop();
        };
        _stop.parent = this;
    }
}

static this()
{
    Application.enableVisualStyles();
}

void main()
{
    Application.run(new MainForm());
}

DFL: NotifyIconのサンプルコード

NotifyIconのサンプルコードです。

NotifyIconというのは、タスクバーに出るアイコンです。

上のスクリーンショットで右端にあるフェイスマークを表示しています。

import dfl;

class MainForm : Form
{
    private NotifyIcon _notifyIcon;

    public this()
    {
        this.text = "NotifyIcon example";
        this.size = Size(300, 200);
        
        MenuItem menuItem1 = new MenuItem("Show");
        menuItem1.click ~= (MenuItem mi, EventArgs e) { msgBox("Hi!"); };

        MenuItem menuItem2 = new MenuItem("Close");
        menuItem2.click ~= (MenuItem mi, EventArgs e) { this.close(); };
        
        _notifyIcon = new NotifyIcon;
        _notifyIcon.icon = new Icon(r".\image\icon.ico");
        _notifyIcon.text = "This is tooltip text";
        _notifyIcon.contextMenu = new ContextMenu;
        _notifyIcon.contextMenu.menuItems.add(menuItem1);
        _notifyIcon.contextMenu.menuItems.add(menuItem2);
        _notifyIcon.show();
    }
}

static this()
{
    Application.enableVisualStyles();
}

void main()
{
    Application.run(new MainForm());
}

DFLのダウンロード

github.com

HBITMAP(DDB)からBITMAPINFO(DIB)を得る

GetDIBits()を使う、以上。

で終わる話なのですが、BITMAPINFOを作成するために必要なメモリサイズを算出するのに手こずったので、結果だけ置いておきます。

なお、このコードで作成されるのは、スクリーンショットを撮ったときに得られるビットマップデータに合わせてbiCompressionがBI_BITFIELDSで、カラーマスクあり、カラーパレットなしの32ビットカラーDIBビットマップです。

BITMAPINFO* createBitmapInfo(HBITMAP hBitmap)
{
    BITMAP bitmap;
    GetObject(hBitmap, BITMAP.sizeof, &bitmap);
    HDC hdc = GetDC(null);

    // Allocates memory of BITMAPINFO
    const uint bitsPerPixel = bitmap.bmPlanes * 32; // 32 bits color
    const uint colorMaskBytes = {
        // Contains color mask when biCompression is BI_BITFIELDS.
        return 4 * 3; // 4 bytes * 3 masks(R,G,B)
    }();
    const uint numPallet = 0; // Wants that no color palette.
    const uint widthBytes = (bitmap.bmWidth * bitsPerPixel + 31) / 32 * 4;
    const uint bitmapBodySize = widthBytes * bitmap.bmHeight;

    BITMAPINFO* pbi = cast(BITMAPINFO*)new ubyte[
        BITMAPINFOHEADER.sizeof + colorMaskBytes + RGBQUAD.sizeof * numPallet + bitmapBodySize];

    pbi.bmiHeader.biSize = BITMAPINFOHEADER.sizeof; // First 6 members
    pbi.bmiHeader.biWidth = bitmap.bmWidth;
    pbi.bmiHeader.biHeight = bitmap.bmHeight;
    pbi.bmiHeader.biPlanes = bitmap.bmPlanes;
    pbi.bmiHeader.biBitCount = 32; // 32 bits color
    pbi.bmiHeader.biCompression = BI_BITFIELDS; // Contains color mask
    if (0 == core.sys.windows.wingdi.GetDIBits(
        hdc, hBitmap, 0, bitmap.bmHeight,
        cast(ubyte*)pbi + BITMAPINFOHEADER.sizeof + colorMaskBytes + RGBQUAD.sizeof * numPallet, pbi, DIB_RGB_COLORS))
    {
        throw new Exception("createBitmapInfo failure");
    }

    ReleaseDC(null, hdc);
    return pbi;
}

BITMAPINFO(DIB)からHBITMAP(DDB)を得る

CreateDIBitmap()を使う、以上。

で終わる話ではあるのですが、パックされたビットマップの場合、BITMAPINFOの先頭アドレスとBITMAPINFOHEADERの内容から、後に続くカラーマスクとカラーパレットのバイト数を算出して、ピクセルデータ列の先頭アドレスを求める必要があり、これがなかなか大変だったので、結果だけ置いておきます。

HBITMAP createHBitmap(BITMAPINFO* pbi)
{
    const uint bitsPerPixel = pbi.bmiHeader.biPlanes * pbi.bmiHeader.biBitCount;
    const uint colorMaskBytes = {
        if (pbi.bmiHeader.biCompression == BI_BITFIELDS)
            return 4 * 3; // 4 bytes * 3 masks(R,G,B)
        else
            return 0;
    }();
    const uint numPallet = {
        if (bitsPerPixel <= 8) // 1, 4, 8 bits color
        {
            if (pbi.bmiHeader.biClrUsed == 0)
                return 2 ^^ bitsPerPixel;
            else
                return pbi.bmiHeader.biClrUsed;
        }
        else if (bitsPerPixel <= 32) // 16, 24, 32 bits color
            return pbi.bmiHeader.biClrUsed;
        else
            throw new Exception("Illegal color bits bitmap");
    }();
    HDC hdc = GetDC(null);
    HBITMAP hBitmap = CreateDIBitmap(
        hdc, &pbi.bmiHeader, CBM_INIT,
        cast(ubyte*)pbi + BITMAPINFOHEADER.sizeof + colorMaskBytes + RGBQUAD.sizeof * numPallet, pbi, DIB_RGB_COLORS);
    ReleaseDC(null, hdc);
    return hBitmap;
}

BITMAPINFOから画像データ全体のバイナリ列を得る

かなり手こずったので結果だけ置いておきます。

ubyte[] getBitmapBuffer(BITMAPINFO* pbi)
{
    assert(pbi);
    const uint bitsPerPixel = pbi.bmiHeader.biPlanes * pbi.bmiHeader.biBitCount;
    const uint colorMaskBytes = {
        if (pbi.bmiHeader.biCompression == BI_BITFIELDS)
            return 4 * 3; // 4 bytes * 3 masks(R,G,B)
        else
            return 0;
    }();
    const uint numPallet = {
        if (bitsPerPixel <= 8) // 1, 4, 8 bits color
        {
            if (pbi.bmiHeader.biClrUsed == 0)
                return 2 ^^ bitsPerPixel;
            else
                return pbi.bmiHeader.biClrUsed;
        }
        else if (bitsPerPixel <= 32) // 16, 24, 32 bits color
            return pbi.bmiHeader.biClrUsed;
        else
            throw new Exception("Illegal color bits bitmap");
    }();
    const uint widthBytes = (pbi.bmiHeader.biWidth * bitsPerPixel + 31) / 32 * 4;
    const uint pixelBufSize = widthBytes * pbi.bmiHeader.biHeight;
    return (cast(ubyte*)pbi)[0 .. BITMAPINFOHEADER.sizeof + colorMaskBytes + RGBQUAD.sizeof * numPallet + pixelBufSize];
}

DFL: PictureBoxのサンプルコード

画像ファイルを簡単に表示することができるPictureBoxクラスのサンプルです。

Bitmapクラスで読み込める形式の画像ファイルなら表示できるようで、bmp、jpgは表示できました。 未確認ながらgifも可能かもしれませんが、pngはダメっぽいです。

WinFormsにあるPictureBoxSizeMode.ZOOMが不足していたので、ついでに追加しています。

上段左上から右に向かって1,2,3,下段左から右に向かって4,5とすると、以下の表示モードになります。

  • 1=NORMAL
  • 2=STRECH_IMAGE
  • 3=ZOOM
  • 4=CENTER_IMAGE
  • 5=AUTO_SIZE

DFLのダウンロード

github.com

参考

DFL: ClippingFormのサンプルコード

透過する部分を白色にしたビットマップを与えて、矩形でないウィンドウを作れるClippingFormのサンプルコードです。

ウィンドウタイトル(キャプション)がなく、かつ、常に最前面に表示されるウィンドウが作成されるようになっています。

閉じるボタンがないので、サンプルコードでは、クリックしたら終了するようにしてあります。 また、ドラッグしてウィンドウの位置を変更できるようにするには、自分でその処理を書く必要があります。

import dfl;

class MainForm : ClippingForm
{
    public this()
    {
        this.text = "Clipping Form example";
        this.size = Size(300, 200);
        this.clipping = new Bitmap(r".\image\clipping.bmp"); // 白色が透過される
        this.click ~= (Control c, EventArgs e) { // クリックしたら終了する
            this.close();
        };
    }
}

static this()
{
    Application.enableVisualStyles();
}

void main()
{
    Application.run(new MainForm());
}

DFLのダウンロード

github.com