2013-07-30 4 views
9

В микшере громкоговорителя Windows, когда ваше приложение воспроизводит звуки, оно добавляет значок вашего приложения и пользовательский ползунок для регулировки тома, специфичного для этого приложения ... приятно! Однако при использовании значка большого размера для вашего приложения (особенно важно в высоком DPI, когда Windows масштабирует ваши значки на панели задач и т. Д.), Значок в Volume Mixer не масштабируется правильно. В частности, следующий код, что я использую, чтобы установить значок приложения:Размер значка объемного громкоговорителя Windows слишком большой

// set icons the normal way 
cWnd.SetIcon(theApp.LoadIcon(res_id), FALSE); 
cWnd.SetIcon(theApp.LoadIcon(res_id), TRUE); 

// set hi-res if available 
OSVERSIONINFO osv; 
osv.dwOSVersionInfoSize = sizeof(osv); 
if (GetVersionEx(&osv)) { 
    // if we're Vista or more recent, use hi-def icons 
    if (osv.dwMajorVersion >= 6) { 
     HICON hIcon = (HICON)::LoadImage(theApp.m_hInstance, MAKEINTRESOURCE(res_id), IMAGE_ICON, 256, 256, LR_SHARED); 
     if (hIcon) { 
      cWnd.SetIcon(hIcon, TRUE); 
     } 
    } 
} 

Виновником является «Hi-Res если доступно» часть. Если я включу это, значок панели задач выглядит великолепно, но Volume Mixer не масштабируется и выглядит ужасно. Если я исключаю, что панель задач значок выглядит плохо (страшное масштабирование), но Volume Mixer по крайней мере, это правильный размер:

Desktop Scaling 125% with 256x256 icon set Desktop Scaling 125% with regular icons

Кто-нибудь нашел решение, которое делает его так, чтобы иконки хорошо выглядеть ?

EDIT: В моем файле значков у меня есть следующие разрешения: 256x256, 48x48, 32x32, 24x24 и 16x16, все 32-разрядные. 256x256 один сжимается PNG, остальные - необработанные. Все размеры отлично смотрятся на разрешениях, которые они находятся в файле (я пытался разместить ICO здесь или в imgur, но, по-видимому, не допускал значков). Кроме того, я попытался включить некоторые 8-битные изображения, но это, похоже, не меняет ситуацию.

EDIT: Я использую GetDeviceCaps(hdc, LOGPIXELSX) (и Y) для определения масштаба рабочего стола. Обычно масштабирование рабочего стола составляет 100%, и я получаю нормальный результат 96. Но все больше и больше я вижу компьютеры по умолчанию 125%. Это можно изменить, щелкнув правой кнопкой мыши «Рабочий стол», «Персонализировать», другое: «Дисплей» ... там есть слайдер (для выхода требуется вход/выход для изменения).

EDIT: Я также хочу отметить, что лоток ICON страдает подобный вопрос масштабирования судьбы, когда в высоких DPI режимах (то есть, при использовании Shell_NotifyIcon). В этом случае, однако, я могу использовать GetDeviceCaps(hdc, LOGPIXELSX), чтобы определить, чего хочет Windows. Если у меня есть размер, предоставьте его напрямую, в противном случае укажите 256x256, а Windows сделает.

EDIT: Печаль возникает. Эта проблема может быть проблемой Windows. При захвате изображений в демонстрационных целях я заметил, что значок объемного микшера сам выглядит бедно. Для сравнения: Volume Mixer Comparison

FINAL EDIT: Как описано ниже, обходной путь для проблемы является масштабирование значков. Таким образом, окончательный код, который работает, чтобы загрузить указатель на функцию LoadIconWithScaleDown из Comctl32.dll (не показан) и использовать его, если он был доступен, или упасть обратно в «обычной/старым» способом:

HICON hIcon = 0; 
if (FAILED(comctl32Loader.LoadIconWithScaleDown(theApp.m_hInstance, MAKEINTRESOURCE(res_id), GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), &hIcon))) { 
    hIcon = theApp.LoadIcon(res_id); 
} 
cWnd.SetIcon(hIcon, FALSE); 
if (FAILED(comctl32Loader.LoadIconWithScaleDown(theApp.m_hInstance, MAKEINTRESOURCE(res_id), GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), &hIcon))) { 
    hIcon = theApp.LoadIcon(res_id); 
} 
cWnd.SetIcon(hIcon, TRUE); 

