1 #include <sstream>
2 #include <string>
3 #include <utility>
4 #include <stdexcept>
5 #include <stdint.h>
6 #if defined(_WIN32) || defined(_CYGWIN_)
7 #define UUID UUID_
8 #include <windows.h>
9 #undef UUID
10 #else
11 #include <dlfcn.h>
12 #endif
13 #include <map>
14 #include <type_traits>
15 #include <functional>
16
17 // #include "tfxparam.h"
18 #include <toonzqt/addfxcontextmenu.h> // as receiver
19 #include "tenv.h"
20 #include "../include/toonzqt/fxsettings.h"
21 #include "toonz/tcolumnfx.h"
22
23 #include "pluginhost.h"
24 #include "toonz_plugin.h"
25 #include "toonz_hostif.h"
26 #include "toonz_params.h"
27 #include "plugin_tile_interface.h"
28 #include "plugin_port_interface.h"
29 #include "plugin_fxnode_interface.h"
30 #include "plugin_param_interface.h"
31 #include "plugin_param_view_interface.h"
32 #include "plugin_ui_page_interface.h"
33 #include "plugin_utilities.h"
34 #include <QFileInfo>
35 #include <QDir>
36 #include <QLabel>
37 #include <QHBoxLayout>
38 #include <QFormLayout>
39 //#include <QtConcurrent>
40
41 #include "plugin_param_traits.h"
42 #include "../include/toonzqt/pluginloader.h"
43
44 using namespace toonz; // plugin namespace
45
46 extern std::map<std::string, PluginInformation *> plugin_dict_;
47
48 /*
49 PluginLoadController が main thread queue を使うことと,
50 QThread で他スレッドの待ち合わせがしにくい(sendor thread が QThread::wait()
51 でブロックしていると emit signal が処理できずデッドロックする)ので、
52 大人しく polling にした.
53 */
load_entries(const std::string & basepath)54 bool PluginLoader::load_entries(const std::string &basepath) {
55 static PluginLoadController *aw = NULL; /* main() から一度だけ呼ばれる */
56 if (!aw) {
57 aw = new PluginLoadController(basepath, NULL);
58 }
59 bool ret = aw->wait(16 /* ms */);
60 if (ret) aw = NULL; /* deleteLater で消えるはず */
61 return ret;
62 }
63
create_host(const std::string & fxId)64 TFx *PluginLoader::create_host(const std::string &fxId) {
65 std::string id = fxId.substr(5);
66 auto it = plugin_dict_.find(id);
67 if (it != plugin_dict_.end()) {
68 auto plugin = new RasterFxPluginHost(it->second);
69 plugin->notify();
70 return plugin;
71 }
72 return NULL;
73 }
74
create_menu_items(std::function<void (QTreeWidgetItem *)> && l1_handler,std::function<void (QTreeWidgetItem *)> && l2_handler)75 std::map<std::string, QTreeWidgetItem *> PluginLoader::create_menu_items(
76 std::function<void(QTreeWidgetItem *)> &&l1_handler,
77 std::function<void(QTreeWidgetItem *)> &&l2_handler) {
78 std::map<std::string, QTreeWidgetItem *> vendors;
79 for (auto plugin : plugin_dict_) {
80 PluginDescription *desc = plugin.second->desc_;
81 if (vendors.count(desc->vendor_) == 0) {
82 auto vendor = new QTreeWidgetItem(
83 (QTreeWidget *)NULL,
84 QStringList(QString::fromStdString(desc->vendor_)));
85 vendors.insert(std::make_pair(desc->vendor_, vendor));
86 l1_handler(vendor);
87 }
88
89 auto vendor = vendors[desc->vendor_];
90 auto item = new QTreeWidgetItem(
91 (QTreeWidget *)NULL, QStringList(QString::fromStdString(desc->name_)));
92 item->setData(0, Qt::UserRole,
93 QVariant("_plg_" + QString::fromStdString(desc->id_)));
94 l2_handler(item);
95 vendor->addChild(item);
96 }
97 return vendors;
98 }
99
100 static bool copy_rendering_setting(toonz_rendering_setting_t *dst,
101 const TRenderSettings &src);
102
103 class PluginSetupMessage final : public TThread::Message {
104 PluginInformation *pi_;
105
106 public:
PluginSetupMessage(PluginInformation * pi)107 PluginSetupMessage(PluginInformation *pi) : pi_(pi) {}
108
onDeliver()109 void onDeliver() override {
110 RasterFxPluginHost *fx = new RasterFxPluginHost(pi_);
111 if (pi_ && pi_->handler_) {
112 pi_->handler_->setup(fx);
113 /* fx は pi のラッパーとしてのみ構築されており、即座に削除される. 実
114 instance に引き継がれないので ここで createParam()
115 等を呼び出しても意味がない.
116 ここで createParamsByDesc() などを呼び出しても、 instance の parameter
117 は 0 になる. */
118 }
119 delete fx;
120 }
121
clone() const122 TThread::Message *clone() const override {
123 return new PluginSetupMessage(*this);
124 }
125 };
126
~PluginInformation()127 PluginInformation::~PluginInformation() {
128 if (library_) {
129 if (library_.use_count() == 1 && fin_) {
130 fin_();
131 }
132 }
133 }
134
add_ref()135 void PluginInformation::add_ref() { ++ref_count_; }
136
release()137 void PluginInformation::release() {
138 if (--ref_count_ == 0) {
139 delete this;
140 }
141 }
142
PluginDescription(const plugin_probe_t * const probe)143 PluginDescription::PluginDescription(const plugin_probe_t *const probe) {
144 name_ = probe->name ? probe->name : "unnamed-plugin";
145 vendor_ = probe->vendor ? probe->vendor : "";
146 id_ = probe->id ? probe->id : "unnamed-plugin.plugin";
147 note_ = probe->note ? probe->note : "";
148 url_ = probe->helpurl ? probe->helpurl : "";
149 clss_ = probe->clss;
150 fullname_ = id_ + "$" + name_ + "$" + vendor_;
151 plugin_ver_ = probe->plugin_ver;
152 }
153
RasterFxPluginHost(PluginInformation * pinfo)154 RasterFxPluginHost::RasterFxPluginHost(PluginInformation *pinfo)
155 : TRasterFx(), pi_(pinfo), user_data_(nullptr) {
156 pi_->add_ref();
157 }
158
create_param_view(toonz_node_handle_t node,toonz_param_view_handle_t * view)159 static int create_param_view(toonz_node_handle_t node,
160 toonz_param_view_handle_t *view) {
161 ParamView *p = NULL;
162 try {
163 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
164 if (!fx) {
165 printf("create_param_view: invalid toonz_node_handle_t\n");
166 return TOONZ_ERROR_INVALID_HANDLE;
167 }
168
169 if ((p = fx->createParamView())) {
170 *view = p;
171 } else {
172 printf("create_param_view: invalid param name");
173 return TOONZ_ERROR_FAILED_TO_CREATE;
174 }
175 } catch (const std::exception &e) {
176 printf("create_param_view: exception: %s\n", e.what());
177 delete p;
178 return TOONZ_ERROR_UNKNOWN;
179 }
180 return TOONZ_OK;
181 }
182
setup_input_port(toonz_node_handle_t node,const char * name,int type)183 static int setup_input_port(toonz_node_handle_t node, const char *name,
184 int type) {
185 try {
186 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
187 if (!fx) return TOONZ_ERROR_INVALID_HANDLE;
188 if (!fx->addPortDesc({true, name, type})) {
189 printf("add_input_port: failed to add: already have\n");
190 return TOONZ_ERROR_BUSY;
191 }
192 } catch (const std::exception &e) {
193 printf("setup_putput_port: exception: %s\n", e.what());
194 return TOONZ_ERROR_UNKNOWN;
195 }
196 return TOONZ_OK;
197 }
198
setup_output_port(toonz_node_handle_t node,const char * name,int type)199 static int setup_output_port(toonz_node_handle_t node, const char *name,
200 int type) {
201 try {
202 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
203 if (!fx) return TOONZ_ERROR_INVALID_HANDLE;
204 if (!fx->addPortDesc({false, name, type})) {
205 printf("add_input_port: failed to add: already have\n");
206 return TOONZ_ERROR_BUSY;
207 }
208 } catch (const std::exception &e) {
209 printf("setup_putput_port: exception: %s\n", e.what());
210 return TOONZ_ERROR_UNKNOWN;
211 }
212 return TOONZ_OK;
213 }
214
add_input_port(toonz_node_handle_t node,const char * name,int type,toonz_port_handle_t * port)215 static int add_input_port(toonz_node_handle_t node, const char *name, int type,
216 toonz_port_handle_t *port) {
217 try {
218 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
219 if (!fx) return TOONZ_ERROR_INVALID_HANDLE;
220 auto p = std::make_shared<TRasterFxPort>();
221 /* TRasterFxPort は non-copyable なスマートポインタなのでポインタで引き回す
222 */
223 if (!fx->addInputPort(name, p)) { // overloaded version
224 printf("add_input_port: failed to add: already have\n");
225 return TOONZ_ERROR_BUSY;
226 }
227 *port = p.get();
228 } catch (const std::exception &e) {
229 printf("add_input_port: exception: %s\n", e.what());
230 return TOONZ_ERROR_UNKNOWN;
231 }
232 return TOONZ_OK;
233 }
234
get_input_port(toonz_node_handle_t node,const char * name,toonz_port_handle_t * port)235 static int get_input_port(toonz_node_handle_t node, const char *name,
236 toonz_port_handle_t *port) {
237 if (!(node && port)) {
238 return TOONZ_ERROR_NULL;
239 }
240
241 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
242 std::string portName(name);
243 TFxPort *tfxport = fx->getInputPort(portName);
244 if (!tfxport) {
245 return TOONZ_ERROR_NOT_FOUND;
246 }
247 *port = tfxport;
248
249 return TOONZ_OK;
250 }
251
add_output_port(toonz_node_handle_t node,const char * name,int type,toonz_port_handle_t * port)252 static int add_output_port(toonz_node_handle_t node, const char *name, int type,
253 toonz_port_handle_t *port) {
254 TRasterFxPort *p = NULL;
255 try {
256 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
257 if (!fx) return TOONZ_ERROR_INVALID_HANDLE;
258 p = new TRasterFxPort();
259 /* TRasterFxPort は non-copyable なスマートポインタなのでポインタで引き回す
260 */
261 if (fx->addOutputPort(name, p)) { // overloaded version
262 delete p;
263 return TOONZ_ERROR_BUSY;
264 }
265 *port = p;
266 } catch (const std::exception &) {
267 delete p;
268 return TOONZ_ERROR_UNKNOWN;
269 }
270 return TOONZ_OK;
271 }
272
get_rect(toonz_rect_t * rect,double * x0,double * y0,double * x1,double * y1)273 static int get_rect(toonz_rect_t *rect, double *x0, double *y0, double *x1,
274 double *y1) {
275 if (!(rect && x0 && y0 && x1 && y1)) {
276 return -2;
277 }
278 *x0 = rect->x0;
279 *y0 = rect->y0;
280 *x1 = rect->x1;
281 *y1 = rect->y1;
282 return 0;
283 }
284
set_rect(toonz_rect_t * rect,double x0,double y0,double x1,double y1)285 static int set_rect(toonz_rect_t *rect, double x0, double y0, double x1,
286 double y1) {
287 if (!rect) {
288 return -2;
289 }
290 rect->x0 = x0;
291 rect->y0 = y0;
292 rect->x1 = x1;
293 rect->y1 = y1;
294 return 0;
295 }
296
add_preference(toonz_node_handle_t node,const char * name,toonz_ui_page_handle_t * ui)297 static int add_preference(toonz_node_handle_t node, const char *name,
298 toonz_ui_page_handle_t *ui) {
299 UIPage *p = NULL;
300 try {
301 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
302 if (!fx) {
303 printf("add_preference: invalid toonz_node_handle_t\n");
304 return TOONZ_ERROR_INVALID_HANDLE;
305 }
306
307 if ((p = fx->createUIPage(name))) {
308 *ui = p;
309 } else {
310 printf("add_preference: failed to get UIPage\n");
311 return TOONZ_ERROR_FAILED_TO_CREATE;
312 }
313 } catch (const std::exception &e) {
314 printf("add_preference: exception: %s\n", e.what());
315 delete p;
316 return TOONZ_ERROR_UNKNOWN;
317 }
318 return TOONZ_OK;
319 }
320
add_param(toonz_node_handle_t node,const char * name,int type,toonz_param_handle_t * param)321 static int add_param(toonz_node_handle_t node, const char *name, int type,
322 toonz_param_handle_t *param) {
323 Param *p = NULL;
324 try {
325 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
326 if (!fx) {
327 printf("add_param: invalid toonz_node_handle_t\n");
328 return TOONZ_ERROR_INVALID_HANDLE;
329 }
330
331 if ((p = fx->createParam(name, toonz_param_type_enum(type)))) {
332 *param = p;
333 } else {
334 printf("add_param: invalid type");
335 return TOONZ_ERROR_FAILED_TO_CREATE;
336 }
337 } catch (const std::exception &e) {
338 printf("add_param: exception: %s\n", e.what());
339 delete p;
340 return TOONZ_ERROR_UNKNOWN;
341 }
342 return TOONZ_OK;
343 }
344
get_param(toonz_node_handle_t node,const char * name,toonz_param_handle_t * param)345 static int get_param(toonz_node_handle_t node, const char *name,
346 toonz_param_handle_t *param) {
347 try {
348 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
349 if (!fx) {
350 printf("get_param: invalid toonz_node_handle_t\n");
351 return TOONZ_ERROR_INVALID_HANDLE;
352 }
353
354 if (Param *p = fx->getParam(name)) {
355 *param = p;
356 } else {
357 printf("get_param: invalid type");
358 return TOONZ_ERROR_NOT_FOUND;
359 }
360 } catch (const std::exception &) {
361 }
362 return TOONZ_OK;
363 }
364
set_user_data(toonz_node_handle_t node,void * user_data)365 static int set_user_data(toonz_node_handle_t node, void *user_data) {
366 if (!node) {
367 return TOONZ_ERROR_NULL;
368 }
369 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
370 fx->setUserData(user_data);
371 return TOONZ_OK;
372 }
373
get_user_data(toonz_node_handle_t node,void ** user_data)374 static int get_user_data(toonz_node_handle_t node, void **user_data) {
375 if (!node || !user_data) {
376 return TOONZ_ERROR_NULL;
377 }
378 RasterFxPluginHost *fx = reinterpret_cast<RasterFxPluginHost *>(node);
379 *user_data = fx->getUserData();
380 return TOONZ_OK;
381 }
382
addPortDesc(port_description_t && desc)383 bool RasterFxPluginHost::addPortDesc(port_description_t &&desc) {
384 printf("addPortDesc: name:%s dir:%d type:%d\n", desc.name_.c_str(),
385 desc.input_, desc.type_);
386 auto ret = pi_->port_mapper_.insert(std::make_pair(desc.name_, desc));
387 return ret.second;
388 }
389
notify()390 void RasterFxPluginHost::notify() {
391 /* 最低限必要な setup をしてから通知する */
392 QString nm = QString::fromStdString(pi_->desc_->name_.c_str());
393 setName(nm.toStdWString());
394
395 createParamsByDesc();
396 createPortsByDesc();
397
398 if (pi_ && pi_->handler_->create) pi_->handler_->create(this);
399 }
400
~RasterFxPluginHost()401 RasterFxPluginHost::~RasterFxPluginHost() {
402 if (pi_ && pi_->handler_->destroy) {
403 pi_->handler_->destroy(this);
404 pi_->release();
405 }
406 inputs_.clear();
407 }
408
409 /*
410 node を click するなどの要因で頻繁に呼ばれる.
411 click した場合は FxsData::setFxs から呼ばれ、新しいインスタンスは
412 FxsData::m_fxs に入れられ、 FxsData
413 のインスタンスと同時に(大抵の場合は)即座に消される.
414 */
clone(bool recursive) const415 TFx *RasterFxPluginHost::clone(bool recursive) const {
416 RasterFxPluginHost *plugin = newInstance(pi_);
417 plugin->user_data_ = user_data_;
418 // clone ports before TFx::clone().
419 for (auto &ip : pi_->port_mapper_) {
420 if (ip.second.input_) {
421 #if 0
422 /* addInputPort() 内で行われる port owner の更新は後勝ちだが,
423 clone された新しいインスタンスのほうが先に消えてしまう場合に, 無効なポインタを示す owner が port に残ってしまう. この問題が解決したら共有できるようにしたい.
424 (このため、 plugin 空間に通知される全ての handle には一貫性がない. ただし、後から一貫性がなくなるよりは遥かにいいだろう)
425 */
426 plugin->addInputPort(getInputPortName(i), ip);
427 #else
428 plugin->addInputPort(ip.first,
429 std::shared_ptr<TFxPort>(new TRasterFxPort));
430 #endif
431 }
432 }
433
434 printf("recursive:%d params:%d\n", (int)recursive, (int)params_.size());
435 // clone params before TFx::clone().
436 /* ui_pages_, param_views_ は pi に移ったが createParam
437 * の呼び出しだけはしておかないと Fx Settings 構築時に assert failed になる */
438 for (auto const ¶m : params_) {
439 /* 古い createParam() は desc
440 * をとらず、コンストラクト時にデフォルト値を持つタイプの T*Param
441 * を再作成できない */
442 plugin->createParam(param->desc());
443 }
444
445 return TFx::clone(plugin, recursive);
446 }
447
newInstance(PluginInformation * pi) const448 RasterFxPluginHost *RasterFxPluginHost::newInstance(
449 PluginInformation *pi) const {
450 return new RasterFxPluginHost(pi);
451 }
452
getDeclaration() const453 const TPersistDeclaration *RasterFxPluginHost::getDeclaration() const {
454 printf("RasterFxPluginHost::getDeclaration()\n");
455 return pi_->decl_;
456 }
457
PluginDeclaration(PluginInformation * pi)458 PluginDeclaration::PluginDeclaration(PluginInformation *pi)
459 : TFxDeclaration(TFxInfo(pi->desc_->id_, false)), pi_(pi) {}
460
create() const461 TPersist *PluginDeclaration::create() const {
462 RasterFxPluginHost *fx = new RasterFxPluginHost(pi_);
463 fx->notify();
464 return fx;
465 }
466
getPluginId() const467 std::string RasterFxPluginHost::getPluginId() const { return pi_->desc_->id_; };
468
getUserData()469 void *RasterFxPluginHost::getUserData() { return user_data_; }
470
setUserData(void * user_data)471 void RasterFxPluginHost::setUserData(void *user_data) {
472 user_data_ = user_data;
473 }
474
doGetBBox(double frame,TRectD & bbox,const TRenderSettings & info)475 bool RasterFxPluginHost::doGetBBox(double frame, TRectD &bbox,
476 const TRenderSettings &info) {
477 using namespace plugin::utils;
478 bool ret = true; /* 負論理 */
479 if (pi_ && pi_->handler_->do_get_bbox) {
480 rendering_setting_t info_;
481 copy_rendering_setting(&info_, info);
482
483 rect_t rc;
484 copy_rect(&rc, bbox);
485
486 ret = ret && pi_->handler_->do_get_bbox(this, &info_, frame, &rc);
487 bbox = restore_rect(&rc);
488 }
489 return !ret;
490 }
491
doCompute(TTile & tile,double frame,const TRenderSettings & info)492 void RasterFxPluginHost::doCompute(TTile &tile, double frame,
493 const TRenderSettings &info) {
494 if (pi_ && pi_->handler_->do_compute) {
495 rendering_setting_t info_;
496 copy_rendering_setting(&info_, info);
497 pi_->handler_->do_compute(this, &info_, frame, &tile);
498 }
499 }
500
getMemoryRequirement(const TRectD & rect,double frame,const TRenderSettings & info)501 int RasterFxPluginHost::getMemoryRequirement(const TRectD &rect, double frame,
502 const TRenderSettings &info) {
503 using namespace plugin::utils;
504 if (pi_ && pi_->handler_->get_memory_requirement) {
505 rendering_setting_t rs;
506 copy_rendering_setting(&rs, info);
507
508 rect_t rc;
509 copy_rect(&rc, rect);
510
511 size_t ignore =
512 pi_->handler_->get_memory_requirement(this, &rs, frame, &rc);
513 return 0;
514 }
515 return 0;
516 }
517
canHandle(const TRenderSettings & info,double frame)518 bool RasterFxPluginHost::canHandle(const TRenderSettings &info, double frame) {
519 if (pi_ && pi_->handler_->can_handle) {
520 rendering_setting_t rs;
521 copy_rendering_setting(&rs, info);
522 return pi_->handler_->can_handle(this, &rs, frame);
523 }
524 /* 適切なデフォルト値は 'geometric' の場合とそうでない場合で異なる */
525 return isPluginZerary(); /* better-default depends on that is 'geometric' or
526 not */
527 }
528
addInputPort(const std::string & nm,std::shared_ptr<TFxPort> port)529 bool RasterFxPluginHost::addInputPort(const std::string &nm,
530 std::shared_ptr<TFxPort> port) {
531 /* setOwnFx は addInputPort 内で行われている. setFx()
532 * は接続なので自分自身に呼んではダメ */
533 // port->setFx(this);
534 bool ret = TFx::addInputPort(nm, *port.get());
535 if (ret) {
536 inputs_.push_back(port);
537 }
538 return ret;
539 }
540
addOutputPort(const std::string & nm,TRasterFxPort * port)541 bool RasterFxPluginHost::addOutputPort(const std::string &nm,
542 TRasterFxPort *port) {
543 port->setFx(this);
544 return TFx::addOutputConnection(port);
545 }
546
callStartRenderHandler()547 void RasterFxPluginHost::callStartRenderHandler() {
548 if (pi_ && pi_->handler_ && pi_->handler_->start_render) {
549 pi_->handler_->start_render(this);
550 }
551 }
552
callEndRenderHandler()553 void RasterFxPluginHost::callEndRenderHandler() {
554 if (pi_ && pi_->handler_ && pi_->handler_->end_render) {
555 pi_->handler_->end_render(this);
556 }
557 }
558
callStartRenderFrameHandler(const TRenderSettings * rs,double frame)559 void RasterFxPluginHost::callStartRenderFrameHandler(const TRenderSettings *rs,
560 double frame) {
561 toonz_rendering_setting_t trs;
562 copy_rendering_setting(&trs, *rs);
563 if (pi_ && pi_->handler_ && pi_->handler_->on_new_frame) {
564 pi_->handler_->on_new_frame(this, &trs, frame);
565 }
566 }
567
callEndRenderFrameHandler(const TRenderSettings * rs,double frame)568 void RasterFxPluginHost::callEndRenderFrameHandler(const TRenderSettings *rs,
569 double frame) {
570 toonz_rendering_setting_t trs;
571 copy_rendering_setting(&trs, *rs);
572 if (pi_ && pi_->handler_ && pi_->handler_->on_end_frame) {
573 pi_->handler_->on_end_frame(this, &trs, frame);
574 }
575 }
576
getUrl() const577 std::string RasterFxPluginHost::getUrl() const { return pi_->desc_->url_; }
578
createUIPage(const char * name)579 UIPage *RasterFxPluginHost::createUIPage(const char *name) {
580 pi_->ui_pages_.push_back(NULL);
581 pi_->ui_pages_.back() = new UIPage(name);
582 return pi_->ui_pages_.back();
583 }
584
585 // deprecated. for migration.
createParam(const char * name,toonz_param_type_enum e)586 Param *RasterFxPluginHost::createParam(const char *name,
587 toonz_param_type_enum e) {
588 toonz_param_desc_t *desc = new toonz_param_desc_t;
589 memset(desc, 0, sizeof(toonz_param_desc_t));
590 desc->base.ver = {1, 0};
591 desc->key = name;
592 desc->traits_tag = e;
593 return createParam(desc);
594 }
595
createParam(const toonz_param_desc_t * desc)596 Param *RasterFxPluginHost::createParam(const toonz_param_desc_t *desc) {
597 TParamP p = parameter_factory(desc);
598 if (!p) return nullptr;
599
600 p->setDescription(desc->note);
601 p->setUILabel(desc->base.label);
602
603 bindPluginParam(this, desc->key, p);
604
605 params_.push_back(std::make_shared<Param>(
606 this, desc->key, toonz_param_type_enum(desc->traits_tag), desc));
607 return params_.back().get();
608 }
609
getParam(const char * name) const610 Param *RasterFxPluginHost::getParam(const char *name) const {
611 for (auto ¶m : params_) {
612 if (param->name() == name) {
613 return param.get();
614 }
615 }
616 return nullptr;
617 }
618
createParamView()619 ParamView *RasterFxPluginHost::createParamView() {
620 pi_->param_views_.push_back(NULL);
621 pi_->param_views_.back() = new ParamView();
622 return pi_->param_views_.back();
623 }
624
625 /* build で構築された GUI は plugin のインスタンスには紐づかない.
626 * 通常一度だけ呼ばれ使い回される. */
build(ParamsPageSet * pages)627 void RasterFxPluginHost::build(ParamsPageSet *pages) {
628 printf(">>>> RasterFxPluginHost::build: ui_pages:%d\n",
629 (int)pi_->ui_pages_.size());
630 for (std::size_t i = 0, size = pi_->ui_pages_.size(); i < size; ++i) {
631 pi_->ui_pages_[i]->build(this, pages);
632 }
633 auto aboutpage = pages->createParamsPage();
634
635 #if 1
636 /* FIXME: fxsettings で大きさの測定のためにいろいろやっているので使える
637 layout/widget に制限がありそう.
638 しかしなぜか最後の widget しか出ない */
639 aboutpage->beginGroup("Name");
640 aboutpage->addWidget(new QLabel(pi_->desc_->name_.c_str(), aboutpage));
641 aboutpage->endGroup();
642 aboutpage->beginGroup("Vendor");
643 aboutpage->addWidget(new QLabel(pi_->desc_->vendor_.c_str(), aboutpage));
644 aboutpage->endGroup();
645 aboutpage->beginGroup("Version");
646 auto version =
647 QString::fromStdString(std::to_string(pi_->desc_->plugin_ver_.major)) +
648 "." +
649 QString::fromStdString(std::to_string(pi_->desc_->plugin_ver_.minor));
650 aboutpage->addWidget(new QLabel(version, aboutpage));
651 aboutpage->endGroup();
652 aboutpage->beginGroup("Note");
653 aboutpage->addWidget(new QLabel(pi_->desc_->note_.c_str()), aboutpage);
654 aboutpage->endGroup();
655 #endif
656 pages->addParamsPage(aboutpage, "Version");
657 aboutpage->setPageSpace();
658 }
659
660 template <typename T, uint32_t compat_maj, uint32_t compat_min>
is_compatible(const T & d)661 static inline bool is_compatible(const T &d) {
662 return d.ver.major == compat_maj && d.ver.minor == compat_min;
663 }
664
665 template <uint32_t compat_maj, uint32_t compat_min>
is_compatible(const toonz_if_version_t & v)666 static inline bool is_compatible(const toonz_if_version_t &v) {
667 return v.major == compat_maj && v.minor == compat_min;
668 }
669
check_base_sanity(const toonz_param_page_t * p)670 static int check_base_sanity(const toonz_param_page_t *p) {
671 int err = 0;
672 if (!is_compatible<toonz_param_base_t_, 1, 0>(p->base))
673 err |= TOONZ_PARAM_ERROR_VERSION;
674 if (p->base.label == NULL) err |= TOONZ_PARAM_ERROR_LABEL;
675 if (p->base.type != TOONZ_PARAM_DESC_TYPE_PAGE) err |= TOONZ_PARAM_ERROR_TYPE;
676 return err;
677 }
678
check_base_sanity(const toonz_param_group_t * p)679 static int check_base_sanity(const toonz_param_group_t *p) {
680 int err = 0;
681 if (!is_compatible<toonz_param_base_t_, 1, 0>(p->base))
682 err |= TOONZ_PARAM_ERROR_VERSION;
683 if (p->base.label == NULL) err |= TOONZ_PARAM_ERROR_LABEL;
684 if (p->base.type != TOONZ_PARAM_DESC_TYPE_GROUP)
685 err |= TOONZ_PARAM_ERROR_TYPE;
686 return err;
687 }
688
check_base_sanity(const toonz_param_desc_t * p)689 static int check_base_sanity(const toonz_param_desc_t *p) {
690 int err = 0;
691 if (!is_compatible<toonz_param_base_t_, 1, 0>(p->base))
692 err |= TOONZ_PARAM_ERROR_VERSION;
693 if (p->base.label == NULL) err |= TOONZ_PARAM_ERROR_LABEL;
694 if (p->base.type != TOONZ_PARAM_DESC_TYPE_PARAM)
695 err |= TOONZ_PARAM_ERROR_TYPE;
696 return err;
697 }
698
setParamStructure(int n,toonz_param_page_t * p,int & err,void * & pos)699 bool RasterFxPluginHost::setParamStructure(int n, toonz_param_page_t *p,
700 int &err, void *&pos) {
701 /* 適当に現実的な最大値: あまりに大きい場合は領域の破壊を疑う */
702 static const int max_pages_ = 31;
703 static const int max_groups_ = 32;
704 static const int max_params_ = 65535;
705
706 pos = p;
707 if (pi_) {
708 if (n > max_pages_ || p == NULL) {
709 /* parameter が null
710 * でないことは上位でチェックされているはずで、ここで返せるエラーは定義していない.
711 */
712 if (p == NULL) err |= TOONZ_PARAM_ERROR_UNKNOWN;
713 err |= TOONZ_PARAM_ERROR_PAGE_NUM;
714 return false;
715 }
716
717 /* SAN 値チェック */
718 for (int k = 0; k < n; k++) {
719 toonz_param_page_t *pg = &p[k];
720 pos = pg;
721 if (int e = check_base_sanity(pg)) {
722 err = e;
723 return false;
724 }
725 if (pg->num > max_groups_) {
726 err |= TOONZ_PARAM_ERROR_GROUP_NUM;
727 return false;
728 }
729
730 for (int l = 0; l < pg->num; l++) {
731 toonz_param_group_t *grp = &pg->array[l];
732 pos = grp;
733 if (int e = check_base_sanity(grp)) {
734 err = e;
735 return false;
736 }
737 if (grp->num > max_params_) {
738 err |= TOONZ_PARAM_ERROR_GROUP_NUM;
739 return false;
740 }
741 for (int i = 0; i < grp->num; i++) {
742 toonz_param_desc_t *desc = &grp->array[i];
743 pos = desc;
744 if (int e = check_base_sanity(desc)) err |= e;
745 if (desc->key == NULL)
746 err |= TOONZ_PARAM_ERROR_NO_KEY;
747 else {
748 if (!validateKeyName(desc->key)) err |= TOONZ_PARAM_ERROR_KEY_NAME;
749 for (auto it : params_) {
750 if (it->name() == desc->key) {
751 err |= TOONZ_PARAM_ERROR_KEY_DUP;
752 break;
753 }
754 }
755 }
756 if (desc->reserved_[0] ||
757 desc->reserved_[1]) // reserved fields must be zero
758 err |= TOONZ_PARAM_ERROR_POLLUTED;
759 err |= check_traits_sanity(desc);
760
761 if (err) return false;
762 }
763 }
764 }
765
766 if (err) return false;
767
768 // deep copy param resources
769 std::vector<std::shared_ptr<void>> ¶m_resources = pi_->param_resources_;
770 std::vector<std::shared_ptr<std::string>> &strtbl = pi_->param_string_tbl_;
771
772 auto patch_string = [&](const char *srcstr) {
773 strtbl.push_back(std::shared_ptr<std::string>(new std::string("")));
774 if (srcstr) strtbl.back()->assign(srcstr);
775 return strtbl.back()->c_str();
776 };
777
778 auto deep_copy_base = [&](toonz_param_base_t_ &dst,
779 const toonz_param_base_t_ &src) {
780 dst.ver = src.ver;
781 dst.type = src.type;
782 dst.label = patch_string(src.label);
783 };
784
785 param_resources.clear();
786
787 std::unique_ptr<toonz_param_page_t[]> origin_pages(
788 new toonz_param_page_t[n]);
789 for (int i = 0; i < n; i++) {
790 toonz_param_page_t &dst_page = origin_pages[i];
791 const toonz_param_page_t &src_page = p[i];
792
793 deep_copy_base(dst_page.base, src_page.base);
794
795 const int group_num = dst_page.num = src_page.num;
796 dst_page.array = new toonz_param_group_t[group_num];
797
798 for (int j = 0; j < group_num; j++) {
799 toonz_param_group_t &dst_group = dst_page.array[j];
800 const toonz_param_group_t &src_group = src_page.array[j];
801
802 deep_copy_base(dst_group.base, src_group.base);
803
804 const int desc_num = dst_group.num = src_group.num;
805 dst_group.array = new toonz_param_desc_t[desc_num];
806 for (int k = 0; k < desc_num; k++) {
807 toonz_param_desc_t &dst_desc = dst_group.array[k];
808 const toonz_param_desc_t &src_desc = src_group.array[k];
809
810 deep_copy_base(dst_desc.base, src_desc.base); // base
811
812 dst_desc.key = patch_string(src_desc.key); // key
813 dst_desc.note = patch_string(src_desc.note); // note
814 memcpy(dst_desc.reserved_, src_desc.reserved_,
815 sizeof(src_desc.reserved_)); // reserved fields
816 dst_desc.traits_tag = src_desc.traits_tag; // tag
817
818 // traits
819 if (dst_desc.traits_tag == TOONZ_PARAM_TYPE_ENUM) {
820 dst_desc.traits.e.def = src_desc.traits.e.def;
821 int enums = dst_desc.traits.e.enums = src_desc.traits.e.enums;
822 auto a = std::shared_ptr<void>(new char *[enums]);
823 param_resources.push_back(a);
824 dst_desc.traits.e.array = static_cast<const char **>(a.get());
825 for (int i = 0; i < enums; i++)
826 dst_desc.traits.e.array[i] =
827 patch_string(src_desc.traits.e.array[i]);
828 } else if (dst_desc.traits_tag == TOONZ_PARAM_TYPE_SPECTRUM) {
829 int points = dst_desc.traits.g.points = src_desc.traits.g.points;
830 auto ptr = std::shared_ptr<void>(
831 new toonz_param_traits_spectrum_t::valuetype[points]);
832 param_resources.push_back(ptr);
833 dst_desc.traits.g.array =
834 static_cast<toonz_param_traits_spectrum_t::valuetype *>(
835 ptr.get());
836
837 for (int i = 0; i < dst_desc.traits.g.points; i++)
838 memcpy(&dst_desc.traits.g.array[i], &src_desc.traits.g.array[i],
839 sizeof(toonz_param_traits_spectrum_t::valuetype));
840 } else if (dst_desc.traits_tag == TOONZ_PARAM_TYPE_STRING) {
841 dst_desc.traits.s.def = patch_string(src_desc.traits.s.def);
842 } else if (dst_desc.traits_tag == TOONZ_PARAM_TYPE_TONECURVE) {
843 /* has no default values */
844 } else {
845 memcpy(&dst_desc.traits, &src_desc.traits, sizeof(src_desc.traits));
846 }
847 }
848 }
849 }
850
851 pi_->param_page_num_ = n;
852 pi_->param_pages_ = std::move(origin_pages);
853 return true;
854 }
855 return false;
856 }
857
createParamsByDesc()858 void RasterFxPluginHost::createParamsByDesc() {
859 printf("RasterFxPluginHost::createParamsByDesc: num:%d\n",
860 pi_->param_page_num_);
861 for (int k = 0; k < pi_->param_page_num_; k++) {
862 toonz_param_page_t *pg = &pi_->param_pages_[k];
863 void *page = NULL;
864 int r = add_preference(this, pg->base.label, &page);
865 printf(
866 "RasterFxPluginHost::createParamsByDesc: add_preference: r:0x%x "
867 "page:%p\n",
868 r, page);
869
870 for (int l = 0; l < pg->num; l++) {
871 toonz_param_group_t *grp = &pg->array[l];
872 begin_group(page, grp->base.label);
873
874 for (int i = 0; i < grp->num; i++) {
875 toonz_param_desc_t *desc = &grp->array[i];
876 Param *p = createParam(desc);
877 printf("RasterFxPluginHost::createParam: p:%p key:%s tag:%d\n", p,
878 desc->key, desc->traits_tag);
879 if (p) {
880 void *v = NULL;
881 int r = create_param_view(this, &v);
882 printf(
883 "RasterFxPluginHost::createParam: create_param_view: r:0x%x "
884 "v:%p\n",
885 r, v);
886 r = add_param_field(v, NULL);
887 printf(
888 "RasterFxPluginHost::createParam: add_param_field: r:0x%x v:%p "
889 "p:%p\n",
890 r, v, p);
891 /* set_param_range()
892 * の中で型チェックをしているので全型について呼び出してよい */
893
894 r = bind_param(page, p, v);
895
896 set_param_default<tpbind_dbl_t>(p, desc);
897 set_param_default<tpbind_int_t>(p, desc);
898 set_param_default<tpbind_rng_t>(p, desc);
899 set_param_default<tpbind_pnt_t>(p, desc);
900 set_param_default<tpbind_enm_t>(p, desc);
901 set_param_default<tpbind_col_t>(p, desc);
902 set_param_default<tpbind_bool_t>(p, desc);
903 set_param_default<tpbind_str_t>(p, desc);
904 set_param_default<tpbind_spc_t>(p, desc);
905 set_param_default<tpbind_tcv_t>(p, desc);
906
907 set_param_range<tpbind_dbl_t>(p, desc);
908 set_param_range<tpbind_int_t>(p, desc);
909 set_param_range<tpbind_rng_t>(p, desc);
910 set_param_range<tpbind_pnt_t>(p, desc);
911
912 printf("RasterFxPluginHost::createParam: bind_param: r:0x%x\n", r);
913 }
914 }
915 end_group(page, grp->base.label);
916 }
917 }
918 }
919
920 /*
921 パラメタのキー名として適切か確認
922 */
validateKeyName(const char * name)923 bool RasterFxPluginHost::validateKeyName(const char *name) {
924 if (name[0] == '\0') return false;
925 if (!isalpha(name[0]) && name[0] != '_') return false;
926
927 for (int i = 1; name[i] != '\0'; i++)
928 if (!isalnum(name[i]) && name[i] != '_') return false;
929
930 /* XMLの仕様ではXMLから始まるタグ名は認められないので、ここで弾く */
931 if (strlen(name) >= 3 && (name[0] == 'X' || name[0] == 'x') &&
932 (name[1] == 'M' || name[1] == 'm') && (name[2] == 'L' || name[2] == 'l'))
933 return false;
934
935 return true;
936 }
937
938 /*
939 strict sanity check:
940 未初期化値を受け入れて互換性が崩れないよう厳しくチェックする
941 */
942 #define VERBOSE
check(const plugin_probe_t * begin,const plugin_probe_t * end)943 static inline bool check(const plugin_probe_t *begin,
944 const plugin_probe_t *end) {
945 /*
946 printf("dump toonz_plugin_probe_t: ver:(%d, %d) (%s, %s, %s, %s) resv:[%p, %p,
947 %p, %p, %p] clss:0x%x resv:[%d, %d, %d, %d, %d]\n",
948 x->ver.major, x->ver.minor,
949 x->name, x->id, x->note, x->url,
950 x->reserved_ptr_[0], x->reserved_ptr_[1], x->reserved_ptr_[2],
951 x->reserved_ptr_[3], x->reserved_ptr_[4],
952 x->clss,
953 x->reserved_int_[0], x->reserved_int_[1], x->reserved_int_[2],
954 x->reserved_int_[3], x->reserved_int_[4], x->reserved_int_[5],
955 x->reserved_int_[6], x->reserved_int_[7]);
956 */
957 int idx = 0;
958 if (!is_compatible<plugin_probe_t, 1, 0>(*begin)) {
959 #if defined(VERBOSE)
960 printf("sanity check(): first interface version is unknown\n");
961 #endif
962 return false;
963 }
964
965 toonz_if_version_t v = begin->ver;
966 for (auto x = begin; x < end; x++, idx++) {
967 /* 異なるバージョンの構造体の混在はエラーとする.
968 しかし toonz_plugin_probe_t は reservation filed
969 を持っており、サイズが変わらない限りは混在も対応可能だが、まずは sanity
970 check で落とす.
971
972 For now we permit mixed versions. Not that we never support it since size
973 of toonz_plugin_probe_t is constant.
974 */
975 if (!(x->ver.major == v.major && x->ver.minor == v.minor)) {
976 #if defined(VERBOSE)
977 printf(
978 "sanity check(): versions are ambiguous: first:(%d, %d) "
979 "plugin[%d]:(%d, %d)\n",
980 v.major, v.minor, idx, x->ver.major, x->ver.minor);
981 #endif
982 return false;
983 }
984
985 if (!x->clss) {
986 #if defined(VERBOSE)
987 printf("sanity check(): plugin[%d] class is zero\n", idx);
988 #endif
989 return false;
990 } else {
991 uint32_t m = x->clss & TOONZ_PLUGIN_CLASS_MODIFIER_MASK;
992 uint32_t c = x->clss & ~TOONZ_PLUGIN_CLASS_MODIFIER_MASK;
993 if (!(m == 0 || m == TOONZ_PLUGIN_CLASS_MODIFIER_GEOMETRIC)) {
994 #if defined(VERBOSE)
995 printf("sanity check(): plugin[%d] unknown modifier: 0x%x\n", idx, m);
996 #endif
997 return false; // we don't know the modifier
998 }
999 if (!(c == TOONZ_PLUGIN_CLASS_POSTPROCESS_SLAB)) {
1000 #if defined(VERBOSE)
1001 printf("sanity check(): plugin[%d] unknown class: 0x%x\n", idx, c);
1002 #endif
1003 return false; // we don't know the class
1004 }
1005 }
1006
1007 // reservations are must be zero
1008 for (int i = 0; i < 3; i++)
1009 if (x->reserved_ptr_[i]) {
1010 #if defined(VERBOSE)
1011 printf(
1012 "sanity check(): plugin[%d] reserved_ptr_[%d] is NOT all zero-ed\n",
1013 idx, i);
1014 #endif
1015 return false;
1016 }
1017 for (int i = 0; i < 7; i++)
1018 if (x->reserved_int_[i]) {
1019 #if defined(VERBOSE)
1020 printf(
1021 "sanity check(): plugin[%d] reserved_int_[%d] is NOT all zero-ed\n",
1022 idx, i);
1023 #endif
1024 return false;
1025 }
1026 for (int i = 0; i < 3; i++)
1027 if (x->reserved_ptr_trail_[i]) {
1028 #if defined(VERBOSE)
1029 printf(
1030 "sanity check(): plugin[%d] reserved_ptr_trail_[%d] is NOT all "
1031 "zero-ed\n",
1032 idx, i);
1033 #endif
1034 return false;
1035 }
1036
1037 if (x->handler == NULL) {
1038 #if defined(VERBOSE)
1039 printf("sanity check(): plugin[%d] handler is null\n", idx);
1040 #endif
1041 return false; // handler must be NOT null
1042 }
1043 }
1044
1045 // check if the end is empty
1046 const char *b = reinterpret_cast<const char *>(end);
1047 for (int i = 0; i < sizeof(plugin_probe_t); i++) {
1048 if (b[i] != 0) {
1049 #if defined(VERBOSE)
1050 printf("sanity check(): empty is NOT all zero-ed\n");
1051 #endif
1052 return false; // must be zero
1053 }
1054 }
1055 return true;
1056 }
1057
check_and_copy(nodal_rasterfx_handler_t * __restrict dst,const nodal_rasterfx_handler_t * __restrict src)1058 static inline bool check_and_copy(
1059 nodal_rasterfx_handler_t *__restrict dst,
1060 const nodal_rasterfx_handler_t *__restrict src) {
1061 // do we know the version?
1062 if (!(src->ver.major == 1 && src->ver.minor == 0)) return false;
1063 dst->ver = src->ver;
1064 dst->do_compute = src->do_compute;
1065 dst->do_get_bbox = src->do_get_bbox;
1066 dst->can_handle = src->can_handle;
1067 dst->get_memory_requirement = src->get_memory_requirement;
1068 dst->on_new_frame = src->on_new_frame;
1069 dst->on_end_frame = src->on_end_frame;
1070 dst->create = src->create;
1071 dst->destroy = src->destroy;
1072 dst->setup = src->setup;
1073 dst->start_render = src->start_render;
1074 dst->end_render = src->end_render;
1075 return true;
1076 }
1077
uuid_matches(const UUID * x,const UUID * y)1078 static inline bool uuid_matches(const UUID *x, const UUID *y) {
1079 return x->uid0 == y->uid0 && x->uid1 == y->uid1 && x->uid2 == y->uid2 &&
1080 x->uid3 == y->uid3 && x->uid4 == y->uid4;
1081 }
1082
1083 static UUID uuid_nodal_ = {0xCC14EA21, 0x13D8, 0x4A3B, 0x9375, 0xAA4F68C9DDDD};
1084 static UUID uuid_port_ = {0x2F89A423, 0x1D2D, 0x433F, 0xB93E, 0xCFFD83745F6F};
1085 static UUID uuid_tile_ = {0x882BD525, 0x937E, 0x427C, 0x9D68, 0x4ECA651F6562};
1086 // static UUID uuid_ui_page_ = {0xD2EF0310, 0x3414, 0x4753, 0x84CA,
1087 // 0xD5447C70DD89};
1088 static UUID uuid_fx_node_ = {0x26F9FC53, 0x632B, 0x422F, 0x87A0,
1089 0x8A4547F55474};
1090 // static UUID uuid_param_view_ = {0x5133A63A, 0xDD92, 0x41BD, 0xA255,
1091 // 0x6F97BE7292EA};
1092 static UUID uuid_param_ = {0x2E3E4A55, 0x8539, 0x4520, 0xA266, 0x15D32189EC4D};
1093 static UUID uuid_setup_ = {0xcfde9107, 0xc59d, 0x414c, 0xae4a, 0x3d115ba97933};
1094 static UUID uuid_null_ = {0, 0, 0, 0, 0};
1095
1096 template <typename T, int major, int minor>
1097 T *base_interface_factory() {
1098 T *t = new T;
1099 memset(t, 0, sizeof(T));
1100 t->ver.major = major;
1101 t->ver.minor = minor;
1102 return t;
1103 }
1104
1105 template <typename T, uint32_t major, uint32_t minor>
1106 struct interface_t {
factoryinterface_t1107 static T *factory() {
1108 T *t = base_interface_factory<T, major, minor>();
1109 return t;
1110 }
1111 };
1112
1113 extern "C" {
1114 int set_parameter_pages(toonz_node_handle_t, int num,
1115 toonz_param_page_t *params);
1116 int set_parameter_pages_with_error(toonz_node_handle_t, int num,
1117 toonz_param_page_t *params, int *, void **);
1118 }
1119
1120 template <uint32_t major, uint32_t minor>
1121 struct interface_t<setup_interface_t, major, minor> {
factoryinterface_t1122 static setup_interface_t *factory() {
1123 setup_interface_t *t =
1124 base_interface_factory<setup_interface_t, major, minor>();
1125 t->set_parameter_pages = set_parameter_pages;
1126 t->set_parameter_pages_with_error = set_parameter_pages_with_error;
1127 t->add_input_port = setup_input_port;
1128 return t;
1129 }
1130 };
1131
1132 /*
1133 template < uint32_t major, uint32_t minor >
1134 struct interface_t < ui_page_interface_t, major, minor > {
1135 static ui_page_interface_t* factory()
1136 {
1137 ui_page_interface_t* t = base_interface_factory<
1138 ui_page_interface_t, major, minor >();
1139 t->begin_group = begin_group;
1140 t->end_group = end_group;
1141 t->bind_param = bind_param;
1142 return t;
1143 }
1144 };
1145
1146 template < uint32_t major, uint32_t minor >
1147 struct interface_t < param_view_interface_t, major, minor > {
1148 static param_view_interface_t* factory()
1149 {
1150 param_view_interface_t* t = base_interface_factory<
1151 param_view_interface_t, major, minor >();
1152 t->add_param_field = add_param_field;
1153 t->add_custom_field = add_custom_field;
1154 t->add_lineedit = add_lineedit;
1155 t->add_slider = add_slider;
1156 t->add_spinbox = add_spinbox;
1157 t->add_checkbox = add_checkbox;
1158 t->add_radiobutton = add_radiobutton;
1159 t->add_combobox = add_combobox;
1160 return t;
1161 }
1162 };
1163 */
1164
1165 template <uint32_t major, uint32_t minor>
1166 struct interface_t<param_interface_t, major, minor> {
factoryinterface_t1167 static param_interface_t *factory() {
1168 param_interface_t *t =
1169 base_interface_factory<param_interface_t, major, minor>();
1170 t->get_type = get_type;
1171 // t->hint_default_value = hint_default_value;
1172 // t->hint_value_range = hint_value_range;
1173 // t->hint_unit = hint_unit;
1174 // t->hint_item = hint_item;
1175 // t->get_value_type = get_value_type;
1176 t->get_value = get_value;
1177 // t->set_value = set_value;
1178 t->get_string_value = get_string_value;
1179 t->get_spectrum_value = get_spectrum_value;
1180 return t;
1181 }
1182 };
1183
1184 template <uint32_t major, uint32_t minor>
1185 struct interface_t<node_interface_t, major, minor> {
factoryinterface_t1186 static node_interface_t *factory() {
1187 node_interface_t *t =
1188 base_interface_factory<node_interface_t, major, minor>();
1189 // t->add_input_port = add_input_port;
1190 // t->add_output_port = add_output_port;
1191 t->get_input_port = get_input_port;
1192 t->get_rect = get_rect;
1193 t->set_rect = set_rect;
1194 // t->add_preference = add_preference;
1195 // t->add_param = add_param;
1196 t->get_param = get_param;
1197 // t->create_param_view = create_param_view;
1198 t->set_user_data = set_user_data;
1199 t->get_user_data = get_user_data;
1200 return t;
1201 }
1202 };
1203
1204 template <uint32_t major, uint32_t minor>
1205 struct interface_t<toonz_tile_interface_t, major, minor> {
factoryinterface_t1206 static toonz_tile_interface_t *factory() {
1207 toonz_tile_interface_t *t =
1208 base_interface_factory<toonz_tile_interface_t, major, minor>();
1209 t->get_raw_address_unsafe = tile_interface_get_raw_address_unsafe;
1210 t->get_raw_stride = tile_interface_get_raw_stride;
1211 t->get_element_type = tile_interface_get_element_type;
1212 t->copy_rect = tile_interface_copy_rect;
1213 t->create_from = tile_interface_create_from;
1214 t->create = tile_interface_create;
1215 t->destroy = tile_interface_destroy;
1216 t->get_rectangle = tile_interface_get_rectangle;
1217 t->safen = tile_interface_safen;
1218 return t;
1219 }
1220 };
1221
1222 template <uint32_t major, uint32_t minor>
1223 struct interface_t<port_interface_t, major, minor> {
factoryinterface_t1224 static port_interface_t *factory() {
1225 port_interface_t *t =
1226 base_interface_factory<port_interface_t, major, minor>();
1227 t->is_connected = is_connected;
1228 t->get_fx = get_fx;
1229 return t;
1230 }
1231 };
1232
1233 template <uint32_t major, uint32_t minor>
1234 struct interface_t<fxnode_interface_t, major, minor> {
factoryinterface_t1235 static fxnode_interface_t *factory() {
1236 fxnode_interface_t *t =
1237 base_interface_factory<fxnode_interface_t, major, minor>();
1238 t->get_bbox = fxnode_get_bbox;
1239 t->can_handle = fxnode_can_handle;
1240 t->get_input_port_count = fxnode_get_input_port_count;
1241 t->get_input_port = fxnode_get_input_port;
1242 t->compute_to_tile = fxnode_compute_to_tile;
1243 return t;
1244 }
1245 };
1246
1247 /*
1248 template <>
1249 template < uint32_t major, uint32_t minor >
1250 struct interface_t< toonz_nodal_rasterfx_interface_t >::i_< major, minor > {
1251 static toonz_nodal_rasterfx_interface_t* factory()
1252 {
1253 printf("toonz_nodal_rasterfx_interface_t::factory\n");
1254 toonz_nodal_rasterfx_interface_t* t = base_interface_factory<
1255 toonz_nodal_rasterfx_interface_t, major, minor >();
1256 return t;
1257 }
1258 };
1259 */
1260
1261 template <typename T, uint32_t major, uint32_t minor>
1262 T *interface_factory() {
1263 // return interface_t< T >::i_< major, minor >().factory();
1264 return interface_t<T, major, minor>::factory();
1265 }
1266
query_interface(const UUID * uuid,void ** interf)1267 static int query_interface(const UUID *uuid, void **interf) {
1268 typedef std::pair<const UUID *, int> uuid_dict_t;
1269 static const uuid_dict_t dict[] = {
1270 uuid_dict_t(&uuid_nodal_, 1), uuid_dict_t(&uuid_port_, 2),
1271 uuid_dict_t(&uuid_tile_, 3),
1272 // uuid_dict_t(&uuid_ui_page_, 4),
1273 uuid_dict_t(&uuid_fx_node_, 5),
1274 // uuid_dict_t(&uuid_param_view_, 6),
1275 uuid_dict_t(&uuid_param_, 7), uuid_dict_t(&uuid_setup_, 8),
1276 uuid_dict_t(&uuid_null_, 0)};
1277
1278 if (!(uuid && interf)) return TOONZ_ERROR_NULL;
1279
1280 try {
1281 const uuid_dict_t *it = &dict[0];
1282 while (it->first != &uuid_null_) {
1283 if (uuid_matches(it->first, uuid)) {
1284 switch (it->second) {
1285 case 1:
1286 *interf = interface_factory<toonz_node_interface_t, 1, 0>();
1287 break;
1288 case 2:
1289 *interf = interface_factory<toonz_port_interface_t, 1, 0>();
1290 break;
1291 case 3:
1292 *interf = interface_factory<toonz_tile_interface_t, 1, 0>();
1293 break;
1294 // case 4:
1295 // *interf = interface_factory< toonz_ui_page_interface_t, 1, 0
1296 //>();
1297 // break;
1298 case 5:
1299 *interf = interface_factory<toonz_fxnode_interface_t, 1, 0>();
1300 break;
1301 // case 6:
1302 //*interf = interface_factory< toonz_param_view_interface_t, 1, 0 >();
1303 // break;
1304 case 7:
1305 *interf = interface_factory<toonz_param_interface_t, 1, 0>();
1306 break;
1307 case 8:
1308 *interf = interface_factory<toonz_setup_interface_t, 1, 0>();
1309 break;
1310 default:
1311 return TOONZ_ERROR_NOT_IMPLEMENTED;
1312 break;
1313 }
1314 }
1315 it++;
1316 }
1317 } catch (const std::bad_alloc &) {
1318 return TOONZ_ERROR_OUT_OF_MEMORY;
1319 } catch (const std::exception &) {
1320 return TOONZ_ERROR_UNKNOWN;
1321 }
1322
1323 return TOONZ_OK;
1324 }
1325
release_interface(void * interf)1326 static void release_interface(void *interf) {
1327 if (interf) delete interf;
1328 }
1329
Loader()1330 Loader::Loader() {}
1331
walkDirectory(const QString & path)1332 void Loader::walkDirectory(const QString &path) {
1333 walkDirectory_(path);
1334 emit fixup();
1335 }
1336
walkDictionary(const QString & path)1337 void Loader::walkDictionary(const QString &path) {
1338 /* only for emitting a signal for fixup */
1339 printf("walkDictionary: %s [dry]\n", path.toLocal8Bit().data());
1340 emit fixup();
1341 }
1342
walkDirectory_(const QString & path)1343 void Loader::walkDirectory_(const QString &path) {
1344 printf("walkDirectory_: %s\n", path.toLocal8Bit().data());
1345 QDir dir(path, QString::fromStdString("*.plugin"), QDir::Name,
1346 QDir::AllDirs | QDir::Files | QDir::NoDot | QDir::NoDotDot);
1347 auto lst = dir.entryInfoList();
1348 for (auto &e : lst) {
1349 if (e.isDir()) {
1350 walkDirectory_(e.filePath());
1351 } else if (e.isFile()) { // file or symlink-to-file
1352 doLoad(e.filePath());
1353 }
1354 }
1355 }
1356
1357 #if defined(_WIN32) || defined(_CYGWIN_)
end_library(HMODULE mod)1358 static void end_library(HMODULE mod) { FreeLibrary(mod); }
1359 #else
end_library(void * mod)1360 static void end_library(void *mod) { dlclose(mod); }
1361 #endif
1362
doLoad(const QString & file)1363 void Loader::doLoad(const QString &file) {
1364 #if defined(_WIN32) || defined(_CYGWIN_)
1365 HMODULE handle = LoadLibraryA(file.toLocal8Bit().data());
1366 printf("doLoad handle:%p path:%s\n", handle, file.toLocal8Bit().data());
1367 #else
1368 void *handle = dlopen(file.toUtf8().data(), RTLD_LOCAL);
1369 printf("doLoad handle:%p path:%s\n", handle, file.toUtf8().data());
1370 #endif
1371 PluginInformation *pi = new PluginInformation;
1372 if (handle) {
1373 pi->library_ = library_t(handle, end_library); // shared_ptr
1374 /*
1375 probe に使う plugin 情報を探す.
1376 テーブルを export
1377 したほうが楽だが、開発者はデバッグしにくいので関数フォームも提供する.
1378 toonz_plugin_info で検索し、なければ toonz_plugin_probe() を呼び出す.
1379 */
1380 #if defined(_WIN32) || defined(_CYGWIN_)
1381 auto ini = (int (*)(host_interface_t *))GetProcAddress(handle,
1382 "toonz_plugin_init");
1383 auto fin = (void (*)(void))GetProcAddress(handle,
1384 "toonz_plugin_exit"); // optional
1385 const plugin_probe_list_t *problist =
1386 reinterpret_cast<const plugin_probe_list_t *>(
1387 GetProcAddress(handle, "toonz_plugin_info_list"));
1388 #else
1389 auto ini = (int (*)(host_interface_t *))dlsym(handle, "toonz_plugin_init");
1390 auto fin = (void (*)(void))dlsym(handle, "toonz_plugin_exit"); // optional
1391 const plugin_probe_list_t *problist =
1392 reinterpret_cast<const plugin_probe_list_t *>(
1393 dlsym(handle, "toonz_plugin_info_list"));
1394 #endif
1395 pi->ini_ = ini;
1396 pi->fin_ = fin;
1397
1398 const plugin_probe_t *probinfo_begin = NULL;
1399 const plugin_probe_t *probinfo_end = NULL;
1400 try {
1401 if (problist) {
1402 if (!is_compatible<plugin_probe_list_t, 1, 0>(*problist))
1403 throw std::domain_error(
1404 "invaid toonz_plugin_info_list: version unmatched");
1405 probinfo_begin = problist->begin;
1406 probinfo_end = problist->end;
1407 }
1408
1409 if (!probinfo_begin || !probinfo_end) {
1410 printf("use function-formed prober:toonz_plugin_probe\n");
1411 // look at function-formed
1412 #if defined(_WIN32) || defined(_CYGWIN_)
1413 void *probe = GetProcAddress(handle, "toonz_plugin_probe");
1414 #else
1415 void *probe = dlsym(handle, "toonz_plugin_probe");
1416 #endif
1417 if (probe) {
1418 printf("function-formed prober found\n");
1419 const plugin_probe_list_t *lst =
1420 (reinterpret_cast<const plugin_probe_list_t *(*)(void)>(probe))();
1421 if (!lst || !is_compatible<plugin_probe_list_t, 1, 0>(*lst))
1422 throw std::domain_error("invalid plugin list");
1423 plugin_probe_t *begin = lst->begin;
1424 plugin_probe_t *end = lst->end;
1425 if (!begin || !end)
1426 throw std::domain_error(
1427 "invalid plugin information address (begin or end is null)");
1428 else if (begin >= end)
1429 throw std::domain_error(
1430 "invalid plugin information address (begin >= end)");
1431 else if (begin == end - 1)
1432 throw std::domain_error(
1433 "invalid plugin information address (information is empty)");
1434 probinfo_begin = begin;
1435 probinfo_end = end;
1436 } else {
1437 throw std::domain_error(
1438 "found toonz_plugin_probe nor toonz_plugin_info");
1439 }
1440 } else {
1441 }
1442 int plugin_num = probinfo_end - probinfo_begin;
1443 printf("plugin count:%d begin:%p end:%p\n", plugin_num, probinfo_begin,
1444 probinfo_end);
1445
1446 /* sanity check に失敗した場合は予期せぬアドレスを参照して toonz
1447 本体ごと落ちる可能性があるので
1448 致命的エラー扱いで早期に抜ける. */
1449 if (!probinfo_begin || !probinfo_end ||
1450 !check(probinfo_begin, probinfo_end))
1451 throw std::domain_error("ill-formed plugin information");
1452
1453 for (const plugin_probe_t *probinfo = probinfo_begin;
1454 probinfo < probinfo_end; probinfo++) {
1455 pi->desc_ = new PluginDescription(probinfo);
1456 nodal_rasterfx_handler_t *nodal = probinfo->handler;
1457 /* probinfo は sanity check 通過済みなのでチェック不要. handler は null
1458 * でないことのみが確認されている */
1459 if (is_compatible<nodal_rasterfx_handler_t, 1, 0>(*nodal)) {
1460 uint32_t c = probinfo->clss & ~(TOONZ_PLUGIN_CLASS_MODIFIER_MASK);
1461 uint32_t m = probinfo->clss & (TOONZ_PLUGIN_CLASS_MODIFIER_MASK);
1462 if (c == TOONZ_PLUGIN_CLASS_POSTPROCESS_SLAB) {
1463 pi->handler_ = new nodal_rasterfx_handler_t;
1464 if (!check_and_copy(pi->handler_, nodal))
1465 throw std::domain_error("ill-formed nodal interface");
1466 } else {
1467 // unknown plugin-class : gracefully end
1468 /* sanity check しているので来ないはずだ */
1469 }
1470 } else {
1471 // unknown version : gracefully end
1472 }
1473
1474 emit load_finished(pi);
1475
1476 if (pi) {
1477 try {
1478 if (pi->ini_) {
1479 /* interface は plugin 内部で破壊されても他に影響させないため
1480 * plugin instance ごとに割り当てる. */
1481 host_interface_t *host = new host_interface_t;
1482 host->ver.major = 1;
1483 host->ver.minor = 0;
1484 host->query_interface = query_interface;
1485 host->release_interface = release_interface;
1486 int ret = pi->ini_(host);
1487 if (ret) {
1488 delete host;
1489 std::domain_error(
1490 "failed initialized: error on _toonz_plugin_init");
1491 }
1492 pi->host_ = host;
1493 pi->decl_ = new PluginDeclaration(pi);
1494 } else {
1495 /* もっと早期にエラーを出して終了することもできるが
1496 センシティブすぎると見通しが立てにくいのである程度我慢してから出す
1497 */
1498 throw std::domain_error("not found _toonz_plugin_init");
1499 }
1500 } catch (const std::exception &e) {
1501 printf("Exception occured after plugin loading: %s\n", e.what());
1502 }
1503
1504 if (pi->handler_ && pi->handler_->setup) {
1505 PluginSetupMessage(pi).sendBlocking();
1506 }
1507
1508 if (probinfo + 1 < probinfo_end) {
1509 /* for a next plugin on the library */
1510 auto prev = pi->library_;
1511 pi = new PluginInformation;
1512 /* instance に依存しない unique なリソースは引き継ぐ必要がある */
1513 pi->library_ = prev;
1514 pi->ini_ = ini;
1515 pi->fin_ = fin;
1516 }
1517 }
1518 }
1519 } catch (const std::exception &e) {
1520 printf("Exception occured while plugin loading: %s\n", e.what());
1521 delete pi;
1522 pi = NULL;
1523 }
1524 }
1525 }
1526
createPortsByDesc()1527 void RasterFxPluginHost::createPortsByDesc() {
1528 if (pi_) {
1529 for (auto pm : pi_->port_mapper_) {
1530 /* TRasterFxPort は non-copyable
1531 * なスマートポインタなのでポインタで引き回す */
1532 printf("createPortsByDesc: name:%s dir:%d type:%d\n", pm.first.c_str(),
1533 pm.second.input_, pm.second.type_);
1534 if (pm.second.input_) {
1535 auto p = std::make_shared<TRasterFxPort>();
1536 if (!addInputPort(pm.first, p)) { // overloaded version
1537 printf("createPortsByDesc: failed to add: already have\n");
1538 }
1539 } else {
1540 auto p = new TRasterFxPort();
1541 /* TRasterFxPort は non-copyable
1542 * なスマートポインタなのでポインタで引き回す */
1543 if (addOutputPort(pm.first, p)) { // overloaded version
1544 delete p;
1545 printf("createPortsByDesc: failed to add: already have\n");
1546 }
1547 }
1548 }
1549 }
1550 }
1551
1552 /*
1553 TODO: addfxcontextmenu に移したほうがいい
1554 */
PluginLoadController(const std::string & basedir,QObject * listener)1555 PluginLoadController::PluginLoadController(const std::string &basedir,
1556 QObject *listener) {
1557 Loader *ld = new Loader;
1558
1559 ld->moveToThread(&work_entity);
1560 connect(&work_entity, &QThread::finished, ld, &QObject::deleteLater);
1561 /* AddFxContextMenu から呼ばれていたが、プラグインの検索が load_entries()
1562 を通じて起動時に呼ばれるようにした関係で,
1563 (あまりよくはないが)listner の有無によって receiver を分けるようにしている.
1564 listener がいる場合は従来通り context menu の構築のために
1565 AddFxContextMenu::fixup() に接続するが
1566 それ以外では plugin_dict_ への追加のため PluginLoadController::finished
1567 に接続する.
1568 */
1569 if (listener) {
1570 AddFxContextMenu *a = qobject_cast<AddFxContextMenu *>(listener);
1571 connect(ld, &Loader::fixup, a, &AddFxContextMenu::fixup);
1572 connect(this, &PluginLoadController::start, ld, &Loader::walkDictionary);
1573 } else {
1574 connect(this, &PluginLoadController::start, ld, &Loader::walkDirectory);
1575 connect(ld, &Loader::load_finished, this, &PluginLoadController::result);
1576 if (!connect(ld, &Loader::fixup, this, &PluginLoadController::finished))
1577 assert(false);
1578 }
1579 work_entity.start();
1580
1581 QString pluginbase = (TEnv::getStuffDir() + "plugins").getQString();
1582 printf("plugin search directory:%s\n", pluginbase.toLocal8Bit().data());
1583 emit start(pluginbase);
1584 }
1585
finished()1586 void PluginLoadController::finished() {
1587 printf("===== PluginLoadController::finished() =====\n");
1588 work_entity.exit();
1589 }
1590
result(PluginInformation * pi)1591 void PluginLoadController::result(PluginInformation *pi) {
1592 /* slot receives PluginInformation on the main thread たぶん */
1593 printf("PluginLoadController::result() pi:%p\n", pi);
1594 if (pi) {
1595 /* addfxcontextmenu.cpp の dict に登録する */
1596 plugin_dict_.insert(
1597 std::pair<std::string, PluginInformation *>(pi->desc_->id_, pi));
1598 }
1599 }
1600
copy_rendering_setting(toonz_rendering_setting_t * dst,const TRenderSettings & src)1601 static bool copy_rendering_setting(toonz_rendering_setting_t *dst,
1602 const TRenderSettings &src) {
1603 plugin::utils::copy_affine(&dst->affine, src.m_affine);
1604 dst->gamma = src.m_gamma;
1605 dst->time_stretch_from = src.m_timeStretchFrom;
1606 dst->time_stretch_to = src.m_timeStretchTo;
1607 dst->stereo_scopic_shift = src.m_stereoscopicShift;
1608 dst->bpp = src.m_bpp;
1609 dst->max_tile_size = src.m_maxTileSize;
1610 dst->quality = src.m_quality;
1611 dst->field_prevalence = src.m_fieldPrevalence;
1612 dst->stereoscopic = src.m_stereoscopic;
1613 dst->is_swatch = src.m_isSwatch;
1614 dst->user_cachable = src.m_userCachable;
1615 dst->apply_shrink_to_viewer = src.m_applyShrinkToViewer;
1616 dst->context = &src;
1617 dst->is_canceled = src.m_isCanceled;
1618 plugin::utils::copy_rect(&dst->camera_box, src.m_cameraBox);
1619 return true;
1620 }
1621
1622 //#include "pluginhost.moc"
1623