SDL獲得屏幕屬性及實現分析


[時間:2017-05] [狀態:Open]
[關鍵詞:sdl2,屏幕分辨率,顯示區域,多媒體渲染,窗口,sdl2源碼分析]

0 引言

本文的主要目標在於使用SDL2獲得屏幕相關的屬性,比如分辨率、屏幕個數以及屏幕可用區域的范圍。

通常情況下,有過圖形界面編程經驗的人都知道桌面系統的構成,屏幕分辨率是指的整個屏幕區域的寬高,而通常屏幕區域有一些系統的任務欄或者菜單欄;舉個例子,windows下的任務欄一般位於下面,並且通常非全屏窗口是不能占用任務欄的。

1 獲得屏幕個數

SDL2中提供了獲得屏幕個數的接口SDL_GetNumVideoDisplays,具體建議參考sdl2-wiki

當然屏幕還有其他屬性,比如顯示模式等,可以通過SDL_GetNumDisplayModesSDL_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上的實現邏輯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM