INASOFT 管理人のひとこと


フリーソフトダウンロードサイト「INASOFT」の管理人 矢吹拓也 が日々の「ひとこと」を語るページです。
2021年1月1日より、旧ブログ(blog.inasoft.org)からお引越ししました。
・INASOFT Webサイト: https://www.inasoft.org/
・管理人のふたこと(長文記事/寄稿文): https://www.inasoft.org/talk/
2022年7月下旬より再び本業多忙化してきているため、更新頻度は落ちます。 [2022/7/24 19:32]

目次 | ←前へ / 2025-04-29 00:00

■AIにプログラミングのやり方を聞くのは、どれくらい信用できるの?NumLockLockの機能強化で実際に使ってみた

2025年 4月29日(火) 0:00:00



RSSRSS配信中
https://www.inasoft.org/



昨日NumLockLock ver.1.44.04βを公開しました。

既報のとおり「監視対象デバイス接続時に通知表示を行う」での通知方法を、文字ではなく、通知領域アイコンを紫をベースとしたものに変えるように変更しました。



で、画像下側の「通知領域アイコンに各キーのロック状態を表示する」を利用した場合のアイコン群について、稼働状態3種(ON, OFF, 変更中)×NumLock状態2種(ON, OFF)×CapsLock状態2種(ON, OFF)×ScrollLock状態2種(ON, OFF)=24種類のアイコンがあり、それらすべてについて、ベースを紫に変えるバージョンを準備するとなると、48種類のアイコンリソースを準備することになってしまい、かなり手間がかかるようになってしまう上に、実行ファイルのファイルサイズも膨らんでしまうので、アイコンの準備方式を変えたことを昨日触れました。今日はこれについて掘り下げたいと思います。

さて、48種類ものアイコンを準備するのは大変ですし、作った後にメンテナンスをするのも大変です。そこで、部品となるアイコンの形状のみを定義し、それらを組み合わせて(画像同士をOR演算でマージできるようにしておき)必要なアイコンを動的に生成する仕組みにできないかを考えました。

Windows APIとC++によるプログラミングで、2つ(以上)のアイコンを読み込んでOR演算的に合成する方法をネット検索で調べようとしたのですが、なかなか見つかりません。関連検索結果として、他の言語のことだったり、画像編集ツールの使い方ばかりが出てくる。

…と、そういえば、Windows APIとC++によるプログラミングなのですっかり失念していましたが、プログラミングの方法をAIに聞くというやり方があるんでした。すっかり脳がバックデートしてました。Windows APIとC++を使っていると、生成AIの事を忘れてしまう。

最近の生成AIは大変よくできていて、アイディアの列挙、謝罪文の作成などもできますし、下にあるような「AIがプログラミングする様子を絵にして」と依頼すれば、絵も作ってくれます。

(なんで、モニタの裏側にソースコードっぽい絵が映っているの?)

そういえば僕は、擬音語の「しゃなり」という言葉がよく分からなかったのですが、AIに絵にしてくれと依頼したら、こんなのが出てきました。

なるほど、こんな感じが「しゃなり」なんですね。

と、今回も知らないことを聞いてしまいましたが、プログラミングでも知らないことを聞いてうまくいくのか、試してみることにしました。間違っていたら動かないプログラムが作れるのだから、気軽に試せるということで。


というわけで「2つのLoadImage APIで取得したアイコンハンドルを重ね合わせて、1つのアイコンにし、そのアイコンハンドルを取得する方法」として聞いてみました。重ね合わせる方法はOR演算です。聞く相手は、Windowsについて詳しそうなCopilotが良いかなぁ。教えてAI!


Copilotの回答 (一部加工)

