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-05-02 00:00

■Copilot以外の別の生成AIにも聞いてみたら、正しいプログラムが得られるのでは?

2025年 5月 2日(金) 0:00:00



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



Windows APIを利用したアイコンのOR合成の方法について、先日は「Windowsに詳しそう」という理由だけでCopilotに答えを求めて失敗してしまいましたが、別の生成AIだとどんな回答がもらえるか、試してみないとAIの名誉のためによろしくないかな?ということで、別の生成AIにも聞いてい見ることにしました。

というわけで、ChatGPTに聞いてみることにしました。


2つのLoadImage APIで取得したアイコンハンドルをOR演算で重ね合わせて1つのアイコンに合成し、そのアイコンハンドルを取得する方法を教えてください。


手順の概要

  1. LoadImage で2つの HICON を取得
  2. GetIconInfo を使って各アイコンのビットマップ(マスク/カラー)を取得
  3. GetDIBits でビットマップのピクセルデータを取得
  4. 対応するピクセルを OR 演算
  5. 新しいビットマップを作成し、CreateIconIndirect で新しいアイコンを作成
#include <windows.h>

HICON CombineIcons(HICON hIcon1, HICON hIcon2)
{
    ICONINFO iconInfo1, iconInfo2;
    GetIconInfo(hIcon1, &iconInfo1);
    GetIconInfo(hIcon2, &iconInfo2);

    BITMAP bmp1, bmp2;
    GetObject(iconInfo1.hbmColor, sizeof(BITMAP), &bmp1);
    GetObject(iconInfo2.hbmColor, sizeof(BITMAP), &bmp2);

    int width = bmp1.bmWidth;
    int height = bmp1.bmHeight;

    // 対応するBITMAPINFO作成
    BITMAPINFO bmi = {0};
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = width;
    bmi.bmiHeader.biHeight = -height; // top-down DIB
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 32; // RGBA
    bmi.bmiHeader.biCompression = BI_RGB;

    HDC hdc = GetDC(NULL);
    DWORD* pixels1 = new DWORD[width * height];
    DWORD* pixels2 = new DWORD[width * height];
    DWORD* combinedPixels = new DWORD[width * height];

    GetDIBits(hdc, iconInfo1.hbmColor, 0, height, pixels1, &bmi, DIB_RGB_COLORS);
    GetDIBits(hdc, iconInfo2.hbmColor, 0, height, pixels2, &bmi, DIB_RGB_COLORS);

    for (int i = 0; i < width * height; ++i) {
        combinedPixels[i] = pixels1[i] | pixels2[i]; // ピクセルごとに OR 合成
    }

    HBITMAP hbmColor = CreateCompatibleBitmap(hdc, width, height);
    SetDIBits(hdc, hbmColor, 0, height, combinedPixels, &bmi, DIB_RGB_COLORS);

    // マスクビットマップは片方のものを使う(必要に応じて OR 処理)
    HICON hCombinedIcon;
    ICONINFO ii = {};
    ii.fIcon = TRUE;
    ii.hbmColor = hbmColor;
    ii.hbmMask = iconInfo1.hbmMask;

    hCombinedIcon = CreateIconIndirect(&ii);

    // 後始末
    delete[] pixels1;
    delete[] pixels2;
    delete[] combinedPixels;
    DeleteObject(iconInfo1.hbmColor);
    DeleteObject(iconInfo2.hbmColor);
    DeleteObject(iconInfo1.hbmMask);
    DeleteObject(iconInfo2.hbmMask);
    ReleaseDC(NULL, hdc);

    return hCombinedIcon;
}
補足
このコードは32ビットのカラーアイコンに対応しています。
OR 演算を他の(AND, XORなど)に変えることで、別の合成効果も作れます。
マスクビットマップも合成したい場合は、同様の方法でORしてください。


なんか大丈夫そうです。でも、アイコンは16色モードで使いたいので、16色モード用のプログラムにしてもらませんかね。


16色(4ビット)モードのアイコンを作成するには、ビットマップの カラービット数を4ビット(biBitCount = 4) に設定し、カラーパレット(RGBQUAD)を含むDIB を用いる必要があります。
以下は、2つの16色アイコンをOR演算で合成し、新しい16色アイコンを作成するC++のサンプルコードです。

前提条件

  • 両方のアイコンが16色(4ビット)であること
  • OR演算はインデックスカラー(パレットインデックス)同士に対して行う
  • カラーマスクビットマップ(ANDマスク)も必要に応じて合成
サンプルコード(16色アイコン合成)
#include <windows.h>
#include <vector>

