├── README.md ├── lib └── win64_vc14 │ └── gmic64 │ ├── COPYING │ ├── README │ ├── libcgmicstatic.def │ └── libcgmicstatic.dll └── patch └── compositor_nodes.diff /README.md: -------------------------------------------------------------------------------- 1 | # Blender custom compositor nodes 2 | 3 | Support for three new compositor nodes: 4 | 5 | * [G'MIC](http://gmic.eu/) node 6 | * OpenGL GLSL [Shadertoy](https://www.shadertoy.com/) (warning: heavy site) compatible fragment shader node 7 | * Python script node 8 | 9 | ## [Get the latest binary release (Win64)](/../../releases/latest) 10 | 11 | If you encounter missing .dll errors when starting Blender, try installing [Microsoft Visual C++ Redistributable for Visual Studio 2017](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads) 12 | 13 | The build is based on Blender master branch, so Python version 3.6 is bundled instead of 3.5. This is important if you use addons that contain native code compiled against a specific Python version. This also means the build contains fixes and additions not in the stock Blender, but possibly also new bugs or incompatibilities. 14 | 15 | ## What's new? 16 | 17 | **0.3.0** 18 | 19 | * Added GLSL node 20 | * Added Python script node 21 | 22 | ## G'MIC Node 23 | 24 | [Watch YouTube demo](https://www.youtube.com/watch?v=vGNB13ovwk4) 25 | 26 | Some operations can be pretty slow, especially with large images. You can adjust the scaling quality if you are not rendering the final image and you are just tweaking other nodes. 27 | 28 | The release ships with a version of the GIMP-plugin and most filters seem to work out of the box. This is still work in progess, so some of them don't work yet. Many GIMP filters use images that are placed in your *%APPDATA%/gmic*-directory. G'MIC bundled with the Blender release will find them from there if they exist. You can download the whole package from [this link](http://gmic.eu/gmic_all_data.zip) and extract the contents into the target directory. 29 | 30 | * Quality setting can be used to scale down the image for faster processing. This is useful if you are working with a large image with a complex node tree. Just remember to set quality to high once you are ready to save the final output. 31 | 32 | * Normalize can be used to normalize RGBA values to 0-1.0 range, perform color space conversion and image orientation fixes. You usually want to keep this on, but you can also do these manually if you want more control. 33 | 34 | * Argument slots can be used to pass arguments to your commands. You can reference variables like this: "$arg1". 35 | 36 | ## GLSL Shader Node 37 | 38 | [Watch YouTube demo](https://www.youtube.com/watch?v=S5NdgZBxneE) 39 | 40 | The shader node uses (mostly) Shadertoy compatible functions and variables. See the [Shadertoy documentation](https://www.shadertoy.com/howto) for more information. This node can not be used if Blender is run from command line in a background mode because the whole OpenGL subsystem is not initialized in that case. You can use this very simple script to test node functionality: 41 | 42 | ```glsl 43 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 44 | { 45 | fragColor = vec4(0.0, 1.0, 0.0, 1.0); //Output a completely green image (RGBA) 46 | } 47 | ``` 48 | 49 | Another simple example is a mix node that mixes input image channels 0 and 1 together. If the images are read from a file/render layer and not generated in shader, disable node gamma correction. Control the mix factor by adjusting the node Input 0 (should be from 0.0 to 1.0). 50 | 51 | ```glsl 52 | void mainImage(out vec4 fragColor, in vec2 fragCoord) 53 | { 54 | vec2 uv = fragCoord.xy / iResolution.xy; 55 | fragColor = mix(texture(iChannel0, uv), texture(iChannel1, uv), input0); 56 | } 57 | ``` 58 | 59 | ## Python Script Node 60 | 61 | [Watch YouTube demo](https://www.youtube.com/watch?v=RaYfQT7r74s) 62 | 63 | The purpose of this node is to enable complex new functionality in the compositor without recompiling Blender. The node can be used to make simple modifications to the input image, draw shapes or text, generate procedural images or even use complex third-party libraries like [OpenCV](https://opencv.org/) and [Pillow](https://pillow.readthedocs.io/en/4.3.x/). As a convenience Pillow image processing library is included in the distribution. Thread safety of Pillow is not explicitly documented, so it might be safest to only use it during script import and in on_main(). 64 | 65 | A **pycompositor** helper module is can be imported by the scripts. It contains some helper functions and it can be used to interface with the Pillow library. The module is located in *2.79/scripts/modules* directory and can be useful as an example. 66 | 67 | Here is a simple example script. Images are [NumPy arrays](http://www.numpy.org/) which makes it possible to do image processing in an efficient manner. The images are 3D-arrays with a shape of (height, width, depth). Depth is always 4 (red, green, blue and alpha). Image data is also "upside down" as the y-axis starts from the bottom, but you can use *numpy.flip(image, 0)* to flip the image vertically. 68 | 69 | ```python 70 | import bpy # If you want to read any Blender data 71 | import numpy as np 72 | import pycompositor # Helper script 73 | from PIL import Image, ImageDraw # Pillow 74 | 75 | def on_main(context): 76 | """on_main() is called in the main thread and can be used to access Blender and other libraries in a 77 | thread-safe manner. It is possible to add extra data to the context object and use it later 78 | in the on_async().""" 79 | images = context["images"] # A list of 4 node input images 80 | inputs = context["inputs"] # A list of 8 node input values 81 | outputs = context["outputs"] # A list of 1 node output image. Might have more in the future 82 | out = outputs[0] # Final output image 83 | 84 | # Copy Image 0 to output without any modifications 85 | #out[:] = images[0] 86 | 87 | # Set output to green 88 | #out[:] = (0, 1, 0, 1) 89 | 90 | # Draw a 10 pixel wide horizontal line at the middle of the image 91 | #y = int(out.shape[0] / 2) 92 | #out[y:y+10] = (1, 0, 0, 1) #Overwrite image alpha with 1.0 93 | #out[y:y+10,:,0:3] = (1, 0, 0) #Same as above, but don't touch image alpha, only RGB 94 | 95 | # Convert input Image 0 to Pillow image and metadata 96 | im, meta = pycompositor.array_to_pil(images[0]) 97 | 98 | # Draw a red line 99 | draw = ImageDraw.Draw(im) 100 | draw.line((0, 0) + im.size, fill=(255, 0, 0, 255), width=5) 101 | 102 | # Write Pillow image back to output image 103 | out[:] = pycompositor.pil_to_array(im, meta) 104 | 105 | def on_async(context): 106 | """on_async() is called in background thread after on_main() and should be used for processing 107 | that takes a long time in order to avoid blocking Blender UI. Do not touch anything that is not 108 | thread safe from this function.""" 109 | pass 110 | ``` 111 | 112 | ## Troubleshooting and tips 113 | 114 | * If the node image output is red, there was an error with the node. Usually a misspelled command or wrong arguments. Check the console (Window -> Toggle System Console) for more information about what went wrong. 115 | 116 | * You can accidentally put commands (like "-display" or just a single, lone "-"-character) into the node which will prompt GMIC to do something interactively. Don't do that. You can usually notice this if the progress bar in the compositing view is not going away (or the filter can just be slow). In some cases you can open the console and type some text and press enter to recover. Sometimes the safest bet is to kill the Blender process and start it again. 117 | 118 | * Try to fiddle with the "Use Alpha"-flag in the final "Composite"-node. Or connect the image source node "Alpha" directly into the final "Composite"-node alpha to ignore any (possibly unwanted) alpha changes done by the G'MIC filter. 119 | 120 | * Some filters can output very large RGB-values (way above the common range of 0.0 to 1.0). Useful nodes for debugging and working with black and white filters: "Alpha Over", "Set Alpha", "RGB to BW" and "Normalize". 121 | 122 | * When you see "-normalize 0,255" or "-n 0,255" in G'MIC examples, try to use "-n 0,1" instead. Same goes for "-equalize 255" etc. The Blender compositor uses 32-bit floating point values (where values are usually between 0.0 and 1.0) instead of normal 8-bit (0 to 255) values used in many image formats. 123 | 124 | * Hover your cursor over the final compositor image and press mouse button (right or left depending on your configuration) so you can see the individual RGBA values of the pixel under the cursor. Sometimes the image is there, but alpha is 0 or RGB-values are massively overblown. 125 | 126 | ## Links 127 | 128 | * [G'MIC Reference](http://gmic.eu/reference.shtml) 129 | * [G'MIC gallery and examples](http://gmic.eu/gallery.shtml) 130 | -------------------------------------------------------------------------------- /lib/win64_vc14/gmic64/COPYING: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitsawer/blender-custom-nodes/30a6518502323fa393f908e647086490dc7c15cb/lib/win64_vc14/gmic64/COPYING -------------------------------------------------------------------------------- /lib/win64_vc14/gmic64/README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- 2 | -------------------------------------------------------------------------------- 3 | 4 | _____ _ __ __ _____ _____ 5 | / ____| | | | \/ |_ _/ ____| 6 | | | __ |_| | \ / | | || | 7 | | | |_ | | |\/| | | || | 8 | | |__| | | | | |_| || |____ 9 | \_____| |_| |_|_____\_____| 10 | 11 | GREYC's Magic for Image Computing 12 | 13 | ( http://gmic.eu ) 14 | 15 | 2.0.5_pre 16 | 17 | -------------------------------------------------------------------------------- 18 | 19 | # In a nutshell 20 | #--------------- 21 | 22 | G'MIC is a full-featured open-source framework for image processing, distributed 23 | under the CeCILL license (GPL compatible). 24 | It provides several different user interfaces to convert/manipulate/filter/ 25 | visualize generic image datasets, ranging from 1d scalar signals to 3d+t 26 | sequences of multi-spectral volumetric images, thus including 2d color images. 27 | 28 | These user interfaces are: 29 | 30 | 1 - A command-line interface 'gmic', to use the G'MIC image processing features 31 | from a shell. In this setting, G'MIC may be seen as a friendly companion to 32 | the ImageMagick or GraphicsMagick software suites. 33 | 34 | 2 - A small, portable, thread-safe and multi-threaded, C++ image processing 35 | library 'libgmic', to be linked to third-party applications. Its simple API 36 | allows programmers to add all G'MIC features in their own software without 37 | much efforts (a C API is provided as well). 38 | 39 | 3 - A plug-in 'gmic_gimp_qt' (or 'gmic_gimp_gtk'), to bring G'MIC capabilities 40 | to the image retouching software GIMP. More than 450 filters are already 41 | available, sorted by category (Artistic, Black & white, Colors, Contours, 42 | Deformations, Degradations, Details, Film emulation, Frames, Layers, 43 | Light & shadows, Patterns, Rendering, Repair, Sequences, etc.). 44 | 45 | 4 - A web service G'MIC Online, to allow users applying image processing 46 | algorithms on their images, directly from a web browser. 47 | 48 | 5 - A Qt-based interface ZArt, for real-time processing of video streaming 49 | coming from webcams or video files. 50 | 51 | G'MIC is an open framework: the default script language can be extended with 52 | custom G'MIC-written commands, defining thus new image available filters or 53 | effects. By the way, it already contains a substantial set of pre-defined image 54 | processing algorithms and pipelines (more than 1000). 55 | 56 | G'MIC has been designed with portability in mind, and runs on different 57 | platforms (Windows, Unix, MacOSX). It is distributed under the CeCILL license 58 | (GPL-compatible). Since 2008, it has been developed in the Image Team of the 59 | GREYC laboratory, in Caen/France, by permanent researchers working in the field 60 | of image processing on a daily basis. 61 | 62 | # Project Managers and main developers 63 | #------------------------------------- 64 | 65 | David Tschumperlé ( http://tschumperle.users.greyc.fr/ ) 66 | Sébastien Fourey ( https://foureys.users.greyc.fr/ ) 67 | 68 | A complete list of contributors is available on the project web page: 69 | 70 | http://gmic.eu 71 | 72 | # Institution 73 | #------------- 74 | 75 | GREYC Image / CNRS UMR 6072 / FRANCE 76 | 77 | Team web page: https://www.greyc.fr/image 78 | 79 | # License 80 | #--------- 81 | 82 | The C++ source code of G'MIC is distributed under the CECILL v.2.0 license 83 | (file 'CeCILL.rtf'). This License is a Free-Software copyleft license, 84 | compatible with the GPL (using those files in a distributed closed-source 85 | project is then forbidden). 86 | 87 | Purchasing proprietary licenses for using G'MIC in closed-source applications 88 | is possible. Contact us if you are interested in getting one. 89 | 90 | The CeCiLL v.2.0 license ( http://www.cecill.info/index.en.html ) have been 91 | created under the supervision of the three biggest research institutions on 92 | computer sciences in France: 93 | 94 | - CNRS ( http://www.cnrs.fr/ ) 95 | - CEA ( http://www.cea.fr/ ) 96 | - INRIA ( http://www.inria.fr/ ) 97 | 98 | # More information online 99 | #------------------------- 100 | 101 | - Home page : http://gmic.eu 102 | - G'MIC Online Service : https://gmicol.greyc.fr 103 | 104 | - Google+ group: https://plus.google.com/117441237982283011318/posts 105 | - Pixls.us forum: https://discuss.pixls.us/c/software/gmic 106 | - Flickr forum: http://www.flickr.com/groups/gmic/discuss 107 | - GimpChat forum: http://gimpchat.com/viewforum.php?f=28 108 | 109 | - Tutorial page: http://gmic.eu/tutorial 110 | - Reference documentation: http://gmic.eu/reference.shtml 111 | - G'MIC wiki: https://github.com/dtschump/gmic-community/wiki 112 | 113 | -------------------------------------------------------------------------------- 114 | -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /lib/win64_vc14/gmic64/libcgmicstatic.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | gmic_call @24 3 | gmic_delete_external @25 4 | gmic_get_stdlib @26 5 | -------------------------------------------------------------------------------- /lib/win64_vc14/gmic64/libcgmicstatic.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitsawer/blender-custom-nodes/30a6518502323fa393f908e647086490dc7c15cb/lib/win64_vc14/gmic64/libcgmicstatic.dll -------------------------------------------------------------------------------- /patch/compositor_nodes.diff: -------------------------------------------------------------------------------- 1 | diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt 2 | index 91919ad..fed9e0c 100644 3 | --- a/extern/CMakeLists.txt 4 | +++ b/extern/CMakeLists.txt 5 | @@ -37,6 +37,7 @@ if(CMAKE_COMPILER_IS_GNUCC) 6 | endif() 7 | 8 | 9 | +add_subdirectory(gmic64) 10 | add_subdirectory(rangetree) 11 | add_subdirectory(wcwidth) 12 | 13 | diff --git a/extern/gmic64/CMakeLists.txt b/extern/gmic64/CMakeLists.txt 14 | new file mode 100644 15 | index 0000000..fe4dbb4 16 | --- /dev/null 17 | +++ b/extern/gmic64/CMakeLists.txt 18 | @@ -0,0 +1,38 @@ 19 | +# ***** BEGIN GPL LICENSE BLOCK ***** 20 | +# 21 | +# This program is free software; you can redistribute it and/or 22 | +# modify it under the terms of the GNU General Public License 23 | +# as published by the Free Software Foundation; either version 2 24 | +# of the License, or (at your option) any later version. 25 | +# 26 | +# This program is distributed in the hope that it will be useful, 27 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 29 | +# GNU General Public License for more details. 30 | +# 31 | +# You should have received a copy of the GNU General Public License 32 | +# along with this program; if not, write to the Free Software Foundation, 33 | +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 34 | +# 35 | +# The Original Code is Copyright (C) 2006, Blender Foundation 36 | +# All rights reserved. 37 | +# 38 | +# The Original Code is: all of this file. 39 | +# 40 | +# Contributor(s): 41 | +# 42 | +# ***** END GPL LICENSE BLOCK ***** 43 | + 44 | +set(INC 45 | + include 46 | +) 47 | + 48 | +set(INC_SYS 49 | + 50 | +) 51 | + 52 | +set(SRC 53 | + include/gmic_libc.h 54 | +) 55 | + 56 | +# blender_add_lib(extern_gmic64 "${SRC}" "${INC}" "${INC_SYS}") 57 | diff --git a/extern/gmic64/README b/extern/gmic64/README 58 | new file mode 100644 59 | index 0000000..daaddf7 60 | --- /dev/null 61 | +++ b/extern/gmic64/README 62 | @@ -0,0 +1,114 @@ 63 | +-------------------------------------------------------------------------------- 64 | +-------------------------------------------------------------------------------- 65 | + 66 | + _____ _ __ __ _____ _____ 67 | + / ____| | | | \/ |_ _/ ____| 68 | + | | __ |_| | \ / | | || | 69 | + | | |_ | | |\/| | | || | 70 | + | |__| | | | | |_| || |____ 71 | + \_____| |_| |_|_____\_____| 72 | + 73 | + GREYC's Magic for Image Computing 74 | + 75 | + ( http://gmic.eu ) 76 | + 77 | + 2.0.5_pre 78 | + 79 | +-------------------------------------------------------------------------------- 80 | + 81 | +# In a nutshell 82 | +#--------------- 83 | + 84 | + G'MIC is a full-featured open-source framework for image processing, distributed 85 | + under the CeCILL license (GPL compatible). 86 | + It provides several different user interfaces to convert/manipulate/filter/ 87 | + visualize generic image datasets, ranging from 1d scalar signals to 3d+t 88 | + sequences of multi-spectral volumetric images, thus including 2d color images. 89 | + 90 | + These user interfaces are: 91 | + 92 | + 1 - A command-line interface 'gmic', to use the G'MIC image processing features 93 | + from a shell. In this setting, G'MIC may be seen as a friendly companion to 94 | + the ImageMagick or GraphicsMagick software suites. 95 | + 96 | + 2 - A small, portable, thread-safe and multi-threaded, C++ image processing 97 | + library 'libgmic', to be linked to third-party applications. Its simple API 98 | + allows programmers to add all G'MIC features in their own software without 99 | + much efforts (a C API is provided as well). 100 | + 101 | + 3 - A plug-in 'gmic_gimp_qt' (or 'gmic_gimp_gtk'), to bring G'MIC capabilities 102 | + to the image retouching software GIMP. More than 450 filters are already 103 | + available, sorted by category (Artistic, Black & white, Colors, Contours, 104 | + Deformations, Degradations, Details, Film emulation, Frames, Layers, 105 | + Light & shadows, Patterns, Rendering, Repair, Sequences, etc.). 106 | + 107 | + 4 - A web service G'MIC Online, to allow users applying image processing 108 | + algorithms on their images, directly from a web browser. 109 | + 110 | + 5 - A Qt-based interface ZArt, for real-time processing of video streaming 111 | + coming from webcams or video files. 112 | + 113 | + G'MIC is an open framework: the default script language can be extended with 114 | + custom G'MIC-written commands, defining thus new image available filters or 115 | + effects. By the way, it already contains a substantial set of pre-defined image 116 | + processing algorithms and pipelines (more than 1000). 117 | + 118 | + G'MIC has been designed with portability in mind, and runs on different 119 | + platforms (Windows, Unix, MacOSX). It is distributed under the CeCILL license 120 | + (GPL-compatible). Since 2008, it has been developed in the Image Team of the 121 | + GREYC laboratory, in Caen/France, by permanent researchers working in the field 122 | + of image processing on a daily basis. 123 | + 124 | +# Project Managers and main developers 125 | +#------------------------------------- 126 | + 127 | + David Tschumperlé ( http://tschumperle.users.greyc.fr/ ) 128 | + Sébastien Fourey ( https://foureys.users.greyc.fr/ ) 129 | + 130 | + A complete list of contributors is available on the project web page: 131 | + 132 | + http://gmic.eu 133 | + 134 | +# Institution 135 | +#------------- 136 | + 137 | + GREYC Image / CNRS UMR 6072 / FRANCE 138 | + 139 | + Team web page: https://www.greyc.fr/image 140 | + 141 | +# License 142 | +#--------- 143 | + 144 | + The C++ source code of G'MIC is distributed under the CECILL v.2.0 license 145 | + (file 'CeCILL.rtf'). This License is a Free-Software copyleft license, 146 | + compatible with the GPL (using those files in a distributed closed-source 147 | + project is then forbidden). 148 | + 149 | + Purchasing proprietary licenses for using G'MIC in closed-source applications 150 | + is possible. Contact us if you are interested in getting one. 151 | + 152 | + The CeCiLL v.2.0 license ( http://www.cecill.info/index.en.html ) have been 153 | + created under the supervision of the three biggest research institutions on 154 | + computer sciences in France: 155 | + 156 | + - CNRS ( http://www.cnrs.fr/ ) 157 | + - CEA ( http://www.cea.fr/ ) 158 | + - INRIA ( http://www.inria.fr/ ) 159 | + 160 | +# More information online 161 | +#------------------------- 162 | + 163 | + - Home page : http://gmic.eu 164 | + - G'MIC Online Service : https://gmicol.greyc.fr 165 | + 166 | + - Google+ group: https://plus.google.com/117441237982283011318/posts 167 | + - Pixls.us forum: https://discuss.pixls.us/c/software/gmic 168 | + - Flickr forum: http://www.flickr.com/groups/gmic/discuss 169 | + - GimpChat forum: http://gimpchat.com/viewforum.php?f=28 170 | + 171 | + - Tutorial page: http://gmic.eu/tutorial 172 | + - Reference documentation: http://gmic.eu/reference.shtml 173 | + - G'MIC wiki: https://github.com/dtschump/gmic-community/wiki 174 | + 175 | +-------------------------------------------------------------------------------- 176 | +-------------------------------------------------------------------------------- 177 | \ No newline at end of file 178 | diff --git a/extern/gmic64/include/gmic_libc.h b/extern/gmic64/include/gmic_libc.h 179 | new file mode 100644 180 | index 0000000..f88f2d9 181 | --- /dev/null 182 | +++ b/extern/gmic64/include/gmic_libc.h 183 | @@ -0,0 +1,109 @@ 184 | +/* 185 | + # 186 | + # File : gmic_libc.h 187 | + # ( C++ header file ) 188 | + # 189 | + # Description : GREYC's Magic for Image Computing 190 | + # ( http://gmic.eu ) 191 | + # 192 | + # Note : Include this file in your C source code, if you 193 | + # want to use the G'MIC interpreter in your own program, 194 | + # through the C bridge to the G'MIC library. 195 | + # 196 | + # Copyright : Tobias Fleischer 197 | + # ( https://plus.google.com/u/0/b/117441237982283011318/+TobiasFleischer ) 198 | + # 199 | + # License : CeCILL-B v1.0 200 | + # ( http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html ) 201 | + # 202 | + # This software is governed either by the CeCILL-B license 203 | + # under French law and abiding by the rules of distribution of free software. 204 | + # You can use, modify and or redistribute the software under the terms of 205 | + # the CeCILL-B licenses as circulated by CEA, CNRS and INRIA 206 | + # at the following URL: "http://www.cecill.info". 207 | + # 208 | + # As a counterpart to the access to the source code and rights to copy, 209 | + # modify and redistribute granted by the license, users are provided only 210 | + # with a limited warranty and the software's author, the holder of the 211 | + # economic rights, and the successive licensors have only limited 212 | + # liability. 213 | + # 214 | + # In this respect, the user's attention is drawn to the risks associated 215 | + # with loading, using, modifying and/or developing or reproducing the 216 | + # software by the user in light of its specific status of free software, 217 | + # that may mean that it is complicated to manipulate, and that also 218 | + # therefore means that it is reserved for developers and experienced 219 | + # professionals having in-depth computer knowledge. Users are therefore 220 | + # encouraged to load and test the software's suitability as regards their 221 | + # requirements in conditions enabling the security of their systems and/or 222 | + # data to be ensured and, more generally, to use and operate it in the 223 | + # same conditions as regards security. 224 | + # 225 | + # The fact that you are presently reading this means that you have had 226 | + # knowledge of the CeCILL-B licenses and that you accept its terms. 227 | + # 228 | +*/ 229 | + 230 | +#ifndef _GMIC_LIBC_H_ 231 | +#define _GMIC_LIBC_H_ 232 | + 233 | +#if !defined(_MSC_VER) || (_MSC_VER >= 1900) 234 | +#include 235 | +#endif 236 | + 237 | +#if defined(WIN32) || defined(_WIN32) 238 | + #ifdef gmic_build 239 | + #define GMIC_DLLINTERFACE __declspec(dllexport) 240 | + #else // #ifdef gmic_build 241 | + #define GMIC_DLLINTERFACE __declspec(dllimport) 242 | + #endif // #ifdef gmic_build 243 | + #define GMIC_CALLCONV __stdcall 244 | +#else // #if defined(WIN32) || defined(_WIN32) 245 | + #define GMIC_DLLINTERFACE 246 | + #define GMIC_CALLCONV 247 | +#endif // #if defined(WIN32) || defined(_WIN32) 248 | + 249 | +#define MAX_IMAGE_NAME_LENGTH 255 250 | + 251 | +typedef enum { 252 | + E_FORMAT_FLOAT = 0, 253 | + E_FORMAT_BYTE = 1 254 | +} EPixelFormat; 255 | + 256 | +typedef struct { 257 | + void* data; 258 | + unsigned int width; 259 | + unsigned int height; 260 | + unsigned int depth; 261 | + unsigned int spectrum; 262 | + bool is_interleaved; 263 | + EPixelFormat format; 264 | + char name[MAX_IMAGE_NAME_LENGTH + 1]; 265 | +} gmic_interface_image; 266 | + 267 | +typedef struct { 268 | + const char* custom_commands; 269 | + bool ignore_stdlib; 270 | + float* p_progress; 271 | + bool* p_is_abort; 272 | + bool interleave_output; 273 | + EPixelFormat output_format; 274 | + bool no_inplace_processing; 275 | + char* error_message_buffer; 276 | +} gmic_interface_options; 277 | + 278 | +#ifdef __cplusplus 279 | +extern "C" 280 | +{ 281 | +#endif 282 | + 283 | +GMIC_DLLINTERFACE int GMIC_CALLCONV gmic_delete_external(float* p); 284 | +GMIC_DLLINTERFACE int GMIC_CALLCONV gmic_call(const char* _cmd, unsigned int* _nofImages, 285 | + gmic_interface_image* _images, gmic_interface_options* _options); 286 | +GMIC_DLLINTERFACE const char* GMIC_CALLCONV gmic_get_stdlib(); 287 | + 288 | +#ifdef __cplusplus 289 | +} //#ifdef __cplusplus 290 | +#endif 291 | + 292 | +#endif // #ifndef _GMIC_LIBC_H 293 | diff --git a/extern/gmic64/libcgmicstatic.lib b/extern/gmic64/libcgmicstatic.lib 294 | new file mode 100644 295 | index 0000000000000000000000000000000000000000..21873539fab69ab4eccd9a6a5ae5b4773687a4dd 296 | GIT binary patch 297 | literal 2312 298 | zcmcIm&2G~`5dM;ov{(p5g#)dM6hx$!OX8++Py|9Ql&VqFv`W(hC&#r-8Yv-FVkB7ntv5e3HPDszXJ%)2X21Pr?MKheu66uE$)-ljnx+;bECFF3!Ci|gY9HkW~+r@B+b}P 302 | z%W)mU`QSR;cP2L)S4#E`$2EGcO|Jd@Bsnx*T7XxA06UKoB5`1ggwr`-nUEV$B_I(& 303 | z6t@sZg10d`-QOc8oyS8)l0zIl0CjwkhqQ#uteeV?K{tzOYem7px-v?Fj7=Ar! 305 | zcdEa*B1XLqb;#`sTn%QIAb-M+>2;)rL|fl&&){V;zC|YOTc1{ 307 | zLLi_6Ui8O8i%yLjJI&_hPd7)>y{zC}7XLdJ*@^uhSQyl-BrFrS?1Xv#1efHk5 310 | z+k8MZ_)1QB147?wL0oEz{SyS{+%@HYPWnBq9}i>?J<2-)vr^0_4ZN?)icE|uR$ 311 | C$>Ze! 312 | 313 | literal 0 314 | HcmV?d00001 315 | 316 | diff --git a/release/scripts/modules/pycompositor.py b/release/scripts/modules/pycompositor.py 317 | new file mode 100644 318 | index 0000000..048fd97 319 | --- /dev/null 320 | +++ b/release/scripts/modules/pycompositor.py 321 | @@ -0,0 +1,36 @@ 322 | + 323 | +import numpy as np 324 | +from PIL import Image, ImageOps 325 | + 326 | +def mix_arrays(a, b, factor): 327 | + """Basic linear interpolation, factor from 0.0 to 1.0""" 328 | + return a + max(min(factor, 1.0), 0.0) * (b - a) 329 | + 330 | +def get_array_rgb(rgba): 331 | + """Return RGB values of an RGBA 3D-array.""" 332 | + return rgba[...,0:3] 333 | + 334 | +def get_array_alpha(rgba): 335 | + """Return alpha values of an RGBA 3D-array.""" 336 | + return rgba[...,3] 337 | + 338 | +def array_to_pil(rgba): 339 | + """Convert a 3D floating point numpy array into a PIL image. Returns the image and 340 | + a meta object that should be passed to pil_to_array() once the image is 341 | + converted back into an array.""" 342 | + maxRgb = max(get_array_rgb(rgba).max(), 1.0) 343 | + maxAlpha = max(get_array_alpha(rgba).max(), 1.0) 344 | + 345 | + #Normalize values to 8-bit range (0-255) that Pillow can handle 346 | + norm = rgba / maxRgb * 255.0 #RGB values 347 | + norm[...,3] = rgba[...,3] / maxAlpha * 255.0 #Alpha value 348 | + im = ImageOps.flip(Image.fromarray(np.uint8(norm), "RGBA")) 349 | + return im, (maxRgb, maxAlpha) 350 | + 351 | +def pil_to_array(im, meta): 352 | + """Convert a PIL image into a 3D floating point numpy array.""" 353 | + #Restore values from 0-255 range to original floating point values 354 | + rgba = np.array(ImageOps.flip(im)) / 255.0 355 | + rgba[...,0:3] *= meta[0] 356 | + rgba[...,3] *= meta[1] 357 | + return rgba 358 | diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py 359 | index 8a8032e..de42f97 100644 360 | --- a/release/scripts/startup/nodeitems_builtins.py 361 | +++ b/release/scripts/startup/nodeitems_builtins.py 362 | @@ -367,6 +367,9 @@ compositor_node_categories = [ 363 | NodeItem("CompositorNodeInpaint"), 364 | NodeItem("CompositorNodeDBlur"), 365 | NodeItem("CompositorNodePixelate"), 366 | + NodeItem("CompositorNodeGmic"), 367 | + NodeItem("CompositorNodeGlsl"), 368 | + NodeItem("CompositorNodePython"), 369 | NodeItem("CompositorNodeSunBeams"), 370 | ]), 371 | CompositorNodeCategory("CMP_OP_VECTOR", "Vector", items=[ 372 | diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h 373 | index c364d0e..063ff16 100644 374 | --- a/source/blender/blenkernel/BKE_node.h 375 | +++ b/source/blender/blenkernel/BKE_node.h 376 | @@ -927,6 +927,9 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria 377 | #define CMP_NODE_TONEMAP 302 378 | #define CMP_NODE_LENSDIST 303 379 | #define CMP_NODE_SUNBEAMS 304 380 | +#define CMP_NODE_GMIC 305 //TODO use 400-range for future compat? 381 | +#define CMP_NODE_GLSL 306 382 | +#define CMP_NODE_PYTHON 307 383 | 384 | #define CMP_NODE_COLORCORRECTION 312 385 | #define CMP_NODE_MASK_BOX 313 386 | diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c 387 | index 2ca414c..22f11b0 100644 388 | --- a/source/blender/blenkernel/intern/node.c 389 | +++ b/source/blender/blenkernel/intern/node.c 390 | @@ -3469,7 +3469,10 @@ static void registerCompositNodes(void) 391 | register_node_type_cmp_despeckle(); 392 | register_node_type_cmp_defocus(); 393 | register_node_type_cmp_sunbeams(); 394 | - 395 | + register_node_type_cmp_gmic(); 396 | + register_node_type_cmp_glsl(); 397 | + register_node_type_cmp_python(); 398 | + 399 | register_node_type_cmp_valtorgb(); 400 | register_node_type_cmp_rgbtobw(); 401 | register_node_type_cmp_setalpha(); 402 | diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt 403 | index 3e1dd83..1728ab7 100644 404 | --- a/source/blender/compositor/CMakeLists.txt 405 | +++ b/source/blender/compositor/CMakeLists.txt 406 | @@ -31,6 +31,7 @@ set(INC 407 | ../blenkernel 408 | ../blenlib 409 | ../blentranslation 410 | + ../gpu 411 | ../imbuf 412 | ../makesdna 413 | ../makesrna 414 | @@ -41,12 +42,15 @@ set(INC 415 | ../render/extern/include 416 | ../render/intern/include 417 | ../../../extern/clew/include 418 | + ../../../extern/gmic64/include 419 | ../../../intern/guardedalloc 420 | + ../../../intern/glew-mx 421 | ../../../intern/atomic 422 | ) 423 | 424 | set(INC_SYS 425 | - 426 | + ${GLEW_INCLUDE_PATH} 427 | + ${PYTHON_INCLUDE_DIRS} 428 | ) 429 | 430 | set(SRC 431 | @@ -276,6 +280,24 @@ set(SRC 432 | operations/COM_VectorBlurOperation.cpp 433 | operations/COM_VectorBlurOperation.h 434 | 435 | + # GMIC 436 | + nodes/COM_GmicNode.cpp 437 | + nodes/COM_GmicNode.h 438 | + operations/COM_GmicOperation.cpp 439 | + operations/COM_GmicOperation.h 440 | + 441 | + # GLSL 442 | + nodes/COM_GlslNode.cpp 443 | + nodes/COM_GlslNode.h 444 | + operations/COM_GlslOperation.cpp 445 | + operations/COM_GlslOperation.h 446 | + 447 | + # Python 448 | + nodes/COM_PythonNode.cpp 449 | + nodes/COM_PythonNode.h 450 | + operations/COM_PythonOperation.cpp 451 | + operations/COM_PythonOperation.h 452 | + 453 | nodes/COM_FilterNode.cpp 454 | nodes/COM_FilterNode.h 455 | nodes/COM_DespeckleNode.cpp 456 | diff --git a/source/blender/compositor/intern/COM_Converter.cpp b/source/blender/compositor/intern/COM_Converter.cpp 457 | index 9fa59be..00cc2a4 100644 458 | --- a/source/blender/compositor/intern/COM_Converter.cpp 459 | +++ b/source/blender/compositor/intern/COM_Converter.cpp 460 | @@ -116,6 +116,9 @@ extern "C" { 461 | #include "COM_ViewerNode.h" 462 | #include "COM_ZCombineNode.h" 463 | #include "COM_PixelateNode.h" 464 | +#include "COM_GmicNode.h" 465 | +#include "COM_GlslNode.h" 466 | +#include "COM_PythonNode.h" 467 | #include "COM_PlaneTrackDeformNode.h" 468 | 469 | bool Converter::is_fast_node(bNode *b_node) 470 | @@ -403,6 +406,15 @@ Node *Converter::convert(bNode *b_node) 471 | case CMP_NODE_CORNERPIN: 472 | node = new CornerPinNode(b_node); 473 | break; 474 | + case CMP_NODE_GMIC: 475 | + node = new GmicNode(b_node); 476 | + break; 477 | + case CMP_NODE_GLSL: 478 | + node = new GlslNode(b_node); 479 | + break; 480 | + case CMP_NODE_PYTHON: 481 | + node = new PythonNode(b_node); 482 | + break; 483 | case CMP_NODE_SUNBEAMS: 484 | node = new SunBeamsNode(b_node); 485 | break; 486 | diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cpp b/source/blender/compositor/intern/COM_ExecutionGroup.cpp 487 | index 9a47c6b..4bf0ec8 100644 488 | --- a/source/blender/compositor/intern/COM_ExecutionGroup.cpp 489 | +++ b/source/blender/compositor/intern/COM_ExecutionGroup.cpp 490 | @@ -492,6 +492,8 @@ bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber) 491 | return false; 492 | } 493 | 494 | +extern "C" int wm_window_process_main_queue_events(); 495 | + 496 | bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph, int xChunk, int yChunk) 497 | { 498 | if (xChunk < 0 || xChunk >= (int)this->m_numberOfXChunks) { 499 | @@ -542,6 +544,11 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph, int xChun 500 | scheduleChunk(chunkNumber); 501 | } 502 | 503 | + if (BLI_thread_is_main()) { 504 | + // When Blender is run in background mode, we need to pump events manually here 505 | + wm_window_process_main_queue_events(); 506 | + } 507 | + 508 | return false; 509 | } 510 | 511 | diff --git a/source/blender/compositor/nodes/COM_GlslNode.cpp b/source/blender/compositor/nodes/COM_GlslNode.cpp 512 | new file mode 100644 513 | index 0000000..39418a7 514 | --- /dev/null 515 | +++ b/source/blender/compositor/nodes/COM_GlslNode.cpp 516 | @@ -0,0 +1,62 @@ 517 | + 518 | +#include "COM_GlslNode.h" 519 | + 520 | +#include "COM_GlslOperation.h" 521 | +#include "COM_GammaOperation.h" 522 | +#include "COM_ExecutionSystem.h" 523 | +#include "BKE_scene.h" 524 | +#include "BKE_global.h" 525 | +#include "BKE_main.h" 526 | +#include "BLI_path_util.h" 527 | + 528 | +GlslNode::GlslNode(bNode *editorNode) : Node(editorNode) 529 | +{ 530 | + /* pass */ 531 | +} 532 | + 533 | +void GlslNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const 534 | +{ 535 | + const NodeGlsl* rna = (NodeGlsl*)this->getbNode()->storage; 536 | + const Scene* scene = context.getScene(); 537 | + 538 | + char absolute[FILE_MAX] = { 0 }; 539 | + strcpy(absolute, rna->filepath); 540 | + BLI_path_abs(absolute, G.main->name); 541 | + 542 | + GlslOperationParams params; 543 | + params.absolute = absolute; 544 | + params.frameCurrent = BKE_scene_frame_get(scene); 545 | + params.frameTime = 0; //Set later 546 | + params.frameDelta = 1.0f / scene->r.frs_sec; //Assume steady progress 547 | + 548 | + GlslOperation *operation = new GlslOperation(); 549 | + operation->setParams(params); 550 | + operation->setData(rna); 551 | + converter.addOperation(operation); 552 | + 553 | + for (int i = 0; i < GLSL_CHANNELS; i++) { 554 | + bool linked = getInputSocket(i)->isLinked(); 555 | + operation->setChannelLinked(i, linked); 556 | + converter.mapInputSocket(getInputSocket(i), operation->getInputSocket(i)); 557 | + } 558 | + for (int i = GLSL_CHANNELS; i < GLSL_CHANNELS + GLSL_VALUE_SOCKETS; i++) { 559 | + converter.mapInputSocket(getInputSocket(i), operation->getInputSocket(i)); 560 | + } 561 | + 562 | + //Force node resolution to render settings 563 | + int w = (scene->r.size * scene->r.xsch) / 100; 564 | + int h = (scene->r.size * scene->r.ysch) / 100; 565 | + unsigned int size[2] = { w, h }; 566 | + operation->setResolution(size); 567 | + 568 | + if (rna->flag & CMP_NODE_GLSL_GAMMA) { 569 | + GammaOperation *gamma = new GammaOperation(); 570 | + converter.addOperation(gamma); 571 | + converter.addLink(operation->getOutputSocket(0), gamma->getInputSocket(0)); 572 | + converter.addInputValue(gamma->getInputSocket(1), 2.2f); //For OpenGL sRGB... 573 | + converter.mapOutputSocket(this->getOutputSocket(0), gamma->getOutputSocket(0)); 574 | + } 575 | + else { 576 | + converter.mapOutputSocket(this->getOutputSocket(0), operation->getOutputSocket(0)); 577 | + } 578 | +} 579 | diff --git a/source/blender/compositor/nodes/COM_GlslNode.h b/source/blender/compositor/nodes/COM_GlslNode.h 580 | new file mode 100644 581 | index 0000000..bba75cf 582 | --- /dev/null 583 | +++ b/source/blender/compositor/nodes/COM_GlslNode.h 584 | @@ -0,0 +1,13 @@ 585 | + 586 | +#ifndef _COM_GlslNode_h_ 587 | +#define _COM_GlslNode_h_ 588 | + 589 | +#include "COM_Node.h" 590 | + 591 | +class GlslNode : public Node { 592 | +public: 593 | + GlslNode(bNode *editorNode); 594 | + void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; 595 | +}; 596 | + 597 | +#endif 598 | diff --git a/source/blender/compositor/nodes/COM_GmicNode.cpp b/source/blender/compositor/nodes/COM_GmicNode.cpp 599 | new file mode 100644 600 | index 0000000..02c998e 601 | --- /dev/null 602 | +++ b/source/blender/compositor/nodes/COM_GmicNode.cpp 603 | @@ -0,0 +1,25 @@ 604 | + 605 | +#include "COM_GmicNode.h" 606 | + 607 | +#include "COM_GmicOperation.h" 608 | +#include "COM_ExecutionSystem.h" 609 | + 610 | +GmicNode::GmicNode(bNode *editorNode) : Node(editorNode) 611 | +{ 612 | + /* pass */ 613 | +} 614 | + 615 | +void GmicNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const 616 | +{ 617 | + GmicOperation *operation = new GmicOperation(); 618 | + 619 | + const NodeGmic* rna = (NodeGmic*)this->getbNode()->storage; 620 | + operation->setData(rna); 621 | + 622 | + converter.addOperation(operation); 623 | + 624 | + for (int i = 0; i < (1 + 5); i++) { 625 | + converter.mapInputSocket(this->getInputSocket(i), operation->getInputSocket(i)); 626 | + } 627 | + converter.mapOutputSocket(this->getOutputSocket(0), operation->getOutputSocket(0)); 628 | +} 629 | diff --git a/source/blender/compositor/nodes/COM_GmicNode.h b/source/blender/compositor/nodes/COM_GmicNode.h 630 | new file mode 100644 631 | index 0000000..658bace 632 | --- /dev/null 633 | +++ b/source/blender/compositor/nodes/COM_GmicNode.h 634 | @@ -0,0 +1,13 @@ 635 | + 636 | +#ifndef _COM_GeglNode_h_ 637 | +#define _COM_GeglNode_h_ 638 | + 639 | +#include "COM_Node.h" 640 | + 641 | +class GmicNode : public Node { 642 | +public: 643 | + GmicNode(bNode *editorNode); 644 | + void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; 645 | +}; 646 | + 647 | +#endif 648 | diff --git a/source/blender/compositor/nodes/COM_PythonNode.cpp b/source/blender/compositor/nodes/COM_PythonNode.cpp 649 | new file mode 100644 650 | index 0000000..743ed5c 651 | --- /dev/null 652 | +++ b/source/blender/compositor/nodes/COM_PythonNode.cpp 653 | @@ -0,0 +1,31 @@ 654 | + 655 | +#include "COM_PythonNode.h" 656 | + 657 | +#include "COM_PythonOperation.h" 658 | +#include "BKE_global.h" 659 | +#include "BKE_main.h" 660 | +#include "BLI_path_util.h" 661 | + 662 | +PythonNode::PythonNode(bNode *editorNode) : Node(editorNode) 663 | +{ 664 | + /* pass */ 665 | +} 666 | + 667 | +void PythonNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const 668 | +{ 669 | + const NodePython* rna = (NodePython*)getbNode()->storage; 670 | + 671 | + char absolute[FILE_MAX] = { 0 }; 672 | + strcpy(absolute, rna->filepath); 673 | + BLI_path_abs(absolute, G.main->name); 674 | + 675 | + PythonOperation *operation = new PythonOperation(); 676 | + operation->setData(rna); 677 | + operation->setPath(absolute); 678 | + 679 | + converter.addOperation(operation); 680 | + for (int i = 0; i < COM_PYTHON_INPUT_IMAGES + COM_PYTHON_INPUT_VALUES; i++) { 681 | + converter.mapInputSocket(getInputSocket(i), operation->getInputSocket(i)); 682 | + } 683 | + converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); 684 | +} 685 | diff --git a/source/blender/compositor/nodes/COM_PythonNode.h b/source/blender/compositor/nodes/COM_PythonNode.h 686 | new file mode 100644 687 | index 0000000..59316af 688 | --- /dev/null 689 | +++ b/source/blender/compositor/nodes/COM_PythonNode.h 690 | @@ -0,0 +1,13 @@ 691 | + 692 | +#ifndef _COM_PythonNode_h_ 693 | +#define _COM_PythonNode_h_ 694 | + 695 | +#include "COM_Node.h" 696 | + 697 | +class PythonNode : public Node { 698 | +public: 699 | + PythonNode(bNode *editorNode); 700 | + void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; 701 | +}; 702 | + 703 | +#endif 704 | diff --git a/source/blender/compositor/operations/COM_GlslOperation.cpp b/source/blender/compositor/operations/COM_GlslOperation.cpp 705 | new file mode 100644 706 | index 0000000..5f7eb8c 707 | --- /dev/null 708 | +++ b/source/blender/compositor/operations/COM_GlslOperation.cpp 709 | @@ -0,0 +1,362 @@ 710 | + 711 | +#include "BKE_global.h" 712 | +#include "WM_api.h" 713 | +#include "GPU_framebuffer.h" 714 | +#include "GPU_texture.h" 715 | +#include "GPU_shader.h" 716 | +#include "GPU_glew.h" 717 | + 718 | +#include 719 | + 720 | +#include "COM_GlslOperation.h" 721 | + 722 | +GlslOperation::GlslOperation() : SingleThreadedOperation() 723 | +{ 724 | + for (int i = 0; i < GLSL_CHANNELS; i++) { 725 | + this->addInputSocket(COM_DT_COLOR); 726 | + this->m_channelLinked[i] = false; 727 | + } 728 | + for (int i = 0; i < GLSL_VALUE_SOCKETS; i++) { 729 | + this->addInputSocket(COM_DT_VALUE); 730 | + } 731 | + this->addOutputSocket(COM_DT_COLOR); 732 | + this->setResolutionInputSocketIndex(0); 733 | + this->m_data = NULL; 734 | +} 735 | + 736 | +void GlslOperation::initExecution() 737 | +{ 738 | + SingleThreadedOperation::initExecution(); 739 | +} 740 | + 741 | +void GlslOperation::deinitExecution() 742 | +{ 743 | + SingleThreadedOperation::deinitExecution(); 744 | +} 745 | + 746 | +struct GlslChannelInput { 747 | + float pixel[4]; 748 | + float* rgba; 749 | + int width; 750 | + int height; 751 | +}; 752 | + 753 | +struct ThreadResult { 754 | + ThreadCondition condition; 755 | + ThreadMutex *mutex; 756 | + const GlslOperationParams *params; 757 | + GlslChannelInput inputs[GLSL_CHANNELS]; 758 | + std::string script; 759 | + int width; 760 | + int height; 761 | + float *output; 762 | +}; 763 | + 764 | +const char* GLSL_NODE_UNIFORMS = 765 | +"uniform vec3 iResolution;\n" 766 | +"uniform float iTime;\n" 767 | +"uniform float iTimeDelta;\n" 768 | +"uniform float iFrame;\n" 769 | +"uniform float iChannelTime[4];\n" 770 | +"uniform vec4 iMouse;\n" 771 | +"uniform vec4 iDate;\n" 772 | +"uniform float iSampleRate;\n" 773 | +"uniform vec3 iChannelResolution[4];\n" 774 | +"uniform sampler2D iChannel0;\n" 775 | +"uniform sampler2D iChannel1;\n" 776 | +"uniform sampler2D iChannel2;\n" 777 | +"uniform sampler2D iChannel3;\n" 778 | +"uniform vec4 userInput;\n" //Our addition 779 | +; 780 | + 781 | +const char *GLSL_DEFINES = 782 | +"#define input0 userInput.x\n" 783 | +"#define input1 userInput.y\n" 784 | +"#define input2 userInput.z\n" 785 | +"#define input3 userInput.w\n" 786 | +; 787 | + 788 | +const char *GLSL_NODE_VS = 789 | +"in vec4 position;\n" 790 | +"out vec2 varTex;\n" 791 | + 792 | +"void main() {\n" 793 | +" gl_Position.xyz = vec3(position.x, position.y, 0.0);\n" 794 | +" gl_Position.w = 1.0;\n" 795 | +" varTex = position.zw;\n" 796 | +"}" 797 | +; 798 | + 799 | +const char *GLSL_NODE_PS = 800 | +"in vec2 varTex;\n" 801 | +"out vec4 glFragColor;\n" 802 | + 803 | +"void main() {\n" 804 | +" mainImage(glFragColor, gl_FragCoord.xy);\n" 805 | +"}\n" 806 | +; 807 | + 808 | +const float FULLSCREEN_QUAD[] = { 809 | + -1, -1, 0.0, 0.0, //Bottom left 810 | + 1, -1, 1.0, 0.0, //Bottom right 811 | + - 1, 1, 0.0, 1.0, //Top left 812 | + 1, 1, 1.0, 1.0, //Top right 813 | +}; 814 | + 815 | +GPUShader *createShader(ThreadResult *data) 816 | +{ 817 | + if (data->script.empty()) { 818 | + return NULL; 819 | + } 820 | + 821 | + std::string fragment; 822 | + fragment += GLSL_NODE_UNIFORMS; 823 | + fragment += data->script; 824 | + fragment += "\n"; 825 | + fragment += GLSL_NODE_PS; 826 | + 827 | + return GPU_shader_create(GLSL_NODE_VS, fragment.c_str(), NULL, NULL, GLSL_DEFINES, 0, 0, 0); 828 | +} 829 | + 830 | +void setFloat(GPUShader *shader, const char *name, const float *value, int length) 831 | +{ 832 | + int location = GPU_shader_get_uniform(shader, name); 833 | + if (location != -1) { 834 | + GPU_shader_uniform_vector(shader, location, length, 1, value); 835 | + } 836 | +} 837 | + 838 | +void setUniforms(GPUShader *shader, ThreadResult *data) 839 | +{ 840 | + float value[4] = { 0 }; 841 | + 842 | + value[0] = data->width; 843 | + value[1] = data->height; 844 | + value[3] = 1; 845 | + setFloat(shader, "iResolution", value, 3); 846 | + 847 | + value[0] = data->params->frameTime; 848 | + setFloat(shader, "iTime", value, 1); 849 | + 850 | + value[0] = data->params->frameDelta; 851 | + setFloat(shader, "iTimeDelta", value, 1); 852 | + 853 | + value[0] = data->params->frameCurrent; 854 | + setFloat(shader, "iFrame", value, 1); 855 | + 856 | + float resolutions[3 * GLSL_CHANNELS]; 857 | + for (int i = 0; i < GLSL_CHANNELS; i++) { 858 | + int index = i * 3; 859 | + resolutions[index] = data->inputs[i].width; 860 | + resolutions[index + 1] = data->inputs[i].height; 861 | + resolutions[index + 2] = 1; 862 | + } 863 | + GPU_shader_uniform_vector(shader, GPU_shader_get_uniform(shader, "iChannelResolution"), 3, GLSL_CHANNELS, resolutions); 864 | + 865 | + setFloat(shader, "userInput", data->params->input0, 4); 866 | +} 867 | + 868 | +void drawImage(GPUShader *shader, ThreadResult *data, std::vector *textures) 869 | +{ 870 | + GPU_shader_bind(shader); 871 | + 872 | + setUniforms(shader, data); 873 | + 874 | + for (int i = 0; i < textures->size(); i++) { 875 | + std::string name = "iChannel" + std::to_string(i); 876 | + int loc = GPU_shader_get_uniform(shader, name.c_str()); 877 | + if (textures->at(i) && loc != -1) { 878 | + GPU_texture_bind(textures->at(i), i); 879 | + GPU_shader_uniform_texture(shader, loc, textures->at(i)); 880 | + } 881 | + } 882 | + 883 | + int index = GPU_shader_get_attribute(shader, "position"); 884 | + glVertexAttribPointer(index, 4, GL_FLOAT, GL_FALSE, 0, FULLSCREEN_QUAD); 885 | + glEnableVertexAttribArray(index); 886 | + 887 | + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 888 | + 889 | + glDisableVertexAttribArray(index); 890 | + 891 | + for (int i = 0; i < textures->size(); i++) { 892 | + if (textures->at(i)) { 893 | + GPU_texture_unbind(textures->at(i)); 894 | + } 895 | + } 896 | + GPU_shader_unbind(); 897 | +} 898 | + 899 | +std::vector createTextures(ThreadResult *data) 900 | +{ 901 | + std::vector results; 902 | + char error[256]; 903 | + 904 | + for (int i = 0; i < GLSL_CHANNELS; i++) { 905 | + GlslChannelInput *input = &data->inputs[i]; 906 | + GPUTexture *tex = NULL; 907 | + if (input->rgba) { 908 | + tex = GPU_texture_create_2D(input->width, input->height, input->rgba, GPU_HDR_NONE, error); 909 | + if (tex) { 910 | + GPU_texture_bind(tex, 0); 911 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 912 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 913 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 914 | + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 915 | + GPU_texture_unbind(tex); 916 | + } 917 | + } 918 | + results.push_back(tex); 919 | + } 920 | + return results; 921 | +} 922 | + 923 | +void freeTextures(std::vector *textures) 924 | +{ 925 | + for (int i = 0; i < textures->size(); i++) { 926 | + if (textures->at(i)) { 927 | + GPU_texture_free(textures->at(i)); 928 | + } 929 | + } 930 | + textures->clear(); 931 | +} 932 | + 933 | +void glslMainThreadCallback(void* userData) 934 | +{ 935 | + BLI_assert(BLI_thread_is_main()); 936 | + 937 | + ThreadResult* data = (ThreadResult*)userData; 938 | + 939 | + std::vector textures = createTextures(data); 940 | + 941 | + GPUShader *shader = createShader(data); 942 | + if (shader) { 943 | + char error[256]; 944 | + GPUOffScreen *offscreen = GPU_offscreen_create(data->width, data->height, 0, error); 945 | + 946 | + bool resized = false; 947 | + if (offscreen) { 948 | + if (GPU_offscreen_width(offscreen) != data->width || GPU_offscreen_height(offscreen) != data->height) { 949 | + printf("Invalid offscreen size!\n"); 950 | + resized = true; 951 | + } 952 | + } 953 | + else { 954 | + printf("Can't create an offscreen buffer: %s, %ix%i\n", error, data->width, data->height); 955 | + } 956 | + 957 | + if (offscreen) { 958 | + GPU_offscreen_bind(offscreen, true); 959 | + 960 | + if (!resized) { 961 | + drawImage(shader, data, &textures); 962 | + 963 | + GPU_offscreen_read_pixels(offscreen, GL_FLOAT, data->output); 964 | + } 965 | + GPU_offscreen_unbind(offscreen, true); 966 | + 967 | + GPU_offscreen_free(offscreen); 968 | + } 969 | + GPU_shader_free(shader); 970 | + } 971 | + 972 | + freeTextures(&textures); 973 | + 974 | + BLI_condition_notify_all(&data->condition); 975 | +} 976 | + 977 | +std::string readScriptContent(const std::string& path) 978 | +{ 979 | + std::ifstream input(path); 980 | + std::stringstream buffer; 981 | + buffer << input.rdbuf(); 982 | + return buffer.str(); 983 | +} 984 | + 985 | +MemoryBuffer *GlslOperation::createMemoryBuffer(rcti *source) 986 | +{ 987 | + rcti rect; 988 | + rect.xmin = 0; 989 | + rect.ymin = 0; 990 | + rect.xmax = source->xmax; 991 | + rect.ymax = source->ymax; 992 | + MemoryBuffer *result = new MemoryBuffer(COM_DT_COLOR, &rect); 993 | + 994 | + const float invalid[] = { 1, 0, 0, 1 }; 995 | + for (int y = 0; y < rect.ymax; y++) { 996 | + for (int x = 0; x < rect.xmax; x++) { 997 | + result->writePixel(x, y, invalid); 998 | + } 999 | + } 1000 | + 1001 | + if (G.background) { 1002 | + printf("GLSL node not supported in background mode\n"); 1003 | + return result; 1004 | + } 1005 | + 1006 | + ThreadResult threadResult; 1007 | + threadResult.condition = NULL; 1008 | + threadResult.mutex = BLI_mutex_alloc(); 1009 | + BLI_condition_init(&threadResult.condition); 1010 | + 1011 | + memset(threadResult.inputs, 0, sizeof(threadResult.inputs)); 1012 | + for (int i = 0; i < GLSL_CHANNELS; i++) { 1013 | + GlslChannelInput *input = &threadResult.inputs[i]; 1014 | + SocketReader *reader = getInputSocketReader(i); 1015 | + 1016 | + if (m_channelLinked[i]) { 1017 | + MemoryBuffer *tile = (MemoryBuffer*)reader->initializeTileData(NULL); 1018 | + if (tile) { 1019 | + input->width = tile->getWidth(); 1020 | + input->height = tile->getHeight(); 1021 | + input->rgba = tile->getBuffer(); 1022 | + } 1023 | + } 1024 | + else { 1025 | + //Save memory by using a single color 1x1 texture 1026 | + input->width = 1; 1027 | + input->height = 1; 1028 | + input->rgba = input->pixel; 1029 | + reader->readSampled(input->rgba, 0, 0, COM_PS_NEAREST); 1030 | + } 1031 | + } 1032 | + 1033 | + float value[4] = {}; 1034 | + getInputSocketReader(GLSL_CHANNELS)->readSampled(value, 0, 0, COM_PS_NEAREST); 1035 | + m_params.frameTime = value[0]; 1036 | + 1037 | + for (int i = 0; i < 4; i++) { 1038 | + getInputSocketReader(GLSL_CHANNELS + 1 + i)->readSampled(value, 0, 0, COM_PS_NEAREST); 1039 | + m_params.input0[i] = value[0]; 1040 | + } 1041 | + 1042 | + threadResult.params = &m_params; 1043 | + threadResult.width = rect.xmax; 1044 | + threadResult.height = rect.ymax; 1045 | + threadResult.output = result->getBuffer(); 1046 | + threadResult.script = readScriptContent(m_params.absolute); 1047 | + 1048 | + // Run OpenGL operations in the main thread and wait until they are ready 1049 | + WM_run_in_main_thread(glslMainThreadCallback, &threadResult); 1050 | + BLI_condition_wait(&threadResult.condition, threadResult.mutex); 1051 | + 1052 | + BLI_condition_end(&threadResult.condition); 1053 | + BLI_mutex_free(threadResult.mutex); 1054 | + 1055 | + return result; 1056 | +} 1057 | + 1058 | +bool GlslOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) 1059 | +{ 1060 | + if (isCached()) { 1061 | + return false; 1062 | + } 1063 | + else { 1064 | + rcti newInput; 1065 | + newInput.xmin = 0; 1066 | + newInput.ymin = 0; 1067 | + newInput.xmax = this->getWidth(); 1068 | + newInput.ymax = this->getHeight(); 1069 | + return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); 1070 | + } 1071 | +} 1072 | diff --git a/source/blender/compositor/operations/COM_GlslOperation.h b/source/blender/compositor/operations/COM_GlslOperation.h 1073 | new file mode 100644 1074 | index 0000000..9f8cb45 1075 | --- /dev/null 1076 | +++ b/source/blender/compositor/operations/COM_GlslOperation.h 1077 | @@ -0,0 +1,42 @@ 1078 | + 1079 | +#ifndef _COM_GlslOperation_h_ 1080 | +#define _COM_GlslOperation_h_ 1081 | + 1082 | +#include 1083 | + 1084 | +#include "COM_NodeOperation.h" 1085 | +#include "COM_SingleThreadedOperation.h" 1086 | + 1087 | +#define GLSL_CHANNELS 4 1088 | +#define GLSL_VALUE_SOCKETS 5 1089 | + 1090 | +struct GlslOperationParams { 1091 | + std::string absolute; 1092 | + float input0[4]; 1093 | + float frameCurrent; 1094 | + float frameTime; 1095 | + float frameDelta; 1096 | +}; 1097 | + 1098 | +class GlslOperation : public SingleThreadedOperation { 1099 | +private: 1100 | + GlslOperationParams m_params; 1101 | + const NodeGlsl *m_data; 1102 | + bool m_channelLinked[GLSL_CHANNELS]; 1103 | +public: 1104 | + GlslOperation(); 1105 | + 1106 | + void initExecution(); 1107 | + void deinitExecution(); 1108 | + bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output); 1109 | + 1110 | + void setParams(const GlslOperationParams& params) { this->m_params = params; } 1111 | + void setData(const NodeGlsl *data) { this->m_data = data; } 1112 | + void setChannelLinked(int index, bool linked) { this->m_channelLinked[index] = linked; } 1113 | + 1114 | +protected: 1115 | + 1116 | + MemoryBuffer *createMemoryBuffer(rcti *rect); 1117 | +}; 1118 | + 1119 | +#endif 1120 | diff --git a/source/blender/compositor/operations/COM_GmicOperation.cpp b/source/blender/compositor/operations/COM_GmicOperation.cpp 1121 | new file mode 100644 1122 | index 0000000..813fcdc 1123 | --- /dev/null 1124 | +++ b/source/blender/compositor/operations/COM_GmicOperation.cpp 1125 | @@ -0,0 +1,343 @@ 1126 | + 1127 | +#include 1128 | +#include 1129 | +#include 1130 | +#include "gmic_libc.h" 1131 | +#include "WM_api.h" 1132 | + 1133 | +extern "C" { 1134 | +#include "BKE_appdir.h" 1135 | +} 1136 | + 1137 | +#include "COM_GmicOperation.h" 1138 | + 1139 | +GmicOperation::GmicOperation() : SingleThreadedOperation() 1140 | +{ 1141 | + this->addInputSocket(COM_DT_COLOR); 1142 | + for (int i = 0; i < 5; i++) { 1143 | + this->addInputSocket(COM_DT_VALUE); 1144 | + } 1145 | + 1146 | + this->addOutputSocket(COM_DT_COLOR); 1147 | + this->setResolutionInputSocketIndex(0); 1148 | + this->m_inputProgram = NULL; 1149 | + this->m_data = NULL; 1150 | +} 1151 | + 1152 | +void GmicOperation::initExecution() 1153 | +{ 1154 | + SingleThreadedOperation::initExecution(); 1155 | + this->m_inputProgram = getInputSocketReader(0); 1156 | +} 1157 | + 1158 | +void GmicOperation::deinitExecution() 1159 | +{ 1160 | + this->m_inputProgram = NULL; 1161 | + SingleThreadedOperation::deinitExecution(); 1162 | +} 1163 | + 1164 | +std::string readGimpCommands() 1165 | +{ 1166 | + std::string path = std::string(BKE_appdir_program_dir()) + "gimp.gmic"; 1167 | + printf("Trying to read GIMP commands from file: %s...\n", path.c_str()); 1168 | + 1169 | + std::ifstream filters(path); 1170 | + std::stringstream buffer; 1171 | + buffer << filters.rdbuf(); 1172 | + return buffer.str(); 1173 | +} 1174 | + 1175 | +const std::string& getGimpCommands() 1176 | +{ 1177 | + static std::string commands = readGimpCommands(); //Never freed 1178 | + return commands; 1179 | +} 1180 | + 1181 | +struct GmicFilterParam { 1182 | + std::string key; 1183 | + std::string options; 1184 | +}; 1185 | + 1186 | +struct GmicFilterHelp { 1187 | + std::string name; 1188 | + std::string command; 1189 | + std::vector parameters; 1190 | +}; 1191 | + 1192 | +std::vector readGmicHelp() 1193 | +{ 1194 | + std::vector results; 1195 | + 1196 | + const std::string& commands = getGimpCommands(); 1197 | + 1198 | + std::stringstream ss(commands); 1199 | + std::string to; 1200 | + 1201 | + const std::string tag = "#@gimp "; 1202 | + GmicFilterHelp* current = NULL; 1203 | + int mergeCount = 0; 1204 | + 1205 | + while (std::getline(ss, to, '\n')) { 1206 | + bool skipFirst = false; 1207 | + if (to.find(tag, 0) == 0 && to.find(" : ") != std::string::npos && current == NULL) { 1208 | + delete current; 1209 | + current = new GmicFilterHelp(); 1210 | + mergeCount = 0; 1211 | + 1212 | + size_t start = to.find(" : ") + 3; 1213 | + size_t end = to.find(",", start); 1214 | + current->name = to.substr(tag.size(), start - tag.size() - 3); 1215 | + current->command = to.substr(start, end - start); 1216 | + results.push_back(current); 1217 | + skipFirst = true; 1218 | + } 1219 | + else if (to.find("#") != 0) { 1220 | + current = NULL; 1221 | + } 1222 | + 1223 | + if (current && !skipFirst) { 1224 | + size_t start = to.find(" : "); 1225 | + if (start != std::string::npos) { 1226 | + std::string body = to.substr(start + 3); 1227 | + std::string key = body.substr(0, body.find(" = ")); 1228 | + std::string options = body.substr(body.find(" = ") + 3); 1229 | + 1230 | + if (mergeCount > 0) { 1231 | + current->parameters[current->parameters.size() - 1]->options += body; 1232 | + } 1233 | + else { 1234 | + GmicFilterParam *param = new GmicFilterParam(); 1235 | + param->key = key; 1236 | + param->options = options; 1237 | + current->parameters.push_back(param); 1238 | + } 1239 | + 1240 | + mergeCount += std::count(body.begin(), body.end(), '{'); 1241 | + mergeCount -= std::count(body.begin(), body.end(), '}'); 1242 | + } 1243 | + } 1244 | + } 1245 | + printf("Found help for %i G'MIC filters\n", (int)results.size()); 1246 | + 1247 | + return results; 1248 | +} 1249 | + 1250 | +const std::vector& getGmicHelp() 1251 | +{ 1252 | + static std::vector help = readGmicHelp(); //Never freed 1253 | + return help; 1254 | +} 1255 | + 1256 | +std::vector findCommands(const char *command) 1257 | +{ 1258 | + const size_t len = strlen(command); 1259 | + const size_t minCommandLen = 1; 1260 | + 1261 | + std::vector results; 1262 | + std::string current; 1263 | + 1264 | + bool inCommand = false; 1265 | + for (int i = 0; i < len; i++) { 1266 | + char tok = command[i]; 1267 | + 1268 | + if (tok == '-') { 1269 | + inCommand = true; 1270 | + } 1271 | + else if (isspace(tok)) { 1272 | + inCommand = false; 1273 | + if (current.size() > minCommandLen) { 1274 | + results.push_back(current); 1275 | + } 1276 | + current = ""; 1277 | + } 1278 | + 1279 | + if (inCommand && tok != '-') { 1280 | + current += tok; 1281 | + } 1282 | + } 1283 | + if (current.size() > minCommandLen) { 1284 | + results.push_back(current); 1285 | + } 1286 | + 1287 | + //Remove duplicates 1288 | + std::sort(results.begin(), results.end()); 1289 | + results.erase(std::unique(results.begin(), results.end()), results.end()); 1290 | + 1291 | + return results; 1292 | +} 1293 | + 1294 | +extern "C" char** explainGmicCommands(const char *command, int *resultCount) 1295 | +{ 1296 | + std::vector commands = findCommands(command); 1297 | + const std::vector& help = getGmicHelp(); 1298 | + std::vector found; 1299 | + 1300 | + for (size_t i = 0; i < commands.size(); i++) { 1301 | + for (size_t x = 0; x < help.size(); x++) { 1302 | + if (commands[i] == help[x]->command) { 1303 | + found.push_back(help[x]); 1304 | + break; 1305 | + } 1306 | + } 1307 | + } 1308 | + 1309 | + std::vector results; 1310 | + for (size_t i = 0; i < found.size(); i++) { 1311 | + GmicFilterHelp *help = found[i]; 1312 | + results.push_back(help->name); 1313 | + results.push_back(""); 1314 | + 1315 | + for (size_t x = 0; x < help->parameters.size(); x++) { 1316 | + GmicFilterParam *param = help->parameters[x]; 1317 | + if (param->key != "Sep") { 1318 | + results.push_back(param->key); 1319 | + results.push_back(param->options); 1320 | + } 1321 | + } 1322 | + } 1323 | + 1324 | + const int count = results.size(); 1325 | + *resultCount = count; 1326 | + char** texts = new char*[count]; 1327 | + 1328 | + for (size_t i = 0; i < count; i++) { 1329 | + std::string& entry = results[i]; 1330 | + char *data = new char[entry.size() + 1]; 1331 | + std::copy(entry.begin(), entry.end(), data); 1332 | + data[entry.size()] = '\0'; 1333 | + texts[i] = data; 1334 | + } 1335 | + return texts; 1336 | +} 1337 | + 1338 | +extern "C" void freeGmicExplainCommands(char **results, int resultCount) 1339 | +{ 1340 | + for (int i = 0; i < resultCount; i++) { 1341 | + delete[] results[i]; 1342 | + } 1343 | + delete[] results; 1344 | +} 1345 | + 1346 | +MemoryBuffer *GmicOperation::createMemoryBuffer(rcti *source) 1347 | +{ 1348 | + MemoryBuffer *tile = (MemoryBuffer*)this->m_inputProgram->initializeTileData(source); 1349 | + 1350 | + rcti rect; 1351 | + rect.xmin = 0; 1352 | + rect.ymin = 0; 1353 | + rect.xmax = source->xmax; 1354 | + rect.ymax = source->ymax; 1355 | + MemoryBuffer *result = new MemoryBuffer(COM_DT_COLOR, &rect); 1356 | + 1357 | + gmic_interface_image images[10]; //Hardcoded amount in C-API, why...? 1358 | + memset(&images, 0, sizeof(images)); 1359 | + strcpy(images[0].name, "image0"); 1360 | + images[0].width = rect.xmax; 1361 | + images[0].height = rect.ymax; 1362 | + images[0].spectrum = 4; 1363 | + images[0].depth = 1; 1364 | + images[0].is_interleaved = true; 1365 | + images[0].format = E_FORMAT_FLOAT; 1366 | + 1367 | + const size_t image_size = images[0].width*images[0].height*images[0].spectrum*images[0].depth * sizeof(float); 1368 | + float *data = (float*)malloc(image_size); 1369 | + memcpy(data, tile->getBuffer(), image_size); 1370 | + images[0].data = data; 1371 | + 1372 | + gmic_interface_options options; 1373 | + memset(&options, 0, sizeof(options)); 1374 | + options.ignore_stdlib = false; 1375 | + bool abort = false; 1376 | + float progress = 0.0f; 1377 | + options.p_is_abort = &abort; 1378 | + options.p_progress = &progress; 1379 | + options.interleave_output = true; 1380 | + options.no_inplace_processing = true; 1381 | + options.output_format = E_FORMAT_FLOAT; 1382 | + 1383 | + const std::string& gimpCommands = getGimpCommands(); 1384 | + if (gimpCommands.size() > 0) { 1385 | + printf("Found GIMP commands!\n"); 1386 | + options.custom_commands = gimpCommands.c_str(); 1387 | + } 1388 | + else { 1389 | + printf("No GIMP commands found\n"); 1390 | + } 1391 | + 1392 | + std::string interpolation = "1"; 1393 | + std::string command; 1394 | + 1395 | + // Add argument variables 1396 | + for (int i = 0; i < 5; i++) { 1397 | + float value[4]; 1398 | + this->getInputSocketReader(i + 1)->readSampled(&value[0], 0, 0, COM_PS_NEAREST); 1399 | + command += "arg" + std::to_string(i + 1) + "=" + std::to_string(value[0]) + " "; 1400 | + } 1401 | + 1402 | + // Downscale 1403 | + int scale = std::min(std::max((int)(this->m_data->quality * 100), 10), 100); 1404 | + command += "--resize2dx " + std::to_string(scale) + "%," + interpolation + " "; 1405 | + command += "-remove[0] "; 1406 | + 1407 | + const bool normalize = this->m_data->flag & CMP_NODE_GMIC_NORMALIZE; 1408 | + if (normalize) { 1409 | + command += " -n 0,255 -rgb2srgb -mirror[-1] y "; 1410 | + } 1411 | + 1412 | + // Add the real user command 1413 | + command.append(m_data->command); 1414 | + 1415 | + if (normalize) { 1416 | + command += " -mirror[-1] y -srgb2rgb -n 0,1 "; 1417 | + } 1418 | + // Upscale last image to correct size and format 1419 | + command += " --resize[-1] " + std::to_string(rect.xmax) + "," + std::to_string(rect.ymax) + ",1,4," + interpolation + " "; 1420 | + 1421 | + unsigned int image_count = 1; 1422 | + int error = gmic_call(command.c_str(), &image_count, &images[0], &options); 1423 | + printf("Full GMIC command: '%s', error code: %i\n", command.c_str(), error); 1424 | + 1425 | + for (int i = 0; i < image_count; ++i) { 1426 | + printf("Image %u: %ux%u Depth: %u Spectrum: %u\n", i, images[i].width, images[i].height, images[i].depth, images[i].spectrum); 1427 | + } 1428 | + 1429 | + if (error == 0) { 1430 | + // All ok, copy the final image into the result buffer 1431 | + gmic_interface_image *final_image = &images[image_count - 1]; 1432 | + memcpy(result->getBuffer(), final_image->data, image_size); 1433 | + } 1434 | + else { 1435 | + // An error occurred, fill the result buffer with a solid color 1436 | + float invalid[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; 1437 | + for (int y = 0; y < rect.ymax; y++) { 1438 | + for (int x = 0; x < rect.xmax; x++) { 1439 | + result->writePixel(x, y, invalid); 1440 | + } 1441 | + } 1442 | + } 1443 | + 1444 | + // Free all temporary buffers 1445 | + for (int i = 0; i < image_count; ++i) { 1446 | + if (images[i].data != data) { 1447 | + gmic_delete_external((float*)images[i].data); 1448 | + } 1449 | + } 1450 | + free(data); 1451 | + 1452 | + return result; 1453 | +} 1454 | + 1455 | +bool GmicOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) 1456 | +{ 1457 | + if (isCached()) { 1458 | + return false; 1459 | + } 1460 | + else { 1461 | + rcti newInput; 1462 | + newInput.xmin = 0; 1463 | + newInput.ymin = 0; 1464 | + newInput.xmax = this->getWidth(); 1465 | + newInput.ymax = this->getHeight(); 1466 | + return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); 1467 | + } 1468 | +} 1469 | diff --git a/source/blender/compositor/operations/COM_GmicOperation.h b/source/blender/compositor/operations/COM_GmicOperation.h 1470 | new file mode 100644 1471 | index 0000000..234d745 1472 | --- /dev/null 1473 | +++ b/source/blender/compositor/operations/COM_GmicOperation.h 1474 | @@ -0,0 +1,26 @@ 1475 | + 1476 | +#ifndef _COM_GeglOperation_h_ 1477 | +#define _COM_GeglOperation_h_ 1478 | + 1479 | +#include "COM_NodeOperation.h" 1480 | +#include "COM_SingleThreadedOperation.h" 1481 | + 1482 | +class GmicOperation : public SingleThreadedOperation { 1483 | +private: 1484 | + SocketReader *m_inputProgram; 1485 | + const NodeGmic *m_data; 1486 | +public: 1487 | + GmicOperation(); 1488 | + 1489 | + void initExecution(); 1490 | + void deinitExecution(); 1491 | + bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output); 1492 | + 1493 | + void setData(const NodeGmic *data) { this->m_data = data; } 1494 | + 1495 | +protected: 1496 | + 1497 | + MemoryBuffer *createMemoryBuffer(rcti *rect); 1498 | +}; 1499 | + 1500 | +#endif 1501 | diff --git a/source/blender/compositor/operations/COM_PythonOperation.cpp b/source/blender/compositor/operations/COM_PythonOperation.cpp 1502 | new file mode 100644 1503 | index 0000000..d36a5d4 1504 | --- /dev/null 1505 | +++ b/source/blender/compositor/operations/COM_PythonOperation.cpp 1506 | @@ -0,0 +1,297 @@ 1507 | + 1508 | +#include 1509 | +#include 1510 | +#include 1511 | + 1512 | +#include "WM_api.h" 1513 | +#include "Python.h" 1514 | +#include "numpy\arrayobject.h" 1515 | + 1516 | +#include "COM_PythonOperation.h" 1517 | + 1518 | +extern "C" 1519 | +void initCompositorPython() 1520 | +{ 1521 | + if (PyArray_API == NULL) { 1522 | + //This does not seem to be the best way to do this... 1523 | + if (_import_array() < 0) { 1524 | + if (PyErr_Occurred()) { 1525 | + PyErr_Print(); 1526 | + } 1527 | + Py_FatalError("Can't import numpy array"); 1528 | + } 1529 | + } 1530 | +} 1531 | + 1532 | +void fatal(const char *message) 1533 | +{ 1534 | + if (PyErr_Occurred()) { 1535 | + PyErr_Print(); 1536 | + } 1537 | + Py_FatalError(message); 1538 | +} 1539 | + 1540 | +PythonOperation::PythonOperation() : SingleThreadedOperation() 1541 | +{ 1542 | + for (int i = 0; i < COM_PYTHON_INPUT_IMAGES; i++) { 1543 | + addInputSocket(COM_DT_COLOR); 1544 | + } 1545 | + for (int i = 0; i < COM_PYTHON_INPUT_VALUES; i++) { 1546 | + addInputSocket(COM_DT_VALUE); 1547 | + } 1548 | + addOutputSocket(COM_DT_COLOR); 1549 | + setResolutionInputSocketIndex(0); 1550 | + 1551 | + m_data = NULL; 1552 | +} 1553 | + 1554 | +void PythonOperation::initExecution() 1555 | +{ 1556 | + SingleThreadedOperation::initExecution(); 1557 | +} 1558 | + 1559 | +void PythonOperation::deinitExecution() 1560 | +{ 1561 | + SingleThreadedOperation::deinitExecution(); 1562 | +} 1563 | + 1564 | +std::string readPythonFileContent(const std::string& path) 1565 | +{ 1566 | + std::ifstream input(path); 1567 | + std::stringstream buffer; 1568 | + buffer << input.rdbuf(); 1569 | + return buffer.str(); 1570 | +} 1571 | + 1572 | +PyObject *createScriptModule(const std::string& content, const std::string& path) 1573 | +{ 1574 | + PyObject *pyModule = PyModule_New("blender_com_python"); 1575 | + if (pyModule) { 1576 | + PyModule_AddStringConstant(pyModule, "__file__", path.c_str()); 1577 | + 1578 | + PyObject *locals = PyModule_GetDict(pyModule); //Borrowed ref 1579 | + PyObject *builtins = PyEval_GetBuiltins(); //Borrowed ref 1580 | + PyDict_SetItemString(locals, "__builtins__", builtins); 1581 | + 1582 | + PyObject *pyValue = PyRun_String(content.c_str(), Py_file_input, locals, locals); 1583 | + if (pyValue) { 1584 | + Py_DECREF(pyValue); 1585 | + } 1586 | + else { 1587 | + PyErr_Print(); 1588 | + } 1589 | + } 1590 | + return pyModule; 1591 | +} 1592 | + 1593 | +bool callMethod(PyObject* pyModule, const char *name, PyObject* context) 1594 | +{ 1595 | + bool ok = true; //Assume no function is defined with the given name 1596 | + 1597 | + PyObject* callback = PyDict_GetItemString(PyModule_GetDict(pyModule), name); //Borrowed ref 1598 | + if (callback) { 1599 | + ok = false; //Function found 1600 | + 1601 | + PyObject* args = Py_BuildValue("(O)", context); 1602 | + if (args) { 1603 | + PyObject *tmp = PyObject_CallObject(callback, args); 1604 | + if (tmp) { 1605 | + ok = true; 1606 | + Py_CLEAR(tmp); 1607 | + } 1608 | + else { 1609 | + PyErr_Print(); 1610 | + } 1611 | + Py_CLEAR(args); 1612 | + } 1613 | + else { 1614 | + PyErr_Print(); 1615 | + } 1616 | + } 1617 | + else { 1618 | + PyErr_Print(); 1619 | + } 1620 | + return ok; 1621 | +} 1622 | + 1623 | +void addBuffersToContext(PyObject *context, const char *name, std::vector *buffers) 1624 | +{ 1625 | + const int count = buffers->size(); 1626 | + PyObject *results = PyList_New(count); 1627 | + 1628 | + for (int i = 0; i < count; i++) { 1629 | + MemoryBuffer *buffer = buffers->at(i); 1630 | + 1631 | + npy_intp dims[3]; 1632 | + dims[0] = buffer->getHeight(); //Height first dimension 1633 | + dims[1] = buffer->getWidth(); 1634 | + dims[2] = 4; 1635 | + PyObject *arr = PyArray_SimpleNewFromData(3, dims, NPY_FLOAT32, buffer->getBuffer()); 1636 | + if (arr) { 1637 | + PyList_SetItem(results, i, arr); //Steals ref 1638 | + } 1639 | + else { 1640 | + fatal("Can't create a numpy array from a buffer"); 1641 | + } 1642 | + } 1643 | + 1644 | + PyDict_SetItemString(context, name, results); 1645 | + Py_DECREF(results); 1646 | +} 1647 | + 1648 | +void addValuesToContext(PyObject *context, const char *name, std::vector *values) 1649 | +{ 1650 | + const int count = values->size(); 1651 | + PyObject *results = PyList_New(count); 1652 | + 1653 | + for (int i = 0; i < count; i++) { 1654 | + PyObject *pyFloat = PyFloat_FromDouble(values->at(i)); 1655 | + if (pyFloat) { 1656 | + PyList_SET_ITEM(results, i, pyFloat); //Steals ref 1657 | + } 1658 | + else { 1659 | + fatal("Can't create a value float"); 1660 | + } 1661 | + } 1662 | + 1663 | + PyDict_SetItemString(context, name, results); 1664 | + Py_DECREF(results); 1665 | +} 1666 | + 1667 | +void clearBuffer(MemoryBuffer *buffer) 1668 | +{ 1669 | + const float invalid[] = { 1, 0, 0, 1 }; 1670 | + const int width = buffer->getWidth(); 1671 | + const int height = buffer->getHeight(); 1672 | + 1673 | + for (int y = 0; y < height; y++) { 1674 | + for (int x = 0; x < width; x++) { 1675 | + buffer->writePixel(x, y, invalid); 1676 | + } 1677 | + } 1678 | +} 1679 | + 1680 | +struct ThreadData { 1681 | + ThreadCondition condition; 1682 | + ThreadMutex *mutex; 1683 | + 1684 | + PyObject *context; 1685 | + PyObject *pyModule; 1686 | + std::string scriptPath; 1687 | + std::string scriptContent; 1688 | + bool startOk; 1689 | +}; 1690 | + 1691 | +void pythonMainThreadCallback(void* userData) 1692 | +{ 1693 | + BLI_assert(BLI_thread_is_main()); 1694 | + 1695 | + PyGILState_STATE gstate = PyGILState_Ensure(); 1696 | + 1697 | + ThreadData* data = (ThreadData*)userData; 1698 | + 1699 | + data->pyModule = createScriptModule(data->scriptContent, data->scriptPath); 1700 | + if (data->pyModule) { 1701 | + data->startOk = callMethod(data->pyModule, "on_main", data->context); 1702 | + } 1703 | + PyGILState_Release(gstate); 1704 | + 1705 | + BLI_condition_notify_all(&data->condition); 1706 | +} 1707 | + 1708 | +MemoryBuffer *PythonOperation::createMemoryBuffer(rcti *source) 1709 | +{ 1710 | + rcti rect; 1711 | + rect.xmin = 0; 1712 | + rect.ymin = 0; 1713 | + rect.xmax = source->xmax; 1714 | + rect.ymax = source->ymax; 1715 | + MemoryBuffer *result = new MemoryBuffer(COM_DT_COLOR, &rect); 1716 | + 1717 | + std::vector inputs; 1718 | + for (int i = 0; i < COM_PYTHON_INPUT_IMAGES; i++) { 1719 | + MemoryBuffer *tile = (MemoryBuffer*)getInputSocketReader(i)->initializeTileData(source); 1720 | + inputs.push_back(tile); 1721 | + } 1722 | + 1723 | + std::vector outputs; 1724 | + outputs.push_back(result); 1725 | + 1726 | + for (int i = 0; i < outputs.size(); i++) { 1727 | + clearBuffer(outputs[i]); 1728 | + } 1729 | + 1730 | + const std::string content = readPythonFileContent(m_path); 1731 | + 1732 | + // Script import and main callback 1733 | + PyGILState_STATE gstate; 1734 | + gstate = PyGILState_Ensure(); 1735 | + 1736 | + PyObject* context = PyDict_New(); 1737 | + addBuffersToContext(context, "images", &inputs); 1738 | + addBuffersToContext(context, "outputs", &outputs); 1739 | + 1740 | + std::vector values; 1741 | + float value[4] = { 0 }; 1742 | + for (int i = 0; i < COM_PYTHON_INPUT_VALUES; i++) { 1743 | + getInputSocketReader(COM_PYTHON_INPUT_IMAGES + i)->readSampled(value, 0, 0, COM_PS_NEAREST); 1744 | + values.push_back(value[0]); 1745 | + } 1746 | + addValuesToContext(context, "inputs", &values); 1747 | + 1748 | + PyGILState_Release(gstate); 1749 | + 1750 | + ThreadData threadData; 1751 | + threadData.condition = NULL; 1752 | + threadData.mutex = BLI_mutex_alloc(); 1753 | + BLI_condition_init(&threadData.condition); 1754 | + 1755 | + threadData.context = context; 1756 | + threadData.pyModule = NULL; 1757 | + threadData.scriptContent = content; 1758 | + threadData.scriptPath = m_path; 1759 | + threadData.startOk = false; 1760 | + 1761 | + // Run Python init in the main thread and wait until it is ready 1762 | + WM_run_in_main_thread(pythonMainThreadCallback, &threadData); 1763 | + BLI_condition_wait(&threadData.condition, threadData.mutex); 1764 | + 1765 | + // Async callback 1766 | + gstate = PyGILState_Ensure(); 1767 | + 1768 | + bool endOk = false; 1769 | + if (threadData.pyModule) { 1770 | + endOk = callMethod(threadData.pyModule, "on_async", context); 1771 | + } 1772 | + Py_CLEAR(context); 1773 | + Py_CLEAR(threadData.pyModule); 1774 | + 1775 | + PyGILState_Release(gstate); 1776 | + 1777 | + if (!threadData.startOk || !endOk) { 1778 | + // A callback failed, erase all output buffers 1779 | + for (int i = 0; i < outputs.size(); i++) { 1780 | + clearBuffer(outputs[i]); 1781 | + } 1782 | + } 1783 | + 1784 | + BLI_condition_end(&threadData.condition); 1785 | + BLI_mutex_free(threadData.mutex); 1786 | + 1787 | + return result; 1788 | +} 1789 | + 1790 | +bool PythonOperation::determineDependingAreaOfInterest(rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) 1791 | +{ 1792 | + if (isCached()) { 1793 | + return false; 1794 | + } 1795 | + else { 1796 | + rcti newInput; 1797 | + newInput.xmin = 0; 1798 | + newInput.ymin = 0; 1799 | + newInput.xmax = getWidth(); 1800 | + newInput.ymax = getHeight(); 1801 | + return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); 1802 | + } 1803 | +} 1804 | diff --git a/source/blender/compositor/operations/COM_PythonOperation.h b/source/blender/compositor/operations/COM_PythonOperation.h 1805 | new file mode 100644 1806 | index 0000000..42123e7 1807 | --- /dev/null 1808 | +++ b/source/blender/compositor/operations/COM_PythonOperation.h 1809 | @@ -0,0 +1,32 @@ 1810 | + 1811 | +#ifndef _COM_PythonOperation_h_ 1812 | +#define _COM_PythonOperation_h_ 1813 | + 1814 | +#include 1815 | + 1816 | +#include "COM_NodeOperation.h" 1817 | +#include "COM_SingleThreadedOperation.h" 1818 | + 1819 | +#define COM_PYTHON_INPUT_IMAGES 4 1820 | +#define COM_PYTHON_INPUT_VALUES 8 1821 | + 1822 | +class PythonOperation : public SingleThreadedOperation { 1823 | +private: 1824 | + const NodePython *m_data; 1825 | + std::string m_path; 1826 | +public: 1827 | + PythonOperation(); 1828 | + 1829 | + void initExecution(); 1830 | + void deinitExecution(); 1831 | + bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output); 1832 | + 1833 | + void setData(const NodePython *data) { this->m_data = data; } 1834 | + void setPath(const std::string& path) { this->m_path = path; } 1835 | + 1836 | +protected: 1837 | + 1838 | + MemoryBuffer *createMemoryBuffer(rcti *rect); 1839 | +}; 1840 | + 1841 | +#endif 1842 | diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c 1843 | index 8b10320..482811c 100644 1844 | --- a/source/blender/editors/space_node/drawnode.c 1845 | +++ b/source/blender/editors/space_node/drawnode.c 1846 | @@ -2483,6 +2483,49 @@ static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout), bContext *UNU 1847 | { 1848 | } 1849 | 1850 | +extern char** explainGmicCommands(const char*, int*); 1851 | +extern void freeGmicExplainCommands(char**, int); 1852 | + 1853 | +static void node_composit_buts_gmic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) 1854 | +{ 1855 | + uiItemR(layout, ptr, "quality", UI_ITEM_R_SLIDER, NULL, ICON_NONE); 1856 | + uiItemR(layout, ptr, "command", 0, "", ICON_NONE); 1857 | + uiItemR(layout, ptr, "normalize", 0, NULL, ICON_NONE); 1858 | + uiItemR(layout, ptr, "explain", 0, NULL, ICON_NONE); 1859 | + 1860 | + bNode *node = ptr->data; 1861 | + NodeGmic *data = node->storage; 1862 | + if (data->flag & CMP_NODE_GMIC_EXPLAIN) { 1863 | + uiLayout *base = layout; 1864 | + 1865 | + int count = 0; 1866 | + char** help = explainGmicCommands(data->command, &count); 1867 | + for (int i = 0; i < count; i += 2) { 1868 | + if (strlen(help[i + 1]) == 0) { 1869 | + base = uiLayoutBox(layout); 1870 | + } 1871 | + uiLayout *row = uiLayoutRow(base, false); 1872 | + 1873 | + uiItemL(row, help[i], ICON_NONE); 1874 | + uiItemL(row, help[i + 1], ICON_NONE); 1875 | + } 1876 | + freeGmicExplainCommands(help, count); 1877 | + } 1878 | +} 1879 | + 1880 | +static void node_composit_buts_glsl(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) 1881 | +{ 1882 | + uiItemL(layout, IFACE_("Fragment shader:"), ICON_NONE); 1883 | + uiItemR(layout, ptr, "filepath", 0, "", ICON_NONE); 1884 | + uiItemR(layout, ptr, "gamma", 0, NULL, ICON_NONE); 1885 | +} 1886 | + 1887 | +static void node_composit_buts_python(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) 1888 | +{ 1889 | + uiItemL(layout, IFACE_("Script file:"), ICON_NONE); 1890 | + uiItemR(layout, ptr, "filepath", 0, "", ICON_NONE); 1891 | +} 1892 | + 1893 | static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) 1894 | { 1895 | uiItemR(layout, ptr, "source", UI_ITEM_R_EXPAND, "", ICON_NONE); 1896 | @@ -2721,6 +2764,18 @@ static void node_composit_set_butfunc(bNodeType *ntype) 1897 | case CMP_NODE_SUNBEAMS: 1898 | ntype->draw_buttons = node_composit_buts_sunbeams; 1899 | break; 1900 | + case CMP_NODE_GMIC: 1901 | + ntype->draw_buttons = node_composit_buts_gmic; 1902 | + ntype->width = ntype->width * 2; 1903 | + break; 1904 | + case CMP_NODE_GLSL: 1905 | + ntype->draw_buttons = node_composit_buts_glsl; 1906 | + ntype->width += 50; 1907 | + break; 1908 | + case CMP_NODE_PYTHON: 1909 | + ntype->draw_buttons = node_composit_buts_python; 1910 | + ntype->width += 50; 1911 | + break; 1912 | case CMP_NODE_BRIGHTCONTRAST: 1913 | ntype->draw_buttons = node_composit_buts_brightcontrast; 1914 | } 1915 | diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h 1916 | index e6bc315..448dbab 100644 1917 | --- a/source/blender/makesdna/DNA_node_types.h 1918 | +++ b/source/blender/makesdna/DNA_node_types.h 1919 | @@ -893,6 +893,23 @@ typedef struct NodeShaderUVMap { 1920 | char uv_map[64]; 1921 | } NodeShaderUVMap; 1922 | 1923 | +typedef struct NodeGmic { 1924 | + float quality; 1925 | + char flag; 1926 | + char command[1024]; 1927 | + char pad[3]; 1928 | +} NodeGmic; 1929 | + 1930 | +typedef struct NodeGlsl { 1931 | + char filepath[1024]; /* 1024 = FILE_MAX */ 1932 | + char flag; 1933 | + char pad[3]; 1934 | +} NodeGlsl; 1935 | + 1936 | +typedef struct NodePython { 1937 | + char filepath[1024]; /* 1024 = FILE_MAX */ 1938 | +} NodePython; 1939 | + 1940 | typedef struct NodeSunBeams { 1941 | float source[2]; 1942 | 1943 | @@ -906,6 +923,12 @@ typedef struct NodeSunBeams { 1944 | /* script node flag */ 1945 | #define NODE_SCRIPT_AUTO_UPDATE 1 1946 | 1947 | +/* GMIC node flag */ 1948 | +#define CMP_NODE_GMIC_NORMALIZE 1 1949 | +#define CMP_NODE_GMIC_EXPLAIN 2 1950 | + 1951 | +/* GLSL node flag */ 1952 | +#define CMP_NODE_GLSL_GAMMA 1 1953 | 1954 | /* frame node flags */ 1955 | #define NODE_FRAME_SHRINK 1 /* keep the bounding box minimal */ 1956 | diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c 1957 | index 4c096be..93126eb 100644 1958 | --- a/source/blender/makesrna/intern/rna_nodetree.c 1959 | +++ b/source/blender/makesrna/intern/rna_nodetree.c 1960 | @@ -6799,6 +6799,64 @@ static void def_cmp_planetrackdeform(StructRNA *srna) 1961 | RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); 1962 | } 1963 | 1964 | +static void def_cmp_gmic(StructRNA *srna) 1965 | +{ 1966 | + PropertyRNA *prop; 1967 | + 1968 | + RNA_def_struct_sdna_from(srna, "NodeGmic", "storage"); 1969 | + 1970 | + prop = RNA_def_property(srna, "quality", PROP_FLOAT, PROP_UNSIGNED); 1971 | + RNA_def_property_float_sdna(prop, NULL, "quality"); 1972 | + RNA_def_property_range(prop, 0.0f, 100.0f); 1973 | + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 10, 3); 1974 | + RNA_def_property_ui_text(prop, "Quality", ""); 1975 | + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); 1976 | + 1977 | + prop = RNA_def_property(srna, "normalize", PROP_BOOLEAN, PROP_NONE); 1978 | + RNA_def_property_boolean_sdna(prop, NULL, "flag", CMP_NODE_GMIC_NORMALIZE); 1979 | + RNA_def_property_ui_text(prop, "Normalize image data", "Normalize, convert and mirror image data"); 1980 | + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); 1981 | + 1982 | + prop = RNA_def_property(srna, "explain", PROP_BOOLEAN, PROP_NONE); 1983 | + RNA_def_property_boolean_sdna(prop, NULL, "flag", CMP_NODE_GMIC_EXPLAIN); 1984 | + RNA_def_property_ui_text(prop, "Show parameters for used GIMP filters", "Try to show command parameters and limits in plain text"); 1985 | + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); 1986 | + 1987 | + prop = RNA_def_property(srna, "command", PROP_STRING, PROP_NONE); 1988 | + RNA_def_property_string_sdna(prop, NULL, "command"); 1989 | + RNA_def_property_ui_text(prop, "Command", ""); 1990 | + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); 1991 | +} 1992 | + 1993 | +static void def_cmp_glsl(StructRNA *srna) 1994 | +{ 1995 | + PropertyRNA *prop; 1996 | + 1997 | + RNA_def_struct_sdna_from(srna, "NodeGlsl", "storage"); 1998 | + 1999 | + prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); 2000 | + RNA_def_property_string_sdna(prop, NULL, "filepath"); 2001 | + RNA_def_property_ui_text(prop, "Shader file", ""); 2002 | + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); 2003 | + 2004 | + prop = RNA_def_property(srna, "gamma", PROP_BOOLEAN, PROP_NONE); 2005 | + RNA_def_property_boolean_sdna(prop, NULL, "flag", CMP_NODE_GLSL_GAMMA); 2006 | + RNA_def_property_ui_text(prop, "Gamma correction", "Apply gamma correction to result image"); 2007 | + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); 2008 | +} 2009 | + 2010 | +static void def_cmp_python(StructRNA *srna) 2011 | +{ 2012 | + PropertyRNA *prop; 2013 | + 2014 | + RNA_def_struct_sdna_from(srna, "NodePython", "storage"); 2015 | + 2016 | + prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); 2017 | + RNA_def_property_string_sdna(prop, NULL, "filepath"); 2018 | + RNA_def_property_ui_text(prop, "Script file", ""); 2019 | + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); 2020 | +} 2021 | + 2022 | static void def_cmp_sunbeams(StructRNA *srna) 2023 | { 2024 | PropertyRNA *prop; 2025 | diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt 2026 | index 21f9afe..8e01122 100644 2027 | --- a/source/blender/nodes/CMakeLists.txt 2028 | +++ b/source/blender/nodes/CMakeLists.txt 2029 | @@ -124,6 +124,9 @@ set(SRC 2030 | composite/nodes/node_composite_switch.c 2031 | composite/nodes/node_composite_switchview.c 2032 | composite/nodes/node_composite_colorcorrection.c 2033 | + composite/nodes/node_composite_gmic.c 2034 | + composite/nodes/node_composite_glsl.c 2035 | + composite/nodes/node_composite_python.c 2036 | composite/nodes/node_composite_pixelate.c 2037 | 2038 | composite/node_composite_tree.c 2039 | diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h 2040 | index a5c2e60..2c6de16 100644 2041 | --- a/source/blender/nodes/NOD_composite.h 2042 | +++ b/source/blender/nodes/NOD_composite.h 2043 | @@ -138,6 +138,9 @@ void register_node_type_cmp_pixelate(void); 2044 | void register_node_type_cmp_trackpos(void); 2045 | void register_node_type_cmp_planetrackdeform(void); 2046 | void register_node_type_cmp_cornerpin(void); 2047 | +void register_node_type_cmp_gmic(void); 2048 | +void register_node_type_cmp_glsl(void); 2049 | +void register_node_type_cmp_python(void); 2050 | 2051 | void node_cmp_rlayers_outputs(struct bNodeTree *ntree, struct bNode *node); 2052 | void node_cmp_rlayers_register_pass(struct bNodeTree *ntree, struct bNode *node, struct Scene *scene, struct SceneRenderLayer *srl, const char *name, int type); 2053 | diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h 2054 | index 1384429..76de95b 100644 2055 | --- a/source/blender/nodes/NOD_static_types.h 2056 | +++ b/source/blender/nodes/NOD_static_types.h 2057 | @@ -215,6 +215,9 @@ DefNode( CompositorNode, CMP_NODE_PIXELATE, 0, "PIXEL 2058 | DefNode( CompositorNode, CMP_NODE_PLANETRACKDEFORM,def_cmp_planetrackdeform,"PLANETRACKDEFORM",PlaneTrackDeform,"Plane Track Deform","" ) 2059 | DefNode( CompositorNode, CMP_NODE_CORNERPIN, 0, "CORNERPIN", CornerPin, "Corner Pin", "" ) 2060 | DefNode( CompositorNode, CMP_NODE_SUNBEAMS, def_cmp_sunbeams, "SUNBEAMS", SunBeams, "Sun Beams", "" ) 2061 | +DefNode( CompositorNode, CMP_NODE_GMIC, def_cmp_gmic, "GMIC", Gmic, "G'MIC", "" ) 2062 | +DefNode( CompositorNode, CMP_NODE_GLSL, def_cmp_glsl, "GLSL", Glsl, "GLSL Shader", "" ) 2063 | +DefNode( CompositorNode, CMP_NODE_PYTHON, def_cmp_python, "PYTHON", Python, "Python Script", "" ) 2064 | 2065 | DefNode( TextureNode, TEX_NODE_OUTPUT, def_tex_output, "OUTPUT", Output, "Output", "" ) 2066 | DefNode( TextureNode, TEX_NODE_CHECKER, 0, "CHECKER", Checker, "Checker", "" ) 2067 | diff --git a/source/blender/nodes/composite/nodes/node_composite_glsl.c b/source/blender/nodes/composite/nodes/node_composite_glsl.c 2068 | new file mode 100644 2069 | index 0000000..9a390fd 2070 | --- /dev/null 2071 | +++ b/source/blender/nodes/composite/nodes/node_composite_glsl.c 2072 | @@ -0,0 +1,42 @@ 2073 | + 2074 | +#include "node_composite_util.h" 2075 | + 2076 | +static bNodeSocketTemplate cmp_node_glsl_in[] = { 2077 | + { SOCK_RGBA, 1, N_("Channel 0"), 0.0f, 0.0f, 0.0f, 1.0f }, 2078 | + { SOCK_RGBA, 1, N_("Channel 1"), 0.0f, 0.0f, 0.0f, 1.0f }, 2079 | + { SOCK_RGBA, 1, N_("Channel 2"), 0.0f, 0.0f, 0.0f, 1.0f }, 2080 | + { SOCK_RGBA, 1, N_("Channel 3"), 0.0f, 0.0f, 0.0f, 1.0f }, 2081 | + { SOCK_FLOAT, 1, N_("Time"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2082 | + { SOCK_FLOAT, 1, N_("Input 0"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2083 | + { SOCK_FLOAT, 1, N_("Input 1"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2084 | + { SOCK_FLOAT, 1, N_("Input 2"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2085 | + { SOCK_FLOAT, 1, N_("Input 3"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2086 | + { -1, 0, "" } 2087 | +}; 2088 | + 2089 | +static bNodeSocketTemplate cmp_node_glsl_out[] = { 2090 | + { SOCK_RGBA, 0, N_("Image") }, 2091 | + { -1, 0, "" } 2092 | +}; 2093 | + 2094 | +static void node_composit_init_glsl(bNodeTree *UNUSED(ntree), bNode *node) 2095 | +{ 2096 | + NodeGlsl *data = MEM_callocN(sizeof(NodeGlsl), "glsl node"); 2097 | + 2098 | + data->flag = CMP_NODE_GLSL_GAMMA; 2099 | + memset(data->filepath, 0, sizeof(data->filepath)); 2100 | + 2101 | + node->storage = data; 2102 | +} 2103 | + 2104 | +void register_node_type_cmp_glsl(void) 2105 | +{ 2106 | + static bNodeType ntype; 2107 | + 2108 | + cmp_node_type_base(&ntype, CMP_NODE_GLSL, "GLSL Shader", NODE_CLASS_OP_FILTER, 0); 2109 | + node_type_socket_templates(&ntype, cmp_node_glsl_in, cmp_node_glsl_out); 2110 | + node_type_init(&ntype, node_composit_init_glsl); 2111 | + node_type_storage(&ntype, "NodeGlsl", node_free_standard_storage, node_copy_standard_storage); 2112 | + 2113 | + nodeRegisterType(&ntype); 2114 | +} 2115 | diff --git a/source/blender/nodes/composite/nodes/node_composite_gmic.c b/source/blender/nodes/composite/nodes/node_composite_gmic.c 2116 | new file mode 100644 2117 | index 0000000..7e76fee 2118 | --- /dev/null 2119 | +++ b/source/blender/nodes/composite/nodes/node_composite_gmic.c 2120 | @@ -0,0 +1,40 @@ 2121 | + 2122 | +#include "node_composite_util.h" 2123 | + 2124 | +static bNodeSocketTemplate cmp_node_gmic_in[] = { 2125 | + { SOCK_RGBA, 1, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f }, 2126 | + { SOCK_FLOAT, 1, N_("$arg1"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, FLT_MAX }, 2127 | + { SOCK_FLOAT, 1, N_("$arg2"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, FLT_MAX }, 2128 | + { SOCK_FLOAT, 1, N_("$arg3"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, FLT_MAX }, 2129 | + { SOCK_FLOAT, 1, N_("$arg4"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, FLT_MAX }, 2130 | + { SOCK_FLOAT, 1, N_("$arg5"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, FLT_MAX }, 2131 | + { -1, 0, "" } 2132 | +}; 2133 | + 2134 | +static bNodeSocketTemplate cmp_node_gmic_out[] = { 2135 | + { SOCK_RGBA, 0, N_("Image") }, 2136 | + { -1, 0, "" } 2137 | +}; 2138 | + 2139 | +static void node_composit_init_gmic(bNodeTree *UNUSED(ntree), bNode *node) 2140 | +{ 2141 | + NodeGmic *data = MEM_callocN(sizeof(NodeGmic), "gmic node"); 2142 | + 2143 | + data->quality = 0.5f; 2144 | + data->flag = CMP_NODE_GMIC_NORMALIZE; 2145 | + memset(data->command, 0, sizeof(data->command)); 2146 | + 2147 | + node->storage = data; 2148 | +} 2149 | + 2150 | +void register_node_type_cmp_gmic(void) 2151 | +{ 2152 | + static bNodeType ntype; 2153 | + 2154 | + cmp_node_type_base(&ntype, CMP_NODE_GMIC, "G'MIC", NODE_CLASS_OP_FILTER, 0); 2155 | + node_type_socket_templates(&ntype, cmp_node_gmic_in, cmp_node_gmic_out); 2156 | + node_type_init(&ntype, node_composit_init_gmic); 2157 | + node_type_storage(&ntype, "NodeGmic", node_free_standard_storage, node_copy_standard_storage); 2158 | + 2159 | + nodeRegisterType(&ntype); 2160 | +} 2161 | diff --git a/source/blender/nodes/composite/nodes/node_composite_python.c b/source/blender/nodes/composite/nodes/node_composite_python.c 2162 | new file mode 100644 2163 | index 0000000..4651303 2164 | --- /dev/null 2165 | +++ b/source/blender/nodes/composite/nodes/node_composite_python.c 2166 | @@ -0,0 +1,43 @@ 2167 | + 2168 | +#include "node_composite_util.h" 2169 | + 2170 | +static bNodeSocketTemplate cmp_node_python_in[] = { 2171 | + { SOCK_RGBA, 1, N_("Image 0"), 1.0f, 1.0f, 1.0f, 1.0f }, 2172 | + { SOCK_RGBA, 1, N_("Image 1"), 1.0f, 1.0f, 1.0f, 1.0f }, 2173 | + { SOCK_RGBA, 1, N_("Image 2"), 1.0f, 1.0f, 1.0f, 1.0f }, 2174 | + { SOCK_RGBA, 1, N_("Image 3"), 1.0f, 1.0f, 1.0f, 1.0f }, 2175 | + { SOCK_FLOAT, 1, N_("Input 0"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2176 | + { SOCK_FLOAT, 1, N_("Input 1"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2177 | + { SOCK_FLOAT, 1, N_("Input 2"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2178 | + { SOCK_FLOAT, 1, N_("Input 3"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2179 | + { SOCK_FLOAT, 1, N_("Input 4"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2180 | + { SOCK_FLOAT, 1, N_("Input 5"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2181 | + { SOCK_FLOAT, 1, N_("Input 6"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2182 | + { SOCK_FLOAT, 1, N_("Input 7"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX }, 2183 | + { -1, 0, "" } 2184 | +}; 2185 | + 2186 | +static bNodeSocketTemplate cmp_node_python_out[] = { 2187 | + { SOCK_RGBA, 0, N_("Output 0") }, 2188 | + { -1, 0, "" } 2189 | +}; 2190 | + 2191 | +static void node_composit_init_python(bNodeTree *UNUSED(ntree), bNode *node) 2192 | +{ 2193 | + NodePython *data = MEM_callocN(sizeof(NodePython), "python node"); 2194 | + memset(data->filepath, 0, sizeof(data->filepath)); 2195 | + 2196 | + node->storage = data; 2197 | +} 2198 | + 2199 | +void register_node_type_cmp_python(void) 2200 | +{ 2201 | + static bNodeType ntype; 2202 | + 2203 | + cmp_node_type_base(&ntype, CMP_NODE_PYTHON, "Python Script", NODE_CLASS_OP_FILTER, 0); 2204 | + node_type_socket_templates(&ntype, cmp_node_python_in, cmp_node_python_out); 2205 | + node_type_init(&ntype, node_composit_init_python); 2206 | + node_type_storage(&ntype, "NodePython", node_free_standard_storage, node_copy_standard_storage); 2207 | + 2208 | + nodeRegisterType(&ntype); 2209 | +} 2210 | diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c 2211 | index 20cfd36..0e10e21 100644 2212 | --- a/source/blender/python/intern/bpy_interface.c 2213 | +++ b/source/blender/python/intern/bpy_interface.c 2214 | @@ -231,6 +231,8 @@ static struct _inittab bpy_internal_modules[] = { 2215 | {NULL, NULL} 2216 | }; 2217 | 2218 | +int initCompositorPython(); 2219 | + 2220 | /* call BPY_context_set first */ 2221 | void BPY_python_start(int argc, const char **argv) 2222 | { 2223 | @@ -326,6 +328,8 @@ void BPY_python_start(int argc, const char **argv) 2224 | 2225 | pyrna_alloc_types(); 2226 | 2227 | + initCompositorPython(); 2228 | + 2229 | #ifndef WITH_PYTHON_MODULE 2230 | /* py module runs atexit when bpy is freed */ 2231 | BPY_atexit_register(); /* this can init any time */ 2232 | diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h 2233 | index e400268..a603cfc 100644 2234 | --- a/source/blender/windowmanager/WM_api.h 2235 | +++ b/source/blender/windowmanager/WM_api.h 2236 | @@ -496,6 +496,10 @@ bool WM_jobs_has_running(struct wmWindowManager *wm); 2237 | void WM_job_main_thread_lock_acquire(struct wmJob *job); 2238 | void WM_job_main_thread_lock_release(struct wmJob *job); 2239 | 2240 | + /* sync */ 2241 | +typedef void(*MainThreadCallback)(void*); 2242 | +void WM_run_in_main_thread(MainThreadCallback, void*); 2243 | + 2244 | /* clipboard */ 2245 | char *WM_clipboard_text_get(bool selection, int *r_len); 2246 | char *WM_clipboard_text_get_firstline(bool selection, int *r_len); 2247 | diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c 2248 | index e73ec2b..ad93b28 100644 2249 | --- a/source/blender/windowmanager/intern/wm_init_exit.c 2250 | +++ b/source/blender/windowmanager/intern/wm_init_exit.c 2251 | @@ -147,11 +147,14 @@ static void wm_undo_kill_callback(bContext *C) 2252 | WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(C)); 2253 | } 2254 | 2255 | +void wm_window_create_main_queue(); 2256 | + 2257 | bool wm_start_with_console = false; /* used in creator.c */ 2258 | 2259 | /* only called once, for startup */ 2260 | void WM_init(bContext *C, int argc, const char **argv) 2261 | { 2262 | + wm_window_create_main_queue(); 2263 | 2264 | if (!G.background) { 2265 | wm_ghost_init(C); /* note: it assigns C to ghost! */ 2266 | diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c 2267 | index fbd6d89..b1c5445 100644 2268 | --- a/source/blender/windowmanager/intern/wm_operators.c 2269 | +++ b/source/blender/windowmanager/intern/wm_operators.c 2270 | @@ -1856,6 +1856,9 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar 2271 | else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) { 2272 | version_suffix = STRINGIFY(BLENDER_VERSION_CHAR); 2273 | } 2274 | + 2275 | + version_suffix = "Nodes 0.3.0"; 2276 | + 2277 | if (version_suffix != NULL && version_suffix[0]) { 2278 | /* placed after the version number in the image, 2279 | * placing y is tricky to match baseline */ 2280 | diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c 2281 | index 79078b3..61f0531 100644 2282 | --- a/source/blender/windowmanager/intern/wm_window.c 2283 | +++ b/source/blender/windowmanager/intern/wm_window.c 2284 | @@ -45,6 +45,7 @@ 2285 | 2286 | #include "BLI_math.h" 2287 | #include "BLI_blenlib.h" 2288 | +#include "BLI_threads.h" 2289 | #include "BLI_utildefines.h" 2290 | 2291 | #include "BLT_translation.h" 2292 | @@ -1355,12 +1356,54 @@ static int wm_window_timer(const bContext *C) 2293 | return retval; 2294 | } 2295 | 2296 | +ThreadQueue* wm_main_thread_queue = NULL; 2297 | + 2298 | +typedef struct MainThreadWork { 2299 | + MainThreadCallback callback; 2300 | + void *user_data; 2301 | +} MainThreadWork; 2302 | + 2303 | +void WM_run_in_main_thread(MainThreadCallback callback, void *user_data) 2304 | +{ 2305 | + MainThreadWork* work = malloc(sizeof(MainThreadWork)); 2306 | + work->callback = callback; 2307 | + work->user_data = user_data; 2308 | + BLI_thread_queue_push(wm_main_thread_queue, work); 2309 | +} 2310 | + 2311 | +void wm_window_create_main_queue() 2312 | +{ 2313 | + //Never freed 2314 | + if (wm_main_thread_queue == NULL) { 2315 | + wm_main_thread_queue = BLI_thread_queue_init(); 2316 | + } 2317 | +} 2318 | + 2319 | +int wm_window_process_main_queue_events() 2320 | +{ 2321 | + BLI_assert(BLI_thread_is_main()); 2322 | + 2323 | + int count = 0; 2324 | + 2325 | + MainThreadWork *work = NULL; 2326 | + while (work = BLI_thread_queue_pop_timeout(wm_main_thread_queue, 0)) { 2327 | + (work->callback)(work->user_data); 2328 | + 2329 | + free(work); 2330 | + 2331 | + count++; 2332 | + } 2333 | + return count; 2334 | +} 2335 | + 2336 | void wm_window_process_events(const bContext *C) 2337 | { 2338 | int hasevent; 2339 | 2340 | BLI_assert(BLI_thread_is_main()); 2341 | 2342 | + wm_window_process_main_queue_events(); 2343 | + 2344 | hasevent = GHOST_ProcessEvents(g_system, 0); /* 0 is no wait */ 2345 | 2346 | if (hasevent) 2347 | diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt 2348 | index 2bda651..5c1679d 100644 2349 | --- a/source/creator/CMakeLists.txt 2350 | +++ b/source/creator/CMakeLists.txt 2351 | @@ -1012,6 +1012,7 @@ add_dependencies(blender makesdna) 2352 | 2353 | setup_blender_sorted_libs() 2354 | target_link_libraries(blender ${BLENDER_SORTED_LIBS}) 2355 | +target_link_libraries(blender "${CMAKE_SOURCE_DIR}/extern/gmic64/libcgmicstatic.lib") 2356 | 2357 | setup_liblinks(blender) 2358 | 2359 | --------------------------------------------------------------------------------