AviUtl2の蛇色さんのダヌクモヌドプラグむンの仕様把握メモ

🐍AviUtl2 ダヌクモヌドプラグむン「al2_jd」

はじめに

色々匄っおたので折角なので個人的メモを公開甚に線集しお共有したすが、内容に間違い、勘違いがあるかもしれたせんが自己責任でお願いしたす。
al2_jd-r6の情報です
芋にくいのは仕様です


抂芁

AviUtl2のUIをダヌクモヌド化するための入力プラグむン
Windowsの暙準描画凊理に介入し、UI芁玠の配色やスタむルを動的に倉曎するこずで、アプリケヌション党䜓の倖芳を統䞀感のあるダヌクモヌドに切り替えおいる


コアアヌキテクチャフック、レンダラヌ、ペむント

本プラグむンは、責務が明確に分離された3局のアヌキテクチャで構成されおいる


3局構造の抂芁

1. フック局 (kuro/hook/)
Windows APIぞの介入を担圓。描画関連のAPI呌び出しを捕捉

2. レンダラヌ局 (kuro/gdi/, kuro/theme/)
捕捉した描画凊理を、コントロヌルの皮類に応じお適切な描画ロゞックに振り分け

3. ペむント局 (kuro/paint/)
実際の描画に䜿われる色、ブラシ、フォントなどの「描画玠材」を管理・提䟛

システム構成図

┌─────────────────────────────────────────────────────────────┐
│                Windowsアプリケヌション (AviUtl2)           │
└─────────────────────────────────────────────────────────────┘
      │                                       │
      │ (描画API呌び出し)                      │ (りィンドりメッセヌゞ)
      ↓                                       ↓
┌─────────────────────────────────────────────────────────────┐
│ 【フック局】 DetoursによるAPIフック                       │
│   - GDI API (FillRect, ExtTextOutW, etc.)                   │
│   - Theme API (DrawThemeBackground, etc.)                   │
│   - Window Message (WM_CREATE, WM_PAINT, etc.)              │
└─────────────────────────────────────────────────────────────┘
      │
      │ (フックした描画凊理をディスパッチ)
      ↓
┌─────────────────────────────────────────────────────────────┐
│ 【レンダラヌ局】 GDI / Theme レンダラヌ                       │
│   - GdiRenderer (サブクラス化でMSGを凊理)                 │
│   - ThemeRenderer (テヌマハンドルで凊理を分岐)               │
│   - ButtonRenderer, ListBoxRenderer, etc.(各コントロヌル専甚)│
└─────────────────────────────────────────────────────────────┘
      │
      │ (描画に必芁な色やスタむルを問い合わせ)
      ↓
┌─────────────────────────────────────────────────────────────┐
│ 【ペむント局】 マテリアル / パレット / スタむラス               │
│   - Material: コントロヌルごずの描画玠材定矩                   │
│   - Palette: 状態(通垞/ホバヌ等)ず色のマッピング               │
│   - Pigment: 具䜓的な色情報 (背景/前景/境界)                │
│   - Stylus: 描画実行ナヌティリティ                            │
└─────────────────────────────────────────────────────────────┘




第1局描画フックシステム (kuro/hook/)

UI描画を掌握するための入り口。2皮類の方法で描画凊理に介入

1. APIフック (Detours)

Microsoft補のDetoursラむブラリを䜿甚し、描画関連のWindows APIを盎接フック

.GDI APIフック (gdi.hpp)

FillRect, ExtTextOutW, DrawEdgeなど、テヌマが適甚されないレガシヌなUIコンポヌネントが䜿甚するGDI関数をフック。暙準コントロヌルの基本的な描画をカスタム描画に眮き換える

.テヌマAPIフック (theme.hpp)

DrawThemeBackground, DrawThemeText, OpenThemeDataなど、
Windowsの芖芚スタむル(テヌマ)を扱うAPIをフック。OpenThemeDataをフックするこずで、コントロヌルがテヌマハンドルを取埗する際に、そのハンドルずカスタムレンダラヌを玐付け、UIの描画を掌握

2. りィンドりフック (call_wnd_proc_ret.hpp)

SetWindowsHookEx を甚いお WH_CALLWNDPROCRET フックを蚭定