LoadImage APIで取得した2つのアイコンハンドルを重ね合わせて1つのアイコンを作成するには、Win32 APIの機能を活用する必要があります。以下に実現する方法の手順を示します:

  1. アイコン情報の取得

    アイコンハンドルを元に各アイコンのビットマップ情報を取得します。これには GetIconInfo 関数を使用します。

    ICONINFO iconInfo1;
    GetIconInfo(hIcon1, &iconInfo1);
    
    ICONINFO iconInfo2;
    GetIconInfo(hIcon2, &iconInfo2);
    
  2. ビットマップをメモリに読み込む

    各ビットマップデータを操作するために、DIB(Device Independent Bitmap)形式に変換してピクセルデータをメモリに取り込みます。

    BITMAP bmp1, bmp2;
    GetObject(iconInfo1.hbmColor, sizeof(BITMAP), &bmp1);
    GetObject(iconInfo2.hbmColor, sizeof(BITMAP), &bmp2);
    
  3. ピクセルデータにアクセスする

    GetDIBits 関数を用いて、各ビットマップのピクセルデータをバッファに取り出します。

    HDC hdc = CreateCompatibleDC(NULL);
    
    int width = bmp1.bmWidth;
    int height = bmp1.bmHeight;
    int dataSize = width * height * 4; // 32ビットカラーを想定
    
    BYTE* buffer1 = new BYTE[dataSize];
    BYTE* buffer2 = new BYTE[dataSize];
    
    GetDIBits(hdc, iconInfo1.hbmColor, 0, height, buffer1, (BITMAPINFO*)&bmp1, DIB_RGB_COLORS);
    GetDIBits(hdc, iconInfo2.hbmColor, 0, height, buffer2, (BITMAPINFO*)&bmp2, DIB_RGB_COLORS);
    
  4. OR演算を実行

    デバイスコンテキストにアイコンのビットマップを選択します。

    for (int i = 0; i < dataSize; i++) {
        buffer1[i] |= buffer2[i];
    }
    
  5. 新しいビットマップを作成

    OR演算後のデータを使用して、新しいビットマップを作成します。

    SetDIBits(hdc, iconInfo1.hbmColor, 0, height, buffer1, (BITMAPINFO*)&bmp1, DIB_RGB_COLORS);
    
  6. 新しいアイコンを作成

    加工したビットマップを使用して新しいアイコンを作成します。

    ICONINFO newIconInfo = iconInfo1;
    newIconInfo.hbmColor = iconInfo1.hbmColor; // 更新済みビットマップを使用
    
    HICON hNewIcon = CreateIconIndirect(&newIconInfo);
    
  7. リソースの解放

    メモリやデバイスコンテキストを適切に解放します。

    DeleteDC(hdc);
    delete[] buffer1;
    delete[] buffer2;
    DeleteObject(iconInfo1.hbmColor);
    DeleteObject(iconInfo2.hbmColor);
    

アイコンのサイズが一致している必要があります。同じ幅と高さを持つアイコンを使用してください。エラー処理を適切に行うことで、コードの堅牢性を高めることができます。


コンパイル通りそうなプログラムが出てきましたので、さっそく試してみました。

……結果から言うと、うまく動きませんでした。AIは堂々と嘘をつくんですよ。どこが間違えているか、AI自身に聞いてみるという手もありますが、まぁ、ちゃんと考えてみたいと思います。

まず、生成AI自身も語っている通り、エラー処理を適切に行えとあります。なので、各API呼び出しでエラーが返ってきていないかを調べると、3番目のGetDIBits()でエラーが返ってきていることがわかります。なぜなら、

BITMAP bmp1;
として定義した変数に対し、
(BITMAPINFO*)&bmp1
という、無茶苦茶なキャストを行っているからです。構造体へのポインタのキャストですので、バイト列を他の構造体で読みかえる行為となります。BITMAPとBITMAPINFOは全然違う構造体なので、こんなことできるわけがない。

というわけで、この辺りをガラリと書き換える必要があります。ちなみに、アイコンは16色(4bit)なので、1ピクセル当たり0.5バイトとしての書き換えも必要。カラーパレットも16個分必要。そのあたりも書き換えを行っておきます。

