15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


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

"Прозрачной" называется та часть картинки, которая не изменяет содержимого экрана. Например, функция 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); // Цвет для прозрачных
                                      // пикселей (в данном случае
                                      // белый).