スポンサーリンク

Rust+windows-rsでDIBを使う

CreateDIBSectionを使ってDIBを作り、CreateCompatibleDCを使って描画してみる。

各関数・データ型・定数がwindows-rsのどこにあるかさえわかれば、あとはそこまで難しくはない。

  • win32dib.rs ... DIB関連の変数を一括で管理する構造体+CreateDIBSection,CreateCompatibleDCを呼び出す関数
  • main.rs ... エントリポイント
  • winmain.rs ... ウィンドウを表示して描画を行う

一番厄介なのはwinmain.rs内でmydibのインスタンスであるグローバル変数で、これはstatic + Lazy + Mutexで実現しないといけない。

win32dib.rs

use windows::Win32::Graphics::Gdi::*;
use windows::core::*;
use windows::Win32::Foundation::*;

// ■ BITMAPINFO         ... windows::Win32::Graphics::Gdi::BITMAPINFO
// ■ BITMAPINFOHEADER   ... windows::Win32::Graphics::Gdi::BITMAPINFOHEADER
// ■ BI_RGB             ... windows::Win32::Graphics::Gdi::BI_RGB
// ■ CreatedHDC         ... windows::Win32::Graphics::Gdi::CreatedHDC
// ■ CreateDCA          ... windows::Win32::Graphics::Gdi::CreateDCA
// ■ CreateCompatibleDC ... windows::Win32::Graphics::Gdi::CreateCompatibleDC
// ■ CreateDIBSection   ... windows::Win32::Graphics::Gdi::CreateDIBSection
// ■ DIB_RGB_COLORS     ... windows::Win32::Graphics::Gdi::DIB_RGB_COLORS
// ■ DeleteDC           ... windows::Win32::Graphics::Gdi::DeleteDC
// ■ HBITMAP            ... windows::Win32::Graphics::Gdi::HBITMAP
// ■ HBITMAP            ... windows::Win32::Graphics::Gdi::HBITMAP
// ■ SelectObject       ... windows::Win32::Graphics::Gdi::SelectObject

// ■ HANDLE             ... windows::Win32::Foundation::HANDLE

// ■ PCSTR              ... windows::core::PCSTR

 pub struct Win32DIBCanvas{
    pub bitmap     :HBITMAP,
    pub bitmapInfo :BITMAPINFO,
    pub ppixel     :*mut std::ffi::c_void,
    pub hMemDC     : CreatedHDC,
}

// ppixel:*mut std::ffi::c_void, があるので、Send,Syncを実装しなければmyDIBをLazy<Mutex<myDIB>>に入れられない
unsafe impl Send for Win32DIBCanvas {}
unsafe impl Sync for Win32DIBCanvas {}

impl Win32DIBCanvas{
    pub fn new()->Win32DIBCanvas{
        Win32DIBCanvas{
            bitmap    :HBITMAP(0),
            bitmapInfo:Default::default(),
            ppixel    :std::ptr::null_mut(),
            hMemDC    :CreatedHDC(0),
        }
    }
    pub fn create(&mut self,width:i32,height:i32){

        

        unsafe{
            let mut hdca = CreateDCA(
                PCSTR::from_raw("DISPLAY\0".as_ptr()),
                None,
                None,
                None
            );

            self.bitmapInfo.bmiHeader.biSize        = std::mem::size_of::<BITMAPINFOHEADER>() as u32;
            self.bitmapInfo.bmiHeader.biWidth       = width;
            self.bitmapInfo.bmiHeader.biHeight      = -height;
            self.bitmapInfo.bmiHeader.biPlanes      = 1;
            self.bitmapInfo.bmiHeader.biBitCount    = 3*8;
            self.bitmapInfo.bmiHeader.biCompression = BI_RGB;

            let hBitmap = CreateDIBSection(
                hdca,
                &self.bitmapInfo,
                DIB_RGB_COLORS,
                &mut self.ppixel,
                HANDLE(0),
                0
            );

            self.hMemDC =  CreateCompatibleDC(hdca);

            if let Ok(hbmp) = hBitmap{
                SelectObject(self.hMemDC,hbmp);
            }

            DeleteDC(hdca);
        }
    }
}

main.rs

main関数。CではWinMain開始だが、Rustではwindowsプログラムもmainから始める。

気分的にWinMainが欲しいので、main関数からWinMainを呼ぶようにしている。

