1
0

crop-filter.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. #include <obs-module.h>
  2. #include <graphics/vec2.h>
  3. struct crop_filter_data {
  4. obs_source_t *context;
  5. gs_effect_t *effect;
  6. gs_eparam_t *param_mul;
  7. gs_eparam_t *param_add;
  8. gs_eparam_t *param_multiplier;
  9. int left;
  10. int right;
  11. int top;
  12. int bottom;
  13. int abs_cx;
  14. int abs_cy;
  15. int width;
  16. int height;
  17. bool absolute;
  18. struct vec2 mul_val;
  19. struct vec2 add_val;
  20. };
  21. static const char *crop_filter_get_name(void *unused)
  22. {
  23. UNUSED_PARAMETER(unused);
  24. return obs_module_text("CropFilter");
  25. }
  26. static void *crop_filter_create(obs_data_t *settings, obs_source_t *context)
  27. {
  28. struct crop_filter_data *filter = bzalloc(sizeof(*filter));
  29. char *effect_path = obs_module_file("crop_filter.effect");
  30. filter->context = context;
  31. obs_enter_graphics();
  32. filter->effect = gs_effect_create_from_file(effect_path, NULL);
  33. obs_leave_graphics();
  34. bfree(effect_path);
  35. if (!filter->effect) {
  36. bfree(filter);
  37. return NULL;
  38. }
  39. filter->param_mul = gs_effect_get_param_by_name(filter->effect, "mul_val");
  40. filter->param_add = gs_effect_get_param_by_name(filter->effect, "add_val");
  41. filter->param_multiplier = gs_effect_get_param_by_name(filter->effect, "multiplier");
  42. obs_source_update(context, settings);
  43. return filter;
  44. }
  45. static void crop_filter_destroy(void *data)
  46. {
  47. struct crop_filter_data *filter = data;
  48. obs_enter_graphics();
  49. gs_effect_destroy(filter->effect);
  50. obs_leave_graphics();
  51. bfree(filter);
  52. }
  53. static void crop_filter_update(void *data, obs_data_t *settings)
  54. {
  55. struct crop_filter_data *filter = data;
  56. filter->absolute = !obs_data_get_bool(settings, "relative");
  57. filter->left = (int)obs_data_get_int(settings, "left");
  58. filter->top = (int)obs_data_get_int(settings, "top");
  59. filter->right = (int)obs_data_get_int(settings, "right");
  60. filter->bottom = (int)obs_data_get_int(settings, "bottom");
  61. filter->abs_cx = (int)obs_data_get_int(settings, "cx");
  62. filter->abs_cy = (int)obs_data_get_int(settings, "cy");
  63. }
  64. static bool relative_clicked(obs_properties_t *props, obs_property_t *p, obs_data_t *settings)
  65. {
  66. bool relative = obs_data_get_bool(settings, "relative");
  67. obs_property_set_description(obs_properties_get(props, "left"), relative ? obs_module_text("Crop.Left") : "X");
  68. obs_property_set_description(obs_properties_get(props, "top"), relative ? obs_module_text("Crop.Top") : "Y");
  69. obs_property_set_visible(obs_properties_get(props, "right"), relative);
  70. obs_property_set_visible(obs_properties_get(props, "bottom"), relative);
  71. obs_property_set_visible(obs_properties_get(props, "cx"), !relative);
  72. obs_property_set_visible(obs_properties_get(props, "cy"), !relative);
  73. UNUSED_PARAMETER(p);
  74. return true;
  75. }
  76. static obs_properties_t *crop_filter_properties(void *data)
  77. {
  78. obs_properties_t *props = obs_properties_create();
  79. obs_property_t *p = obs_properties_add_bool(props, "relative", obs_module_text("Crop.Relative"));
  80. obs_property_set_modified_callback(p, relative_clicked);
  81. obs_properties_add_int(props, "left", obs_module_text("Crop.Left"), -8192, 8192, 1);
  82. obs_properties_add_int(props, "top", obs_module_text("Crop.Top"), -8192, 8192, 1);
  83. obs_properties_add_int(props, "right", obs_module_text("Crop.Right"), -8192, 8192, 1);
  84. obs_properties_add_int(props, "bottom", obs_module_text("Crop.Bottom"), -8192, 8192, 1);
  85. obs_properties_add_int(props, "cx", obs_module_text("Crop.Width"), 0, 8192, 1);
  86. obs_properties_add_int(props, "cy", obs_module_text("Crop.Height"), 0, 8192, 1);
  87. UNUSED_PARAMETER(data);
  88. return props;
  89. }
  90. static void crop_filter_defaults(obs_data_t *settings)
  91. {
  92. obs_data_set_default_bool(settings, "relative", true);
  93. }
  94. static void calc_crop_dimensions(struct crop_filter_data *filter, struct vec2 *mul_val, struct vec2 *add_val)
  95. {
  96. obs_source_t *target = obs_filter_get_target(filter->context);
  97. uint32_t width;
  98. uint32_t height;
  99. if (!target) {
  100. return;
  101. } else {
  102. width = obs_source_get_base_width(target);
  103. height = obs_source_get_base_height(target);
  104. }
  105. if (filter->absolute) {
  106. filter->width = filter->abs_cx;
  107. filter->height = filter->abs_cy;
  108. } else {
  109. filter->width = (int)width - filter->left - filter->right;
  110. filter->height = (int)height - filter->top - filter->bottom;
  111. }
  112. if (filter->width < 1)
  113. filter->width = 1;
  114. if (filter->height < 1)
  115. filter->height = 1;
  116. if (width) {
  117. mul_val->x = (float)filter->width / (float)width;
  118. add_val->x = (float)filter->left / (float)width;
  119. }
  120. if (height) {
  121. mul_val->y = (float)filter->height / (float)height;
  122. add_val->y = (float)filter->top / (float)height;
  123. }
  124. }
  125. static void crop_filter_tick(void *data, float seconds)
  126. {
  127. struct crop_filter_data *filter = data;
  128. vec2_zero(&filter->mul_val);
  129. vec2_zero(&filter->add_val);
  130. calc_crop_dimensions(filter, &filter->mul_val, &filter->add_val);
  131. UNUSED_PARAMETER(seconds);
  132. }
  133. static const char *get_tech_name_and_multiplier(enum gs_color_space current_space, enum gs_color_space source_space,
  134. float *multiplier)
  135. {
  136. const char *tech_name = "Draw";
  137. *multiplier = 1.f;
  138. switch (source_space) {
  139. case GS_CS_SRGB:
  140. case GS_CS_SRGB_16F:
  141. if (current_space == GS_CS_709_SCRGB) {
  142. tech_name = "DrawMultiply";
  143. *multiplier = obs_get_video_sdr_white_level() / 80.0f;
  144. }
  145. break;
  146. case GS_CS_709_EXTENDED:
  147. switch (current_space) {
  148. case GS_CS_SRGB:
  149. case GS_CS_SRGB_16F:
  150. tech_name = "DrawTonemap";
  151. break;
  152. case GS_CS_709_SCRGB:
  153. tech_name = "DrawMultiply";
  154. *multiplier = obs_get_video_sdr_white_level() / 80.0f;
  155. break;
  156. case GS_CS_709_EXTENDED:
  157. break;
  158. }
  159. break;
  160. case GS_CS_709_SCRGB:
  161. switch (current_space) {
  162. case GS_CS_SRGB:
  163. case GS_CS_SRGB_16F:
  164. tech_name = "DrawMultiplyTonemap";
  165. *multiplier = 80.0f / obs_get_video_sdr_white_level();
  166. break;
  167. case GS_CS_709_EXTENDED:
  168. tech_name = "DrawMultiply";
  169. *multiplier = 80.0f / obs_get_video_sdr_white_level();
  170. break;
  171. case GS_CS_709_SCRGB:
  172. break;
  173. }
  174. }
  175. return tech_name;
  176. }
  177. static void crop_filter_render(void *data, gs_effect_t *effect)
  178. {
  179. UNUSED_PARAMETER(effect);
  180. struct crop_filter_data *filter = data;
  181. const enum gs_color_space preferred_spaces[] = {
  182. GS_CS_SRGB,
  183. GS_CS_SRGB_16F,
  184. GS_CS_709_EXTENDED,
  185. };
  186. const enum gs_color_space source_space = obs_source_get_color_space(
  187. obs_filter_get_target(filter->context), OBS_COUNTOF(preferred_spaces), preferred_spaces);
  188. float multiplier;
  189. const char *technique = get_tech_name_and_multiplier(gs_get_color_space(), source_space, &multiplier);
  190. const enum gs_color_format format = gs_get_format_from_space(source_space);
  191. if (obs_source_process_filter_begin_with_color_space(filter->context, format, source_space,
  192. OBS_NO_DIRECT_RENDERING)) {
  193. gs_effect_set_vec2(filter->param_mul, &filter->mul_val);
  194. gs_effect_set_vec2(filter->param_add, &filter->add_val);
  195. gs_effect_set_float(filter->param_multiplier, multiplier);
  196. gs_blend_state_push();
  197. gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);
  198. obs_source_process_filter_tech_end(filter->context, filter->effect, filter->width, filter->height,
  199. technique);
  200. gs_blend_state_pop();
  201. }
  202. }
  203. static uint32_t crop_filter_width(void *data)
  204. {
  205. struct crop_filter_data *crop = data;
  206. return (uint32_t)crop->width;
  207. }
  208. static uint32_t crop_filter_height(void *data)
  209. {
  210. struct crop_filter_data *crop = data;
  211. return (uint32_t)crop->height;
  212. }
  213. static enum gs_color_space crop_filter_get_color_space(void *data, size_t count,
  214. const enum gs_color_space *preferred_spaces)
  215. {
  216. const enum gs_color_space potential_spaces[] = {
  217. GS_CS_SRGB,
  218. GS_CS_SRGB_16F,
  219. GS_CS_709_EXTENDED,
  220. };
  221. struct crop_filter_data *const filter = data;
  222. const enum gs_color_space source_space = obs_source_get_color_space(
  223. obs_filter_get_target(filter->context), OBS_COUNTOF(potential_spaces), potential_spaces);
  224. enum gs_color_space space = source_space;
  225. for (size_t i = 0; i < count; ++i) {
  226. space = preferred_spaces[i];
  227. if (space == source_space)
  228. break;
  229. }
  230. return space;
  231. }
  232. struct obs_source_info crop_filter = {
  233. .id = "crop_filter",
  234. .type = OBS_SOURCE_TYPE_FILTER,
  235. .output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_SRGB,
  236. .get_name = crop_filter_get_name,
  237. .create = crop_filter_create,
  238. .destroy = crop_filter_destroy,
  239. .update = crop_filter_update,
  240. .get_properties = crop_filter_properties,
  241. .get_defaults = crop_filter_defaults,
  242. .video_tick = crop_filter_tick,
  243. .video_render = crop_filter_render,
  244. .get_width = crop_filter_width,
  245. .get_height = crop_filter_height,
  246. .video_get_color_space = crop_filter_get_color_space,
  247. };