среда, 1 июля 2026 г.

Win resurs graf

 Поскольку ты хочешь картинку на форме, а не в меню, и при этом работаешь на чистом C с cl.exe (без C++/WinForms), есть два рабочих пути: простой (BMP без прозрачности) и с прозрачностью (PNG через GDI+).


Вариант 1: BMP на форме (самый простой, без GDI+)

Подходит, если картинка — обычный BMP без прозрачных краёв.

.rc (resources.rc)

#include <windows.h>

IDB_LOGO BITMAP "logo.bmp"

main.c

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#define IDB_LOGO 201

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nShowCmd)
{
    const char CLASS_NAME[] = "ImgForm";

    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, "Картинка на форме",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
        NULL, NULL, hInstance, NULL
    );

    if (!hwnd) return 0;

    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

static HBITMAP hLogo = NULL;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
        case WM_CREATE: {
            HINSTANCE hInst = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
            hLogo = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_LOGO));
            return 0;
        }

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            if (hLogo) {
                HDC hMemDC = CreateCompatibleDC(hdc);
                HBITMAP hOld = (HBITMAP)SelectObject(hMemDC, hLogo);

                BITMAP bm;
                GetObject(hLogo, sizeof(bm), &bm);

                // рисуем картинку в точке (50,50)
                BitBlt(hdc, 50, 50, bm.bmWidth, bm.bmHeight,
                       hMemDC, 0, 0, SRCCOPY);

                SelectObject(hMemDC, hOld);
                DeleteDC(hMemDC);
            }

            EndPaint(hwnd, &ps);
            return 0;
        }

        case WM_DESTROY:
            if (hLogo) DeleteObject(hLogo);
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

Сборка:

rc resources.rc
cl /TC main.c resources.res /link user32.lib gdi32.lib

Картинка logo.bmp должна лежать рядом с исходниками (или указать полный путь).


Вариант 2: PNG с прозрачными краями на форме (через GDI+)

Если нужны прозрачные края — BMP не годится: нужен PNG и GDI+.

Важное: GDI+ в чистом C

GDI+ API сделан под C++, но его можно использовать из C: вместо классов используем указатели на интерфейсы и вызовы через таблицы функций. Для простоты покажу вариант с использованием C++ синтаксиса только для инициализации GDI+ и работы с Image/Graphics, но логика остаётся максимально близкой к C‑подходу. Если критично «строго C без C++», можно использовать AlphaBlend с 32‑bit DIB вручную, но это заметно сложнее.

gdiplus_img.c (упрощённо)

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <gdiplus.h>

#pragma comment(lib, "gdiplus.lib")

ULONG_PTR gdiToken;
Gdiplus::Image *pImage = NULL;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nShowCmd)
{
    GdiplusStartupInput input = {1};
    GdiplusStartup(&gdiToken, &input, NULL);

    // PNG с прозрачностью
    pImage = Gdiplus::Image::FromFile(L"logo.png");

    const char CLASS_NAME[] = "GdiPlusImg";
    WNDCLASS wc = {0};
    wc.lpfnWndProc = [](HWND h, UINT m, WPARAM w, LPARAM l) -> LRESULT {
        if (m == WM_PAINT) {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(h, &ps);
            Gdiplus::Graphics g(hdc);
            if (pImage) g.DrawImage(pImage, 50, 50);
            EndPaint(h, &ps);
            return 0;
        } else if (m == WM_DESTROY) {
            delete pImage;
            GdiplusShutdown(gdiToken);
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(h, m, w, l);
    };

    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    RegisterClass(&wc);

    CreateWindowEx(0, CLASS_NAME, "PNG с прозрачностью",
                   WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                   640, 480, NULL, NULL, hInstance, NULL);

    ShowWindow(GetLastActivePopup(), nShowCmd);
    UpdateWindow(GetLastActivePopup());

    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

Сборка:

cl /TP gdiplus_img.c /link gdiplus.lib user32.lib gdi32.lib

/TP — компилировать как C++ (нужно для GDI+ классов). Если принципиально только /TC (чистый C), тогда вместо GDI+ придётся вручную готовить 32‑bit DIB с альфа‑каналом и вызывать AlphaBlend.

PNG logo.png должен лежать рядом с .exe и иметь альфа‑канал.


Что выбрать под твои задачи

  • Если ты делаешь шахматную программу (учитывая прошлые вопросы про битборды, Syzygy и т. д.) и нужна, например, простая иконка/логотип на форме без сложной графики — Вариант 1 (BMP + BitBlt) проще, быстрее и полностью на чистом C.

  • Если нужны красивые картинки с прозрачными краями (например, фигуры или декоративные элементы) — Вариант 2 (GDI+).


Практические советы

  • Формат BMP для Варианта 1: лучше 24‑bit или 32‑bit BMP без сжатия; не используй PNG/JPG напрямую в ресурсе — BITMAP в .rc работает только с BMP.

  • Позиционирование: в BitBlt меняй координаты (50, 50) на любые нужные. Чтобы центрировать картинку, считай размеры окна в WM_SIZE и перерисовывай.

  • Ресурсы vs файлы: в Варианте 1 картинка встроена в .exe; в Варианте 2 (GDI+) проще читать файл с диска — так удобнее менять картинки без пересборки.


Учитывая твой интерес к сборке через cl.exe, ресурсам и «чистому C», я могу дать полностью C‑версию с AlphaBlend (без GDI+), если скажешь, что важнее: «строго C» или «максимальная простота кода». Что ближе?



Да, в Win32 на чистом C можно:

  • обрабатывать события мыши (WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP и т. д.);

  • обрабатывать нажатия клавиш (WM_KEYDOWN, WM_CHAR);

  • рисовать PNG с альфа‑каналом (прозрачностью) — но для этого почти всегда используют GDI+ либо AlphaBlend с 32‑bit DIB.

Учитывая твой стек (чистый C, cl.exe, шахматные задачи), покажу рабочий пример, где:

  • рисуется PNG с прозрачностью через GDI+;

  • отслеживаются клики мыши и нажатия клавиш;

  • всё собрано одной командой через cl.exe.


Полный пример: окно, PNG с прозрачностью, события мыши и клавиатуры

main.c

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <gdiplus.h>

#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")

ULONG_PTR gdiToken;
Gdiplus::Image *pImage = NULL;

// Глобальные переменные для демонстрации реакции на ввод
int lastX = 0, lastY = 0;
char lastKey = 0;
BOOL mouseDown = FALSE;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg) {
        case WM_CREATE:
            // Инициализация GDI+ уже сделана в WinMain
            return 0;

        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hwnd, &ps);

            if (pImage) {
                Gdiplus::Graphics g(hdc);
                // Рисуем PNG с прозрачностью
                g.DrawImage(pImage, 50, 50);
            }

            EndPaint(hwnd, &ps);
            return 0;
        }

        // --- События мыши ---
        case WM_LBUTTONDOWN: {
            lastX = LOWORD(lParam);
            lastY = HIWORD(lParam);
            mouseDown = TRUE;
            // Можно что-то сделать при клике
            InvalidateRect(hwnd, NULL, FALSE); // перерисовать, если нужно показать эффект
            return 0;
        }
        case WM_MOUSEMOVE: {
            if (mouseDown) {
                lastX = LOWORD(lParam);
                lastY = HIWORD(lParam);
                InvalidateRect(hwnd, NULL, FALSE);
            }
            return 0;
        }
        case WM_LBUTTONUP: {
            mouseDown = FALSE;
            return 0;
        }

        // --- События клавиатуры ---
        case WM_KEYDOWN: {
            // wParam = виртуальный код клавиши (VK_*)
            // Например, VK_ESCAPE, VK_LEFT, VK_A и т.д.
            // Здесь можно реагировать на конкретные клавиши
            return 0;
        }
        case WM_CHAR: {
            // wParam = ASCII-код символа (для печатаемых символов)
            lastKey = (char)wParam;
            InvalidateRect(hwnd, NULL, FALSE);
            return 0;
        }

        case WM_DESTROY: {
            delete pImage;
            GdiplusShutdown(gdiToken);
            PostQuitMessage(0);
            return 0;
        }
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nShowCmd)
{
    // Инициализация GDI+
    GdiplusStartupInput input = {1};
    GdiplusStartup(&gdiToken, &input, NULL);

    // Загрузка PNG с прозрачностью (альфа‑канал)
    pImage = Gdiplus::Image::FromFile(L"logo.png");

    const char CLASS_NAME[] = "ImgInputDemo";

    WNDCLASS wc = {0};
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, "PNG + мышь + клава",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 640, 480,
        NULL, NULL, hInstance, NULL
    );

    if (!hwnd) return 0;

    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

