Scenes: Implement more item positioning features

There are a ridiculous number of features related to scaling and
positioning due to requests by a number of people who complained that
they hated the way that OBS1 would always resize their sources when the
source's base size changed.  There were also people who wanted more
control for how the resizing was handled, or the ability to completely
prevent resizing entirely if desired.  So I made it so that you can
optionally use a 'bounds' system, which allows you to specify different
styles of controlling resizing.

If disabled, the source will always automatically resize and only the
base scale is applied.  If enabled, you have a variety of different ways
to limit/control how it can resize within the bounds, or make it so it
can't resize at all.  You can also control alignment within that
bounding box, so you can make it so that a source always aligns to a
side or corner of the box.

I also added an alignment value which changes how the source is oriented
relative to the position of the scene item.  For example, setting
bottom-right alignment will make it so that the position of the item is
the bottom right corner of the source.  When the source resizies, it
will resize leftward and upward in that case, which solves the problem
of how a source resizes relative to a desired position.
This commit is contained in:
jp9000 2014-06-15 00:16:03 -07:00
parent 254d830c08
commit b23f8cc6e1
5 changed files with 376 additions and 38 deletions

View file

@ -34,14 +34,14 @@
*
* Reset to zero each major version
*/
#define LIBOBS_API_MINOR_VER 2
#define LIBOBS_API_MINOR_VER 3
/*
* Increment if backward-compatible bug fix
*
* Reset to zero each major or minor version
*/
#define LIBOBS_API_PATCH_VER 4
#define LIBOBS_API_PATCH_VER 0
#define LIBOBS_API_VER ((LIBOBS_API_MAJOR_VER << 24) | \
(LIBOBS_API_MINOR_VER << 16) | \

View file

@ -20,6 +20,12 @@
/** Maximum number of source channels for output and per display */
#define MAX_CHANNELS 64
#define OBS_ALIGN_CENTER (0)
#define OBS_ALIGN_LEFT (1<<0)
#define OBS_ALIGN_RIGHT (1<<1)
#define OBS_ALIGN_TOP (1<<2)
#define OBS_ALIGN_BOTTOM (1<<3)
#define MODULE_SUCCESS 0
#define MODULE_ERROR -1
#define MODULE_FILE_NOT_FOUND -2

View file