mod winmain;
use winmain::WinMain;


use windows::Win32::Foundation::*;
use windows::Win32::System::LibraryLoader::*;
use windows::Win32::System::Threading::*;

// ■ HINSTANCE        ... windows::Win32::Foundation::HINSTANCE
// ■ GetModuleHandleA ... windows::Win32::System::LibraryLoader::GetModuleHandleA
// ■ STARTUPINFOW     ... windows::Win32::System::Threading::STARTUPINFOW
// ■ GetStartupInfoW  ... windows::Win32::System::Threading::GetStartupInfoW


// Rustのwindows-rsからウィンドウズアプリケーションをビルドする時には、
// WinWain ではなくmainをエントリポイントとする。

fn main()-> windows::core::Result<()>  {

    let hInstance:HINSTANCE;
    unsafe{
        // https://github.com/microsoft/windows-samples-rs/blob/master/create_window/src/main.rs
        hInstance = GetModuleHandleA(None).unwrap();
    }

    // https://doc.rust-jp.rs/book-ja/ch12-01-accepting-command-line-arguments.html
    let args: Vec<String> = std::env::args().collect();


    // https://stackoverflow.com/questions/68322072/how-to-get-args-from-winmain-or-wwinmain-in-rust
    let mut si = STARTUPINFOW {
        cb: std::mem::size_of::<STARTUPINFOW>() as u32,
        ..Default::default()
    };
    unsafe {
        GetStartupInfoW(&mut si) 
    };
    let cmd_show = si.wShowWindow as i32;

    ////////////////////////////////////////////////////////////////////////////////////
    // 自作WinMainを呼び出す

    WinMain(
        hInstance,
        HINSTANCE(0),
        args,
        cmd_show
    );


    Ok(())

}

winmain.rs

#[path="./win32dib.rs"]
mod win32dib;
use win32dib::Win32DIBCanvas;


use windows::{
    core::*, 
    Win32::Foundation::*,
    Win32::Graphics::Gdi::*,
    Win32::UI::WindowsAndMessaging::*,
};

use once_cell::sync::Lazy;
use std::sync::Mutex;

// ■ Mutex ... std::sync::Mutex
// ■ Lazy  ... once_cell::sync::Lazy


// DIBを管理する構造体のグローバル変数
static mydib:Lazy<Mutex<Win32DIBCanvas>> = 
        Lazy::new(|| 
            Mutex::new(
                Win32DIBCanvas::new()
            )
        );


// ■ COLORREF            ... windows::Win32::Foundation::COLORREF
// ■ HINSTANCE           ... windows::Win32::Foundation::HINSTANCE
// ■ HWND                ... windows::Win32::Foundation::HWND
// ■ LPARAM              ... windows::Win32::Foundation::LPARAM
// ■ LRESULT             ... windows::Win32::Foundation::LRESULT
// ■ POINT               ... windows::Win32::Foundation::POINT
// ■ RECT                ... windows::Win32::Foundation::RECT        
// ■ WPARAM              ... windows::Win32::Foundation::WPARAM


// ■ BeginPaint          ... windows::Win32::Graphics::Gdi::BeginPaint
// ■ BitBlt              ... windows::Win32::Graphics::Gdi::BitBlt
// ■ CreatePen           ... windows::Win32::Graphics::Gdi::CreatePen
// ■ EndPaint            ... windows::Win32::Graphics::Gdi::EndPaint
// ■ LineTo              ... windows::Win32::Graphics::Gdi::LineTo
// ■ MoveToEx            ... windows::Win32::Graphics::Gdi::MoveToEx
// ■ PS_SOLID            ... windows::Win32::Graphics::Gdi::PS_SOLID
// ■ PAINTSTRUCT         ... windows::Win32::Graphics::Gdi::PAINTSTRUCT
// ■ SelectObject        ... windows::Win32::Graphics::Gdi::SelectObject
// ■ SRCCOPY             ... windows::Win32::Graphics::Gdi::SRCCOPY


