UI: Add option to draw safe areas in preview

master
Clayton Groeneveld 2021-08-19 23:56:49 -05:00 committed by Jim
parent 1b29bfc884
commit afd58a78e5
7 changed files with 165 additions and 92 deletions

View File

@ -17,6 +17,9 @@
#pragma once
#include <graphics/vec4.h>
#include <graphics/matrix4.h>
static inline void GetScaleAndCenterPos(int baseCX, int baseCY, int windowCX,
int windowCY, int &x, int &y,
float &scale)
@ -53,3 +56,90 @@ static inline QSize GetPixelSize(QWidget *widget)
{
return widget->size() * widget->devicePixelRatioF();
}
#define OUTLINE_COLOR 0xFFD0D0D0
#define LINE_LENGTH 0.1f
// Rec. ITU-R BT.1848-1 / EBU R 95
#define ACTION_SAFE_PERCENT 0.035f // 3.5%
#define GRAPHICS_SAFE_PERCENT 0.05f // 5.0%
#define FOURBYTHREE_SAFE_PERCENT 0.1625f // 16.25%
static inline void InitSafeAreas(gs_vertbuffer_t **actionSafeMargin,
gs_vertbuffer_t **graphicsSafeMargin,
gs_vertbuffer_t **fourByThreeSafeMargin,
gs_vertbuffer_t **leftLine,
gs_vertbuffer_t **topLine,
gs_vertbuffer_t **rightLine)
{
obs_enter_graphics();
// All essential action should be placed inside this area
gs_render_start(true);
gs_vertex2f(ACTION_SAFE_PERCENT, ACTION_SAFE_PERCENT);
gs_vertex2f(ACTION_SAFE_PERCENT, 1 - ACTION_SAFE_PERCENT);
gs_vertex2f(1 - ACTION_SAFE_PERCENT, 1 - ACTION_SAFE_PERCENT);
gs_vertex2f(1 - ACTION_SAFE_PERCENT, ACTION_SAFE_PERCENT);
gs_vertex2f(ACTION_SAFE_PERCENT, ACTION_SAFE_PERCENT);
*actionSafeMargin = gs_render_save();
// All graphics should be placed inside this area
gs_render_start(true);
gs_vertex2f(GRAPHICS_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);
gs_vertex2f(GRAPHICS_SAFE_PERCENT, 1 - GRAPHICS_SAFE_PERCENT);
gs_vertex2f(1 - GRAPHICS_SAFE_PERCENT, 1 - GRAPHICS_SAFE_PERCENT);
gs_vertex2f(1 - GRAPHICS_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);
gs_vertex2f(GRAPHICS_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);
*graphicsSafeMargin = gs_render_save();
// 4:3 safe area for widescreen
gs_render_start(true);
gs_vertex2f(FOURBYTHREE_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);
gs_vertex2f(1 - FOURBYTHREE_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);
gs_vertex2f(1 - FOURBYTHREE_SAFE_PERCENT, 1 - GRAPHICS_SAFE_PERCENT);
gs_vertex2f(FOURBYTHREE_SAFE_PERCENT, 1 - GRAPHICS_SAFE_PERCENT);
gs_vertex2f(FOURBYTHREE_SAFE_PERCENT, GRAPHICS_SAFE_PERCENT);
*fourByThreeSafeMargin = gs_render_save();
gs_render_start(true);
gs_vertex2f(0.0f, 0.5f);
gs_vertex2f(LINE_LENGTH, 0.5f);
*leftLine = gs_render_save();
gs_render_start(true);
gs_vertex2f(0.5f, 0.0f);
gs_vertex2f(0.5f, LINE_LENGTH);
*topLine = gs_render_save();
gs_render_start(true);
gs_vertex2f(1.0f, 0.5f);
gs_vertex2f(1 - LINE_LENGTH, 0.5f);
*rightLine = gs_render_save();
obs_leave_graphics();
}
static inline void RenderSafeAreas(gs_vertbuffer_t *vb, int cx, int cy)
{
if (!vb)
return;
matrix4 transform;
matrix4_identity(&transform);
transform.x.x = cx;
transform.y.y = cy;
gs_load_vertexbuffer(vb);
gs_matrix_push();
gs_matrix_mul(&transform);
gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
gs_effect_set_color(color, OUTLINE_COLOR);
while (gs_effect_loop(solid, "Solid"))
gs_draw(GS_LINESTRIP, 0, 0);
gs_matrix_pop();
}

