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

User.32 dll - узнать реализацию функции


Antonshka

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

Привет, кто-нибудь пробовал такое?

Есть функция DrawIconEx в User.32 dll. Нужно узнать как она работает изнутри. Как она обрабатывает маску иконки и само изображение иконки.

 

Зачем это нужно? Дело в том что DrawIconEx корректно рисует иконку. Но если пробовать сделать тоже самое вручную, через BitBlt, как например это описано у Фень, Щупака, на MSDN, то получаются артефакты.

Спойлер
HDC hdcColor = CreateCompatibleDC(compatibleDC.origDC);
HDC hdcMask = CreateCompatibleDC(compatibleDC.origDC);
HBITMAP prevColor = reinterpret_cast<HBITMAP>(GetCurrentObject(hdcColor, OBJ_BITMAP));
HBITMAP prevMask = reinterpret_cast<HBITMAP>(GetCurrentObject(hdcMask, OBJ_BITMAP));
HBITMAP color = CreateCompatibleBitmap(compatibleDC.origDC, 32, 32);
HBITMAP mask = CreateCompatibleBitmap(compatibleDC.origDC, 32, 32);
HICON icon1 = reinterpret_cast<HICON>(LoadImageW(WGW::hInstance, MAKEINTRESOURCE(_IDI_FRUT), IMAGE_ICON, 32, 32, 0));

SelectObject(hdcColor, color);
SelectObject(hdcMask, mask);
ICONINFO iconInfo{ 0 };
BITMAP bitmap{ 0 };

GetIconInfo(icon1, &iconInfo);
GetObjectW(iconInfo.hbmMask, sizeof(bitmap), &bitmap);

SelectObject(hdcColor, iconInfo.hbmMask);
BitBlt(compatibleDC.origDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcColor, 0, 0, SRCAND);
SelectObject(hdcMask, iconInfo.hbmColor);
BitBlt(compatibleDC.origDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMask, 0, 0,	SRCINVERT);

DrawIconEx(compatibleDC.origDC, 50, 0, icon1, 32, 32, NULL, NULL, DI_MASK | DI_IMAGE);

 

 

Спойлер

spacer.png

 

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

Загрузил User.32 dll в Ghydra.

DrawIconEx из User.32 dll вызывает NtUserDrawIconEx в win32u.dll, предварительно записав все нужные параметры в регистры и стек.

NtUserDrawIconEx из win32u.dll вызывает syscall.

Спойлер
//win32u.dll
undefined8 NtUserDrawIconEx(void)

{
  code *pcVar1;
  undefined8 uVar2;
  
                    /* 0x1ec0  883  NtUserDrawIconEx */
  if ((DAT_7ffe0308 & 1) == 0) {
    syscall();
    return 0x105a;
  }
  pcVar1 = (code *)swi(0x2e);
  uVar2 = (*pcVar1)();
  return uVar2;
}

 

А дальше все, тайна кода ядра.

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

HDC hdcColor = CreateCompatibleDC(compatibleDC.origDC);
HDC hdcMask = CreateCompatibleDC(compatibleDC.origDC);
HBITMAP prevColor = reinterpret_cast<HBITMAP>(GetCurrentObject(hdcColor, OBJ_BITMAP));
HBITMAP prevMask = reinterpret_cast<HBITMAP>(GetCurrentObject(hdcMask, OBJ_BITMAP));
HBITMAP color = CreateCompatibleBitmap(compatibleDC.origDC, 32, 32);
HBITMAP mask = CreateCompatibleBitmap(compatibleDC.origDC, 32, 32);
HICON icon1 = reinterpret_cast<HICON>(LoadImageW(WGW::hInstance, MAKEINTRESOURCE(_IDI_FRUT), IMAGE_ICON, 32, 32, 0));

SelectObject(hdcColor, color);
SelectObject(hdcMask, mask);
ICONINFO iconInfo{ 0 };
BITMAP bitmap{ 0 };

GetIconInfo(icon1, &iconInfo);
GetObjectW(iconInfo.hbmMask, sizeof(bitmap), &bitmap);

SelectObject(hdcColor, iconInfo.hbmMask);
BitBlt(compatibleDC.origDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcColor, 0, 0, SRCAND);
SelectObject(hdcMask, iconInfo.hbmColor);
BitBlt(compatibleDC.origDC, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcMask, 0, 0,	SRCINVERT);

DrawIconEx(compatibleDC.origDC, 50, 0, icon1, 32, 32, NULL, NULL, DI_MASK | DI_IMAGE);

 

HBITMAP prevColor = reinterpret_cast<HBITMAP>(GetCurrentObject(hdcColor, OBJ_BITMAP)); 
HBITMAP prevMask = reinterpret_cast<HBITMAP>(GetCurrentObject(hdcMask, OBJ_BITMAP));

Зачем это предварительное расшаркивание? SelectObject возвращает замещенный объект

Дальше. Если мне не изменяет память, при создании GDI структур их нельзя вчистую нулями заполнять. У большинства из них есть поле dwSize, которое нужно заполнять, чтобы функции, использующие эти структуры, корректно отрабатывали. Но тут это не точно.

Для загрузки иконок вроде как есть LoadIcon, заточенный специально под иконки

