浏览代码

libobs: Implement additional source blending modes

jw0z96 4 年之前
父节点
当前提交
447b17e75e
共有 7 个文件被更改,包括 227 次插入1 次删除
  1. 10 0
      UI/data/locale/en-US.ini
  2. 40 0
      UI/window-basic-main.cpp
  3. 4 0
      UI/window-basic-main.hpp
  4. 16 0
      docs/sphinx/reference-scenes.rst
  5. 140 1
      libobs/obs-scene.c
  6. 2 0
      libobs/obs-scene.h
  7. 15 0
      libobs/obs.h

+ 10 - 0
UI/data/locale/en-US.ini

@@ -478,6 +478,16 @@ ScaleFiltering.Bicubic="Bicubic"
 ScaleFiltering.Lanczos="Lanczos"
 ScaleFiltering.Lanczos="Lanczos"
 ScaleFiltering.Area="Area"
 ScaleFiltering.Area="Area"
 
 
+# blending modes
+BlendingMode="Blending Mode"
+BlendingMode.Normal="Normal"
+BlendingMode.Additive="Additive"
+BlendingMode.Subtract="Subtract"
+BlendingMode.Screen="Screen"
+BlendingMode.Multiply="Multiply"
+BlendingMode.Lighten="Lighten"
+BlendingMode.Darken="Darken"
+
 # deinterlacing
 # deinterlacing
 Deinterlacing="Deinterlacing"
 Deinterlacing="Deinterlacing"
 Deinterlacing.Discard="Discard"
 Deinterlacing.Discard="Discard"

+ 40 - 0
UI/window-basic-main.cpp

@@ -2568,6 +2568,7 @@ OBSBasic::~OBSBasic()
 	delete sourceProjector;
 	delete sourceProjector;
 	delete sceneProjectorMenu;
 	delete sceneProjectorMenu;
 	delete scaleFilteringMenu;
 	delete scaleFilteringMenu;
+	delete blendingModeMenu;
 	delete colorMenu;
 	delete colorMenu;
 	delete colorWidgetAction;
 	delete colorWidgetAction;
 	delete colorSelect;
 	delete colorSelect;
@@ -5268,6 +5269,40 @@ QMenu *OBSBasic::AddScaleFilteringMenu(QMenu *menu, obs_sceneitem_t *item)
 	return menu;
 	return menu;
 }
 }
 
 
+void OBSBasic::SetBlendingMode()
+{
+	QAction *action = reinterpret_cast<QAction *>(sender());
+	obs_blending_type mode =
+		(obs_blending_type)action->property("mode").toInt();
+	OBSSceneItem sceneItem = GetCurrentSceneItem();
+
+	obs_sceneitem_set_blending_mode(sceneItem, mode);
+}
+
+QMenu *OBSBasic::AddBlendingModeMenu(QMenu *menu, obs_sceneitem_t *item)
+{
+	obs_blending_type blendingMode = obs_sceneitem_get_blending_mode(item);
+	QAction *action;
+
+#define ADD_MODE(name, mode)                               \
+	action = menu->addAction(QTStr("" name), this,     \
+				 SLOT(SetBlendingMode())); \
+	action->setProperty("mode", (int)mode);            \
+	action->setCheckable(true);                        \
+	action->setChecked(blendingMode == mode);
+
+	ADD_MODE("BlendingMode.Normal", OBS_BLEND_NORMAL);
+	ADD_MODE("BlendingMode.Additive", OBS_BLEND_ADDITIVE);
+	ADD_MODE("BlendingMode.Subtract", OBS_BLEND_SUBTRACT);
+	ADD_MODE("BlendingMode.Screen", OBS_BLEND_SCREEN);
+	ADD_MODE("BlendingMode.Multiply", OBS_BLEND_MULTIPLY);
+	ADD_MODE("BlendingMode.Lighten", OBS_BLEND_LIGHTEN);
+	ADD_MODE("BlendingMode.Darken", OBS_BLEND_DARKEN);
+#undef ADD_MODE
+
+	return menu;
+}
+
 QMenu *OBSBasic::AddBackgroundColorMenu(QMenu *menu,
 QMenu *OBSBasic::AddBackgroundColorMenu(QMenu *menu,
 					QWidgetAction *widgetAction,
 					QWidgetAction *widgetAction,
 					ColorSelect *select,
 					ColorSelect *select,
@@ -5338,6 +5373,7 @@ void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
 	delete previewProjectorSource;
 	delete previewProjectorSource;
 	delete sourceProjector;
 	delete sourceProjector;
 	delete scaleFilteringMenu;
 	delete scaleFilteringMenu;
+	delete blendingModeMenu;
 	delete colorMenu;
 	delete colorMenu;
 	delete colorWidgetAction;
 	delete colorWidgetAction;
 	delete colorSelect;
 	delete colorSelect;
@@ -5469,6 +5505,10 @@ void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
 			AddScaleFilteringMenu(scaleFilteringMenu, sceneItem));
 			AddScaleFilteringMenu(scaleFilteringMenu, sceneItem));
 		popup.addSeparator();
 		popup.addSeparator();
 
 
