Как рисовать прозрачные битмапы
"Прозрачной" называется та часть картинки, которая не изменяет содержимого экрана. Например, функция DrawIcon может создавать изображения с прозрачностью. Так же можно добиться прозрачности при помощи функции BitBlt, однако прийдётся воспользоваться дополнительным кодом.
Сперва необходимо получить содержимое области, где будет нарисован битмап, чтобы сохранить это фоновое изображение в памяти контекста устройства (DC). Далее накладывается маска на область фона, которая отвечает за непрозрачную часть битмапа, а также на все прозрачные пиксели битмапа. Потом, используется растровая операция XOR, чтобы совместить битмап картинки с битмапом фоновой картнки. В заключении, при помощи BitBlt, совмещённая картинка помещается в DC.
Теперь более подробно рассмотрим процесс рисования прозрачных битмапов:
- Создаём DC для хранения битмапа.
- В DC выбираем битмап.
- Создаём в памяти DC для хранения конечной картинки. В него будем выводить выводить конечную картинку.
- Копируем часть экрана, на которое будет наложена картинка в конечный DC.
- Создаём "маску AND", которая содержит маску цветов для рисования (непрозрачная часть картинки). Для этого делаем следующие шаги:
- Устанавливаем фоновый цвет в DC в цвет, который будет прозрачным в картинке.
- Создаём монохромный DC.
- Применяем BitBlt к картинке в монохромном DC.
Путём установки фонового цвета удовлетворяющих пикселей в белый(1), а всех остальных в чёрный(0), будет создана маска AND для битмапа.
- Используем BitBlt с растровой операцией SRCAND, чтобы скопировать маску AND на конечный DC.
- Используем BitBlt с растровой операцией SRCAND, чтобы скопировать инверсию маски AND в DC картинки.
- Используем BitBlt с растровой операцией SRCPAINT, чтобы скопировать DC картинки в конечный DC.
- Используем 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); // Цвет для прозрачных
// пикселей (в данном случае
// белый).