View File

@ -595,6 +595,13 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="previewSafeAreas">
<property name="text">
<string>Basic.Settings.General.Multiview.DrawSafeAreas</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="overflowHide">
<property name="text">

View File

@ -458,6 +458,8 @@ OBSBasic::OBSBasic(QWidget *parent)
connect(ui->broadcastButton, &QPushButton::clicked, this,
&OBSBasic::BroadcastButtonClicked);
UpdatePreviewSafeAreas();
}
static void SaveAudioDevice(const char *name, int channel, obs_data_t *parent,
@ -1642,6 +1644,8 @@ void OBSBasic::InitPrimitives()
}
circle = gs_render_save();
InitSafeAreas(&actionSafeMargin, &graphicsSafeMargin,
&fourByThreeSafeMargin, &leftLine, &topLine, &rightLine);
obs_leave_graphics();
}
@ -2643,6 +2647,12 @@ OBSBasic::~OBSBasic()
gs_vertexbuffer_destroy(boxRight);
gs_vertexbuffer_destroy(boxBottom);
gs_vertexbuffer_destroy(circle);
gs_vertexbuffer_destroy(actionSafeMargin);
gs_vertexbuffer_destroy(graphicsSafeMargin);
gs_vertexbuffer_destroy(fourByThreeSafeMargin);
gs_vertexbuffer_destroy(leftLine);
gs_vertexbuffer_destroy(topLine);
gs_vertexbuffer_destroy(rightLine);
obs_leave_graphics();
/* When shutting down, sometimes source references can get in to the
@ -4081,6 +4091,19 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy)
window->ui->preview->DrawSceneEditing();
uint32_t targetCX = window->previewCX;
uint32_t targetCY = window->previewCY;
if (window->drawSafeAreas) {
RenderSafeAreas(window->actionSafeMargin, targetCX, targetCY);
RenderSafeAreas(window->graphicsSafeMargin, targetCX, targetCY);
RenderSafeAreas(window->fourByThreeSafeMargin, targetCX,
targetCY);
RenderSafeAreas(window->leftLine, targetCX, targetCY);
RenderSafeAreas(window->topLine, targetCX, targetCY);
RenderSafeAreas(window->rightLine, targetCX, targetCY);
}
/* --------------------------------------- */
gs_projection_pop();
@ -9650,3 +9673,9 @@ void OBSBasic::ShowStatusBarMessage(const QString &message)
ui->statusbar->clearMessage();
ui->statusbar->showMessage(message, 10000);
}
void OBSBasic::UpdatePreviewSafeAreas()
{
drawSafeAreas = config_get_bool(App()->GlobalConfig(), "BasicWindow",
"ShowSafeAreas");
}

View File

@ -243,6 +243,13 @@ private:
gs_vertbuffer_t *boxBottom = nullptr;
gs_vertbuffer_t *circle = nullptr;
gs_vertbuffer_t *actionSafeMargin = nullptr;
gs_vertbuffer_t *graphicsSafeMargin = nullptr;
gs_vertbuffer_t *fourByThreeSafeMargin = nullptr;
gs_vertbuffer_t *leftLine = nullptr;
gs_vertbuffer_t *topLine = nullptr;
gs_vertbuffer_t *rightLine = nullptr;
int previewX = 0, previewY = 0;
int previewCX = 0, previewCY = 0;
float previewScale = 0.0f;
@ -570,6 +577,9 @@ private:
#endif
void BroadcastButtonClicked();
void UpdatePreviewSafeAreas();
bool drawSafeAreas = false;
public slots:
void DeferSaveBegin();
void DeferSaveEnd();

