[時間:2017-05] [狀態:Open]
[關鍵詞:sdl2,屏幕分辨率,顯示區域,多媒體渲染,窗口,sdl2源碼分析]
0 引言
本文的主要目標在於使用SDL2獲得屏幕相關的屬性,比如分辨率、屏幕個數以及屏幕可用區域的范圍。
通常情況下,有過圖形界面編程經驗的人都知道桌面系統的構成,屏幕分辨率是指的整個屏幕區域的寬高,而通常屏幕區域有一些系統的任務欄或者菜單欄;舉個例子,windows下的任務欄一般位於下面,並且通常非全屏窗口是不能占用任務欄的。
1 獲得屏幕個數
SDL2中提供了獲得屏幕個數的接口SDL_GetNumVideoDisplays
,具體建議參考sdl2-wiki。
當然屏幕還有其他屬性,比如顯示模式等,可以通過SDL_GetNumDisplayModes
和SDL_GetDisplayMode
獲得SDL_DisplayMode
的結構,其中包括顯示格式(YUV、RGB等)、寬高、刷新率等。
2 每個屏幕的分辨率
SDL_GetDisplayBounds
可以獲得指定屏幕的顯示區域,可以通過顯示區域獲取屏幕分辨率。
3 獲取實際顯示區域的大小
SDL_GetDisplayUsableBounds
返回的是實際可用顯示區域的大小,這個通常比屏幕分辨率小。
4 SDL2內部實現的原理分析
這里以SDL_GetDisplayBounds
為例說明,如果讀者對其他函數感興趣,可以查看sdl2代碼。
int SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect)
{
CHECK_DISPLAY_INDEX(displayIndex, -1);
if (rect) {
SDL_VideoDisplay *display = &_this->displays[displayIndex];
if (_this->GetDisplayBounds) {
if (_this->GetDisplayBounds(_this, display, rect) == 0) {
return 0;
}
}
/* Assume that the displays are left to right */
if (displayIndex == 0) {
rect->x = 0;
rect->y = 0;
} else {
SDL_GetDisplayBounds(displayIndex-1, rect);
rect->x += rect->w;
}
rect->w = display->current_mode.w;
rect->h = display->current_mode.h;
}
return 0; /* !!! FIXME: should this be an error if (rect==NULL) ? */
}
很明顯這個函數直接調用了SDL_VideoDevice->GetDisplayBounds
函數,那么我們找一個實現版本看看,比如windows下的,代碼如下:
static SDL_VideoDevice * WIN_CreateDevice(int devindex)
{
// ...
device->GetDisplayBounds = WIN_GetDisplayBounds;
// ...
}
這個實現被重定向到WIN_GetDisplayBounds函數中,代碼如下:
int
WIN_GetDisplayBounds(_THIS, SDL_VideoDisplay * display, SDL_Rect * rect)
{
SDL_DisplayModeData *data = (SDL_DisplayModeData *) display->current_mode.driverdata;
rect->x = (int)SDL_ceil(data->DeviceMode.dmPosition.x * data->ScaleX);
rect->y = (int)SDL_ceil(data->DeviceMode.dmPosition.y * data->ScaleY);
rect->w = (int)SDL_ceil(data->DeviceMode.dmPelsWidth * data->ScaleX);
rect->h = (int)SDL_ceil(data->DeviceMode.dmPelsHeight * data->ScaleY);
return 0;
}
實現比較簡單,直接從display->current_mode中獲得數據。那么這個mode在哪里賦值的呢?
在SDL_windowsvideo.c中查找下,發下如下調用:
static SDL_bool WIN_AddDisplay(_THIS, LPTSTR DeviceName)
{
SDL_VideoDisplay display;
SDL_DisplayData *displaydata;
SDL_DisplayMode mode;
DISPLAY_DEVICE device;
if (!WIN_GetDisplayMode(_this, DeviceName, ENUM_CURRENT_SETTINGS, &mode)) {
return SDL_FALSE;
}
displaydata = (SDL_DisplayData *) SDL_malloc(sizeof(*displaydata));
if (!displaydata) {
return SDL_FALSE;
}
SDL_memcpy(displaydata->DeviceName, DeviceName,
sizeof(displaydata->DeviceName));
SDL_zero(display);
device.cb = sizeof(device);
if (EnumDisplayDevices(DeviceName, 0, &device, 0)) {
display.name = WIN_StringToUTF8(device.DeviceString);
}
display.desktop_mode = mode;
display.current_mode = mode;
display.driverdata = displaydata;
SDL_AddVideoDisplay(&display);
SDL_free(display.name);
return SDL_TRUE;
}
明顯這是從EnumDisplayDevices中獲取。
至此所有實現邏輯基本理清。
5 小結
本文主要整理我近期遇到的SDL2關於窗口和屏幕屬性獲取的邏輯,同時分析了SDL_GetDisplayBounds
在windows上的實現邏輯。