예제 #1
0
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();
}
예제 #2
0
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);
}
예제 #3
0
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);
}
예제 #4
0
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);
	}
}
예제 #5
0
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();
}
예제 #6
0
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();
}
예제 #7
0
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);
}
예제 #8
0
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);
    }
}
예제 #9
0
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);
}
예제 #10
0
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);
}
예제 #11
0
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);
}
예제 #12
0
파일: obs-scene.c 프로젝트: ab22/obs-studio
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);
}
예제 #13
0
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]);
    }
}
예제 #14
0
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();
}
예제 #15
0
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);
}
예제 #16
0
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);
}
예제 #17
0
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);
}
예제 #18
0
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;
}
예제 #19
0
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");
}
예제 #20
0
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();
}