static inline void render_item(struct obs_scene_item *item) { if (item->crop_render) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); uint32_t cx = calc_cx(item, width); uint32_t cy = calc_cy(item, height); if (cx && cy && gs_texrender_begin(item->crop_render, cx, cy)) { float cx_scale = (float)width / (float)cx; float cy_scale = (float)height / (float)cy; gs_matrix_scale3f(cx_scale, cy_scale, 1.0f); gs_matrix_translate3f( -(float)item->crop.left, -(float)item->crop.top, 0.0f); obs_source_video_render(item->source); gs_texrender_end(item->crop_render); } } gs_matrix_push(); gs_matrix_mul(&item->draw_transform); if (item->crop_render) { gs_texture_t *tex = gs_texrender_get_texture(item->crop_render); while (gs_effect_loop(obs->video.default_effect, "Draw")) obs_source_draw(tex, 0, 0, 0, 0, 0); } else { obs_source_video_render(item->source); } gs_matrix_pop(); }
static void stinger_video_render(void *data, gs_effect_t *effect) { struct stinger_info *s = data; float t = obs_transition_get_time(s->source); bool use_a = t < s->transition_point; enum obs_transition_target target = use_a ? OBS_TRANSITION_SOURCE_A : OBS_TRANSITION_SOURCE_B; if (!obs_transition_video_render_direct(s->source, target)) return; /* --------------------- */ float source_cx = (float)obs_source_get_width(s->source); float source_cy = (float)obs_source_get_height(s->source); uint32_t media_cx = obs_source_get_width(s->media_source); uint32_t media_cy = obs_source_get_height(s->media_source); if (!media_cx || !media_cy) return; float scale_x = source_cx / (float)media_cx; float scale_y = source_cy / (float)media_cy; gs_matrix_push(); gs_matrix_scale3f(scale_x, scale_y, 1.0f); obs_source_video_render(s->media_source); gs_matrix_pop(); UNUSED_PARAMETER(effect); }
static void scene_video_render(void *data) { struct obs_scene *scene = data; struct obs_scene_item *item; pthread_mutex_lock(&scene->mutex); item = scene->first_item; while (item) { if (obs_source_removed(item->source)) { struct obs_scene_item *del_item = item; item = item->next; obs_sceneitem_remove(del_item); continue; } gs_matrix_push(); gs_matrix_translate3f(item->origin.x, item->origin.y, 0.0f); gs_matrix_scale3f(item->scale.x, item->scale.y, 1.0f); gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD(-item->rot)); gs_matrix_translate3f(-item->pos.x, -item->pos.y, 0.0f); obs_source_video_render(item->source); gs_matrix_pop(); item = item->next; } pthread_mutex_unlock(&scene->mutex); }
void obs_source_video_render(obs_source_t source) { if (source->callbacks.video_render) { if (source->filters.num && !source->rendering_filter) { source->rendering_filter = true; obs_source_video_render(source->filters.array[0]); source->rendering_filter = false; } else { source->callbacks.video_render(source->data); } } else if (source->filter_target) { obs_source_video_render(source->filter_target); } else { obs_source_render_async_video(source); } }
void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) { OBSProjector *window = reinterpret_cast<OBSProjector*>(data); if (!window->ready) return; OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow()); OBSSource source = window->source; uint32_t targetCX; uint32_t targetCY; int x, y; int newCX, newCY; float scale; if (source) { targetCX = std::max(obs_source_get_width(source), 1u); targetCY = std::max(obs_source_get_height(source), 1u); } else { struct obs_video_info ovi; obs_get_video_info(&ovi); targetCX = ovi.base_width; targetCY = ovi.base_height; } GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale); newCX = int(scale * float(targetCX)); newCY = int(scale * float(targetCY)); startRegion(x, y, newCX, newCY, 0.0f, float(targetCX), 0.0f, float(targetCY)); if (window->type == ProjectorType::Preview && main->IsPreviewProgramMode()) { OBSSource curSource = main->GetCurrentSceneSource(); if (source != curSource) { obs_source_dec_showing(source); obs_source_inc_showing(curSource); source = curSource; window->source = source; } } if (source) obs_source_video_render(source); else obs_render_main_texture(); endRegion(); }
static inline void render_item(struct obs_scene_item *item) { if (item->item_render) { uint32_t width = obs_source_get_width(item->source); uint32_t height = obs_source_get_height(item->source); uint32_t cx = calc_cx(item, width); uint32_t cy = calc_cy(item, height); if (cx && cy && gs_texrender_begin(item->item_render, cx, cy)) { float cx_scale = (float)width / (float)cx; float cy_scale = (float)height / (float)cy; struct vec4 clear_color; vec4_zero(&clear_color); gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0); gs_ortho(0.0f, (float)width, 0.0f, (float)height, -100.0f, 100.0f); gs_matrix_scale3f(cx_scale, cy_scale, 1.0f); gs_matrix_translate3f( -(float)item->crop.left, -(float)item->crop.top, 0.0f); obs_source_video_render(item->source); gs_texrender_end(item->item_render); } } gs_matrix_push(); gs_matrix_mul(&item->draw_transform); if (item->item_render) { render_item_texture(item); } else { obs_source_video_render(item->source); } gs_matrix_pop(); }
static inline void render_filter_bypass(obs_source_t target, effect_t effect, bool use_matrix) { const char *tech_name = use_matrix ? "DrawMatrix" : "Draw"; technique_t tech = effect_gettechnique(effect, tech_name); size_t passes, i; passes = technique_begin(tech); for (i = 0; i < passes; i++) { technique_beginpass(tech, i); obs_source_video_render(target); technique_endpass(tech); } technique_end(tech); }
void obs_source_video_render(obs_source_t source) { if (source->callbacks.video_render) { if (source->filters.num && !source->rendering_filter) obs_source_render_filters(source); else obs_source_main_render(source); } else if (source->filter_target) { obs_source_video_render(source->filter_target); } else { obs_source_render_async_video(source); } }
void obs_source_process_filter(obs_source_t filter, effect_t effect, uint32_t width, uint32_t height, enum gs_color_format format, enum allow_direct_render allow_direct) { obs_source_t target, parent; uint32_t target_flags, parent_flags; int cx, cy; bool use_matrix, expects_def, can_directly; if (!filter) return; target = obs_filter_gettarget(filter); parent = obs_filter_getparent(filter); target_flags = target->info.output_flags; parent_flags = parent->info.output_flags; cx = obs_source_getwidth(target); cy = obs_source_getheight(target); use_matrix = !!(target_flags & OBS_SOURCE_COLOR_MATRIX); expects_def = !(parent_flags & OBS_SOURCE_CUSTOM_DRAW); can_directly = allow_direct == ALLOW_DIRECT_RENDERING; /* if the parent does not use any custom effects, and this is the last * filter in the chain for the parent, then render the parent directly * using the filter effect instead of rendering to texture to reduce * the total number of passes */ if (can_directly && expects_def && target == parent) { render_filter_bypass(target, effect, use_matrix); return; } if (!filter->filter_texrender) filter->filter_texrender = texrender_create(format, GS_ZS_NONE); if (texrender_begin(filter->filter_texrender, cx, cy)) { gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f); if (expects_def && parent == target) obs_source_default_render(parent, use_matrix); else obs_source_video_render(target); texrender_end(filter->filter_texrender); } /* --------------------------- */ render_filter_tex(texrender_gettexture(filter->filter_texrender), effect, width, height, use_matrix); }
void obs_source_video_render(obs_source_t source) { if (!source) return; if (source->filters.num && !source->rendering_filter) obs_source_render_filters(source); else if (source->info.video_render) obs_source_main_render(source); else if (source->filter_target) obs_source_video_render(source->filter_target); else obs_source_render_async_video(source); }
static inline void render_filter_bypass(obs_source_t target, effect_t effect, uint32_t width, uint32_t height, bool yuv) { const char *tech_name = yuv ? "DrawYUV" : "DrawRGB"; technique_t tech = effect_gettechnique(effect, tech_name); eparam_t diffuse = effect_getparambyname(effect, "diffuse"); size_t passes, i; passes = technique_begin(tech); for (i = 0; i < passes; i++) { technique_beginpass(tech, i); obs_source_video_render(target); technique_endpass(tech); } technique_end(tech); }
static void scene_video_render(void *data, gs_effect_t *effect) { DARRAY(struct obs_scene_item*) remove_items; struct obs_scene *scene = data; struct obs_scene_item *item; da_init(remove_items); video_lock(scene); item = scene->first_item; gs_blend_state_push(); gs_reset_blend_state(); while (item) { if (obs_source_removed(item->source)) { struct obs_scene_item *del_item = item; item = item->next; remove_without_release(del_item); da_push_back(remove_items, &del_item); continue; } if (source_size_changed(item)) update_item_transform(item); if (item->user_visible) { gs_matrix_push(); gs_matrix_mul(&item->draw_transform); obs_source_video_render(item->source); gs_matrix_pop(); } item = item->next; } gs_blend_state_pop(); video_unlock(scene); for (size_t i = 0; i < remove_items.num; i++) obs_sceneitem_release(remove_items.array[i]); da_free(remove_items); UNUSED_PARAMETER(effect); }
static inline void render_child(obs_source_t *transition, obs_source_t *child, size_t idx) { uint32_t cx = transition->transition_actual_cx; uint32_t cy = transition->transition_actual_cy; struct vec4 blank; if (!child) return; if (gs_texrender_begin(transition->transition_texrender[idx], cx, cy)) { vec4_zero(&blank); gs_clear(GS_CLEAR_COLOR, &blank, 0.0f, 0); gs_matrix_push(); gs_matrix_mul(&transition->transition_matrices[idx]); obs_source_video_render(child); gs_matrix_pop(); gs_texrender_end(transition->transition_texrender[idx]); } }
void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy) { OBSProjector *window = reinterpret_cast<OBSProjector*>(data); uint32_t targetCX; uint32_t targetCY; int x, y; int newCX, newCY; float scale; if (window->source) { targetCX = std::max(obs_source_get_width(window->source), 1u); targetCY = std::max(obs_source_get_height(window->source), 1u); } else { struct obs_video_info ovi; obs_get_video_info(&ovi); targetCX = ovi.base_width; targetCY = ovi.base_height; } GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale); newCX = int(scale * float(targetCX)); newCY = int(scale * float(targetCY)); gs_viewport_push(); gs_projection_push(); gs_ortho(0.0f, float(targetCX), 0.0f, float(targetCY), -100.0f, 100.0f); gs_set_viewport(x, y, newCX, newCY); if (window->source) obs_source_video_render(window->source); else obs_render_main_view(); gs_projection_pop(); gs_viewport_pop(); }
static void render_display(struct obs_display *display) { size_t i; render_begin(display); for (i = 0; i < MAX_CHANNELS; i++) { struct obs_source **p_source; p_source = (display) ? display->channels+i : obs->data.channels+i; if (*p_source) { if ((*p_source)->removed) { obs_source_release(*p_source); *p_source = NULL; } else { obs_source_video_render(*p_source); } } } render_end(display); }
void obs_view_render(obs_view_t *view) { if (!view) return; pthread_mutex_lock(&view->channels_mutex); for (size_t i = 0; i < MAX_CHANNELS; i++) { struct obs_source *source; source = view->channels[i]; if (source) { if (source->removed) { obs_source_release(source); view->channels[i] = NULL; } else { obs_source_video_render(source); } } } pthread_mutex_unlock(&view->channels_mutex); }
void obs_source_process_filter(obs_source_t filter, texrender_t texrender, effect_t effect, uint32_t width, uint32_t height, enum allow_direct_render allow_direct) { obs_source_t target = obs_filter_gettarget(filter); obs_source_t parent = obs_filter_getparent(filter); uint32_t target_flags = obs_source_get_output_flags(target); uint32_t parent_flags = obs_source_get_output_flags(parent); int cx = obs_source_getwidth(target); int cy = obs_source_getheight(target); bool yuv = (target_flags & SOURCE_YUV) != 0; bool expects_def = (parent_flags & SOURCE_DEFAULT_EFFECT) != 0; bool can_directly = allow_direct == ALLOW_DIRECT_RENDERING; /* if the parent does not use any custom effects, and this is the last * filter in the chain for the parent, then render the parent directly * using the filter effect instead of rendering to texture to reduce * the total number of passes */ if (can_directly && expects_def && target == parent) { render_filter_bypass(target, effect, width, height, yuv); return; } if (texrender_begin(texrender, cx, cy)) { gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f); if (expects_def && parent == target) obs_source_default_render(parent, yuv); else obs_source_video_render(target); texrender_end(texrender); } /* --------------------------- */ render_filter_tex(texrender_gettexture(texrender), effect, width, height, yuv); }
static inline void obs_source_render_filters(obs_source_t source) { source->rendering_filter = true; obs_source_video_render(source->filters.array[0]); source->rendering_filter = false; }
void obs_transition_video_render(obs_source_t *transition, obs_transition_video_render_callback_t callback) { struct transition_state state; bool locked = false; bool stopped = false; bool video_stopped = false; float t; if (!transition_valid(transition, "obs_transition_video_render")) return; t = get_video_time(transition); lock_transition(transition); if (t >= 1.0f && transition->transitioning_video) { transition->transitioning_video = false; video_stopped = true; if (!transition->transitioning_audio) { obs_transition_stop(transition); stopped = true; } } copy_transition_state(transition, &state); unlock_transition(transition); if (state.transitioning_video) locked = trylock_textures(transition) == 0; if (state.transitioning_video && locked && callback) { gs_texture_t *tex[2]; for (size_t i = 0; i < 2; i++) { if (state.s[i]) { render_child(transition, state.s[i], i); tex[i] = get_texture(transition, i); if (!tex[i]) tex[i] = obs->video.transparent_texture; } else { tex[i] = obs->video.transparent_texture; } } callback(transition->context.data, tex[0], tex[1], t, transition->transition_actual_cx, transition->transition_actual_cy); } else if (state.transitioning_audio) { if (state.s[1]) obs_source_video_render(state.s[1]); } else { if (state.s[0]) obs_source_video_render(state.s[0]); } if (locked) unlock_textures(transition); obs_source_release(state.s[0]); obs_source_release(state.s[1]); if (video_stopped) obs_source_dosignal(transition, "source_transition_video_stop", "transition_video_stop"); if (stopped) obs_source_dosignal(transition, "source_transition_stop", "transition_stop"); }
void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy) { OBSProjector *window = (OBSProjector *)data; if (updatingMultiview || !window->ready) return; OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window(); uint32_t targetCX, targetCY; int x, y; float scale; targetCX = (uint32_t)window->fw; targetCY = (uint32_t)window->fh; GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale); OBSSource previewSrc = main->GetCurrentSceneSource(); 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_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy); }; auto setRegion = [&](float bx, float by, float cx, float cy) { float vX = int(x + bx * scale); float vY = int(y + by * scale); float vCX = int(cx * scale); float vCY = int(cy * scale); float oL = bx; float oT = by; float oR = (bx + cx); float oB = (by + cy); startRegion(vX, vY, vCX, vCY, oL, oR, oT, oB); }; auto calcBaseSource = [&](size_t i) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = (i % 6) * window->scenesCX; window->sourceY = window->pvwprgCY + (i / 6) * window->scenesCY; break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->pvwprgCX; window->sourceY = (i / 2 ) * window->scenesCY; if (i % 2 != 0) window->sourceX += window->scenesCX; break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = 0; window->sourceY = (i / 2 ) * window->scenesCY; if (i % 2 != 0) window->sourceX = window->scenesCX; break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = 0; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->scenesCY; } break; default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES: if (i < 4) { window->sourceX = (float(i) * window->scenesCX); window->sourceY = window->pvwprgCY; } else { window->sourceX = (float(i - 4) * window->scenesCX); window->sourceY = window->pvwprgCY + window->scenesCY; } } window->siX = window->sourceX + window->thickness; window->siY = window->sourceY + window->thickness; }; auto calcPreviewProgram = [&](bool program) { switch (multiviewLayout) { case MultiviewLayout::HORIZONTAL_TOP_24_SCENES: window->sourceX = window->thickness + window->pvwprgCX / 2; window->sourceY = window->thickness; window->labelX = window->offset + window->pvwprgCX / 2; window->labelY = window->pvwprgCY * 0.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } break; case MultiviewLayout::VERTICAL_LEFT_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceY = window->thickness; window->labelY = window->pvwprgCY * 0.85f; } break; case MultiviewLayout::VERTICAL_RIGHT_8_SCENES: window->sourceX = window->pvwprgCX + window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->pvwprgCX + window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceY = window->thickness; window->labelY = window->pvwprgCY * 0.85f; } break; case MultiviewLayout::HORIZONTAL_BOTTOM_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->pvwprgCY + window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 1.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } break; default: // MultiviewLayout::HORIZONTAL_TOP_8_SCENES: window->sourceX = window->thickness; window->sourceY = window->thickness; window->labelX = window->offset; window->labelY = window->pvwprgCY * 0.85f; if (program) { window->sourceX += window->pvwprgCX; window->labelX += window->pvwprgCX; } } }; auto paintAreaWithColor = [&](float tx, float ty, float cx, float cy, uint32_t color) { gs_matrix_push(); gs_matrix_translate3f(tx, ty, 0.0f); drawBox(cx, cy, color); gs_matrix_pop(); }; // Define the whole usable region for the multiview startRegion(x, y, targetCX * scale, targetCY * scale, 0.0f, window->fw, 0.0f, window->fh); // Change the background color to highlight all sources drawBox(window->fw, window->fh, outerColor); /* ----------------------------- */ /* draw sources */ for (size_t i = 0; i < maxSrcs; i++) { // Handle all the offsets calcBaseSource(i); if (i >= numSrcs) { // Just paint the background and continue paintAreaWithColor(window->sourceX, window->sourceY, window->scenesCX, window->scenesCY, outerColor); paintAreaWithColor(window->siX, window->siY, window->siCX, window->siCY, backgroundColor); continue; } OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]); // We have a source. Now chose the proper highlight color uint32_t colorVal = outerColor; if (src == programSrc) colorVal = programColor; else if (src == previewSrc) colorVal = studioMode ? previewColor : programColor; // Paint the background paintAreaWithColor(window->sourceX, window->sourceY, window->scenesCX, window->scenesCY, colorVal); paintAreaWithColor(window->siX, window->siY, window->siCX, window->siCY, backgroundColor); /* ----------- */ // Render the source gs_matrix_push(); gs_matrix_translate3f(window->siX, window->siY, 0.0f); gs_matrix_scale3f(window->siScaleX, window->siScaleY, 1.0f); setRegion(window->siX, window->siY, window->siCX, window->siCY); obs_source_video_render(src); endRegion(); gs_matrix_pop(); /* ----------- */ // Render the label if (!drawLabel) continue; obs_source *label = window->multiviewLabels[i + 2]; if (!label) continue; window->offset = labelOffset(label, window->scenesCX); gs_matrix_push(); gs_matrix_translate3f(window->sourceX + window->offset, (window->scenesCY * 0.85f) + window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(label), obs_source_get_height(label) + int(window->sourceY * 0.015f), labelColor); obs_source_video_render(label); gs_matrix_pop(); } /* ----------------------------- */ /* draw preview */ obs_source_t *previewLabel = window->multiviewLabels[0]; window->offset = labelOffset(previewLabel, window->pvwprgCX); calcPreviewProgram(false); // Paint the background paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY, backgroundColor); // Scale and Draw the preview gs_matrix_push(); gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); setRegion(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY); if (studioMode) 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); } endRegion(); gs_matrix_pop(); /* ----------- */ // Draw the Label if (drawLabel) { gs_matrix_push(); gs_matrix_translate3f(window->labelX, window->labelY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(previewLabel), obs_source_get_height(previewLabel) + int(window->pvwprgCX * 0.015f), labelColor); obs_source_video_render(previewLabel); gs_matrix_pop(); } /* ----------------------------- */ /* draw program */ obs_source_t *programLabel = window->multiviewLabels[1]; window->offset = labelOffset(programLabel, window->pvwprgCX); calcPreviewProgram(true); paintAreaWithColor(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY, backgroundColor); // Scale and Draw the program gs_matrix_push(); gs_matrix_translate3f(window->sourceX, window->sourceY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); setRegion(window->sourceX, window->sourceY, window->ppiCX, window->ppiCY); obs_render_main_texture(); endRegion(); gs_matrix_pop(); /* ----------- */ // Draw the Label if (drawLabel) { gs_matrix_push(); gs_matrix_translate3f(window->labelX, window->labelY, 0.0f); gs_matrix_scale3f(window->ppiScaleX, window->ppiScaleY, 1.0f); drawBox(obs_source_get_width(programLabel), obs_source_get_height(programLabel) + int(window->pvwprgCX * 0.015f), labelColor); obs_source_video_render(programLabel); gs_matrix_pop(); } // Region for future usage with aditional info. if (multiviewLayout == MultiviewLayout::HORIZONTAL_TOP_24_SCENES) { // Just paint the background for now paintAreaWithColor(window->thickness, window->thickness, window->siCX, window->siCY * 2 + window->thicknessx2, backgroundColor); paintAreaWithColor(window->thickness + 2.5 * ( window->thicknessx2 + window->ppiCX), window->thickness, window->siCX, window->siCY * 2 + window->thicknessx2, backgroundColor); } endRegion(); }