Перейти к содержанию

Конвертация цветной иконки в черно-белую (WinAPI)


Рекомендуемые сообщения

Привет всем, никто не сталкивался с такой задачей?

Пользователь, для пункта меню, предоставляет только цветную версию иконки (для удобства). Метод "Добавить пункт" класса "Меню", автоматически создает ее черно-белую версию. Она будет рисоваться когда пункт меню будет выключен.

 

DrawState из WinAPI хоть и умеет делать из цветной иконки черно-белую, но результат не всегда хороший.

Поэтому сейчас ищу наиболее лучший и быстрый способ конвертации. Основная идею конвертации это изменение RGB значения каждого пикселя изображения на определенное значение.

Как появится первый рабочий вариант, то обязательно его выложу.

Ссылка на комментарий
Поделиться на другие сайты

24 минуты назад, youneuoy сказал:

Из первой же ссылки в гугле:

Это верно.

Мне был интересен сам подход. Самый быстрый и лучший. Так как я полагал что существует несколько подходов. Через CompatibleDC например, или возможно напрямую как-нибудь.

Ссылка на комментарий
Поделиться на другие сайты

23 минуты назад, Antonshka сказал:

Через CompatibleDC например, или возможно напрямую как-нибудь.

ничего про это не знаю. Обычная конвертация так то довольно быстро должна произойти. У тебя ведь иконки, а не огромные картины.

Ссылка на комментарий
Поделиться на другие сайты

21 час назад, Antonshka сказал:

Мне был интересен сам подход

Блиттинг с серой маской.

Или средневзвешенное значение цвета. Правда, может дать непредсказуемый результат

Но самое быстрое и эффективное - применение формулы выше для картинки, загруженной в DIB секцию.

Впрочем, картинку необязательно грузить в вышеупомянутую секцию, так как есть WinAPI функции для получения битов картинки в массив для последующей быстрой обработки

Ссылка на комментарий
Поделиться на другие сайты

В 26.06.2022 в 18:31, Xipho сказал:

Блиттинг с серой маской.

Или средневзвешенное значение цвета. Правда, может дать непредсказуемый результат

Но самое быстрое и эффективное - применение формулы выше для картинки, загруженной в DIB секцию.

Впрочем, картинку необязательно грузить в вышеупомянутую секцию, так как есть WinAPI функции для получения битов картинки в массив для последующей быстрой обработки

Последний вариант мне пригляделся больше. Ты имел ввиду GetDIBits функцию?

 

Я пытаюсь сделать меню как в Maya 3D, по внешнему виду и функционалу. Там оказывается при наведении на пункт меню иконка еще и подсвечивается, то есть становиться ярче.

В интернете нашел две теории как сделать иконку ярче. Первая это через умножение каждого канала на множитель. Вторая это перевод RGB в HSV, затем изменение V (яркости), и затем обратный перевод HSV в RGB.

С HSV не вышло, даже без изменения V некоторые цвета становятся другими. Я пробовал два разных исходника. Результат один и тот же.

Пришлось использовать множитель.  С ним цвета нормальные.

 

Это некоторые методы для иконки

Спойлер
//Public method.
VOID PopupMenu::AddItem(MenuItemEnableState enableState, MenuItemType itemType, MenuItemCheckableState checkableState, MenuItemCheckedState checkedState, MenuItemCheckMarkType checkMarkType, DWORD iconID, std::wstring text, std::wstring hotkeysText, OnEvent itemClickEvent, PopupMenu* popupMenu, MenuItemSettingsButtonState settingsButtonState, OnEvent settingsButtonClickEvent)
{
  HICON icon = NULL;
  if (iconID)
    icon = reinterpret_cast<HICON>(LoadImageW(WGW::hInstance, MAKEINTRESOURCE(iconID), IMAGE_ICON,
                                              GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0));
  AddItem(enableState, itemType, checkableState, checkedState, checkMarkType, icon, text, hotkeysText, itemClickEvent, popupMenu, settingsButtonState, settingsButtonClickEvent);
}