BITMAPINFO *pbi;
BYTE bi[sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 16]; // BITMAPINFO構造体(末尾のパレット情報格納部がかなり特殊)のためのエリア
BYTE buf1[16*16/2]; // 4bitカラーの16x16アイコンのピクセルデータ格納エリア

// ベースアイコン
GetIconInfo(hIcon1, &iconInfo1);
GetObject(iconInfo1.hbmColor, sizeof(BITMAP), &bmp1);

// ピクセルデータ格納エリアの初期化
ZeroMemory(buf1, sizeof(buf1));

// パレット情報を得るためにデバイスコンテキストを作成
HDC hdc = GetDC(0);
HDC hdc_mem = CreateCompatibleDC(hdc);
ReleaseDC(0, hdc);
HBITMAP hBmpOld = (HBITMAP)SelectObject(hdc_mem, iconInfo1.hbmColor);

// BITMAPINFOの準備
ZeroMemory(bi, sizeof(bi));
pbi = (BITMAPINFO *)bi;
pbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbi->bmiHeader.biWidth = 16;
pbi->bmiHeader.biHeight = 16;
pbi->bmiHeader.biPlanes = 1;
pbi->bmiHeader.biBitCount = 4;
pbi->bmiHeader.biCompression = BI_RGB;
GetDIBColorTable(hdc_mem, 0, 16, pbi->bmiColors); // パレットの準備

// ピクセルデータをバッファの読み出し(上下逆転したようなビットマップデータが得られる)
GetDIBits(hdc_mem, iconInfo1.hbmColor, 0, height, buf1, pbi, DIB_RGB_COLORS);

	:
	:(この辺りはAIのプログラミングが使えるので省略)// 最終合成結果のビットマップデータからHBITMAPを作成
SetDIBits(hdc_mem, iconInfo1.hbmColor, 0, 16, buf1, pbi, DIB_RGB_COLORS);

// 更新済みビットマップを使用し新しいアイコンハンドルを作成
HICON hNewIcon = CreateIconIndirect(&iconInfo1);

// リソース解放
SelectObject(hdc_mem, hBmpOld);
DeleteObject(iconInfo1.hbmColor);
DeleteDC(hdc_mem);

こんな感じでしょうかね。「BITMAPINFOの準備」で作ったpbiをGetDIBits()やSetDIBits()のBITMAPINFO *引数の箇所に与えれば、動きます。

やはり、AIが単独でプログラミングするのはまだ難しいですが、一方で僕一人で上記の手順にたどり着くこともまた難しかったと思います。なので、AIの出力物に対し、適切なレビューまたは手直しを行う人間がいれば、かなり期間短縮が果たせるということは、実証できたのかなといったところです。

ちなみに現在公開しているβ版は、技術検証しながらの作成だったこともあり、hdcやhdc_memはアイコン合成をやるたびに作ったり消したりを繰り返す状態になってます。48回もこれを繰り返すのは無駄でしかないので、正式版公開にあたっては、その辺りの流れを効率よくしておきたいと思います。



目次 | ←前へ / 2025-04-29 00:00


目次の表示:


ブログではないので、コメント機能とトラックバック機能は提供していません。ご質問・ご意見等はメールフィードバックまたはTwitter等からお願いします。いただいたご質問・ご意見などは、この「管理人のひとこと」の記事に追加、あるいは新規の記事にする形で一部または全文をそのまま、あるいは加工させていただいた上で、ご紹介させていただく場合があります。
当サイトでは掲載内容による不具合等に関する責任を持ちません。また、内容の正確性についての保証もありませんので、情報をご利用の際は、利用者の自己責任で確認をお願いします。本ページは公開から1年半後の任意のタイミングで削除される予定です。


- 最近の更新 -



3285250 (+0036)[+0928]

Copyright© 2010-2025 INASOFT