2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "BaseRenderer.h"
11 #include "ServiceBroker.h"
12 #include "cores/VideoPlayer/VideoRenderers/RenderFlags.h"
13 #include "guilib/GUIComponent.h"
14 #include "guilib/GUIWindowManager.h"
15 #include "guilib/LocalizeStrings.h"
16 #include "settings/DisplaySettings.h"
17 #include "settings/Settings.h"
18 #include "settings/SettingsComponent.h"
19 #include "settings/lib/SettingDefinitions.h"
20 #include "utils/MathUtils.h"
21 #include "utils/log.h"
22 #include "windowing/GraphicContext.h"
25 #include <cstdlib> // std::abs(int) prototype
28 CBaseRenderer::CBaseRenderer()
30 for (int i
=0; i
< 4; i
++)
32 m_rotatedDestCoords
[i
].x
= 0;
33 m_rotatedDestCoords
[i
].y
= 0;
34 m_savedRotatedDestCoords
[i
].x
= 0;
35 m_savedRotatedDestCoords
[i
].y
= 0;
39 CBaseRenderer::~CBaseRenderer() = default;
41 float CBaseRenderer::GetAspectRatio() const
43 return m_sourceFrameRatio
;
46 void CBaseRenderer::GetVideoRect(CRect
& source
, CRect
& dest
, CRect
& view
) const
48 source
= m_sourceRect
;
53 inline void CBaseRenderer::ReorderDrawPoints()
55 // 0 - top left, 1 - top right, 2 - bottom right, 3 - bottom left
56 float origMat
[4][2] = {{m_destRect
.x1
, m_destRect
.y1
},
57 {m_destRect
.x2
, m_destRect
.y1
},
58 {m_destRect
.x2
, m_destRect
.y2
},
59 {m_destRect
.x1
, m_destRect
.y2
}};
61 int pointOffset
= m_renderOrientation
/ 90;
63 // if renderer doesn't support rotation
64 // treat orientation as 0 degree so that
65 // ffmpeg might handle it.
66 if (!Supports(RENDERFEATURE_ROTATION
))
71 for (int destIdx
=0, srcIdx
=pointOffset
; destIdx
< 4; destIdx
++)
73 m_rotatedDestCoords
[destIdx
].x
= origMat
[srcIdx
][0];
74 m_rotatedDestCoords
[destIdx
].y
= origMat
[srcIdx
][1];
81 void CBaseRenderer::saveRotatedCoords()
83 for (int i
= 0; i
< 4; i
++)
84 m_savedRotatedDestCoords
[i
] = m_rotatedDestCoords
[i
];
87 void CBaseRenderer::syncDestRectToRotatedPoints()
89 m_rotatedDestCoords
[0].x
= m_destRect
.x1
;
90 m_rotatedDestCoords
[0].y
= m_destRect
.y1
;
91 m_rotatedDestCoords
[1].x
= m_destRect
.x2
;
92 m_rotatedDestCoords
[1].y
= m_destRect
.y1
;
93 m_rotatedDestCoords
[2].x
= m_destRect
.x2
;
94 m_rotatedDestCoords
[2].y
= m_destRect
.y2
;
95 m_rotatedDestCoords
[3].x
= m_destRect
.x1
;
96 m_rotatedDestCoords
[3].y
= m_destRect
.y2
;
99 void CBaseRenderer::restoreRotatedCoords()
101 for (int i
= 0; i
< 4; i
++)
102 m_rotatedDestCoords
[i
] = m_savedRotatedDestCoords
[i
];
105 void CBaseRenderer::CalcDestRect(float offsetX
,
109 float inputFrameRatio
,
114 // if view window is empty, set empty destination
115 if (height
== 0 || width
== 0)
117 destRect
.SetRect(0.0f
, 0.0f
, 0.0f
, 0.0f
);
121 // scale up image as much as possible
122 // and keep the aspect ratio (introduces with black bars)
123 // calculate the correct output frame ratio (using the users pixel ratio setting
124 // and the output pixel ratio setting)
126 float outputFrameRatio
= inputFrameRatio
/ CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo().fPixelRatio
;
128 // allow a certain error to maximize size of render area
129 float fCorrection
= width
/ height
/ outputFrameRatio
- 1.0f
;
130 float fAllowed
= CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_ERRORINASPECT
) * 0.01f
;
131 if (fCorrection
> fAllowed
)
132 fCorrection
= fAllowed
;
133 if (fCorrection
< -fAllowed
)
134 fCorrection
= - fAllowed
;
136 outputFrameRatio
*= 1.0f
+ fCorrection
;
138 bool isRotated
= false;
139 if (m_renderOrientation
== 90 ||
140 m_renderOrientation
== 270)
148 // maximize the movie width
150 newHeight
= newWidth
/ outputFrameRatio
;
151 if (newHeight
> height
)
154 newWidth
= newHeight
* outputFrameRatio
;
159 // maximize the movie height
160 newHeight
= std::min(width
, height
);
161 newWidth
= newHeight
/ outputFrameRatio
;
162 if (newWidth
> width
)
164 newWidth
= std::min(width
, height
);
165 newHeight
= newWidth
* outputFrameRatio
;
169 // Scale the movie up by set zoom amount
170 newWidth
*= zoomAmount
;
171 newHeight
*= zoomAmount
;
173 // if we are less than one pixel off use the complete screen instead
174 if (std::abs(newWidth
- width
) < 1.0f
)
176 if (std::abs(newHeight
- height
) < 1.0f
)
180 float posY
= (height
- newHeight
) / 2;
181 float posX
= (width
- newWidth
) / 2;
183 // vertical shift range -1 to 1 shifts within the top and bottom black bars
184 // if there are no top and bottom black bars, this range does nothing
185 float blackBarSize
= std::max((height
- newHeight
) / 2.0f
, 0.0f
);
186 posY
+= blackBarSize
* std::max(std::min(verticalShift
, 1.0f
), -1.0f
);
188 // vertical shift ranges -2 to -1 and 1 to 2 will shift the image out of the screen
189 // if vertical shift is -2 it will be completely shifted out the top,
190 // if it's 2 it will be completely shifted out the bottom
191 float shiftRange
= std::min(newHeight
, newHeight
- (newHeight
- height
) / 2.0f
);
192 if (verticalShift
> 1.0f
)
193 posY
+= shiftRange
* (verticalShift
- 1.0f
);
194 else if (verticalShift
< -1.0f
)
195 posY
+= shiftRange
* (verticalShift
+ 1.0f
);
197 destRect
.x1
= static_cast<float>(MathUtils::round_int(static_cast<double>(posX
+ offsetX
)));
198 destRect
.x2
= destRect
.x1
+ MathUtils::round_int(static_cast<double>(newWidth
));
199 destRect
.y1
= static_cast<float>(MathUtils::round_int(static_cast<double>(posY
+ offsetY
)));
200 destRect
.y2
= destRect
.y1
+ MathUtils::round_int(static_cast<double>(newHeight
));
203 void CBaseRenderer::CalcNormalRenderRect(float offsetX
,
207 float inputFrameRatio
,
211 CalcDestRect(offsetX
, offsetY
, width
, height
, inputFrameRatio
, zoomAmount
, verticalShift
,
214 // bail out if view window is empty
215 if (height
== 0 || width
== 0)
219 if (m_alwaysClip
|| !(CServiceBroker::GetWinSystem()->GetGfxContext().IsFullScreenVideo() ||
220 CServiceBroker::GetWinSystem()->GetGfxContext().IsCalibrating()))
222 CRect
original(m_destRect
);
223 m_destRect
.Intersect(CRect(offsetX
, offsetY
, offsetX
+ width
, offsetY
+ height
));
224 if (m_destRect
!= original
)
226 float scaleX
= m_sourceRect
.Width() / original
.Width();
227 float scaleY
= m_sourceRect
.Height() / original
.Height();
228 m_sourceRect
.x1
+= (m_destRect
.x1
- original
.x1
) * scaleX
;
229 m_sourceRect
.y1
+= (m_destRect
.y1
- original
.y1
) * scaleY
;
230 m_sourceRect
.x2
+= (m_destRect
.x2
- original
.x2
) * scaleX
;
231 m_sourceRect
.y2
+= (m_destRect
.y2
- original
.y2
) * scaleY
;
238 //***************************************************************************************
239 // CalculateFrameAspectRatio()
241 // Considers the source frame size and output frame size (as suggested by mplayer)
242 // to determine if the pixels in the source are not square. It calculates the aspect
243 // ratio of the output frame. We consider the cases of VCD, SVCD and DVD separately,
244 // as these are intended to be viewed on a non-square pixel TV set, so the pixels are
245 // defined to be the same ratio as the intended display pixels.
246 // These formats are determined by frame size.
247 //***************************************************************************************
248 void CBaseRenderer::CalculateFrameAspectRatio(unsigned int desired_width
, unsigned int desired_height
)
250 m_sourceFrameRatio
= (float)desired_width
/ desired_height
;
252 // Check whether mplayer has decided that the size of the video file should be changed
253 // This indicates either a scaling has taken place (which we didn't ask for) or it has
254 // found an aspect ratio parameter from the file, and is changing the frame size based
256 if (m_sourceWidth
== desired_width
&& m_sourceHeight
== desired_height
)
259 // mplayer is scaling in one or both directions. We must alter our Source Pixel Ratio
260 float imageFrameRatio
= (float)m_sourceWidth
/ m_sourceHeight
;
262 // OK, most sources will be correct now, except those that are intended
263 // to be displayed on non-square pixel based output devices (ie PAL or NTSC TVs)
264 // This includes VCD, SVCD, and DVD (and possibly others that we are not doing yet)
265 // For this, we can base the pixel ratio on the pixel ratios of PAL and NTSC,
266 // though we will need to adjust for anamorphic sources (ie those whose
267 // output frame ratio is not 4:3) and for SVCDs which have 2/3rds the
268 // horizontal resolution of the default NTSC or PAL frame sizes
270 // The following are the defined standard ratios for PAL and NTSC pixels
271 // NOTE: These aren't technically (in terms of BT601) correct - the commented values are,
272 // but it seems that many DVDs nowadays are mastered incorrectly, so two wrongs
273 // may indeed make a right. The "wrong" values here ensure the output frame is
275 const float PALPixelRatio
= 16.0f
/ 15.0f
; // 128.0f / 117.0f;
276 const float NTSCPixelRatio
= 8.0f
/ 9.0f
; // 4320.0f / 4739.0f;
278 // Calculate the correction needed for anamorphic sources
279 float Non4by3Correction
= m_sourceFrameRatio
/ (4.0f
/ 3.0f
);
281 // Finally, check for a VCD, SVCD or DVD frame size as these need special aspect ratios
282 if (m_sourceWidth
== 352)
284 if (m_sourceHeight
== 240) // NTSC
285 m_sourceFrameRatio
= imageFrameRatio
* NTSCPixelRatio
;
286 if (m_sourceHeight
== 288) // PAL
287 m_sourceFrameRatio
= imageFrameRatio
* PALPixelRatio
;
289 if (m_sourceWidth
== 480)
291 if (m_sourceHeight
== 480) // NTSC
292 m_sourceFrameRatio
= imageFrameRatio
* 3.0f
/ 2.0f
* NTSCPixelRatio
* Non4by3Correction
;
293 if (m_sourceHeight
== 576) // PAL
294 m_sourceFrameRatio
= imageFrameRatio
* 3.0f
/ 2.0f
* PALPixelRatio
* Non4by3Correction
;
296 if (m_sourceWidth
== 720)
298 if (m_sourceHeight
== 480) // NTSC
299 m_sourceFrameRatio
= imageFrameRatio
* NTSCPixelRatio
* Non4by3Correction
;
300 if (m_sourceHeight
== 576) // PAL
301 m_sourceFrameRatio
= imageFrameRatio
* PALPixelRatio
* Non4by3Correction
;
305 void CBaseRenderer::ManageRenderArea()
307 m_viewRect
= CServiceBroker::GetWinSystem()->GetGfxContext().GetViewWindow();
309 m_sourceRect
.x1
= 0.0f
;
310 m_sourceRect
.y1
= 0.0f
;
311 m_sourceRect
.x2
= (float)m_sourceWidth
;
312 m_sourceRect
.y2
= (float)m_sourceHeight
;
314 unsigned int stereo_mode
= CONF_FLAGS_STEREO_MODE_MASK(m_iFlags
);
315 int stereo_view
= CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoView();
317 if(CONF_FLAGS_STEREO_CADENCE(m_iFlags
) == CONF_FLAGS_STEREO_CADANCE_RIGHT_LEFT
)
319 if (stereo_view
== RENDER_STEREO_VIEW_LEFT
) stereo_view
= RENDER_STEREO_VIEW_RIGHT
;
320 else if(stereo_view
== RENDER_STEREO_VIEW_RIGHT
) stereo_view
= RENDER_STEREO_VIEW_LEFT
;
325 case CONF_FLAGS_STEREO_MODE_TAB
:
326 if (stereo_view
== RENDER_STEREO_VIEW_LEFT
)
327 m_sourceRect
.y2
*= 0.5f
;
328 else if(stereo_view
== RENDER_STEREO_VIEW_RIGHT
)
329 m_sourceRect
.y1
+= m_sourceRect
.y2
*0.5f
;
332 case CONF_FLAGS_STEREO_MODE_SBS
:
333 if (stereo_view
== RENDER_STEREO_VIEW_LEFT
)
334 m_sourceRect
.x2
*= 0.5f
;
335 else if(stereo_view
== RENDER_STEREO_VIEW_RIGHT
)
336 m_sourceRect
.x1
+= m_sourceRect
.x2
*0.5f
;
343 CalcNormalRenderRect(m_viewRect
.x1
, m_viewRect
.y1
, m_viewRect
.Width(), m_viewRect
.Height(),
344 GetAspectRatio() * CDisplaySettings::GetInstance().GetPixelRatio(),
345 CDisplaySettings::GetInstance().GetZoomAmount(),
346 CDisplaySettings::GetInstance().GetVerticalShift());
349 EShaderFormat
CBaseRenderer::GetShaderFormat()
351 EShaderFormat ret
= SHADER_NONE
;
353 if (m_format
== AV_PIX_FMT_YUV420P
)
355 else if (m_format
== AV_PIX_FMT_YUV420P9
)
357 else if (m_format
== AV_PIX_FMT_YUV420P10
)
358 ret
= SHADER_YV12_10
;
359 else if (m_format
== AV_PIX_FMT_YUV420P12
)
360 ret
= SHADER_YV12_12
;
361 else if (m_format
== AV_PIX_FMT_YUV420P14
)
362 ret
= SHADER_YV12_14
;
363 else if (m_format
== AV_PIX_FMT_YUV420P16
)
364 ret
= SHADER_YV12_16
;
365 else if (m_format
== AV_PIX_FMT_NV12
)
367 else if (m_format
== AV_PIX_FMT_YUYV422
)
369 else if (m_format
== AV_PIX_FMT_UYVY422
)
372 CLog::Log(LOGERROR
, "CBaseRenderer::GetShaderFormat - unsupported format {}", m_format
);
377 void CBaseRenderer::SetViewMode(int viewMode
)
379 if (viewMode
< ViewModeNormal
|| viewMode
> ViewModeZoom110Width
)
380 viewMode
= ViewModeNormal
;
382 m_videoSettings
.m_ViewMode
= viewMode
;
384 // get our calibrated full screen resolution
385 RESOLUTION_INFO info
= CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
386 float screenWidth
= (float)(info
.Overscan
.right
- info
.Overscan
.left
);
387 float screenHeight
= (float)(info
.Overscan
.bottom
- info
.Overscan
.top
);
389 // and the source frame ratio
390 float sourceFrameRatio
= GetAspectRatio();
392 bool is43
= (sourceFrameRatio
< 8.f
/(3.f
*sqrt(3.f
)) &&
393 m_videoSettings
.m_ViewMode
== ViewModeNormal
);
395 // Splitres scaling factor
396 float xscale
= (float)info
.iScreenWidth
/ (float)info
.iWidth
;
397 float yscale
= (float)info
.iScreenHeight
/ (float)info
.iHeight
;
399 screenWidth
*= xscale
;
400 screenHeight
*= yscale
;
402 CDisplaySettings::GetInstance().SetVerticalShift(0.0f
);
403 CDisplaySettings::GetInstance().SetNonLinearStretched(false);
405 if (m_videoSettings
.m_ViewMode
== ViewModeZoom
||
406 (is43
&& CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_STRETCH43
) == ViewModeZoom
))
407 { // zoom image so no black bars
408 CDisplaySettings::GetInstance().SetPixelRatio(1.0);
409 // calculate the desired output ratio
410 float outputFrameRatio
= sourceFrameRatio
* CDisplaySettings::GetInstance().GetPixelRatio() / info
.fPixelRatio
;
411 // now calculate the correct zoom amount. First zoom to full height.
412 float newHeight
= screenHeight
;
413 float newWidth
= newHeight
* outputFrameRatio
;
414 CDisplaySettings::GetInstance().SetZoomAmount(newWidth
/ screenWidth
);
415 if (newWidth
< screenWidth
)
416 { // zoom to full width
417 newWidth
= screenWidth
;
418 newHeight
= newWidth
/ outputFrameRatio
;
419 CDisplaySettings::GetInstance().SetZoomAmount(newHeight
/ screenHeight
);
422 else if (m_videoSettings
.m_ViewMode
== ViewModeStretch4x3
)
423 { // stretch image to 4:3 ratio
424 CDisplaySettings::GetInstance().SetZoomAmount(1.0);
425 // now we need to set CDisplaySettings::GetInstance().GetPixelRatio() so that
426 // fOutputFrameRatio = 4:3.
427 CDisplaySettings::GetInstance().SetPixelRatio((4.0f
/ 3.0f
) / sourceFrameRatio
);
429 else if (m_videoSettings
.m_ViewMode
== ViewModeWideZoom
||
430 (is43
&& CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_STRETCH43
) == ViewModeWideZoom
))
432 float stretchAmount
= (screenWidth
/ screenHeight
) * info
.fPixelRatio
/ sourceFrameRatio
;
433 CDisplaySettings::GetInstance().SetPixelRatio(pow(stretchAmount
, float(2.0/3.0)));
434 CDisplaySettings::GetInstance().SetZoomAmount(
435 pow(stretchAmount
, float((stretchAmount
< 1.0f
) ? -1.0 / 3.0 : 1.0 / 3.0)));
436 CDisplaySettings::GetInstance().SetNonLinearStretched(true);
438 else if (m_videoSettings
.m_ViewMode
== ViewModeStretch16x9
||
439 m_videoSettings
.m_ViewMode
== ViewModeStretch16x9Nonlin
||
440 (is43
&& (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_STRETCH43
) == ViewModeStretch16x9
||
441 CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_STRETCH43
) == ViewModeStretch16x9Nonlin
)))
442 { // stretch image to 16:9 ratio
443 CDisplaySettings::GetInstance().SetZoomAmount(1.0);
444 // stretch to the limits of the 16:9 screen.
445 // incorrect behaviour, but it's what the users want, so...
446 CDisplaySettings::GetInstance().SetPixelRatio((screenWidth
/ screenHeight
) * info
.fPixelRatio
/ sourceFrameRatio
);
447 bool nonlin
= (is43
&& CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_VIDEOPLAYER_STRETCH43
) == ViewModeStretch16x9Nonlin
) ||
448 m_videoSettings
.m_ViewMode
== ViewModeStretch16x9Nonlin
;
449 CDisplaySettings::GetInstance().SetNonLinearStretched(nonlin
);
451 else if (m_videoSettings
.m_ViewMode
== ViewModeOriginal
)
452 { // zoom image so that the height is the original size
453 CDisplaySettings::GetInstance().SetPixelRatio(1.0);
454 // get the size of the media file
455 // calculate the desired output ratio
456 float outputFrameRatio
= sourceFrameRatio
* CDisplaySettings::GetInstance().GetPixelRatio() / info
.fPixelRatio
;
457 // now calculate the correct zoom amount. First zoom to full width.
458 float newHeight
= screenWidth
/ outputFrameRatio
;
459 if (newHeight
> screenHeight
)
460 { // zoom to full height
461 newHeight
= screenHeight
;
463 // now work out the zoom amount so that no zoom is done
464 CDisplaySettings::GetInstance().SetZoomAmount(m_sourceHeight
/ newHeight
);
466 else if (m_videoSettings
.m_ViewMode
== ViewModeCustom
)
468 CDisplaySettings::GetInstance().SetZoomAmount(m_videoSettings
.m_CustomZoomAmount
);
469 CDisplaySettings::GetInstance().SetPixelRatio(m_videoSettings
.m_CustomPixelRatio
);
470 CDisplaySettings::GetInstance().SetNonLinearStretched(m_videoSettings
.m_CustomNonLinStretch
);
471 CDisplaySettings::GetInstance().SetVerticalShift(m_videoSettings
.m_CustomVerticalShift
);
473 else if (m_videoSettings
.m_ViewMode
== ViewModeZoom120Width
)
475 float fitHeightZoom
= sourceFrameRatio
* screenHeight
/ (info
.fPixelRatio
* screenWidth
);
476 CDisplaySettings::GetInstance().SetPixelRatio(1.0f
);
477 CDisplaySettings::GetInstance().SetZoomAmount(fitHeightZoom
< 1.0f
? 1.0f
: (fitHeightZoom
> 1.2f
? 1.2f
: fitHeightZoom
));
479 else if (m_videoSettings
.m_ViewMode
== ViewModeZoom110Width
)
481 float fitHeightZoom
= sourceFrameRatio
* screenHeight
/ (info
.fPixelRatio
* screenWidth
);
482 CDisplaySettings::GetInstance().SetPixelRatio(1.0f
);
483 CDisplaySettings::GetInstance().SetZoomAmount(fitHeightZoom
< 1.0f
? 1.0f
: (fitHeightZoom
> 1.1f
? 1.1f
: fitHeightZoom
));
485 else // if (CMediaSettings::GetInstance().GetCurrentVideoSettings().m_ViewMode == ViewModeNormal)
487 CDisplaySettings::GetInstance().SetPixelRatio(1.0);
488 CDisplaySettings::GetInstance().SetZoomAmount(1.0);
492 m_videoSettings
.m_CustomZoomAmount
= CDisplaySettings::GetInstance().GetZoomAmount();
493 m_videoSettings
.m_CustomPixelRatio
= CDisplaySettings::GetInstance().GetPixelRatio();
494 m_videoSettings
.m_CustomNonLinStretch
= CDisplaySettings::GetInstance().IsNonLinearStretched();
495 m_videoSettings
.m_CustomVerticalShift
= CDisplaySettings::GetInstance().GetVerticalShift();
498 void CBaseRenderer::MarkDirty()
500 CServiceBroker::GetGUI()->GetWindowManager().MarkDirty(m_destRect
);
503 void CBaseRenderer::EnableAlwaysClip()
508 void CBaseRenderer::SetVideoSettings(const CVideoSettings
&settings
)
510 m_videoSettings
= settings
;
513 void CBaseRenderer::SettingOptionsRenderMethodsFiller(
514 const std::shared_ptr
<const CSetting
>& setting
,
515 std::vector
<IntegerSettingOption
>& list
,
519 list
.emplace_back(g_localizeStrings
.Get(13416), RENDER_METHOD_AUTO
);
522 list
.push_back(IntegerSettingOption(g_localizeStrings
.Get(16319), RENDER_METHOD_DXVA
));
523 list
.push_back(IntegerSettingOption(g_localizeStrings
.Get(13431), RENDER_METHOD_D3D_PS
));
524 list
.push_back(IntegerSettingOption(g_localizeStrings
.Get(13419), RENDER_METHOD_SOFTWARE
));