+		blendingModeMenu = new QMenu(QTStr("BlendingMode"));
+		popup.addMenu(AddBlendingModeMenu(blendingModeMenu, sceneItem));
+		popup.addSeparator();
+
 		popup.addMenu(sourceProjector);
 		popup.addMenu(sourceProjector);
 		popup.addAction(sourceWindow);
 		popup.addAction(sourceWindow);
 		popup.addAction(QTStr("Screenshot.Source"), this,
 		popup.addAction(QTStr("Screenshot.Source"), this,

+ 4 - 0
UI/window-basic-main.hpp

@@ -308,6 +308,7 @@ private:
 	QPointer<QMenu> sceneProjectorMenu;
 	QPointer<QMenu> sceneProjectorMenu;
 	QPointer<QMenu> sourceProjector;
 	QPointer<QMenu> sourceProjector;
 	QPointer<QMenu> scaleFilteringMenu;
 	QPointer<QMenu> scaleFilteringMenu;
+	QPointer<QMenu> blendingModeMenu;
 	QPointer<QMenu> colorMenu;
 	QPointer<QMenu> colorMenu;
 	QPointer<QWidgetAction> colorWidgetAction;
 	QPointer<QWidgetAction> colorWidgetAction;
 	QPointer<ColorSelect> colorSelect;
 	QPointer<ColorSelect> colorSelect;
@@ -707,6 +708,8 @@ private slots:
 
 
 	void SetScaleFilter();
 	void SetScaleFilter();
 
 
+	void SetBlendingMode();
+
 	void IconActivated(QSystemTrayIcon::ActivationReason reason);
 	void IconActivated(QSystemTrayIcon::ActivationReason reason);
 	void SetShowing(bool showing);
 	void SetShowing(bool showing);
 
 
@@ -880,6 +883,7 @@ public:
 
 
 	QMenu *AddDeinterlacingMenu(QMenu *menu, obs_source_t *source);
 	QMenu *AddDeinterlacingMenu(QMenu *menu, obs_source_t *source);
 	QMenu *AddScaleFilteringMenu(QMenu *menu, obs_sceneitem_t *item);
 	QMenu *AddScaleFilteringMenu(QMenu *menu, obs_sceneitem_t *item);
+	QMenu *AddBlendingModeMenu(QMenu *menu, obs_sceneitem_t *item);
 	QMenu *AddBackgroundColorMenu(QMenu *menu, QWidgetAction *widgetAction,
 	QMenu *AddBackgroundColorMenu(QMenu *menu, QWidgetAction *widgetAction,
 				      ColorSelect *select,
 				      ColorSelect *select,
 				      obs_sceneitem_t *item);
 				      obs_sceneitem_t *item);

+ 16 - 0
docs/sphinx/reference-scenes.rst

@@ -492,6 +492,22 @@ Scene Item Functions
 
 
 ---------------------
 ---------------------
 
 
+.. function:: void obs_sceneitem_set_blending_mode(obs_sceneitem_t *item, enum obs_blending_type type)
+              enum obs_blending_type obs_sceneitem_get_blending_mode(obs_sceneitem_t *item)
+
+   Sets/gets the blending mode used for the scene item.
+
+   :param type: | Can be one of the following values:
+                | OBS_BLEND_NORMAL
+                | OBS_BLEND_ADDITIVE
+                | OBS_BLEND_SUBTRACT
+                | OBS_BLEND_SCREEN
+                | OBS_BLEND_MULTIPLY
+                | OBS_BLEND_LIGHTEN
+                | OBS_BLEND_DARKEN
+
+---------------------
+
 .. function:: void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)
 .. function:: void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)
               void obs_sceneitem_defer_update_end(obs_sceneitem_t *item)
               void obs_sceneitem_defer_update_end(obs_sceneitem_t *item)
 
 

