From ceffb3558efafb706af50c04b4b150250e2963f0 Mon Sep 17 00:00:00 2001 From: Helge Norberg Date: Tue, 7 Mar 2017 10:11:57 +0100 Subject: [PATCH] [scene] Added missing mixer features --- core/producer/scene/scene.xsd | 35 +++++++++ core/producer/scene/scene_producer.cpp | 89 ++++++++++++++++------ core/producer/scene/scene_producer.h | 17 +++++ core/producer/scene/xml_scene_producer.cpp | 13 ++++ 4 files changed, 129 insertions(+), 25 deletions(-) diff --git a/core/producer/scene/scene.xsd b/core/producer/scene/scene.xsd index 269c78804..47844ca69 100644 --- a/core/producer/scene/scene.xsd +++ b/core/producer/scene/scene.xsd @@ -67,6 +67,21 @@ The Y coordinate of the layer in the scene coordinate system. Can be an expression. The width of the layer. Is by default calculated by the producer itself but can be overridden. Can be an expression. The height of the layer. Is by default calculated by the producer itself but can be overridden. Can be an expression. + + + + Properties regarding viewport clipping. The coordinates are relative to the scene coordinate system. They are addressable from expressions as layer.[id].clip.[property]. + + + + + The X position within the scene coordinate system to clip the upper left corner. Can be an expression. + The Y position within the scene coordinate system to clip the upper left corner. Can be an expression. + The X position within the scene coordinate system to clip the lower right corner. Can be an expression. + The Y position within the scene coordinate system to clip the lower right corner. Can be an expression. + + + The X anchor within the layer coordinate system where position and rotation are done relative to/around. Can be an expression. The Y anchor within the layer coordinate system where position and rotation are done relative to/around. Can be an expression. The rotation of the layer around anchor_x/anchor_y expressed in degrees. Can be an expression. @@ -91,6 +106,25 @@ The opacity of the layer. Can be an expression. + The contrast of the layer. Can be an expression. + The saturation of the layer. Can be an expression. + The brightness of the layer. Can be an expression. + + + + + + + Properties regarding level adjustments. They are addressable from expressions as layer.[id].level.[property]. + + + + + The minimum input level of the layer. Can be an expression. + The maximum input level of the layer. Can be an expression. + The gamma correction of the layer. Can be an expression. + The minimum output level of the layer. Can be an expression. + The maximum output level of the layer. Can be an expression. @@ -111,6 +145,7 @@ + The audio volume of the layer. Can be an expression. diff --git a/core/producer/scene/scene_producer.cpp b/core/producer/scene/scene_producer.cpp index c2ddc3e88..b6339e85a 100644 --- a/core/producer/scene/scene_producer.cpp +++ b/core/producer/scene/scene_producer.cpp @@ -36,7 +36,9 @@ namespace caspar { namespace core { namespace scene { layer::layer(const std::wstring& name, const spl::shared_ptr& producer) - : name(name), producer(producer) + : name(name) + , producer(producer) + , volume(1.0) { crop.lower_right.x.bind(producer.get()->pixel_constraints().width); crop.lower_right.y.bind(producer.get()->pixel_constraints().height); @@ -48,6 +50,18 @@ layer::layer(const std::wstring& name, const spl::shared_ptr& pr adjustments::adjustments() : opacity(1.0) + , contrast(1.0) + , saturation(1.0) + , brightness(1.0) +{ +} + +levels::levels() + : min_input(0.0) + , max_input(1.0) + , gamma(1.0) + , min_output(0.0) + , max_output(1.0) { } @@ -131,16 +145,16 @@ struct scene_producer::impl mutable tbb::atomic m_y_; binding mouse_x_; binding mouse_y_; - double frame_fraction_ = 0.0; + double frame_fraction_ = 0.0; std::map timelines_; std::map> variables_; std::vector variable_names_; std::multimap markers_by_frame_; std::vector> task_subscriptions_; monitor::subject monitor_subject_; - bool paused_ = true; - bool removed_ = false; - bool going_to_mark_ = false; + bool paused_ = true; + bool removed_ = false; + bool going_to_mark_ = false; impl( std::wstring producer_name, @@ -193,6 +207,8 @@ struct scene_producer::impl layer.position.x.set(x); layer.position.y.set(y); + layer.clip.lower_right.x.bind(pixel_constraints_.width); + layer.clip.lower_right.y.bind(pixel_constraints_.height); layers_.push_back(layer); @@ -277,37 +293,58 @@ struct scene_producer::impl auto& anchor = transform.image_transform.anchor; auto& pos = transform.image_transform.fill_translation; auto& scale = transform.image_transform.fill_scale; + auto& clip_pos = transform.image_transform.clip_translation; + auto& clip_scale = transform.image_transform.clip_scale; auto& angle = transform.image_transform.angle; auto& crop = transform.image_transform.crop; auto& pers = transform.image_transform.perspective; - - anchor[0] = layer.anchor.x.get() / layer.producer.get()->pixel_constraints().width.get(); - anchor[1] = layer.anchor.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pos[0] = layer.position.x.get() / pixel_constraints_.width.get(); - pos[1] = layer.position.y.get() / pixel_constraints_.height.get(); - scale[0] = layer.producer.get()->pixel_constraints().width.get() / pixel_constraints_.width.get(); - scale[1] = layer.producer.get()->pixel_constraints().height.get() / pixel_constraints_.height.get(); - crop.ul[0] = layer.crop.upper_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); - crop.ul[1] = layer.crop.upper_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); - crop.lr[0] = layer.crop.lower_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); - crop.lr[1] = layer.crop.lower_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pers.ul[0] = layer.perspective.upper_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); - pers.ul[1] = layer.perspective.upper_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pers.ur[0] = layer.perspective.upper_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); - pers.ur[1] = layer.perspective.upper_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pers.lr[0] = layer.perspective.lower_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); - pers.lr[1] = layer.perspective.lower_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); - pers.ll[0] = layer.perspective.lower_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); - pers.ll[1] = layer.perspective.lower_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); + auto& levels = transform.image_transform.levels; + + anchor[0] = layer.anchor.x.get() / layer.producer.get()->pixel_constraints().width.get(); + anchor[1] = layer.anchor.y.get() / layer.producer.get()->pixel_constraints().height.get(); + + pos[0] = layer.position.x.get() / pixel_constraints_.width.get(); + pos[1] = layer.position.y.get() / pixel_constraints_.height.get(); + scale[0] = layer.producer.get()->pixel_constraints().width.get() / pixel_constraints_.width.get(); + scale[1] = layer.producer.get()->pixel_constraints().height.get() / pixel_constraints_.height.get(); + + clip_pos[0] = layer.clip.upper_left.x.get() / pixel_constraints_.width.get(); + clip_pos[1] = layer.clip.upper_left.y.get() / pixel_constraints_.height.get(); + clip_scale[0] = layer.clip.lower_right.x.get() / pixel_constraints_.width.get() - clip_pos[0]; + clip_scale[1] = layer.clip.lower_right.y.get() / pixel_constraints_.height.get() - clip_pos[1]; + + crop.ul[0] = layer.crop.upper_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); + crop.ul[1] = layer.crop.upper_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); + crop.lr[0] = layer.crop.lower_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); + crop.lr[1] = layer.crop.lower_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); + + pers.ul[0] = layer.perspective.upper_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); + pers.ul[1] = layer.perspective.upper_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); + pers.ur[0] = layer.perspective.upper_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); + pers.ur[1] = layer.perspective.upper_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); + pers.lr[0] = layer.perspective.lower_right.x.get() / layer.producer.get()->pixel_constraints().width.get(); + pers.lr[1] = layer.perspective.lower_right.y.get() / layer.producer.get()->pixel_constraints().height.get(); + pers.ll[0] = layer.perspective.lower_left.x.get() / layer.producer.get()->pixel_constraints().width.get(); + pers.ll[1] = layer.perspective.lower_left.y.get() / layer.producer.get()->pixel_constraints().height.get(); static const double PI = 3.141592653589793; - angle = layer.rotation.get() * PI / 180.0; + angle = layer.rotation.get() * PI / 180.0; + + levels.min_input = layer.levels.min_input.get(); + levels.max_input = layer.levels.max_input.get(); + levels.gamma = layer.levels.gamma.get(); + levels.min_output = layer.levels.min_output.get(); + levels.max_output = layer.levels.max_output.get(); transform.image_transform.opacity = layer.adjustments.opacity.get(); + transform.image_transform.contrast = layer.adjustments.contrast.get(); + transform.image_transform.saturation = layer.adjustments.saturation.get(); + transform.image_transform.brightness = layer.adjustments.brightness.get(); transform.image_transform.is_key = layer.is_key.get(); transform.image_transform.use_mipmap = layer.use_mipmap.get(); transform.image_transform.blend_mode = layer.blend_mode.get(); + transform.image_transform.chroma.enable = layer.chroma_key.enable.get(); transform.image_transform.chroma.target_hue = layer.chroma_key.target_hue.get(); transform.image_transform.chroma.hue_width = layer.chroma_key.hue_width.get(); @@ -317,6 +354,8 @@ struct scene_producer::impl transform.image_transform.chroma.spill_suppress = layer.chroma_key.spill_suppress.get(); transform.image_transform.chroma.spill_suppress_saturation = layer.chroma_key.spill_suppress_saturation.get(); + transform.audio_transform.volume = layer.volume.get(); + // Mark as sublayer, so it will be composited separately by the mixer. transform.image_transform.layer_depth = 1; diff --git a/core/producer/scene/scene_producer.h b/core/producer/scene/scene_producer.h index c954f12cc..08b2e7fb6 100644 --- a/core/producer/scene/scene_producer.h +++ b/core/producer/scene/scene_producer.h @@ -54,10 +54,24 @@ struct corners struct adjustments { binding opacity; + binding contrast; + binding saturation; + binding brightness; adjustments(); }; +struct levels +{ + binding min_input; + binding max_input; + binding gamma; + binding min_output; + binding max_output; + + levels(); +}; + struct chroma_key { binding enable; @@ -77,14 +91,17 @@ struct layer scene::coord position; scene::rect crop; scene::corners perspective; + scene::rect clip; binding rotation; scene::adjustments adjustments; + scene::levels levels; binding> producer; binding hidden; binding is_key; binding use_mipmap; binding blend_mode; scene::chroma_key chroma_key; + binding volume; explicit layer(const std::wstring& name, const spl::shared_ptr& producer); }; diff --git a/core/producer/scene/xml_scene_producer.cpp b/core/producer/scene/xml_scene_producer.cpp index 089e036f8..a13a2b624 100644 --- a/core/producer/scene/xml_scene_producer.cpp +++ b/core/producer/scene/xml_scene_producer.cpp @@ -166,6 +166,10 @@ spl::shared_ptr create_xml_scene_producer( layer.position.y = scene->create_variable(variable_prefix + L"y", false, ptree_get(elem.second, L"y")); layer.anchor.x = scene->create_variable(variable_prefix + L"anchor_x", false, elem.second.get(L"anchor_x", L"0.0")); layer.anchor.y = scene->create_variable(variable_prefix + L"anchor_y", false, elem.second.get(L"anchor_y", L"0.0")); + layer.clip.upper_left.x = scene->create_variable(variable_prefix + L"clip.upper_left_x", false, elem.second.get(L"clip.upper_left_x", L"0.0")); + layer.clip.upper_left.y = scene->create_variable(variable_prefix + L"clip.upper_left_y", false, elem.second.get(L"clip.upper_left_y", L"0.0")); + layer.clip.lower_right.x = scene->create_variable(variable_prefix + L"clip.lower_right_x", false, elem.second.get(L"clip.lower_right_x", L"${scene_width}")); + layer.clip.lower_right.y = scene->create_variable(variable_prefix + L"clip.lower_right_y", false, elem.second.get(L"clip.lower_right_y", L"${scene_height}")); layer.rotation = scene->create_variable(variable_prefix + L"rotation", false, elem.second.get(L"rotation", L"0.0")); layer.crop.upper_left.x = scene->create_variable(variable_prefix + L"crop_upper_left_x", false, elem.second.get(L"crop_upper_left_x", L"0.0")); layer.crop.upper_left.y = scene->create_variable(variable_prefix + L"crop_upper_left_y", false, elem.second.get(L"crop_upper_left_y", L"0.0")); @@ -181,6 +185,15 @@ spl::shared_ptr create_xml_scene_producer( layer.perspective.lower_left.y = scene->create_variable(variable_prefix + L"perspective_lower_left_y", false, elem.second.get(L"perspective_lower_left_y", L"${" + variable_prefix + L"height}")); layer.adjustments.opacity = scene->create_variable(variable_prefix + L"adjustment.opacity", false, elem.second.get(L"adjustments.opacity", L"1.0")); + layer.adjustments.contrast = scene->create_variable(variable_prefix + L"adjustment.contrast", false, elem.second.get(L"adjustments.contrast", L"1.0")); + layer.adjustments.saturation = scene->create_variable(variable_prefix + L"adjustment.saturation", false, elem.second.get(L"adjustments.saturation", L"1.0")); + layer.adjustments.brightness = scene->create_variable(variable_prefix + L"adjustment.brightness", false, elem.second.get(L"adjustments.brightness", L"1.0")); + layer.levels.min_input = scene->create_variable(variable_prefix + L"level.min_input", false, elem.second.get(L"levels.min_input", L"0.0")); + layer.levels.max_input = scene->create_variable(variable_prefix + L"level.max_input", false, elem.second.get(L"levels.max_input", L"1.0")); + layer.levels.gamma = scene->create_variable(variable_prefix + L"level.gamma", false, elem.second.get(L"levels.gamma", L"1.0")); + layer.levels.min_output = scene->create_variable(variable_prefix + L"level.min_output", false, elem.second.get(L"levels.min_output", L"0.0")); + layer.levels.max_output = scene->create_variable(variable_prefix + L"level.max_output", false, elem.second.get(L"levels.max_output", L"1.0")); + layer.volume = scene->create_variable(variable_prefix + L"volume", false, elem.second.get(L"volume", L"1.0")); layer.is_key = scene->create_variable(variable_prefix + L"is_key", false, elem.second.get(L"is_key", L"false")); layer.use_mipmap = scene->create_variable(variable_prefix + L"use_mipmap", false, elem.second.get(L"use_mipmap", L"false")); layer.blend_mode = scene->create_variable(variable_prefix + L"blend_mode", false, elem.second.get(L"blend_mode", L"normal")).transformed([](const std::wstring& b) { return get_blend_mode(b); }); -- 2.39.2