// ■ CreateWindowExA     ... windows::Win32::UI::WindowsAndMessaging::CreateWindowExA
// ■ CS_HREDRAW          ... windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW
// ■ CS_VREDRAW          ... windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW
// ■ CW_USEDEFAULT       ... windows::Win32::UI::WindowsAndMessaging::CW_USEDEFAULT
// ■ DefWindowProcA      ... windows::Win32::UI::WindowsAndMessaging::DefWindowProcA
// ■ DispatchMessageA    ... windows::Win32::UI::WindowsAndMessaging::DispatchMessageA
// ■ GetMessageA         ... windows::Win32::UI::WindowsAndMessaging::GetMessageA
// ■ LoadCursorW         ... windows::Win32::UI::WindowsAndMessaging::LoadCursorW
// ■ IDC_ARROW           ... windows::Win32::UI::WindowsAndMessaging::IDC_ARROW
// ■ MSG                 ... windows::Win32::UI::WindowsAndMessaging::MSG
// ■ PostQuitMessage     ... windows::Win32::UI::WindowsAndMessaging::PostQuitMessage
// ■ RegisterClassA      ... windows::Win32::UI::WindowsAndMessaging::RegisterClassA
// ■ WNDCLASSA           ... windows::Win32::UI::WindowsAndMessaging::WNDCLASSA
// ■ WS_OVERLAPPEDWINDOW ... windows::Win32::UI::WindowsAndMessaging::WS_OVERLAPPEDWINDOW
// ■ WS_VISIBLE          ... windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE


// ■ PCSTR               ... windows::core::PCSTR




extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {


    unsafe {
        
        let rectnullptr: Option<*const RECT> = None;


        match message as u32 {
            WM_CREATE => {

                let refmydib = &mut mydib.lock().unwrap();
                refmydib.create(640,480);

                let hPen =
                CreatePen(
                    PS_SOLID, 
                    1,
                    COLORREF(0x000000FF)
                );  // 赤いペンを作成


                if hPen.is_invalid() == false{
                    // ペンが作成できていたら線を引く

                    let oldPen = SelectObject(
                        refmydib.hMemDC,
                        hPen
                    );

                    // refmydib.hMemDC の 50,50 ~ 100,100 に線を引く
                    MoveToEx(
                        refmydib.hMemDC,
                        50,
                        50,
                        None as Option<*mut POINT>
                    );
                    LineTo(
                        refmydib.hMemDC,
                        100,
                        100
                    );

                    SelectObject(
                        refmydib.hMemDC,
                        oldPen
                    );

                    // ペンを削除
                    DeleteObject(hPen);

                }

                LRESULT(0)
            }
            WM_PAINT => {

                let mut ps = PAINTSTRUCT::default();
                let hdc = BeginPaint(window, &mut ps);
                let refmydib = &mut mydib.lock().unwrap();

                BitBlt(
                    hdc,
                    0,
                    0,
                    640,
                    480,
                    refmydib.hMemDC,
                    0,
                    0,
                    SRCCOPY
                );

                EndPaint(window, &ps);

                LRESULT(0)
            }
            WM_DESTROY => {

                PostQuitMessage(0);

                LRESULT(0)
            }
            _ => DefWindowProcA(window, message, wparam, lparam),
        }
    }
}



/// 自作 WinMain
pub fn WinMain(
        hInstance : HINSTANCE, 
        hPrevInstance : HINSTANCE, 
        lpCmdLine:Vec<String>, 
        nCmdShow:i32
    )->i32{

    unsafe{



        let _hCursor = LoadCursorW(None,IDC_ARROW).unwrap();
        let _hInstance = hInstance;
        let _lpszClassName = PCSTR::from_raw("MY_NEW_WINDOW\0".as_ptr());
        let _style = CS_HREDRAW | CS_VREDRAW;

        // https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/WindowsAndMessaging/struct.WNDCLASSA.html
        let wc=WNDCLASSA{
            hCursor :_hCursor,
            hInstance: _hInstance,
            lpszClassName: _lpszClassName,
            style: _style,
            lpfnWndProc: Some(wndproc),
            ..Default::default()
        };

        let atom = RegisterClassA(&wc);
        debug_assert!(atom != 0);


        let nullptr: Option<*const ::core::ffi::c_void> = None;

        CreateWindowExA(
            Default::default(),
            _lpszClassName,
            PCSTR::from_raw("This is a sample window\0".as_ptr()),
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            None,
            None,
            _hInstance,
            nullptr,
        );

        let mut message = MSG::default();

        while GetMessageA(&mut message, HWND(0), 0, 0).into() {
            DispatchMessageA(&mut message);
        }
    }
    return 0;


}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: