]> git.sesse.net Git - casparcg/blob - modules/psd/psd_scene_producer.cpp
* support for clearing/overriding a channel lock
[casparcg] / modules / psd / psd_scene_producer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Niklas P Andersson, niklas.p.andersson@svt.se
20 */
21
22 #include "psd_scene_producer.h"
23 #include "layer.h"
24 #include "doc.h"
25
26 #include <core/frame/pixel_format.h>
27 #include <core/frame/frame_factory.h>
28 #include <core/producer/frame_producer.h>
29 #include <core/producer/text/text_producer.h>
30 #include <core/producer/scene/scene_producer.h>
31 #include <core/producer/scene/const_producer.h>
32 #include <core/producer/scene/hotswap_producer.h>
33 #include <core/frame/draw_frame.h>
34
35 #include <common/env.h>
36 #include <common/memory.h>
37
38 #include <boost/filesystem.hpp>
39 #include <boost/foreach.hpp>
40 #include <boost/thread/future.hpp>
41 #include <boost/algorithm/string.hpp>
42
43 namespace caspar { namespace psd {
44
45 void init()
46 {
47         core::register_producer_factory(create_psd_scene_producer);
48 }
49
50 core::text::text_info get_text_info(const boost::property_tree::wptree& ptree)
51 {
52         core::text::text_info result;
53         int font_index = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.Font", 0);
54         result.size = ptree.get(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.FontSize", 30.0f);
55
56         int child_index = 0;
57         auto color_node = ptree.get_child(L"EngineDict.StyleRun.RunArray..StyleSheet.StyleSheetData.FillColor.Values");
58         for(auto it = color_node.begin(); it != color_node.end(); ++it, ++child_index)
59         {
60                 auto& value_node = (*it).second;
61                 float value = value_node.get_value(0.0f);
62                 switch(child_index)
63                 {
64                 case 0: result.color.a = value; break;
65                 case 1: result.color.r = value; break;
66                 case 2: result.color.g = value; break;
67                 case 3: result.color.b = value; break;
68                 }
69         }
70
71         //find fontname
72         child_index = 0;
73         auto fontset_node = ptree.get_child(L"ResourceDict.FontSet");
74         for(auto it = fontset_node.begin(); it != fontset_node.end(); ++it, ++child_index)
75         {
76                 auto& font_node = (*it).second;
77                 if(child_index == font_index)
78                 {
79                         result.font = font_node.get(L"Name", L"");
80                         break;
81                 }
82         }
83
84         return result;
85 }
86
87         class layer_link_constructor
88         {
89                 struct linked_layer_record
90                 {
91                         caspar::core::scene::layer* layer;
92                         int link_id;
93                         bool is_master;
94                 };              
95
96                 std::vector<linked_layer_record> layers;
97                 std::vector<linked_layer_record> masters;
98
99         public:
100                 void add(caspar::core::scene::layer* layer, int link_group, bool master)
101                 {
102                         linked_layer_record rec;
103                         rec.layer = layer;
104                         rec.link_id = link_group;
105                         rec.is_master = master;
106                         layers.push_back(rec);
107
108                         if(rec.is_master)
109                         {
110                                 auto it = std::find_if(masters.begin(), masters.end(), [=](linked_layer_record const &r){ return r.link_id == link_group; });
111                                 if(it == masters.end())
112                                         masters.push_back(rec);
113                                 else
114                                         masters.erase(it);      //ambigous, two linked layers with locked position
115                         }
116                 }
117
118                 void calculate()
119                 {
120                         for(auto it = masters.begin(); it != masters.end(); ++it)
121                         {
122                                 auto& master = (*it);
123                                 //std::for_each(layers.begin(), layers.end(), [&master](linked_layer_record &r) mutable { 
124                                 BOOST_FOREACH(auto &r, layers) {
125                                         if(r.link_id == master.link_id && r.layer != master.layer)
126                                         {
127                                                 {       //x-coords
128                                                         double slave_left = r.layer->position.x.get();
129                                                         double slave_right = slave_left + r.layer->producer.get()->pixel_constraints().width.get();
130
131                                                         double master_left = master.layer->position.x.get();
132                                                         double master_right = master_left + master.layer->producer.get()->pixel_constraints().width.get();
133
134                                                         if((slave_left >= master_left && slave_right <= master_right) || (slave_left <= master_left && slave_right >= master_right))
135                                                         {
136                                                                 //change width of slave
137                                                                 r.layer->position.x = master.layer->position.x + (slave_left - master_left);
138                                                                 r.layer->producer.get()->pixel_constraints().width = master.layer->producer.get()->pixel_constraints().width + (slave_right - slave_left - master_right + master_left); 
139                                                         }
140                                                         else if(slave_left >= master_right)
141                                                         {
142                                                                 //push slave ahead of master
143                                                                 r.layer->position.x = master.layer->position.x + master.layer->producer.get()->pixel_constraints().width + (slave_left - master_right);
144                                                         }
145                                                         else if(slave_right <= master_left)
146                                                         {
147                                                                 r.layer->position.x = master.layer->position.x - (master_left - slave_left);
148                                                         }
149                                                 }
150
151                                                 {       //y-coords
152                                                         double slave_top = r.layer->position.y.get();
153                                                         double slave_bottom = slave_top + r.layer->producer.get()->pixel_constraints().height.get();
154
155                                                         double master_top = master.layer->position.y.get();
156                                                         double master_bottom = master_top + master.layer->producer.get()->pixel_constraints().height.get();
157
158                                                         if((slave_top >= master_top && slave_bottom <= master_bottom) || (slave_top <= master_top && slave_bottom >= master_bottom))
159                                                         {
160                                                                 //change width of slave
161                                                                 r.layer->position.y = master.layer->position.y + (slave_top - master_top);
162                                                                 r.layer->producer.get()->pixel_constraints().height = master.layer->producer.get()->pixel_constraints().height + (slave_bottom - slave_top - master_bottom + master_top); 
163                                                         }
164                                                         else if(slave_top >= master_bottom)
165                                                         {
166                                                                 //push slave ahead of master
167                                                                 r.layer->position.y = master.layer->position.y + master.layer->producer.get()->pixel_constraints().height + (slave_top - master_bottom);
168                                                         }
169                                                         else if(slave_bottom <= master_top)
170                                                         {
171                                                                 r.layer->position.y = master.layer->position.y - (master_top - slave_top);
172                                                         }
173                                                 }
174
175                                         }
176                                 }
177                         }
178                 }
179         };
180
181 spl::shared_ptr<core::frame_producer> create_psd_scene_producer(const spl::shared_ptr<core::frame_factory>& frame_factory, const core::video_format_desc& format_desc, const std::vector<std::wstring>& params)
182 {
183         std::wstring filename = env::media_folder() + L"\\" + params[0] + L".psd";
184         if(!boost::filesystem::is_regular_file(boost::filesystem::path(filename)))
185                 return core::frame_producer::empty();
186
187         psd_document doc;
188         if(!doc.parse(filename))
189                 return core::frame_producer::empty();
190
191         spl::shared_ptr<core::scene::scene_producer> root(spl::make_shared<core::scene::scene_producer>(doc.width(), doc.height()));
192
193         layer_link_constructor link_constructor;
194
195         std::vector<std::pair<std::wstring, spl::shared_ptr<core::text_producer>>> text_producers_by_layer_name;
196
197         auto layers_end = doc.layers().end();
198         for(auto it = doc.layers().begin(); it != layers_end; ++it)
199         {
200                 if((*it)->is_visible())
201                 {
202                         if((*it)->is_text())
203                         {
204                                 std::wstring str = (*it)->text_data().get(L"EngineDict.Editor.Text", L"");
205                         
206                                 core::text::text_info text_info(std::move(get_text_info((*it)->text_data())));
207                                 auto text_producer = core::text_producer::create(frame_factory, 0, 0, str, text_info, doc.width(), doc.height());
208                         
209                                 core::text::string_metrics metrics = text_producer->measure_string(str);
210                         
211                                 auto& new_layer = root->create_layer(text_producer, (*it)->location().x - 2, (*it)->location().y + 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
212                                 new_layer.adjustments.opacity.set((*it)->opacity() / 255.0);
213                                 new_layer.hidden.set(!(*it)->is_visible());
214
215                                 if((*it)->link_group_id() != 0)
216                                         link_constructor.add(&new_layer, (*it)->link_group_id(), true);
217
218                                 text_producers_by_layer_name.push_back(std::make_pair((*it)->name(), text_producer));
219                         }
220                         else if((*it)->bitmap())
221                         {
222                                 std::wstring layer_name = (*it)->name();
223                                 std::shared_ptr<core::frame_producer> layer_producer;
224
225                                 if (boost::algorithm::istarts_with(layer_name, L"[producer]"))
226                                 {
227                                         auto hotswap = std::make_shared<core::hotswap_producer>((*it)->bitmap()->width(), (*it)->bitmap()->height());
228                                         hotswap->producer().set(core::create_producer(frame_factory, format_desc, layer_name.substr(10)));
229                                         layer_producer = hotswap;
230                                 }
231                                 else
232                                 {
233                                         core::pixel_format_desc pfd(core::pixel_format::bgra);
234                                         pfd.planes.push_back(core::pixel_format_desc::plane((*it)->bitmap()->width(), (*it)->bitmap()->height(), 4));
235
236                                         auto frame = frame_factory->create_frame(it->get(), pfd);
237                                         memcpy(frame.image_data().data(), (*it)->bitmap()->data(), frame.image_data().size());
238
239                                         layer_producer = core::create_const_producer(core::draw_frame(std::move(frame)), (*it)->bitmap()->width(), (*it)->bitmap()->height());
240                                 }
241
242                                 auto& new_layer = root->create_layer(spl::make_shared_ptr(layer_producer), (*it)->location().x, (*it)->location().y);
243                                 new_layer.adjustments.opacity.set((*it)->opacity() / 255.0);
244                                 new_layer.hidden.set(!(*it)->is_visible());
245
246                                 if((*it)->link_group_id() != 0)
247                                         link_constructor.add(&new_layer, (*it)->link_group_id(), false);
248                         }
249                 }
250         }
251
252         link_constructor.calculate();
253
254         // Reset all dynamic text fields to empty strings and expose them as a scene parameter.
255         BOOST_FOREACH(auto& text_layer, text_producers_by_layer_name)
256                 text_layer.second->text().bind(root->create_parameter<std::wstring>(text_layer.first, L""));
257
258         auto params2 = params;
259         params2.erase(params2.cbegin());
260
261         root->call(params2);
262
263         return root;
264 }
265
266 }}