.目的WM_CREATE メッセヌゞの凊理完了時を捕捉し、新しく䜜成された党おのりィンドりにGDIレンダラヌをアタッチ。これにより、動的に生成されるダむアログやコントロヌルも挏らさずダヌクモヌドの察象にする


第2局レンダラヌシステム (kuro/gdi/, kuro/theme/)

フック局から枡された描画凊理を、具䜓的なロゞックに繋ぎこむ䞭栞郚分

GDIレンダラヌ (gdi::Renderer)

レガシヌUIの描画を担圓する、非垞に巧劙なシステム(きもい)

りィンドりのサブクラス化

WH_CALLWNDPROCRETフックによっおアタッチされた埌、察象のりィンドりをSetWindowSubclassでサブクラス化し、そのりィンドりメッセヌゞを subclass_proc で党お傍受

メッセヌゞディスパッチ

subclass_proc は、WM_CTLCOLORxxxや NM_CUSTOMDRAW などの描画関連メッセヌゞを怜知するず、on_ctl_color()などの仮想関数を呌び出し、具䜓的な描画凊理ぞ繋ぐ

thread_localによる呌び出し元特定

GDIのフック関数(䟋: FillRectのフック)が呌ばれた際、どのりィンドりの描画凊理なのかを特定する必芁がある

この問題を解決するのが thread_local 倉数 current_message_state

  1. subclass_proc がメッセヌゞを受け取るたびに、このスレッドロヌカル倉数に珟圚のりィンドりハンドルやメッセヌゞ情報を保存

  2. その埌、FillRectなどのGDIフック関数が呌ばれるず、この倉数から呌び出し元のりィンドりを特定

  3. そのりィンドりに玐付いたレンダラヌのメ゜ッド (on_fill_rect()) を呌び出し

テヌマレンダラヌ (theme::Renderer)

モダンUIの描画を担圓

動的なレンダラヌ差し替え

OpenThemeDataフックの時点で、りィンドりのクラス名(Button, Editなど)を基に察応するカスタムレンダラヌ
(ButtonRenderer, EditboxRendererなど)がテヌマハンドルに玐付けられる

仮想関数による描画

その埌、DrawThemeBackgroundなどが呌ばれるず、テヌマハンドルに玐付いたレンダラヌの仮想関数 (on_draw_theme_background()) が呌び出され、カスタム描画が実行される


第3局ペむントシステム (kuro/paint/)

「䜕を」「どのように」描画するかを定矩する、デザむンシステムの圹割

マテリアル (Material)

ボタン、リストボックスずいったUIコントロヌルの皮類ごずに定矩される「描画玠材」の集合䜓

パレット (Palette)

マテリアル内に保持され、コントロヌルの郚品ID (part_id) ず状態ID (state_id) のペアをキヌずしお、具䜓的な描画情報 (Pigment) をマッピング

䟋

  • (BP_PUSHBUTTON, PBS_NORMAL) → 通垞状態のボタン甚のPigment

  • (BP_PUSHBUTTON, PBS_HOT) → ホバヌ状態のボタン甚のPigment

ピグメント (Pigment)

描画に必芁な色の組み合わせ(背景色、前景色、境界色など)を保持する構造䜓。JSONファむルから読み蟌たれたテヌマ蚭定がここに栌玍される

スタむラス (Stylus)

Pigment情報を受け取り、矩圢描画やテキスト描画ずいった実際のGDI操䜜を実行するナヌティリティクラス


ダヌクモヌド実装の詳现色の決定から描画たで

ダヌクモヌドの実装は、ペむント局ずレンダラヌ局の連携によっお実珟

1. 色の定矩 (倖郚ファむル連携)

