libobs: Add ability to use scale filters on scene items

Allows the ability to use scale filters such as point, bicubic, lanczos
on specific scene items, disabled by default.  When using one of the
latter two options, if the item's scale is under half of the source's
original size, it uses the bilinear low resolution downscale shader
instead.
This commit is contained in:
jp9000 2016-06-29 06:08:54 -07:00
parent 9413a43869
commit d49833830c
6 changed files with 133 additions and 26 deletions

View file

@ -238,6 +238,7 @@ struct obs_core_video {
gs_effect_t *lanczos_effect;
gs_effect_t *bilinear_lowres_effect;
gs_effect_t *premultiplied_alpha_effect;
gs_samplerstate_t *point_sampler;
gs_stagesurf_t *mapped_surface;
int cur_texture;

View file

@ -337,6 +337,8 @@ static void update_item_transform(struct obs_scene_item *item)
matrix4_translate3f(&item->draw_transform, &item->draw_transform,
item->pos.x, item->pos.y, 0.0f);
item->output_scale = scale;
/* ----------------------- */
if (item->bounds_type != OBS_BOUNDS_NONE) {
@ -383,15 +385,70 @@ static inline bool crop_enabled(const struct obs_sceneitem_crop *crop)
return crop->left || crop->right || crop->top || crop->bottom;
}
static inline bool scale_filter_enabled(const struct obs_scene_item *item)
{
return item->scale_filter != OBS_SCALE_DISABLE;
}
static inline bool item_texture_enabled(const struct obs_scene_item *item)
{
return crop_enabled(&item->crop) || scale_filter_enabled(item);
}
static void render_item_texture(struct obs_scene_item *item)
{
gs_texture_t *tex = gs_texrender_get_texture(item->item_render);
gs_effect_t *effect = obs->video.default_effect;
enum obs_scale_type type = item->scale_filter;
uint32_t cx = gs_texture_get_width(tex);
uint32_t cy = gs_texture_get_height(tex);
if (type != OBS_SCALE_DISABLE) {
if (type == OBS_SCALE_POINT) {
gs_eparam_t *image = gs_effect_get_param_by_name(
effect, "image");
gs_effect_set_next_sampler(image,
obs->video.point_sampler);
} else if (!close_float(item->output_scale.x, 1.0f, EPSILON) ||
!close_float(item->output_scale.y, 1.0f, EPSILON)) {
gs_eparam_t *scale_param;
if (item->output_scale.x < 0.5f ||
item->output_scale.y < 0.5f) {
effect = obs->video.bilinear_lowres_effect;
} else if (type == OBS_SCALE_BICUBIC) {
effect = obs->video.bicubic_effect;
} else if (type == OBS_SCALE_LANCZOS) {
effect = obs->video.lanczos_effect;
}
scale_param = gs_effect_get_param_by_name(effect,
"base_dimension_i");
if (scale_param) {
struct vec2 base_res_i = {
1.0f / (float)cx,
1.0f / (float)cy
};
gs_effect_set_vec2(scale_param, &base_res_i);
}
}
}
while (gs_effect_loop(effect, "Draw"))
obs_source_draw(tex, 0, 0, 0, 0, 0);
}
static inline void render_item(struct obs_scene_item *item)
{
if (item->crop_render) {
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->crop_render, cx, cy)) {
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;
@ -408,17 +465,14 @@ static inline void render_item(struct obs_scene_item *item)
0.0f);
obs_source_video_render(item->source);
gs_texrender_end(item->crop_render);
gs_texrender_end(item->item_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);
if (item->item_render) {
render_item_texture(item);
} else {
obs_source_video_render(item->source);
}
@ -433,8 +487,8 @@ static void scene_video_tick(void *data, float seconds)
video_lock(scene);
item = scene->first_item;
while (item) {
if (item->crop_render)
gs_texrender_reset(item->crop_render);
if (item->item_render)
gs_texrender_reset(item->item_render);
item = item->next;
}
video_unlock(scene);
@ -553,15 +607,18 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
item->crop.right = (uint32_t)obs_data_get_int(item_data, "crop_right");
item->crop.bottom = (uint32_t)obs_data_get_int(item_data, "crop_bottom");
if (item->crop_render && !crop_enabled(&item->crop)) {
item->scale_filter = (enum obs_scale_type)obs_data_get_int(item_data,
"scale_filter");
if (item->item_render && !item_texture_enabled(item)) {
obs_enter_graphics();
gs_texrender_destroy(item->crop_render);
item->crop_render = NULL;
gs_texrender_destroy(item->item_render);
item->item_render = NULL;
obs_leave_graphics();
} else if (!item->crop_render && crop_enabled(&item->crop)) {
} else if (!item->item_render && item_texture_enabled(item)) {
obs_enter_graphics();
item->crop_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
obs_leave_graphics();
}
@ -609,6 +666,7 @@ static void scene_save_item(obs_data_array_t *array,
obs_data_set_int (item_data, "crop_top", (int)item->crop.top);
obs_data_set_int (item_data, "crop_right", (int)item->crop.right);
obs_data_set_int (item_data, "crop_bottom", (int)item->crop.bottom);
obs_data_set_int (item_data, "scale_filter", (int)item->scale_filter);
obs_data_array_push_back(array, item_data);
obs_data_release(item_data);
@ -1227,9 +1285,9 @@ obs_sceneitem_t *obs_scene_add(obs_scene_t *scene, obs_source_t *source)
static void obs_sceneitem_destroy(obs_sceneitem_t *item)
{
if (item) {
if (item->crop_render) {
if (item->item_render) {
obs_enter_graphics();
gs_texrender_destroy(item->crop_render);
gs_texrender_destroy(item->item_render);
obs_leave_graphics();
}
obs_hotkey_pair_unregister(item->toggle_visibility);
@ -1695,7 +1753,7 @@ static inline bool crop_equal(const struct obs_sceneitem_crop *crop1,
void obs_sceneitem_set_crop(obs_sceneitem_t *item,
const struct obs_sceneitem_crop *crop)
{
bool now_enabled;
bool item_tex_now_enabled;
if (!obs_ptr_valid(item, "obs_sceneitem_set_crop"))
return;
@ -1704,16 +1762,16 @@ void obs_sceneitem_set_crop(obs_sceneitem_t *item,
if (crop_equal(crop, &item->crop))
return;
now_enabled = crop_enabled(crop);
item_tex_now_enabled = crop_enabled(crop) || scale_filter_enabled(item);
obs_enter_graphics();
if (!now_enabled) {
gs_texrender_destroy(item->crop_render);
item->crop_render = NULL;
if (!item_tex_now_enabled) {
gs_texrender_destroy(item->item_render);
item->item_render = NULL;
} else if (!item->crop_render) {
item->crop_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
} else if (!item->item_render) {
item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
}
memcpy(&item->crop, crop, sizeof(*crop));
@ -1738,6 +1796,36 @@ void obs_sceneitem_get_crop(const obs_sceneitem_t *item,
memcpy(crop, &item->crop, sizeof(*crop));
}
void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item,
enum obs_scale_type filter)
{
if (!obs_ptr_valid(item, "obs_sceneitem_set_scale_filter"))
return;
item->scale_filter = filter;
obs_enter_graphics();
if (!item_texture_enabled(item)) {
gs_texrender_destroy(item->item_render);
item->item_render = NULL;
} else if (!item->item_render) {
item->item_render = gs_texrender_create(GS_RGBA, GS_ZS_NONE);
}
obs_leave_graphics();
update_item_transform(item);
}
enum obs_scale_type obs_sceneitem_get_scale_filter(
obs_sceneitem_t *item)
{
return obs_ptr_valid(item, "obs_sceneitem_get_scale_filter") ?
item->scale_filter : OBS_SCALE_DISABLE;
}
void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)
{
if (!obs_ptr_valid(item, "obs_sceneitem_defer_update_begin"))

View file

@ -40,7 +40,7 @@ struct obs_scene_item {
bool visible;
bool selected;
gs_texrender_t *crop_render;
gs_texrender_t *item_render;
struct obs_sceneitem_crop crop;
struct vec2 pos;
@ -53,6 +53,9 @@ struct obs_scene_item {
uint32_t last_width;
uint32_t last_height;
struct vec2 output_scale;
enum obs_scale_type scale_filter;
struct matrix4 box_transform;
struct matrix4 draw_transform;

View file

@ -126,7 +126,8 @@ static inline gs_effect_t *get_scale_effect_internal(
switch (video->scale_type) {
case OBS_SCALE_BILINEAR: return video->default_effect;
case OBS_SCALE_LANCZOS: return video->lanczos_effect;
case OBS_SCALE_BICUBIC:;
case OBS_SCALE_BICUBIC:
default:;
}
return video->bicubic_effect;

View file

@ -231,6 +231,7 @@ static int obs_init_graphics(struct obs_video_info *ovi)
struct obs_core_video *video = &obs->video;
uint8_t transparent_tex_data[2*2*4] = {0};
const uint8_t *transparent_tex = transparent_tex_data;
struct gs_sampler_info point_sampler = {0};
bool success = true;
int errorcode;
@ -296,6 +297,8 @@ static int obs_init_graphics(struct obs_video_info *ovi)
NULL);
bfree(filename);
video->point_sampler = gs_samplerstate_create(&point_sampler);
obs->video.transparent_texture = gs_texture_create(2, 2, GS_RGBA, 1,
&transparent_tex, 0);
@ -315,6 +318,8 @@ static int obs_init_graphics(struct obs_video_info *ovi)
success = false;
if (!video->transparent_texture)
success = false;
if (!video->point_sampler)
success = false;
gs_leave_context();
return success ? OBS_VIDEO_SUCCESS : OBS_VIDEO_FAIL;
@ -459,6 +464,8 @@ static void obs_free_graphics(void)
gs_texture_destroy(video->transparent_texture);
gs_samplerstate_destroy(video->point_sampler);
gs_effect_destroy(video->default_effect);
gs_effect_destroy(video->default_rect_effect);
gs_effect_destroy(video->opaque_effect);

View file

@ -112,6 +112,8 @@ enum obs_allow_direct_render {
};
enum obs_scale_type {
OBS_SCALE_DISABLE,
OBS_SCALE_POINT,
OBS_SCALE_BICUBIC,
OBS_SCALE_BILINEAR,
OBS_SCALE_LANCZOS
@ -1253,6 +1255,11 @@ EXPORT void obs_sceneitem_set_crop(obs_sceneitem_t *item,
EXPORT void obs_sceneitem_get_crop(const obs_sceneitem_t *item,
struct obs_sceneitem_crop *crop);
EXPORT void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item,
enum obs_scale_type filter);
EXPORT enum obs_scale_type obs_sceneitem_get_scale_filter(
obs_sceneitem_t *item);
EXPORT void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item);
EXPORT void obs_sceneitem_defer_update_end(obs_sceneitem_t *item);