ответ

2

Да , Я могу воспроизвести описанную вами проблему, когда я использую тот же код, что и в вопросе. Как мы уже выяснили, проблема проявляется только при высоких настройках DPI. При 100% масштабировании (~ 96 точек на дюйм) даже в Windows Vista и позже вам нужно использовать только «большую» версию значка (SM_CXICON и SM_CYICON, обычно 32x32 пикселя) для окна, а не 256x256 пикселей. Это то, что делают приложения в комплекте с Windows, включая апплет Volume Mixer, с которым вы тестируете.

Проблема возникает, когда вы используете высокие настройки DPI, что делает «большой» размера идти вверх:

╔════════════╦═════════════════╦═════════════════╗ 
║ DPI  ║ Large Icon Size ║ Small Icon Size ║ 
║   ║ (SM_C?ICON) ║ (SM_C?SMICON) ║ 
╠════════════╬═════════════════╬═════════════════╣ 
║ 96 (100%) ║  32x32  ║  16x16  ║ 
║ 120 (125%) ║  40x40  ║  20x20  ║ 
║ 144 (150%) ║  48x48  ║  24x24  ║ 
║ 192 (200%) ║  64x64  ║  32x32  ║ 
╚════════════╩═════════════════╩═════════════════╝ 

вещь прекрасно работает независимо от DPI при загрузке значка 256х256 пикселя, так как для Windows является автоматически масштабируя его до требуемого размера. Это создает значок гораздо лучшего качества (без всех jaggies и других артефактов), чем попытка масштабирования до значок 32x32 пикселей. Итак, ваша догадка правильная, проблема действительно связана с масштабированием.

Я собираюсь предположить, что то, что вы видите в апплете Volume Mixer при использовании пиктограммы размером 256x256, является ошибкой - оно должно масштабировать этот большой значок до требуемого размера, что является " большой "значок (SM_C?ICON). Предположительно, он вызывает функцию DrawIconEx с параметрами cxWidth и cxHeight, установленными как 0, так и , а не, пропуская флаг DI_DEFAULTSIZE. Это приводит к тому, что значок будет рисоваться с использованием его фактического размера.

Вам придется вручную обходить проблему, самостоятельно масштабируя значки. К счастью, Windows Vista вводит ряд функций, специально разработанных для этой цели. Самый простой в использовании в этом случае - LoadIconWithScaleDown. Как и в случае с названием, он работает аналогично более старым функциям , но вместо того, чтобы масштабировать слишком маленькую иконку, она масштабирует более крупную иконку, идеально подходящую, когда у вас есть гигантский высококачественный значок 256x256 пикселей в ваш файл ICO.

К сожалению, эти функции недоступны в старых версиях Windows, что, вероятно, будет иметь такую ​​же проблему при использовании в более высоких настройках DPI. Вам нужно будет найти альтернативы там, или просто согласиться на зубчатые, масштабированные значки на этих старых ОС.

Пример кода:

#include <CommCtrl.h>     // include Common Controls header 
#pragma comment(lib, "comctl32.lib") // link to Common Controls library 

// Embed a standard manifest to use Common Controls v6 
#pragma comment(linker, "/manifestdependency:\"type='win32' "       \ 
         "name='Microsoft.Windows.Common-Controls' version='6.0.0.0' " \ 
         "processorArchitecture='*' "         \ 
         "publicKeyToken='6595b64144ccf1df' "       \ 
         "language='*'\"") 
// Load and set "large" icon (typically 32x32 pixels, but not necessarily) 
HICON hIconLg; 
if (SUCCEEDED(LoadIconWithScaleDown(g_hInstance, 
            MAKEINTRESOURCE(IDI_ICON), 
            GetSystemMetrics(SM_CXICON), 
            GetSystemMetrics(SM_CYICON), 
            &hIconLg))) 
{ 
    SendMessage(hWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIconLg)); 
} 

// Load and set "small" icon (typically 16x16 pixels, but not necessarily) 
HICON hIconSm; 
if (SUCCEEDED(LoadIconWithScaleDown(g_hInstance, 
            MAKEINTRESOURCE(IDI_ICON), 
            GetSystemMetrics(SM_CXSMICON), 
            GetSystemMetrics(SM_CYSMICON), 
            &hIconSm))) 
{ 
    SendMessage(hWnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIconSm)); 
} 