HICON Combine16ColorIcons(HICON hIcon1, HICON hIcon2) {
    ICONINFO ii1, ii2;
    GetIconInfo(hIcon1, &ii1);
    GetIconInfo(hIcon2, &ii2);

    BITMAP bmp;
    GetObject(ii1.hbmColor, sizeof(BITMAP), &bmp);

    int width = bmp.bmWidth;
    int height = bmp.bmHeight;

    BITMAPINFO bmi = {};
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = width;
    bmi.bmiHeader.biHeight = height;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biBitCount = 4; // 4bpp (16色)
    bmi.bmiHeader.biCompression = BI_RGB;

    const int imageSize = ((width + 1) / 2) * height; // 4bpp = 2ピクセル/バイト
    std::vector pixels1(imageSize);
    std::vector pixels2(imageSize);
    std::vector combinedPixels(imageSize);

    HDC hdc = GetDC(NULL);
    GetDIBits(hdc, ii1.hbmColor, 0, height, pixels1.data(), &bmi, DIB_RGB_COLORS);
    GetDIBits(hdc, ii2.hbmColor, 0, height, pixels2.data(), &bmi, DIB_RGB_COLORS);

    for (int i = 0; i < imageSize; ++i) {
        combinedPixels[i] = pixels1[i] | pixels2[i]; // バイト単位で OR(2ピクセル単位)
    }

    // パレット(標準16色パレット)
    RGBQUAD palette[16] = {
        {0x00, 0x00, 0x00, 0}, // 0: 黒
        {0x80, 0x00, 0x00, 0}, // 1: 暗赤
        {0x00, 0x80, 0x00, 0}, // 2: 暗緑
        {0x80, 0x80, 0x00, 0}, // 3: 暗黄
        {0x00, 0x00, 0x80, 0}, // 4: 暗青
        {0x80, 0x00, 0x80, 0}, // 5: 紫
        {0x00, 0x80, 0x80, 0}, // 6: 暗水色
        {0xC0, 0xC0, 0xC0, 0}, // 7: 明灰
        {0x80, 0x80, 0x80, 0}, // 8: 暗灰
        {0xFF, 0x00, 0x00, 0}, // 9: 赤
        {0x00, 0xFF, 0x00, 0}, // 10: 緑
        {0xFF, 0xFF, 0x00, 0}, // 11: 黄
        {0x00, 0x00, 0xFF, 0}, // 12: 青
        {0xFF, 0x00, 0xFF, 0}, // 13: マゼンタ
        {0x00, 0xFF, 0xFF, 0}, // 14: シアン
        {0xFF, 0xFF, 0xFF, 0}  // 15: 白
    };

    HBITMAP hbmColor = CreateCompatibleBitmap(hdc, width, height);
    SetDIBits(hdc, hbmColor, 0, height, combinedPixels.data(), &bmi, DIB_RGB_COLORS);

    // ANDマスクは一方のものを使う(または別途OR処理も可)
    ICONINFO iiNew = {};
    iiNew.fIcon = TRUE;
    iiNew.hbmColor = hbmColor;
    iiNew.hbmMask = ii1.hbmMask;

    HICON hNewIcon = CreateIconIndirect(&iiNew);

    // クリーンアップ
    DeleteObject(ii1.hbmColor);
    DeleteObject(ii1.hbmMask);
    DeleteObject(ii2.hbmColor);
    DeleteObject(ii2.hbmMask);
    ReleaseDC(NULL, hdc);

    return hNewIcon;
}
注意点
SetDIBits に与える BITMAPINFO には RGBQUAD パレットを含む必要がありますが、標準パレットを使うため省略可です(必要なら BITMAPINFO を拡張)。
もし元アイコンが24bitや32bitカラーの場合、4bitに減色処理を事前に行う必要があります。
16色アイコンの透明度は ANDマスク(白=透明, 黒=不透明)で表現されます。

RGBQUAD palette[16]を定義しているけど、どこにも使われていないじゃん……という不思議さがありますが、「SetDIBits に与える BITMAPINFO には RGBQUAD パレットを含む必要がありますが、標準パレットを使うため省略可です(必要なら BITMAPINFO を拡張)」って、知らなかった……!省略可能だったのか!

AIは平気な顔で嘘をつくというけど、まさかこれは嘘じゃないよね……。


ちなみに、GoogleのGeminiにも聞いてみたのですが、生成されたコードはエラー処理を含む極めて巨大なもので、記事としてネタとして成立しづらいものだったので省略します。16色にしてほしいとお願いしたけど16色になっていなかったり(※)、最終結果の出力先を画面にしていたのですが、その様子があまりにもおかしいコードが出力されていました。

(※)16色アイコンは「色情報が失われたり、パレットが異なる場合にうまく合成できなかったりするから、使わない方が良い」とかいう、いらんアドバイスをされた上で、求められてない32bit色アイコンの合成コードを示された感じ。AIも「余計なお世話」をしてくるんですね。



目次 | ←前へ / 2025-05-02 00:00


目次の表示:


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


- 最近の更新 -



3286475 (+0075)[+0504]

Copyright© 2010-2025 INASOFT