@ -26,6 +26,9 @@ static const char *obs_scene_signals[] = {
"void item_move_down(ptr scene, ptr item)",
"void item_move_top(ptr scene, ptr item)",
"void item_move_bottom(ptr scene, ptr item)",
"void item_select(ptr scene, ptr item)",
"void item_deselect(ptr scene, ptr item)",
"void item_transform(ptr scene, ptr item)",
NULL
};
@ -155,6 +158,138 @@ static inline void attach_sceneitem(struct obs_scene *parent,
}
}
static void add_alignment(struct vec2 *v, uint32_t align, int cx, int cy)
{
if (align & OBS_ALIGN_RIGHT)
v->x += (float)cx;
else if ((align & OBS_ALIGN_LEFT) == 0)
v->x += (float)(cx / 2);
if (align & OBS_ALIGN_BOTTOM)
v->y += (float)cy;
else if ((align & OBS_ALIGN_TOP) == 0)
v->y += (float)(cy / 2);
}
static void calculate_bounds_data(struct obs_scene_item *item,
struct vec2 *origin, struct vec2 *scale,
uint32_t *cx, uint32_t *cy)
{
float width = (float)(*cx) * fabsf(scale->x);
float height = (float)(*cy) * fabsf(scale->y);
float item_aspect = width / height;
float bounds_aspect = item->bounds.x / item->bounds.y;
float width_diff, height_diff;
if (item->bounds_type == OBS_BOUNDS_SCALE_INNER ||
item->bounds_type == OBS_BOUNDS_SCALE_OUTER) {
bool use_width = (bounds_aspect < item_aspect);
float mul;
if (item->bounds_type == OBS_BOUNDS_SCALE_OUTER)
use_width = !use_width;
mul = use_width ?
item->bounds.x / width :
item->bounds.y / height;
vec2_mulf(scale, scale, mul);
} else if (item->bounds_type == OBS_BOUNDS_SCALE_TO_WIDTH) {
vec2_mulf(scale, scale, item->bounds.x / width);
} else if (item->bounds_type == OBS_BOUNDS_SCALE_TO_HEIGHT) {
vec2_mulf(scale, scale, item->bounds.y / height);
} else if (item->bounds_type == OBS_BOUNDS_STRETCH) {
scale->x = item->bounds.x / (float)(*cx);
scale->y = item->bounds.y / (float)(*cy);
}
width = (float)(*cx) * scale->x;
height = (float)(*cy) * scale->y;
width_diff = item->bounds.x - width;
height_diff = item->bounds.y - height;
*cx = (uint32_t)item->bounds.x;
*cy = (uint32_t)item->bounds.y;
add_alignment(origin, item->bounds_align,
(int)-width_diff, (int)-height_diff);
}
static void update_item_transform(struct obs_scene_item *item)
{
uint32_t width = obs_source_getwidth(item->source);
uint32_t height = obs_source_getheight(item->source);
uint32_t cx = width;
uint32_t cy = height;
struct vec2 base_origin = {0.0f, 0.0f};
struct vec2 origin = {0.0f, 0.0f};
struct vec2 scale = item->scale;
struct calldata params = {0};
/* ----------------------- */
if (item->bounds_type != OBS_BOUNDS_NONE) {
calculate_bounds_data(item, &origin, &scale, &cx, &cy);
} else {
cx = (uint32_t)((float)cx * scale.x);
cy = (uint32_t)((float)cy * scale.y);
}
add_alignment(&origin, item->align, (int)cx, (int)cy);
matrix4_identity(&item->draw_transform);
matrix4_scale3f(&item->draw_transform, &item->draw_transform,
scale.x, scale.y, 1.0f);
matrix4_translate3f(&item->draw_transform, &item->draw_transform,
-origin.x, -origin.y, 0.0f);
matrix4_rotate_aa4f(&item->draw_transform, &item->draw_transform,
0.0f, 0.0f, 1.0f, RAD(item->rot));
matrix4_translate3f(&item->draw_transform, &item->draw_transform,
item->pos.x, item->pos.y, 0.0f);
/* ----------------------- */
if (item->bounds_type != OBS_BOUNDS_NONE) {
vec2_copy(&scale, &item->bounds);
} else {
scale.x = (float)width * item->scale.x;
scale.y = (float)height * item->scale.y;
}
add_alignment(&base_origin, item->align, (int)scale.x, (int)scale.y);
matrix4_identity(&item->box_transform);
matrix4_scale3f(&item->box_transform, &item->box_transform,
scale.x, scale.y, 1.0f);
matrix4_translate3f(&item->box_transform, &item->box_transform,
-base_origin.x, -base_origin.y, 0.0f);
matrix4_rotate_aa4f(&item->box_transform, &item->box_transform,
0.0f, 0.0f, 1.0f, RAD(item->rot));
matrix4_translate3f(&item->box_transform, &item->box_transform,
item->pos.x, item->pos.y, 0.0f);
/* ----------------------- */
item->last_width = width;
item->last_height = height;
calldata_setptr(&params, "scene", item->parent);
calldata_setptr(&params, "item", item);
signal_handler_signal(item->parent->source->context.signals,
"item_transform", &params);
calldata_free(&params);
}
static inline bool source_size_changed(struct obs_scene_item *item)
{
uint32_t width = obs_source_getwidth(item->source);
uint32_t height = obs_source_getheight(item->source);
return item->last_width != width || item->last_height != height;
}
static void scene_video_render(void *data, effect_t effect)
{
struct obs_scene *scene = data;
@ -173,14 +308,12 @@ static void scene_video_render(void *data, effect_t effect)
continue;
}
if (source_size_changed(item))
update_item_transform(item);
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);
gs_matrix_mul(&item->draw_transform);
obs_source_video_render(item->source);
gs_matrix_pop();
item = item->next;
@ -205,12 +338,24 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t item_data)
item = obs_scene_add(scene, source);
obs_data_set_default_int(item_data, "align",
OBS_ALIGN_TOP | OBS_ALIGN_LEFT);
item->rot = (float)obs_data_getdouble(item_data, "rot");
item->align = (uint32_t)obs_data_getint(item_data, "align");
item->visible = obs_data_getbool(item_data, "visible");
obs_data_get_vec2(item_data, "origin", &item->origin);
obs_data_get_vec2(item_data, "pos", &item->pos);
obs_data_get_vec2(item_data, "scale", &item->scale);
item->bounds_type =
(enum obs_bounds_type)obs_data_getint(item_data, "bounds_type");
item->bounds_align =
(uint32_t)obs_data_getint(item_data, "bounds_align");
obs_data_get_vec2(item_data, "bounds", &item->bounds);
obs_source_release(source);
update_item_transform(item);
}
static void scene_load(void *scene, obs_data_t settings)
@ -238,12 +383,15 @@ static void scene_save_item(obs_data_array_t array, struct obs_scene_item *item)
obs_data_t item_data = obs_data_create();
const char *name = obs_source_getname(item->source);
obs_data_setstring(item_data, "name", name);
obs_data_setbool (item_data, "visible", item->visible);
obs_data_setdouble(item_data, "rot", item->rot);
obs_data_set_vec2 (item_data, "origin", &item->origin);
obs_data_set_vec2 (item_data, "pos", &item->pos);
obs_data_set_vec2 (item_data, "scale", &item->scale);
obs_data_setstring(item_data, "name", name);
obs_data_setbool (item_data, "visible", item->visible);
obs_data_setdouble(item_data, "rot", item->rot);
obs_data_set_vec2 (item_data, "pos", &item->pos);
obs_data_set_vec2 (item_data, "scale", &item->scale);
obs_data_setint (item_data, "align", (int)item->align);
obs_data_setint (item_data, "bounds_type", (int)item->bounds_type);
obs_data_setint (item_data, "bounds_align", (int)item->bounds_align);
obs_data_set_vec2 (item_data, "bounds", &item->bounds);
obs_data_array_push_back(array, item_data);
obs_data_release(item_data);
@ -400,7 +548,10 @@ obs_sceneitem_t obs_scene_add(obs_scene_t scene, obs_source_t source)
item->visible = true;
item->parent = scene;
item->ref = 1;
item->align = OBS_ALIGN_TOP | OBS_ALIGN_LEFT;
vec2_set(&item->scale, 1.0f, 1.0f);
matrix4_identity(&item->draw_transform);
matrix4_identity(&item->box_transform);
obs_source_addref(source);
obs_source_add_child(scene->source, source);
@ -495,28 +646,59 @@ obs_source_t obs_sceneitem_getsource(obs_sceneitem_t item)
return item ? item->source : NULL;
}
void obs_sceneitem_select(obs_sceneitem_t item, bool select)
{
struct calldata params = {0};
const char *command = select ? "item_select" : "item_deselect";
if (!item || item->selected == select)
return;
item->selected = select;
calldata_setptr(&params, "scene", item->parent);
calldata_setptr(&params, "item", item);
signal_handler_signal(item->parent->source->context.signals,
command, &params);
calldata_free(&params);
}
bool obs_sceneitem_selected(obs_sceneitem_t item)
{
return item ? item->selected : false;
}
void obs_sceneitem_setpos(obs_sceneitem_t item, const struct vec2 *pos)
{
if (item)
if (item) {
vec2_copy(&item->pos, pos);
update_item_transform(item);
}
}
void obs_sceneitem_setrot(obs_sceneitem_t item, float rot)
{
if (item)
if (item) {
item->rot = rot;
}
void obs_sceneitem_setorigin(obs_sceneitem_t item, const struct vec2 *origin)
{
if (item)
vec2_copy(&item->origin, origin);
update_item_transform(item);
}
}
void obs_sceneitem_setscale(obs_sceneitem_t item, const struct vec2 *scale)
{
if (item)
if (item) {
vec2_copy(&item->scale, scale);
update_item_transform(item);
}
}
void obs_sceneitem_setalignment(obs_sceneitem_t item, uint32_t alignment)
{
if (item) {
item->align = alignment;
update_item_transform(item);
}
}
static inline void signal_move_dir(struct obs_scene_item *item,
@ -583,6 +765,32 @@ void obs_sceneitem_setorder(obs_sceneitem_t item, enum order_movement movement)
obs_scene_release(scene);
}
void obs_sceneitem_set_bounds_type(obs_sceneitem_t item,
enum obs_bounds_type type)
{
if (item) {
item->bounds_type = type;
update_item_transform(item);
}
}
void obs_sceneitem_set_bounds_alignment(obs_sceneitem_t item,
uint32_t alignment)
{
if (item) {
item->bounds_align = alignment;
update_item_transform(item);
}
}
void obs_sceneitem_set_bounds(obs_sceneitem_t item, const struct vec2 *bounds)
{
if (item) {
item->bounds = *bounds;
update_item_transform(item);
}
}
void obs_sceneitem_getpos(obs_sceneitem_t item, struct vec2 *pos)
{
if (item)
@ -594,14 +802,72 @@ float obs_sceneitem_getrot(obs_sceneitem_t item)
return item ? item->rot : 0.0f;
}
void obs_sceneitem_getorigin(obs_sceneitem_t item, struct vec2 *origin)
{
if (item)
vec2_copy(origin, &item->origin);
}
void obs_sceneitem_getscale(obs_sceneitem_t item, struct vec2 *scale)
{
if (item)
vec2_copy(scale, &item->scale);
}
uint32_t obs_sceneitem_getalignment(obs_sceneitem_t item)
{
return item ? item->align : 0;
}
enum obs_bounds_type obs_sceneitem_get_bounds_type(obs_sceneitem_t item)
{
return item ? item->bounds_type : OBS_BOUNDS_NONE;
}
uint32_t obs_sceneitem_get_bounds_alignment(obs_sceneitem_t item)
{
return item ? item->bounds_align : 0;
}
void obs_sceneitem_get_bounds(obs_sceneitem_t item, struct vec2 *bounds)
{
if (item)
*bounds = item->bounds;
}
void obs_sceneitem_get_info(obs_sceneitem_t item,
struct obs_sceneitem_info *info)
{
if (item && info) {
info->pos = item->pos;
info->rot = item->rot;
info->scale = item->scale;
info->alignment = item->align;
info->bounds_type = item->bounds_type;
info->bounds_alignment = item->bounds_align;
info->bounds = item->bounds;
}
}
void obs_sceneitem_set_info(obs_sceneitem_t item,
const struct obs_sceneitem_info *info)
{
if (item && info) {
item->pos = info->pos;
item->rot = info->rot;
item->scale = info->scale;
item->align = info->alignment;
item->bounds_type = info->bounds_type;
item->bounds_align = info->bounds_alignment;
item->bounds = info->bounds;
update_item_transform(item);
}
}
void obs_sceneitem_get_draw_transform(obs_sceneitem_t item,
struct matrix4 *transform)
{
if (item)
matrix4_copy(transform, &item->draw_transform);
}
void obs_sceneitem_get_box_transform(obs_sceneitem_t item,
struct matrix4 *transform)
{
if (item)
matrix4_copy(transform, &item->box_transform);
}