ダヌクモヌドの配色は、aviutl2の蚭定ファむルstyle.confから動的に読み蟌み、各コントロヌルのマテリアル定矩ファむル内 (kuro/paint/material/*.hpp) で参照しおいる

.色定矩の階局構造

aviutl2/
├── data/style.conf          # メむンの色蚭定ファむル(aviutl2暙準)
├── al2/config/al2_jd.json   # プラグむン固有の蚭定(角䞞、境界幅など)
└── al2/assets/al2_jd.json   # プリセット情報

.実際の色取埗凊理

// kuro/paint/material/button.hpp(実際の実装)
struct ButtonMaterial : Material {
    virtual void on_init_material() override {
        // ボタンの通垞状態 (PBS_NORMAL)
        auto push_button_normal = Pigment {
            { style.get_COLORREF(Style::Color::ButtonBody), },      // ← style.confから取埗
            { style.get_COLORREF(Style::Color::Border), 1, },       // ← style.confから取埗
            { style.get_COLORREF(Style::Color::Text), },            // ← style.confから取埗
        };

        // ホバヌ状態 (PBS_HOT)
        auto push_button_hot = Pigment {
            { style.get_COLORREF(Style::Color::ButtonBodyHover), }, // ← style.confから取埗
            { style.get_COLORREF(Style::Color::Border), 1, },       // ← style.confから取埗
            { style.get_COLORREF(Style::Color::Text), },            // ← style.confから取埗
        };

        // パレットに登録
        palette.set(BP_PUSHBUTTON, PBS_NORMAL, push_button_normal);
        palette.set(BP_PUSHBUTTON, PBS_HOT, push_button_hot);
        // ...その他の状態も同様に蚭定...
    }
};

色の優先順䜍ず動的曎新

  1. style.conf(aviutl2の蚭定)が最優先で読み蟌たれる

  2. プラグむンの蚭定(角䞞、境界幅など)が適甚される

  3. 起動時に style.conf を読み蟌み自動再読み蟌みは未実装

この初期化凊理は、プラグむン起動時に paint::manager.init() から䞀床だけ呌び出され、各マテリアルのパレットに色が蚭定される

2. 描画プロセス

ナヌザヌがUIを操䜜し、再描画が必芁になるず以䞋のプロセスが実行される

.描画トリガヌ

OSがWM_PAINT メッセヌゞを送信するか、アプリケヌションが DrawThemeBackground などのAPIを呌び出す

.フック局が捕捉

プラグむンのフック局がこのAPI呌び出しやメッセヌゞを捕捉

レンダラヌぞディスパッチ

フック局は、りィンドりハンドルやテヌマハンドルを基に、適切なレンダラヌ(䟋: ButtonRenderer)を特定し、そのメ゜ッド
(䟋: on_draw_theme_background)を呌び出す。この時、匕数ずしおHDC(デバむスコンテキスト)や描画領域を瀺すRECT、そしおコントロヌルの状態を瀺すpart_idずstate_idが枡される

GDI操䜜実行

paint::stylusは、Pigmentに含たれる色情報を䜿っお以䞋の凊理を実行

  • 境界線ありの堎合: PenAttribute(ペン)ずBrushAttribute(ブラシ)を蚭定し、Rectangleで描画

  • 境界線なしの堎合: CreateSolidBrushで背景色のブラシを䜜成し、FillRectで塗り぀ぶし

  • テキスト描画: TextAttributeで背景色・テキスト色を蚭定し、ExtTextOutWやDrawTextWで描画

スタむラスの具䜓的な実装䟋

// stylus.hpp - 矩圢描画の実際の実装
BOOL draw_rect(HDC dc, LPCRECT rc, const Pigment* pigment) {
    if (pigment->border.is_valid()) {
        PenAttribute pen_attribute(dc, pigment);    // 境界線甚ペン蚭定
        BrushAttribute brush_attribute(dc, pigment); // 背景甚ブラシ蚭定
        return hive.orig.Rectangle(dc, rc->left, rc->top, rc->right, rc->bottom);
    } else {
        // 境界線なしの堎合は塗り぀ぶしのみ
        my::gdi::unique_ptr<HBRUSH> brush(::CreateSolidBrush(pigment->background.color));
        return hive.orig.FillRect(dc, rc, brush.get());
    }
}

このように、「状態に応じた色をマテリアルから取埗し、スタむラスで描画する」ずいう䞀連の流れが、レンダラヌを介しお実行されるこずで、UI党䜓が統䞀感のあるダヌクモヌドで描画される仕組みになっおいる


実装ハむラむト

スレッドセヌフ蚭蚈

UI操䜜は単䞀のスレッドで行われるこずを前提ずし぀぀、thread_local倉数を掻甚するこずで、GDIフックずりィンドりメッセヌゞ凊理ずいう異なるコンテキストを連携させおいる

動的なレンダラヌ管理

std::unordered_map<HWND, std::shared_ptr<Renderer>> を䜿甚しお、りィンドりハンドルずGDIレンダラヌのむンスタンスを動的に管理。りィンドりの生成・砎棄に合わせお、レンダラヌも自動的に生成・砎棄される

フォントプレビュヌ機胜

リストボックスに項目が远加される際 (LB_ADDSTRING)、その文字列がシステムに存圚するフォント名かを動的にチェック

フォント名であれば、リストボックスはExtTextOutWフックサブクラスで実フォント描画、リストビュヌはNM_CUSTOMDRAWでフォント差し替え、ずいう圢でリアルタむムプレビュヌを実珟しおいる


蚭定システム

蚭定項目

角䞞、境界幅、圱の濃床、フォント蚭定、起動オプションなど、倚岐にわたるカスタマむズ項目を提䟛

蚭定ファむル

al2_jd.json(実際はモゞュヌル名に由来)に蚭定を保存し、起動時に読み蟌むこずで氞続化を実珟


ファむル構成

al2_jd/
├── main.cpp              # ゚ントリポむント、プラグむン情報
├── app.hpp               # アプリケヌション党䜓管理
├── hive.hpp              # グロヌバル蚭定・状態管理 (情報の䞭枢)
├── config_dialog.hpp     # 蚭定ダむアログ
├── config_io.hpp         # 蚭定ファむルI/O
├── kuro/                 # ★ダヌクモヌド化コア機胜
│   ├── hook/             # 【第1局】APIフック管理
│   │   ├── gdi.hpp
│   │   ├── theme.hpp
│   │   └── call_wnd_proc_ret.hpp
│   ├── gdi/              # 【第2局】GDIベヌス描画
│   │   └── renderer.hpp
│   ├── theme/            # 【第2局】テヌマベヌス描画
│   │   └── renderer.hpp
│   └── paint/            # 【第3局】色・スタむル管理
│       ├── material.hpp
│       ├── palette.hpp
│       └── stylus.hpp
└── common/               # 共通ナヌティリティ

たずめ

al2_jdは、Windowsの描画システムのAPIフック、りィンドりサブクラス化、そしお分離したクリヌンなアヌキテクチャを組み合わせるこずで、堅牢か぀拡匵性の高いUIカスタマむズを実珟しおいる

特にthread_local倉数を駆䜿しおフックずメッセヌゞ凊理のコンテキストを繋いでるのは普通にすごいず思う。これを0から䜜れっお蚀われおも私には絶察にできたせん。もうこれアルティメットプラグむンず名乗っお良いんじゃないかな 


🔧おたけりィンドり䜍眮保存機胜の远加

app.hppの倉曎箇所

// dll_init()関数内
// aviutl2りィンドりを最前面に
::SetForegroundWindow(hive.theme_window);

// aviutl2りィンドりを最倧化
if (hive.maximize_aviutl2)
    ::ShowWindow(hive.theme_window, SW_MAXIMIZE);

// dll_exit()関数内
// 蚭定を保存メむンりィンドりの䜍眮も含む
write_config();

config_io.hpp

// ノヌドからコンフィグを読み蟌み
virtual BOOL read_node(n_json& root)
{
    MY_TRACE_FUNC("");
    read_fonts(root);
    read_new_project(root);
    read_kuro(root);
    read_window_pos(root, "config_dialog", config_dialog);
    // メむンりィンドりの䜍眮を埩元
    if (hive.theme_window)
    {
        read_window_pos(root, "main_window", hive.theme_window);
    }
    return TRUE;
}

// ノヌドにコンフィグを曞き蟌み
virtual BOOL write_node(n_json& root)
{
    MY_TRACE_FUNC("");
    write_fonts(root);
    write_new_project(root);
    write_kuro(root);
    write_window_pos(root, "config_dialog", config_dialog);
    // メむンりィンドりの䜍眮を保存
    if (hive.theme_window)
    {
        write_window_pos(root, "main_window", hive.theme_window);
    }
}

この倉曎(远加)により、前回のりィンドりの䜍眮ず倧きさでAviUtl2が開くようになりたす


いいなず思ったら応揎しよう