X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fpsd%2Fpsd_scene_producer.cpp;h=59e67b288448d1c155f1a4bc0176e6d200417f7d;hb=53ab502eaf89eed44c57d1edf4a86eb145bac43f;hp=66e55f346452faeb8e40d21d04d4665aff77bf0a;hpb=53e85c272ee4de4efb3d454441eaa53bbc0d4c26;p=casparcg diff --git a/modules/psd/psd_scene_producer.cpp b/modules/psd/psd_scene_producer.cpp index 66e55f346..59e67b288 100644 --- a/modules/psd/psd_scene_producer.cpp +++ b/modules/psd/psd_scene_producer.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -34,11 +35,13 @@ #include #include +#include #include #include #include #include +#include namespace caspar { namespace psd { @@ -52,6 +55,10 @@ core::text::text_info get_text_info(const boost::property_tree::wptree& ptree) core::text::text_info result; int font_index = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.Font", 0); result.size = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.FontSize", 30.0f); + //result.leading = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.Leading", 0); + result.tracking = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.Tracking", 0); + result.baseline_shift = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.BaselineShift", 0); + //result.kerning = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.Kerning", 0); int child_index = 0; auto color_node = ptree.get_child(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.FillColor.Values"); @@ -91,18 +98,22 @@ core::text::text_info get_text_info(const boost::property_tree::wptree& ptree) caspar::core::scene::layer* layer; int link_id; bool is_master; - }; + double adjustment_x; + double adjustment_y; + }; std::vector layers; std::vector masters; public: - void add(caspar::core::scene::layer* layer, int link_group, bool master) + void add(caspar::core::scene::layer* layer, int link_group, bool master, double adjustment_x, double adjustment_y) { linked_layer_record rec; rec.layer = layer; rec.link_id = link_group; rec.is_master = master; + rec.adjustment_x = adjustment_x; + rec.adjustment_y = adjustment_y; layers.push_back(rec); if(rec.is_master) @@ -125,50 +136,50 @@ core::text::text_info get_text_info(const boost::property_tree::wptree& ptree) if(r.link_id == master.link_id && r.layer != master.layer) { { //x-coords - double slave_left = r.layer->position.x.get(); + double slave_left = r.layer->position.x.get() + r.adjustment_x; double slave_right = slave_left + r.layer->producer.get()->pixel_constraints().width.get(); - double master_left = master.layer->position.x.get(); + double master_left = master.layer->position.x.get() + master.adjustment_x; double master_right = master_left + master.layer->producer.get()->pixel_constraints().width.get(); if((slave_left >= master_left && slave_right <= master_right) || (slave_left <= master_left && slave_right >= master_right)) { //change width of slave - r.layer->position.x = master.layer->position.x + (slave_left - master_left); - r.layer->producer.get()->pixel_constraints().width = master.layer->producer.get()->pixel_constraints().width + (slave_right - slave_left - master_right + master_left); + r.layer->position.x.bind(master.layer->position.x - r.adjustment_x + master.adjustment_x + (slave_left - master_left)); + r.layer->producer.get()->pixel_constraints().width.bind(master.layer->producer.get()->pixel_constraints().width + (slave_right - slave_left - master_right + master_left)); } else if(slave_left >= master_right) { //push slave ahead of master - r.layer->position.x = master.layer->position.x + master.layer->producer.get()->pixel_constraints().width + (slave_left - master_right); + r.layer->position.x.bind(master.layer->position.x - r.adjustment_x + master.adjustment_x + master.layer->producer.get()->pixel_constraints().width + (slave_left - master_right)); } else if(slave_right <= master_left) { - r.layer->position.x = master.layer->position.x - (master_left - slave_left); + r.layer->position.x.bind(master.layer->position.x - r.adjustment_x + master.adjustment_x - (master_left - slave_left)); } } { //y-coords - double slave_top = r.layer->position.y.get(); + double slave_top = r.layer->position.y.get() + r.adjustment_y; double slave_bottom = slave_top + r.layer->producer.get()->pixel_constraints().height.get(); - double master_top = master.layer->position.y.get(); + double master_top = master.layer->position.y.get() + master.adjustment_y; double master_bottom = master_top + master.layer->producer.get()->pixel_constraints().height.get(); if((slave_top >= master_top && slave_bottom <= master_bottom) || (slave_top <= master_top && slave_bottom >= master_bottom)) { - //change width of slave - r.layer->position.y = master.layer->position.y + (slave_top - master_top); - r.layer->producer.get()->pixel_constraints().height = master.layer->producer.get()->pixel_constraints().height + (slave_bottom - slave_top - master_bottom + master_top); + //change height of slave + r.layer->position.y.bind(master.layer->position.y - r.adjustment_y + master.adjustment_y + (slave_top - master_top)); + r.layer->producer.get()->pixel_constraints().height.bind(master.layer->producer.get()->pixel_constraints().height + (slave_bottom - slave_top - master_bottom + master_top)); } else if(slave_top >= master_bottom) { //push slave ahead of master - r.layer->position.y = master.layer->position.y + master.layer->producer.get()->pixel_constraints().height + (slave_top - master_bottom); + r.layer->position.y.bind(master.layer->position.y - r.adjustment_y + master.adjustment_y + master.layer->producer.get()->pixel_constraints().height + (slave_top - master_bottom)); } else if(slave_bottom <= master_top) { - r.layer->position.y = master.layer->position.y - (master_top - slave_top); + r.layer->position.y.bind(master.layer->position.y - r.adjustment_y + master.adjustment_y - (master_top - slave_top)); } } @@ -178,6 +189,108 @@ core::text::text_info get_text_info(const boost::property_tree::wptree& ptree) } }; +int64_t get_frame_number( + const core::video_format_desc& format_desc, + const boost::rational& at_second) +{ + return static_cast( + boost::rational_cast(at_second) * format_desc.fps); +} + +boost::rational get_rational(const boost::property_tree::wptree& node) +{ + return boost::rational( + node.get(L"numerator"), node.get(L"denominator")); +} + +void create_timelines( + const spl::shared_ptr& scene, + const core::video_format_desc& format_desc, + core::scene::layer& layer, + const layer_ptr& psd_layer, + double adjustment_x, + double adjustment_y) +{ + auto timeline = psd_layer->timeline_data(); + auto start = get_rational(timeline.get_child(L"timeScope.Strt")); + auto end_offset = get_rational(timeline.get_child(L"timeScope.outTime")); + auto end = start + end_offset; + auto start_frame = get_frame_number(format_desc, start); + auto end_frame = get_frame_number(format_desc, end); + + layer.hidden = scene->frame() < start_frame || scene->frame() > end_frame; + + auto tracklist = timeline.get_child_optional(L"trackList"); + + if (!tracklist) + return; + + double original_pos_x = psd_layer->location().x; + double original_pos_y = psd_layer->location().y; + + BOOST_FOREACH(auto& track, *tracklist) + { + auto track_id = track.second.get(L"stdTrackID"); + + if (track_id == L"sheetPositionTrack") + { + BOOST_FOREACH(auto& key, track.second.get_child(L"keyList")) + { + bool tween = key.second.get(L"animInterpStyle") + == L"Lnr "; + auto time = get_rational(key.second.get_child(L"time")); + auto hrzn = key.second.get(L"animKey.Hrzn"); + auto vrtc = key.second.get(L"animKey.Vrtc"); + auto x = original_pos_x + hrzn + adjustment_x; + auto y = original_pos_y + vrtc + adjustment_y; + auto frame = get_frame_number(format_desc, time); + + if (frame == 0) // Consider as initial value (rewind) + { + layer.position.x.set(x); + layer.position.y.set(y); + } + + frame = start_frame + frame; // translate to global timeline + + if (tween) + { + scene->add_keyframe(layer.position.x, x, frame, L"easeOutSine"); + scene->add_keyframe(layer.position.y, y, frame, L"easeOutSine"); + } + else + { + scene->add_keyframe(layer.position.x, x, frame); + scene->add_keyframe(layer.position.y, y, frame); + } + } + } + else if (track_id == L"opacityTrack") + { + auto& opacity = layer.adjustments.opacity; + + BOOST_FOREACH(auto& key, track.second.get_child(L"keyList")) + { + bool tween = key.second.get(L"animInterpStyle") + == L"Lnr "; + auto time = get_rational(key.second.get_child(L"time")); + auto opct = key.second.get(L"animKey.Opct.#Prc") / 100.0; + auto frame = get_frame_number(format_desc, time); + + if (frame == 0) // Consider as initial value (rewind) + opacity.set(opct); + + frame = start_frame + frame; // translate to global timeline + + if (tween) + scene->add_keyframe(opacity, opct, frame, L"easeOutSine"); + else + scene->add_keyframe(opacity, opct, frame); + } + } + } +} + spl::shared_ptr create_psd_scene_producer(const spl::shared_ptr& frame_factory, const core::video_format_desc& format_desc, const std::vector& params) { std::wstring filename = env::media_folder() + L"\\" + params[0] + L".psd"; @@ -197,52 +310,74 @@ spl::shared_ptr create_psd_scene_producer(const spl::share auto layers_end = doc.layers().end(); for(auto it = doc.layers().begin(); it != layers_end; ++it) { - if((*it)->is_text() && (*it)->visible()) + if((*it)->is_visible()) { - std::wstring str = (*it)->text_data().get(L"EngineDict.Editor.Text", L""); + if((*it)->is_text()) + { + std::wstring str = (*it)->text_data().get(L"EngineDict.Editor.Text", L""); - core::text::text_info text_info(std::move(get_text_info((*it)->text_data()))); - auto text_producer = core::text_producer::create(frame_factory, 0, 0, str, text_info, doc.width(), doc.height()); + core::text::text_info text_info(std::move(get_text_info((*it)->text_data()))); + text_info.size *= (*it)->text_scale(); + + auto text_producer = core::text_producer::create(frame_factory, 0, 0, str, text_info, doc.width(), doc.height()); - core::text::string_metrics metrics = text_producer->measure_string(str); + core::text::string_metrics metrics = text_producer->measure_string(str); - auto& new_layer = root->create_layer(text_producer, (*it)->rect().left - 2, (*it)->rect().top + metrics.bearingY, (*it)->name()); //the 2 offset is just a hack for now. don't know why our text is rendered 2 px to the right of that in photoshop - new_layer.adjustments.opacity.set((*it)->opacity() / 255.0); - new_layer.hidden.set(!(*it)->visible()); + auto adjustment_x = -2; //the 2 offset is just a hack for now. don't know why our text is rendered 2 px to the right of that in photoshop + auto adjustment_y = metrics.bearingY; + auto& new_layer = root->create_layer(text_producer, (*it)->location().x + adjustment_x, (*it)->location().y + adjustment_y, (*it)->name()); + new_layer.adjustments.opacity.set((*it)->opacity() / 255.0); + new_layer.hidden.set(!(*it)->is_visible()); - if((*it)->link_group_id() != 0) - link_constructor.add(&new_layer, (*it)->link_group_id(), true); + if ((*it)->has_timeline()) + create_timelines(root, format_desc, new_layer, (*it), adjustment_x, adjustment_y); - text_producers_by_layer_name.push_back(std::make_pair((*it)->name(), text_producer)); - } - else if((*it)->image() && (*it)->visible()) - { - std::wstring layer_name = (*it)->name(); - std::shared_ptr layer_producer; + if((*it)->link_group_id() != 0) + link_constructor.add(&new_layer, (*it)->link_group_id(), (*it)->is_position_protected(), -adjustment_x, -adjustment_y); - if (boost::algorithm::istarts_with(layer_name, L"[producer]")) - { - auto hotswap = std::make_shared((*it)->rect().width(), (*it)->rect().height()); - hotswap->producer().set(core::create_producer(frame_factory, format_desc, layer_name.substr(10))); - layer_producer = hotswap; + text_producers_by_layer_name.push_back(std::make_pair((*it)->name(), text_producer)); } else { - core::pixel_format_desc pfd(core::pixel_format::bgra); - pfd.planes.push_back(core::pixel_format_desc::plane((*it)->rect().width(), (*it)->rect().height(), 4)); + std::wstring layer_name = (*it)->name(); + std::shared_ptr layer_producer; + if((*it)->is_solid()) + { + layer_producer = layer_producer = core::create_const_producer(core::create_color_frame(it->get(), frame_factory, (*it)->solid_color().to_uint32()), (*it)->bitmap()->width(), (*it)->bitmap()->height()); + } + else if((*it)->bitmap()) + { + /*if (boost::algorithm::istarts_with(layer_name, L"[producer]")) + { + auto hotswap = std::make_shared((*it)->rect().width(), (*it)->rect().height()); + hotswap->producer().set(core::create_producer(frame_factory, format_desc, layer_name.substr(10))); + layer_producer = hotswap; + } + else*/ + { + core::pixel_format_desc pfd(core::pixel_format::bgra); + pfd.planes.push_back(core::pixel_format_desc::plane((*it)->bitmap()->width(), (*it)->bitmap()->height(), 4)); - auto frame = frame_factory->create_frame(it->get(), pfd); - memcpy(frame.image_data().data(), (*it)->image()->data(), frame.image_data().size()); + auto frame = frame_factory->create_frame(it->get(), pfd); + memcpy(frame.image_data().data(), (*it)->bitmap()->data(), frame.image_data().size()); - layer_producer = core::create_const_producer(core::draw_frame(std::move(frame)), (*it)->rect().width(), (*it)->rect().height()); - } + layer_producer = core::create_const_producer(core::draw_frame(std::move(frame)), (*it)->bitmap()->width(), (*it)->bitmap()->height()); + } + } - auto& new_layer = root->create_layer(spl::make_shared_ptr(layer_producer), (*it)->rect().left, (*it)->rect().top); - new_layer.adjustments.opacity.set((*it)->opacity() / 255.0); - new_layer.hidden.set(!(*it)->visible()); + if(layer_producer) + { + auto& new_layer = root->create_layer(spl::make_shared_ptr(layer_producer), (*it)->location().x, (*it)->location().y, (*it)->name()); + new_layer.adjustments.opacity.set((*it)->opacity() / 255.0); + new_layer.hidden.set(!(*it)->is_visible()); - if((*it)->link_group_id() != 0) - link_constructor.add(&new_layer, (*it)->link_group_id(), false); + if ((*it)->has_timeline()) + create_timelines(root, format_desc, new_layer, (*it), 0, 0); + + if((*it)->link_group_id() != 0) + link_constructor.add(&new_layer, (*it)->link_group_id(), (*it)->is_position_protected(), 0, 0); + } + } } } @@ -250,7 +385,7 @@ spl::shared_ptr create_psd_scene_producer(const spl::share // Reset all dynamic text fields to empty strings and expose them as a scene parameter. BOOST_FOREACH(auto& text_layer, text_producers_by_layer_name) - text_layer.second->text().bind(root->create_parameter(text_layer.first, L"")); + text_layer.second->text().bind(root->create_variable(boost::to_lower_copy(text_layer.first), true, L"")); auto params2 = params; params2.erase(params2.cbegin());