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