rustraight 0.6.4

A simple 2D game library for Rust, inspired by DXLib
// graphics.rs — 画像ファイルのデコード(WIC 経由で RGBA8 化)だけを担当する。
// デコード結果の保持・GPU アップロード・ハンドル管理は window モジュール側の
// スクリーン(Screen)レジストリで一元管理する。

/// デコード済み画像データ(RGBA8、行優先)。
pub struct ImageData {
    pub width: u32,
    pub height: u32,
    pub rgba: Vec<u8>,
}

/// 画像の読み込みに失敗した際の代替スプライト(8x8 マゼンタ)。
pub(crate) fn missing_image() -> ImageData {
    const W: u32 = 8;
    const H: u32 = 8;
    let mut rgba = vec![0u8; (W * H * 4) as usize];
    for pixel in rgba.chunks_exact_mut(4) {
        pixel[0] = 0xFF;
        pixel[1] = 0x00;
        pixel[2] = 0xFF;
        pixel[3] = 0xFF;
    }
    ImageData { width: W, height: H, rgba }
}

/// WIC (Windows Imaging Component) で画像ファイルを RGBA8 にデコードする。
/// PNG/JPEG/BMP/TIFF/GIF/WebP など WIC が対応する全フォーマットを読める。
#[cfg(target_os = "windows")]
fn load_image_wic(path: &str) -> Option<ImageData> {
    use windows::{
        core::PCWSTR,
        Win32::Foundation::GENERIC_ACCESS_RIGHTS,
        Win32::Graphics::Imaging::{
            CLSID_WICImagingFactory, GUID_WICPixelFormat32bppRGBA,
            IWICBitmapFrameDecode, IWICFormatConverter, IWICImagingFactory, IWICPalette,
            WICBitmapDitherTypeNone, WICBitmapPaletteTypeMedianCut,
            WICDecodeMetadataCacheOnDemand,
        },
        Win32::System::Com::{
            CoCreateInstance, CoInitializeEx, CLSCTX_INPROC_SERVER, COINIT_APARTMENTTHREADED,
        },
    };
    unsafe {
        // すでに別モードで初期化済みでも WIC オブジェクトはアパートメント非依存なので続行する
        let _ = CoInitializeEx(None, COINIT_APARTMENTTHREADED);

        let factory: IWICImagingFactory =
            CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER).ok()?;

        let path_w: Vec<u16> = path.encode_utf16().chain(std::iter::once(0)).collect();
        let decoder = factory
            .CreateDecoderFromFilename(
                PCWSTR(path_w.as_ptr()),
                None,
                GENERIC_ACCESS_RIGHTS(0x8000_0000), // GENERIC_READ
                WICDecodeMetadataCacheOnDemand,
            )
            .ok()?;

        let frame: IWICBitmapFrameDecode = decoder.GetFrame(0).ok()?;
        let mut width = 0u32;
        let mut height = 0u32;
        frame.GetSize(&mut width, &mut height).ok()?;

        // RGBA8 フォーマットコンバータを作成
        let converter: IWICFormatConverter = factory.CreateFormatConverter().ok()?;
        converter
            .Initialize(
                &frame,
                &GUID_WICPixelFormat32bppRGBA,
                WICBitmapDitherTypeNone,
                None::<&IWICPalette>,
                0.0,
                WICBitmapPaletteTypeMedianCut,
            )
            .ok()?;

        let stride = width * 4;
        let mut rgba = vec![0u8; (stride * height) as usize];
        converter
            .CopyPixels(std::ptr::null(), stride, &mut rgba)
            .ok()?;

        Some(ImageData { width, height, rgba })
    }
}

/// 画像ファイルをデコードする。失敗時はログを出して代替スプライトを返す。
pub(crate) fn decode_image_file(path: &str) -> ImageData {
    #[cfg(target_os = "windows")]
    if let Some(s) = load_image_wic(path) {
        return s;
    }
    crate::log_warn!("画像の読み込みに失敗しました: '{path}'、代替スプライトを使用します");
    missing_image()
}

// -----------------------------------------------------------------------

/// 画像描画時の合成モード。
#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
pub enum BlendMode { #[default] Normal, Add, Mul }

/// `draw_screen_ex` / `overlay_draw_screen_ex` に渡す拡張描画パラメータ。
#[derive(Copy, Clone)]
pub struct DrawScreenParams {
    pub scale_x:  f32,
    pub scale_y:  f32,
    pub rotation: f32,   // 度数法
    pub alpha:    f32,
    pub flip_x:   bool,
    pub flip_y:   bool,
}

impl Default for DrawScreenParams {
    fn default() -> Self {
        Self {
            scale_x:  1.0,
            scale_y:  1.0,
            rotation: 0.0,
            alpha:    1.0,
            flip_x:   false,
            flip_y:   false,
        }
    }
}