libobs: Add automatic cropping to bounds for scene items

This commit is contained in:
derrod
2023-12-16 18:00:11 -06:00
committed by Lain
parent 09ffae07d8
commit 7e456d9dea
3 changed files with 101 additions and 5 deletions
+96 -5
View File
@@ -351,6 +351,14 @@ void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy)
v->y += (float)(cy / 2);
}
static inline bool crop_to_bounds(const struct obs_scene_item *item)
{
return item->crop_to_bounds &&
(item->bounds_type == OBS_BOUNDS_SCALE_OUTER ||
item->bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT ||
item->bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH);
}
static void calculate_bounds_data(struct obs_scene_item *item,
struct vec2 *origin, struct vec2 *scale,
uint32_t *cx, uint32_t *cy)
@@ -402,6 +410,63 @@ static void calculate_bounds_data(struct obs_scene_item *item,
add_alignment(origin, item->bounds_align, (int)-width_diff,
(int)-height_diff);
/* Set cropping if enabled and large enough size difference exists */
if (crop_to_bounds(item) && (width_diff < -0.1 || height_diff < -0.1)) {
bool crop_width = width_diff < -0.1;
bool crop_flipped = crop_width ? width < 0.0f : height < 0.0f;
float crop_diff = crop_width ? width_diff : height_diff;
float crop_scale = crop_width ? scale->x : scale->y;
float crop_origin = crop_width ? origin->x : origin->y;
/* Only get alignment for relevant axis */
uint32_t crop_align_mask =
crop_width ? OBS_ALIGN_LEFT | OBS_ALIGN_RIGHT
: OBS_ALIGN_TOP | OBS_ALIGN_BOTTOM;
uint32_t crop_align = item->bounds_align & crop_align_mask;
/* Cropping values need to scaled to input source */
float overdraw = fabsf(crop_diff / crop_scale);
/* tl = top / left, br = bottom / right */
float overdraw_tl;
if (crop_align & (OBS_ALIGN_TOP | OBS_ALIGN_LEFT))
overdraw_tl = 0;
else if (crop_align & (OBS_ALIGN_BOTTOM | OBS_ALIGN_RIGHT))
overdraw_tl = overdraw;
else
overdraw_tl = overdraw / 2;
float overdraw_br = overdraw - overdraw_tl;
int crop_br, crop_tl;
if (crop_flipped) {
/* Adjust origin for flips */
if (crop_align == OBS_ALIGN_CENTER)
crop_origin *= 2;
else if (crop_align & (OBS_ALIGN_TOP | OBS_ALIGN_LEFT))
crop_origin -= crop_diff;
/* Note that crops are swapped if the axis is flipped */
crop_br = (int)roundf(overdraw_tl);
crop_tl = (int)roundf(overdraw_br);
} else {
crop_origin = 0;
crop_br = (int)roundf(overdraw_br);
crop_tl = (int)roundf(overdraw_tl);
}
if (crop_width) {
item->bounds_crop.right = crop_br;
item->bounds_crop.left = crop_tl;
origin->x = crop_origin;
} else {
item->bounds_crop.bottom = crop_br;
item->bounds_crop.top = crop_tl;
origin->y = crop_origin;
}
}
/* Makes the item stay in-place in the box if flipped */
origin->x += (width < 0.0f) ? width : 0.0f;
origin->y += (height < 0.0f) ? height : 0.0f;
@@ -410,14 +475,16 @@ static void calculate_bounds_data(struct obs_scene_item *item,
static inline uint32_t calc_cx(const struct obs_scene_item *item,
uint32_t width)
{
uint32_t crop_cx = item->crop.left + item->crop.right;
uint32_t crop_cx = item->crop.left + item->crop.right +
item->bounds_crop.left + item->bounds_crop.right;
return (crop_cx > width) ? 2 : (width - crop_cx);
}
static inline uint32_t calc_cy(const struct obs_scene_item *item,
uint32_t height)
{
uint32_t crop_cy = item->crop.top + item->crop.bottom;
uint32_t crop_cy = item->crop.top + item->crop.bottom +
item->bounds_crop.top + item->bounds_crop.bottom;
return (crop_cy > height) ? 2 : (height - crop_cy);
}
@@ -436,6 +503,9 @@ static void update_item_transform(struct obs_scene_item *item, bool update_tex)
if (os_atomic_load_long(&item->defer_update) > 0)
return;
/* Reset bounds crop */
memset(&item->bounds_crop, 0, sizeof(item->bounds_crop));
width = obs_source_get_width(item->source);
height = obs_source_get_height(item->source);
cx = calc_cx(item, width);
@@ -538,7 +608,8 @@ static inline bool item_is_scene(const struct obs_scene_item *item)
static inline bool item_texture_enabled(const struct obs_scene_item *item)
{
return crop_enabled(&item->crop) || scale_filter_enabled(item) ||
return crop_enabled(&item->crop) || crop_enabled(&item->bounds_crop) ||
scale_filter_enabled(item) ||
(item->blend_method == OBS_BLEND_METHOD_SRGB_OFF) ||
!default_blending_enabled(item) ||
(item_is_scene(item) && !item->is_group);
@@ -764,8 +835,11 @@ static inline void render_item(struct obs_scene_item *item)
-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);
gs_matrix_translate3f(-(float)(item->crop.left +
item->bounds_crop.left),
-(float)(item->crop.top +
item->bounds_crop.top),
0.0f);
if (item->user_visible &&
transition_active(item->show_transition)) {
@@ -1031,6 +1105,7 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
item_data, "bounds_type");
item->bounds_align =
(uint32_t)obs_data_get_int(item_data, "bounds_align");
item->crop_to_bounds = obs_data_get_bool(item_data, "bounds_crop");
obs_data_get_vec2(item_data, "bounds", &item->bounds);
item->crop.left = (uint32_t)obs_data_get_int(item_data, "crop_left");
@@ -1159,6 +1234,7 @@ static void scene_save_item(obs_data_array_t *array,
obs_data_set_int(item_data, "align", (int)item->align);
obs_data_set_int(item_data, "bounds_type", (int)item->bounds_type);
obs_data_set_int(item_data, "bounds_align", (int)item->bounds_align);
obs_data_set_bool(item_data, "bounds_crop", item->crop_to_bounds);
obs_data_set_vec2(item_data, "bounds", &item->bounds);
obs_data_set_int(item_data, "crop_left", (int)item->crop.left);
obs_data_set_int(item_data, "crop_top", (int)item->crop.top);
@@ -2686,6 +2762,14 @@ void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item,
}
}
void obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop)
{
if (item) {
item->crop_to_bounds = crop;
do_update_transform(item);
}
}
void obs_sceneitem_set_bounds(obs_sceneitem_t *item, const struct vec2 *bounds)
{
if (item) {
@@ -2726,6 +2810,11 @@ uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item)
return item ? item->bounds_align : 0;
}
bool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item)
{
return item ? item->crop_to_bounds : false;
}
void obs_sceneitem_get_bounds(const obs_sceneitem_t *item, struct vec2 *bounds)
{
if (item)
@@ -2743,6 +2832,7 @@ void obs_sceneitem_get_info(const obs_sceneitem_t *item,
info->bounds_type = item->bounds_type;
info->bounds_alignment = item->bounds_align;
info->bounds = item->bounds;
info->crop_to_bounds = item->crop_to_bounds;
}
}
@@ -2759,6 +2849,7 @@ void obs_sceneitem_set_info(obs_sceneitem_t *item,
item->bounds_type = info->bounds_type;
item->bounds_align = info->bounds_alignment;
item->bounds = info->bounds;
item->crop_to_bounds = info->crop_to_bounds;
do_update_transform(item);
}
}
+2
View File
@@ -73,6 +73,8 @@ struct obs_scene_item {
enum obs_bounds_type bounds_type;
uint32_t bounds_align;
struct vec2 bounds;
bool crop_to_bounds;
struct obs_sceneitem_crop bounds_crop;
obs_hotkey_pair_id toggle_visibility;
+3
View File
@@ -162,6 +162,7 @@ struct obs_transform_info {
enum obs_bounds_type bounds_type;
uint32_t bounds_alignment;
struct vec2 bounds;
bool crop_to_bounds;
};
/**
@@ -1865,6 +1866,7 @@ EXPORT void obs_sceneitem_set_bounds_type(obs_sceneitem_t *item,
enum obs_bounds_type type);
EXPORT void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t *item,
uint32_t alignment);
EXPORT void obs_sceneitem_set_bounds_crop(obs_sceneitem_t *item, bool crop);
EXPORT void obs_sceneitem_set_bounds(obs_sceneitem_t *item,
const struct vec2 *bounds);
@@ -1880,6 +1882,7 @@ EXPORT uint32_t obs_sceneitem_get_alignment(const obs_sceneitem_t *item);
EXPORT enum obs_bounds_type
obs_sceneitem_get_bounds_type(const obs_sceneitem_t *item);
EXPORT uint32_t obs_sceneitem_get_bounds_alignment(const obs_sceneitem_t *item);
EXPORT bool obs_sceneitem_get_bounds_crop(const obs_sceneitem_t *item);
EXPORT void obs_sceneitem_get_bounds(const obs_sceneitem_t *item,
struct vec2 *bounds);