//Public method.
VOID PopupMenu::AddItem(MenuItemEnableState enableState, MenuItemType itemType, MenuItemCheckableState checkableState, MenuItemCheckedState checkedState, MenuItemCheckMarkType checkMarkType, std::wstring iconPath, std::wstring text, std::wstring hotkeysText, OnEvent itemClickEvent, PopupMenu* popupMenu, MenuItemSettingsButtonState settingsButtonState, OnEvent settingsButtonClickEvent)
{
  HICON icon = NULL;
  if (iconPath.size())
    icon = reinterpret_cast<HICON>(LoadImageW(NULL, iconPath.data(), IMAGE_ICON,
                                              GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
  AddItem(enableState, itemType, checkableState, checkedState, checkMarkType, icon, text, hotkeysText, itemClickEvent, popupMenu, settingsButtonState, settingsButtonClickEvent);
}

//Private method.
VOID PopupMenu::AddItem(MenuItemEnableState enableState, MenuItemType itemType, MenuItemCheckableState checkableState, MenuItemCheckedState checkedState, MenuItemCheckMarkType checkMarkType, HICON icon, std::wstring text, std::wstring hotkeysText, OnEvent itemClickEvent, PopupMenu* popupMenu, MenuItemSettingsButtonState settingsButtonState, OnEvent settingsButtonClickEvent)
{
  WindowPropsMenuItem* item = new WindowPropsMenuItem();
  items.push_back(item);
  item->enableState = enableState;
  item->type = itemType;
  item->checkableState = checkableState;
  item->checkedState = checkedState;
  item->checkMarkType = checkMarkType;
  if (icon)
  {
    item->coloredIcon = icon;
    CreateBrighterIconAndGrayscaleIcon(item);
  }
  item->text = text;
  item->hotkeysText = hotkeysText;
  item->clickEvent = itemClickEvent;
  item->popupMenu = popupMenu;
  item->settingsButtonState = settingsButtonState;
  item->settingsButtonClickEvent = settingsButtonClickEvent;
  CalculatePopupMenuSize();
  CalculatePopupMenuItemsRects();
}

//Private method.
VOID PopupMenu::CreateBrighterIconAndGrayscaleIcon(WindowPropsMenuItem* item)
{
  INT brightnessIncreased = 0;
  FLOAT brightnessIncreaseDegree = 1.2;
  std::unique_ptr<BYTE[]> bytesForBrighterIcon;
  std::unique_ptr<BYTE[]> bytesForGrayscaleIcon;
  INT bytesOffset = 0;
  INT bytesPerPixel = 0;
  INT bitsStride = 0;
  BITMAP coloredIconBitmap{ 0 };
  ICONINFO coloredIconInfo{ 0 };
  DWORD coloredIconSizeInBytes = 0;
  BITMAPINFO desiredBitmapInfo{ 0 };
  HDC desktopDC = NULL;
  BYTE grayscale = 0;
  GetIconInfo(item->coloredIcon, &coloredIconInfo);
  if (coloredIconInfo.hbmColor)
  {
    GetObjectW(coloredIconInfo.hbmColor, sizeof(BITMAP), &coloredIconBitmap);
    if (coloredIconBitmap.bmBitsPixel >= 24)
    {
      desktopDC = GetDC(HWND_DESKTOP);
      coloredIconSizeInBytes = ((coloredIconBitmap.bmWidth * coloredIconBitmap.bmBitsPixel + 31) / 32) * 4 * coloredIconBitmap.bmHeight;
      desiredBitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
      desiredBitmapInfo.bmiHeader.biWidth = coloredIconBitmap.bmWidth;
      desiredBitmapInfo.bmiHeader.biHeight = coloredIconBitmap.bmHeight;
      desiredBitmapInfo.bmiHeader.biPlanes = 1;
      desiredBitmapInfo.bmiHeader.biBitCount = coloredIconBitmap.bmBitsPixel;
      desiredBitmapInfo.bmiHeader.biCompression = BI_RGB;
      bytesPerPixel = coloredIconBitmap.bmBitsPixel / 8;
      bitsStride = coloredIconBitmap.bmWidth + (coloredIconBitmap.bmWidth * bytesPerPixel) % 4;
      bytesForBrighterIcon.reset(new BYTE[coloredIconSizeInBytes]);
      bytesForGrayscaleIcon.reset(new BYTE[coloredIconSizeInBytes]);
      GetDIBits(desktopDC, coloredIconInfo.hbmColor, 0, coloredIconBitmap.bmHeight, bytesForBrighterIcon.get(), &desiredBitmapInfo, DIB_RGB_COLORS);
      GetDIBits(desktopDC, coloredIconInfo.hbmColor, 0, coloredIconBitmap.bmHeight, bytesForGrayscaleIcon.get(), &desiredBitmapInfo, DIB_RGB_COLORS);
      for (INT y = 0; y < coloredIconBitmap.bmHeight; y++)
      {
        for (INT x = 0; x < bitsStride; x++)
        {
          bytesOffset = (x + y * bitsStride) * bytesPerPixel;
          brightnessIncreased = bytesForBrighterIcon[bytesOffset + 0] * brightnessIncreaseDegree;
          if (brightnessIncreased > 255)
            brightnessIncreased = 255;
          if (brightnessIncreased < 0)
            brightnessIncreased = 0;
          bytesForBrighterIcon[bytesOffset + 0] = brightnessIncreased;
          brightnessIncreased = bytesForBrighterIcon[bytesOffset + 1] * brightnessIncreaseDegree;
          if (brightnessIncreased > 255)
            brightnessIncreased = 255;
          if (brightnessIncreased < 0)
            brightnessIncreased = 0;
          bytesForBrighterIcon[bytesOffset + 1] = brightnessIncreased;
          brightnessIncreased = bytesForBrighterIcon[bytesOffset + 2] * brightnessIncreaseDegree;
          if (brightnessIncreased > 255)
            brightnessIncreased = 255;
          if (brightnessIncreased < 0)
            brightnessIncreased = 0;
          bytesForBrighterIcon[bytesOffset + 2] = brightnessIncreased;
        }
      }
      for (INT y = 0; y < coloredIconBitmap.bmHeight; y++)
      {
        for (INT x = 0; x < bitsStride; x++)
        {
          bytesOffset = (x + y * bitsStride) * bytesPerPixel;
          grayscale = (50 + 11 * bytesForGrayscaleIcon[bytesOffset + 0] + 59 * bytesForGrayscaleIcon[bytesOffset + 1] + 30 * bytesForGrayscaleIcon[bytesOffset + 2]) / 100;
          bytesForGrayscaleIcon[bytesOffset + 0] = bytesForGrayscaleIcon[bytesOffset + 1] = bytesForGrayscaleIcon[bytesOffset + 2] = grayscale;
        }
      }
      SetDIBits(desktopDC, coloredIconInfo.hbmColor, 0, coloredIconBitmap.bmHeight, bytesForBrighterIcon.get(), &desiredBitmapInfo, DIB_RGB_COLORS);
      item->brighterIcon = CreateIconIndirect(&coloredIconInfo);
      SetDIBits(desktopDC, coloredIconInfo.hbmColor, 0, coloredIconBitmap.bmHeight, bytesForGrayscaleIcon.get(), &desiredBitmapInfo, DIB_RGB_COLORS);
      item->grayscaleIcon = CreateIconIndirect(&coloredIconInfo);
      ReleaseDC(HWND_DESKTOP, desktopDC);
    }
  }
}

 

 

 

 

Изменено пользователем Antonshka
Ссылка на комментарий
Поделиться на другие сайты

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.