+ 140 - 1
libobs/obs-scene.c

@@ -59,6 +59,73 @@ static const char *obs_scene_signals[] = {
 	NULL,
 	NULL,
 };
 };
 
 
+static const struct {
+	enum gs_blend_type src_color;
+	enum gs_blend_type src_alpha;
+	enum gs_blend_type dst_color;
+	enum gs_blend_type dst_alpha;
+	enum gs_blend_op_type op;
+} obs_blend_mode_params[] = {
+	/* clang-format off */
+	// OBS_BLEND_NORMAL
+	{
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_INVSRCALPHA,
+		GS_BLEND_INVSRCALPHA,
+		GS_BLEND_OP_ADD,
+	},
+	// OBS_BLEND_ADDITIVE
+	{
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_OP_ADD,
+	},
+	// OBS_BLEND_SUBTRACT
+	{
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_OP_REVERSE_SUBTRACT,
+	},
+	// OBS_BLEND_SCREEN
+	{
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_INVSRCCOLOR,
+		GS_BLEND_INVSRCALPHA,
+		GS_BLEND_OP_ADD
+	},
+	// OBS_BLEND_MULTIPLY
+	{
+		GS_BLEND_DSTCOLOR,
+		GS_BLEND_DSTALPHA,
+		GS_BLEND_INVSRCALPHA,
+		GS_BLEND_INVSRCALPHA,
+		GS_BLEND_OP_ADD
+	},
+	// OBS_BLEND_LIGHTEN
+	{
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_OP_MAX,
+	},
+	// OBS_BLEND_DARKEN
+	{
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_ONE,
+		GS_BLEND_OP_MIN,
+	},
+	/* clang-format on */
+};
+
 static inline void signal_item_remove(struct obs_scene_item *item)
 static inline void signal_item_remove(struct obs_scene_item *item)
 {
 {
 	struct calldata params;
 	struct calldata params;
@@ -472,6 +539,11 @@ static inline bool scale_filter_enabled(const struct obs_scene_item *item)
 	return item->scale_filter != OBS_SCALE_DISABLE;
 	return item->scale_filter != OBS_SCALE_DISABLE;
 }
 }
 
 
+static inline bool default_blending_enabled(const struct obs_scene_item *item)
+{
+	return item->blend_type == OBS_BLEND_NORMAL;
+}
+
 static inline bool item_is_scene(const struct obs_scene_item *item)
 static inline bool item_is_scene(const struct obs_scene_item *item)
 {
 {
 	return item->source && item->source->info.type == OBS_SOURCE_TYPE_SCENE;
 	return item->source && item->source->info.type == OBS_SOURCE_TYPE_SCENE;
@@ -480,6 +552,7 @@ static inline bool item_is_scene(const struct obs_scene_item *item)
 static inline bool item_texture_enabled(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) || scale_filter_enabled(item) ||
+	       !default_blending_enabled(item) ||
 	       (item_is_scene(item) && !item->is_group);
 	       (item_is_scene(item) && !item->is_group);
 }
 }
 
 
@@ -545,7 +618,13 @@ static void render_item_texture(struct obs_scene_item *item)
 	}
 	}
 
 
 	gs_blend_state_push();
 	gs_blend_state_push();
-	gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
+
+	gs_blend_function_separate(
+		obs_blend_mode_params[item->blend_type].src_color,
+		obs_blend_mode_params[item->blend_type].dst_color,
+		obs_blend_mode_params[item->blend_type].src_alpha,
+		obs_blend_mode_params[item->blend_type].dst_alpha);
+	gs_blend_op(obs_blend_mode_params[item->blend_type].op);
 
 
 	while (gs_effect_loop(effect, tech))
 	while (gs_effect_loop(effect, tech))
 		obs_source_draw(tex, 0, 0, 0, 0, 0);
 		obs_source_draw(tex, 0, 0, 0, 0, 0);