View file

@ -19,6 +19,7 @@
#include "obs.h"
#include "obs-internal.h"
#include "graphics/matrix4.h"
/* how obs scene! */
@ -29,11 +30,24 @@ struct obs_scene_item {
struct obs_scene *parent;
struct obs_source *source;
bool visible;
bool selected;
struct vec2 origin;
struct vec2 pos;
struct vec2 scale;
float rot;
uint32_t align;
/* last width/height of the source, this is used to check whether
* ths transform needs updating */
uint32_t last_width;
uint32_t last_height;
struct matrix4 box_transform;
struct matrix4 draw_transform;
enum obs_bounds_type bounds_type;
uint32_t bounds_align;
struct vec2 bounds;
/* would do **prev_next, but not really great for reordering */
struct obs_scene_item *prev;

View file

@ -21,6 +21,7 @@
#include "util/bmem.h"
#include "graphics/graphics.h"
#include "graphics/vec2.h"
#include "graphics/vec3.h"
#include "media-io/audio-io.h"
#include "media-io/video-io.h"
#include "callback/signal.h"
@ -32,6 +33,8 @@
#include "obs-ui.h"
#include "obs-properties.h"
struct matrix4;
/* opaque types */
struct obs_display;
struct obs_view;
@ -85,13 +88,38 @@ enum allow_direct_render {
ALLOW_DIRECT_RENDERING,
};
/**
* Used with scene items to indicate the type of bounds to use for scene items.
* Mostly determines how the image will be scaled within those bounds, or
* whether to use bounds at all.
*/
enum obs_bounds_type {
OBS_BOUNDS_NONE, /**< no bounds */
OBS_BOUNDS_ALIGN_ONLY, /**< no scaling to bounds, align only */
OBS_BOUNDS_SCALE_INNER, /**< scales to inner rectangle */
OBS_BOUNDS_SCALE_OUTER, /**< scales to outer rectangle */
OBS_BOUNDS_SCALE_TO_WIDTH, /**< scales to the width */
OBS_BOUNDS_SCALE_TO_HEIGHT, /**< scales to the height */
OBS_BOUNDS_STRETCH /**< stretch (ignores base scale) */
};
struct obs_sceneitem_info {
struct vec2 pos;
float rot;
struct vec2 scale;
uint32_t alignment;
enum obs_bounds_type bounds_type;
uint32_t bounds_alignment;
struct vec2 bounds;
};
/**
* Video initialization structure
*/
struct obs_video_info {
/**
* Graphics module to use (usually "libobs-opengl" or
* "libobs-d3d11")
* Graphics module to use (usually "libobs-opengl" or "libobs-d3d11")
*/
const char *graphics_module;
@ -669,20 +697,44 @@ EXPORT obs_scene_t obs_sceneitem_getscene(obs_sceneitem_t item);
/** Gets the source of a scene item. */
EXPORT obs_source_t obs_sceneitem_getsource(obs_sceneitem_t item);
/* Functions for gettings/setting specific oriantation of a scene item */
EXPORT void obs_sceneitem_select(obs_sceneitem_t item, bool select);
EXPORT bool obs_sceneitem_selected(obs_sceneitem_t item);
/* Functions for gettings/setting specific orientation of a scene item */
EXPORT void obs_sceneitem_setpos(obs_sceneitem_t item, const struct vec2 *pos);
EXPORT void obs_sceneitem_setrot(obs_sceneitem_t item, float rot);
EXPORT void obs_sceneitem_setorigin(obs_sceneitem_t item,
const struct vec2 *origin);
EXPORT void obs_sceneitem_setrot(obs_sceneitem_t item, float rot_deg);
EXPORT void obs_sceneitem_setscale(obs_sceneitem_t item,
const struct vec2 *scale);
EXPORT void obs_sceneitem_setalignment(obs_sceneitem_t item,
uint32_t alignment);
EXPORT void obs_sceneitem_setorder(obs_sceneitem_t item,
enum order_movement movement);
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(obs_sceneitem_t item,
const struct vec2 *bounds);
EXPORT void obs_sceneitem_getpos(obs_sceneitem_t item, struct vec2 *pos);
EXPORT float obs_sceneitem_getrot(obs_sceneitem_t item);
EXPORT void obs_sceneitem_getorigin(obs_sceneitem_t item, struct vec2 *center);
EXPORT void obs_sceneitem_getscale(obs_sceneitem_t item, struct vec2 *scale);
EXPORT uint32_t obs_sceneitem_getalignment(obs_sceneitem_t item);
EXPORT enum obs_bounds_type obs_sceneitem_get_bounds_type(obs_sceneitem_t item);
EXPORT uint32_t obs_sceneitem_get_bounds_alignment(obs_sceneitem_t item);
EXPORT void obs_sceneitem_get_bounds(obs_sceneitem_t item, struct vec2 *bounds);
EXPORT void obs_sceneitem_get_info(obs_sceneitem_t item,
struct obs_sceneitem_info *info);
EXPORT void obs_sceneitem_set_info(obs_sceneitem_t item,
const struct obs_sceneitem_info *info);
EXPORT void obs_sceneitem_get_draw_transform(obs_sceneitem_t item,
struct matrix4 *transform);
EXPORT void obs_sceneitem_get_box_transform(obs_sceneitem_t item,
struct matrix4 *transform);
/* ------------------------------------------------------------------------- */