Как рисовать прозрачные битмапы

"Прозрачной" называется та часть картинки, которая не изменяет содержимого экрана. Например, функция DrawIcon может создавать изображения с прозрачностью. Так же можно добиться прозрачности при помощи функции BitBlt, однако прийдётся воспользоваться дополнительным кодом.

Сперва необходимо получить содержимое области, где будет нарисован битмап, чтобы сохранить это фоновое изображение в памяти контекста устройства (DC). Далее накладывается маска на область фона, которая отвечает за непрозрачную часть битмапа, а также на все прозрачные пиксели битмапа. Потом, используется растровая операция XOR, чтобы совместить битмап картинки с битмапом фоновой картнки. В заключении, при помощи BitBlt, совмещённая картинка помещается в DC.

Теперь более подробно рассмотрим процесс рисования прозрачных битмапов:

  1. Создаём DC для хранения битмапа.
  2. В DC выбираем битмап.
  3. Создаём в памяти DC для хранения конечной картинки. В него будем выводить выводить конечную картинку.
  4. Копируем часть экрана, на которое будет наложена картинка в конечный DC.
  5. Создаём "маску AND", которая содержит маску цветов для рисования (непрозрачная часть картинки). Для этого делаем следующие шаги:
    1. Устанавливаем фоновый цвет в DC в цвет, который будет прозрачным в картинке.
    2. Создаём монохромный DC.
    3. Применяем BitBlt к картинке в монохромном DC.

    Путём установки фонового цвета удовлетворяющих пикселей в белый(1), а всех остальных в чёрный(0), будет создана маска AND для битмапа.

  6. Используем BitBlt с растровой операцией SRCAND, чтобы скопировать маску AND на конечный DC.
  7. Используем BitBlt с растровой операцией SRCAND, чтобы скопировать инверсию маски AND в DC картинки.
  8. Используем BitBlt с растровой операцией SRCPAINT, чтобы скопировать DC картинки в конечный DC.
  9. Используем BitBlt, чтобы скопировать содержимое конечного DC в соответствующую часть экрана.

Следующая функция демонстрирует описанные выше шаги:

   void DrawTransparentBitmap(HDC hdc, HBITMAP hBitmap, short xStart,
short yStart, COLORREF cTransparentColor)
{
BITMAP bm;
COLORREF cColor;
HBITMAP bmAndBack, bmAndObject, bmAndMem, bmSave;
HBITMAP bmBackOld, bmObjectOld, bmMemOld, bmSaveOld;
HDC hdcMem, hdcBack, hdcObject, hdcTemp, hdcSave;
POINT ptSize;

hdcTemp = CreateCompatibleDC(hdc);
SelectObject(hdcTemp, hBitmap); // Выбираем битмап

GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&bm);
ptSize.x = bm.bmWidth; // Получаем ширину битмапа
ptSize.y = bm.bmHeight; // Получаем высоту битмапа
DPtoLP(hdcTemp, &ptSize, 1); // Конвертируем из координат
// устройства в логические
// точки

// Создаём несколько DC для хранения временных данных.
hdcBack = CreateCompatibleDC(hdc);
hdcObject = CreateCompatibleDC(hdc);
hdcMem = CreateCompatibleDC(hdc);
hdcSave = CreateCompatibleDC(hdc);

// Создаём битмап для каждого DC.

// Монохромный DC
bmAndBack = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);

// Монохромный DC
bmAndObject = CreateBitmap(ptSize.x, ptSize.y, 1, 1, NULL);

bmAndMem = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);
bmSave = CreateCompatibleBitmap(hdc, ptSize.x, ptSize.y);

// В каждом DC должен быть выбран объект битмапа для хранения
// пикселей.
bmBackOld = SelectObject(hdcBack, bmAndBack);
bmObjectOld = SelectObject(hdcObject, bmAndObject);
bmMemOld = SelectObject(hdcMem, bmAndMem);
bmSaveOld = SelectObject(hdcSave, bmSave);

// Устанавливаем режим маппинга.
SetMapMode(hdcTemp, GetMapMode(hdc));

// Сохраняем битмап, переданный в параметре функции, так как
// он будет изменён.
BitBlt(hdcSave, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCCOPY);

// Устанавливаем фоновый цвет (в исходном DC) тех частей,
// которые будут прозрачными.
cColor = SetBkColor(hdcTemp, cTransparentColor);

// Создаём маску для битмапа путём вызова BitBlt из исходного
// битмапа на монохромный битмап.
BitBlt(hdcObject, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0,
SRCCOPY);

// Устанавливаем фоновый цвет исходного DC обратно в
// оригинальный цвет.
SetBkColor(hdcTemp, cColor);

// Создаём инверсию маски.
BitBlt(hdcBack, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0,
NOTSRCCOPY);

// Копируем фон главного DC в конечный.
BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdc, xStart, yStart,
SRCCOPY);

// Накладываем маску на те места, где будет помещён битмап.
BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcObject, 0, 0, SRCAND);

// Накладываем маску на прозрачные пиксели битмапа.
BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcBack, 0, 0, SRCAND);

// XOR-им битмап с фоном на конечном DC.
BitBlt(hdcMem, 0, 0, ptSize.x, ptSize.y, hdcTemp, 0, 0, SRCPAINT);

// Копируем на экран.
BitBlt(hdc, xStart, yStart, ptSize.x, ptSize.y, hdcMem, 0, 0,
SRCCOPY);

// Помещаем оригинальный битмап обратно в битмап, переданный в
// параметре функции.
BitBlt(hdcTemp, 0, 0, ptSize.x, ptSize.y, hdcSave, 0, 0, SRCCOPY);

// Удаляем битмапы из памяти.
DeleteObject(SelectObject(hdcBack, bmBackOld));
DeleteObject(SelectObject(hdcObject, bmObjectOld));
DeleteObject(SelectObject(hdcMem, bmMemOld));
DeleteObject(SelectObject(hdcSave, bmSaveOld));

// Удаляем DC из памяти.
DeleteDC(hdcMem);
DeleteDC(hdcBack);
DeleteDC(hdcObject);
DeleteDC(hdcSave);
DeleteDC(hdcTemp);
}

Следующий пример показывает как вызывать приведённую выше функцию DrawTransparentBitmap:

   DrawTransparentBitmap(hdc,         // Конечный DC.

hBitmap, // Битмап, который будет нарисован.
xPos, // координата X.
yPos, // координата Y.
0x00FFFFFF); // Цвет для прозрачных
// пикселей (в данном случае
// белый).