diff --git a/CMakePresets.json b/CMakePresets.json index 1becb6e4a..4ddfe117a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -103,7 +103,7 @@ "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { "BUILD_TESTING": "OFF", - "CMAKE_INSTALL_PREFIX": "${sourceDir}/install/${presetName}", + "CMAKE_INSTALL_PREFIX": "c:/Projects/Shotcut", "CMAKE_BUILD_TYPE": "RelWithDebInfo", "CMAKE_PREFIX_PATH": "C:/Qt/$env{QT_VERSION}/mingw_64;C:/msys64/mingw64;C:/msys64/clangarm64", "MOD_GLAXNIMATE_QT6": "ON", diff --git a/src/modules/openfx/filter_openfx.c b/src/modules/openfx/filter_openfx.c index fda54adb0..8c7560a48 100644 --- a/src/modules/openfx/filter_openfx.c +++ b/src/modules/openfx/filter_openfx.c @@ -19,6 +19,7 @@ #include "mlt_openfx.h" +#include #include #include #include @@ -28,6 +29,154 @@ extern mlt_properties mltofx_context; extern mlt_properties mltofx_dl; static const char OFX_IMAGE_EFFECT[] = "_ofx_image_effect"; +static const char NATRON_FORMAT_CHOICE_PARAM[] = "NatronParamFormatChoice"; +static const char NATRON_FORMAT_SIZE_PARAM[] = "NatronParamFormatSize"; + +typedef struct +{ + int saw_choice; + int width; + int height; +} natron_format_compat_state; + +static int parse_choice_dimensions(const char *choice, int *out_w, int *out_h) +{ + if (!choice || !out_w || !out_h) + return 0; + + for (const char *p = choice; *p; ++p) { + if (!isdigit((unsigned char) *p)) + continue; + + // Only start at the beginning of a numeric token, not the middle of one. + if (p > choice && isdigit((unsigned char) p[-1])) + continue; + + const char *q = p; + int w = 0; + while (isdigit((unsigned char) *q)) { + w = w * 10 + (*q - '0'); + ++q; + } + + if (*q != 'x' && *q != 'X') + continue; + ++q; + if (!isdigit((unsigned char) *q)) + continue; + + int h = 0; + while (isdigit((unsigned char) *q)) { + h = h * 10 + (*q - '0'); + ++q; + } + + // Height token must also end cleanly (space/end/punctuation), not in the middle of digits. + if (isdigit((unsigned char) *q)) + continue; + + if (w > 0 && h > 0) { + *out_w = w; + *out_h = h; + return 1; + } + } + + return 0; +} + +static void natron_compat_track_choice(natron_format_compat_state *state, + const char *param_name, + const char *value) +{ + if (!state || !param_name || !value) + return; + if (strcmp(param_name, NATRON_FORMAT_CHOICE_PARAM) != 0) + return; + + state->saw_choice = 1; + parse_choice_dimensions(value, &state->width, &state->height); +} + +static void natron_compat_apply_size(mlt_properties image_effect_params, + const natron_format_compat_state *state) +{ + if (!image_effect_params || !state) + return; + if (!state->saw_choice || state->width <= 0 || state->height <= 0) + return; + + mltofx_param_set_value(image_effect_params, + (char *) NATRON_FORMAT_SIZE_PARAM, + mltofx_prop_int2d, + state->width, + state->height); +} + +static void reset_image_effect_action_props(mlt_properties image_effect, const char *name) +{ + mlt_properties old_props = mlt_properties_get_properties(image_effect, name); + mlt_properties_set_properties(image_effect, name, mlt_properties_new()); + if (old_props) + mlt_properties_close(old_props); +} + +static void init_image_effect_action_props(mlt_properties image_effect) +{ + reset_image_effect_action_props(image_effect, "begin_sequence_props"); + reset_image_effect_action_props(image_effect, "end_sequence_props"); + reset_image_effect_action_props(image_effect, "get_rod_in_args"); + reset_image_effect_action_props(image_effect, "get_rod_out_args"); + reset_image_effect_action_props(image_effect, "get_roi_in_args"); + reset_image_effect_action_props(image_effect, "get_roi_out_args"); + reset_image_effect_action_props(image_effect, "get_clippref_args"); + reset_image_effect_action_props(image_effect, "render_in_args"); +} + +static mlt_properties create_image_effect_instance(OfxPlugin *plugin, + mlt_properties filter_properties, + int sync_param_backlink) +{ + mlt_properties params = mlt_properties_new(); + if (!params) + return NULL; + + mlt_properties image_effect = mltofx_fetch_params(plugin, params, NULL); + if (!image_effect) { + mlt_properties_close(params); + return NULL; + } + + if (sync_param_backlink) { + mlt_properties image_effect_params = mlt_properties_get_properties(image_effect, "params"); + int param_count = mlt_properties_count(image_effect_params); + for (int i = 0; i < param_count; ++i) { + char *param_name = mlt_properties_get_name(image_effect_params, i); + mlt_properties param = mlt_properties_get_properties(image_effect_params, param_name); + if (!param) + continue; + mlt_properties param_props = mlt_properties_get_properties(param, "p"); + if (!param_props) + continue; + mlt_properties_set_data(param_props, + "_filter_properties", + filter_properties, + 0, + NULL, + NULL); + } + } + + mltofx_create_instance(plugin, image_effect); + init_image_effect_action_props(image_effect); + mlt_properties_set_data(image_effect, + "_runtime_params", + params, + 0, + (mlt_destructor) mlt_properties_close, + NULL); + return image_effect; +} static mlt_image_format select_image_format(mlt_image_format incoming, mltofx_depths_mask plugin_support_depths, @@ -60,6 +209,8 @@ static void update_plugin_params(mlt_properties properties, mlt_position position, mlt_position length) { + natron_format_compat_state natron_compat = {0}; + int params_count = mlt_properties_count(params); for (int i = 0; i < params_count; ++i) { char *param_key = mlt_properties_get_name(params, i); @@ -69,6 +220,8 @@ static void update_plugin_params(mlt_properties properties, char *param_name = mlt_properties_get(param, "identifier"); if (!param_name) continue; + if (!mlt_properties_exists(properties, param_name)) + continue; char *type = mlt_properties_get(param, "type"); char *widget = mlt_properties_get(param, "widget"); if (!type) @@ -77,6 +230,12 @@ static void update_plugin_params(mlt_properties properties, && strcmp(type, "float") == 0) { mlt_rect value = mlt_properties_anim_get_rect(properties, param_name, position, length); mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_double2d, value); + } else if (widget && (strcmp(widget, "point") == 0 || strcmp(widget, "size") == 0) + && strcmp(type, "integer") == 0) { + mlt_rect value = mlt_properties_anim_get_rect(properties, param_name, position, length); + int x = (int) value.x; + int y = (int) value.y; + mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_int2d, x, y); } else if (strcmp(type, "float") == 0) { double value = mlt_properties_anim_get_double(properties, param_name, position, length); mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_double, value); @@ -85,14 +244,18 @@ static void update_plugin_params(mlt_properties properties, mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_int, value); } else if (strcmp(type, "string") == 0) { char *value = mlt_properties_anim_get(properties, param_name, position, length); - if (value) + if (value) { + natron_compat_track_choice(&natron_compat, param_name, value); mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_string, value); + } } else if (strcmp(type, "color") == 0) { mlt_color value = mlt_properties_anim_get_color(properties, param_name, position, length); mltofx_param_set_value(image_effect_params, param_name, mltofx_prop_color, value); } } + + natron_compat_apply_size(image_effect_params, &natron_compat); } static int filter_get_image(mlt_frame frame, @@ -105,9 +268,18 @@ static int filter_get_image(mlt_frame frame, mlt_filter filter = (mlt_filter) mlt_frame_pop_service(frame); mlt_properties properties = MLT_FILTER_PROPERTIES(filter); OfxPlugin *plugin = mlt_properties_get_data(properties, "ofx_plugin", NULL); - mlt_properties image_effect = mlt_properties_get_properties(properties, OFX_IMAGE_EFFECT); + mlt_properties base_image_effect = mlt_properties_get_properties(properties, OFX_IMAGE_EFFECT); + int allow_frame_threading = mlt_properties_get_int(properties, "_allow_frame_threading"); + mlt_properties image_effect = base_image_effect; + if (allow_frame_threading) { + image_effect = create_image_effect_instance(plugin, properties, 0); + if (!image_effect) + image_effect = base_image_effect; + } + mlt_properties params = mlt_properties_get_properties(image_effect, "mltofx_params"); mlt_properties image_effect_params = mlt_properties_get_properties(image_effect, "params"); + int lock_service = (image_effect == base_image_effect); mlt_image_format requested_format = *format; mltofx_depths_mask plugin_support_depths = mltofx_plugin_supported_depths(image_effect); @@ -133,6 +305,10 @@ static int filter_get_image(mlt_frame frame, if (pixel_aspect_ratio <= 0.0) pixel_aspect_ratio = 1.0; + mlt_profile profile = mlt_service_profile(MLT_FILTER_SERVICE(filter)); + double render_scale_x = mlt_profile_scale_width(profile, *width); + double render_scale_y = mlt_profile_scale_height(profile, *height); + mlt_position position = mlt_filter_get_position(filter, frame); mlt_position length = mlt_filter_get_length2(filter, frame); double ofx_time = (double) position; @@ -195,7 +371,11 @@ static int filter_get_image(mlt_frame frame, : use_float ? (uint8_t *) float_out : *image; - mlt_service_lock(MLT_FILTER_SERVICE(filter)); + if (lock_service) + mlt_service_lock(MLT_FILTER_SERVICE(filter)); + + // In serialized mode, image_effect is shared across frames; update scale under lock. + mltofx_set_render_scale(image_effect, render_scale_x, render_scale_y); update_plugin_params(properties, image_effect_params, params, position, length); @@ -219,8 +399,10 @@ static int filter_get_image(mlt_frame frame, pixel_aspect_ratio, ofx_depth); - // OFX pre-render action order: GetClipPreferences → GetRegionsOfInterest → BeginSequenceRender + // OFX pre-render action order: + // GetClipPreferences -> GetRegionOfDefinition -> GetRegionsOfInterest -> BeginSequenceRender mltofx_get_clip_preferences(plugin, image_effect); + mltofx_get_region_of_definition(plugin, image_effect, ofx_time); mltofx_get_regions_of_interest(plugin, image_effect, ofx_time, (double) *width, (double) *height); mltofx_begin_sequence_render(plugin, image_effect, ofx_time); @@ -245,7 +427,13 @@ static int filter_get_image(mlt_frame frame, mltofx_action_render(plugin, image_effect, ofx_time, *width, *height); mltofx_end_sequence_render(plugin, image_effect, ofx_time); - mlt_service_unlock(MLT_FILTER_SERVICE(filter)); + if (lock_service) + mlt_service_unlock(MLT_FILTER_SERVICE(filter)); + + if (image_effect != base_image_effect) { + mltofx_destroy_instance(plugin, image_effect); + mlt_properties_close(image_effect); + } // Convert output back to rgba64 in-place — outside the lock, per-frame only. if (use_half) { @@ -319,30 +507,24 @@ mlt_filter filter_openfx_init(mlt_profile profile, mlt_service_type type, const mlt_filter_close(filter); return NULL; } - mlt_properties params = mlt_properties_new(); - mlt_properties image_effect = mltofx_fetch_params(pt, params, NULL); - mltofx_create_instance(pt, image_effect); - - mlt_properties_set_properties(image_effect, "begin_sequence_props", mlt_properties_new()); - mlt_properties_close(mlt_properties_get_properties(image_effect, "get_rod_in_args")); - mlt_properties_set_properties(image_effect, "end_sequence_props", mlt_properties_new()); - mlt_properties_close(mlt_properties_get_properties(image_effect, "get_rod_out_args")); - mlt_properties_set_properties(image_effect, "get_rod_in_args", mlt_properties_new()); - mlt_properties_close(mlt_properties_get_properties(image_effect, "get_roi_in_args")); - mlt_properties_set_properties(image_effect, "get_rod_out_args", mlt_properties_new()); - mlt_properties_close(mlt_properties_get_properties(image_effect, "get_roi_out_args")); - mlt_properties_set_properties(image_effect, "get_roi_in_args", mlt_properties_new()); - mlt_properties_close(mlt_properties_get_properties(image_effect, "get_roi_in_args")); - mlt_properties_set_properties(image_effect, "get_roi_out_args", mlt_properties_new()); - mlt_properties_close(mlt_properties_get_properties(image_effect, "get_roi_out_args")); - mlt_properties_set_properties(image_effect, "get_clippref_args", mlt_properties_new()); - mlt_properties_close(mlt_properties_get_properties(image_effect, "get_clippref_args")); - mlt_properties_set_properties(image_effect, "render_in_args", mlt_properties_new()); - mlt_properties_close(mlt_properties_get_properties(image_effect, "render_in_args")); + mlt_properties image_effect = create_image_effect_instance(pt, properties, 1); + if (!image_effect) { + mlt_log_error(filter, "Failed to create OpenFX image effect instance: %s\n", id); + mlt_filter_close(filter); + return NULL; + } + + mlt_properties_set_int(properties, + "_allow_frame_threading", + mltofx_allows_frame_threading(image_effect)); + mlt_log_info(filter, + "OpenFX threading mode for %s: %s\n", + id, + mlt_properties_get_int(properties, "_allow_frame_threading") ? "frame-threaded" + : "serialized"); mlt_properties_set_data(properties, "ofx_plugin", pt, 0, NULL, NULL); mlt_properties_set_properties(properties, OFX_IMAGE_EFFECT, image_effect); mlt_properties_close(image_effect); - mlt_properties_close(params); filter->process = filter_process; filter->close = filter_close; return filter; diff --git a/src/modules/openfx/mlt_openfx.c b/src/modules/openfx/mlt_openfx.c index 1effc2911..ca225f4c6 100644 --- a/src/modules/openfx/mlt_openfx.c +++ b/src/modules/openfx/mlt_openfx.c @@ -142,6 +142,10 @@ static OfxStatus propGetIntN(OfxPropertySetHandle properties, const char *property, int count, int *value); +static OfxStatus propSetString(OfxPropertySetHandle properties, + const char *property, + int index, + const char *value); static OfxStatus getPropertySet(OfxImageEffectHandle imageEffect, OfxPropertySetHandle *propHandle) { @@ -179,6 +183,11 @@ static OfxStatus clipDefine(OfxImageEffectHandle imageEffect, mlt_properties_close(clip_props); *propertySet = (OfxPropertySetHandle) clip_props; + propSetString((OfxPropertySetHandle) clip_props, + kOfxImageEffectPropPreMultiplication, + 0, + kOfxImageUnPreMultiplied); + return kOfxStatOK; } @@ -211,19 +220,12 @@ static OfxStatus clipGetImage(OfxImageClipHandle clip, const OfxRectD *region, OfxPropertySetHandle *imageHandle) { + (void) time; + (void) region; if (!clip) return kOfxStatErrBadHandle; *imageHandle = (OfxPropertySetHandle) mlt_properties_get_properties((mlt_properties) clip, "props"); - if (region != NULL) { - OfxRectI rect = {0, 0, 0, 0}; - propGetIntN((OfxPropertySetHandle) *imageHandle, kOfxImagePropBounds, 4, &rect.x1); - OfxRectD *region2 = (OfxRectD *) region; - region2->x1 = (double) rect.x1; - region2->x2 = (double) rect.x2; - region2->y1 = (double) rect.y1; - region2->y2 = (double) rect.y2; - } return kOfxStatOK; } @@ -696,6 +698,7 @@ static OfxStatus paramDefine(OfxParamSetHandle paramSet, || strcmp(kOfxParamTypePushButton, paramType) == 0) { mlt_properties pt = mlt_properties_new(); mlt_properties_set_string(pt, "t", paramType); + mlt_properties_set_string(pt, "identifier", name); mlt_properties param_props = mlt_properties_new(); mlt_properties_set_properties(pt, "p", param_props); @@ -840,23 +843,31 @@ static OfxStatus paramGetValueImpl(OfxParamHandle paramHandle, va_list ap) OfxStatus status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); - status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); - status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); - status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 3, alpha); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 3, alpha); if (status != kOfxStatOK) { status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, red); - status = propGetDouble((OfxPropertySetHandle) param_props, - "OfxParamPropDefault", - 1, - green); - status - = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 2, blue); - status = propGetDouble((OfxPropertySetHandle) param_props, - "OfxParamPropDefault", - 3, - alpha); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, + "OfxParamPropDefault", + 1, + green); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, + "OfxParamPropDefault", + 2, + blue); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, + "OfxParamPropDefault", + 3, + alpha); if (status != kOfxStatOK) return kOfxStatErrUnknown; } @@ -867,32 +878,105 @@ static OfxStatus paramGetValueImpl(OfxParamHandle paramHandle, va_list ap) OfxStatus status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); - status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); - status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); if (status != kOfxStatOK) { status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, red); - status = propGetDouble((OfxPropertySetHandle) param_props, - "OfxParamPropDefault", - 1, - green); - status - = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 2, blue); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, + "OfxParamPropDefault", + 1, + green); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, + "OfxParamPropDefault", + 2, + blue); if (status != kOfxStatOK) return kOfxStatErrUnknown; } } else if (strcmp(param_type, kOfxParamTypeDouble2D) == 0) { + double *X = va_arg(ap, double *); double *Y = va_arg(ap, double *); + + OfxStatus status + = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, X); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, Y); + + if (status != kOfxStatOK) { + status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, X); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, + "OfxParamPropDefault", + 1, + Y); + if (status != kOfxStatOK) + return kOfxStatErrUnknown; + } + } else if (strcmp(param_type, kOfxParamTypeInteger2D) == 0) { + int *X = va_arg(ap, int *); + int *Y = va_arg(ap, int *); + + OfxStatus status = propGetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, X); + if (status == kOfxStatOK) + status = propGetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, Y); + + if (status != kOfxStatOK) { + status = propGetInt((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, X); + if (status == kOfxStatOK) + status = propGetInt((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 1, Y); + if (status != kOfxStatOK) + return kOfxStatErrUnknown; + } + } else if (strcmp(param_type, kOfxParamTypeDouble3D) == 0) { double *X = va_arg(ap, double *); + double *Y = va_arg(ap, double *); + double *Z = va_arg(ap, double *); OfxStatus status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, X); - status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, Y); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, Y); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, Z); if (status != kOfxStatOK) { status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, X); - status = propGetDouble((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 1, Y); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, + "OfxParamPropDefault", + 1, + Y); + if (status == kOfxStatOK) + status = propGetDouble((OfxPropertySetHandle) param_props, + "OfxParamPropDefault", + 2, + Z); + if (status != kOfxStatOK) + return kOfxStatErrUnknown; + } + } else if (strcmp(param_type, kOfxParamTypeInteger3D) == 0) { + int *X = va_arg(ap, int *); + int *Y = va_arg(ap, int *); + int *Z = va_arg(ap, int *); + + OfxStatus status = propGetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, X); + if (status == kOfxStatOK) + status = propGetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, Y); + if (status == kOfxStatOK) + status = propGetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, Z); + + if (status != kOfxStatOK) { + status = propGetInt((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 0, X); + if (status == kOfxStatOK) + status = propGetInt((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 1, Y); + if (status == kOfxStatOK) + status = propGetInt((OfxPropertySetHandle) param_props, "OfxParamPropDefault", 2, Z); if (status != kOfxStatOK) return kOfxStatErrUnknown; } @@ -933,6 +1017,95 @@ static OfxStatus paramGetIntegral(OfxParamHandle paramHandle, OfxTime time1, Ofx return kOfxStatErrUnsupported; } +static uint8_t mltofx_double_to_color_component(double value) +{ + if (!(value >= 0.0)) + value = 0.0; + else if (value > 1.0) + value = 1.0; + return (uint8_t) lrint(value * 255.0); +} + +static void mltofx_sync_filter_property_from_param(mlt_properties param, mlt_properties param_props) +{ + mlt_properties filter_properties = mlt_properties_get_data(param_props, + "_filter_properties", + NULL); + char *param_name = mlt_properties_get(param, "identifier"); + char *param_type = mlt_properties_get(param, "t"); + if (!filter_properties || !param_name || !param_type) + return; + + if (strcmp(param_type, kOfxParamTypeInteger) == 0 + || strcmp(param_type, kOfxParamTypeBoolean) == 0) { + int value = 0; + if (propGetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, &value) + == kOfxStatOK) + mlt_properties_set_int(filter_properties, param_name, value); + } else if (strcmp(param_type, kOfxParamTypeChoice) == 0 + || strcmp(param_type, kOfxParamTypeString) == 0 + || strcmp(param_type, kOfxParamTypeStrChoice) == 0) { + char *value = NULL; + if (propGetString((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, &value) + == kOfxStatOK + && value) { + mlt_properties_set(filter_properties, param_name, value); + } + } else if (strcmp(param_type, kOfxParamTypeDouble) == 0) { + double value = 0.0; + if (propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, &value) + == kOfxStatOK) { + mlt_properties_set_double(filter_properties, param_name, value); + } + } else if (strcmp(param_type, kOfxParamTypeDouble2D) == 0) { + double x = 0.0; + double y = 0.0; + if (propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, &x) + == kOfxStatOK + && propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, &y) + == kOfxStatOK) { + char value[90] = ""; + snprintf(value, sizeof(value), "%.4f %.4f", x, y); + mlt_properties_set(filter_properties, param_name, value); + } + } else if (strcmp(param_type, kOfxParamTypeRGBA) == 0) { + double red = 0.0; + double green = 0.0; + double blue = 0.0; + double alpha = 1.0; + if (propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, &red) + == kOfxStatOK + && propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, &green) + == kOfxStatOK + && propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, &blue) + == kOfxStatOK + && propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 3, &alpha) + == kOfxStatOK) { + mlt_color value = {mltofx_double_to_color_component(red), + mltofx_double_to_color_component(green), + mltofx_double_to_color_component(blue), + mltofx_double_to_color_component(alpha)}; + mlt_properties_set_color(filter_properties, param_name, value); + } + } else if (strcmp(param_type, kOfxParamTypeRGB) == 0) { + double red = 0.0; + double green = 0.0; + double blue = 0.0; + if (propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, &red) + == kOfxStatOK + && propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, &green) + == kOfxStatOK + && propGetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, &blue) + == kOfxStatOK) { + mlt_color value = {mltofx_double_to_color_component(red), + mltofx_double_to_color_component(green), + mltofx_double_to_color_component(blue), + 255}; + mlt_properties_set_color(filter_properties, param_name, value); + } + } +} + static OfxStatus paramSetValueImpl(OfxParamHandle paramHandle, va_list ap) { mlt_properties param = (mlt_properties) paramHandle; @@ -958,37 +1131,55 @@ static OfxStatus paramSetValueImpl(OfxParamHandle paramHandle, va_list ap) double value = va_arg(ap, double); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); } else if (strcmp(param_type, kOfxParamTypeRGBA) == 0) { - mlt_color value = va_arg(ap, mlt_color); - - double red = (double) value.r / 255.0; - double green = (double) value.g / 255.0; - double blue = (double) value.b / 255.0; - double alpha = (double) value.a / 255.0; + double red = va_arg(ap, double); + double green = va_arg(ap, double); + double blue = va_arg(ap, double); + double alpha = va_arg(ap, double); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 3, alpha); } else if (strcmp(param_type, kOfxParamTypeRGB) == 0) { - mlt_color value = va_arg(ap, mlt_color); - - double red = (double) value.r / 255.0; - double green = (double) value.g / 255.0; - double blue = (double) value.b / 255.0; + double red = va_arg(ap, double); + double green = va_arg(ap, double); + double blue = va_arg(ap, double); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, red); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, green); propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, blue); } else if (strcmp(param_type, kOfxParamTypeDouble2D) == 0) { - mlt_rect value = va_arg(ap, mlt_rect); - propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value.x); - propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, value.y); + double x = va_arg(ap, double); + double y = va_arg(ap, double); + propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, x); + propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, y); + } else if (strcmp(param_type, kOfxParamTypeInteger2D) == 0) { + int x = va_arg(ap, int); + int y = va_arg(ap, int); + propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, x); + propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, y); + } else if (strcmp(param_type, kOfxParamTypeDouble3D) == 0) { + double x = va_arg(ap, double); + double y = va_arg(ap, double); + double z = va_arg(ap, double); + propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, x); + propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, y); + propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, z); + } else if (strcmp(param_type, kOfxParamTypeInteger3D) == 0) { + int x = va_arg(ap, int); + int y = va_arg(ap, int); + int z = va_arg(ap, int); + propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, x); + propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, y); + propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 2, z); } else if (strcmp(param_type, kOfxParamTypeString) == 0 || strcmp(param_type, kOfxParamTypeStrChoice) == 0) { char *value = va_arg(ap, char *); propSetString((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, value); } + mltofx_sync_filter_property_from_param(param, param_props); + return kOfxStatOK; } @@ -1191,7 +1382,7 @@ static OfxStatus multiThread(OfxThreadFunctionV1 func, unsigned int nThreads, vo if (ofx_get_multi_thread_depth()) return kOfxStatErrExists; #endif - mlt_log_verbose(NULL, "[openfx] multiThread: func=%p nThreads=%u\n", func, nThreads); + mlt_log_debug(NULL, "[openfx] multiThread: func=%p nThreads=%u\n", func, nThreads); maxThreads = (unsigned int) mlt_slices_count_normal(); if (maxThreads < 1) maxThreads = 1; @@ -1344,9 +1535,76 @@ static OfxMultiThreadSuiteV1 MltOfxMultiThreadSuiteV1 = {multiThread, mutexUnLock, mutexTryLock}; +static void mltofx_log_message(const char *kind, + const char *messageType, + const char *messageId, + const char *format, + va_list ap) +{ + char formatted[1024] = ""; + const char *type = messageType ? messageType : "OfxMessageUnknown"; + const char *id = (messageId && messageId[0]) ? messageId : NULL; + + if (format && format[0]) { + vsnprintf(formatted, sizeof(formatted), format, ap); + } + + if (strcmp(type, "OfxMessageFatal") == 0) { + mlt_log_fatal(NULL, + "[openfx] %s type=%s%s%s%s%s\n", + kind, + type, + id ? " id=" : "", + id ? id : "", + formatted[0] ? " msg=" : "", + formatted); + } else if (strcmp(type, "OfxMessageError") == 0) { + mlt_log_error(NULL, + "[openfx] %s type=%s%s%s%s%s\n", + kind, + type, + id ? " id=" : "", + id ? id : "", + formatted[0] ? " msg=" : "", + formatted); + } else if (strcmp(type, "OfxMessageWarning") == 0) { + mlt_log_warning(NULL, + "[openfx] %s type=%s%s%s%s%s\n", + kind, + type, + id ? " id=" : "", + id ? id : "", + formatted[0] ? " msg=" : "", + formatted); + } else if (strcmp(type, "OfxMessageLog") == 0) { + mlt_log_verbose(NULL, + "[openfx] %s type=%s%s%s%s%s\n", + kind, + type, + id ? " id=" : "", + id ? id : "", + formatted[0] ? " msg=" : "", + formatted); + } else { + mlt_log_info(NULL, + "[openfx] %s type=%s%s%s%s%s\n", + kind, + type, + id ? " id=" : "", + id ? id : "", + formatted[0] ? " msg=" : "", + formatted); + } +} + static OfxStatus message( void *handle, const char *messageType, const char *messageId, const char *format, ...) { + (void) handle; + va_list ap; + va_start(ap, format); + mltofx_log_message("message", messageType, messageId, format, ap); + va_end(ap); return kOfxStatOK; } @@ -1355,11 +1613,18 @@ static OfxMessageSuiteV1 MltOfxMessageSuiteV1 = {message}; static OfxStatus setPersistentMessage( void *handle, const char *messageType, const char *messageId, const char *format, ...) { + (void) handle; + va_list ap; + va_start(ap, format); + mltofx_log_message("persistent", messageType, messageId, format, ap); + va_end(ap); return kOfxStatOK; } static OfxStatus clearPersistentMessage(void *handle) { + (void) handle; + mlt_log_debug(NULL, "[openfx] persistent message cleared\n"); return kOfxStatOK; } @@ -1649,6 +1914,180 @@ static void mltofx_log_status_code(OfxStatus code, char *msg) mlt_log_debug(NULL, "\n"); } +static void mltofx_make_clip_pref_key(char *key, + size_t key_size, + const char *prefix, + const char *clip_name) +{ + snprintf(key, key_size, "%s%s", prefix, clip_name); +} + +static void mltofx_prime_clip_preferences(mlt_properties image_effect, mlt_properties out_args) +{ + if (!image_effect || !out_args) + return; + + mlt_properties clips = mlt_properties_get_properties(image_effect, "clips"); + int clips_count = clips ? mlt_properties_count(clips) : 0; + + for (int i = 0; i < clips_count; ++i) { + char *clip_name = mlt_properties_get_name(clips, i); + if (!clip_name) + continue; + + mlt_properties clip = NULL; + mlt_properties clip_props = NULL; + clipGetHandle((OfxImageEffectHandle) image_effect, + clip_name, + (OfxImageClipHandle *) &clip, + (OfxPropertySetHandle *) &clip_props); + if (!clip_props) + continue; + + char key[256] = {'\0'}; + char *str_value = NULL; + double dbl_value = 0.0; + + mltofx_make_clip_pref_key(key, sizeof(key), "OfxImageClipPropComponents_", clip_name); + if (propGetString((OfxPropertySetHandle) clip_props, + kOfxImageEffectPropComponents, + 0, + &str_value) + == kOfxStatOK + && str_value) { + propSetString((OfxPropertySetHandle) out_args, key, 0, str_value); + } + + mltofx_make_clip_pref_key(key, sizeof(key), "OfxImageClipPropDepth_", clip_name); + if (propGetString((OfxPropertySetHandle) clip_props, + kOfxImageEffectPropPixelDepth, + 0, + &str_value) + == kOfxStatOK + && str_value) { + propSetString((OfxPropertySetHandle) out_args, key, 0, str_value); + } + + mltofx_make_clip_pref_key(key, sizeof(key), "OfxImageClipPropPAR_", clip_name); + if (propGetDouble((OfxPropertySetHandle) clip_props, + kOfxImagePropPixelAspectRatio, + 0, + &dbl_value) + == kOfxStatOK) { + propSetDouble((OfxPropertySetHandle) out_args, key, 0, dbl_value); + } + } + + mlt_properties output_clip = NULL; + mlt_properties output_clip_props = NULL; + clipGetHandle((OfxImageEffectHandle) image_effect, + "Output", + (OfxImageClipHandle *) &output_clip, + (OfxPropertySetHandle *) &output_clip_props); + + if (output_clip_props) { + int int_value = 0; + + propSetString((OfxPropertySetHandle) out_args, + kOfxImageEffectPropPreMultiplication, + 0, + kOfxImageUnPreMultiplied); + + char *str_value = NULL; + if (propGetString((OfxPropertySetHandle) output_clip_props, + kOfxImageClipPropFieldOrder, + 0, + &str_value) + == kOfxStatOK + && str_value) { + propSetString((OfxPropertySetHandle) out_args, + kOfxImageClipPropFieldOrder, + 0, + str_value); + } + if (propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImageClipPropContinuousSamples, + 0, + &int_value) + == kOfxStatOK) { + propSetInt((OfxPropertySetHandle) out_args, + kOfxImageClipPropContinuousSamples, + 0, + int_value); + } else { + propSetInt((OfxPropertySetHandle) out_args, kOfxImageClipPropContinuousSamples, 0, 0); + } + } + + mlt_properties effect_props = mlt_properties_get_properties(image_effect, "props"); + if (effect_props) { + int frame_varying = 0; + if (propGetInt((OfxPropertySetHandle) effect_props, + kOfxImageEffectFrameVarying, + 0, + &frame_varying) + == kOfxStatOK) { + propSetInt((OfxPropertySetHandle) out_args, + kOfxImageEffectFrameVarying, + 0, + frame_varying); + } else { + propSetInt((OfxPropertySetHandle) out_args, kOfxImageEffectFrameVarying, 0, 0); + } + } +} + +static void mltofx_apply_clip_preferences(mlt_properties image_effect, mlt_properties pref_args) +{ + if (!image_effect || !pref_args) + return; + + mlt_properties output_clip = NULL; + mlt_properties output_clip_props = NULL; + clipGetHandle((OfxImageEffectHandle) image_effect, + "Output", + (OfxImageClipHandle *) &output_clip, + (OfxPropertySetHandle *) &output_clip_props); + + if (output_clip_props) { + int int_value = 0; + propSetString((OfxPropertySetHandle) output_clip_props, + kOfxImageEffectPropPreMultiplication, + 0, + kOfxImageUnPreMultiplied); + if (propGetInt((OfxPropertySetHandle) pref_args, + kOfxImageClipPropContinuousSamples, + 0, + &int_value) + == kOfxStatOK) { + propSetInt((OfxPropertySetHandle) output_clip_props, + kOfxImageClipPropContinuousSamples, + 0, + int_value); + } + } + + mlt_properties effect_props = mlt_properties_get_properties(image_effect, "props"); + if (effect_props) { + int int_value = 0; + if (propGetInt((OfxPropertySetHandle) pref_args, kOfxImageEffectFrameVarying, 0, &int_value) + == kOfxStatOK) { + propSetInt((OfxPropertySetHandle) effect_props, + kOfxImageEffectFrameVarying, + 0, + int_value); + } + } +} + +static void mltofx_apply_cached_clip_preferences(mlt_properties image_effect) +{ + if (!image_effect) + return; + mlt_properties pref_args = mlt_properties_get_properties(image_effect, "get_clippref_args"); + mltofx_apply_clip_preferences(image_effect, pref_args); +} + const void *MltOfxfetchSuite(OfxPropertySetHandle host, const char *suiteName, int suiteVersion) { mlt_log_debug(NULL, "[openfx] fetchSuite: `%s` v%d\n", suiteName, suiteVersion); @@ -1773,6 +2212,58 @@ void mltofx_destroy_instance(OfxPlugin *plugin, mlt_properties image_effect) mltofx_log_status_code(status_code, kOfxActionDestroyInstance); } +void mltofx_set_render_scale(mlt_properties image_effect, double scale_x, double scale_y) +{ + if (!image_effect) + return; + if (scale_x <= 0.0) + scale_x = 1.0; + if (scale_y <= 0.0) + scale_y = 1.0; + mlt_properties_set_double(image_effect, "_render_scale_x", scale_x); + mlt_properties_set_double(image_effect, "_render_scale_y", scale_y); +} + +static void mltofx_get_render_scale(mlt_properties image_effect, double *scale_x, double *scale_y) +{ + if (scale_x) + *scale_x = 1.0; + if (scale_y) + *scale_y = 1.0; + if (!image_effect) + return; + + double x = mlt_properties_get_double(image_effect, "_render_scale_x"); + double y = mlt_properties_get_double(image_effect, "_render_scale_y"); + if (x > 0.0 && scale_x) + *scale_x = x; + if (y > 0.0 && scale_y) + *scale_y = y; +} + +int mltofx_allows_frame_threading(mlt_properties image_effect) +{ + if (!image_effect) + return 0; + + mlt_properties props = mlt_properties_get_properties(image_effect, "props"); + if (!props) + return 0; + + char *thread_safety = NULL; + if (propGetString((OfxPropertySetHandle) props, + kOfxImageEffectPluginRenderThreadSafety, + 0, + &thread_safety) + != kOfxStatOK + || !thread_safety) { + // OFX default is instance-safe when not provided. + return 1; + } + + return strcmp(thread_safety, kOfxImageEffectRenderUnsafe) != 0; +} + void mltofx_set_source_clip_data(OfxPlugin *plugin, mlt_properties image_effect, uint8_t *image, @@ -1784,6 +2275,9 @@ void mltofx_set_source_clip_data(OfxPlugin *plugin, { mlt_properties clip; mlt_properties clip_prop; + double render_scale_x = 1.0; + double render_scale_y = 1.0; + mltofx_get_render_scale(image_effect, &render_scale_x, &render_scale_y); clipGetHandle((OfxImageEffectHandle) image_effect, "Source", @@ -1898,10 +2392,19 @@ void mltofx_set_source_clip_data(OfxPlugin *plugin, propSetString((OfxPropertySetHandle) clip_prop, kOfxImagePropUniqueIdentifier, 0, tstr); free(tstr); - propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropRenderScale, 0, 1.0); - propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropRenderScale, 1, 1.0); + propSetDouble((OfxPropertySetHandle) clip_prop, + kOfxImageEffectPropRenderScale, + 0, + render_scale_x); + propSetDouble((OfxPropertySetHandle) clip_prop, + kOfxImageEffectPropRenderScale, + 1, + render_scale_y); propSetInt((OfxPropertySetHandle) clip_prop, kOfxImageClipPropConnected, 0, 1); + + // Preserve plugin-selected clip preferences across clip data refreshes. + mltofx_apply_cached_clip_preferences(image_effect); } void mltofx_set_output_clip_data(OfxPlugin *plugin, @@ -1915,6 +2418,9 @@ void mltofx_set_output_clip_data(OfxPlugin *plugin, { mlt_properties clip; mlt_properties clip_prop; + double render_scale_x = 1.0; + double render_scale_y = 1.0; + mltofx_get_render_scale(image_effect, &render_scale_x, &render_scale_y); clipGetHandle((OfxImageEffectHandle) image_effect, "Output", @@ -2015,13 +2521,22 @@ void mltofx_set_output_clip_data(OfxPlugin *plugin, propSetString((OfxPropertySetHandle) clip_prop, kOfxImagePropUniqueIdentifier, 0, tstr); free(tstr); - propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropRenderScale, 0, 1.0); - propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImageEffectPropRenderScale, 1, 1.0); + propSetDouble((OfxPropertySetHandle) clip_prop, + kOfxImageEffectPropRenderScale, + 0, + render_scale_x); + propSetDouble((OfxPropertySetHandle) clip_prop, + kOfxImageEffectPropRenderScale, + 1, + render_scale_y); propSetDouble((OfxPropertySetHandle) clip_prop, kOfxImagePropPixelAspectRatio, 0, pixel_aspect_ratio); + + // Preserve plugin-selected clip preferences across clip data refreshes. + mltofx_apply_cached_clip_preferences(image_effect); } int mltofx_detect_plugin(OfxPlugin *plugin) @@ -2130,10 +2645,8 @@ int mltofx_detect_plugin(OfxPlugin *plugin) static int param_type_is_supported(const char *type) { - return strcmp(type, kOfxParamTypeInteger2D) && strcmp(type, kOfxParamTypeDouble3D) - && strcmp(type, kOfxParamTypeInteger3D) && strcmp(type, kOfxParamTypeCustom) - && strcmp(type, kOfxParamTypeBytes) && strcmp(type, kOfxParamTypePage) - && strcmp(type, kOfxParamTypePushButton); + return strcmp(type, kOfxParamTypeCustom) && strcmp(type, kOfxParamTypeBytes) + && strcmp(type, kOfxParamTypePage) && strcmp(type, kOfxParamTypePushButton); } void *mltofx_fetch_params(OfxPlugin *plugin, mlt_properties params, mlt_properties mlt_metadata) @@ -2324,6 +2837,9 @@ void *mltofx_fetch_params(OfxPlugin *plugin, mlt_properties params, mlt_properti } else if (strcmp(coordinate_system, kOfxParamCoordinatesNormalised) == 0) { mlt_properties_set(p, "normalized_coordinates", "yes"); } + } else if (strcmp(param_type, kOfxParamTypeInteger2D) == 0) { + // can be rendered as 2 integer number input fields + mlt_properties_set(p, "type", "integer"); } if (strcmp(param_type, kOfxParamTypeGroup) != 0) { @@ -2342,11 +2858,29 @@ void *mltofx_fetch_params(OfxPlugin *plugin, mlt_properties params, mlt_properti char *p_name = mlt_properties_get_name(dim1, jt); if (strcmp(p_name, kOfxParamPropDefault) == 0) { if (strcmp(param_type, kOfxParamTypeInteger) == 0 - || strcmp(param_type, kOfxParamTypeChoice) == 0 || strcmp(param_type, kOfxParamTypeBoolean) == 0) { int default_value = 0; propGetInt((OfxPropertySetHandle) ppp, p_name, 0, &default_value); mlt_properties_set_int(p, "default", default_value); + } else if (strcmp(param_type, kOfxParamTypeChoice) == 0) { + int default_value = 0; + int choice_count = 0; + char *choice_str = NULL; + + propGetInt((OfxPropertySetHandle) ppp, p_name, 0, &default_value); + propGetDimension((OfxPropertySetHandle) ppp, + kOfxParamPropChoiceOption, + &choice_count); + + if (default_value >= 0 && default_value < choice_count + && propGetString((OfxPropertySetHandle) ppp, + kOfxParamPropChoiceOption, + default_value, + &choice_str) + == kOfxStatOK + && choice_str) { + mlt_properties_set(p, "default", choice_str); + } } else if (strcmp(param_type, kOfxParamTypeDouble) == 0) { double default_value = 0.0; propGetDouble((OfxPropertySetHandle) ppp, p_name, 0, &default_value); @@ -2538,6 +3072,13 @@ void mltofx_param_set_value(mlt_properties params, char *key, mltofx_property_ty propSetDouble((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, value.y); } break; + case mltofx_prop_int2d: { + int x = va_arg(ap, int); + int y = va_arg(ap, int); + propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 0, x); + propSetInt((OfxPropertySetHandle) param_props, "MltOfxParamValue", 1, y); + } break; + default: break; } @@ -2549,30 +3090,160 @@ void mltofx_get_clip_preferences(OfxPlugin *plugin, mlt_properties image_effect) { mlt_properties get_clippref_args = mlt_properties_get_properties(image_effect, "get_clippref_args"); + + mltofx_prime_clip_preferences(image_effect, get_clippref_args); + OfxStatus status_code = plugin->mainEntry(kOfxImageEffectActionGetClipPreferences, (OfxImageEffectHandle) image_effect, NULL, (OfxPropertySetHandle) get_clippref_args); mltofx_log_status_code(status_code, kOfxImageEffectActionGetClipPreferences); + + if (status_code == kOfxStatOK || status_code == kOfxStatReplyDefault) + mltofx_apply_clip_preferences(image_effect, get_clippref_args); } void mltofx_get_region_of_definition(OfxPlugin *plugin, mlt_properties image_effect, double ofx_time) { mlt_properties get_rod_in_args = mlt_properties_get_properties(image_effect, "get_rod_in_args"); + mlt_properties get_rod_out_args = mlt_properties_get_properties(image_effect, + "get_rod_out_args"); + + mlt_properties output_clip = NULL; + mlt_properties output_clip_props = NULL; + double render_scale_x = 1.0; + double render_scale_y = 1.0; + mltofx_get_render_scale(image_effect, &render_scale_x, &render_scale_y); + clipGetHandle((OfxImageEffectHandle) image_effect, + "Output", + (OfxImageClipHandle *) &output_clip, + (OfxPropertySetHandle *) &output_clip_props); + + int default_x1 = 0; + int default_y1 = 0; + int default_x2 = 0; + int default_y2 = 0; + if (output_clip_props) { + // Use current output RoD as default outArgs as required by OFX. + if (propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropRegionOfDefinition, + 0, + &default_x1) + != kOfxStatOK + || propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropRegionOfDefinition, + 1, + &default_y1) + != kOfxStatOK + || propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropRegionOfDefinition, + 2, + &default_x2) + != kOfxStatOK + || propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropRegionOfDefinition, + 3, + &default_y2) + != kOfxStatOK) { + propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropBounds, + 0, + &default_x1); + propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropBounds, + 1, + &default_y1); + propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropBounds, + 2, + &default_x2); + propGetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropBounds, + 3, + &default_y2); + } + } propSetDouble((OfxPropertySetHandle) get_rod_in_args, kOfxPropTime, 0, ofx_time); - propSetDouble((OfxPropertySetHandle) get_rod_in_args, kOfxImageEffectPropRenderScale, 0, 1.0); - propSetDouble((OfxPropertySetHandle) get_rod_in_args, kOfxImageEffectPropRenderScale, 1, 1.0); + propSetDouble((OfxPropertySetHandle) get_rod_in_args, + kOfxImageEffectPropRenderScale, + 0, + render_scale_x); + propSetDouble((OfxPropertySetHandle) get_rod_in_args, + kOfxImageEffectPropRenderScale, + 1, + render_scale_y); - mlt_properties get_rod_out_args = mlt_properties_get_properties(image_effect, - "get_rod_out_args"); + propSetDouble((OfxPropertySetHandle) get_rod_out_args, + kOfxImageEffectPropRegionOfDefinition, + 0, + (double) default_x1); + propSetDouble((OfxPropertySetHandle) get_rod_out_args, + kOfxImageEffectPropRegionOfDefinition, + 1, + (double) default_y1); + propSetDouble((OfxPropertySetHandle) get_rod_out_args, + kOfxImageEffectPropRegionOfDefinition, + 2, + (double) default_x2); + propSetDouble((OfxPropertySetHandle) get_rod_out_args, + kOfxImageEffectPropRegionOfDefinition, + 3, + (double) default_y2); OfxStatus status_code = plugin->mainEntry(kOfxImageEffectActionGetRegionOfDefinition, (OfxImageEffectHandle) image_effect, (OfxPropertySetHandle) get_rod_in_args, (OfxPropertySetHandle) get_rod_out_args); + if (output_clip_props && (status_code == kOfxStatOK || status_code == kOfxStatReplyDefault)) { + double rod_x1 = (double) default_x1; + double rod_y1 = (double) default_y1; + double rod_x2 = (double) default_x2; + double rod_y2 = (double) default_y2; + propGetDouble((OfxPropertySetHandle) get_rod_out_args, + kOfxImageEffectPropRegionOfDefinition, + 0, + &rod_x1); + propGetDouble((OfxPropertySetHandle) get_rod_out_args, + kOfxImageEffectPropRegionOfDefinition, + 1, + &rod_y1); + propGetDouble((OfxPropertySetHandle) get_rod_out_args, + kOfxImageEffectPropRegionOfDefinition, + 2, + &rod_x2); + propGetDouble((OfxPropertySetHandle) get_rod_out_args, + kOfxImageEffectPropRegionOfDefinition, + 3, + &rod_y2); + + int out_x1 = (int) floor(rod_x1); + int out_y1 = (int) floor(rod_y1); + int out_x2 = (int) ceil(rod_x2); + int out_y2 = (int) ceil(rod_y2); + + if (out_x2 >= out_x1 && out_y2 >= out_y1) { + propSetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropRegionOfDefinition, + 0, + out_x1); + propSetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropRegionOfDefinition, + 1, + out_y1); + propSetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropRegionOfDefinition, + 2, + out_x2); + propSetInt((OfxPropertySetHandle) output_clip_props, + kOfxImagePropRegionOfDefinition, + 3, + out_y2); + } + } + mltofx_log_status_code(status_code, kOfxImageEffectActionGetRegionOfDefinition); } @@ -2582,11 +3253,20 @@ void mltofx_get_regions_of_interest( mlt_properties get_roi_in_args = mlt_properties_get_properties(image_effect, "get_roi_in_args"); mlt_properties get_roi_out_args = mlt_properties_get_properties(image_effect, "get_roi_out_args"); + double render_scale_x = 1.0; + double render_scale_y = 1.0; + mltofx_get_render_scale(image_effect, &render_scale_x, &render_scale_y); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxPropTime, 0, ofx_time); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxPropTime, 1, ofx_time); - propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRenderScale, 0, 1.0); - propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRenderScale, 1, 1.0); + propSetDouble((OfxPropertySetHandle) get_roi_in_args, + kOfxImageEffectPropRenderScale, + 0, + render_scale_x); + propSetDouble((OfxPropertySetHandle) get_roi_in_args, + kOfxImageEffectPropRenderScale, + 1, + render_scale_y); propSetDouble((OfxPropertySetHandle) get_roi_in_args, kOfxImageEffectPropRegionOfInterest, @@ -2616,6 +3296,9 @@ void mltofx_begin_sequence_render(OfxPlugin *plugin, mlt_properties image_effect { mlt_properties begin_sequence_props = mlt_properties_get_properties(image_effect, "begin_sequence_props"); + double render_scale_x = 1.0; + double render_scale_y = 1.0; + mltofx_get_render_scale(image_effect, &render_scale_x, &render_scale_y); propSetDouble((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropFrameRange, 0, @@ -2631,11 +3314,11 @@ void mltofx_begin_sequence_render(OfxPlugin *plugin, mlt_properties image_effect propSetDouble((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropRenderScale, 0, - 1.0); + render_scale_x); propSetDouble((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropRenderScale, 1, - 1.0); + render_scale_y); propSetInt((OfxPropertySetHandle) begin_sequence_props, kOfxImageEffectPropSequentialRenderStatus, @@ -2660,6 +3343,9 @@ void mltofx_end_sequence_render(OfxPlugin *plugin, mlt_properties image_effect, { mlt_properties end_sequence_props = mlt_properties_get_properties(image_effect, "end_sequence_props"); + double render_scale_x = 1.0; + double render_scale_y = 1.0; + mltofx_get_render_scale(image_effect, &render_scale_x, &render_scale_y); propSetDouble((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropFrameRange, 0, @@ -2674,8 +3360,14 @@ void mltofx_end_sequence_render(OfxPlugin *plugin, mlt_properties image_effect, propSetInt((OfxPropertySetHandle) end_sequence_props, kOfxPropIsInteractive, 0, 0); - propSetDouble((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropRenderScale, 0, 1.0); - propSetDouble((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropRenderScale, 1, 1.0); + propSetDouble((OfxPropertySetHandle) end_sequence_props, + kOfxImageEffectPropRenderScale, + 0, + render_scale_x); + propSetDouble((OfxPropertySetHandle) end_sequence_props, + kOfxImageEffectPropRenderScale, + 1, + render_scale_y); propSetInt((OfxPropertySetHandle) end_sequence_props, kOfxImageEffectPropSequentialRenderStatus, @@ -2700,6 +3392,9 @@ void mltofx_action_render( OfxPlugin *plugin, mlt_properties image_effect, double ofx_time, int width, int height) { mlt_properties render_in_args = mlt_properties_get_properties(image_effect, "render_in_args"); + double render_scale_x = 1.0; + double render_scale_y = 1.0; + mltofx_get_render_scale(image_effect, &render_scale_x, &render_scale_y); propSetDouble((OfxPropertySetHandle) render_in_args, kOfxPropTime, 0, ofx_time); propSetString((OfxPropertySetHandle) render_in_args, @@ -2712,8 +3407,14 @@ void mltofx_action_render( propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderWindow, 2, width); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderWindow, 3, height); - propSetDouble((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderScale, 0, 1.0); - propSetDouble((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropRenderScale, 1, 1.0); + propSetDouble((OfxPropertySetHandle) render_in_args, + kOfxImageEffectPropRenderScale, + 0, + render_scale_x); + propSetDouble((OfxPropertySetHandle) render_in_args, + kOfxImageEffectPropRenderScale, + 1, + render_scale_y); propSetInt((OfxPropertySetHandle) render_in_args, kOfxImageEffectPropSequentialRenderStatus, diff --git a/src/modules/openfx/mlt_openfx.h b/src/modules/openfx/mlt_openfx.h index 357676749..ffe6db141 100644 --- a/src/modules/openfx/mlt_openfx.h +++ b/src/modules/openfx/mlt_openfx.h @@ -40,6 +40,7 @@ typedef enum { mltofx_prop_pointer = 16, mltofx_prop_color = 32, mltofx_prop_double2d = 64, + mltofx_prop_int2d = 128, } mltofx_property_type; typedef enum { @@ -86,6 +87,10 @@ void *mltofx_fetch_params(OfxPlugin *plugin, mlt_properties params, mlt_properti void mltofx_param_set_value(mlt_properties params, char *key, mltofx_property_type type, ...); +void mltofx_set_render_scale(mlt_properties image_effect, double scale_x, double scale_y); + +int mltofx_allows_frame_threading(mlt_properties image_effect); + void mltofx_get_clip_preferences(OfxPlugin *plugin, mlt_properties image_effect); void mltofx_get_region_of_definition(OfxPlugin *plugin,