198 lines
5.1 KiB
C
198 lines
5.1 KiB
C
|
|
#include <obs-module.h>
|
|
#include "multi-canvas-source.h"
|
|
|
|
struct multi_canvas_info {
|
|
obs_source_t *source;
|
|
uint32_t width;
|
|
uint32_t height;
|
|
DARRAY(obs_canvas_t *) canvas;
|
|
DARRAY(uint32_t) widths;
|
|
DARRAY(uint32_t) heights;
|
|
DARRAY(gs_texrender_t *) renders;
|
|
};
|
|
|
|
const char *multi_canvas_get_name(void *type_data)
|
|
{
|
|
UNUSED_PARAMETER(type_data);
|
|
return "vertical_multi_canvas";
|
|
}
|
|
|
|
void *multi_canvas_create(obs_data_t *settings, obs_source_t *source)
|
|
{
|
|
UNUSED_PARAMETER(settings);
|
|
struct multi_canvas_info *multi_canvas = bzalloc(sizeof(struct multi_canvas_info));
|
|
multi_canvas->source = source;
|
|
return multi_canvas;
|
|
}
|
|
|
|
void multi_canvas_destroy(void *data)
|
|
{
|
|
struct multi_canvas_info *mc = data;
|
|
da_free(mc->canvas);
|
|
da_free(mc->widths);
|
|
da_free(mc->heights);
|
|
for (size_t i = 0; i < mc->renders.num; i++) {
|
|
gs_texrender_destroy(mc->renders.array[i]);
|
|
}
|
|
da_free(mc->renders);
|
|
bfree(data);
|
|
}
|
|
|
|
bool view_get_width(void *data, struct obs_video_info *ovi)
|
|
{
|
|
uint32_t *width = data;
|
|
if (ovi->base_width > *width)
|
|
*width = ovi->base_width;
|
|
return true;
|
|
}
|
|
|
|
bool view_get_height(void *data, struct obs_video_info *ovi)
|
|
{
|
|
uint32_t *height = data;
|
|
if (ovi->base_height > *height)
|
|
*height = ovi->base_height;
|
|
return true;
|
|
}
|
|
|
|
static void multi_canvas_video_render(void *data, gs_effect_t *effect)
|
|
{
|
|
struct multi_canvas_info *mc = data;
|
|
gs_matrix_push();
|
|
for (uint32_t i = 0; i < MAX_CHANNELS; i++) {
|
|
obs_source_t *s = obs_get_output_source(i);
|
|
if (!s)
|
|
continue;
|
|
if (obs_source_removed(s)) {
|
|
obs_source_release(s);
|
|
continue;
|
|
}
|
|
gs_matrix_push();
|
|
gs_blend_state_push();
|
|
obs_source_video_render(s);
|
|
gs_blend_state_pop();
|
|
gs_matrix_pop();
|
|
obs_source_release(s);
|
|
}
|
|
|
|
struct obs_video_info ovi;
|
|
obs_get_video_info(&ovi);
|
|
gs_matrix_translate3f((float)ovi.base_width, 0.0f, 0.0f);
|
|
|
|
effect = obs_get_base_effect(OBS_EFFECT_DEFAULT);
|
|
|
|
for (size_t i = 0; i < mc->canvas.num; i++) {
|
|
obs_canvas_t *canvas = mc->canvas.array[i];
|
|
const enum gs_color_format format = gs_get_format_from_space(gs_get_color_space());
|
|
if (gs_texrender_get_format(mc->renders.array[i]) != format) {
|
|
gs_texrender_destroy(mc->renders.array[i]);
|
|
mc->renders.array[i] = gs_texrender_create(format, GS_ZS_NONE);
|
|
}
|
|
|
|
gs_texrender_reset(mc->renders.array[i]);
|
|
gs_blend_state_push();
|
|
gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);
|
|
if (gs_texrender_begin_with_color_space(mc->renders.array[i], mc->widths.array[i], mc->heights.array[i],
|
|
gs_get_color_space())) {
|
|
|
|
struct vec4 clear_color;
|
|
|
|
vec4_zero(&clear_color);
|
|
gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);
|
|
gs_ortho(0.0f, (float)mc->widths.array[i], 0.0f, (float)mc->heights.array[i], -100.0f, 100.0f);
|
|
|
|
obs_canvas_render(canvas);
|
|
|
|
gs_texrender_end(mc->renders.array[i]);
|
|
}
|
|
gs_blend_state_pop();
|
|
|
|
gs_texture_t *tex = gs_texrender_get_texture(mc->renders.array[i]);
|
|
if (tex) {
|
|
const bool previous = gs_framebuffer_srgb_enabled();
|
|
gs_enable_framebuffer_srgb(true);
|
|
|
|
gs_effect_set_texture_srgb(gs_effect_get_param_by_name(effect, "image"), tex);
|
|
|
|
while (gs_effect_loop(effect, "Draw"))
|
|
gs_draw_sprite(tex, 0, mc->widths.array[i], mc->heights.array[i]);
|
|
|
|
gs_enable_framebuffer_srgb(previous);
|
|
}
|
|
|
|
gs_matrix_translate3f((float)mc->widths.array[i], 0.0f, 0.0f);
|
|
}
|
|
gs_matrix_pop();
|
|
}
|
|
|
|
uint32_t multi_canvas_get_width(void *data)
|
|
{
|
|
struct multi_canvas_info *mc = data;
|
|
return mc->width;
|
|
}
|
|
|
|
uint32_t multi_canvas_get_height(void *data)
|
|
{
|
|
struct multi_canvas_info *mc = data;
|
|
return mc->height;
|
|
}
|
|
|
|
void multi_canvas_update_size(struct multi_canvas_info *mc)
|
|
{
|
|
struct obs_video_info ovi;
|
|
obs_get_video_info(&ovi);
|
|
uint32_t width = ovi.base_width;
|
|
uint32_t height = ovi.base_height;
|
|
|
|
for (size_t i = 0; i < mc->widths.num; i++) {
|
|
width += mc->widths.array[i];
|
|
if (mc->heights.array[i] > height)
|
|
height = mc->heights.array[i];
|
|
}
|
|
mc->width = width;
|
|
mc->height = height;
|
|
}
|
|
|
|
void multi_canvas_source_add_canvas(void *data, obs_canvas_t *canvas, uint32_t width, uint32_t height)
|
|
{
|
|
struct multi_canvas_info *mc = data;
|
|
for (size_t i = 0; i < mc->canvas.num; i++) {
|
|
if (mc->canvas.array[i] == canvas)
|
|
return;
|
|
}
|
|
da_push_back(mc->widths, &width);
|
|
da_push_back(mc->heights, &height);
|
|
da_push_back(mc->canvas, &canvas);
|
|
gs_texrender_t *render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
|
|
da_push_back(mc->renders, &render);
|
|
|
|
multi_canvas_update_size(mc);
|
|
}
|
|
|
|
void multi_canvas_source_remove_canvas(void *data, obs_canvas_t *canvas)
|
|
{
|
|
struct multi_canvas_info *mc = data;
|
|
for (size_t i = 0; i < mc->canvas.num; i++) {
|
|
if (mc->canvas.array[i] == canvas) {
|
|
gs_texrender_destroy(mc->renders.array[i]);
|
|
da_erase(mc->canvas, i);
|
|
da_erase(mc->widths, i);
|
|
da_erase(mc->heights, i);
|
|
da_erase(mc->renders, i);
|
|
break;
|
|
}
|
|
}
|
|
multi_canvas_update_size(mc);
|
|
}
|
|
|
|
struct obs_source_info multi_canvas_source = {
|
|
.id = "vertical_multi_canvas_source",
|
|
.type = OBS_SOURCE_TYPE_INPUT,
|
|
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CAP_DISABLED | OBS_SOURCE_CUSTOM_DRAW,
|
|
.get_name = multi_canvas_get_name,
|
|
.create = multi_canvas_create,
|
|
.destroy = multi_canvas_destroy,
|
|
.video_render = multi_canvas_video_render,
|
|
.get_width = multi_canvas_get_width,
|
|
.get_height = multi_canvas_get_height,
|
|
};
|