diff --git a/libobs/obs-scene.c b/libobs/obs-scene.c index 5f0b935cf..d85752de4 100644 --- a/libobs/obs-scene.c +++ b/libobs/obs-scene.c @@ -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); } } diff --git a/libobs/obs-scene.h b/libobs/obs-scene.h index 386cff466..1efdfc08a 100644 --- a/libobs/obs-scene.h +++ b/libobs/obs-scene.h @@ -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; diff --git a/libobs/obs.h b/libobs/obs.h index a425fc048..4c1fa6e61 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -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);