/*** Oak Video Editor - Non-Linear Video Editor Copyright (C) 2026 Oak Team This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ***/ #ifndef OAK_NODE_C_API_VALUECONVERT_H #define OAK_NODE_C_API_VALUECONVERT_H // Internal helpers shared by the oaknode c_api translation units: // oaknode_value <-> olive::Variant mapping, the pinned // oaknode_value_type <-> olive::NodeValue::Type mapping, two-stage string // copy, OakUndoCommand wrapping and the debug alive counter. #include "node/node.h" #include #include #include #include "value.h" // Internal layout of the OakUndoCommand handle, shared with the oakundo // module (src/undo/c_api/commandhandle.h). Included so undoable variants // can hand out owned handles wrapping freshly created olive commands. #include "../../undo/c_api/commandhandle.h" namespace oaknode_c_api { /** * @brief Bump/release the debug alive counter (defined in node.cpp). */ void alive_inc(); void alive_dec(); /** * @brief Shared two-stage string getter. * * Returns the required buffer size in bytes (including the terminating * NUL) as a non-negative value. */ inline int copy_string(const std::string &value, char *buf, int buf_size) { int required = static_cast(value.size()) + 1; if (buf && buf_size > 0) { size_t copy_len = value.size(); if (copy_len > static_cast(buf_size) - 1) { copy_len = static_cast(buf_size) - 1; } memcpy(buf, value.data(), copy_len); buf[copy_len] = '\0'; } return required; } /** * @brief Pinned mapping olive::NodeValue::Type -> oaknode_value_type * (see the table on oaknode_value_type in node/node.h). Types without a * POD representation map to OAKNODE_VALUE_NONE. */ inline int value_type_to_oak(olive::NodeValue::Type type) { switch (type) { case olive::NodeValue::k_int: return OAKNODE_VALUE_INT; case olive::NodeValue::k_float: return OAKNODE_VALUE_FLOAT; case olive::NodeValue::k_boolean: return OAKNODE_VALUE_BOOL; case olive::NodeValue::k_rational: return OAKNODE_VALUE_RATIONAL; case olive::NodeValue::k_color: return OAKNODE_VALUE_COLOR; case olive::NodeValue::k_vec2: return OAKNODE_VALUE_VEC2; case olive::NodeValue::k_vec3: return OAKNODE_VALUE_VEC3; case olive::NodeValue::k_vec4: return OAKNODE_VALUE_VEC4; case olive::NodeValue::k_combo: return OAKNODE_VALUE_COMBO; case olive::NodeValue::k_file: case olive::NodeValue::k_text: case olive::NodeValue::k_font: case olive::NodeValue::k_str_combo: return OAKNODE_VALUE_STRING; default: return OAKNODE_VALUE_NONE; } } /** * @brief 1 if the olive type is string-carried (no POD representation, * handled by the dedicated string functions). */ inline bool value_type_is_string(olive::NodeValue::Type type) { return type == olive::NodeValue::k_file || type == olive::NodeValue::k_text || type == olive::NodeValue::k_font || type == olive::NodeValue::k_str_combo; } /** * @brief Build an olive::Variant from an oaknode_value POD. * * `value->type` must be one of the POD-carrying oaknode_value_type * values (STRING is rejected: no string data fits the POD). */ inline bool variant_from_value(const oaknode_value *value, olive::Variant *out) { using olive::core::Color; using olive::core::Rational; using olive::Vector2D; using olive::Vector3D; using olive::Vector4D; switch (value->type) { case OAKNODE_VALUE_INT: case OAKNODE_VALUE_COMBO: *out = olive::Variant(value->num); return true; case OAKNODE_VALUE_FLOAT: *out = olive::Variant(value->f[0]); return true; case OAKNODE_VALUE_BOOL: *out = olive::Variant(value->num != 0); return true; case OAKNODE_VALUE_RATIONAL: *out = olive::Variant::from_value( Rational(static_cast(value->num), static_cast(value->den))); return true; case OAKNODE_VALUE_COLOR: *out = olive::Variant::from_value( Color(static_cast(value->f[0]), static_cast(value->f[1]), static_cast(value->f[2]), static_cast(value->f[3]))); return true; case OAKNODE_VALUE_VEC2: *out = olive::Variant::from_value(Vector2D(static_cast(value->f[0]), static_cast(value->f[1]))); return true; case OAKNODE_VALUE_VEC3: *out = olive::Variant::from_value(Vector3D(static_cast(value->f[0]), static_cast(value->f[1]), static_cast(value->f[2]))); return true; case OAKNODE_VALUE_VEC4: *out = olive::Variant::from_value(Vector4D(static_cast(value->f[0]), static_cast(value->f[1]), static_cast(value->f[2]), static_cast(value->f[3]))); return true; default: return false; } } /** * @brief Map an olive::Variant of declared type `type` into an * oaknode_value POD. * * Returns OAKNODE_OK, OAKNODE_E_INVALID for string-family types (use the * string getters), or OAKNODE_E_FAILED for types without a POD * representation. */ inline int value_from_variant(olive::NodeValue::Type type, const olive::Variant &v, oaknode_value *out) { using olive::core::Color; using olive::core::Rational; using olive::Vector2D; using olive::Vector3D; using olive::Vector4D; if (value_type_is_string(type)) { return OAKNODE_E_INVALID; } *out = oaknode_value(); out->type = value_type_to_oak(type); switch (type) { case olive::NodeValue::k_none: return OAKNODE_OK; case olive::NodeValue::k_int: case olive::NodeValue::k_combo: out->num = v.to_long_long(); return OAKNODE_OK; case olive::NodeValue::k_float: out->f[0] = v.to_double(); return OAKNODE_OK; case olive::NodeValue::k_boolean: out->num = v.to_bool() ? 1 : 0; return OAKNODE_OK; case olive::NodeValue::k_rational: { Rational r = v.value(); out->num = r.numerator(); out->den = r.denominator(); return OAKNODE_OK; } case olive::NodeValue::k_color: { Color c = v.value(); out->f[0] = c.red(); out->f[1] = c.green(); out->f[2] = c.blue(); out->f[3] = c.alpha(); return OAKNODE_OK; } case olive::NodeValue::k_vec2: { Vector2D vec = v.value(); out->f[0] = vec.x(); out->f[1] = vec.y(); return OAKNODE_OK; } case olive::NodeValue::k_vec3: { Vector3D vec = v.value(); out->f[0] = vec.x(); out->f[1] = vec.y(); out->f[2] = vec.z(); return OAKNODE_OK; } case olive::NodeValue::k_vec4: { Vector4D vec = v.value(); out->f[0] = vec.x(); out->f[1] = vec.y(); out->f[2] = vec.z(); out->f[3] = vec.w(); return OAKNODE_OK; } default: out->type = OAKNODE_VALUE_NONE; return OAKNODE_E_FAILED; } } /** * @brief Map an oaknode_value POD carrying ONE per-track component into * an olive::Variant of the input's declared type (the dragger's drag * path; mirrors the facade's component_from_c helper). * * For scalar types (INT/COMBO/FLOAT/BOOLEAN/RATIONAL) the whole POD is * the component; for split-track types (COLOR/VEC2/3/4) `component` * selects which component's value is carried in f[0] (the facade passes * 0 -- the POD's f[0] holds the dragged component). The POD type must * match the declared type. */ inline bool component_from_value(const oaknode_value *value, olive::NodeValue::Type declared, int component, olive::Variant *out) { using olive::core::Rational; switch (declared) { case olive::NodeValue::k_int: case olive::NodeValue::k_combo: if (value->type != OAKNODE_VALUE_INT && value->type != OAKNODE_VALUE_COMBO) { return false; } *out = olive::Variant(value->num); return true; case olive::NodeValue::k_float: case olive::NodeValue::k_bezier: if (value->type != OAKNODE_VALUE_FLOAT) { return false; } *out = olive::Variant(value->f[0]); return true; case olive::NodeValue::k_boolean: if (value->type != OAKNODE_VALUE_BOOL) { return false; } *out = olive::Variant(value->num != 0); return true; case olive::NodeValue::k_rational: if (value->type != OAKNODE_VALUE_RATIONAL) { return false; } *out = olive::Variant::from_value( Rational(static_cast(value->num), static_cast(value->den))); return true; case olive::NodeValue::k_color: case olive::NodeValue::k_vec2: case olive::NodeValue::k_vec3: case olive::NodeValue::k_vec4: if (value->type != value_type_to_oak(declared)) { return false; } *out = olive::Variant(value->f[component]); return true; default: return false; } } /** * @brief Wrap a freshly created olive::UndoCommand in an owned * OakUndoCommand handle (reference count 1). Returns an empty handle * (ctx == NULL) on allocation failure. */ inline OakUndoCommand wrap_command(olive::UndoCommand *command) { if (!command) { return OakUndoCommand{}; } return oakundo_capi::make_command_handle(command, true); } } #endif // OAK_NODE_C_API_VALUECONVERT_H