static inline void obs_source_draw_texture(struct obs_source *source, effect_t effect, float *color_matrix, float const *color_range_min, float const *color_range_max) { texture_t tex = source->async_texture; eparam_t param; if (source->async_convert_texrender) tex = texrender_gettexture(source->async_convert_texrender); if (color_range_min) { size_t const size = sizeof(float) * 3; param = effect_getparambyname(effect, "color_range_min"); effect_setval(effect, param, color_range_min, size); } if (color_range_max) { size_t const size = sizeof(float) * 3; param = effect_getparambyname(effect, "color_range_max"); effect_setval(effect, param, color_range_max, size); } if (color_matrix) { param = effect_getparambyname(effect, "color_matrix"); effect_setval(effect, param, color_matrix, sizeof(float) * 16); } param = effect_getparambyname(effect, "image"); effect_settexture(effect, param, tex); gs_draw_sprite(tex, source->async_flip ? GS_FLIP_V : 0, 0, 0); }
static void render_nv12(struct obs_core_video *video, gs_texture_t *target, int cur_texture, int prev_texture, const char *tech_name, uint32_t width, uint32_t height) { gs_texture_t *texture = video->output_textures[prev_texture]; gs_effect_t *effect = video->conversion_effect; gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_technique_t *tech = gs_effect_get_technique(effect, tech_name); size_t passes, i; gs_effect_set_texture(image, texture); gs_set_render_target(target, NULL); set_render_size(width, height); gs_enable_blending(false); passes = gs_technique_begin(tech); for (i = 0; i < passes; i++) { gs_technique_begin_pass(tech, i); gs_draw_sprite(texture, 0, width, height); gs_technique_end_pass(tech); } gs_technique_end(tech); gs_enable_blending(true); }
static void random_video_render(void *data, effect_t effect) { struct random_tex *rt = data; eparam_t image = effect_getparambyname(effect, "image"); effect_settexture(effect, image, rt->texture); gs_draw_sprite(rt->texture, 0, 0, 0); }
void XCompcapMain::render(gs_effect_t *effect) { if (!p->win) return; PLock lock(&p->lock, true); effect = obs_get_base_effect(OBS_EFFECT_OPAQUE); if (!lock.isLocked() || !p->tex) return; gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, p->tex); while (gs_effect_loop(effect, "Draw")) { gs_draw_sprite(p->tex, 0, 0, 0); } if (p->cursor && p->gltex && p->show_cursor && !p->cursor_outside) { effect = obs_get_base_effect(OBS_EFFECT_DEFAULT); while (gs_effect_loop(effect, "Draw")) { xcursor_render(p->cursor); } } }
static void obs_source_draw_texture(texture_t tex, struct source_frame *frame) { effect_t effect = obs->video.default_effect; bool yuv = is_yuv(frame->format); const char *type = yuv ? "DrawYUV" : "DrawRGB"; technique_t tech; eparam_t param; if (!upload_frame(tex, frame)) return; tech = effect_gettechnique(effect, type); technique_begin(tech); technique_beginpass(tech, 0); if (yuv) { param = effect_getparambyname(effect, "yuv_matrix"); effect_setval(effect, param, frame->yuv_matrix, sizeof(float) * 16); } param = effect_getparambyname(effect, "diffuse"); effect_settexture(effect, param, tex); gs_draw_sprite(tex, frame->flip ? GS_FLIP_V : 0, 0, 0); technique_endpass(tech); technique_end(tech); }
void deinterlace_render(obs_source_t *s) { gs_effect_t *effect = s->deinterlace_effect; uint64_t frame2_ts; gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_eparam_t *prev = gs_effect_get_param_by_name(effect, "previous_image"); gs_eparam_t *field = gs_effect_get_param_by_name(effect, "field_order"); gs_eparam_t *frame2 = gs_effect_get_param_by_name(effect, "frame2"); gs_eparam_t *dimensions = gs_effect_get_param_by_name(effect, "dimensions"); struct vec2 size = {(float)s->async_width, (float)s->async_height}; bool yuv = format_is_yuv(s->async_format); bool limited_range = yuv && !s->async_full_range; const char *tech = yuv ? "DrawMatrix" : "Draw"; gs_texture_t *cur_tex = s->async_texrender ? gs_texrender_get_texture(s->async_texrender) : s->async_texture; gs_texture_t *prev_tex = s->async_prev_texrender ? gs_texrender_get_texture(s->async_prev_texrender) : s->async_prev_texture; if (!cur_tex || !prev_tex || !s->async_width || !s->async_height) return; gs_effect_set_texture(image, cur_tex); gs_effect_set_texture(prev, prev_tex); gs_effect_set_int(field, s->deinterlace_top_first); gs_effect_set_vec2(dimensions, &size); if (yuv) { gs_eparam_t *color_matrix = gs_effect_get_param_by_name( effect, "color_matrix"); gs_effect_set_val(color_matrix, s->async_color_matrix, sizeof(float) * 16); } if (limited_range) { const size_t size = sizeof(float) * 3; gs_eparam_t *color_range_min = gs_effect_get_param_by_name( effect, "color_range_min"); gs_eparam_t *color_range_max = gs_effect_get_param_by_name( effect, "color_range_max"); gs_effect_set_val(color_range_min, s->async_color_range_min, size); gs_effect_set_val(color_range_max, s->async_color_range_max, size); } frame2_ts = s->deinterlace_frame_ts + s->deinterlace_offset + s->deinterlace_half_duration - TWOX_TOLERANCE; gs_effect_set_bool(frame2, obs->video.video_time >= frame2_ts); while (gs_effect_loop(effect, tech)) gs_draw_sprite(NULL, s->async_flip ? GS_FLIP_V : 0, s->async_width, s->async_height); }
static inline void render_output_texture(struct obs_core_video *video, int cur_texture, int prev_texture) { profile_start(render_output_texture_name); gs_texture_t *texture = video->render_textures[prev_texture]; gs_texture_t *target = video->output_textures[cur_texture]; uint32_t width = gs_texture_get_width(target); uint32_t height = gs_texture_get_height(target); struct vec2 base_i; vec2_set(&base_i, 1.0f / (float)video->base_width, 1.0f / (float)video->base_height); gs_effect_t *effect = get_scale_effect(video, width, height); gs_technique_t *tech; if (video->ovi.output_format == VIDEO_FORMAT_RGBA) { tech = gs_effect_get_technique(effect, "Draw"); } else { tech = gs_effect_get_technique(effect, "DrawMatrix"); } gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_eparam_t *matrix = gs_effect_get_param_by_name(effect, "color_matrix"); gs_eparam_t *bres_i = gs_effect_get_param_by_name(effect, "base_dimension_i"); size_t passes, i; if (!video->textures_rendered[prev_texture]) goto end; gs_set_render_target(target, NULL); set_render_size(width, height); if (bres_i) gs_effect_set_vec2(bres_i, &base_i); gs_effect_set_val(matrix, video->color_matrix, sizeof(float) * 16); gs_effect_set_texture(image, texture); gs_enable_blending(false); passes = gs_technique_begin(tech); for (i = 0; i < passes; i++) { gs_technique_begin_pass(tech, i); gs_draw_sprite(texture, 0, width, height); gs_technique_end_pass(tech); } gs_technique_end(tech); gs_enable_blending(true); video->textures_output[cur_texture] = true; end: profile_end(render_output_texture_name); }
static void image_source_render(void *data, effect_t effect) { struct image_source *context = data; if (!context->tex) return; gs_reset_blend_state(); effect_settexture(effect_getparambyname(effect, "image"), context->tex); gs_draw_sprite(context->tex, 0, context->cx, context->cy); }
static void fade_callback(void *data, gs_texture_t *a, gs_texture_t *b, float t, uint32_t cx, uint32_t cy) { struct fade_info *fade = data; gs_effect_set_texture(fade->a_param, a); gs_effect_set_texture(fade->b_param, b); gs_effect_set_float(fade->fade_param, t); while (gs_effect_loop(fade->effect, "Fade")) gs_draw_sprite(NULL, 0, cx, cy); }
static void image_source_render(void *data, gs_effect_t *effect) { struct image_source *context = data; if (!context->image.texture) return; gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"), context->image.texture); gs_draw_sprite(context->image.texture, 0, context->image.cx, context->image.cy); }
static void render_convert_texture(struct obs_core_video *video, int cur_texture, int prev_texture) { profile_start(render_convert_texture_name); gs_texture_t *texture = video->output_textures[prev_texture]; gs_texture_t *target = video->convert_textures[cur_texture]; float fwidth = (float)video->output_width; float fheight = (float)video->output_height; size_t passes, i; gs_effect_t *effect = video->conversion_effect; gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_technique_t *tech = gs_effect_get_technique(effect, video->conversion_tech); if (!video->textures_output[prev_texture]) goto end; set_eparam(effect, "u_plane_offset", (float)video->plane_offsets[1]); set_eparam(effect, "v_plane_offset", (float)video->plane_offsets[2]); set_eparam(effect, "width", fwidth); set_eparam(effect, "height", fheight); set_eparam(effect, "width_i", 1.0f / fwidth); set_eparam(effect, "height_i", 1.0f / fheight); set_eparam(effect, "width_d2", fwidth * 0.5f); set_eparam(effect, "height_d2", fheight * 0.5f); set_eparam(effect, "width_d2_i", 1.0f / (fwidth * 0.5f)); set_eparam(effect, "height_d2_i", 1.0f / (fheight * 0.5f)); set_eparam(effect, "input_height", (float)video->conversion_height); gs_effect_set_texture(image, texture); gs_set_render_target(target, NULL); set_render_size(video->output_width, video->conversion_height); gs_enable_blending(false); passes = gs_technique_begin(tech); for (i = 0; i < passes; i++) { gs_technique_begin_pass(tech, i); gs_draw_sprite(texture, 0, video->output_width, video->conversion_height); gs_technique_end_pass(tech); } gs_technique_end(tech); gs_enable_blending(true); video->textures_converted[cur_texture] = true; end: profile_end(render_convert_texture_name); }
static void draw_texture(struct dc_capture *capture, gs_effect_t *effect) { gs_texture_t *texture = capture->texture; gs_technique_t *tech = gs_effect_get_technique(effect, "Draw"); gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); size_t passes; gs_effect_set_texture(image, texture); passes = gs_technique_begin(tech); for (size_t i = 0; i < passes; i++) { if (gs_technique_begin_pass(tech, i)) { if (capture->compatibility) gs_draw_sprite(texture, GS_FLIP_V, 0, 0); else gs_draw_sprite(texture, 0, 0, 0); gs_technique_end_pass(tech); } } gs_technique_end(tech); }
static void draw_texture(struct dc_capture *capture, int id, effect_t effect) { texture_t texture = capture->textures[id]; technique_t tech = effect_gettechnique(effect, "Draw"); eparam_t image = effect_getparambyname(effect, "image"); size_t passes; effect_settexture(effect, image, texture); passes = technique_begin(tech); for (size_t i = 0; i < passes; i++) { if (technique_beginpass(tech, i)) { if (capture->compatibility) gs_draw_sprite(texture, GS_FLIP_V, 0, 0); else gs_draw_sprite(texture, 0, 0, 0); technique_endpass(tech); } } technique_end(tech); }
void xcursor_render(xcursor_t *data) { gs_effect_t *effect = gs_get_effect(); gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, data->tex); gs_matrix_push(); gs_matrix_translate3f(data->render_x, data->render_y, 0.0f); gs_enable_blending(True); gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); gs_draw_sprite(data->tex, 0, 0, 0); gs_matrix_pop(); }
void XCompcapMain::render(effect_t effect) { PLock lock(&p->lock, true); if (!lock.isLocked() || !p->tex) return; eparam_t image = effect_getparambyname(effect, "image"); effect_settexture(image, p->tex); gs_enable_blending(false); gs_draw_sprite(p->tex, 0, 0, 0); gs_reset_blend_state(); }
void BrowserSource::Impl::RenderCurrentTexture(gs_effect_t *effect) { GetParent()->LockTexture(); if (activeTexture != nullptr) { gs_reset_blend_state(); gs_effect_set_texture( gs_effect_get_param_by_name(effect, "image"), activeTexture); gs_draw_sprite(activeTexture, 0, parent->GetWidth(), parent->GetHeight()); } GetParent()->UnlockTexture(); }
static bool update_async_texrender(struct obs_source *source, const struct source_frame *frame) { texture_t tex = source->async_texture; texrender_t texrender = source->async_convert_texrender; texrender_reset(texrender); upload_raw_frame(tex, frame); uint32_t cx = source->async_width; uint32_t cy = source->async_height; effect_t conv = obs->video.conversion_effect; technique_t tech = effect_gettechnique(conv, select_conversion_technique(frame->format)); if (!texrender_begin(texrender, cx, cy)) return false; technique_begin(tech); technique_beginpass(tech, 0); effect_settexture(conv, effect_getparambyname(conv, "image"), tex); set_eparam(conv, "width", (float)cx); set_eparam(conv, "height", (float)cy); set_eparam(conv, "width_i", 1.0f / cx); set_eparam(conv, "height_i", 1.0f / cy); set_eparam(conv, "width_d2", cx * 0.5f); set_eparam(conv, "height_d2", cy * 0.5f); set_eparam(conv, "width_d2_i", 1.0f / (cx * 0.5f)); set_eparam(conv, "height_d2_i", 1.0f / (cy * 0.5f)); set_eparam(conv, "input_height", (float)cy); gs_ortho(0.f, (float)cx, 0.f, (float)cy, -100.f, 100.f); gs_draw_sprite(tex, 0, cx, cy); technique_endpass(tech); technique_end(tech); texrender_end(texrender); return true; }
void xcursor_render(xcursor_t *data) { /* TODO: why do i need effects ? */ gs_effect_t effect = gs_get_effect(); gs_eparam_t image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, data->tex); gs_matrix_push(); gs_matrix_translate3f(-data->pos_x, -data->pos_y, 0); gs_enable_blending(True); gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA); gs_draw_sprite(data->tex, 0, 0, 0); gs_matrix_pop(); }
static void render_convert_texture(struct obs_core_video *video, int cur_texture, int prev_texture) { texture_t texture = video->output_textures[prev_texture]; texture_t target = video->convert_textures[cur_texture]; float fwidth = (float)video->output_width; float fheight = (float)video->output_height; size_t passes, i; effect_t effect = video->conversion_effect; eparam_t image = effect_getparambyname(effect, "image"); technique_t tech = effect_gettechnique(effect, video->conversion_tech); if (!video->textures_output[prev_texture]) return; set_eparam(effect, "u_plane_offset", (float)video->plane_offsets[1]); set_eparam(effect, "v_plane_offset", (float)video->plane_offsets[2]); set_eparam(effect, "width", fwidth); set_eparam(effect, "height", fheight); set_eparam(effect, "width_i", 1.0f / fwidth); set_eparam(effect, "height_i", 1.0f / fheight); set_eparam(effect, "width_d2", fwidth * 0.5f); set_eparam(effect, "height_d2", fheight * 0.5f); set_eparam(effect, "width_d2_i", 1.0f / (fwidth * 0.5f)); set_eparam(effect, "height_d2_i", 1.0f / (fheight * 0.5f)); set_eparam(effect, "input_height", (float)video->conversion_height); effect_settexture(effect, image, texture); gs_setrendertarget(target, NULL); set_render_size(video->output_width, video->conversion_height); passes = technique_begin(tech); for (i = 0; i < passes; i++) { technique_beginpass(tech, i); gs_draw_sprite(texture, 0, video->output_width, video->conversion_height); technique_endpass(tech); } technique_end(tech); video->textures_converted[cur_texture] = true; }
/** * Render the capture data */ static void xshm_video_render(void *vptr, gs_effect_t effect) { XSHM_DATA(vptr); if (!data->xshm) return; gs_eparam_t image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, data->texture); gs_enable_blending(false); gs_draw_sprite(data->texture, 0, 0, 0); if (data->show_cursor) xcursor_render(data->cursor); gs_reset_blend_state(); }
void XCompcapMain::render(gs_effect_t *effect) { PLock lock(&p->lock, true); if (!lock.isLocked() || !p->tex) return; gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, p->tex); gs_enable_blending(false); gs_draw_sprite(p->tex, 0, 0, 0); if (p->cursor && p->gltex && p->show_cursor && !p->cursor_outside) xcursor_render(p->cursor); gs_reset_blend_state(); }
static inline void render_filter_tex(texture_t tex, effect_t effect, uint32_t width, uint32_t height, bool use_matrix) { const char *tech_name = use_matrix ? "DrawMatrix" : "Draw"; technique_t tech = effect_gettechnique(effect, tech_name); eparam_t image = effect_getparambyname(effect, "image"); size_t passes, i; effect_settexture(effect, image, tex); passes = technique_begin(tech); for (i = 0; i < passes; i++) { technique_beginpass(tech, i); gs_draw_sprite(tex, width, height, 0); technique_endpass(tech); } technique_end(tech); }
static inline void render_filter_tex(texture_t tex, 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; effect_settexture(effect, diffuse, tex); passes = technique_begin(tech); for (i = 0; i < passes; i++) { technique_beginpass(tech, i); gs_draw_sprite(tex, width, height, 0); technique_endpass(tech); } technique_end(tech); }
static inline void render_output_texture(struct obs_core_video *video, int cur_texture, int prev_texture) { texture_t texture = video->render_textures[prev_texture]; texture_t target = video->output_textures[cur_texture]; uint32_t width = texture_getwidth(target); uint32_t height = texture_getheight(target); /* TODO: replace with actual downscalers or unpackers */ effect_t effect = video->default_effect; technique_t tech = effect_gettechnique(effect, "DrawMatrix"); eparam_t image = effect_getparambyname(effect, "image"); eparam_t matrix = effect_getparambyname(effect, "color_matrix"); size_t passes, i; if (!video->textures_rendered[prev_texture]) return; gs_setrendertarget(target, NULL); set_render_size(width, height); /* TODO: replace with programmable code */ const float mat_val[16] = { -0.100644f, -0.338572f, 0.439216f, 0.501961f, 0.182586f, 0.614231f, 0.062007f, 0.062745f, 0.439216f, -0.398942f, -0.040274f, 0.501961f, 0.000000f, 0.000000f, 0.000000f, 1.000000f }; effect_setval(effect, matrix, mat_val, sizeof(mat_val)); effect_settexture(effect, image, texture); passes = technique_begin(tech); for (i = 0; i < passes; i++) { technique_beginpass(tech, i); gs_draw_sprite(texture, 0, width, height); technique_endpass(tech); } technique_end(tech); video->textures_output[cur_texture] = true; }
/** * Render the capture data */ static void xshm_video_render(void *vptr, gs_effect_t *effect) { XSHM_DATA(vptr); effect = obs_get_opaque_effect(); if (!data->texture) return; gs_eparam_t *image = gs_effect_get_param_by_name(effect, "image"); gs_effect_set_texture(image, data->texture); while (gs_effect_loop(effect, "Draw")) { gs_draw_sprite(data->texture, 0, 0, 0); } if (data->show_cursor) { effect = obs_get_default_effect(); while (gs_effect_loop(effect, "Draw")) { xcb_xcursor_render(data->cursor); } } }
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(); }