17 января 2007

Painting of disabled icons

Не найдя стандартных путей рисования иконки в "disabled" состоянии, пришлось изобретать собственный велосипед. Windows предлагает сделать подобное либо имея static control с иконкой и задав ему EnableWindow(FALSE), либо имея ImageList и рисуя уже из него. Мне же нужен был простой способ отрисовать HICON на HDC не плодя при этом control-ов и ImageList-ов.

Велосипед довольно успешно был собран из подручных компонентов - функций WinAPI и небольшой приправы из цикла по перемалыванию байтов:
void DrawDisabledIcon(HDC DC, CRect& Rect, WTL::CIcon& Icon)
{
WTL::CDC MemDC(CreateCompatibleDC(DC));

BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = Rect.Width();
bmi.bmiHeader.biHeight = Rect.Height();
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * 4;

VOID *pvBits;
WTL::CBitmap Bitmap(::CreateDIBSection(MemDC, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0));
WTL::CBitmapHandle PrevBitmap(MemDC.SelectBitmap(Bitmap));

Icon.DrawIconEx(MemDC, 0, 0, bmi.bmiHeader.biWidth, bmi.bmiHeader.biWidth);

// convert to grayscale
for (unsigned char *p = (unsigned char*)pvBits, *end = p + bmi.bmiHeader.biSizeImage; p < end; p += 4)
{
// Gray = 0.3*R + 0.59*G + 0.11*B
p[0] = p[1] = p[2] =
(
static_cast<unsigned int>(p[2]) * 77 +
static_cast<unsigned int>(p[1]) * 151 +
static_cast<unsigned int>(p[0]) * 28
) >> 8;
}

BLENDFUNCTION BlendFunction;
BlendFunction.BlendOp = AC_SRC_OVER;
BlendFunction.BlendFlags = 0;
BlendFunction.SourceConstantAlpha = 0x60; // half transparent
BlendFunction.AlphaFormat = AC_SRC_ALPHA; // use bitmap alpha

AlphaBlend(DC, Rect.left, Rect.top, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight,
MemDC, 0, 0, bmi.bmiHeader.biWidth, bmi.bmiHeader.biHeight, BlendFunction);

MemDC.SelectBitmap(PrevBitmap);
}
Методика проста - отрисовываем иконку во временный буфер, переводим ее там в gray scale и выводим через AlphaBlend(), так как сама иконка содержит в себе alpha-канал. При AlphaBlend-е можно еще подправить ее прозрачность, задав SourceConstantAlpha.

08 января 2007

Case insensitive search performance

Сегодня оптимизировал одно из узких мест в проекте: case insensetive поиск подстроки в строке - то есть не различая регистр.

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

Провел сравнение скорости разных способов проверки вхождения подстроки в строку.
За единицу скорости принял std::wstring::find() - она хотя не делает того, что нужно (не регистро-независимая), но в качестве эталона скорости - самое оно. Тестировал в VC++ 7.1 release mode с включенной оптимизацией по скорости.

Аналог std::wstring::find() из буста - boost::find_first() - оказался в 2,5 раза медленнее эталона.

Та функция из буста, которая делает то, что мне нужно - boost::ifind_first() - в 70 раз медленнее эталона. В коде проекта как раз она и использовалась - отсюда и дикие тормоза.

Первая моя вариация была такой: делаем копии строк, затем применяем к ним boost::to_upper(), затем - std::wstring::find(). Результат - в 51 раз медленнее эталона. Но это уже заметно быстрее boost::ifind_first().

Следующая версия - создание глобальной таблицы wchar_t ToUpper[0x10000], содержащая для каждого символа его upper case варианта. Используя эту таблицу внутри предиката для функции boost::first_finder(), получил версию, всего в 2 раза медленнее эталона!

При нежелании тратить 128Kb на подобную таблицу, ее можно сократить до 2Kb, просчитывая только ее начало и используя подобным образом: (c <= 0x451) ? ToUpper[c] : std::toupper(c, loc). В таком случае скорость получилась в 5 раз медленнее эталона.

Выбрана была последняя версия с буфером 2Kb, куда влезает вся латиница и кириллица. Дальнейшая оптимизация свелась к тому, что искомая подстрока переводилась в upper case всего один раз, а ToUpper[] применялась только к строкам в которых шел поиск. Этот финт, правда, почти не сказался на скорости.