Как собрать

cl /TP main.c /link gdiplus.lib user32.lib gdi32.lib

/TP обязателен: GDI+ в стандартной поставке — это C++ API (классы Image, Graphics). Если принципиально только чистый C без C++ синтаксиса, тогда вместо GDI+ используют AlphaBlend и 32‑bit DIB вручную — код будет заметно длиннее.

Рядом с .exe должен лежать logo.png с альфа‑каналом.


Какие события реально пригодятся для шахматной программы

Учитывая твои прошлые задачи (битборды, Syzygy, SEE, движок):

  • WM_LBUTTONDOWN — самый нужный: по нему определяют, куда кликнул пользователь, берут координаты, переводят в клетку доски и генерируют ходы.

  • WM_MOUSEMOVE — можно подсвечивать клетку под курсором или показывать возможные ходы «в предпросмотре».

  • WM_KEYDOWN — например, VK_ESCAPE для выхода, VK_F2 для переключения режима, стрелки для навигации по истории ходов и т. п.

  • WM_CHAR — если делаешь ввод текста (комментарии, PGN и т. д.).

Координаты мыши приходят в lParam:

int x = LOWORD(lParam);
int y = HIWORD(lParam);

Чтобы превратить их в клетку доски, обычно делают:

int cellX = (x - boardX) / cellSize;
int cellY = (y - boardY) / cellSize;

Если нужна строго чистая C‑реализация без C++ (без GDI+)

Тогда для PNG/альфы используют AlphaBlend из msimg32.lib и 32‑bit DIB с альфа‑каналом:

  1. Читают PNG (или заранее готовят 32‑bit BMP с альфа‑каналом).

  2. Создают совместимый DC и битмап.

  3. Вызывают AlphaBlend(&BLENDFUNCTION, ...).

Это полностью на C, но требует аккуратной работы с заголовками DIB и выравниванием строк. Если скажешь, что важнее — «строго C» или «минимум кода» — могу дать вариант именно с AlphaBlend.


Практические советы под твой стек

  • PNG с прозрачностью — самый простой путь: GDI+ (DrawImage).

  • Мышь — чаще всего нужен именно клик (WM_LBUTTONDOWN) и координаты.

  • Клава — для горячих клавиш и отладки (например, «показать битборд», «переключить движок»).

  • Ресурсы — картинку можно держать как файл рядом с .exe (удобно менять без пересборки), либо встроить в ресурс как RCDATA и читать из него.

Если скажешь, какой именно сценарий тебе нужен (например: «клик по клетке — подсветить возможные ходы», или «нажал F1 — показать подсказку», или «строго C без C++»), дам адаптированный фрагмент под эту задачу.

Комментариев нет:

Отправить комментарий