Обратите внимание, что использовать эти новые функции, вам нужно будет связать с 6-й версии общей библиотеки элементов управления. Это требует, чтобы вы инструктировали компилятор связать в comctl32.lib и вставить манифест в ваше приложение. Любой из них может быть выполнен с использованием специфического для MSVC #pragma s, показанного в примере кода выше, или настроенного в свойствах вашего проекта. Если вы не выполните одно из этих действий, при первом запуске приложения вы получите сообщение об ошибке времени или ошибку «ординал не найден».

+0

Вау, благодарю вас за все ваши детали и анализ. В моем ICO-файле все резолюции хорошо отражают резолюции, которые я предоставляю (см. EDIT выше).Мое нижнее изображение «jaggy» действительно масштабируется Windows (у меня нет ICO, который выглядит так, как в моем ICO-файле). Windows делает это, когда находится в режиме более высокого DPI. К вашему моменту, когда я делаю GetDeviceCaps при обычном масштабировании рабочего стола (100%), я получаю 96 ... но если я настрою свой рабочий стол на 125% масштабирование или масштабирование на 200% (щелкните правой кнопкой мыши Desktop, Personalize, other: Display), Я получаю другой DPI, и Windows требует, чтобы большие изображения начали масштабировать ИКОНы этим «плохим» способом. – mark

+0

Net-net of above: если я «разрешу Windows решить», что ICO использовать в режиме с высоким разрешением DPI (т.е. без «LoadImage» или через «GetSystemMetrics (SM_CXSMICON)»), тогда он выбирает значок с низким разрешением и масштабируется (и плохо). – mark

+0

Просто чтобы убедиться, что я не сходил с ума, я использовал свой код вместо моего ... он выглядит «просто отлично» при 100% масштабировании рабочего стола. Я поместил свой рабочий стол на 125% масштабирование, и * оба * тома Mixer и иконки панели задач выглядят плохо (искажения артефактов). – mark

1

Я столкнулся с аналогичной проблемой с программой C#/WPF.

В моем случае проблема, казалось, была вызвана отсутствующими размерами внутри файла ico. Самый маленький размер файла iico моего приложения был 64x64. Как только я добавил меньшие размеры внутри этого файла, проблема была решена.

0

Благодарим вас, ребята, за это. Я получил эту работу в нашем приложении Wx, если кто-то хочет немного предварительно выпеченный код, чтобы заскочить в их приложение Wx здесь моя ниже:

#ifdef __WXMSW__ 
#include <Windows.h> 
#include <CommCtrl.h> 
#include <wx/msw/private.h> 
typedef int (WINAPI *func_LoadIconWithScaleDown)(HINSTANCE, LPCWSTR, int, int, HICON*); 
#endif 

void MainFrame::BindAppIcon() { 
#ifdef __WXMSW__ 
    wxDynamicLibrary comctl32("comctl32", wxDL_DEFAULT | wxDL_QUIET); 
    func_LoadIconWithScaleDown load_icon_scaled = reinterpret_cast<func_LoadIconWithScaleDown>(comctl32.GetSymbol("LoadIconWithScaleDown")); 
    int icon_set_count = 0; 

    HICON hIconLg; 
    if (load_icon_scaled && SUCCEEDED(load_icon_scaled(wxGetInstance(), _T("AAAAA_MAINICON"), ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), &hIconLg))) { 
     ::SendMessage(GetHandle(), WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIconLg)); 
     ++icon_set_count; 
    } 
    HICON hIconSm; 
    if (load_icon_scaled && SUCCEEDED(load_icon_scaled(wxGetInstance(), _T("AAAAA_MAINICON"), ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), &hIconSm))) { 
     ::SendMessage(GetHandle(), WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIconSm)); 
     ++icon_set_count; 
    } 

    if (icon_set_count == 2) return; 
    // otherwise fall back to Wx method of setting icon 
#endif 
    wxIcon icon = wxXmlResource::Get()->LoadIcon(wxT("MainIcon")); 

    if (!icon.IsOk()) { 
     wxLogInfo(_("Main icon not found")); 
     icon = wxICON(wxvbam); 
    } 

    SetIcon(icon); 
} 

Первая строка в вашем app.rc затем будет что-то вроде этого :

AAAAA_MAINICON ICON "icons/app.ico" 
Смежные вопросы