@@ -789,6 +868,7 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
 	const char *name = obs_data_get_string(item_data, "name");
 	const char *name = obs_data_get_string(item_data, "name");
 	obs_source_t *source;
 	obs_source_t *source;
 	const char *scale_filter_str;
 	const char *scale_filter_str;
+	const char *blend_str;
 	struct obs_scene_item *item;
 	struct obs_scene_item *item;
 	bool visible;
 	bool visible;
 	bool lock;
 	bool lock;
@@ -868,6 +948,26 @@ static void scene_load_item(struct obs_scene *scene, obs_data_t *item_data)
 			item->scale_filter = OBS_SCALE_AREA;
 			item->scale_filter = OBS_SCALE_AREA;
 	}
 	}
 
 
+	blend_str = obs_data_get_string(item_data, "blend_type");
+	item->blend_type = OBS_BLEND_NORMAL;
+
+	if (blend_str) {
+		if (astrcmpi(blend_str, "normal") == 0)
+			item->blend_type = OBS_BLEND_NORMAL;
+		else if (astrcmpi(blend_str, "additive") == 0)
+			item->blend_type = OBS_BLEND_ADDITIVE;
+		else if (astrcmpi(blend_str, "subtract") == 0)
+			item->blend_type = OBS_BLEND_SUBTRACT;
+		else if (astrcmpi(blend_str, "screen") == 0)
+			item->blend_type = OBS_BLEND_SCREEN;
+		else if (astrcmpi(blend_str, "multiply") == 0)
+			item->blend_type = OBS_BLEND_MULTIPLY;
+		else if (astrcmpi(blend_str, "lighten") == 0)
+			item->blend_type = OBS_BLEND_LIGHTEN;
+		else if (astrcmpi(blend_str, "darken") == 0)
+			item->blend_type = OBS_BLEND_DARKEN;
+	}
+
 	obs_data_t *show_data = obs_data_get_obj(item_data, "show_transition");
 	obs_data_t *show_data = obs_data_get_obj(item_data, "show_transition");
 	if (show_data) {
 	if (show_data) {
 		obs_sceneitem_transition_load(item, show_data, true);
 		obs_sceneitem_transition_load(item, show_data, true);
@@ -937,6 +1037,7 @@ static void scene_save_item(obs_data_array_t *array,
 	obs_data_t *item_data = obs_data_create();
 	obs_data_t *item_data = obs_data_create();
 	const char *name = obs_source_get_name(item->source);
 	const char *name = obs_source_get_name(item->source);
 	const char *scale_filter;
 	const char *scale_filter;
+	const char *blend_type;
 	struct vec2 pos = item->pos;
 	struct vec2 pos = item->pos;
 	struct vec2 scale = item->scale;
 	struct vec2 scale = item->scale;
 	float rot = item->rot;
 	float rot = item->rot;
@@ -995,6 +1096,25 @@ static void scene_save_item(obs_data_array_t *array,
 
 
 	obs_data_set_string(item_data, "scale_filter", scale_filter);
 	obs_data_set_string(item_data, "scale_filter", scale_filter);
 
 
+	if (item->blend_type == OBS_BLEND_NORMAL)
+		blend_type = "normal";
+	else if (item->blend_type == OBS_BLEND_ADDITIVE)
+		blend_type = "additive";
+	else if (item->blend_type == OBS_BLEND_SUBTRACT)
+		blend_type = "subtract";
+	else if (item->blend_type == OBS_BLEND_SCREEN)
+		blend_type = "screen";
+	else if (item->blend_type == OBS_BLEND_MULTIPLY)
+		blend_type = "multiply";
+	else if (item->blend_type == OBS_BLEND_LIGHTEN)
+		blend_type = "lighten";
+	else if (item->blend_type == OBS_BLEND_DARKEN)
+		blend_type = "darken";
+	else
+		blend_type = "normal";
+
+	obs_data_set_string(item_data, "blend_type", blend_type);
+
 	obs_data_t *show_data = obs_sceneitem_transition_save(item, true);
 	obs_data_t *show_data = obs_sceneitem_transition_save(item, true);
 	obs_data_set_obj(item_data, "show_transition", show_data);
 	obs_data_set_obj(item_data, "show_transition", show_data);
 	obs_data_release(show_data);
 	obs_data_release(show_data);
@@ -1393,6 +1513,7 @@ static inline void duplicate_item_data(struct obs_scene_item *dst,
 	dst->last_height = src->last_height;
 	dst->last_height = src->last_height;
 	dst->output_scale = src->output_scale;
 	dst->output_scale = src->output_scale;
 	dst->scale_filter = src->scale_filter;
 	dst->scale_filter = src->scale_filter;
+	dst->blend_type = src->blend_type;
 	dst->box_transform = src->box_transform;
 	dst->box_transform = src->box_transform;
 	dst->box_scale = src->box_scale;
 	dst->box_scale = src->box_scale;
 	dst->draw_transform = src->draw_transform;
 	dst->draw_transform = src->draw_transform;
@@ -2741,6 +2862,24 @@ enum obs_scale_type obs_sceneitem_get_scale_filter(obs_sceneitem_t *item)
 		       : OBS_SCALE_DISABLE;
 		       : OBS_SCALE_DISABLE;
 }
 }
 
 
+void obs_sceneitem_set_blending_mode(obs_sceneitem_t *item,
+				     enum obs_blending_type type)
+{
+	if (!obs_ptr_valid(item, "obs_sceneitem_set_blending_mode"))
+		return;
+
+	item->blend_type = type;
+
+	os_atomic_set_bool(&item->update_transform, true);
+}
+
+enum obs_blending_type obs_sceneitem_get_blending_mode(obs_sceneitem_t *item)
+{
+	return obs_ptr_valid(item, "obs_sceneitem_get_blending_mode")
+		       ? item->blend_type
+		       : OBS_BLEND_NORMAL;
+}
+
 void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)
 void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item)
 {
 {
 	if (!obs_ptr_valid(item, "obs_sceneitem_defer_update_begin"))
 	if (!obs_ptr_valid(item, "obs_sceneitem_defer_update_begin"))

+ 2 - 0
libobs/obs-scene.h

@@ -63,6 +63,8 @@ struct obs_scene_item {
 	struct vec2 output_scale;
 	struct vec2 output_scale;
 	enum obs_scale_type scale_filter;
 	enum obs_scale_type scale_filter;
 
 
+	enum obs_blending_type blend_type;
+
 	struct matrix4 box_transform;
 	struct matrix4 box_transform;
 	struct vec2 box_scale;
 	struct vec2 box_scale;
 	struct matrix4 draw_transform;
 	struct matrix4 draw_transform;

+ 15 - 0
libobs/obs.h

@@ -121,6 +121,16 @@ enum obs_scale_type {
 	OBS_SCALE_AREA,
 	OBS_SCALE_AREA,
 };
 };
 
 
+enum obs_blending_type {
+	OBS_BLEND_NORMAL,
+	OBS_BLEND_ADDITIVE,
+	OBS_BLEND_SUBTRACT,
+	OBS_BLEND_SCREEN,
+	OBS_BLEND_MULTIPLY,
+	OBS_BLEND_LIGHTEN,
+	OBS_BLEND_DARKEN,
+};
+
 /**
 /**
  * Used with scene items to indicate the type of bounds to use for scene items.
  * 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
  * Mostly determines how the image will be scaled within those bounds, or
@@ -1752,6 +1762,11 @@ EXPORT void obs_sceneitem_set_scale_filter(obs_sceneitem_t *item,
 EXPORT enum obs_scale_type
 EXPORT enum obs_scale_type
 obs_sceneitem_get_scale_filter(obs_sceneitem_t *item);
 obs_sceneitem_get_scale_filter(obs_sceneitem_t *item);
 
 
+EXPORT void obs_sceneitem_set_blending_mode(obs_sceneitem_t *item,
+					    enum obs_blending_type type);
+EXPORT enum obs_blending_type
+obs_sceneitem_get_blending_mode(obs_sceneitem_t *item);
+
 EXPORT void obs_sceneitem_force_update_transform(obs_sceneitem_t *item);
 EXPORT void obs_sceneitem_force_update_transform(obs_sceneitem_t *item);
 
 
 EXPORT void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item);
 EXPORT void obs_sceneitem_defer_update_begin(obs_sceneitem_t *item);