Ну а артефакты говорят о том, что ты неправильно работаешь с маской. В случае с иконками, помнится, использовал режим SRCPAINT. Но это для старых иконок, где фон, по сути, был черным. 

По остальному - на выхах, если будет время, попробую освежить контекст в голове по рисованию с GDI. Но что-то ты явно с маской делаешь не так.

 

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

7 часов назад, Xipho сказал:

Зачем это предварительное расшаркивание? SelectObject возвращает замещенный объект

Это от привычки. Бывали случаи что новые GDI объекты выбирались мною в DC не сразу. Плюс чтобы просто не запутаться. Взял один раз все стандартные объекты DC, и далее уже не задумываясь выбираешь свои собственные. Потом в конце восстанавливаешь.

 

7 часов назад, Xipho сказал:

Дальше. Если мне не изменяет память, при создании GDI структур их нельзя вчистую нулями заполнять. У большинства из них есть поле dwSize, которое нужно заполнять, чтобы функции, использующие эти структуры, корректно отрабатывали. Но тут это не точно.

У меня VS ругается, если нет инициализации. Я все переменные инициализирую.

А так, да, MSDN обычно пишет в описании функций что параметр-структура должен быть с заполненным полем cbSize. Главное не проморгать.

 

7 часов назад, Xipho сказал:

Для загрузки иконок вроде как есть LoadIcon, заточенный специально под иконки

LoadIcon я использовал на первых порах. У нее есть ограничения на размер иконки. Плюс она замещена на LoadImage.

Note  This function has been superseded by the LoadImage function.

 

7 часов назад, Xipho сказал:

Ну а артефакты говорят о том, что ты неправильно работаешь с маской. В случае с иконками, помнится, использовал режим SRCPAINT. Но это для старых иконок, где фон, по сути, был черным.

Для старых иконок используется тоже AND и XOR, то-есть SRCAND и SRCINVERT. Raymond Chen

Вообще все, Фень, Щупак, MSDN, Raymond, пишут что AND и XOR. Но видимо есть еще какой-то хитрый момент.

 

8 часов назад, Xipho сказал:

По остальному - на выхах, если будет время, попробую освежить контекст в голове по рисованию с GDI. Но что-то ты явно с маской делаешь не так.

Я тоже, так просто эту иконку я не оставлю. Raymond Chen написал четыре хорошие статьи по иконкам, нужно почитать.

 

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

Иконка рисовалась с артефактами из-за того что ее Bitmap использует альфа-канал.

 

Иконка, которую я тестировал, имеет 32 бита на пиксель, - 32bpp. Если бы эта иконка имела 24bpp, то для ее рисования нужно было бы использовать функцию BitBlt. Вначале для маски, с SRCAND, затем для изображения, с SRCINVERT. Но так как моя иконка 32bpp, с альфа каналом для каждого пикселя, то для ее вывода нужно использовать AlphaBlend(). Ее использует и DrawIconEx.

Фукнция AlphaBlend не использует маску иконки вообще. Также эта функция ожидает что значение пикселя является premultiplied.

Спойлер

Note that the APIs use premultiplied alpha, which means that the red, green and blue channel values in the bitmap must be premultiplied with the alpha channel value. For example, if the alpha channel value is x, the red, green and blue channels must be multiplied by x and divided by 0xff prior to the call.

 

Если значение пикселя будет не premultiplied, тогда получатся артефакты.

Существует всего два вида представления пикселя с альфа каналом. Premultiplied и нет. Моя иконка была без "pre". Стоило мне перевести каждый ее пиксел в pre, как все артефакты исчезли.

Я пробовал создавать свою собственную иконку в программе IcoFx. В формате 32bpp с альфа. Но эта программа сохраняет иконку без "pre". Что также сбило меня с толку.

Моя тестовая иконка была скачана с сайта иконок. Получается что они там все без "pre".

 

Примечательно, что точно определить является ли иконка "pre" или не "pre" невозможно. Можно лишь сделать предположение. По видимому DrawIconEx этим и занимается.

 

R = R * A / 255

G = G * A / 255

B = B * A / 255

Исходя из этой формулы преобразования пикселя из не "pre" в "pre", можно точно сказать что R и B и G всегда меньше A (alpha). Значит, скорее всего, эта иконка с "pre". Никакого преобразования в "pre" делать не нужно. Перед вызовом функции AlphaBlend.

Но если мы пройдя по каждому пикселю обнаружим что хотя бы один пиксель имеет значение R или B или G канала большим чем A, то скорее всего эта иконка не "pre".

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

16 минут назад, Xipho сказал:

Хорошее исследование провел, а у меня так руки и не дошли за эти выходные

А я и вовсе уже как две недели занимаюсь всем чем ни попадя. Но только не библиотекой.

То почитаю это, то почитаю то. То поиграю с иконками, то с файлами .bmp.

Иной раз досадно, что работа так часто приостанавливается. Но, программа для человека, а не человек для программы.

 

У тебя на видео ты изменяешь SourceConstantAlpha глобально, применительно ко всей картинке. А я бы еще хотел протестировать альфа смешивание считающееся с альфа каналом каждого пикселю. Чтобы в итоге выглядело как например PNG картинки наложенные друг на друга.

Тебе не приходилось такое проворачивать?

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

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

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

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