rustraight 0.5.2

A simple 2D game library for Rust, inspired by DXLib
use crate::graphics::DrawImageParams;

// ── Vertex types ──────────────────────────────────────────────────────────────

/// 画像描画用の頂点(NDC座標・UV・マスク情報・アルファを含む、stride: 48 bytes)。
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct ImageVertex {
    pub(crate) pos:       [f32; 2], // NDC 座標
    pub(crate) uv:        [f32; 2],
    pub(crate) screen_xy: [f32; 2], // 画面ピクセル位置(マスクサンプリング用に補間される)
    pub(crate) mask_ox:   f32,
    pub(crate) mask_oy:   f32,
    pub(crate) mask_w:    f32,
    pub(crate) mask_h:    f32,
    pub(crate) mask_on:   f32,      // 1.0 = マスク適用, 0.0 = マスクなし
    pub(crate) alpha:     f32,
}

// ── Helpers ───────────────────────────────────────────────────────────────────

/// 通常描画(メインウィンドウ/オフスクリーンスクリーン)用の画像矩形頂点を構築する。
/// 拡大縮小・回転(中心基準)・反転・マスク座標・アルファを適用した6頂点(2三角形)を返す。
pub(crate) fn build_image_quad_ex(
    x: i32, y: i32,
    image_w: u32, image_h: u32,
    screen_w: u32, screen_h: u32,
    mask_ox: f32, mask_oy: f32, mask_w: f32, mask_h: f32, mask_on: f32,
    params: &DrawImageParams,
) -> [ImageVertex; 6] {
    let sw     = screen_w as f32;
    let sh     = screen_h as f32;
    let draw_w = image_w as f32 * params.scale_x;
    let draw_h = image_h as f32 * params.scale_y;
    let cx     = x as f32 + draw_w * 0.5;
    let cy     = y as f32 + draw_h * 0.5;
    let hw     = draw_w * 0.5;
    let hh     = draw_h * 0.5;

    let rad = params.rotation.to_radians();
    let cos = rad.cos();
    let sin = rad.sin();
    let rot = |lx: f32, ly: f32| -> (f32, f32) { (lx * cos - ly * sin, lx * sin + ly * cos) };

    let (tl_x, tl_y) = rot(-hw, -hh);
    let (tr_x, tr_y) = rot( hw, -hh);
    let (bl_x, bl_y) = rot(-hw,  hh);
    let (br_x, br_y) = rot( hw,  hh);
    let corners = [
        (cx + tl_x, cy + tl_y),
        (cx + tr_x, cy + tr_y),
        (cx + bl_x, cy + bl_y),
        (cx + br_x, cy + br_y),
    ];

    let (u0, u1) = if params.flip_x { (1.0f32, 0.0f32) } else { (0.0f32, 1.0f32) };
    let (v0, v1) = if params.flip_y { (1.0f32, 0.0f32) } else { (0.0f32, 1.0f32) };

    let ndc = |px: f32, py: f32| -> [f32; 2] { [px / sw * 2.0 - 1.0, 1.0 - py / sh * 2.0] };
    let v = |idx: usize, u: f32, tv: f32| ImageVertex {
        pos:       ndc(corners[idx].0, corners[idx].1),
        uv:        [u, tv],
        screen_xy: [corners[idx].0, corners[idx].1],
        mask_ox, mask_oy, mask_w, mask_h, mask_on,
        alpha:     params.alpha,
    };

    let tl = v(0, u0, v0);
    let tr = v(1, u1, v0);
    let bl = v(2, u0, v1);
    let br = v(3, u1, v1);
    [tl, tr, bl, tr, br, bl]
}

/// オーバーレイ描画用の画像矩形頂点を構築する。
/// x/y は仮想スクリーンのピクセル座標。win_scale_* でディスプレイピクセル座標に変換し、
/// main_x/main_y でメインウィンドウの画面上位置だけオフセットする。
#[allow(clippy::too_many_arguments)]
pub(crate) fn build_image_quad_overlay(
    x: i32, y: i32,
    image_w: u32, image_h: u32,
    display_w: u32, display_h: u32,
    main_x: i32, main_y: i32,
    win_scale_x: f32, win_scale_y: f32,
    mask_ox: f32, mask_oy: f32, mask_w: f32, mask_h: f32, mask_on: f32,
    params: &DrawImageParams,
) -> [ImageVertex; 6] {
    let dw     = display_w as f32;
    let dh     = display_h as f32;
    // 画像サイズ・位置を仮想スクリーンピクセルからディスプレイピクセルへスケールする
    let draw_w = image_w as f32 * params.scale_x * win_scale_x;
    let draw_h = image_h as f32 * params.scale_y * win_scale_y;
    let cx = x as f32 * win_scale_x + draw_w * 0.5 + main_x as f32;
    let cy = y as f32 * win_scale_y + draw_h * 0.5 + main_y as f32;
    let hw = draw_w * 0.5;
    let hh = draw_h * 0.5;

    // マスク座標を仮想スクリーン空間からディスプレイ空間へスケールする
    let (dmox, dmoy, dmw, dmh) = if mask_on > 0.5 {
        (mask_ox * win_scale_x + main_x as f32,
         mask_oy * win_scale_y + main_y as f32,
         mask_w  * win_scale_x,
         mask_h  * win_scale_y)
    } else {
        (mask_ox, mask_oy, mask_w, mask_h)
    };

    let rad = params.rotation.to_radians();
    let cos = rad.cos();
    let sin = rad.sin();
    let rot = |lx: f32, ly: f32| -> (f32, f32) { (lx * cos - ly * sin, lx * sin + ly * cos) };
    let (tl_x, tl_y) = rot(-hw, -hh);
    let (tr_x, tr_y) = rot( hw, -hh);
    let (bl_x, bl_y) = rot(-hw,  hh);
    let (br_x, br_y) = rot( hw,  hh);
    let corners = [(cx+tl_x, cy+tl_y), (cx+tr_x, cy+tr_y), (cx+bl_x, cy+bl_y), (cx+br_x, cy+br_y)];

    let (u0, u1) = if params.flip_x { (1.0f32, 0.0f32) } else { (0.0f32, 1.0f32) };
    let (v0, v1) = if params.flip_y { (1.0f32, 0.0f32) } else { (0.0f32, 1.0f32) };
    let ndc = |px: f32, py: f32| -> [f32; 2] { [px / dw * 2.0 - 1.0, 1.0 - py / dh * 2.0] };
    let sv = |idx: usize, u: f32, tv: f32| ImageVertex {
        pos: ndc(corners[idx].0, corners[idx].1),
        uv: [u, tv],
        screen_xy: [corners[idx].0, corners[idx].1],
        mask_ox: dmox, mask_oy: dmoy, mask_w: dmw, mask_h: dmh, mask_on,
        alpha: params.alpha,
    };
    let tl = sv(0, u0, v0); let tr = sv(1, u1, v0);
    let bl = sv(2, u0, v1); let br = sv(3, u1, v1);
    [tl, tr, bl, tr, br, bl]
}