View File

@ -408,6 +408,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->overflowHide, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->overflowAlwaysVisible,CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->overflowSelectionHide,CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->previewSafeAreas, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->automaticSearch, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->doubleClickSwitch, CHECK_CHANGED, GENERAL_CHANGED);
HookWidget(ui->studioPortraitLayout, CHECK_CHANGED, GENERAL_CHANGED);
@ -1313,6 +1314,10 @@ void OBSBasicSettings::LoadGeneralSettings()
GetGlobalConfig(), "BasicWindow", "OverflowSelectionHidden");
ui->overflowSelectionHide->setChecked(overflowSelectionHide);
bool safeAreas = config_get_bool(GetGlobalConfig(), "BasicWindow",
"ShowSafeAreas");
ui->previewSafeAreas->setChecked(safeAreas);
bool automaticSearch = config_get_bool(GetGlobalConfig(), "General",
"AutomaticCollectionSearch");
ui->automaticSearch->setChecked(automaticSearch);
@ -3021,6 +3026,12 @@ void OBSBasicSettings::SaveGeneralSettings()
config_set_bool(GetGlobalConfig(), "BasicWindow",
"OverflowSelectionHidden",
ui->overflowSelectionHide->isChecked());
if (WidgetChanged(ui->previewSafeAreas)) {
config_set_bool(GetGlobalConfig(), "BasicWindow",
"ShowSafeAreas",
ui->previewSafeAreas->isChecked());
main->UpdatePreviewSafeAreas();
}
if (WidgetChanged(ui->doubleClickSwitch))
config_set_bool(GetGlobalConfig(), "BasicWindow",
"TransitionOnDoubleClick",

View File

@ -70,58 +70,9 @@ OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, int monitor,
&OBSProjector::ScreenRemoved);
if (type == ProjectorType::Multiview) {
obs_enter_graphics();
// All essential action should be placed inside this area
gs_render_start(true);
gs_vertex2f(actionSafePercentage, actionSafePercentage);
gs_vertex2f(actionSafePercentage, 1 - actionSafePercentage);
gs_vertex2f(1 - actionSafePercentage, 1 - actionSafePercentage);
gs_vertex2f(1 - actionSafePercentage, actionSafePercentage);
gs_vertex2f(actionSafePercentage, actionSafePercentage);
actionSafeMargin = gs_render_save();
// All graphics should be placed inside this area
gs_render_start(true);
gs_vertex2f(graphicsSafePercentage, graphicsSafePercentage);
gs_vertex2f(graphicsSafePercentage, 1 - graphicsSafePercentage);
gs_vertex2f(1 - graphicsSafePercentage,
1 - graphicsSafePercentage);
gs_vertex2f(1 - graphicsSafePercentage, graphicsSafePercentage);
gs_vertex2f(graphicsSafePercentage, graphicsSafePercentage);
graphicsSafeMargin = gs_render_save();
// 4:3 safe area for widescreen
gs_render_start(true);
gs_vertex2f(fourByThreeSafePercentage, graphicsSafePercentage);
gs_vertex2f(1 - fourByThreeSafePercentage,
graphicsSafePercentage);
gs_vertex2f(1 - fourByThreeSafePercentage,
1 - graphicsSafePercentage);
gs_vertex2f(fourByThreeSafePercentage,
1 - graphicsSafePercentage);
gs_vertex2f(fourByThreeSafePercentage, graphicsSafePercentage);
fourByThreeSafeMargin = gs_render_save();
gs_render_start(true);
gs_vertex2f(0.0f, 0.5f);
gs_vertex2f(lineLength, 0.5f);
leftLine = gs_render_save();
gs_render_start(true);
gs_vertex2f(0.5f, 0.0f);
gs_vertex2f(0.5f, lineLength);
topLine = gs_render_save();
gs_render_start(true);
gs_vertex2f(1.0f, 0.5f);
gs_vertex2f(1 - lineLength, 0.5f);
rightLine = gs_render_save();
obs_leave_graphics();
solid = obs_get_base_effect(OBS_EFFECT_SOLID);
color = gs_effect_get_param_by_name(solid, "color");
InitSafeAreas(&actionSafeMargin, &graphicsSafeMargin,
&fourByThreeSafeMargin, &leftLine, &topLine,
&rightLine);
UpdateMultiview();
multiviewProjectors.push_back(this);
@ -297,31 +248,13 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)
OBSSource programSrc = main->GetProgramSource();
bool studioMode = main->IsPreviewProgramMode();
auto renderVB = [&](gs_vertbuffer_t *vb, int cx, int cy,
uint32_t colorVal) {
if (!vb)
return;
matrix4 transform;
matrix4_identity(&transform);
transform.x.x = cx;
transform.y.y = cy;
gs_load_vertexbuffer(vb);
gs_matrix_push();
gs_matrix_mul(&transform);
gs_effect_set_color(window->color, colorVal);
while (gs_effect_loop(window->solid, "Solid"))
gs_draw(GS_LINESTRIP, 0, 0);
gs_matrix_pop();
};
auto drawBox = [&](float cx, float cy, uint32_t colorVal) {
gs_effect_set_color(window->color, colorVal);
while (gs_effect_loop(window->solid, "Solid"))
gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
gs_eparam_t *color =
gs_effect_get_param_by_name(solid, "color");
gs_effect_set_color(color, colorVal);
while (gs_effect_loop(solid, "Solid"))
gs_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy);
};
@ -549,17 +482,17 @@ void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)
obs_source_video_render(previewSrc);
else
obs_render_main_texture();
if (drawSafeArea) {
renderVB(window->actionSafeMargin, targetCX, targetCY,
outerColor);
renderVB(window->graphicsSafeMargin, targetCX, targetCY,
outerColor);
renderVB(window->fourByThreeSafeMargin, targetCX, targetCY,
outerColor);
renderVB(window->leftLine, targetCX, targetCY, outerColor);
renderVB(window->topLine, targetCX, targetCY, outerColor);
renderVB(window->rightLine, targetCX, targetCY, outerColor);
RenderSafeAreas(window->actionSafeMargin, targetCX, targetCY);
RenderSafeAreas(window->graphicsSafeMargin, targetCX, targetCY);
RenderSafeAreas(window->fourByThreeSafeMargin, targetCX,
targetCY);
RenderSafeAreas(window->leftLine, targetCX, targetCY);
RenderSafeAreas(window->topLine, targetCX, targetCY);
RenderSafeAreas(window->rightLine, targetCX, targetCY);
}
endRegion();
gs_matrix_pop();

View File

@ -49,8 +49,6 @@ private:
gs_vertbuffer_t *leftLine = nullptr;
gs_vertbuffer_t *topLine = nullptr;
gs_vertbuffer_t *rightLine = nullptr;
gs_effect_t *solid = nullptr;
gs_eparam_t *color = nullptr;
// Multiview position helpers
float thickness = 4;
float offset, thicknessx2 = thickness * 2, pvwprgCX, pvwprgCY, sourceX,
@ -58,11 +56,6 @@ private:
siX, siY, siCX, siCY, ppiScaleX, ppiScaleY, siScaleX,
siScaleY, fw, fh, ratio;
float lineLength = 0.1f;
// Rec. ITU-R BT.1848-1 / EBU R 95
float actionSafePercentage = 0.035f; // 3.5%
float graphicsSafePercentage = 0.05f; // 5.0%
float fourByThreeSafePercentage = 0.1625f; // 16.25%
bool ready = false;
// argb colors