Fix #35561: freestyle + read full sample layers = crash.
[blender.git] / source / blender / freestyle / intern / blender_interface / FRS_freestyle.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
22  *  \ingroup freestyle
23  */
24
25 #include <iostream>
26 #include <map>
27 #include <set>
28
29 #include "../application/AppCanvas.h"
30 #include "../application/AppConfig.h"
31 #include "../application/AppView.h"
32 #include "../application/Controller.h"
33
34 using namespace std;
35 using namespace Freestyle;
36
37 extern "C" {
38
39 #include "MEM_guardedalloc.h"
40
41 #include "DNA_camera_types.h"
42 #include "DNA_freestyle_types.h"
43 #include "DNA_group_types.h"
44 #include "DNA_text_types.h"
45
46 #include "BKE_freestyle.h"
47 #include "BKE_global.h"
48 #include "BKE_library.h"
49 #include "BKE_linestyle.h"
50 #include "BKE_main.h"
51 #include "BKE_text.h"
52 #include "BKE_context.h"
53
54 #include "BLI_blenlib.h"
55 #include "BLI_math.h"
56 #include "BLI_callbacks.h"
57
58 #include "BPY_extern.h"
59
60 #include "renderpipeline.h"
61 #include "pixelblending.h"
62
63 #include "FRS_freestyle.h"
64
65 #define DEFAULT_SPHERE_RADIUS 1.0f
66 #define DEFAULT_DKR_EPSILON   0.0f
67
68 // Freestyle configuration
69 static short freestyle_is_initialized = 0;
70 static Config::Path *pathconfig = NULL;
71 static Controller *controller = NULL;
72 static AppView *view = NULL;
73
74 // line set buffer for copy & paste
75 static FreestyleLineSet lineset_buffer;
76 static bool lineset_copied = false;
77
78 // camera information
79 float freestyle_viewpoint[3];
80 float freestyle_mv[4][4];
81 float freestyle_proj[4][4];
82 int freestyle_viewport[4];
83
84 // current scene
85 Scene *freestyle_scene;
86
87 static string default_module_path;
88
89 static void load_post_callback(struct Main *main, struct ID *id, void *arg)
90 {
91         lineset_copied = false;
92 }
93
94 static bCallbackFuncStore load_post_callback_funcstore = {
95         NULL, NULL, /* next, prev */
96         load_post_callback, /* func */
97         NULL, /* arg */
98         0 /* alloc */
99 };
100
101 //=======================================================
102 //   Initialization 
103 //=======================================================
104
105 void FRS_initialize()
106 {
107         if (freestyle_is_initialized)
108                 return;
109
110         pathconfig = new Config::Path;
111         controller = new Controller();
112         view = new AppView;
113         controller->setView(view);
114         controller->Clear();
115         freestyle_scene = NULL;
116         lineset_copied = false;
117
118         default_module_path = pathconfig->getProjectDir() + Config::DIR_SEP + "style_modules" +
119                               Config::DIR_SEP + "contour.py";
120
121         BLI_callback_add(&load_post_callback_funcstore, BLI_CB_EVT_LOAD_POST);
122
123         freestyle_is_initialized = 1;
124 }
125
126 void FRS_set_context(bContext *C)
127 {
128         if (G.debug & G_DEBUG_FREESTYLE) {
129                 cout << "FRS_set_context: context 0x" << C << " scene 0x" << CTX_data_scene(C) << endl;
130         }
131         controller->setContext(C);
132 }
133
134 void FRS_exit()
135 {
136         delete pathconfig;
137         delete controller;
138         delete view;
139 }
140
141 //=======================================================
142 //   Rendering 
143 //=======================================================
144
145 static void init_view(Render *re)
146 {
147         int width = re->winx;
148         int height = re->winy;
149         int xmin = re->disprect.xmin;
150         int ymin = re->disprect.ymin;
151         int xmax = re->disprect.xmax;
152         int ymax = re->disprect.ymax;
153
154         float thickness = 1.0f;
155         switch (re->r.line_thickness_mode) {
156         case R_LINE_THICKNESS_ABSOLUTE:
157                 thickness = re->r.unit_line_thickness * (re->r.size / 100.f);
158                 break;
159         case R_LINE_THICKNESS_RELATIVE:
160                 thickness = height / 480.f;
161                 break;
162         }
163
164         freestyle_viewport[0] = freestyle_viewport[1] = 0;
165         freestyle_viewport[2] = width;
166         freestyle_viewport[3] = height;
167
168         view->setWidth(width);
169         view->setHeight(height);
170         view->setBorder(xmin, ymin, xmax, ymax);
171         view->setThickness(thickness);
172
173         if (G.debug & G_DEBUG_FREESTYLE) {
174                 cout << "\n===  Dimensions of the 2D image coordinate system  ===" << endl;
175                 cout << "Width  : " << width << endl;
176                 cout << "Height : " << height << endl;
177                 if (re->r.mode & R_BORDER)
178                         cout << "Border : (" << xmin << ", " << ymin << ") - (" << xmax << ", " << ymax << ")" << endl;
179                 cout << "Unit line thickness : " << thickness << " pixel(s)" << endl;
180         }
181 }
182
183 static void init_camera(Render *re)
184 {
185         // It is assumed that imported meshes are in the camera coordinate system.
186         // Therefore, the view point (i.e., camera position) is at the origin, and
187         // the the model-view matrix is simply the identity matrix.
188
189         freestyle_viewpoint[0] = 0.0;
190         freestyle_viewpoint[1] = 0.0;
191         freestyle_viewpoint[2] = 0.0;
192
193         unit_m4(freestyle_mv);
194
195         copy_m4_m4(freestyle_proj, re->winmat);
196
197 #if 0
198         print_m4("mv", freestyle_mv);
199         print_m4("proj", freestyle_proj);
200 #endif
201 }
202
203 static char *escape_quotes(char *name)
204 {
205         char *s = (char *)MEM_mallocN(strlen(name) * 2 + 1, "escape_quotes");
206         char *p = s;
207         while (*name) {
208                 if (*name == '\'')
209                         *(p++) = '\\';
210                 *(p++) = *(name++);
211         }
212         *p = '\0';
213         return s;
214 }
215
216 static Text *create_lineset_handler(Main *bmain, char *layer_name, char *lineset_name)
217 {
218         char *s1 = escape_quotes(layer_name);
219         char *s2 = escape_quotes(lineset_name);
220         Text *text = BKE_text_add(bmain, lineset_name);
221         BKE_text_write(text, "import parameter_editor; parameter_editor.process('");
222         BKE_text_write(text, s1);
223         BKE_text_write(text, "', '");
224         BKE_text_write(text, s2);
225         BKE_text_write(text, "')\n");
226         MEM_freeN(s1);
227         MEM_freeN(s2);
228         return text;
229 }
230
231 struct edge_type_condition
232 {
233         int edge_type, value;
234 };
235
236 // examines the conditions and returns true if the target edge type needs to be computed
237 static bool test_edge_type_conditions(struct edge_type_condition *conditions,
238                                       int num_edge_types, bool logical_and, int target, bool distinct)
239 {
240         int target_condition = 0;
241         int num_non_target_positive_conditions = 0;
242         int num_non_target_negative_conditions = 0;
243
244         for (int i = 0; i < num_edge_types; i++) {
245                 if (conditions[i].edge_type == target)
246                         target_condition = conditions[i].value;
247                 else if (conditions[i].value > 0)
248                         ++num_non_target_positive_conditions;
249                 else if (conditions[i].value < 0)
250                         ++num_non_target_negative_conditions;
251         }
252         if (distinct) {
253                 // In this case, the 'target' edge type is assumed to appear on distinct edge
254                 // of its own and never together with other edge types.
255                 if (logical_and) {
256                         if (num_non_target_positive_conditions > 0)
257                                 return false;
258                         if (target_condition > 0)
259                                 return true;
260                         if (target_condition < 0)
261                                 return false;
262                         if (num_non_target_negative_conditions > 0)
263                                 return true;
264                 }
265                 else {
266                         if (target_condition > 0)
267                                 return true;
268                         if (num_non_target_negative_conditions > 0)
269                                 return true;
270                         if (target_condition < 0)
271                                 return false;
272                         if (num_non_target_positive_conditions > 0)
273                                 return false;
274                 }
275         }
276         else {
277                 // In this case, the 'target' edge type may appear together with other edge types.
278                 if (target_condition > 0)
279                         return true;
280                 if (target_condition < 0)
281                         return true;
282                 if (logical_and) {
283                         if (num_non_target_positive_conditions > 0)
284                                 return false;
285                         if (num_non_target_negative_conditions > 0)
286                                 return true;
287                 }
288                 else {
289                         if (num_non_target_negative_conditions > 0)
290                                 return true;
291                         if (num_non_target_positive_conditions > 0)
292                                 return false;
293                 }
294         }
295         return true;
296 }
297
298 static void prepare(Main *bmain, Render *re, SceneRenderLayer *srl)
299 {
300         // load mesh
301         re->i.infostr = "Freestyle: Mesh loading";
302         re->stats_draw(re->sdh, &re->i);
303         re->i.infostr = NULL;
304         if (controller->LoadMesh(re, srl)) // returns if scene cannot be loaded or if empty
305                 return;
306         if (re->test_break(re->tbh))
307                 return;
308
309         // add style modules
310         FreestyleConfig *config = &srl->freestyleConfig;
311
312         if (G.debug & G_DEBUG_FREESTYLE) {
313                 cout << "\n===  Rendering options  ===" << endl;
314         }
315         int layer_count = 0;
316
317         switch (config->mode) {
318         case FREESTYLE_CONTROL_SCRIPT_MODE:
319                 if (G.debug & G_DEBUG_FREESTYLE) {
320                         cout << "Modules :" << endl;
321                 }
322                 for (FreestyleModuleConfig *module_conf = (FreestyleModuleConfig *)config->modules.first;
323                      module_conf;
324                      module_conf = module_conf->next)
325                 {
326                         if (module_conf->script && module_conf->is_displayed) {
327                                 const char *id_name = module_conf->script->id.name + 2;
328                                 if (G.debug & G_DEBUG_FREESTYLE) {
329                                         cout << "  " << layer_count + 1 << ": " << id_name;
330                                         if (module_conf->script->name)
331                                                 cout << " (" << module_conf->script->name << ")";
332                                         cout << endl;
333                                 }
334                                 controller->InsertStyleModule(layer_count, id_name, module_conf->script);
335                                 controller->toggleLayer(layer_count, true);
336                                 layer_count++;
337                         }
338                 }
339                 if (G.debug & G_DEBUG_FREESTYLE) {
340                         cout << endl;
341                 }
342                 controller->setComputeRidgesAndValleysFlag((config->flags & FREESTYLE_RIDGES_AND_VALLEYS_FLAG) ? true : false);
343                 controller->setComputeSuggestiveContoursFlag((config->flags & FREESTYLE_SUGGESTIVE_CONTOURS_FLAG) ? true : false);
344                 controller->setComputeMaterialBoundariesFlag((config->flags & FREESTYLE_MATERIAL_BOUNDARIES_FLAG) ? true : false);
345                 break;
346         case FREESTYLE_CONTROL_EDITOR_MODE:
347                 int use_ridges_and_valleys = 0;
348                 int use_suggestive_contours = 0;
349                 int use_material_boundaries = 0;
350                 struct edge_type_condition conditions[] = {
351                         {FREESTYLE_FE_SILHOUETTE, 0},
352                         {FREESTYLE_FE_BORDER, 0},
353                         {FREESTYLE_FE_CREASE, 0},
354                         {FREESTYLE_FE_RIDGE_VALLEY, 0},
355                         {FREESTYLE_FE_SUGGESTIVE_CONTOUR, 0},
356                         {FREESTYLE_FE_MATERIAL_BOUNDARY, 0},
357                         {FREESTYLE_FE_CONTOUR, 0},
358                         {FREESTYLE_FE_EXTERNAL_CONTOUR, 0},
359                         {FREESTYLE_FE_EDGE_MARK, 0}
360                 };
361                 int num_edge_types = sizeof(conditions) / sizeof(struct edge_type_condition);
362                 if (G.debug & G_DEBUG_FREESTYLE) {
363                         cout << "Linesets:" << endl;
364                 }
365                 for (FreestyleLineSet *lineset = (FreestyleLineSet *)config->linesets.first;
366                      lineset;
367                      lineset = lineset->next)
368                 {
369                         if (lineset->flags & FREESTYLE_LINESET_ENABLED) {
370                                 if (G.debug & G_DEBUG_FREESTYLE) {
371                                         cout << "  " << layer_count+1 << ": " << lineset->name << " - " <<
372                                                 lineset->linestyle->id.name + 2 << endl;
373                                 }
374                                 Text *text = create_lineset_handler(bmain, srl->name, lineset->name);
375                                 controller->InsertStyleModule(layer_count, lineset->name, text);
376                                 controller->toggleLayer(layer_count, true);
377                                 if (!(lineset->selection & FREESTYLE_SEL_EDGE_TYPES) || !lineset->edge_types) {
378                                         ++use_ridges_and_valleys;
379                                         ++use_suggestive_contours;
380                                         ++use_material_boundaries;
381                                 }
382                                 else {
383                                         // conditions for feature edge selection by edge types
384                                         for (int i = 0; i < num_edge_types; i++) {
385                                                 if (!(lineset->edge_types & conditions[i].edge_type))
386                                                         conditions[i].value = 0; // no condition specified
387                                                 else if (!(lineset->exclude_edge_types & conditions[i].edge_type))
388                                                         conditions[i].value = 1; // condition: X
389                                                 else
390                                                         conditions[i].value = -1; // condition: NOT X
391                                         }
392                                         // logical operator for the selection conditions
393                                         bool logical_and = ((lineset->flags & FREESTYLE_LINESET_FE_AND) != 0);
394                                         // negation operator
395                                         if (lineset->flags & FREESTYLE_LINESET_FE_NOT) {
396                                                 // convert an Exclusive condition into an Inclusive equivalent using De Morgan's laws:
397                                                 //   NOT (X OR Y) --> (NOT X) AND (NOT Y)
398                                                 //   NOT (X AND Y) --> (NOT X) OR (NOT Y)
399                                                 for (int i = 0; i < num_edge_types; i++)
400                                                         conditions[i].value *= -1;
401                                                 logical_and = !logical_and;
402                                         }
403                                         if (test_edge_type_conditions(conditions, num_edge_types, logical_and,
404                                                                       FREESTYLE_FE_RIDGE_VALLEY, true))
405                                         {
406                                                 ++use_ridges_and_valleys;
407                                         }
408                                         if (test_edge_type_conditions(conditions, num_edge_types, logical_and,
409                                                                       FREESTYLE_FE_SUGGESTIVE_CONTOUR, true))
410                                         {
411                                                 ++use_suggestive_contours;
412                                         }
413                                         if (test_edge_type_conditions(conditions, num_edge_types, logical_and,
414                                                                       FREESTYLE_FE_MATERIAL_BOUNDARY, true))
415                                         {
416                                                 ++use_material_boundaries;
417                                         }
418                                 }
419                                 layer_count++;
420                         }
421                 }
422                 controller->setComputeRidgesAndValleysFlag(use_ridges_and_valleys > 0);
423                 controller->setComputeSuggestiveContoursFlag(use_suggestive_contours > 0);
424                 controller->setComputeMaterialBoundariesFlag(use_material_boundaries > 0);
425                 break;
426         }
427
428         // set parameters
429         if (config->flags & FREESTYLE_ADVANCED_OPTIONS_FLAG) {
430                 controller->setSphereRadius(config->sphere_radius);
431                 controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon);
432         }
433         else {
434                 controller->setSphereRadius(DEFAULT_SPHERE_RADIUS);
435                 controller->setSuggestiveContourKrDerivativeEpsilon(DEFAULT_DKR_EPSILON);
436         }
437         controller->setFaceSmoothness((config->flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) ? true : false);
438         controller->setCreaseAngle(RAD2DEGF(config->crease_angle));
439         controller->setVisibilityAlgo((config->flags & FREESTYLE_CULLING) ?
440                                       FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE :
441                                       FREESTYLE_ALGO_ADAPTIVE_CUMULATIVE);
442
443         if (G.debug & G_DEBUG_FREESTYLE) {
444                 cout << "Crease angle : " << controller->getCreaseAngle() << endl;
445                 cout << "Sphere radius : " << controller->getSphereRadius() << endl;
446                 cout << "Face smoothness : " << (controller->getFaceSmoothness() ? "enabled" : "disabled") << endl;
447                 cout << "Redges and valleys : " << (controller->getComputeRidgesAndValleysFlag() ? "enabled" : "disabled") <<
448                         endl;
449                 cout << "Suggestive contours : " <<
450                         (controller->getComputeSuggestiveContoursFlag() ? "enabled" : "disabled") << endl;
451                 cout << "Suggestive contour Kr derivative epsilon : " <<
452                         controller->getSuggestiveContourKrDerivativeEpsilon() << endl;
453                 cout << "Material boundaries : " <<
454                         (controller->getComputeMaterialBoundariesFlag() ? "enabled" : "disabled") << endl;
455                 cout << endl;
456         }
457
458         // set diffuse and z depth passes
459         RenderLayer *rl = RE_GetRenderLayer(re->result, srl->name);
460         bool diffuse = false, z = false;
461         for (RenderPass *rpass = (RenderPass *)rl->passes.first; rpass; rpass = rpass->next) {
462                 switch (rpass->passtype) {
463                 case SCE_PASS_DIFFUSE:
464                         controller->setPassDiffuse(rpass->rect, rpass->rectx, rpass->recty);
465                         diffuse = true;
466                         break;
467                 case SCE_PASS_Z:
468                         controller->setPassZ(rpass->rect, rpass->rectx, rpass->recty);
469                         z = true;
470                         break;
471                 }
472         }
473         if (G.debug & G_DEBUG_FREESTYLE) {
474                 cout << "Passes :" << endl;
475                 cout << "  Diffuse = " << (diffuse ? "enabled" : "disabled") << endl;
476                 cout << "  Z = " << (z ? "enabled" : "disabled") << endl;
477         }
478
479         // compute view map
480         re->i.infostr = "Freestyle: View map creation";
481         re->stats_draw(re->sdh, &re->i);
482         re->i.infostr = NULL;
483         controller->ComputeViewMap();
484 }
485
486 void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_render)
487 {
488         RenderLayer *rl;
489         float *src, *dest, *pixSrc, *pixDest;
490         int x, y, rectx, recty;
491
492         if (freestyle_render == NULL || freestyle_render->result == NULL)
493                 return;
494
495         rl = render_get_active_layer( freestyle_render, freestyle_render->result );
496         if (!rl || rl->rectf == NULL) {
497                 if (G.debug & G_DEBUG_FREESTYLE) {
498                         cout << "Cannot find Freestyle result image" << endl;
499                 }
500                 return;
501         }
502         src  = rl->rectf;
503 #if 0
504         if (G.debug & G_DEBUG_FREESTYLE) {
505                 cout << "src: " << rl->rectx << " x " << rl->recty << endl;
506         }
507 #endif
508
509         rl = RE_GetRenderLayer(re->result, srl->name);
510         if (!rl || rl->rectf == NULL) {
511                 if (G.debug & G_DEBUG_FREESTYLE) {
512                         cout << "No layer to composite to" << endl;
513                 }
514                 return;
515         }
516         dest = rl->rectf;
517 #if 0
518         if (G.debug & G_DEBUG_FREESTYLE) {
519                 cout << "dest: " << rl->rectx << " x " << rl->recty << endl;
520         }
521 #endif
522
523         rectx = re->rectx;
524         recty = re->recty;
525         for (y = 0; y < recty; y++) {
526                 for (x = 0; x < rectx; x++) {
527                         pixSrc = src + 4 * (rectx * y + x);
528                         if (pixSrc[3] > 0.0) {
529                                 pixDest = dest + 4 * (rectx * y + x);
530                                 addAlphaOverFloat(pixDest, pixSrc);
531                         }
532                 }
533         }
534 }
535
536 static int displayed_layer_count(SceneRenderLayer *srl)
537 {
538         int count = 0;
539
540         switch (srl->freestyleConfig.mode) {
541         case FREESTYLE_CONTROL_SCRIPT_MODE:
542                 for (FreestyleModuleConfig *module = (FreestyleModuleConfig *)srl->freestyleConfig.modules.first;
543                      module;
544                      module = module->next)
545                 {
546                         if (module->script && module->is_displayed)
547                                 count++;
548                 }
549                 break;
550         case FREESTYLE_CONTROL_EDITOR_MODE:
551                 for (FreestyleLineSet *lineset = (FreestyleLineSet *)srl->freestyleConfig.linesets.first;
552                      lineset;
553                      lineset = lineset->next)
554                 {
555                         if (lineset->flags & FREESTYLE_LINESET_ENABLED)
556                                 count++;
557                 }
558                 break;
559         }
560         return count;
561 }
562
563 int FRS_is_freestyle_enabled(SceneRenderLayer *srl)
564 {
565         return (!(srl->layflag & SCE_LAY_DISABLE) && srl->layflag & SCE_LAY_FRS && displayed_layer_count(srl) > 0);
566 }
567
568 void FRS_init_stroke_rendering(Render *re)
569 {
570         if (G.debug & G_DEBUG_FREESTYLE) {
571                 cout << endl;
572                 cout << "#===============================================================" << endl;
573                 cout << "#  Freestyle" << endl;
574                 cout << "#===============================================================" << endl;
575         }
576
577         init_view(re);
578         init_camera(re);
579
580         controller->ResetRenderCount();
581 }
582
583 Render *FRS_do_stroke_rendering(Render *re, SceneRenderLayer *srl, int render)
584 {
585         Main bmain = {0};
586         Render *freestyle_render = NULL;
587         Text *text, *next_text;
588
589         if (!render)
590                 return controller->RenderStrokes(re, false);
591
592         RenderMonitor monitor(re);
593         controller->setRenderMonitor(&monitor);
594
595         if (G.debug & G_DEBUG_FREESTYLE) {
596                 cout << endl;
597                 cout << "----------------------------------------------------------" << endl;
598                 cout << "|  " << (re->scene->id.name + 2) << "|" << srl->name << endl;
599                 cout << "----------------------------------------------------------" << endl;
600         }
601
602         // prepare Freestyle:
603         //   - load mesh
604         //   - add style modules
605         //   - set parameters
606         //   - compute view map
607         prepare(&bmain, re, srl);
608
609         if (re->test_break(re->tbh)) {
610                 controller->CloseFile();
611                 if (G.debug & G_DEBUG_FREESTYLE) {
612                         cout << "Break" << endl;
613                 }
614                 return NULL;
615         }
616
617         // render and composite Freestyle result
618         if (controller->_ViewMap) {
619                 // render strokes
620                 re->i.infostr = "Freestyle: Stroke rendering";
621                 re->stats_draw(re->sdh, &re->i);
622                 re->i.infostr = NULL;
623                 freestyle_scene = re->scene;
624                 controller->DrawStrokes();
625                 freestyle_render = controller->RenderStrokes(re, true);
626                 controller->CloseFile();
627                 freestyle_scene = NULL;
628
629                 // composite result
630                 FRS_composite_result(re, srl, freestyle_render);
631                 RE_FreeRenderResult(freestyle_render->result);
632                 freestyle_render->result = NULL;
633         }
634
635         // Free temp main (currently only text blocks are stored there)
636         for (text = (Text *) bmain.text.first; text; text = next_text) {
637                 next_text = (Text *) text->id.next;
638
639                 BKE_text_unlink(&bmain, text);
640                 BKE_libblock_free(&bmain.text, text);
641         }
642
643         return freestyle_render;
644 }
645
646 void FRS_finish_stroke_rendering(Render *re)
647 {
648         // clear canvas
649         controller->Clear();
650 }
651
652 //=======================================================
653 //   Freestyle Panel Configuration
654 //=======================================================
655
656 void FRS_copy_active_lineset(FreestyleConfig *config)
657 {
658         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
659
660         if (lineset) {
661                 lineset_buffer.linestyle = lineset->linestyle;
662                 lineset_buffer.flags = lineset->flags;
663                 lineset_buffer.selection = lineset->selection;
664                 lineset_buffer.qi = lineset->qi;
665                 lineset_buffer.qi_start = lineset->qi_start;
666                 lineset_buffer.qi_end = lineset->qi_end;
667                 lineset_buffer.edge_types = lineset->edge_types;
668                 lineset_buffer.exclude_edge_types = lineset->exclude_edge_types;
669                 lineset_buffer.group = lineset->group;
670                 strcpy(lineset_buffer.name, lineset->name);
671                 lineset_copied = true;
672         }
673 }
674
675 void FRS_paste_active_lineset(FreestyleConfig *config)
676 {
677         if (!lineset_copied)
678                 return;
679
680         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
681
682         if (lineset) {
683                 lineset->linestyle->id.us--;
684                 lineset->linestyle = lineset_buffer.linestyle;
685                 lineset->linestyle->id.us++;
686                 lineset->flags = lineset_buffer.flags;
687                 lineset->selection = lineset_buffer.selection;
688                 lineset->qi = lineset_buffer.qi;
689                 lineset->qi_start = lineset_buffer.qi_start;
690                 lineset->qi_end = lineset_buffer.qi_end;
691                 lineset->edge_types = lineset_buffer.edge_types;
692                 lineset->exclude_edge_types = lineset_buffer.exclude_edge_types;
693                 if (lineset->group) {
694                         lineset->group->id.us--;
695                         lineset->group = NULL;
696                 }
697                 if (lineset_buffer.group) {
698                         lineset->group = lineset_buffer.group;
699                         lineset->group->id.us++;
700                 }
701                 strcpy(lineset->name, lineset_buffer.name);
702                 BKE_freestyle_lineset_unique_name(config, lineset);
703                 lineset->flags |= FREESTYLE_LINESET_CURRENT;
704         }
705 }
706
707 void FRS_delete_active_lineset(FreestyleConfig *config)
708 {
709         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
710
711         if (lineset) {
712                 if (lineset->group) {
713                         lineset->group->id.us--;
714                         lineset->group = NULL;
715                 }
716                 lineset->linestyle->id.us--;
717                 lineset->linestyle = NULL;
718                 BLI_remlink(&config->linesets, lineset);
719                 MEM_freeN(lineset);
720                 BKE_freestyle_lineset_set_active_index(config, 0);
721         }
722 }
723
724 void FRS_move_active_lineset_up(FreestyleConfig *config)
725 {
726         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
727
728         if (lineset) {
729                 BLI_remlink(&config->linesets, lineset);
730                 BLI_insertlinkbefore(&config->linesets, lineset->prev, lineset);
731         }
732 }
733
734 void FRS_move_active_lineset_down(FreestyleConfig *config)
735 {
736         FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config);
737
738         if (lineset) {
739                 BLI_remlink(&config->linesets, lineset);
740                 BLI_insertlinkafter(&config->linesets, lineset->next, lineset);
741         }
742 }
743
744 } // extern "C"