2 * Copyright (C) 2011-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
23 #include "EGLNativeTypeRaspberryPI.h"
24 #include "utils/log.h"
25 #include "guilib/gui3d.h"
26 #include "linux/DllBCM.h"
27 #include "utils/StringUtils.h"
29 #ifndef __VIDEOCORE4__
30 #define __VIDEOCORE4__
33 #define __VCCOREVER__ 0x04000000
35 #define IS_WIDESCREEN(m) ( m == 3 || m == 7 || m == 9 || \
36 m == 11 || m == 13 || m == 15 || m == 18 || m == 22 || \
37 m == 24 || m == 26 || m == 28 || m == 30 || m == 36 || \
38 m == 38 || m == 43 || m == 45 || m == 49 || m == 51 || \
39 m == 53 || m == 55 || m == 57 || m == 59)
41 #define MAKEFLAGS(group, mode, interlace) \
42 ( ( (mode)<<24 ) | ( (group)<<16 ) | \
43 ( (interlace) != 0 ? D3DPRESENTFLAG_INTERLACED : D3DPRESENTFLAG_PROGRESSIVE) | \
44 ( ((group) == HDMI_RES_GROUP_CEA && IS_WIDESCREEN(mode) ) ? D3DPRESENTFLAG_WIDESCREEN : 0) )
46 #define GETFLAGS_GROUP(f) ( (HDMI_RES_GROUP_T)( ((f) >> 16) & 0xff ))
47 #define GETFLAGS_MODE(f) ( ( (f) >>24 ) & 0xff )
50 //#define DEBUG_PRINT 1
53 #if defined(DEBUG_PRINT)
54 # define DLOG(fmt, args...) printf(fmt, ##args)
56 # define DLOG(fmt, args...)
60 CEGLNativeTypeRaspberryPI::CEGLNativeTypeRaspberryPI()
62 #if defined(TARGET_RASPBERRY_PI)
64 m_nativeWindow = NULL;
68 CEGLNativeTypeRaspberryPI::~CEGLNativeTypeRaspberryPI()
70 #if defined(TARGET_RASPBERRY_PI)
77 bool CEGLNativeTypeRaspberryPI::CheckCompatibility()
79 #if defined(TARGET_RASPBERRY_PI)
80 DLOG("CEGLNativeTypeRaspberryPI::CheckCompatibility\n");
87 void CEGLNativeTypeRaspberryPI::Initialize()
89 #if defined(TARGET_RASPBERRY_PI)
91 m_dispman_element = DISPMANX_NO_HANDLE;
92 m_dispman_display = DISPMANX_NO_HANDLE;
96 m_initDesktopRes = true;
98 m_DllBcmHost = new DllBcmHost;
103 void CEGLNativeTypeRaspberryPI::Destroy()
105 #if defined(TARGET_RASPBERRY_PI)
106 if(m_DllBcmHost && m_DllBcmHost->IsLoaded())
107 m_DllBcmHost->Unload();
113 bool CEGLNativeTypeRaspberryPI::CreateNativeDisplay()
115 m_nativeDisplay = EGL_DEFAULT_DISPLAY;
119 bool CEGLNativeTypeRaspberryPI::CreateNativeWindow()
121 #if defined(TARGET_RASPBERRY_PI)
123 m_nativeWindow = (EGLNativeWindowType) calloc(1,sizeof( EGL_DISPMANX_WINDOW_T));
124 DLOG("CEGLNativeTypeRaspberryPI::CEGLNativeTypeRaspberryPI\n");
131 bool CEGLNativeTypeRaspberryPI::GetNativeDisplay(XBNativeDisplayType **nativeDisplay) const
135 *nativeDisplay = (XBNativeDisplayType*) &m_nativeDisplay;
139 bool CEGLNativeTypeRaspberryPI::GetNativeWindow(XBNativeDisplayType **nativeWindow) const
141 DLOG("CEGLNativeTypeRaspberryPI::GetNativeWindow\n");
144 *nativeWindow = (XBNativeWindowType*) &m_nativeWindow;
148 bool CEGLNativeTypeRaspberryPI::DestroyNativeDisplay()
150 DLOG("CEGLNativeTypeRaspberryPI::DestroyNativeDisplay\n");
154 bool CEGLNativeTypeRaspberryPI::DestroyNativeWindow()
156 #if defined(TARGET_RASPBERRY_PI)
157 DestroyDispmaxWindow();
158 free(m_nativeWindow);
159 m_nativeWindow = NULL;
160 DLOG("CEGLNativeTypeRaspberryPI::DestroyNativeWindow\n");
167 bool CEGLNativeTypeRaspberryPI::GetNativeResolution(RESOLUTION_INFO *res) const
169 #if defined(TARGET_RASPBERRY_PI)
172 DLOG("CEGLNativeTypeRaspberryPI::GetNativeResolution %s\n", res->strMode.c_str());
179 #if defined(TARGET_RASPBERRY_PI)
180 int CEGLNativeTypeRaspberryPI::FindMatchingResolution(const RESOLUTION_INFO &res, const std::vector<RESOLUTION_INFO> &resolutions)
182 for (int i = 0; i < (int)resolutions.size(); i++)
184 if(resolutions[i].iScreenWidth == res.iScreenWidth && resolutions[i].iScreenHeight == res.iScreenHeight && resolutions[i].fRefreshRate == res.fRefreshRate &&
185 (resolutions[i].dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB)) == (res.dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB)))
194 #if defined(TARGET_RASPBERRY_PI)
195 int CEGLNativeTypeRaspberryPI::AddUniqueResolution(const RESOLUTION_INFO &res, std::vector<RESOLUTION_INFO> &resolutions)
197 int i = FindMatchingResolution(res, resolutions);
199 { // don't replace a progressive resolution with an interlaced one of same resolution
200 if (!(res.dwFlags & D3DPRESENTFLAG_INTERLACED))
201 resolutions[i] = res;
205 resolutions.push_back(res);
211 bool CEGLNativeTypeRaspberryPI::SetNativeResolution(const RESOLUTION_INFO &res)
213 #if defined(TARGET_RASPBERRY_PI)
214 if(!m_DllBcmHost || !m_nativeWindow)
217 DestroyDispmaxWindow();
219 if(!m_fixedMode && GETFLAGS_GROUP(res.dwFlags) && GETFLAGS_MODE(res.dwFlags))
221 sem_init(&m_tv_synced, 0, 0);
222 m_DllBcmHost->vc_tv_register_callback(CallbackTvServiceCallback, this);
224 if (res.dwFlags & (D3DPRESENTFLAG_MODE3DSBS|D3DPRESENTFLAG_MODE3DTB))
226 /* inform TV of any 3D settings. Note this property just applies to next hdmi mode change, so no need to call for 2D modes */
227 HDMI_PROPERTY_PARAM_T property;
228 property.property = HDMI_PROPERTY_3D_STRUCTURE;
229 if (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS)
230 property.param1 = HDMI_3D_FORMAT_SBS_HALF;
231 else if (res.dwFlags & D3DPRESENTFLAG_MODE3DTB)
232 property.param1 = HDMI_3D_FORMAT_TB_HALF;
234 property.param1 = HDMI_3D_FORMAT_NONE;
236 vc_tv_hdmi_set_property(&property);
238 int success = m_DllBcmHost->vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags));
242 CLog::Log(LOGDEBUG, "EGL set HDMI mode (%d,%d)=%d%s%s\n",
243 GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), success,
244 (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ? " SBS":"",
245 (res.dwFlags & D3DPRESENTFLAG_MODE3DTB) ? " TB":"");
247 sem_wait(&m_tv_synced);
251 CLog::Log(LOGERROR, "EGL failed to set HDMI mode (%d,%d)=%d%s%s\n",
252 GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), success,
253 (res.dwFlags & D3DPRESENTFLAG_MODE3DSBS) ? " SBS":"",
254 (res.dwFlags & D3DPRESENTFLAG_MODE3DTB) ? " TB":"");
256 m_DllBcmHost->vc_tv_unregister_callback(CallbackTvServiceCallback);
257 sem_destroy(&m_tv_synced);
262 m_dispman_display = m_DllBcmHost->vc_dispmanx_display_open(0);
264 m_width = res.iWidth;
265 m_height = res.iHeight;
272 dst_rect.width = res.iScreenWidth;
273 dst_rect.height = res.iScreenHeight;
277 src_rect.width = m_width << 16;
278 src_rect.height = m_height << 16;
280 VC_DISPMANX_ALPHA_T alpha;
281 memset(&alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T));
282 alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE;
284 DISPMANX_CLAMP_T clamp;
285 memset(&clamp, 0x0, sizeof(DISPMANX_CLAMP_T));
287 DISPMANX_TRANSFORM_T transform = DISPMANX_NO_ROTATE;
288 DISPMANX_UPDATE_HANDLE_T dispman_update = m_DllBcmHost->vc_dispmanx_update_start(0);
290 CLog::Log(LOGDEBUG, "EGL set resolution %dx%d -> %dx%d @ %.2f fps (%d,%d) flags:%x aspect:%.2f\n",
291 m_width, m_height, dst_rect.width, dst_rect.height, res.fRefreshRate, GETFLAGS_GROUP(res.dwFlags), GETFLAGS_MODE(res.dwFlags), (int)res.dwFlags, res.fPixelRatio);
293 m_dispman_element = m_DllBcmHost->vc_dispmanx_element_add(dispman_update,
297 (DISPMANX_RESOURCE_HANDLE_T)0, // src
299 DISPMANX_PROTECTION_NONE,
302 transform); // transform
304 assert(m_dispman_element != DISPMANX_NO_HANDLE);
305 assert(m_dispman_element != (unsigned)DISPMANX_INVALID);
307 memset(m_nativeWindow, 0, sizeof(EGL_DISPMANX_WINDOW_T));
309 EGL_DISPMANX_WINDOW_T *nativeWindow = (EGL_DISPMANX_WINDOW_T *)m_nativeWindow;
311 nativeWindow->element = m_dispman_element;
312 nativeWindow->width = m_width;
313 nativeWindow->height = m_height;
315 m_DllBcmHost->vc_dispmanx_display_set_background(dispman_update, m_dispman_display, 0x00, 0x00, 0x00);
316 m_DllBcmHost->vc_dispmanx_update_submit_sync(dispman_update);
318 DLOG("CEGLNativeTypeRaspberryPI::SetNativeResolution\n");
326 #if defined(TARGET_RASPBERRY_PI)
327 static float get_display_aspect_ratio(HDMI_ASPECT_T aspect)
329 float display_aspect;
331 case HDMI_ASPECT_4_3: display_aspect = 4.0/3.0; break;
332 case HDMI_ASPECT_14_9: display_aspect = 14.0/9.0; break;
333 case HDMI_ASPECT_16_9: display_aspect = 16.0/9.0; break;
334 case HDMI_ASPECT_5_4: display_aspect = 5.0/4.0; break;
335 case HDMI_ASPECT_16_10: display_aspect = 16.0/10.0; break;
336 case HDMI_ASPECT_15_9: display_aspect = 15.0/9.0; break;
337 case HDMI_ASPECT_64_27: display_aspect = 64.0/27.0; break;
338 default: display_aspect = 16.0/9.0; break;
340 return display_aspect;
343 static float get_display_aspect_ratio(SDTV_ASPECT_T aspect)
345 float display_aspect;
347 case SDTV_ASPECT_4_3: display_aspect = 4.0/3.0; break;
348 case SDTV_ASPECT_14_9: display_aspect = 14.0/9.0; break;
349 case SDTV_ASPECT_16_9: display_aspect = 16.0/9.0; break;
350 default: display_aspect = 4.0/3.0; break;
352 return display_aspect;
356 bool CEGLNativeTypeRaspberryPI::ProbeResolutions(std::vector<RESOLUTION_INFO> &resolutions)
358 #if defined(TARGET_RASPBERRY_PI)
366 /* read initial desktop resolution before probe resolutions.
367 * probing will replace the desktop resolution when it finds the same one.
368 * we raplace it because probing will generate more detailed
369 * resolution flags we don't get with vc_tv_get_state.
374 TV_DISPLAY_STATE_T tv_state;
376 // get current display settings state
377 memset(&tv_state, 0, sizeof(TV_DISPLAY_STATE_T));
378 m_DllBcmHost->vc_tv_get_display_state(&tv_state);
380 if ((tv_state.state & ( VC_HDMI_HDMI | VC_HDMI_DVI )) != 0) // hdtv
382 m_desktopRes.iScreen = 0;
383 m_desktopRes.bFullScreen = true;
384 m_desktopRes.iWidth = tv_state.display.hdmi.width;
385 m_desktopRes.iHeight = tv_state.display.hdmi.height;
386 m_desktopRes.iScreenWidth = tv_state.display.hdmi.width;
387 m_desktopRes.iScreenHeight= tv_state.display.hdmi.height;
388 m_desktopRes.dwFlags = MAKEFLAGS(tv_state.display.hdmi.group, tv_state.display.hdmi.mode, tv_state.display.hdmi.scan_mode);
389 m_desktopRes.fPixelRatio = get_display_aspect_ratio((HDMI_ASPECT_T)tv_state.display.hdmi.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
391 if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_SBS_HALF)
393 m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
394 m_desktopRes.fPixelRatio *= 2.0;
396 else if (tv_state.display.hdmi.format_3d == HDMI_3D_FORMAT_TB_HALF)
398 m_desktopRes.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
399 m_desktopRes.fPixelRatio *= 0.5;
401 m_desktopRes.fRefreshRate = (float)tv_state.display.hdmi.frame_rate;
405 m_desktopRes.iScreen = 0;
406 m_desktopRes.bFullScreen = true;
407 m_desktopRes.iWidth = tv_state.display.sdtv.width;
408 m_desktopRes.iHeight = tv_state.display.sdtv.height;
409 m_desktopRes.iScreenWidth = tv_state.display.sdtv.width;
410 m_desktopRes.iScreenHeight= tv_state.display.sdtv.height;
411 m_desktopRes.dwFlags = D3DPRESENTFLAG_INTERLACED;
412 m_desktopRes.fRefreshRate = (float)tv_state.display.sdtv.frame_rate;
413 m_desktopRes.fPixelRatio = get_display_aspect_ratio((SDTV_ASPECT_T)tv_state.display.sdtv.display_options.aspect) / ((float)m_desktopRes.iScreenWidth / (float)m_desktopRes.iScreenHeight);
416 m_desktopRes.strMode = StringUtils::Format("%dx%d", m_desktopRes.iScreenWidth, m_desktopRes.iScreenHeight);
418 if((int)m_desktopRes.fRefreshRate > 1)
420 m_desktopRes.strMode = StringUtils::Format("%dx%d @ %.2f%s - Full Screen", m_desktopRes.iScreenWidth, m_desktopRes.iScreenHeight, m_desktopRes.fRefreshRate,
421 m_desktopRes.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
423 m_initDesktopRes = false;
425 int gui_width = m_desktopRes.iWidth;
426 int gui_height = m_desktopRes.iHeight;
428 ClampToGUIDisplayLimits(gui_width, gui_height);
430 m_desktopRes.iWidth = gui_width;
431 m_desktopRes.iHeight = gui_height;
433 m_desktopRes.iSubtitles = (int)(0.965 * m_desktopRes.iHeight);
435 CLog::Log(LOGDEBUG, "EGL initial desktop resolution %s (%.2f)\n", m_desktopRes.strMode.c_str(), m_desktopRes.fPixelRatio);
438 GetSupportedModes(HDMI_RES_GROUP_CEA, resolutions);
439 GetSupportedModes(HDMI_RES_GROUP_DMT, resolutions);
441 if(resolutions.size() == 0)
444 CLog::Log(LOGDEBUG, "EGL probe resolution %s:%x\n", m_desktopRes.strMode.c_str(), m_desktopRes.dwFlags);
446 AddUniqueResolution(m_desktopRes, resolutions);
449 if(resolutions.size() < 2)
452 DLOG("CEGLNativeTypeRaspberryPI::ProbeResolutions\n");
459 bool CEGLNativeTypeRaspberryPI::GetPreferredResolution(RESOLUTION_INFO *res) const
461 DLOG("CEGLNativeTypeRaspberryPI::GetPreferredResolution\n");
465 bool CEGLNativeTypeRaspberryPI::ShowWindow(bool show)
467 DLOG("CEGLNativeTypeRaspberryPI::ShowWindow\n");
471 #if defined(TARGET_RASPBERRY_PI)
472 void CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow()
477 DISPMANX_UPDATE_HANDLE_T dispman_update = m_DllBcmHost->vc_dispmanx_update_start(0);
479 if (m_dispman_element != DISPMANX_NO_HANDLE)
481 m_DllBcmHost->vc_dispmanx_element_remove(dispman_update, m_dispman_element);
482 m_dispman_element = DISPMANX_NO_HANDLE;
484 m_DllBcmHost->vc_dispmanx_update_submit_sync(dispman_update);
486 if (m_dispman_display != DISPMANX_NO_HANDLE)
488 m_DllBcmHost->vc_dispmanx_display_close(m_dispman_display);
489 m_dispman_display = DISPMANX_NO_HANDLE;
491 DLOG("CEGLNativeTypeRaspberryPI::DestroyDispmaxWindow\n");
494 void CEGLNativeTypeRaspberryPI::GetSupportedModes(HDMI_RES_GROUP_T group, std::vector<RESOLUTION_INFO> &resolutions)
499 //Supported HDMI CEA/DMT resolutions, preferred resolution will be returned
500 int32_t num_modes = 0;
501 HDMI_RES_GROUP_T prefer_group;
502 uint32_t prefer_mode;
504 TV_SUPPORTED_MODE_NEW_T *supported_modes = NULL;
505 // query the number of modes first
506 int max_supported_modes = m_DllBcmHost->vc_tv_hdmi_get_supported_modes_new(group, NULL, 0, &prefer_group, &prefer_mode);
508 if (max_supported_modes > 0)
509 supported_modes = new TV_SUPPORTED_MODE_NEW_T[max_supported_modes];
513 num_modes = m_DllBcmHost->vc_tv_hdmi_get_supported_modes_new(group,
514 supported_modes, max_supported_modes, &prefer_group, &prefer_mode);
516 CLog::Log(LOGDEBUG, "EGL get supported modes (%d) = %d, prefer_group=%x, prefer_mode=%x\n",
517 group, num_modes, prefer_group, prefer_mode);
520 if (num_modes > 0 && prefer_group != HDMI_RES_GROUP_INVALID)
522 TV_SUPPORTED_MODE_NEW_T *tv = supported_modes;
523 for (i=0; i < num_modes; i++, tv++)
528 res.bFullScreen = true;
529 res.dwFlags = MAKEFLAGS(group, tv->code, tv->scan_mode);
530 res.fRefreshRate = (float)tv->frame_rate;
531 res.iWidth = tv->width;
532 res.iHeight = tv->height;
533 res.iScreenWidth = tv->width;
534 res.iScreenHeight = tv->height;
535 res.fPixelRatio = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res.iScreenWidth / (float)res.iScreenHeight);
537 int gui_width = res.iWidth;
538 int gui_height = res.iHeight;
540 ClampToGUIDisplayLimits(gui_width, gui_height);
542 res.iWidth = gui_width;
543 res.iHeight = gui_height;
545 res.strMode = StringUtils::Format("%dx%d @ %.2f%s - Full Screen", res.iScreenWidth, res.iScreenHeight, res.fRefreshRate,
546 res.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
548 CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) %s%s:%x\n", i, res.strMode.c_str(), res.fPixelRatio,
549 tv->native ? "N" : "", tv->scan_mode ? "I" : "", tv->code);
551 res.iSubtitles = (int)(0.965 * res.iHeight);
553 AddUniqueResolution(res, resolutions);
555 // Also add 3D versions of modes
556 if (tv->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL)
558 RESOLUTION_INFO res2 = res;
559 res2.dwFlags |= D3DPRESENTFLAG_MODE3DSBS;
560 res2.fPixelRatio = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
561 res2.fPixelRatio *= 2.0f;
562 res2.strMode = StringUtils::Format("%dx%d @ %.2f%s - Full Screen", res2.iScreenWidth, res2.iScreenHeight, res2.fRefreshRate,
563 res2.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
564 CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) SBS\n", i, res2.strMode.c_str(), res2.fPixelRatio);
566 res2.iSubtitles = (int)(0.965 * res2.iHeight);
568 AddUniqueResolution(res2, resolutions);
570 if (tv->struct_3d_mask & HDMI_3D_STRUCT_TOP_AND_BOTTOM)
572 RESOLUTION_INFO res2 = res;
573 res2.dwFlags |= D3DPRESENTFLAG_MODE3DTB;
574 res2.fPixelRatio = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio) / ((float)res2.iScreenWidth / (float)res2.iScreenHeight);
575 res2.fPixelRatio *= 0.5f;
576 res2.strMode = StringUtils::Format("%dx%d @ %.2f%s - Full Screen", res2.iScreenWidth, res2.iScreenHeight, res2.fRefreshRate,
577 res2.dwFlags & D3DPRESENTFLAG_INTERLACED ? "i" : "");
578 CLog::Log(LOGDEBUG, "EGL mode %d: %s (%.2f) TAB\n", i, res2.strMode.c_str(), res2.fPixelRatio);
580 res2.iSubtitles = (int)(0.965 * res2.iHeight);
582 AddUniqueResolution(res2, resolutions);
587 delete [] supported_modes;
590 void CEGLNativeTypeRaspberryPI::TvServiceCallback(uint32_t reason, uint32_t param1, uint32_t param2)
592 CLog::Log(LOGDEBUG, "EGL tv_service_callback (%d,%d,%d)\n", reason, param1, param2);
595 case VC_HDMI_UNPLUGGED:
597 case VC_HDMI_STANDBY:
603 //Signal we are ready now
604 sem_post(&m_tv_synced);
611 void CEGLNativeTypeRaspberryPI::CallbackTvServiceCallback(void *userdata, uint32_t reason, uint32_t param1, uint32_t param2)
613 CEGLNativeTypeRaspberryPI *callback = static_cast<CEGLNativeTypeRaspberryPI*>(userdata);
614 callback->TvServiceCallback(reason, param1, param2);
617 bool CEGLNativeTypeRaspberryPI::ClampToGUIDisplayLimits(int &width, int &height)
619 const int max_width = 1280, max_height = 720;
620 float ar = (float)width/(float)height;
621 // bigger than maximum, so need to clamp
622 if (width > max_width || height > max_height) {
623 // wider than max, so clamp width first
624 if (ar > (float)max_width/(float)max_height)
627 height = (float)max_width / ar + 0.5f;
628 // taller than max, so clamp height first
631 width = (float)max_height * ar + 0.5f;